cli: added package graph utility to list changed packages
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user