Skip to content

Commit a1d84ff

Browse files
authored
Merge branch 'master' into conductor-migration
2 parents 127b21f + 0232e5b commit a1d84ff

File tree

151 files changed

+2239
-1859
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

151 files changed

+2239
-1859
lines changed

.github/actions/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
"type": "module",
66
"devDependencies": {
77
"@sourceacademy/modules-repotools": "workspace:^",
8-
"@types/node": "^22.15.30",
9-
"typescript": "^5.8.2",
8+
"@types/node": "^24.0.0",
9+
"typescript": "^6.0.2",
1010
"vitest": "4.1.0"
1111
},
1212
"dependencies": {
1313
"@actions/artifact": "^6.0.0",
14-
"@actions/core": "^1.11.1",
14+
"@actions/core": "^3.0.0",
1515
"@actions/exec": "^3.0.0",
1616
"es-toolkit": "^1.44.0",
1717
"snyk-nodejs-lockfile-parser": "^2.4.2"

.github/actions/src/__tests__/lockfiles.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ describe(utils.extractPackageName, () => {
1616
.toEqual('lodash');
1717
});
1818

19+
it('works on this weird patch thing', () => {
20+
expect(utils.extractPackageName('typescript@patch:typescript@npm:6.0.2'))
21+
.toEqual('typescript');
22+
});
23+
1924
it('throws an error on an invalid package name', () => {
2025
expect(() => utils.extractPackageName('something weird'))
2126
.toThrowError('Invalid package name: something weird');

.github/actions/src/commons.ts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { getExecOutput } from '@actions/exec';
22
import { memoize } from 'es-toolkit';
33

4+
type PackageType = 'bundle' | 'tab' | 'lib' | null;
5+
46
export interface RawPackageRecord {
57
directory: string;
68
hasChanges: boolean;
@@ -9,9 +11,10 @@ export interface RawPackageRecord {
911
devDependencies: Record<string, string>;
1012
dependencies: Record<string, string>;
1113
};
14+
type: PackageType;
1215
}
1316

14-
interface BasePackageRecord {
17+
interface BasePackageRecord<T extends PackageType> {
1518
/**
1619
* Directory within which the `package.json` file was found
1720
*/
@@ -31,17 +34,19 @@ interface BasePackageRecord {
3134
* might need playwright for its tests
3235
*/
3336
needsPlaywright: boolean;
37+
38+
type: T;
3439
}
3540

36-
export interface BundlePackageRecord extends BasePackageRecord {
41+
export interface BundlePackageRecord extends BasePackageRecord<'bundle'> {
3742
bundleName: string;
3843
}
3944

40-
export interface TabPackageRecord extends BasePackageRecord {
45+
export interface TabPackageRecord extends BasePackageRecord<'tab'> {
4146
tabName: string;
4247
}
4348

44-
export type PackageRecord = BundlePackageRecord | TabPackageRecord | BasePackageRecord;
49+
export type PackageRecord = BundlePackageRecord | TabPackageRecord | BasePackageRecord<'lib' | null>;
4550

4651
export function isPackageRecord(obj: unknown): obj is PackageRecord {
4752
if (typeof obj !== 'object' || obj === null) return false;
@@ -76,3 +81,16 @@ export const checkDirForChanges = memoize(async (directory: string) => {
7681
);
7782
return exitCode !== 0;
7883
});
84+
85+
/**
86+
* Format of each entry produced when running the command `yarn workspaces list --json`.
87+
*/
88+
export interface YarnWorkspaceRecord {
89+
location: string;
90+
name: string;
91+
}
92+
93+
export async function runYarnWorkspacesList() {
94+
const { stdout } = await getExecOutput('yarn workspaces list --json', [], { silent: true });
95+
return stdout.trim();
96+
}

.github/actions/src/info/__tests__/index.test.ts

Lines changed: 23 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import fs from 'fs/promises';
33
import pathlib from 'path';
44
import * as core from '@actions/core';
55
import { describe, expect, test, vi } from 'vitest';
6-
import * as git from '../../commons.js';
6+
import * as commons from '../../commons.js';
77
import * as lockfiles from '../../lockfiles.js';
88
import { getAllPackages, getRawPackages, main } from '../index.js';
99

10-
const mockedCheckChanges = vi.spyOn(git, 'checkDirForChanges');
10+
const mockedCheckChanges = vi.spyOn(commons, 'checkDirForChanges');
1111

1212
vi.mock(import('path'), async importOriginal => {
1313
const { posix } = await importOriginal();
@@ -22,6 +22,14 @@ vi.mock(import('../../gitRoot.js'), () => ({
2222
gitRoot: 'root'
2323
}));
2424

25+
vi.mock(import('@actions/core'), async importOriginal => {
26+
const original = await importOriginal();
27+
return {
28+
...original,
29+
setOutput: vi.fn((_name, _value) => {})
30+
};
31+
});
32+
2533
class NodeError extends Error {
2634
constructor(public readonly code: string) {
2735
super();
@@ -119,42 +127,23 @@ function mockReadFile(path: string) {
119127
return recurser(segments, { root: mockDirectory });
120128
}
121129

130+
function getMockYarnWorkspaceRecords(): commons.YarnWorkspaceRecord[] {
131+
return [
132+
{ location: '.', name: '@sourceacademy/modules' },
133+
{ location: 'lib/modules-lib', name: '@sourceacademy/modules-lib' },
134+
{ location: 'src/bundles/bundle0', name: '@sourceacademy/bundle-bundle0' },
135+
{ location: 'src/tabs/tab0', name: '@sourceacademy/tab-Tab0' },
136+
];
137+
}
138+
122139
vi.spyOn(fs, 'readdir').mockImplementation(mockReaddir as any);
123140
vi.spyOn(fs, 'readFile').mockImplementation(mockReadFile as any);
124141
vi.spyOn(lockfiles, 'hasLockFileChanged').mockResolvedValue(false);
142+
vi.spyOn(commons, 'runYarnWorkspacesList').mockResolvedValue(
143+
getMockYarnWorkspaceRecords().map(each => JSON.stringify(each)).join('\n')
144+
);
125145

126146
describe(getRawPackages, () => {
127-
test('maxDepth = 1', async () => {
128-
mockedCheckChanges.mockResolvedValueOnce(true);
129-
const results = Object.entries(await getRawPackages('root', 1));
130-
expect(fs.readdir).toHaveBeenCalledTimes(3);
131-
expect(results.length).toEqual(1);
132-
133-
const [[name, packageData]] = results;
134-
expect(name).toEqual('@sourceacademy/modules');
135-
expect(packageData.hasChanges).toEqual(true);
136-
expect(git.checkDirForChanges).toHaveBeenCalledOnce();
137-
});
138-
139-
test('maxDepth = 3', async () => {
140-
mockedCheckChanges.mockResolvedValue(true);
141-
const results = await getRawPackages('root', 3);
142-
expect(Object.values(results).length).toEqual(4);
143-
expect(fs.readdir).toHaveBeenCalledTimes(8);
144-
145-
expect(results).toHaveProperty('@sourceacademy/bundle-bundle0');
146-
const bundleResult = results['@sourceacademy/bundle-bundle0'];
147-
expect(bundleResult.hasChanges).toEqual(true);
148-
149-
expect(results).toHaveProperty('@sourceacademy/tab-Tab0');
150-
const tabResult = results['@sourceacademy/tab-Tab0'];
151-
expect(tabResult.hasChanges).toEqual(true);
152-
153-
expect(results).toHaveProperty('@sourceacademy/modules-lib');
154-
const libResult = results['@sourceacademy/modules-lib'];
155-
expect(libResult.hasChanges).toEqual(true);
156-
});
157-
158147
test('hasChanges fields accurately reflects value returned from checkChanges', async () => {
159148
mockedCheckChanges.mockImplementation(p => {
160149
switch (p) {
@@ -221,7 +210,7 @@ describe(getAllPackages, () => {
221210
});
222211

223212
describe(main, () => {
224-
const mockedSetOutput = vi.spyOn(core, 'setOutput');
213+
const mockedSetOutput = vi.mocked(core.setOutput);
225214

226215
vi.spyOn(core.summary, 'addHeading').mockImplementation(() => core.summary);
227216
vi.spyOn(core.summary, 'addTable').mockImplementation(() => core.summary);

.github/actions/src/info/__tests__/sorter.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ describe(topoSortPackages, () => {
55
test('Without a cycle', () => {
66
const result = topoSortPackages({
77
'@sourceacademy/0': {
8+
type: null,
89
hasChanges: false,
910
directory: '/',
1011
package: {
@@ -16,6 +17,7 @@ describe(topoSortPackages, () => {
1617
}
1718
},
1819
'@sourceacademy/1': {
20+
type: null,
1921
hasChanges: false,
2022
directory: '/',
2123
package: {
@@ -27,6 +29,7 @@ describe(topoSortPackages, () => {
2729
}
2830
},
2931
'@sourceacademy/2': {
32+
type: null,
3033
hasChanges: false,
3134
directory: '/',
3235
package: {
@@ -47,6 +50,7 @@ describe(topoSortPackages, () => {
4750
test('With a cycle', () => {
4851
const func = () => topoSortPackages({
4952
'@sourceacademy/0': {
53+
type: null,
5054
hasChanges: false,
5155
directory: '/',
5256
package: {
@@ -58,6 +62,7 @@ describe(topoSortPackages, () => {
5862
}
5963
},
6064
'@sourceacademy/1': {
65+
type: null,
6166
hasChanges: false,
6267
directory: '/',
6368
package: {
@@ -69,6 +74,7 @@ describe(topoSortPackages, () => {
6974
}
7075
},
7176
'@sourceacademy/2': {
77+
type: null,
7278
hasChanges: false,
7379
directory: '/',
7480
package: {

0 commit comments

Comments
 (0)