Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 21 additions & 12 deletions .github/workflows/pr-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -105,42 +105,51 @@ jobs:
run: npx tsx --test

check-node-version:
if: github.triggering_actor != 'dependabot[bot]'
name: Check Action Node versions
if: github.triggering_actor != 'dependabot[bot]' && startsWith(github.head_ref, 'backport-')
name: Check Action Node versions for Backport
runs-on: ubuntu-latest
timeout-minutes: 45
timeout-minutes: 5
env:
BASE_REF: ${{ github.base_ref }}

permissions:
contents: read

steps:
- uses: actions/checkout@v6
- name: Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 1

Comment thread
mbg marked this conversation as resolved.
- id: head-version
name: Verify all Actions use the same Node version
name: Determine Node version for HEAD
run: |
NODE_VERSION=$(find . -name "action.yml" -exec yq -e '.runs.using' {} \; | grep node | sort | uniq)
echo "NODE_VERSION: ${NODE_VERSION}"
if [[ $(echo "$NODE_VERSION" | wc -l) -gt 1 ]]; then
echo "::error::More than one node version used in 'action.yml' files."
if [[ ! -f ".nvmrc" ]]; then
echo "::error::Cannot find .nvmrc in the HEAD commit."
exit 1
fi

NODE_VERSION=$(cat .nvmrc)
echo "NODE_VERSION: ${NODE_VERSION}"
echo "node_version=${NODE_VERSION}" >> $GITHUB_OUTPUT
Comment thread
mbg marked this conversation as resolved.

- id: checkout-base
name: 'Backport: Check out base ref'
if: ${{ startsWith(github.head_ref, 'backport-') }}
uses: actions/checkout@v6
with:
ref: ${{ env.BASE_REF }}
fetch-depth: 1

- name: 'Backport: Verify Node versions unchanged'
if: steps.checkout-base.outcome == 'success'
env:
HEAD_VERSION: ${{ steps.head-version.outputs.node_version }}
run: |
BASE_VERSION=$(find . -name "action.yml" -exec yq -e '.runs.using' {} \; | grep node | sort | uniq)
if [[ ! -f ".nvmrc" ]]; then
echo "::error::Cannot find .nvmrc in the base commit."
exit 1
fi

BASE_VERSION=$(cat .nvmrc)
echo "HEAD_VERSION: ${HEAD_VERSION}"
echo "BASE_VERSION: ${BASE_VERSION}"
if [[ "$BASE_VERSION" != "$HEAD_VERSION" ]]; then
Expand Down
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
24
75 changes: 73 additions & 2 deletions build.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { fileURLToPath } from "node:url";

import * as esbuild from "esbuild";
import { globSync } from "glob";
import * as yaml from "js-yaml";

import pkg from "./package.json" with { type: "json" };

Expand All @@ -27,6 +28,70 @@ const cleanPlugin = {
},
};

/** A plugin that checks that the Node versions in all `action.yml` files are the same. */
const checkNodeVersionsPlugin = {
name: "check-node-versions",
setup(build) {
build.onStart(async () => {
// Find all the `action.yml` files. We don't care about the stub in the repository root,
// since that is a `composite` action.
const actionSpecifications = globSync("*/action.yml");

Comment thread
mbg marked this conversation as resolved.
// Track the Node versions we find for each file.
const nodeVersions = {};

// We will store the first Node version we find and use it to compare against the others.
// If there's any disagreement, we set `versionMismatch` to `true` and throw an error
// that includes all the discovered Node versions at the end.
let nodeVersion = undefined;
let versionMismatch = false;

for (const actionSpecification of actionSpecifications) {
// Read the contents of the action.yml file.
const contents = await readFile(actionSpecification, "utf-8");
const specification = yaml.load(contents);

Comment thread
mbg marked this conversation as resolved.
// Find the `runs.using` value in the specification.
const using = specification.runs.using;
if (using === undefined || using === null) {
throw new Error(
`Couldn't find 'runs.using' in ${actionSpecification}`,
);
}

if (typeof using !== "string" || !using.startsWith("node")) {
throw new Error(
`Expected 'runs.using' to be a string starting with 'node' in ${actionSpecification}`,
);
}

if (nodeVersion === undefined) {
// First one we found: set it as the baseline.
nodeVersion = using;
} else if (nodeVersion !== using) {
// Disagreement: set `versionMismatch` to indicate that we should throw an error later.
versionMismatch = true;
}
nodeVersions[actionSpecification] = using;
}

// Throw an error if there was a version mismatch.
if (versionMismatch) {
throw new Error(
`More than one node version used in 'action.yml' files: ${JSON.stringify(nodeVersions)}`,
);
}

// Write the node version to `.nvmrc`.
await writeFile(
join(__dirname, ".nvmrc"),
nodeVersion.substring("node".length) + "\n",
"utf-8",
);
Comment thread
mbg marked this conversation as resolved.
});
},
};

/**
* Copy defaults.json to the output directory since other projects depend on it.
*
Expand Down Expand Up @@ -78,7 +143,7 @@ const UPLOAD_LIB_SRC = "./src/upload-lib";
*
* The virtual module additionally re-exports `upload-lib` under the `uploadLib` namespace so that
* external consumers can access it via the small `lib/upload-lib.js` stub emitted below.
*
*
* A tiny stub file is emitted for each Action entrypoint, and one for `upload-lib`. Each stub
* imports the shared bundle and calls/re-exports from the respective entry point.
*
Expand Down Expand Up @@ -208,7 +273,13 @@ const context = await esbuild.context({
outdir: OUT_DIR,
platform: "node",
external: ["./entry-points"],
plugins: [cleanPlugin, copyDefaultsPlugin, entryPointsPlugin, onEndPlugin],
plugins: [
cleanPlugin,
checkNodeVersionsPlugin,
copyDefaultsPlugin,
entryPointsPlugin,
onEndPlugin,
],
target: ["node20"],
define: {
__CODEQL_ACTION_VERSION__: JSON.stringify(pkg.version),
Expand Down
Loading