Skip to content

Commit

Permalink
feat(core): analyze changes to nx.json and workspace.json (#2338)
Browse files Browse the repository at this point in the history
  • Loading branch information
FrozenPandaz authored Jan 24, 2020
1 parent 616fbd6 commit b30930f
Show file tree
Hide file tree
Showing 7 changed files with 566 additions and 1 deletion.
85 changes: 85 additions & 0 deletions e2e/affected-git.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import {
ensureProject,
readJson,
runCommand,
uniq,
updateFile,
runCLI,
forEachCli,
workspaceConfigName
} from './utils';
import { NxJson } from '@nrwl/workspace/src/core/shared-interfaces';

forEachCli(() => {
describe('Affected (with Git)', () => {
let myapp = uniq('myapp');
let myapp2 = uniq('myapp');
let mylib = uniq('mylib');
it('should not affect other projects by generating a new project', () => {
ensureProject();

const nxJson: NxJson = readJson('nx.json');

delete nxJson.implicitDependencies;

updateFile('nx.json', JSON.stringify(nxJson));
runCommand(`git init`);
runCommand(`git config user.email "test@test.com"`);
runCommand(`git config user.name "Test"`);
runCommand(
`git add . && git commit -am "initial commit" && git checkout -b master`
);
runCLI(`generate @nrwl/angular:app ${myapp}`);
expect(runCommand('yarn affected:apps')).toContain(myapp);
runCommand(`git add . && git commit -am "add ${myapp}"`);

runCLI(`generate @nrwl/angular:app ${myapp2}`);
expect(runCommand('yarn affected:apps')).not.toContain(myapp);
expect(runCommand('yarn affected:apps')).toContain(myapp2);
runCommand(`git add . && git commit -am "add ${myapp2}"`);

runCLI(`generate @nrwl/angular:lib ${mylib}`);
expect(runCommand('yarn affected:apps')).not.toContain(myapp);
expect(runCommand('yarn affected:apps')).not.toContain(myapp2);
expect(runCommand('yarn affected:libs')).toContain(mylib);
runCommand(`git add . && git commit -am "add ${mylib}"`);
}, 1000000);

it('should detect changes to projects based on the nx.json', () => {
const nxJson: NxJson = readJson('nx.json');

nxJson.projects[myapp].tags = ['tag'];
updateFile('nx.json', JSON.stringify(nxJson));
expect(runCommand('yarn affected:apps')).toContain(myapp);
expect(runCommand('yarn affected:apps')).not.toContain(myapp2);
expect(runCommand('yarn affected:libs')).not.toContain(mylib);
runCommand(`git add . && git commit -am "add tag to ${myapp}"`);
});

it('should detect changes to projects based on the workspace.json', () => {
const workspaceJson = readJson(workspaceConfigName());

workspaceJson.projects[myapp].prefix = 'my-app';
updateFile(workspaceConfigName(), JSON.stringify(workspaceJson));
expect(runCommand('yarn affected:apps')).toContain(myapp);
expect(runCommand('yarn affected:apps')).not.toContain(myapp2);
expect(runCommand('yarn affected:libs')).not.toContain(mylib);
runCommand(`git add . && git commit -am "change prefix for ${myapp}"`);
});

it('should affect all projects by removing projects', () => {
const workspaceJson = readJson(workspaceConfigName());
delete workspaceJson.projects[mylib];
updateFile(workspaceConfigName(), JSON.stringify(workspaceJson));

const nxJson = readJson('nx.json');
delete nxJson.projects[mylib];
updateFile('nx.json', JSON.stringify(nxJson));

expect(runCommand('yarn affected:apps')).toContain(myapp);
expect(runCommand('yarn affected:apps')).toContain(myapp2);
expect(runCommand('yarn affected:libs')).not.toContain(mylib);
runCommand(`git add . && git commit -am "remove ${mylib}"`);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import {
TouchedProjectLocator
} from './affected-project-graph-models';
import { normalizeNxJson } from '../normalize-nx-json';
import { getTouchedProjectsInNxJson } from './locators/nx-json-changes';
import { getTouchedProjectsInWorkspaceJson } from './locators/workspace-json-changes';

export function filterAffected(
graph: ProjectGraph,
Expand All @@ -31,7 +33,9 @@ export function filterAffected(
getTouchedProjects,
getImplicitlyTouchedProjects,
getTouchedNpmPackages,
getImplicitlyTouchedProjectsByJsonChanges
getImplicitlyTouchedProjectsByJsonChanges,
getTouchedProjectsInNxJson,
getTouchedProjectsInWorkspaceJson
];
const touchedProjects = touchedProjectLocators.reduce(
(acc, f) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
import { getTouchedProjectsInNxJson } from './nx-json-changes';
import { WholeFileChange } from '../../file-utils';
import { DiffType } from '../../../utils/json-diff';

describe('getTouchedProjectsInNxJson', () => {
it('should not return changes when nx.json is not touched', () => {
const result = getTouchedProjectsInNxJson(
[
{
file: 'source.ts',
ext: '.ts',
mtime: 0,
getChanges: () => [new WholeFileChange()]
}
],
{},
{
npmScope: 'proj',
projects: {
proj1: {
tags: []
}
}
}
);
expect(result).toEqual([]);
});

it('should return all projects for a whole file change', () => {
const result = getTouchedProjectsInNxJson(
[
{
file: 'nx.json',
ext: '.json',
mtime: 0,
getChanges: () => [new WholeFileChange()]
}
],
{},
{
npmScope: 'proj',
projects: {
proj1: {
tags: []
},
proj2: {
tags: []
}
}
}
);
expect(result).toEqual(['proj1', 'proj2']);
});

it('should return all projects for changes to npmScope', () => {
const result = getTouchedProjectsInNxJson(
[
{
file: 'nx.json',
ext: '.json',
mtime: 0,
getChanges: () => [
{
type: DiffType.Modified,
path: ['npmScope'],
value: {
lhs: 'proj',
rhs: 'awesome-proj'
}
}
]
}
],
{},
{
npmScope: 'proj',
projects: {
proj1: {
tags: []
},
proj2: {
tags: []
}
}
}
);
expect(result).toEqual(['proj1', 'proj2']);
});

it('should return projects added in nx.json', () => {
const result = getTouchedProjectsInNxJson(
[
{
file: 'nx.json',
ext: '.json',
mtime: 0,
getChanges: () => [
{
type: DiffType.Added,
path: ['projects', 'proj1', 'tags'],
value: {
lhs: undefined,
rhs: []
}
}
]
}
],
{},
{
npmScope: 'proj',
projects: {
proj1: {
tags: []
},
proj2: {
tags: []
}
}
}
);
expect(result).toEqual(['proj1']);
});

it('should not return projects removed in nx.json', () => {
const result = getTouchedProjectsInNxJson(
[
{
file: 'nx.json',
ext: '.json',
mtime: 0,
getChanges: () => [
{
type: DiffType.Deleted,
path: ['projects', 'proj3', 'tags'],
value: {
lhs: [],
rhs: undefined
}
}
]
}
],
{},
{
npmScope: 'proj',
projects: {
proj1: {
tags: []
},
proj2: {
tags: []
}
}
}
);
expect(result).toEqual(['proj1', 'proj2']);
});

it('should return projects modified in nx.json', () => {
const result = getTouchedProjectsInNxJson(
[
{
file: 'nx.json',
ext: '.json',
mtime: 0,
getChanges: () => [
{
type: DiffType.Modified,
path: ['projects', 'proj1', 'tags', '0'],
value: {
lhs: 'scope:feat',
rhs: 'scope:shared'
}
}
]
}
],
{},
{
npmScope: 'proj',
projects: {
proj1: {
tags: []
},
proj2: {
tags: []
}
}
}
);
expect(result).toEqual(['proj1']);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { isWholeFileChange, WholeFileChange } from '../../file-utils';
import { DiffType, isJsonChange, JsonChange } from '../../../utils/json-diff';
import { TouchedProjectLocator } from '../affected-project-graph-models';

export const getTouchedProjectsInNxJson: TouchedProjectLocator<
WholeFileChange | JsonChange
> = (touchedFiles, workspaceJson, nxJson): string[] => {
const nxJsonChange = touchedFiles.find(change => change.file === 'nx.json');
if (!nxJsonChange) {
return [];
}

const changes = nxJsonChange.getChanges();

if (
changes.some(change => {
if (isJsonChange(change)) {
return change.path[0] !== 'projects';
}
if (isWholeFileChange(change)) {
return true;
}
return false;
})
) {
return Object.keys(nxJson.projects);
}

const touched = [];
for (let i = 0; i < changes.length; i++) {
const change = changes[i];
if (!isJsonChange(change) || change.path[0] !== 'projects') {
return;
}

if (nxJson.projects[change.path[1]]) {
touched.push(change.path[1]);
} else {
// The project was deleted so affect all projects
touched.push(...Object.keys(nxJson.projects));
// Break out of the loop after all projects have been added.
break;
}
}
return touched;
};
Loading

0 comments on commit b30930f

Please sign in to comment.