From f7f4e53584a85fd62810e999d2e7b0c5c5f0103d Mon Sep 17 00:00:00 2001 From: awalsh128 Date: Thu, 5 Nov 2026 17:31:39 -0800 Subject: [PATCH] ci: bundle the action with rollup and upload it for integration jobs The integration jobs download an action-bundle- artifact that nothing built or uploaded, so all of them failed on their first step. v2 also had no bundle: action.yml runs dist/index.js, and an action has no node_modules at runtime. - Add rollup.config.mjs, which bundles the tsc output (dist/src) into a self-contained dist/index.js. npm run build now runs it. - Fix the setup action, which never built: with if_no_artifact_found: ignore the download step always succeeds, so check found_artifact. Download cached builds into dist/, and overwrite the build artifact since several jobs in a run can build it. - Upload action.yml and the bundle as action-bundle- from setup-and-cache. Co-Authored-By: Claude Opus 5.5 --- .github/actions/setup/action.yml | 11 +- .github/workflows/ci.yml | 12 ++ package-lock.json | 204 ++++++++++++++++++++++++++++++- package.json | 6 +- rollup.config.mjs | 47 +++++++ 5 files changed, 273 insertions(+), 7 deletions(-) create mode 100644 rollup.config.mjs diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index 4ebd648..85f2239 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -27,17 +27,22 @@ runs: uses: dawidd6/action-download-artifact@b6e2e70617bc3265edd6dab6c906732b2f1ae151 # v21 with: name: ${{ env.build-artifacts-name }} + path: dist if_no_artifact_found: ignore + # With if_no_artifact_found: ignore the download step always succeeds, so + # check found_artifact instead of the step outcome. - name: Build - if: steps.try-download-artifact.outcome == 'failure' + if: steps.try-download-artifact.outputs.found_artifact != 'true' shell: bash - run: npm run build # Generates dist/ folder + run: npm run build # Generates dist/ folder, including the dist/index.js bundle - name: Upload Build Artifacts - if: steps.try-download-artifact.outcome == 'failure' + if: steps.try-download-artifact.outputs.found_artifact != 'true' uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: ${{ env.build-artifacts-name }} path: dist retention-days: 1 + # Jobs in the same run each build when no earlier run has the artifact. + overwrite: true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 602d683..1583b80 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,6 +41,18 @@ jobs: with: operation: build + # The integration jobs below run this bundle as the action (uses: ./). + - name: Upload Action Bundle + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: action-bundle-${{ github.event.pull_request.head.sha || github.sha }} + path: | + action.yml + dist/index.js + dist/index.js.map + if-no-files-found: error + retention-days: 1 + codeql: # Fork PRs do not have permission to upload security events. if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} diff --git a/package-lock.json b/package-lock.json index f06d719..f1038ad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,9 @@ "devDependencies": { "@devcontainers/cli": "^0.87.0", "@robingenz/zli": "^0.2.0", + "@rollup/plugin-commonjs": "^29.0.3", + "@rollup/plugin-json": "^6.1.0", + "@rollup/plugin-node-resolve": "^16.0.3", "@semantic-release/changelog": "^6.0.3", "@semantic-release/commit-analyzer": "^13.0.1", "@semantic-release/git": "^10.0.1", @@ -1413,6 +1416,160 @@ "dev": true, "license": "MIT" }, + "node_modules/@rollup/plugin-commonjs": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-29.0.3.tgz", + "integrity": "sha512-ZaOxZceP7SOUW7Lqw5IRVweSQYWaeIPnXIGLiB690EBA3FGJTO40EEr2L5yZplJWsgTCogILRSpcAe7+U0Otdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "commondir": "^1.0.1", + "estree-walker": "^2.0.2", + "fdir": "^6.2.0", + "is-reference": "1.2.1", + "magic-string": "^0.30.3", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=16.0.0 || 14 >= 14.17" + }, + "peerDependencies": { + "rollup": "^2.68.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-commonjs/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/plugin-commonjs/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-commonjs/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@rollup/plugin-json": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz", + "integrity": "sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.1.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.3.tgz", + "integrity": "sha512-lUYM3UBGuM93CnMPG1YocWu7X802BrNF3jW2zny5gQyLQgRFJhV1Sq0Zi74+dh/6NBx1DxFC4b4GXg9wUCG5Qg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.78.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.5.1.tgz", + "integrity": "sha512-P9ZvnfouRu4MSC+ilERaw6FmZs8zqKhpHxEs4aGDGWQAMzQGXnhvCSvoNwokfNvdt7t3Y5Y4p6gStQgezeIqug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/pluginutils/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.62.2", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz", @@ -2431,6 +2588,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/resolve": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", + "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/tar": { "version": "6.1.13", "resolved": "https://registry.npmjs.org/@types/tar/-/tar-6.1.13.tgz", @@ -3945,6 +4109,13 @@ "node": ">=12.20" } }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true, + "license": "MIT" + }, "node_modules/compare-func": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", @@ -4290,6 +4461,16 @@ "dev": true, "license": "MIT" }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -6317,6 +6498,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "dev": true, + "license": "MIT" + }, "node_modules/is-negative-zero": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", @@ -6380,6 +6568,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-reference": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, "node_modules/is-regex": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", @@ -12338,9 +12536,9 @@ } }, "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.9.1.tgz", + "integrity": "sha512-a4Ux9VKuIMHsOS7ENbEq6hK2ZGYIT27Rtl0E4eD9KqwxhKiQeMZE/9xb3IdYwWaSOyIBQLExhQgXDTYoiCxnWA==", "license": "0BSD" }, "node_modules/tsutils": { diff --git a/package.json b/package.json index 35b7abd..eba60be 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,8 @@ "runall": "node ./node_modules/npm-run-all/bin/npm-run-all/index.js", "build:lib": "tsc -p tsconfig.build.json", "build:scripts": "tsc -p tsconfig.scripts.json --noEmit", - "build": "npm run -s runall -- build:lib build:scripts", + "bundle": "rollup -c rollup.config.mjs", + "build": "npm run -s runall -- build:lib bundle build:scripts", "clean": "rm -rf dist coverage", "check:latest-action-pin": "node --experimental-strip-types ./scripts/ops/check_latest_action_pin.mts", "check:latest-action-pins": "node --experimental-strip-types ./scripts/ops/check_latest_action_pin.mts --scan", @@ -91,6 +92,9 @@ "devDependencies": { "@devcontainers/cli": "^0.87.0", "@robingenz/zli": "^0.2.0", + "@rollup/plugin-commonjs": "^29.0.3", + "@rollup/plugin-json": "^6.1.0", + "@rollup/plugin-node-resolve": "^16.0.3", "@semantic-release/changelog": "^6.0.3", "@semantic-release/commit-analyzer": "^13.0.1", "@semantic-release/git": "^10.0.1", diff --git a/rollup.config.mjs b/rollup.config.mjs new file mode 100644 index 0000000..5a314f6 --- /dev/null +++ b/rollup.config.mjs @@ -0,0 +1,47 @@ +import commonjs from "@rollup/plugin-commonjs"; +import json from "@rollup/plugin-json"; +import { nodeResolve } from "@rollup/plugin-node-resolve"; + +/** + * Bundles the action and all of its dependencies into dist/index.js, the + * entrypoint action.yml runs. GitHub Actions doesn't install node_modules, so + * the bundle has to be self-contained. + * + * The input is the tsc output from `npm run build:lib`, so TypeScript is only + * compiled one way. + * + * @type {import('rollup').RollupOptions} + */ +export default { + input: "dist/src/index.js", + output: { + file: "dist/index.js", + format: "es", + sourcemap: true, + // Some CommonJS dependencies call require() at runtime. + banner: + "import { createRequire as __createRequire } from 'node:module';\n" + + "const require = __createRequire(import.meta.url);", + }, + plugins: [ + nodeResolve({ preferBuiltins: true, exportConditions: ["node"] }), + commonjs(), + json(), + ], + onwarn(warning, warn) { + // Dependencies trigger these and they are harmless and very noisy: + // top level `this` in ES modules, misplaced /*@__PURE__*/ comments and + // circular imports inside third-party packages. + const fromDependency = (warning.ids ?? [warning.id ?? ""]).every((id) => + id.includes("node_modules"), + ); + if ( + warning.code === "THIS_IS_UNDEFINED" || + (fromDependency && + ["INVALID_ANNOTATION", "CIRCULAR_DEPENDENCY"].includes(warning.code)) + ) { + return; + } + warn(warning); + }, +};