feat(core): add matching support to inputs projects (#17255)
This commit is contained in:
parent
d0421e7aae
commit
471616b04d
@ -1,3 +1,4 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-restricted-imports */
|
/* eslint-disable @typescript-eslint/no-restricted-imports */
|
||||||
export * from 'nx/internal-testing-utils/assert-valid-migrations';
|
export * from 'nx/internal-testing-utils/assert-valid-migrations';
|
||||||
export * from 'nx/internal-testing-utils/run-migration-against-this-workspace';
|
export * from 'nx/internal-testing-utils/run-migration-against-this-workspace';
|
||||||
|
export * from 'nx/internal-testing-utils/with-environment';
|
||||||
|
|||||||
29
packages/nx/internal-testing-utils/with-environment.ts
Normal file
29
packages/nx/internal-testing-utils/with-environment.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
export function withEnvironmentVariables(
|
||||||
|
env: Record<string, string>,
|
||||||
|
callback: () => void
|
||||||
|
): void;
|
||||||
|
export function withEnvironmentVariables(
|
||||||
|
env: Record<string, string>,
|
||||||
|
callback: () => Promise<void>
|
||||||
|
): Promise<void>;
|
||||||
|
export function withEnvironmentVariables(
|
||||||
|
env: Record<string, string>,
|
||||||
|
callback: () => void | Promise<void>
|
||||||
|
): void | Promise<void> {
|
||||||
|
const originalValues: Record<string, string> = {};
|
||||||
|
for (const key in env) {
|
||||||
|
originalValues[key] = process.env[key];
|
||||||
|
process.env[key] = env[key];
|
||||||
|
}
|
||||||
|
const cleanup = () => {
|
||||||
|
for (const key in env) {
|
||||||
|
process.env[key] = originalValues[key];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const p = callback();
|
||||||
|
if (p instanceof Promise) {
|
||||||
|
return p.finally(cleanup);
|
||||||
|
} else {
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -28,6 +28,7 @@ import {
|
|||||||
InProcessTaskHasher,
|
InProcessTaskHasher,
|
||||||
} from './task-hasher';
|
} from './task-hasher';
|
||||||
import { fileHasher } from './impl';
|
import { fileHasher } from './impl';
|
||||||
|
import { withEnvironmentVariables } from '../../internal-testing-utils/with-environment';
|
||||||
|
|
||||||
describe('TaskHasher', () => {
|
describe('TaskHasher', () => {
|
||||||
const packageJson = {
|
const packageJson = {
|
||||||
@ -76,88 +77,104 @@ describe('TaskHasher', () => {
|
|||||||
vol.reset();
|
vol.reset();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create task hash', async () => {
|
it('should create task hash', () =>
|
||||||
process.env.TESTENV = 'env123';
|
withEnvironmentVariables({ TESTENV: 'env123' }, async () => {
|
||||||
const hasher = new InProcessTaskHasher(
|
const hasher = new InProcessTaskHasher(
|
||||||
{
|
{
|
||||||
parent: [{ file: '/file', hash: 'file.hash' }],
|
parent: [{ file: '/file', hash: 'file.hash' }],
|
||||||
unrelated: [{ file: 'libs/unrelated/filec.ts', hash: 'filec.hash' }],
|
unrelated: [{ file: 'libs/unrelated/filec.ts', hash: 'filec.hash' }],
|
||||||
},
|
},
|
||||||
allWorkspaceFiles,
|
allWorkspaceFiles,
|
||||||
{
|
{
|
||||||
nodes: {
|
nodes: {
|
||||||
parent: {
|
parent: {
|
||||||
name: 'parent',
|
name: 'parent',
|
||||||
type: 'lib',
|
type: 'lib',
|
||||||
data: {
|
data: {
|
||||||
root: 'libs/parent',
|
root: 'libs/parent',
|
||||||
targets: {
|
targets: {
|
||||||
build: {
|
build: {
|
||||||
executor: 'nx:run-commands',
|
executor: 'nx:run-commands',
|
||||||
inputs: [
|
inputs: [
|
||||||
'default',
|
'default',
|
||||||
'^default',
|
'^default',
|
||||||
{ runtime: 'echo runtime123' },
|
{ runtime: 'echo runtime123' },
|
||||||
{ env: 'TESTENV' },
|
{ env: 'TESTENV' },
|
||||||
{ env: 'NONEXISTENTENV' },
|
{ env: 'NONEXISTENTENV' },
|
||||||
{ input: 'default', projects: ['unrelated'] },
|
{
|
||||||
],
|
input: 'default',
|
||||||
|
projects: ['unrelated', 'tag:some-tag'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
unrelated: {
|
||||||
unrelated: {
|
name: 'unrelated',
|
||||||
name: 'unrelated',
|
type: 'lib',
|
||||||
type: 'lib',
|
data: {
|
||||||
data: {
|
root: 'libs/unrelated',
|
||||||
root: 'libs/unrelated',
|
targets: { build: {} },
|
||||||
targets: { build: {} },
|
},
|
||||||
|
},
|
||||||
|
tagged: {
|
||||||
|
name: 'tagged',
|
||||||
|
type: 'lib',
|
||||||
|
data: {
|
||||||
|
root: 'libs/tagged',
|
||||||
|
targets: { build: {} },
|
||||||
|
tags: ['some-tag'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
dependencies: {
|
||||||
|
parent: [],
|
||||||
|
},
|
||||||
|
externalNodes: {},
|
||||||
},
|
},
|
||||||
dependencies: {
|
{} as any,
|
||||||
parent: [],
|
{
|
||||||
|
runtimeCacheInputs: ['echo runtime456'],
|
||||||
},
|
},
|
||||||
externalNodes: {},
|
createFileHasher()
|
||||||
},
|
);
|
||||||
{} as any,
|
|
||||||
{
|
|
||||||
runtimeCacheInputs: ['echo runtime456'],
|
|
||||||
},
|
|
||||||
createFileHasher()
|
|
||||||
);
|
|
||||||
|
|
||||||
const hash = await hasher.hashTask({
|
const hash = await hasher.hashTask({
|
||||||
target: { project: 'parent', target: 'build' },
|
target: { project: 'parent', target: 'build' },
|
||||||
id: 'parent-build',
|
id: 'parent-build',
|
||||||
overrides: { prop: 'prop-value' },
|
overrides: { prop: 'prop-value' },
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(hash.value).toContain('file.hash'); //project files
|
expect(hash.value).toContain('file.hash'); //project files
|
||||||
expect(hash.value).toContain('prop-value'); //overrides
|
expect(hash.value).toContain('prop-value'); //overrides
|
||||||
expect(hash.value).toContain('parent'); //project
|
expect(hash.value).toContain('parent'); //project
|
||||||
expect(hash.value).toContain('build'); //target
|
expect(hash.value).toContain('build'); //target
|
||||||
expect(hash.value).toContain('runtime123');
|
expect(hash.value).toContain('runtime123');
|
||||||
expect(hash.value).toContain('runtime456');
|
expect(hash.value).toContain('runtime456');
|
||||||
expect(hash.value).toContain('env123');
|
expect(hash.value).toContain('env123');
|
||||||
expect(hash.value).toContain('filec.hash');
|
expect(hash.value).toContain('filec.hash');
|
||||||
|
|
||||||
expect(hash.details.command).toEqual('parent|build||{"prop":"prop-value"}');
|
expect(hash.details.command).toEqual(
|
||||||
expect(hash.details.nodes).toEqual({
|
'parent|build||{"prop":"prop-value"}'
|
||||||
'parent:{projectRoot}/**/*':
|
);
|
||||||
'/file|file.hash|{"root":"libs/parent","targets":{"build":{"executor":"nx:run-commands","inputs":["default","^default",{"runtime":"echo runtime123"},{"env":"TESTENV"},{"env":"NONEXISTENTENV"},{"input":"default","projects":["unrelated"]}]}}}|{"compilerOptions":{"paths":{"@nx/parent":["libs/parent/src/index.ts"],"@nx/child":["libs/child/src/index.ts"]}}}',
|
expect(hash.details.nodes).toEqual({
|
||||||
target: 'nx:run-commands',
|
'parent:{projectRoot}/**/*':
|
||||||
'unrelated:{projectRoot}/**/*':
|
'/file|file.hash|{"root":"libs/parent","targets":{"build":{"executor":"nx:run-commands","inputs":["default","^default",{"runtime":"echo runtime123"},{"env":"TESTENV"},{"env":"NONEXISTENTENV"},{"input":"default","projects":["unrelated","tag:some-tag"]}]}}}|{"compilerOptions":{"paths":{"@nx/parent":["libs/parent/src/index.ts"],"@nx/child":["libs/child/src/index.ts"]}}}',
|
||||||
'libs/unrelated/filec.ts|filec.hash|{"root":"libs/unrelated","targets":{"build":{}}}|{"compilerOptions":{"paths":{"@nx/parent":["libs/parent/src/index.ts"],"@nx/child":["libs/child/src/index.ts"]}}}',
|
target: 'nx:run-commands',
|
||||||
'{workspaceRoot}/nx.json': 'nx.json.hash',
|
'unrelated:{projectRoot}/**/*':
|
||||||
'{workspaceRoot}/.gitignore': '',
|
'libs/unrelated/filec.ts|filec.hash|{"root":"libs/unrelated","targets":{"build":{}}}|{"compilerOptions":{"paths":{"@nx/parent":["libs/parent/src/index.ts"],"@nx/child":["libs/child/src/index.ts"]}}}',
|
||||||
'{workspaceRoot}/.nxignore': '',
|
'tagged:{projectRoot}/**/*':
|
||||||
'runtime:echo runtime123': 'runtime123',
|
'{"root":"libs/tagged","targets":{"build":{}},"tags":["some-tag"]}|{"compilerOptions":{"paths":{"@nx/parent":["libs/parent/src/index.ts"],"@nx/child":["libs/child/src/index.ts"]}}}',
|
||||||
'runtime:echo runtime456': 'runtime456',
|
'{workspaceRoot}/nx.json': 'nx.json.hash',
|
||||||
'env:TESTENV': 'env123',
|
'{workspaceRoot}/.gitignore': '',
|
||||||
'env:NONEXISTENTENV': '',
|
'{workspaceRoot}/.nxignore': '',
|
||||||
});
|
'runtime:echo runtime123': 'runtime123',
|
||||||
});
|
'runtime:echo runtime456': 'runtime456',
|
||||||
|
'env:TESTENV': 'env123',
|
||||||
|
'env:NONEXISTENTENV': '',
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
it('should hash task where the project has dependencies', async () => {
|
it('should hash task where the project has dependencies', async () => {
|
||||||
const hasher = new InProcessTaskHasher(
|
const hasher = new InProcessTaskHasher(
|
||||||
@ -359,111 +376,117 @@ describe('TaskHasher', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to handle multiple filesets per project', async () => {
|
it('should be able to handle multiple filesets per project', async () => {
|
||||||
process.env.MY_TEST_HASH_ENV = 'MY_TEST_HASH_ENV_VALUE';
|
withEnvironmentVariables(
|
||||||
const hasher = new InProcessTaskHasher(
|
{ MY_TEST_HASH_ENV: 'MY_TEST_HASH_ENV_VALUE' },
|
||||||
{
|
async () => {
|
||||||
parent: [
|
const hasher = new InProcessTaskHasher(
|
||||||
{ file: 'libs/parent/filea.ts', hash: 'a.hash' },
|
{
|
||||||
{ file: 'libs/parent/filea.spec.ts', hash: 'a.spec.hash' },
|
parent: [
|
||||||
],
|
{ file: 'libs/parent/filea.ts', hash: 'a.hash' },
|
||||||
child: [
|
{ file: 'libs/parent/filea.spec.ts', hash: 'a.spec.hash' },
|
||||||
{ file: 'libs/child/fileb.ts', hash: 'b.hash' },
|
],
|
||||||
{ file: 'libs/child/fileb.spec.ts', hash: 'b.spec.hash' },
|
child: [
|
||||||
],
|
{ file: 'libs/child/fileb.ts', hash: 'b.hash' },
|
||||||
},
|
{ file: 'libs/child/fileb.spec.ts', hash: 'b.spec.hash' },
|
||||||
allWorkspaceFiles,
|
],
|
||||||
{
|
},
|
||||||
nodes: {
|
allWorkspaceFiles,
|
||||||
parent: {
|
{
|
||||||
name: 'parent',
|
nodes: {
|
||||||
type: 'lib',
|
parent: {
|
||||||
data: {
|
name: 'parent',
|
||||||
root: 'libs/parent',
|
type: 'lib',
|
||||||
targets: {
|
data: {
|
||||||
test: {
|
root: 'libs/parent',
|
||||||
inputs: ['default', '^prod'],
|
targets: {
|
||||||
executor: 'nx:run-commands',
|
test: {
|
||||||
|
inputs: ['default', '^prod'],
|
||||||
|
executor: 'nx:run-commands',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
child: {
|
||||||
|
name: 'child',
|
||||||
|
type: 'lib',
|
||||||
|
data: {
|
||||||
|
root: 'libs/child',
|
||||||
|
namedInputs: {
|
||||||
|
prod: [
|
||||||
|
'!{projectRoot}/**/*.spec.ts',
|
||||||
|
'{workspaceRoot}/global2',
|
||||||
|
{ env: 'MY_TEST_HASH_ENV' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
targets: {
|
||||||
|
test: {
|
||||||
|
inputs: ['default'],
|
||||||
|
executor: 'nx:run-commands',
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
dependencies: {
|
||||||
child: {
|
parent: [{ source: 'parent', target: 'child', type: 'static' }],
|
||||||
name: 'child',
|
|
||||||
type: 'lib',
|
|
||||||
data: {
|
|
||||||
root: 'libs/child',
|
|
||||||
namedInputs: {
|
|
||||||
prod: [
|
|
||||||
'!{projectRoot}/**/*.spec.ts',
|
|
||||||
'{workspaceRoot}/global2',
|
|
||||||
{ env: 'MY_TEST_HASH_ENV' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
targets: {
|
|
||||||
test: {
|
|
||||||
inputs: ['default'],
|
|
||||||
executor: 'nx:run-commands',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
dependencies: {
|
namedInputs: {
|
||||||
parent: [{ source: 'parent', target: 'child', type: 'static' }],
|
default: ['{projectRoot}/**/*', '{workspaceRoot}/global1'],
|
||||||
},
|
prod: ['!{projectRoot}/**/*.spec.ts'],
|
||||||
},
|
},
|
||||||
{
|
} as any,
|
||||||
namedInputs: {
|
{},
|
||||||
default: ['{projectRoot}/**/*', '{workspaceRoot}/global1'],
|
createFileHasher()
|
||||||
prod: ['!{projectRoot}/**/*.spec.ts'],
|
);
|
||||||
},
|
|
||||||
} as any,
|
|
||||||
{},
|
|
||||||
createFileHasher()
|
|
||||||
);
|
|
||||||
|
|
||||||
const parentHash = await hasher.hashTask({
|
const parentHash = await hasher.hashTask({
|
||||||
target: { project: 'parent', target: 'test' },
|
target: { project: 'parent', target: 'test' },
|
||||||
id: 'parent-test',
|
id: 'parent-test',
|
||||||
overrides: { prop: 'prop-value' },
|
overrides: { prop: 'prop-value' },
|
||||||
});
|
});
|
||||||
|
|
||||||
assertFilesets(parentHash, {
|
assertFilesets(parentHash, {
|
||||||
'child:!{projectRoot}/**/*.spec.ts': {
|
'child:!{projectRoot}/**/*.spec.ts': {
|
||||||
contains: 'libs/child/fileb.ts',
|
contains: 'libs/child/fileb.ts',
|
||||||
excludes: 'fileb.spec.ts',
|
excludes: 'fileb.spec.ts',
|
||||||
},
|
},
|
||||||
'parent:{projectRoot}/**/*': {
|
'parent:{projectRoot}/**/*': {
|
||||||
contains: 'libs/parent/filea.ts|libs/parent/filea.spec.ts',
|
contains: 'libs/parent/filea.ts|libs/parent/filea.spec.ts',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(parentHash.details.nodes['{workspaceRoot}/global1']).toEqual(
|
expect(parentHash.details.nodes['{workspaceRoot}/global1']).toEqual(
|
||||||
'global1.hash'
|
'global1.hash'
|
||||||
);
|
);
|
||||||
expect(parentHash.details.nodes['{workspaceRoot}/global2']).toBe(
|
expect(parentHash.details.nodes['{workspaceRoot}/global2']).toBe(
|
||||||
'global2.hash'
|
'global2.hash'
|
||||||
);
|
);
|
||||||
expect(parentHash.details.nodes['env:MY_TEST_HASH_ENV']).toEqual(
|
expect(parentHash.details.nodes['env:MY_TEST_HASH_ENV']).toEqual(
|
||||||
'MY_TEST_HASH_ENV_VALUE'
|
'MY_TEST_HASH_ENV_VALUE'
|
||||||
);
|
);
|
||||||
|
|
||||||
const childHash = await hasher.hashTask({
|
const childHash = await hasher.hashTask({
|
||||||
target: { project: 'child', target: 'test' },
|
target: { project: 'child', target: 'test' },
|
||||||
id: 'child-test',
|
id: 'child-test',
|
||||||
overrides: { prop: 'prop-value' },
|
overrides: { prop: 'prop-value' },
|
||||||
});
|
});
|
||||||
|
|
||||||
assertFilesets(childHash, {
|
assertFilesets(childHash, {
|
||||||
'child:{projectRoot}/**/*': {
|
'child:{projectRoot}/**/*': {
|
||||||
contains: 'libs/child/fileb.ts|libs/child/fileb.spec.ts',
|
contains: 'libs/child/fileb.ts|libs/child/fileb.spec.ts',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(childHash.details.nodes['{workspaceRoot}/global1']).toEqual(
|
expect(childHash.details.nodes['{workspaceRoot}/global1']).toEqual(
|
||||||
'global1.hash'
|
'global1.hash'
|
||||||
|
);
|
||||||
|
expect(childHash.details.nodes['{workspaceRoot}/global2']).toBe(
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
expect(childHash.details.nodes['env:MY_TEST_HASH_ENV']).toBeUndefined();
|
||||||
|
}
|
||||||
);
|
);
|
||||||
expect(childHash.details.nodes['{workspaceRoot}/global2']).toBe(undefined);
|
|
||||||
expect(childHash.details.nodes['env:MY_TEST_HASH_ENV']).toBeUndefined();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should use targetDefaults from nx.json', async () => {
|
it('should use targetDefaults from nx.json', async () => {
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import { DaemonClient } from '../daemon/client/client';
|
|||||||
import { FileHasher } from './impl/file-hasher-base';
|
import { FileHasher } from './impl/file-hasher-base';
|
||||||
import { hashArray } from './impl';
|
import { hashArray } from './impl';
|
||||||
import { createProjectRootMappings } from '../project-graph/utils/find-project-for-path';
|
import { createProjectRootMappings } from '../project-graph/utils/find-project-for-path';
|
||||||
import { registerPluginTSTranspiler } from '../utils/nx-plugin';
|
import { findMatchingProjects } from '../utils/find-matching-projects';
|
||||||
|
|
||||||
type ExpandedSelfInput =
|
type ExpandedSelfInput =
|
||||||
| { fileset: string }
|
| { fileset: string }
|
||||||
@ -495,7 +495,11 @@ class TaskHasherImpl {
|
|||||||
): Promise<PartialHash[]> {
|
): Promise<PartialHash[]> {
|
||||||
const partialHashes: Promise<PartialHash[]>[] = [];
|
const partialHashes: Promise<PartialHash[]>[] = [];
|
||||||
for (const input of projectInputs) {
|
for (const input of projectInputs) {
|
||||||
for (const project of input.projects) {
|
const projects = findMatchingProjects(
|
||||||
|
input.projects,
|
||||||
|
this.projectGraph.nodes
|
||||||
|
);
|
||||||
|
for (const project of projects) {
|
||||||
const namedInputs = getNamedInputs(
|
const namedInputs = getNamedInputs(
|
||||||
this.nxJson,
|
this.nxJson,
|
||||||
this.projectGraph.nodes[project]
|
this.projectGraph.nodes[project]
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { splitArgsIntoNxArgsAndOverrides } from './command-line-utils';
|
import { splitArgsIntoNxArgsAndOverrides } from './command-line-utils';
|
||||||
|
import { withEnvironmentVariables as withEnvironment } from '../../internal-testing-utils/with-environment';
|
||||||
|
|
||||||
jest.mock('../project-graph/file-utils');
|
jest.mock('../project-graph/file-utils');
|
||||||
|
|
||||||
@ -458,15 +459,3 @@ describe('splitArgs', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function withEnvironment(env: Record<string, string>, callback: () => void) {
|
|
||||||
const originalValues: Record<string, string> = {};
|
|
||||||
for (const key in env) {
|
|
||||||
originalValues[key] = process.env[key];
|
|
||||||
process.env[key] = env[key];
|
|
||||||
}
|
|
||||||
callback();
|
|
||||||
for (const key in env) {
|
|
||||||
process.env[key] = originalValues[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user