Skip to content

Commit

Permalink
feat(nx-spring-boot): make jar of library projects not executable
Browse files Browse the repository at this point in the history
Moreover, we only add 'boot' related executors to `workspace.json` for `application` projects

Closes #67
  • Loading branch information
tinesoft committed Aug 17, 2021
1 parent 15511ae commit 1e2984f
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 18 deletions.
14 changes: 8 additions & 6 deletions packages/nx-spring-boot/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`<sup>*</sup>| `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`<sup>*</sup> | `ignoreWrapper:boolean`, `args: string[]` | Packages the project into an executable Jar using either `./mvnw\|mvn spring-boot:repackage` or `./gradlew\|gradle bootJar` |
| `buildWar`<sup>*</sup> | `ignoreWrapper:boolean`, `args: string[]` | Packages the project into an executable War using either `./mvnw\|mvn spring-boot:repackage` or `./gradlew\|gradle bootWar` |
| `buildInfo`<sup>*</sup> | `ignoreWrapper:boolean`, | Generates a `build-info.properties` using either `./mvnw\|mvn spring-boot:build-info` or `./gradlew\|gradle bootBuildInfo` |
| `buildImage`<sup>*</sup> | `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` |

> <sup>*</sup> = 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.
Expand Down
35 changes: 27 additions & 8 deletions packages/nx-spring-boot/src/generators/project/generator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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}`);
});
Expand Down
16 changes: 13 additions & 3 deletions packages/nx-spring-boot/src/generators/project/generator.ts
Original file line number Diff line number Diff line change
@@ -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}`,
Expand All @@ -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);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = `
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
2 changes: 2 additions & 0 deletions packages/nx-spring-boot/src/generators/project/lib/index.ts
Original file line number Diff line number Diff line change
@@ -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';
Original file line number Diff line number Diff line change
@@ -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 = `
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
`;
const pomXmlPath = `${options.projectRoot}/pom.xml`;
let content = tree.read(pomXmlPath).toString();

content = content.replace(mvnPlugin, '');
tree.write(pomXmlPath, content);
}
}

0 comments on commit 1e2984f

Please sign in to comment.