From 1e2984ff00ca2f94ffbc07b94de26c476b14e500 Mon Sep 17 00:00:00 2001 From: Tine Kondo Date: Wed, 18 Aug 2021 00:29:57 +0200 Subject: [PATCH] feat(nx-spring-boot): make jar of `library` projects not executable Moreover, we only add 'boot' related executors to `workspace.json` for `application` projects Closes #67 --- packages/nx-spring-boot/README.md | 14 ++++---- .../src/generators/project/generator.spec.ts | 35 ++++++++++++++----- .../src/generators/project/generator.ts | 16 +++++++-- .../project/lib/add-build-info-task.ts | 2 +- .../project/lib/disable-boot-jar-task.ts | 29 +++++++++++++++ .../src/generators/project/lib/index.ts | 2 ++ .../project/lib/remove-spring-boot-plugin.ts | 27 ++++++++++++++ 7 files changed, 107 insertions(+), 18 deletions(-) create mode 100644 packages/nx-spring-boot/src/generators/project/lib/disable-boot-jar-task.ts create mode 100644 packages/nx-spring-boot/src/generators/project/lib/remove-spring-boot-plugin.ts diff --git a/packages/nx-spring-boot/README.md b/packages/nx-spring-boot/README.md index 3d1095cd..71c6b1a3 100644 --- a/packages/nx-spring-boot/README.md +++ b/packages/nx-spring-boot/README.md @@ -101,17 +101,19 @@ Option | Value | Description Once your app is generated, you can now use buidlers to manage it. -Here the list of available builders: +Here the list of available executors: | Builder | Arguments | Description | | --------------- | ------------------------------------------ | ------------------------------------------ | -| `run` \| `serve`| `ignoreWrapper:boolean`, `args: string[]` | Runs the project using either `./mvnw\|mvn spring-boot:run` or `./gradlew\|gradle bootRun` | +| `run` \| `serve`*| `ignoreWrapper:boolean`, `args: string[]` | Runs the project using either `./mvnw\|mvn spring-boot:run` or `./gradlew\|gradle bootRun` | | `test` | `ignoreWrapper:boolean`, `args: string[]` | Tests the project using either `./mvnw\|mvn test` or `./gradlew\|gradle test` | | `clean` | `ignoreWrapper:boolean`, `args: string[]` | Cleans the project using either `./mvnw\|mvn clean` or `./gradlew\|gradle clean` | -| `buidlJar` | `ignoreWrapper:boolean`, `args: string[]` | Packages the project into an executable Jar using either `./mvnw\|mvn spring-boot:repackage` or `./gradlew\|gradle bootJar` | -| `buildWar` | `ignoreWrapper:boolean`, `args: string[]` | Packages the project into an executable War using either `./mvnw\|mvn spring-boot:repackage` or `./gradlew\|gradle bootWar` | -| `buildInfo` | `ignoreWrapper:boolean`, | Generates a `build-info.properties` using either `./mvnw\|mvn spring-boot:build-info` or `./gradlew\|gradle bootBuildInfo` | -| `buildImage` | `ignoreWrapper:boolean`, `args: string[]` | Generates an [OCI Image](https://github.com/opencontainers/image-spec) using either `./mvnw\|mvn spring-boot:build-image` or `./gradlew\|gradle bootBuildImage` | +| `buidlJar`* | `ignoreWrapper:boolean`, `args: string[]` | Packages the project into an executable Jar using either `./mvnw\|mvn spring-boot:repackage` or `./gradlew\|gradle bootJar` | +| `buildWar`* | `ignoreWrapper:boolean`, `args: string[]` | Packages the project into an executable War using either `./mvnw\|mvn spring-boot:repackage` or `./gradlew\|gradle bootWar` | +| `buildInfo`* | `ignoreWrapper:boolean`, | Generates a `build-info.properties` using either `./mvnw\|mvn spring-boot:build-info` or `./gradlew\|gradle bootBuildInfo` | +| `buildImage`* | `ignoreWrapper:boolean`, `args: string[]` | Generates an [OCI Image](https://github.com/opencontainers/image-spec) using either `./mvnw\|mvn spring-boot:build-image` or `./gradlew\|gradle bootBuildImage` | + +> * = These executors are only available if the project is a Spring Boot `application`. In order to execute the requested command, each builder will use, by default, the embedded `./mvnw` or `./gradlew` executable, that was generated alongside the project. If you want to rely on a globally installed `mvn` or `gradle` executable instead, add the `--ignoreWrapper` option to bypass it. diff --git a/packages/nx-spring-boot/src/generators/project/generator.spec.ts b/packages/nx-spring-boot/src/generators/project/generator.spec.ts index dcf97c70..7fe68eaa 100644 --- a/packages/nx-spring-boot/src/generators/project/generator.spec.ts +++ b/packages/nx-spring-boot/src/generators/project/generator.spec.ts @@ -52,17 +52,17 @@ describe('project generator', () => { ${'library'} | ${'gradle-project'} | ${'build.gradle'} | ${'gradlew'} `(`should download a spring boot '$projectType' build with $buildSystem`, async ({ projectType, buildSystem, buildFile, wrapperName }) => { - const rootDir = projectType === 'application' ? 'apps': 'libs'; + const rootDir = projectType === 'application' ? 'apps' : 'libs'; const downloadUrl = `${options.springInitializerUrl}/starter.zip?type=${buildSystem}&name=${options.name}`; tree.write(`/${rootDir}/${options.name}/${buildFile}`, ''); - const zipFiles = [`${buildFile}`, `${wrapperName}`, 'README.md', ]; + const zipFiles = [`${buildFile}`, `${wrapperName}`, 'README.md',]; const starterZip = mockZipEntries(zipFiles); // mock the zip content returned by the real call to Spring Initializer jest.spyOn(mockedResponse.body, 'pipe').mockReturnValue(syncToAsyncIterable(starterZip)); - await projectGenerator(tree, { ...options, projectType, buildSystem}); + await projectGenerator(tree, { ...options, projectType, buildSystem }); expect(mockedFetch).toHaveBeenCalledWith( downloadUrl, @@ -80,16 +80,35 @@ describe('project generator', () => { expect(fs.chmodSync).toHaveBeenCalledWith(expect.stringContaining(path.normalize(`${rootDir}/${options.name}/${wrapperName}`)), 0o755); if (buildSystem === 'gradle-project') { - expect(logger.debug).toHaveBeenCalledWith(`Adding 'buildInfo' task to the build.gradle file...`); + + if (projectType === 'library') { + expect(logger.debug).toHaveBeenCalledWith(`Disabling 'bootJar' task on a library project...`); + } else { + expect(logger.debug).toHaveBeenCalledWith(`Adding 'buildInfo' task to the build.gradle file...`); + } + } + + if (buildSystem === 'maven-project' && projectType === 'library') { + expect(logger.debug).toHaveBeenCalledWith(`Removing 'spring-boot' maven plugin on a library project...`); } }); - it('should update workspace.json', async () => { - await projectGenerator(tree, options); + it.each` + projectType | subDir + ${'application'} | ${'apps'} + ${'library'} | ${'libs'} + `(`should update workspace.json for '$projectType'`, async ({ projectType, subDir }) => { + await projectGenerator(tree, { ...options, projectType }); const project = readProjectConfiguration(tree, options.name); - expect(project.root).toBe(`apps/${options.name}`); + expect(project.root).toBe(`${subDir}/${options.name}`); + + const commands = ['test', 'clean'] + const bootOnlyCommands = ['run', 'serve', 'buildJar', 'buildWar', 'buildImage', 'buildInfo']; + + if (projectType === 'application') { + commands.push(...bootOnlyCommands); + } - const commands = ['run', 'serve', 'test', 'clean', 'buildJar', 'buildWar', 'buildImage', 'buildInfo']; commands.forEach(cmd => { expect(project.targets[cmd].executor).toBe(`@nxrocks/nx-spring-boot:${cmd}`); }); diff --git a/packages/nx-spring-boot/src/generators/project/generator.ts b/packages/nx-spring-boot/src/generators/project/generator.ts index 93ffcdb4..3c1cc43d 100644 --- a/packages/nx-spring-boot/src/generators/project/generator.ts +++ b/packages/nx-spring-boot/src/generators/project/generator.ts @@ -1,13 +1,19 @@ import { Tree, addProjectConfiguration, } from '@nrwl/devkit'; import { ProjectGeneratorOptions } from './schema'; -import { normalizeOptions, generateBootProject, addBuilInfoTask, addPluginToNxJson } from './lib'; +import { normalizeOptions, generateBootProject, addBuilInfoTask, addPluginToNxJson, disableBootJarTask, removeBootMavenPlugin } from './lib'; export async function projectGenerator(tree: Tree, options: ProjectGeneratorOptions) { - const normalizedOptions = normalizeOptions(tree,options); + const normalizedOptions = normalizeOptions(tree, options); const targets = {}; - const commands = ['run', 'serve', 'test', 'clean', 'buildJar', 'buildWar', 'buildImage', 'buildInfo']; + const commands = ['test', 'clean']; + const bootOnlyCommands = ['run', 'serve', 'buildJar', 'buildWar', 'buildImage', 'buildInfo']; + + if (options.projectType === 'application') { //only 'application' projects should have 'boot' related commands + commands.push(...bootOnlyCommands); + } + for (const command of commands) { targets[command] = { executor: `@nxrocks/nx-spring-boot:${command}`, @@ -26,6 +32,10 @@ export async function projectGenerator(tree: Tree, options: ProjectGeneratorOpti await generateBootProject(tree, normalizedOptions); addBuilInfoTask(tree, normalizedOptions); + + disableBootJarTask(tree, normalizedOptions); + removeBootMavenPlugin(tree, normalizedOptions); + addPluginToNxJson(tree); } diff --git a/packages/nx-spring-boot/src/generators/project/lib/add-build-info-task.ts b/packages/nx-spring-boot/src/generators/project/lib/add-build-info-task.ts index 08103a16..3a3af58a 100644 --- a/packages/nx-spring-boot/src/generators/project/lib/add-build-info-task.ts +++ b/packages/nx-spring-boot/src/generators/project/lib/add-build-info-task.ts @@ -5,7 +5,7 @@ import { import { NormalizedSchema } from '../schema'; export function addBuilInfoTask(tree: Tree, options: NormalizedSchema) { - if (options.buildSystem === 'gradle-project') { + if (options.projectType === 'application' && options.buildSystem === 'gradle-project') { logger.debug(`Adding 'buildInfo' task to the build.gradle file...`); const buildInfoTask = ` diff --git a/packages/nx-spring-boot/src/generators/project/lib/disable-boot-jar-task.ts b/packages/nx-spring-boot/src/generators/project/lib/disable-boot-jar-task.ts new file mode 100644 index 00000000..c0b7264f --- /dev/null +++ b/packages/nx-spring-boot/src/generators/project/lib/disable-boot-jar-task.ts @@ -0,0 +1,29 @@ +import { + logger, + Tree +} from '@nrwl/devkit'; +import { NormalizedSchema } from '../schema'; + +export function disableBootJarTask(tree: Tree, options: NormalizedSchema) { + if (options.projectType === 'library' && options.buildSystem === 'gradle-project') { + logger.debug(`Disabling 'bootJar' task on a library project...`); + + const bootJarDisabledTask = ` +// spring boot library projects don't need an executable jar, so we disable it +bootJar { + enabled = false +} +`; + const jarEnabledTask = ` +jar { + enabled = true +} +`; + const ext = options.language === 'kotlin' ? '.kts' : '' + const buildGradlePath = `${options.projectRoot}/build.gradle${ext}`; + let content = tree.read(buildGradlePath).toString(); + + content += bootJarDisabledTask + '\n' + jarEnabledTask; + tree.write(buildGradlePath, content); + } +} diff --git a/packages/nx-spring-boot/src/generators/project/lib/index.ts b/packages/nx-spring-boot/src/generators/project/lib/index.ts index 13906a7a..423a67b4 100644 --- a/packages/nx-spring-boot/src/generators/project/lib/index.ts +++ b/packages/nx-spring-boot/src/generators/project/lib/index.ts @@ -1,4 +1,6 @@ export { generateBootProject } from './generate-boot-project'; export { addPluginToNxJson } from './add-plugin-to-nx-json' export { addBuilInfoTask } from './add-build-info-task'; + export { disableBootJarTask } from './disable-boot-jar-task'; + export { removeBootMavenPlugin } from './remove-spring-boot-plugin'; export { normalizeOptions } from './normalize-options'; \ No newline at end of file diff --git a/packages/nx-spring-boot/src/generators/project/lib/remove-spring-boot-plugin.ts b/packages/nx-spring-boot/src/generators/project/lib/remove-spring-boot-plugin.ts new file mode 100644 index 00000000..0e121df3 --- /dev/null +++ b/packages/nx-spring-boot/src/generators/project/lib/remove-spring-boot-plugin.ts @@ -0,0 +1,27 @@ +import { + logger, + Tree +} from '@nrwl/devkit'; +import { NormalizedSchema } from '../schema'; + +export function removeBootMavenPlugin(tree: Tree, options: NormalizedSchema) { + if (options.projectType === 'library' && options.buildSystem === 'maven-project') { + logger.debug(`Removing 'spring-boot' maven plugin on a library project...`); + + const mvnPlugin = ` + + + + org.springframework.boot + spring-boot-maven-plugin + + + +`; + const pomXmlPath = `${options.projectRoot}/pom.xml`; + let content = tree.read(pomXmlPath).toString(); + + content = content.replace(mvnPlugin, ''); + tree.write(pomXmlPath, content); + } +}