Skip to content

Commit 2d59d61

Browse files
committed
fix(migrate): scan composite actions under .github
1 parent deacb7b commit 2d59d61

4 files changed

Lines changed: 94 additions & 15 deletions

File tree

docs/guide/ci.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ You can use `voidzero-dev/setup-vp` to use Vite+ in CI environments.
1111
Set `<setup-vp-version>` in each example to an exact version from the [`setup-vp` releases page](https://github.com/voidzero-dev/setup-vp/releases). You can use a commit SHA instead. Do not use the `v1` tag. The `v1` tag no longer receives updates.
1212

1313
Run `vp migrate` to replace exact `voidzero-dev/setup-vp@v1` references in
14-
GitHub Actions workflows and composite actions with the latest exact release
15-
known to your Vite+ version. Existing exact versions and commit SHAs remain
16-
unchanged.
14+
GitHub Actions workflows and composite actions under `.github` with the latest
15+
exact release known to your Vite+ version. Existing exact versions and commit
16+
SHAs remain unchanged.
1717

1818
### Automatic Version Updates
1919

docs/guide/migrate-rules.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -255,9 +255,10 @@ Unrelated `bunx` commands and other package-executor forms remain unchanged.
255255
## Continuous Integration Rules
256256

257257
Migration replaces exact `voidzero-dev/setup-vp@v1` references in GitHub
258-
Actions workflows and composite actions with the latest exact `setup-vp`
259-
release known to that Vite+ version. The frozen `v1` tag does not receive new
260-
releases. Existing exact versions and commit SHAs are left unchanged.
258+
Actions workflows and composite actions under `.github` with the latest exact
259+
`setup-vp` release known to that Vite+ version. The frozen `v1` tag does not
260+
receive new releases. Existing exact versions and commit SHAs are left
261+
unchanged.
261262

262263
## Node.js Version Rules
263264

@@ -270,8 +271,9 @@ migrations run it unconditionally.
270271
existing `.node-version` is kept.
271272
- When `.nvmrc` is removed, any `actions/setup-node` `node-version-file:
272273
.nvmrc` reference in `.github/workflows/*.{yml,yaml}` and composite actions
273-
(`.github/actions/**/action.{yml,yaml}`) is repointed to `.node-version` so
274-
CI does not fail with "node version file ... does not exist".
274+
under `.github` (`.github/**/action.{yml,yaml}`) is repointed to
275+
`.node-version` so CI does not fail with "node version file ... does not
276+
exist".
275277

276278
## Package-Manager Rules
277279

packages/cli/src/migration/__tests__/migrator.spec.ts

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1484,7 +1484,10 @@ describe('migrateSetupVpVersion', () => {
14841484
].join('\r\n'),
14851485
);
14861486
const actionPath = path.join(actionDir, 'action.yaml');
1487-
fs.writeFileSync(actionPath, 'runs:\n steps:\n - uses : voidzero-dev/setup-vp@v1\n');
1487+
fs.writeFileSync(
1488+
actionPath,
1489+
'runs:\n using: composite\n steps:\n - uses : voidzero-dev/setup-vp@v1\n',
1490+
);
14881491
const nestedWorkflowPath = path.join(workflowsDir, 'nested', 'ignored.yml');
14891492
fs.writeFileSync(nestedWorkflowPath, '- uses: voidzero-dev/setup-vp@v1\n');
14901493
const report = createMigrationReport();
@@ -1519,6 +1522,52 @@ describe('migrateSetupVpVersion', () => {
15191522
expect(migrateSetupVpVersion(tmpDir, report)).toEqual([]);
15201523
expect(report.setupVpVersionUpdatedFileCount).toBe(2);
15211524
});
1525+
1526+
it('updates composite actions elsewhere under .github and stays within that directory', () => {
1527+
const actionPath = path.join(tmpDir, '.github', 'ci', 'actions', 'setup', 'action.yml');
1528+
const nonCompositePath = path.join(
1529+
tmpDir,
1530+
'.github',
1531+
'ci',
1532+
'actions',
1533+
'javascript',
1534+
'action.yml',
1535+
);
1536+
const outsideScopePath = path.join(tmpDir, 'ci', 'actions', 'outside-scope', 'action.yml');
1537+
const compositeAction =
1538+
'runs:\n using: composite\n steps:\n - uses: voidzero-dev/setup-vp@v1\n';
1539+
const nonCompositeAction = [
1540+
'inputs:',
1541+
' example:',
1542+
' description: Example workflow text',
1543+
' default: |',
1544+
' - uses: voidzero-dev/setup-vp@v1',
1545+
'runs:',
1546+
' using: node20',
1547+
' main: index.js',
1548+
'',
1549+
].join('\n');
1550+
1551+
fs.mkdirSync(path.dirname(actionPath), { recursive: true });
1552+
fs.mkdirSync(path.dirname(nonCompositePath), { recursive: true });
1553+
fs.mkdirSync(path.dirname(outsideScopePath), { recursive: true });
1554+
fs.writeFileSync(actionPath, compositeAction);
1555+
fs.writeFileSync(nonCompositePath, nonCompositeAction);
1556+
fs.writeFileSync(outsideScopePath, compositeAction);
1557+
const report = createMigrationReport();
1558+
1559+
const updatedFiles = migrateSetupVpVersion(tmpDir, report).map((filePath) =>
1560+
filePath.split(path.sep).join('/'),
1561+
);
1562+
1563+
expect(updatedFiles).toEqual(['.github/ci/actions/setup/action.yml']);
1564+
expect(fs.readFileSync(actionPath, 'utf8')).toContain(
1565+
`uses: voidzero-dev/setup-vp@${SETUP_VP_VERSION}`,
1566+
);
1567+
expect(fs.readFileSync(nonCompositePath, 'utf8')).toBe(nonCompositeAction);
1568+
expect(fs.readFileSync(outsideScopePath, 'utf8')).toBe(compositeAction);
1569+
expect(report.setupVpVersionUpdatedFileCount).toBe(1);
1570+
});
15221571
});
15231572

