cli: added package graph utility to list changed packages

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2022-02-13 21:27:01 +01:00
parent fb39eb9fc4
commit aa892fccc5
2 changed files with 79 additions and 2 deletions
@@ -15,8 +15,20 @@
*/
import { getPackages } from '@manypkg/get-packages';
import { paths } from '../paths';
import { PackageGraph } from './PackageGraph';
import { listChangedFiles } from '../git';
jest.mock('../git');
const mockListChangedFiles = listChangedFiles as jest.MockedFunction<
typeof listChangedFiles
>;
jest.mock('../paths', () => ({
paths: {
targetRoot: '/',
},
}));
const testPackages = [
{
@@ -53,7 +65,7 @@ const testPackages = [
describe('PackageGraph', () => {
it('is able to construct a graph from this repo', async () => {
const { packages } = await getPackages(paths.ownDir);
const { packages } = await getPackages(__dirname);
const graph = PackageGraph.fromPackages(packages);
expect(graph.has('@backstage/cli')).toBe(true);
});
@@ -124,4 +136,24 @@ describe('PackageGraph', () => {
`Package 'unknown' not found`,
);
});
it('lists changed packages', async () => {
const graph = PackageGraph.fromPackages(testPackages);
mockListChangedFiles.mockResolvedValueOnce(
[
'README.md',
'packages/a/src/foo.ts',
'packages/a/src/bar.ts',
'packages/b/package.json',
'packages/f/src/foo.ts',
'packages/f/README.md',
'plugins/foo/src/index.ts',
].sort(),
);
await expect(
graph.listChangedPackages({ ref: 'origin/master' }),
).resolves.toEqual([graph.get('a'), graph.get('b')]);
});
});
@@ -14,9 +14,11 @@
* limitations under the License.
*/
import { relative as relativePath, posix as posixPath } from 'path';
import { getPackages, Package } from '@manypkg/get-packages';
import { paths } from '../paths';
import { PackageRole } from '../role';
import { listChangedFiles } from '../git';
type PackageJSON = Package['packageJson'];
@@ -158,4 +160,47 @@ export class PackageGraph extends Map<string, PackageGraphNode> {
return targets;
}
async listChangedPackages(options: { ref: string }) {
const changedFiles = await listChangedFiles(options.ref);
const dirMap = new Map(
Array.from(this.values()).map(pkg => [
posixPath.normalize(relativePath(paths.targetRoot, pkg.dir)) +
posixPath.sep,
pkg,
]),
);
const packageDirs = Array.from(dirMap.keys());
const result = new Array<PackageGraphNode>();
let searchIndex = 0;
changedFiles.sort();
packageDirs.sort();
for (const packageDir of packageDirs) {
// Skip through changes that appear before our package dir
while (
searchIndex < changedFiles.length &&
changedFiles[searchIndex] < packageDir
) {
searchIndex += 1;
}
// Check if we arrived at a match, otherwise we move on to the next package dir
if (changedFiles[searchIndex]?.startsWith(packageDir)) {
searchIndex += 1;
result.push(dirMap.get(packageDir)!);
// Skip through the rest of the changed files for the same package
while (changedFiles[searchIndex]?.startsWith(packageDir)) {
searchIndex += 1;
}
}
}
return result;
}
}