@@ -4,6 +4,7 @@ import { fileURLToPath } from "node:url";
44
55import * as esbuild from "esbuild" ;
66import { globSync } from "glob" ;
7+ import * as yaml from "js-yaml" ;
78
89import pkg from "./package.json" with { type : "json" } ;
910
@@ -27,6 +28,57 @@ const cleanPlugin = {
2728 } ,
2829} ;
2930
31+ /** A plugin that checks that the Node versions in all `action.yml` files are the same. */
32+ const checkNodeVersionsPlugin = {
33+ name : "check-node-versions" ,
34+ setup ( build ) {
35+ build . onStart ( async ( ) => {
36+ // Find all the `action.yml` files. We don't care about the stub in the repository root,
37+ // since that is a `composite` action.
38+ const actionSpecifications = globSync ( "*/action.yml" ) ;
39+
40+ // Track the Node versions we find for each file.
41+ const nodeVersions = { } ;
42+
43+ // We will store the first Node version we find and use it to compare against the others.
44+ // If there's any disagreement, we set `versionMismatch` to `true` and throw an error
45+ // that includes all the discovered Node versions at the end.
46+ let nodeVersion = undefined ;
47+ let versionMismatch = false ;
48+
49+ for ( const actionSpecification of actionSpecifications ) {
50+ // Read the contents of the action.yml file.
51+ const contents = await readFile ( actionSpecification , "utf-8" ) ;
52+ const specification = yaml . load ( contents ) ;
53+
54+ // Find the `runs.using` value in the specification.
55+ const using = specification . runs . using ;
56+ if ( using === undefined ) {
57+ throw new Error (
58+ `Couldn't find 'runs.using' in ${ actionSpecification } ` ,
59+ ) ;
60+ }
61+
62+ if ( nodeVersion === undefined ) {
63+ // First one we found: set it as the baseline.
64+ nodeVersion = using ;
65+ } else if ( nodeVersion !== using ) {
66+ // Disagreement: set `versionMismatch` to indicate that we should throw an error later.
67+ versionMismatch = true ;
68+ }
69+ nodeVersions [ actionSpecification ] = using ;
70+ }
71+
72+ // Throw an error if there was a version mismatch.
73+ if ( versionMismatch ) {
74+ throw new Error (
75+ `More than one node version used in 'action.yml' files: ${ JSON . stringify ( nodeVersions ) } ` ,
76+ ) ;
77+ }
78+ } ) ;
79+ } ,
80+ } ;
81+
3082/**
3183 * Copy defaults.json to the output directory since other projects depend on it.
3284 *
@@ -78,7 +130,7 @@ const UPLOAD_LIB_SRC = "./src/upload-lib";
78130 *
79131 * The virtual module additionally re-exports `upload-lib` under the `uploadLib` namespace so that
80132 * external consumers can access it via the small `lib/upload-lib.js` stub emitted below.
81- *
133+ *
82134 * A tiny stub file is emitted for each Action entrypoint, and one for `upload-lib`. Each stub
83135 * imports the shared bundle and calls/re-exports from the respective entry point.
84136 *
@@ -208,7 +260,13 @@ const context = await esbuild.context({
208260 outdir : OUT_DIR ,
209261 platform : "node" ,
210262 external : [ "./entry-points" ] ,
211- plugins : [ cleanPlugin , copyDefaultsPlugin , entryPointsPlugin , onEndPlugin ] ,
263+ plugins : [
264+ cleanPlugin ,
265+ checkNodeVersionsPlugin ,
266+ copyDefaultsPlugin ,
267+ entryPointsPlugin ,
268+ onEndPlugin ,
269+ ] ,
212270 target : [ "node20" ] ,
213271 define : {
214272 __CODEQL_ACTION_VERSION__ : JSON . stringify ( pkg . version ) ,
0 commit comments