Enhance CI Change Detection (#43596)
Realistically speaking, if a `package.json` file is changed we should consider it to be a change for the entire package. This helps because it might impact the config itself. The same is true of changes to `pnpm-lock.yaml` since those may update dependencies for any package. I have also added a `-f` option to the CI Jobs CLI command to trigger all jobs.
This commit is contained in:
parent
43be71a52f
commit
5425a9536a
File diff suppressed because one or more lines are too long
|
@ -21,14 +21,25 @@ const program = new Command( 'ci-jobs' )
|
||||||
'<base-ref>',
|
'<base-ref>',
|
||||||
'Base ref to compare the current ref against for change detection.'
|
'Base ref to compare the current ref against for change detection.'
|
||||||
)
|
)
|
||||||
.action( async ( baseRef: string ) => {
|
.option(
|
||||||
|
'-f --force',
|
||||||
|
'Forces all projects to be marked as changed.',
|
||||||
|
false
|
||||||
|
)
|
||||||
|
.action( async ( baseRef: string, options ) => {
|
||||||
Logger.startTask( 'Parsing Project Graph', true );
|
Logger.startTask( 'Parsing Project Graph', true );
|
||||||
const projectGraph = buildProjectGraph();
|
const projectGraph = buildProjectGraph();
|
||||||
Logger.endTask( true );
|
Logger.endTask( true );
|
||||||
|
|
||||||
Logger.startTask( 'Pulling File Changes', true );
|
let fileChanges;
|
||||||
const fileChanges = getFileChanges( projectGraph, baseRef );
|
if ( options.force ) {
|
||||||
Logger.endTask( true );
|
Logger.warn( 'Forcing all projects to be marked as changed.' );
|
||||||
|
fileChanges = true;
|
||||||
|
} else {
|
||||||
|
Logger.startTask( 'Pulling File Changes', true );
|
||||||
|
fileChanges = getFileChanges( projectGraph, baseRef );
|
||||||
|
Logger.endTask( true );
|
||||||
|
}
|
||||||
|
|
||||||
Logger.startTask( 'Creating Jobs', true );
|
Logger.startTask( 'Creating Jobs', true );
|
||||||
const jobs = await createJobsForChanges( projectGraph, fileChanges, {
|
const jobs = await createJobsForChanges( projectGraph, fileChanges, {
|
||||||
|
|
|
@ -33,7 +33,10 @@ describe( 'Config', () => {
|
||||||
jobs: [
|
jobs: [
|
||||||
{
|
{
|
||||||
type: JobType.Lint,
|
type: JobType.Lint,
|
||||||
changes: [ makeRe( '/src/**/*.{js,jsx,ts,tsx}' ) ],
|
changes: [
|
||||||
|
/^package\.json$/,
|
||||||
|
makeRe( '/src/**/*.{js,jsx,ts,tsx}' ),
|
||||||
|
],
|
||||||
command: 'foo',
|
command: 'foo',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -57,7 +60,10 @@ describe( 'Config', () => {
|
||||||
jobs: [
|
jobs: [
|
||||||
{
|
{
|
||||||
type: JobType.Lint,
|
type: JobType.Lint,
|
||||||
changes: [ makeRe( '/src/**/*.{js,jsx,ts,tsx}' ) ],
|
changes: [
|
||||||
|
/^package\.json$/,
|
||||||
|
makeRe( '/src/**/*.{js,jsx,ts,tsx}' ),
|
||||||
|
],
|
||||||
command: 'foo <baseRef>',
|
command: 'foo <baseRef>',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -100,6 +106,7 @@ describe( 'Config', () => {
|
||||||
{
|
{
|
||||||
type: JobType.Lint,
|
type: JobType.Lint,
|
||||||
changes: [
|
changes: [
|
||||||
|
/^package\.json$/,
|
||||||
makeRe( '/src/**/*.{js,jsx,ts,tsx}' ),
|
makeRe( '/src/**/*.{js,jsx,ts,tsx}' ),
|
||||||
makeRe( '/test/**/*.{js,jsx,ts,tsx}' ),
|
makeRe( '/test/**/*.{js,jsx,ts,tsx}' ),
|
||||||
],
|
],
|
||||||
|
@ -130,7 +137,10 @@ describe( 'Config', () => {
|
||||||
{
|
{
|
||||||
type: JobType.Test,
|
type: JobType.Test,
|
||||||
name: 'default',
|
name: 'default',
|
||||||
changes: [ makeRe( '/src/**/*.{js,jsx,ts,tsx}' ) ],
|
changes: [
|
||||||
|
/^package\.json$/,
|
||||||
|
makeRe( '/src/**/*.{js,jsx,ts,tsx}' ),
|
||||||
|
],
|
||||||
command: 'foo',
|
command: 'foo',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -164,7 +174,10 @@ describe( 'Config', () => {
|
||||||
{
|
{
|
||||||
type: JobType.Test,
|
type: JobType.Test,
|
||||||
name: 'default',
|
name: 'default',
|
||||||
changes: [ makeRe( '/src/**/*.{js,jsx,ts,tsx}' ) ],
|
changes: [
|
||||||
|
/^package\.json$/,
|
||||||
|
makeRe( '/src/**/*.{js,jsx,ts,tsx}' ),
|
||||||
|
],
|
||||||
command: 'foo',
|
command: 'foo',
|
||||||
testEnv: {
|
testEnv: {
|
||||||
start: 'bar',
|
start: 'bar',
|
||||||
|
@ -199,7 +212,10 @@ describe( 'Config', () => {
|
||||||
{
|
{
|
||||||
type: JobType.Test,
|
type: JobType.Test,
|
||||||
name: 'default',
|
name: 'default',
|
||||||
changes: [ makeRe( '/src/**/*.{js,jsx,ts,tsx}' ) ],
|
changes: [
|
||||||
|
/^package\.json$/,
|
||||||
|
makeRe( '/src/**/*.{js,jsx,ts,tsx}' ),
|
||||||
|
],
|
||||||
command: 'foo',
|
command: 'foo',
|
||||||
cascadeKeys: [ 'bar' ],
|
cascadeKeys: [ 'bar' ],
|
||||||
},
|
},
|
||||||
|
|
|
@ -57,4 +57,46 @@ baz/project-d/baz.js`;
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
it( 'should see pnpm-lock.yaml file changes as universal changes', () => {
|
||||||
|
jest.mocked( execSync ).mockImplementation( ( command ) => {
|
||||||
|
if ( command === 'git diff --name-only origin/trunk' ) {
|
||||||
|
return `test/project-a/package.json
|
||||||
|
foo/project-b/foo.js
|
||||||
|
pnpm-lock.yaml
|
||||||
|
bar/project-c/bar.js
|
||||||
|
baz/project-d/baz.js`;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error( 'Invalid command' );
|
||||||
|
} );
|
||||||
|
|
||||||
|
const fileChanges = getFileChanges(
|
||||||
|
{
|
||||||
|
name: 'project-a',
|
||||||
|
path: 'test/project-a',
|
||||||
|
dependencies: [
|
||||||
|
{
|
||||||
|
name: 'project-b',
|
||||||
|
path: 'foo/project-b',
|
||||||
|
dependencies: [
|
||||||
|
{
|
||||||
|
name: 'project-c',
|
||||||
|
path: 'bar/project-c',
|
||||||
|
dependencies: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'project-c',
|
||||||
|
path: 'bar/project-c',
|
||||||
|
dependencies: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'origin/trunk'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect( fileChanges ).toStrictEqual( true );
|
||||||
|
} );
|
||||||
} );
|
} );
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { parseTestEnvConfig } from '../test-environment';
|
||||||
jest.mock( '../test-environment' );
|
jest.mock( '../test-environment' );
|
||||||
|
|
||||||
describe( 'Job Processing', () => {
|
describe( 'Job Processing', () => {
|
||||||
describe( 'getFileChanges', () => {
|
describe( 'createJobsForChanges', () => {
|
||||||
it( 'should do nothing with no CI configs', async () => {
|
it( 'should do nothing with no CI configs', async () => {
|
||||||
const jobs = await createJobsForChanges(
|
const jobs = await createJobsForChanges(
|
||||||
{
|
{
|
||||||
|
@ -686,5 +686,48 @@ describe( 'Job Processing', () => {
|
||||||
},
|
},
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
it( 'should trigger all jobs for a single node with changes set to "true"', async () => {
|
||||||
|
const jobs = await createJobsForChanges(
|
||||||
|
{
|
||||||
|
name: 'test',
|
||||||
|
path: 'test',
|
||||||
|
ciConfig: {
|
||||||
|
jobs: [
|
||||||
|
{
|
||||||
|
type: JobType.Lint,
|
||||||
|
changes: [ /test.js$/ ],
|
||||||
|
command: 'test-lint',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: JobType.Test,
|
||||||
|
name: 'Default',
|
||||||
|
changes: [ /test.js$/ ],
|
||||||
|
command: 'test-cmd',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
dependencies: [],
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
|
expect( jobs.lint ).toHaveLength( 1 );
|
||||||
|
expect( jobs.lint ).toContainEqual( {
|
||||||
|
projectName: 'test',
|
||||||
|
command: 'test-lint',
|
||||||
|
} );
|
||||||
|
expect( jobs.test ).toHaveLength( 1 );
|
||||||
|
expect( jobs.test ).toContainEqual( {
|
||||||
|
projectName: 'test',
|
||||||
|
name: 'Default',
|
||||||
|
command: 'test-cmd',
|
||||||
|
testEnv: {
|
||||||
|
shouldCreate: false,
|
||||||
|
envVars: {},
|
||||||
|
},
|
||||||
|
} );
|
||||||
|
} );
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
|
|
|
@ -80,6 +80,7 @@ describe( 'Project Graph', () => {
|
||||||
command: 'foo',
|
command: 'foo',
|
||||||
type: 'lint',
|
type: 'lint',
|
||||||
changes: [
|
changes: [
|
||||||
|
/^package\.json$/,
|
||||||
/^(?:src(?:\/|\/(?:(?!(?:\/|^)\.).)*?\/)(?!\.)[^/]*?\.js|src(?:\/|\/(?:(?!(?:\/|^)\.).)*?\/)(?!\.)[^/]*?\.jsx|src(?:\/|\/(?:(?!(?:\/|^)\.).)*?\/)(?!\.)[^/]*?\.ts|src(?:\/|\/(?:(?!(?:\/|^)\.).)*?\/)(?!\.)[^/]*?\.tsx)$/,
|
/^(?:src(?:\/|\/(?:(?!(?:\/|^)\.).)*?\/)(?!\.)[^/]*?\.js|src(?:\/|\/(?:(?!(?:\/|^)\.).)*?\/)(?!\.)[^/]*?\.jsx|src(?:\/|\/(?:(?!(?:\/|^)\.).)*?\/)(?!\.)[^/]*?\.ts|src(?:\/|\/(?:(?!(?:\/|^)\.).)*?\/)(?!\.)[^/]*?\.tsx)$/,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
@ -57,9 +57,28 @@ interface BaseJobConfig {
|
||||||
/**
|
/**
|
||||||
* Parses and validates a raw change config entry.
|
* Parses and validates a raw change config entry.
|
||||||
*
|
*
|
||||||
* @param {string|string[]} raw The raw config to parse.
|
* @param {string|string[]} raw The raw config to parse.
|
||||||
|
* @param {string[]} extraGlobs Any extra globs that should be added to the configuration.
|
||||||
*/
|
*/
|
||||||
function parseChangesConfig( raw: unknown ): RegExp[] {
|
function parseChangesConfig(
|
||||||
|
raw: unknown,
|
||||||
|
extraGlobs: string[] = []
|
||||||
|
): RegExp[] {
|
||||||
|
const changes: RegExp[] = [];
|
||||||
|
|
||||||
|
// Make sure to include any extra glob patterns that were passed in.
|
||||||
|
// This allows us to make sure we're watching for changes in files
|
||||||
|
// that may implicitly be impactful but shouldn't need to be
|
||||||
|
// stated explicitly in the list of file changes.
|
||||||
|
for ( const entry of extraGlobs ) {
|
||||||
|
const regex = makeRe( entry );
|
||||||
|
if ( ! regex ) {
|
||||||
|
throw new Error( 'Invalid extra glob pattern.' );
|
||||||
|
}
|
||||||
|
|
||||||
|
changes.push( regex );
|
||||||
|
}
|
||||||
|
|
||||||
if ( typeof raw === 'string' ) {
|
if ( typeof raw === 'string' ) {
|
||||||
const regex = makeRe( raw );
|
const regex = makeRe( raw );
|
||||||
if ( ! regex ) {
|
if ( ! regex ) {
|
||||||
|
@ -68,7 +87,8 @@ function parseChangesConfig( raw: unknown ): RegExp[] {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return [ regex ];
|
changes.push( regex );
|
||||||
|
return changes;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! Array.isArray( raw ) ) {
|
if ( ! Array.isArray( raw ) ) {
|
||||||
|
@ -77,7 +97,6 @@ function parseChangesConfig( raw: unknown ): RegExp[] {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const changes: RegExp[] = [];
|
|
||||||
for ( const entry of raw ) {
|
for ( const entry of raw ) {
|
||||||
if ( typeof entry !== 'string' ) {
|
if ( typeof entry !== 'string' ) {
|
||||||
throw new ConfigError(
|
throw new ConfigError(
|
||||||
|
@ -156,7 +175,7 @@ function parseLintJobConfig( raw: any ): LintJobConfig {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: JobType.Lint,
|
type: JobType.Lint,
|
||||||
changes: parseChangesConfig( raw.changes ),
|
changes: parseChangesConfig( raw.changes, [ 'package.json' ] ),
|
||||||
command: raw.command,
|
command: raw.command,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -306,7 +325,7 @@ function parseTestJobConfig( raw: any ): TestJobConfig {
|
||||||
const config: TestJobConfig = {
|
const config: TestJobConfig = {
|
||||||
type: JobType.Test,
|
type: JobType.Test,
|
||||||
name: raw.name,
|
name: raw.name,
|
||||||
changes: parseChangesConfig( raw.changes ),
|
changes: parseChangesConfig( raw.changes, [ 'package.json' ] ),
|
||||||
command: raw.command,
|
command: raw.command,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -77,20 +77,26 @@ function getChangedFilesForProject(
|
||||||
*
|
*
|
||||||
* @param {Object} projectGraph The project graph to assign changes for.
|
* @param {Object} projectGraph The project graph to assign changes for.
|
||||||
* @param {string} baseRef The git ref to compare against for changes.
|
* @param {string} baseRef The git ref to compare against for changes.
|
||||||
* @return {Object} A map of changed files keyed by the project name.
|
* @return {Object|true} A map of changed files keyed by the project name or true if all projects should be marked as changed.
|
||||||
*/
|
*/
|
||||||
export function getFileChanges(
|
export function getFileChanges(
|
||||||
projectGraph: ProjectNode,
|
projectGraph: ProjectNode,
|
||||||
baseRef: string
|
baseRef: string
|
||||||
): ProjectFileChanges {
|
): ProjectFileChanges | true {
|
||||||
const projectPaths = getProjectPaths( projectGraph );
|
|
||||||
|
|
||||||
// We're going to use git to figure out what files have changed.
|
// We're going to use git to figure out what files have changed.
|
||||||
const output = execSync( `git diff --name-only ${ baseRef }`, {
|
const output = execSync( `git diff --name-only ${ baseRef }`, {
|
||||||
encoding: 'utf8',
|
encoding: 'utf8',
|
||||||
} );
|
} );
|
||||||
const changedFilePaths = output.split( '\n' );
|
const changedFilePaths = output.split( '\n' );
|
||||||
|
|
||||||
|
// If the root lockfile has been changed we have no easy way
|
||||||
|
// of knowing which projects have been impacted. We want
|
||||||
|
// to re-run all jobs in all projects for safety.
|
||||||
|
if ( changedFilePaths.includes( 'pnpm-lock.yaml' ) ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const projectPaths = getProjectPaths( projectGraph );
|
||||||
const changes: ProjectFileChanges = {};
|
const changes: ProjectFileChanges = {};
|
||||||
for ( const projectName in projectPaths ) {
|
for ( const projectName in projectPaths ) {
|
||||||
// Projects with no paths have no changed files for us to identify.
|
// Projects with no paths have no changed files for us to identify.
|
||||||
|
|
|
@ -73,33 +73,39 @@ function replaceCommandVars( command: string, options: CreateOptions ): string {
|
||||||
/**
|
/**
|
||||||
* Checks the config against the changes and creates one if it should be run.
|
* Checks the config against the changes and creates one if it should be run.
|
||||||
*
|
*
|
||||||
* @param {string} projectName The name of the project that the job is for.
|
* @param {string} projectName The name of the project that the job is for.
|
||||||
* @param {Object} config The config object for the lint job.
|
* @param {Object} config The config object for the lint job.
|
||||||
* @param {Array.<string>} changes The file changes that have occurred for the project.
|
* @param {Array.<string>|true} changes The file changes that have occurred for the project or true if all projects should be marked as changed.
|
||||||
* @param {Object} options The options to use when creating the job.
|
* @param {Object} options The options to use when creating the job.
|
||||||
* @return {Object|null} The job that should be run or null if no job should be run.
|
* @return {Object|null} The job that should be run or null if no job should be run.
|
||||||
*/
|
*/
|
||||||
function createLintJob(
|
function createLintJob(
|
||||||
projectName: string,
|
projectName: string,
|
||||||
config: LintJobConfig,
|
config: LintJobConfig,
|
||||||
changes: string[],
|
changes: string[] | true,
|
||||||
options: CreateOptions
|
options: CreateOptions
|
||||||
): LintJob | null {
|
): LintJob | null {
|
||||||
let triggered = false;
|
let triggered = false;
|
||||||
|
|
||||||
// Projects can configure jobs to be triggered when a
|
// When we're forcing changes for all projects we don't need to check
|
||||||
// changed file matches a path regex.
|
// for any changed files before triggering the job.
|
||||||
for ( const file of changes ) {
|
if ( changes === true ) {
|
||||||
for ( const change of config.changes ) {
|
triggered = true;
|
||||||
if ( change.test( file ) ) {
|
} else {
|
||||||
triggered = true;
|
// Projects can configure jobs to be triggered when a
|
||||||
|
// changed file matches a path regex.
|
||||||
|
for ( const file of changes ) {
|
||||||
|
for ( const change of config.changes ) {
|
||||||
|
if ( change.test( file ) ) {
|
||||||
|
triggered = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( triggered ) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( triggered ) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! triggered ) {
|
if ( ! triggered ) {
|
||||||
|
@ -115,47 +121,55 @@ function createLintJob(
|
||||||
/**
|
/**
|
||||||
* Checks the config against the changes and creates one if it should be run.
|
* Checks the config against the changes and creates one if it should be run.
|
||||||
*
|
*
|
||||||
* @param {string} projectName The name of the project that the job is for.
|
* @param {string} projectName The name of the project that the job is for.
|
||||||
* @param {Object} config The config object for the test job.
|
* @param {Object} config The config object for the test job.
|
||||||
* @param {Array.<string>} changes The file changes that have occurred for the project.
|
* @param {Array.<string>|true} changes The file changes that have occurred for the project or true if all projects should be marked as changed.
|
||||||
* @param {Object} options The options to use when creating the job.
|
* @param {Object} options The options to use when creating the job.
|
||||||
* @param {Array.<string>} cascadeKeys The cascade keys that have been triggered in dependencies.
|
* @param {Array.<string>} cascadeKeys The cascade keys that have been triggered in dependencies.
|
||||||
* @return {Promise.<Object|null>} The job that should be run or null if no job should be run.
|
* @return {Promise.<Object|null>} The job that should be run or null if no job should be run.
|
||||||
*/
|
*/
|
||||||
async function createTestJob(
|
async function createTestJob(
|
||||||
projectName: string,
|
projectName: string,
|
||||||
config: TestJobConfig,
|
config: TestJobConfig,
|
||||||
changes: string[],
|
changes: string[] | true,
|
||||||
options: CreateOptions,
|
options: CreateOptions,
|
||||||
cascadeKeys: string[]
|
cascadeKeys: string[]
|
||||||
): Promise< TestJob | null > {
|
): Promise< TestJob | null > {
|
||||||
let triggered = false;
|
let triggered = false;
|
||||||
|
|
||||||
// Some jobs can be configured to trigger when a dependency has a job that
|
// When we're forcing changes for all projects we don't need to check
|
||||||
// was triggered. For example, a code change in a dependency might mean
|
// for any changed files before triggering the job.
|
||||||
// that code is impacted in the current project even if no files were
|
if ( changes === true ) {
|
||||||
// actually changed in this project.
|
|
||||||
if (
|
|
||||||
config.cascadeKeys &&
|
|
||||||
config.cascadeKeys.some( ( value ) => cascadeKeys.includes( value ) )
|
|
||||||
) {
|
|
||||||
triggered = true;
|
triggered = true;
|
||||||
}
|
} else {
|
||||||
|
// Some jobs can be configured to trigger when a dependency has a job that
|
||||||
|
// was triggered. For example, a code change in a dependency might mean
|
||||||
|
// that code is impacted in the current project even if no files were
|
||||||
|
// actually changed in this project.
|
||||||
|
if (
|
||||||
|
config.cascadeKeys &&
|
||||||
|
config.cascadeKeys.some( ( value ) =>
|
||||||
|
cascadeKeys.includes( value )
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
triggered = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Projects can configure jobs to be triggered when a
|
// Projects can configure jobs to be triggered when a
|
||||||
// changed file matches a path regex.
|
// changed file matches a path regex.
|
||||||
if ( ! triggered ) {
|
if ( ! triggered ) {
|
||||||
for ( const file of changes ) {
|
for ( const file of changes ) {
|
||||||
for ( const change of config.changes ) {
|
for ( const change of config.changes ) {
|
||||||
if ( change.test( file ) ) {
|
if ( change.test( file ) ) {
|
||||||
triggered = true;
|
triggered = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( triggered ) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( triggered ) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,15 +203,15 @@ async function createTestJob(
|
||||||
/**
|
/**
|
||||||
* Recursively checks the project for any jobs that should be executed and returns them.
|
* Recursively checks the project for any jobs that should be executed and returns them.
|
||||||
*
|
*
|
||||||
* @param {Object} node The current project node to examine.
|
* @param {Object} node The current project node to examine.
|
||||||
* @param {Object} changedFiles The files that have changed for the project.
|
* @param {Object|true} changes The changed files keyed by their project or true if all projects should be marked as changed.
|
||||||
* @param {Object} options The options to use when creating the job.
|
* @param {Object} options The options to use when creating the job.
|
||||||
* @param {Array.<string>} cascadeKeys The cascade keys that have been triggered in dependencies.
|
* @param {Array.<string>} cascadeKeys The cascade keys that have been triggered in dependencies.
|
||||||
* @return {Promise.<Object>} The jobs that have been created for the project.
|
* @return {Promise.<Object>} The jobs that have been created for the project.
|
||||||
*/
|
*/
|
||||||
async function createJobsForProject(
|
async function createJobsForProject(
|
||||||
node: ProjectNode,
|
node: ProjectNode,
|
||||||
changedFiles: ProjectFileChanges,
|
changes: ProjectFileChanges | true,
|
||||||
options: CreateOptions,
|
options: CreateOptions,
|
||||||
cascadeKeys: string[]
|
cascadeKeys: string[]
|
||||||
): Promise< Jobs > {
|
): Promise< Jobs > {
|
||||||
|
@ -221,7 +235,7 @@ async function createJobsForProject(
|
||||||
|
|
||||||
const dependencyJobs = await createJobsForProject(
|
const dependencyJobs = await createJobsForProject(
|
||||||
dependency,
|
dependency,
|
||||||
changedFiles,
|
changes,
|
||||||
options,
|
options,
|
||||||
dependencyCascade
|
dependencyCascade
|
||||||
);
|
);
|
||||||
|
@ -256,12 +270,23 @@ async function createJobsForProject(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Jobs will check to see whether or not they should trigger based on the files
|
||||||
|
// that have been changed in the project. When "true" is given, however, it
|
||||||
|
// means that we should consider ALL files to have been changed and
|
||||||
|
// trigger any jobs for the project.
|
||||||
|
let projectChanges;
|
||||||
|
if ( changes === true ) {
|
||||||
|
projectChanges = true;
|
||||||
|
} else {
|
||||||
|
projectChanges = changes[ node.name ] ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
switch ( jobConfig.type ) {
|
switch ( jobConfig.type ) {
|
||||||
case JobType.Lint: {
|
case JobType.Lint: {
|
||||||
const created = createLintJob(
|
const created = createLintJob(
|
||||||
node.name,
|
node.name,
|
||||||
jobConfig,
|
jobConfig,
|
||||||
changedFiles[ node.name ] ?? [],
|
projectChanges,
|
||||||
options
|
options
|
||||||
);
|
);
|
||||||
if ( ! created ) {
|
if ( ! created ) {
|
||||||
|
@ -277,7 +302,7 @@ async function createJobsForProject(
|
||||||
const created = await createTestJob(
|
const created = await createTestJob(
|
||||||
node.name,
|
node.name,
|
||||||
jobConfig,
|
jobConfig,
|
||||||
changedFiles[ node.name ] ?? [],
|
projectChanges,
|
||||||
options,
|
options,
|
||||||
cascadeKeys
|
cascadeKeys
|
||||||
);
|
);
|
||||||
|
@ -306,14 +331,14 @@ async function createJobsForProject(
|
||||||
/**
|
/**
|
||||||
* Creates jobs to run for the given project graph and file changes.
|
* Creates jobs to run for the given project graph and file changes.
|
||||||
*
|
*
|
||||||
* @param {Object} root The root node for the project graph.
|
* @param {Object} root The root node for the project graph.
|
||||||
* @param {Object} changes The file changes that have occurred.
|
* @param {Object|true} changes The changed files keyed by their project or true if all projects should be marked as changed.
|
||||||
* @param {Object} options The options to use when creating the job.
|
* @param {Object} options The options to use when creating the job.
|
||||||
* @return {Promise.<Object>} The jobs that should be run.
|
* @return {Promise.<Object>} The jobs that should be run.
|
||||||
*/
|
*/
|
||||||
export function createJobsForChanges(
|
export function createJobsForChanges(
|
||||||
root: ProjectNode,
|
root: ProjectNode,
|
||||||
changes: ProjectFileChanges,
|
changes: ProjectFileChanges | true,
|
||||||
options: CreateOptions
|
options: CreateOptions
|
||||||
): Promise< Jobs > {
|
): Promise< Jobs > {
|
||||||
return createJobsForProject( root, changes, options, [] );
|
return createJobsForProject( root, changes, options, [] );
|
||||||
|
|
Loading…
Reference in New Issue