diff --git a/packages/node/src/generators/application/application.legacy.spec.ts b/packages/node/src/generators/application/application.legacy.spec.ts index 38616eb0d80f9e..9a56a513d9f22d 100644 --- a/packages/node/src/generators/application/application.legacy.spec.ts +++ b/packages/node/src/generators/application/application.legacy.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { readNxJson, readProjectConfiguration, diff --git a/packages/node/src/generators/e2e-project/e2e-project.spec.ts b/packages/node/src/generators/e2e-project/e2e-project.spec.ts index 8b993a68b03b97..b2b142c03a6a52 100644 --- a/packages/node/src/generators/e2e-project/e2e-project.spec.ts +++ b/packages/node/src/generators/e2e-project/e2e-project.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { Tree } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { applicationGenerator } from '../application/application'; diff --git a/packages/node/src/generators/setup-docker/files/Dockerfile__tmpl__ b/packages/node/src/generators/setup-docker/files/Dockerfile__tmpl__ index f6752d0547dc5d..6203ee33f9f45e 100644 --- a/packages/node/src/generators/setup-docker/files/Dockerfile__tmpl__ +++ b/packages/node/src/generators/setup-docker/files/Dockerfile__tmpl__ @@ -14,7 +14,7 @@ WORKDIR /app RUN addgroup --system <%= project %> && \ adduser --system -G <%= project %> <%= project %> -COPY <%= buildLocation %> <%= project %> +COPY <%= buildLocation %> <%= project %>/ RUN chown -R <%= project %>:<%= project %> . # You can remove this install step if you build with `--bundle` option. diff --git a/packages/node/src/generators/setup-docker/setup-docker.spec.ts b/packages/node/src/generators/setup-docker/setup-docker.spec.ts index aa2e8a0c4dde47..8a1d4e4e853caf 100644 --- a/packages/node/src/generators/setup-docker/setup-docker.spec.ts +++ b/packages/node/src/generators/setup-docker/setup-docker.spec.ts @@ -1,16 +1,35 @@ -import { readProjectConfiguration, Tree } from '@nx/devkit'; +import 'nx/src/internal-testing-utils/mock-project-graph'; + +import { + ProjectConfiguration, + readProjectConfiguration, + Tree, +} from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; -import { applicationGenerator } from '../application/application'; + describe('setupDockerGenerator', () => { let tree: Tree; beforeEach(async () => { tree = createTreeWithEmptyWorkspace(); + + jest.resetModules(); }); describe('integrated', () => { it('should create docker assets when --docker is passed', async () => { + const projectName = 'integreated-api'; + // Since we mock the project graph, we need to mock the project configuration as well + mockReadCachedProjectConfiguration({ + name: projectName, + root: projectName, + }); + + const { applicationGenerator } = await import( + '../application/application' + ); + await applicationGenerator(tree, { - name: 'api', + name: projectName, framework: 'express', e2eTestRunner: 'none', docker: true, @@ -18,14 +37,16 @@ describe('setupDockerGenerator', () => { addPlugin: true, }); - const project = readProjectConfiguration(tree, 'api'); + const project = readProjectConfiguration(tree, projectName); - expect(tree.exists('api/Dockerfile')).toBeTruthy(); + const dockerFile = tree.read(`${projectName}/Dockerfile`, 'utf8'); + expect(tree.exists(`${projectName}/Dockerfile`)).toBeTruthy(); + expect(dockerFile).toContain(`COPY dist/${projectName} ${projectName}/`); expect(project.targets).toEqual( expect.objectContaining({ 'docker-build': { dependsOn: ['build'], - command: 'docker build -f api/Dockerfile . -t api', + command: `docker build -f ${projectName}/Dockerfile . -t ${projectName}`, }, }) ); @@ -34,8 +55,14 @@ describe('setupDockerGenerator', () => { describe('standalone', () => { it('should create docker assets when --docker is passed', async () => { + const projectName = 'standalone-api'; + mockReadCachedProjectConfiguration({ name: projectName, root: '' }); + + const { applicationGenerator } = await import( + '../application/application' + ); await applicationGenerator(tree, { - name: 'api', + name: projectName, framework: 'fastify', rootProject: true, docker: true, @@ -43,16 +70,39 @@ describe('setupDockerGenerator', () => { addPlugin: true, }); - const project = readProjectConfiguration(tree, 'api'); - expect(tree.exists('Dockerfile')).toBeTruthy(); + const project = readProjectConfiguration(tree, projectName); + const dockerFile = tree.read(`Dockerfile`, 'utf8'); + + expect(tree.exists(`Dockerfile`)).toBeTruthy(); + expect(dockerFile).toContain(`COPY dist/${projectName} ${projectName}/`); expect(project.targets).toEqual( expect.objectContaining({ 'docker-build': { dependsOn: ['build'], - command: 'docker build -f Dockerfile . -t api', + command: `docker build -f Dockerfile . -t ${projectName}`, }, }) ); }); }); }); + +const mockReadCachedProjectConfiguration = ( + projectConfig: ProjectConfiguration +) => { + jest.mock('nx/src/project-graph/project-graph', () => { + return { + ...jest.requireActual('nx/src/project-graph/project-graph'), + readCachedProjectConfiguration: jest.fn(() => { + return { + root: projectConfig.root, + targets: { + build: { + outputs: [`dist/${projectConfig.name}`], + }, + }, + }; + }), + }; + }); +}; diff --git a/packages/node/src/generators/setup-docker/setup-docker.ts b/packages/node/src/generators/setup-docker/setup-docker.ts index 5176b71fe9d1d2..582a389cbc8229 100644 --- a/packages/node/src/generators/setup-docker/setup-docker.ts +++ b/packages/node/src/generators/setup-docker/setup-docker.ts @@ -13,6 +13,8 @@ import { import { SetUpDockerOptions } from './schema'; import { join } from 'path'; +import { interpolate } from 'nx/src/tasks-runner/utils'; +import { readCachedProjectConfiguration } from 'nx/src/project-graph/project-graph'; function normalizeOptions( tree: Tree, @@ -27,21 +29,38 @@ function normalizeOptions( } function addDocker(tree: Tree, options: SetUpDockerOptions) { - const project = readProjectConfiguration(tree, options.project); - if (!project || !options.targetName) { + // Inferred targets are only available in the project graph + const projectConfig = readCachedProjectConfiguration(options.project); + + if ( + !projectConfig || + !projectConfig.targets || + !projectConfig.targets[options.buildTarget] + ) { return; } - if (tree.exists(joinPathFragments(project.root, 'DockerFile'))) { + // Returns an string like {workspaceRoot}/dist/apps/{projectName} + const tokenizedOutputPath = + projectConfig.targets[`${options.buildTarget}`]?.outputs?.[0]; + if (tree.exists(joinPathFragments(projectConfig.root, 'DockerFile'))) { logger.info( - `Skipping setup since a Dockerfile already exists inside ${project.root}` + `Skipping setup since a Dockerfile already exists inside ${projectConfig.root}` + ); + } else if (!tokenizedOutputPath) { + logger.error( + `Skipping setup since the output path for the build target ${options.buildTarget} is not defined.` ); } else { - const outputPath = - project.targets[`${options.buildTarget}`]?.options.outputPath; - generateFiles(tree, join(__dirname, './files'), project.root, { + const outputPath = interpolate(tokenizedOutputPath, { + projectName: projectConfig.name, + projectRoot: projectConfig.root, + workspaceRoot: '', + }); + + generateFiles(tree, join(__dirname, './files'), projectConfig.root, { tmpl: '', - app: project.sourceRoot, + app: projectConfig.sourceRoot, buildLocation: outputPath, project: options.project, });