feat(gradle): add gradle init generator (#22245)
This commit is contained in:
parent
45e1d78a21
commit
6d83dd7ff0
@ -8,16 +8,17 @@ import {
|
|||||||
runCommand,
|
runCommand,
|
||||||
uniq,
|
uniq,
|
||||||
updateFile,
|
updateFile,
|
||||||
updateJson,
|
|
||||||
} from '@nx/e2e/utils';
|
} from '@nx/e2e/utils';
|
||||||
import { execSync } from 'child_process';
|
import { execSync } from 'child_process';
|
||||||
|
|
||||||
describe('Gradle', () => {
|
describe('Gradle', () => {
|
||||||
|
describe.each([{ type: 'kotlin' }, { type: 'groovy' }])(
|
||||||
|
'$type',
|
||||||
|
({ type }: { type: 'kotlin' | 'groovy' }) => {
|
||||||
let gradleProjectName = uniq('my-gradle-project');
|
let gradleProjectName = uniq('my-gradle-project');
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
newProject();
|
newProject();
|
||||||
createGradleProject(gradleProjectName);
|
createGradleProject(gradleProjectName, type);
|
||||||
});
|
});
|
||||||
afterAll(() => cleanupProject());
|
afterAll(() => cleanupProject());
|
||||||
|
|
||||||
@ -41,47 +42,55 @@ describe('Gradle', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should track dependencies for new app', () => {
|
it('should track dependencies for new app', () => {
|
||||||
|
if (type === 'groovy') {
|
||||||
createFile(
|
createFile(
|
||||||
'app2/build.gradle.kts',
|
`app2/build.gradle`,
|
||||||
`
|
`plugins {
|
||||||
plugins {
|
id 'gradleProject.groovy-application-conventions'
|
||||||
id("gradleProject.kotlin-application-conventions")
|
}
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":app"))
|
implementation project(':app')
|
||||||
}
|
}`
|
||||||
`
|
|
||||||
);
|
);
|
||||||
updateFile(`settings.gradle.kts`, (content) => {
|
} else {
|
||||||
|
createFile(
|
||||||
|
`app2/build.gradle.kts`,
|
||||||
|
`plugins {
|
||||||
|
id("gradleProject.kotlin-library-conventions")
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(project(":app"))
|
||||||
|
}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
updateFile(
|
||||||
|
`settings.gradle${type === 'kotlin' ? '.kts' : ''}`,
|
||||||
|
(content) => {
|
||||||
content += `\r\ninclude("app2")`;
|
content += `\r\ninclude("app2")`;
|
||||||
return content;
|
return content;
|
||||||
});
|
}
|
||||||
|
);
|
||||||
const buildOutput = runCLI('build app2', { verbose: true });
|
const buildOutput = runCLI('build app2', { verbose: true });
|
||||||
// app2 depends on app
|
// app2 depends on app
|
||||||
expect(buildOutput).toContain('nx run app:build');
|
expect(buildOutput).toContain('nx run app:build');
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
function createGradleProject(projectName: string) {
|
function createGradleProject(
|
||||||
|
projectName: string,
|
||||||
|
type: 'kotlin' | 'groovy' = 'kotlin'
|
||||||
|
) {
|
||||||
e2eConsoleLogger(`Using java version: ${execSync('java --version')}`);
|
e2eConsoleLogger(`Using java version: ${execSync('java --version')}`);
|
||||||
e2eConsoleLogger(`Using gradle version: ${execSync('gradle --version')}`);
|
e2eConsoleLogger(`Using gradle version: ${execSync('gradle --version')}`);
|
||||||
e2eConsoleLogger(execSync(`gradle help --task :init`).toString());
|
e2eConsoleLogger(execSync(`gradle help --task :init`).toString());
|
||||||
e2eConsoleLogger(
|
e2eConsoleLogger(
|
||||||
runCommand(
|
runCommand(
|
||||||
`gradle init --type kotlin-application --dsl kotlin --project-name ${projectName} --package gradleProject --no-incubating --split-project`
|
`gradle init --type ${type}-application --dsl ${type} --project-name ${projectName} --package gradleProject --no-incubating --split-project`
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
updateJson('nx.json', (nxJson) => {
|
runCLI(`add @nx/gradle`);
|
||||||
nxJson.plugins = ['@nx/gradle'];
|
|
||||||
return nxJson;
|
|
||||||
});
|
|
||||||
createFile(
|
|
||||||
'build.gradle.kts',
|
|
||||||
`allprojects {
|
|
||||||
apply {
|
|
||||||
plugin("project-report")
|
|
||||||
}
|
|
||||||
}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
11
packages/gradle/generators.json
Normal file
11
packages/gradle/generators.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "Nx Gradle",
|
||||||
|
"version": "0.1",
|
||||||
|
"generators": {
|
||||||
|
"init": {
|
||||||
|
"factory": "./src/generators/init/init#initGenerator",
|
||||||
|
"schema": "./src/generators/init/schema.json",
|
||||||
|
"description": "Initializes a Gradle project in the current workspace"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1 +1,2 @@
|
|||||||
export * from './plugin';
|
export * from './plugin';
|
||||||
|
export { initGenerator } from './src/generators/init/init';
|
||||||
|
|||||||
@ -22,6 +22,14 @@
|
|||||||
"url": "https://github.com/nrwl/nx/issues"
|
"url": "https://github.com/nrwl/nx/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://nx.dev",
|
"homepage": "https://nx.dev",
|
||||||
|
"generators": "./generators.json",
|
||||||
|
"exports": {
|
||||||
|
".": "./index.js",
|
||||||
|
"./package.json": "./package.json",
|
||||||
|
"./migrations.json": "./migrations.json",
|
||||||
|
"./generators.json": "./generators.json",
|
||||||
|
"./plugin": "./plugin.js"
|
||||||
|
},
|
||||||
"nx-migrate": {
|
"nx-migrate": {
|
||||||
"migrations": "./migrations.json"
|
"migrations": "./migrations.json"
|
||||||
},
|
},
|
||||||
|
|||||||
73
packages/gradle/src/generators/init/init.spec.ts
Normal file
73
packages/gradle/src/generators/init/init.spec.ts
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import { readNxJson, Tree, updateNxJson } from '@nx/devkit';
|
||||||
|
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||||
|
|
||||||
|
import { initGenerator } from './init';
|
||||||
|
|
||||||
|
describe('@nx/gradle:init', () => {
|
||||||
|
let tree: Tree;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
tree = createTreeWithEmptyWorkspace();
|
||||||
|
tree.write('settings.gradle', '');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add the plugin', async () => {
|
||||||
|
await initGenerator(tree, {
|
||||||
|
skipFormat: true,
|
||||||
|
skipPackageJson: false,
|
||||||
|
});
|
||||||
|
const nxJson = readNxJson(tree);
|
||||||
|
expect(nxJson.plugins).toMatchInlineSnapshot(`
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"options": {
|
||||||
|
"buildTargetName": "build",
|
||||||
|
"classesTargetName": "classes",
|
||||||
|
"testTargetName": "test",
|
||||||
|
},
|
||||||
|
"plugin": "@nx/gradle/plugin",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not overwrite existing plugins', async () => {
|
||||||
|
updateNxJson(tree, {
|
||||||
|
plugins: ['foo'],
|
||||||
|
});
|
||||||
|
await initGenerator(tree, {
|
||||||
|
skipFormat: true,
|
||||||
|
skipPackageJson: false,
|
||||||
|
});
|
||||||
|
const nxJson = readNxJson(tree);
|
||||||
|
expect(nxJson.plugins).toMatchInlineSnapshot(`
|
||||||
|
[
|
||||||
|
"foo",
|
||||||
|
{
|
||||||
|
"options": {
|
||||||
|
"buildTargetName": "build",
|
||||||
|
"classesTargetName": "classes",
|
||||||
|
"testTargetName": "test",
|
||||||
|
},
|
||||||
|
"plugin": "@nx/gradle/plugin",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not add plugin if already in array', async () => {
|
||||||
|
updateNxJson(tree, {
|
||||||
|
plugins: ['@nx/gradle/plugin'],
|
||||||
|
});
|
||||||
|
await initGenerator(tree, {
|
||||||
|
skipFormat: true,
|
||||||
|
skipPackageJson: false,
|
||||||
|
});
|
||||||
|
const nxJson = readNxJson(tree);
|
||||||
|
expect(nxJson.plugins).toMatchInlineSnapshot(`
|
||||||
|
[
|
||||||
|
"@nx/gradle/plugin",
|
||||||
|
]
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
102
packages/gradle/src/generators/init/init.ts
Normal file
102
packages/gradle/src/generators/init/init.ts
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
import {
|
||||||
|
addDependenciesToPackageJson,
|
||||||
|
formatFiles,
|
||||||
|
GeneratorCallback,
|
||||||
|
logger,
|
||||||
|
readNxJson,
|
||||||
|
runTasksInSerial,
|
||||||
|
Tree,
|
||||||
|
updateNxJson,
|
||||||
|
} from '@nx/devkit';
|
||||||
|
import { updatePackageScripts } from '@nx/devkit/src/utils/update-package-scripts';
|
||||||
|
import { createNodes } from '../../plugin/nodes';
|
||||||
|
import { nxVersion } from '../../utils/versions';
|
||||||
|
import { InitGeneratorSchema } from './schema';
|
||||||
|
import { hasGradlePlugin } from '../../utils/has-gradle-plugin';
|
||||||
|
|
||||||
|
export async function initGenerator(tree: Tree, options: InitGeneratorSchema) {
|
||||||
|
const tasks: GeneratorCallback[] = [];
|
||||||
|
|
||||||
|
if (!options.skipPackageJson && tree.exists('package.json')) {
|
||||||
|
tasks.push(
|
||||||
|
addDependenciesToPackageJson(
|
||||||
|
tree,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
'@nx/gradle': nxVersion,
|
||||||
|
},
|
||||||
|
undefined,
|
||||||
|
options.keepExistingVersions
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
addPlugin(tree);
|
||||||
|
addProjectReportToBuildGradle(tree);
|
||||||
|
|
||||||
|
if (options.updatePackageScripts && tree.exists('package.json')) {
|
||||||
|
await updatePackageScripts(tree, createNodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options.skipFormat) {
|
||||||
|
await formatFiles(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
return runTasksInSerial(...tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addPlugin(tree: Tree) {
|
||||||
|
const nxJson = readNxJson(tree);
|
||||||
|
|
||||||
|
if (!hasGradlePlugin(tree)) {
|
||||||
|
nxJson.plugins ??= [];
|
||||||
|
nxJson.plugins.push({
|
||||||
|
plugin: '@nx/gradle/plugin',
|
||||||
|
options: {
|
||||||
|
testTargetName: 'test',
|
||||||
|
classesTargetName: 'classes',
|
||||||
|
buildTargetName: 'build',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
updateNxJson(tree, nxJson);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function adds the project-report plugin to the build.gradle or build.gradle.kts file
|
||||||
|
*/
|
||||||
|
function addProjectReportToBuildGradle(tree: Tree) {
|
||||||
|
let buildGradleFile: string;
|
||||||
|
if (tree.exists('settings.gradle.kts')) {
|
||||||
|
buildGradleFile = 'build.gradle.kts';
|
||||||
|
} else if (tree.exists('settings.gradle')) {
|
||||||
|
buildGradleFile = 'build.gradle';
|
||||||
|
} else {
|
||||||
|
throw new Error(
|
||||||
|
'Could not find settings.gradle or settings.gradle.kts file in your gradle workspace.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let buildGradleContent = '';
|
||||||
|
if (tree.exists(buildGradleFile)) {
|
||||||
|
buildGradleContent = tree.read(buildGradleFile).toString();
|
||||||
|
}
|
||||||
|
if (buildGradleContent.includes('allprojects')) {
|
||||||
|
if (!buildGradleContent.includes('"project-report')) {
|
||||||
|
logger.warn(`Please add the project-report plugin to your ${buildGradleFile}:
|
||||||
|
allprojects {
|
||||||
|
apply {
|
||||||
|
plugin("project-report")
|
||||||
|
}
|
||||||
|
}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
buildGradleContent += `\n\rallprojects {
|
||||||
|
apply {
|
||||||
|
plugin("project-report")
|
||||||
|
}
|
||||||
|
}`;
|
||||||
|
tree.write(buildGradleFile, buildGradleContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default initGenerator;
|
||||||
6
packages/gradle/src/generators/init/schema.d.ts
vendored
Normal file
6
packages/gradle/src/generators/init/schema.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export interface InitGeneratorSchema {
|
||||||
|
skipFormat?: boolean;
|
||||||
|
skipPackageJson?: boolean;
|
||||||
|
keepExistingVersions?: boolean;
|
||||||
|
updatePackageScripts?: boolean;
|
||||||
|
}
|
||||||
34
packages/gradle/src/generators/init/schema.json
Normal file
34
packages/gradle/src/generators/init/schema.json
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json-schema.org/schema",
|
||||||
|
"$id": "NxGradleInitSchema",
|
||||||
|
"title": "Gradle Init Generator",
|
||||||
|
"description": "Initializes a Gradle project in the current workspace.",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"skipFormat": {
|
||||||
|
"description": "Skip formatting files.",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"x-priority": "internal"
|
||||||
|
},
|
||||||
|
"skipPackageJson": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"description": "Do not add dependencies to `package.json`.",
|
||||||
|
"x-priority": "internal"
|
||||||
|
},
|
||||||
|
"keepExistingVersions": {
|
||||||
|
"type": "boolean",
|
||||||
|
"x-priority": "internal",
|
||||||
|
"description": "Keep existing dependencies versions",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"updatePackageScripts": {
|
||||||
|
"type": "boolean",
|
||||||
|
"x-priority": "internal",
|
||||||
|
"description": "Update `package.json` scripts with inferred targets",
|
||||||
|
"default": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": []
|
||||||
|
}
|
||||||
@ -39,6 +39,7 @@ export const createDependencies: CreateDependencies = async (
|
|||||||
|
|
||||||
if (projectName && depsFile) {
|
if (projectName && depsFile) {
|
||||||
dependencies = dependencies.concat(
|
dependencies = dependencies.concat(
|
||||||
|
Array.from(
|
||||||
processGradleDependencies(
|
processGradleDependencies(
|
||||||
depsFile,
|
depsFile,
|
||||||
gradleProjectToProjectName,
|
gradleProjectToProjectName,
|
||||||
@ -46,6 +47,7 @@ export const createDependencies: CreateDependencies = async (
|
|||||||
gradleFile,
|
gradleFile,
|
||||||
context
|
context
|
||||||
)
|
)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -85,12 +87,15 @@ function processGradleDependencies(
|
|||||||
sourceProjectName: string,
|
sourceProjectName: string,
|
||||||
gradleFile: string,
|
gradleFile: string,
|
||||||
context: CreateDependenciesContext
|
context: CreateDependenciesContext
|
||||||
) {
|
): Set<RawProjectGraphDependency> {
|
||||||
const dependencies: RawProjectGraphDependency[] = [];
|
const dependencies: Set<RawProjectGraphDependency> = new Set();
|
||||||
const lines = readFileSync(depsFile).toString().split('\n');
|
const lines = readFileSync(depsFile).toString().split('\n');
|
||||||
let inDeps = false;
|
let inDeps = false;
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
if (line.startsWith('implementationDependenciesMetadata')) {
|
if (
|
||||||
|
line.startsWith('implementationDependenciesMetadata') ||
|
||||||
|
line.startsWith('compileClasspath')
|
||||||
|
) {
|
||||||
inDeps = true;
|
inDeps = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -116,7 +121,7 @@ function processGradleDependencies(
|
|||||||
sourceFile: gradleFile,
|
sourceFile: gradleFile,
|
||||||
};
|
};
|
||||||
validateDependency(dependency, context);
|
validateDependency(dependency, context);
|
||||||
dependencies.push(dependency);
|
dependencies.add(dependency);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
10
packages/gradle/src/utils/has-gradle-plugin.ts
Normal file
10
packages/gradle/src/utils/has-gradle-plugin.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { readNxJson, Tree } from '@nx/devkit';
|
||||||
|
|
||||||
|
export function hasGradlePlugin(tree: Tree): boolean {
|
||||||
|
const nxJson = readNxJson(tree);
|
||||||
|
return !!nxJson.plugins?.some((p) =>
|
||||||
|
typeof p === 'string'
|
||||||
|
? p === '@nx/gradle/plugin'
|
||||||
|
: p.plugin === '@nx/gradle/plugin'
|
||||||
|
);
|
||||||
|
}
|
||||||
1
packages/gradle/src/utils/versions.ts
Normal file
1
packages/gradle/src/utils/versions.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export const nxVersion = require('../../package.json').version;
|
||||||
@ -63,7 +63,7 @@ async function installPackage(pkgName: string, version: string): Promise<void> {
|
|||||||
writeJsonFile('nx.json', nxJson);
|
writeJsonFile('nx.json', nxJson);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await runNxAsync('');
|
await runNxAsync('--help');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// revert adding the plugin to nx.json
|
// revert adding the plugin to nx.json
|
||||||
nxJson.installation.plugins[pkgName] = undefined;
|
nxJson.installation.plugins[pkgName] = undefined;
|
||||||
|
|||||||
@ -49,6 +49,13 @@ export async function initHandler(options: InitArgs): Promise<void> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
generateDotNxSetup(version);
|
generateDotNxSetup(version);
|
||||||
|
const { plugins } = await detectPlugins();
|
||||||
|
plugins.forEach((plugin) => {
|
||||||
|
execSync(`./nx add ${plugin}`, {
|
||||||
|
stdio: 'inherit',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// invokes the wrapper, thus invoking the initial installation process
|
// invokes the wrapper, thus invoking the initial installation process
|
||||||
runNxSync('--version', { stdio: 'ignore' });
|
runNxSync('--version', { stdio: 'ignore' });
|
||||||
return;
|
return;
|
||||||
@ -190,6 +197,9 @@ async function detectPlugins(): Promise<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (existsSync('gradlew') || existsSync('gradlew.bat')) {
|
||||||
|
detectedPlugins.add('@nx/gradle');
|
||||||
|
}
|
||||||
|
|
||||||
const plugins = Array.from(detectedPlugins);
|
const plugins = Array.from(detectedPlugins);
|
||||||
|
|
||||||
@ -214,7 +224,9 @@ async function detectPlugins(): Promise<
|
|||||||
|
|
||||||
if (pluginsToInstall?.length === 0) return undefined;
|
if (pluginsToInstall?.length === 0) return undefined;
|
||||||
|
|
||||||
const updatePackageScripts = await prompt<{ updatePackageScripts: string }>([
|
const updatePackageScripts =
|
||||||
|
existsSync('package.json') &&
|
||||||
|
(await prompt<{ updatePackageScripts: string }>([
|
||||||
{
|
{
|
||||||
name: 'updatePackageScripts',
|
name: 'updatePackageScripts',
|
||||||
type: 'autocomplete',
|
type: 'autocomplete',
|
||||||
@ -229,10 +241,7 @@ async function detectPlugins(): Promise<
|
|||||||
],
|
],
|
||||||
initial: 0,
|
initial: 0,
|
||||||
},
|
},
|
||||||
]).then((r) => r.updatePackageScripts === 'Yes');
|
]).then((r) => r.updatePackageScripts === 'Yes'));
|
||||||
|
|
||||||
return {
|
return { plugins: pluginsToInstall, updatePackageScripts };
|
||||||
plugins: pluginsToInstall,
|
|
||||||
updatePackageScripts,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,6 +29,7 @@ const scopes = [
|
|||||||
{ value: 'vue', name: 'vue: anything Vue specific' },
|
{ value: 'vue', name: 'vue: anything Vue specific' },
|
||||||
{ value: 'web', name: 'web: anything Web specific' },
|
{ value: 'web', name: 'web: anything Web specific' },
|
||||||
{ value: 'webpack', name: 'webpack: anything Webpack specific' },
|
{ value: 'webpack', name: 'webpack: anything Webpack specific' },
|
||||||
|
{ value: 'gradle', name: 'gradle: anything Gradle specific'},
|
||||||
{value: 'module-federation', name: 'module-federation: anything Module Federation specific'},
|
{value: 'module-federation', name: 'module-federation: anything Module Federation specific'},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user