diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index 85f2239..8af401d 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -35,7 +35,7 @@ runs: - name: Build if: steps.try-download-artifact.outputs.found_artifact != 'true' shell: bash - run: npm run build # Generates dist/ folder, including the dist/index.js bundle + run: npm run build # Generates dist/ folder, including the dist/action/index.js bundle - name: Upload Build Artifacts if: steps.try-download-artifact.outputs.found_artifact != 'true' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1583b80..7db24b1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,8 +48,8 @@ jobs: name: action-bundle-${{ github.event.pull_request.head.sha || github.sha }} path: | action.yml - dist/index.js - dist/index.js.map + dist/action/ + dist/scripts/ if-no-files-found: error retention-days: 1 diff --git a/action.yml b/action.yml index f70c6cf..5da11b0 100644 --- a/action.yml +++ b/action.yml @@ -43,4 +43,4 @@ outputs: runs: using: "node24" - main: "dist/index.js" + main: "dist/action/index.js" diff --git a/rollup.config.mjs b/rollup.config.mjs index 5a314f6..e4e4b6f 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -1,10 +1,27 @@ import commonjs from "@rollup/plugin-commonjs"; import json from "@rollup/plugin-json"; import { nodeResolve } from "@rollup/plugin-node-resolve"; +import fs from "node:fs"; + +// ts-apt runs scripts/dist/apt-fast.sh, which it finds relative to its own +// module (i.e. ../scripts/dist from the bundle). Bundling moves that module +// into dist/action/index.js, so ship the scripts at dist/scripts/dist. +const TS_APT_SCRIPTS_SRC = "node_modules/ts-apt/scripts/dist"; +const TS_APT_SCRIPTS_DEST = "dist/scripts/dist"; + +/** Copies the ts-apt shell scripts next to the bundle after it's written. */ +function copyTsAptScripts() { + return { + name: "copy-ts-apt-scripts", + writeBundle() { + fs.cpSync(TS_APT_SCRIPTS_SRC, TS_APT_SCRIPTS_DEST, { recursive: true }); + }, + }; +} /** - * 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 + * Bundles the action and all of its dependencies into dist/action/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 @@ -15,7 +32,7 @@ import { nodeResolve } from "@rollup/plugin-node-resolve"; export default { input: "dist/src/index.js", output: { - file: "dist/index.js", + file: "dist/action/index.js", format: "es", sourcemap: true, // Some CommonJS dependencies call require() at runtime. @@ -27,6 +44,7 @@ export default { nodeResolve({ preferBuiltins: true, exportConditions: ["node"] }), commonjs(), json(), + copyTsAptScripts(), ], onwarn(warning, warn) { // Dependencies trigger these and they are harmless and very noisy: diff --git a/src/index.ts b/src/index.ts index 2e02257..8a63177 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,9 +1,35 @@ import * as core from "@actions/core"; +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; import { runAction, type ActionInputs } from "./action.js"; import { DefaultCommandRunner } from "ts-apt"; import { Cache, CACHE_DEFAULT_DIRNAME } from "./cache.js"; import { Instruments } from "./instrumentation.js"; +/** + * Marks the bundled ts-apt apt-fast script executable when it's present. + * + * ts-apt spawns the script directly, and some ways of shipping the bundle + * (e.g. workflow artifacts) drop file modes. + * + * @param bundleDir Directory of the running bundle (i.e. dist/action). + */ +function ensureBundledScriptsExecutable( + bundleDir: string = path.dirname(fileURLToPath(import.meta.url)), +): void { + const aptFastPath = path.join( + bundleDir, + "..", + "scripts", + "dist", + "apt-fast.sh", + ); + if (fs.existsSync(aptFastPath)) { + fs.chmodSync(aptFastPath, 0o755); + } +} + /** * Parses empty package behavior input from workflow configuration. * @@ -51,6 +77,7 @@ async function main(): Promise { const inputs = getInputs(); const instruments = new Instruments(cacheDir, inputs.debug || core.isDebug()); try { + ensureBundledScriptsExecutable(); const commandRunner = new DefaultCommandRunner( instruments.execLogger, instruments.execLogger,