15241573
describe('detectNodeVersionManagerFile', () => {

packages/cli/src/migration/migrator/setup.ts

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import path from 'node:path';
44
import * as prompts from '@voidzero-dev/vite-plus-prompts';
55
import { globSync } from 'glob';
66
import semver from 'semver';
7+
import { parse as parseYaml } from 'yaml';
78

89
import { type DownloadPackageManagerResult } from '../../../binding/index.js';
910
import { SETUP_VP_VERSION } from '../../utils/constants.ts';
@@ -130,20 +131,47 @@ export function parseNvmrcVersion(alias: string): string | null {
130131
*/
131132
const NODE_VERSION_FILE_NVMRC_RE = /(node-version-file:[ \t]*)(['"]?)(\.\/)?\.nvmrc\2(?=\s|$)/gm;
132133

134+
function isCompositeActionFile(filePath: string): boolean {
135+
try {
136+
const action = parseYaml(fs.readFileSync(filePath, 'utf8')) as unknown;
137+
if (typeof action !== 'object' || action === null || Array.isArray(action)) {
138+
return false;
139+
}
140+
const runs = (action as Record<string, unknown>).runs;
141+
return (
142+
typeof runs === 'object' &&
143+
runs !== null &&
144+
!Array.isArray(runs) &&
145+
(runs as Record<string, unknown>).using === 'composite'
146+
);
147+
} catch {
148+
return false;
149+
}
150+
}
151+
133152
/**
134153
* Collect GitHub Actions YAML files that migration may inspect: top-level
135154
* workflows (`.github/workflows/*.{yml,yaml}`, which GitHub runs only when flat
136-
* in that directory) and composite action definitions
137-
* (`.github/actions/**\/action.{yml,yaml}`, which may nest at any depth). Returns
138-
* absolute paths; a missing `.github` tree just yields an empty list. `nocase`
139-
* keeps the match case-insensitive on case-sensitive filesystems.
155+
* in that directory) and composite action definitions anywhere beneath
156+
* `.github`. Action metadata is included only when `runs.using` is `composite`.
157+
* Returns absolute paths. `nocase` keeps filename matching case-insensitive on
158+
* case-sensitive filesystems. Recursive discovery intentionally remains scoped
159+
* to the `.github` directory.
140160
*/
141161
function collectGithubActionFiles(projectPath: string): string[] {
142-
return globSync(['workflows/*.{yml,yaml}', 'actions/**/action.{yml,yaml}'], {
162+
const options = {
143163
cwd: path.join(projectPath, '.github'),
144164
absolute: true,
145165
nocase: true,
146-
});
166+
nodir: true,
167+
} as const;
168+
const workflows = globSync('workflows/*.{yml,yaml}', options);
169+
const compositeActions = globSync('**/action.{yml,yaml}', {
170+
...options,
171+
dot: true,
172+
}).filter(isCompositeActionFile);
173+
174+
return [...new Set([...workflows, ...compositeActions])];
147175
}
148176

149177
/**

0 commit comments

Comments
 (0)