From 2da5d13516863b16bf9cc504f75a15101589c3d7 Mon Sep 17 00:00:00 2001 From: Anurag Rajawat Date: Thu, 23 Apr 2026 16:21:38 +0530 Subject: [PATCH] feat: added banner and update subscription check to make maintained actions free for public repos Signed-off-by: Anurag Rajawat --- .github/workflows/actions_release.yml | 7 +- .github/workflows/audit_package.yml | 5 + .github/workflows/auto_cherry_pick.yml | 5 + .github/workflows/check-markdown-task.yml | 2 +- .../workflows/check-npm-dependencies-task.yml | 2 +- .github/workflows/check-npm-task.yml | 2 +- .../check-packaging-ncc-typescript-task.yml | 2 +- .github/workflows/check-taskfiles.yml | 2 +- .github/workflows/check-tsconfig-task.yml | 2 +- .github/workflows/check-typescript-task.yml | 2 +- .github/workflows/test.yml | 2 +- README.md | 2 + action.yml | 2 +- dist/index.js | 270 +++++++++--------- src/main.ts | 38 ++- tsconfig.json | 2 +- 16 files changed, 191 insertions(+), 156 deletions(-) diff --git a/.github/workflows/actions_release.yml b/.github/workflows/actions_release.yml index b2c7eb2..9dd5dcd 100644 --- a/.github/workflows/actions_release.yml +++ b/.github/workflows/actions_release.yml @@ -6,6 +6,10 @@ on: tag: description: "Tag for the release" required: true + node_version: + description: "Specify Node.js version (e.g., '18', '20', 'lts/*')" + required: false + default: "24" permissions: contents: read @@ -19,4 +23,5 @@ jobs: uses: step-security/reusable-workflows/.github/workflows/actions_release.yaml@v1 with: - tag: "${{ github.event.inputs.tag }}" \ No newline at end of file + tag: "${{ github.event.inputs.tag }}" + node_version: "${{ github.event.inputs.node_version }}" \ No newline at end of file diff --git a/.github/workflows/audit_package.yml b/.github/workflows/audit_package.yml index 2cc740e..4fa5ce9 100644 --- a/.github/workflows/audit_package.yml +++ b/.github/workflows/audit_package.yml @@ -11,6 +11,10 @@ on: description: "Specify a base branch" required: false default: "main" + node_version: + description: "Specify Node.js version (e.g., '18', '20', 'lts/*')" + required: false + default: "24" schedule: - cron: "0 0 * * 1" @@ -20,6 +24,7 @@ jobs: with: force: ${{ inputs.force || false }} base_branch: ${{ inputs.base_branch || 'main' }} + node_version: "${{ inputs.node_version || '24' }}" permissions: contents: write diff --git a/.github/workflows/auto_cherry_pick.yml b/.github/workflows/auto_cherry_pick.yml index 3baa3d1..ac82ae0 100644 --- a/.github/workflows/auto_cherry_pick.yml +++ b/.github/workflows/auto_cherry_pick.yml @@ -7,6 +7,10 @@ on: description: "Base branch to create the PR against" required: true default: "main" + node_version: + description: "Specify Node.js version (e.g., '18', '20', 'lts/*')" + required: false + default: "24" permissions: contents: write @@ -21,3 +25,4 @@ jobs: original-owner: "arduino" repo-name: "setup-protoc" base_branch: ${{ inputs.base_branch }} + node_version: "${{ inputs.node_version || '24' }}" diff --git a/.github/workflows/check-markdown-task.yml b/.github/workflows/check-markdown-task.yml index a15b08c..924b029 100644 --- a/.github/workflows/check-markdown-task.yml +++ b/.github/workflows/check-markdown-task.yml @@ -2,7 +2,7 @@ name: Check Markdown env: # See: https://github.com/actions/setup-node/#readme - NODE_VERSION: 20.x + NODE_VERSION: 24.x # See: https://docs.github.com/actions/using-workflows/events-that-trigger-workflows on: diff --git a/.github/workflows/check-npm-dependencies-task.yml b/.github/workflows/check-npm-dependencies-task.yml index 539d654..0d377ca 100644 --- a/.github/workflows/check-npm-dependencies-task.yml +++ b/.github/workflows/check-npm-dependencies-task.yml @@ -2,7 +2,7 @@ name: Check npm Dependencies env: # See: https://github.com/actions/setup-node/#readme - NODE_VERSION: 20.x + NODE_VERSION: 24.x # See: https://docs.github.com/actions/using-workflows/events-that-trigger-workflows on: diff --git a/.github/workflows/check-npm-task.yml b/.github/workflows/check-npm-task.yml index 1a2df85..3ebe2be 100644 --- a/.github/workflows/check-npm-task.yml +++ b/.github/workflows/check-npm-task.yml @@ -2,7 +2,7 @@ name: Check npm env: # See: https://github.com/actions/setup-node/#readme - NODE_VERSION: 20.x + NODE_VERSION: 24.x # See: https://docs.github.com/en/actions/reference/events-that-trigger-workflows on: diff --git a/.github/workflows/check-packaging-ncc-typescript-task.yml b/.github/workflows/check-packaging-ncc-typescript-task.yml index 2828958..d5ec0c1 100644 --- a/.github/workflows/check-packaging-ncc-typescript-task.yml +++ b/.github/workflows/check-packaging-ncc-typescript-task.yml @@ -2,7 +2,7 @@ name: Check Packaging env: # See: https://github.com/actions/setup-node/#readme - NODE_VERSION: 20.x + NODE_VERSION: 24.x on: push: diff --git a/.github/workflows/check-taskfiles.yml b/.github/workflows/check-taskfiles.yml index 54ef160..63f0990 100644 --- a/.github/workflows/check-taskfiles.yml +++ b/.github/workflows/check-taskfiles.yml @@ -2,7 +2,7 @@ name: Check Taskfiles env: # See: https://github.com/actions/setup-node/#readme - NODE_VERSION: 20.x + NODE_VERSION: 24.x # See: https://docs.github.com/actions/using-workflows/events-that-trigger-workflows on: diff --git a/.github/workflows/check-tsconfig-task.yml b/.github/workflows/check-tsconfig-task.yml index afe7fcb..96addf6 100644 --- a/.github/workflows/check-tsconfig-task.yml +++ b/.github/workflows/check-tsconfig-task.yml @@ -2,7 +2,7 @@ name: Check TypeScript Configuration env: # See: https://github.com/actions/setup-node/#readme - NODE_VERSION: 20.x + NODE_VERSION: 24.x # See: https://docs.github.com/en/actions/reference/events-that-trigger-workflows on: diff --git a/.github/workflows/check-typescript-task.yml b/.github/workflows/check-typescript-task.yml index e2b0ef8..9b4fe85 100644 --- a/.github/workflows/check-typescript-task.yml +++ b/.github/workflows/check-typescript-task.yml @@ -2,7 +2,7 @@ name: Check TypeScript env: # See: https://github.com/actions/setup-node/#readme - NODE_VERSION: 20.x + NODE_VERSION: 24.x # See: https://docs.github.com/en/actions/reference/events-that-trigger-workflows on: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0ca596c..a630a3a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,7 +26,7 @@ jobs: - name: Set Node.js 20.x uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: - node-version: 20.x + node-version: 24.x - name: npm install run: npm install diff --git a/README.md b/README.md index d3f2493..bcdb709 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +[![StepSecurity Maintained Action](https://raw.githubusercontent.com/step-security/maintained-actions-assets/main/assets/maintained-action-banner.png)](https://docs.stepsecurity.io/actions/stepsecurity-maintained-actions) + # setup-protoc [![Check npm Dependencies status](https://github.com/step-security/setup-protoc/actions/workflows/check-npm-dependencies-task.yml/badge.svg)](https://github.com/step-security/setup-protoc/actions/workflows/check-npm-dependencies-task.yml) diff --git a/action.yml b/action.yml index 6d5034a..af7ea00 100644 --- a/action.yml +++ b/action.yml @@ -17,7 +17,7 @@ outputs: path: description: 'Path to where the protoc compiler has been installed' runs: - using: 'node20' + using: 'node24' main: 'dist/index.js' branding: icon: 'box' diff --git a/dist/index.js b/dist/index.js index e8a935e..0ae8500 100644 --- a/dist/index.js +++ b/dist/index.js @@ -29,15 +29,6 @@ var __importStar = (this && this.__importStar) || function (mod) { __setModuleDefault(result, mod); return result; }; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.getFileName = exports.getProtoc = void 0; // Load tempDirectory before it gets wiped by tool-cache @@ -69,52 +60,48 @@ const osPlat = os.platform(); const osArch = os.arch(); // This regex is slighty modified from https://semver.org/ to allow only MINOR.PATCH notation. const semverRegex = /^(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/gm; -function getProtoc(version, includePreReleases, repoToken) { - return __awaiter(this, void 0, void 0, function* () { - // resolve the version number - const targetVersion = yield computeVersion(version, includePreReleases, repoToken); - if (targetVersion) { - version = targetVersion; - } - process.stdout.write("Getting protoc version: " + version + os.EOL); - // look if the binary is cached - let toolPath; - toolPath = tc.find("protoc", version); - // if not: download, extract and cache - if (!toolPath) { - toolPath = yield downloadRelease(version); - process.stdout.write("Protoc cached under " + toolPath + os.EOL); - } - // expose outputs - core.setOutput("path", toolPath); - core.setOutput("version", targetVersion); - // add the bin folder to the PATH - core.addPath(path.join(toolPath, "bin")); - }); +async function getProtoc(version, includePreReleases, repoToken) { + // resolve the version number + const targetVersion = await computeVersion(version, includePreReleases, repoToken); + if (targetVersion) { + version = targetVersion; + } + process.stdout.write("Getting protoc version: " + version + os.EOL); + // look if the binary is cached + let toolPath; + toolPath = tc.find("protoc", version); + // if not: download, extract and cache + if (!toolPath) { + toolPath = await downloadRelease(version); + process.stdout.write("Protoc cached under " + toolPath + os.EOL); + } + // expose outputs + core.setOutput("path", toolPath); + core.setOutput("version", targetVersion); + // add the bin folder to the PATH + core.addPath(path.join(toolPath, "bin")); } exports.getProtoc = getProtoc; -function downloadRelease(version) { - return __awaiter(this, void 0, void 0, function* () { - // Download - const fileName = getFileName(version, osPlat, osArch); - const downloadUrl = util.format("https://github.com/protocolbuffers/protobuf/releases/download/%s/%s", version, fileName); - process.stdout.write("Downloading archive: " + downloadUrl + os.EOL); - let downloadPath = null; - try { - downloadPath = yield tc.downloadTool(downloadUrl); - } - catch (err) { - if (err instanceof tc.HTTPError) { - core.debug(err.message); - throw new Error(`Failed to download version ${version}: ${err.name}, ${err.message} - ${err.httpStatusCode}`); - } - throw new Error(`Failed to download version ${version}: ${err}`); +async function downloadRelease(version) { + // Download + const fileName = getFileName(version, osPlat, osArch); + const downloadUrl = util.format("https://github.com/protocolbuffers/protobuf/releases/download/%s/%s", version, fileName); + process.stdout.write("Downloading archive: " + downloadUrl + os.EOL); + let downloadPath = null; + try { + downloadPath = await tc.downloadTool(downloadUrl); + } + catch (err) { + if (err instanceof tc.HTTPError) { + core.debug(err.message); + throw new Error(`Failed to download version ${version}: ${err.name}, ${err.message} - ${err.httpStatusCode}`); } - // Extract - const extPath = yield tc.extractZip(downloadPath); - // Install into the local tool cache - node extracts with a root folder that matches the fileName downloaded - return tc.cacheDir(extPath, "protoc", version); - }); + throw new Error(`Failed to download version ${version}: ${err}`); + } + // Extract + const extPath = await tc.extractZip(downloadPath); + // Install into the local tool cache - node extracts with a root folder that matches the fileName downloaded + return tc.cacheDir(extPath, "protoc", version); } /** * @@ -174,61 +161,57 @@ function getFileName(version, osPlatf, osArc) { } exports.getFileName = getFileName; // Retrieve a list of versions scraping tags from the Github API -function fetchVersions(includePreReleases, repoToken) { - return __awaiter(this, void 0, void 0, function* () { - let rest; - if (repoToken != "") { - rest = new restm.RestClient("setup-protoc", "", [], { - headers: { Authorization: "Bearer " + repoToken }, - }); +async function fetchVersions(includePreReleases, repoToken) { + let rest; + if (repoToken != "") { + rest = new restm.RestClient("setup-protoc", "", [], { + headers: { Authorization: "Bearer " + repoToken }, + }); + } + else { + rest = new restm.RestClient("setup-protoc"); + } + let tags = []; + for (let pageNum = 1, morePages = true; morePages; pageNum++) { + const p = await rest.get("https://api.github.com/repos/protocolbuffers/protobuf/releases?page=" + + pageNum); + const nextPage = p.result || []; + if (nextPage.length > 0) { + tags = tags.concat(nextPage); } else { - rest = new restm.RestClient("setup-protoc"); - } - let tags = []; - for (let pageNum = 1, morePages = true; morePages; pageNum++) { - const p = yield rest.get("https://api.github.com/repos/protocolbuffers/protobuf/releases?page=" + - pageNum); - const nextPage = p.result || []; - if (nextPage.length > 0) { - tags = tags.concat(nextPage); - } - else { - morePages = false; - } + morePages = false; } - return tags - .filter((tag) => tag.tag_name.match(/v\d+\.[\w.]+/g)) - .filter((tag) => includePrerelease(tag.prerelease, includePreReleases)) - .map((tag) => tag.tag_name.replace("v", "")); - }); + } + return tags + .filter((tag) => tag.tag_name.match(/v\d+\.[\w.]+/g)) + .filter((tag) => includePrerelease(tag.prerelease, includePreReleases)) + .map((tag) => tag.tag_name.replace("v", "")); } // Compute an actual version starting from the `version` configuration param. -function computeVersion(version, includePreReleases, repoToken) { - return __awaiter(this, void 0, void 0, function* () { - // strip leading `v` char (will be re-added later) - if (version.startsWith("v")) { - version = version.slice(1, version.length); - } - // strip trailing .x chars - if (version.endsWith(".x")) { - version = version.slice(0, version.length - 2); - } - const allVersions = yield fetchVersions(includePreReleases, repoToken); - const validVersions = allVersions.filter((v) => v.match(semverRegex)); - const possibleVersions = validVersions.filter((v) => v.startsWith(version)); - const versionMap = new Map(); - possibleVersions.forEach((v) => versionMap.set(normalizeVersion(v), v)); - const versions = Array.from(versionMap.keys()) - .sort(semver.rcompare) - .map((v) => versionMap.get(v)); - core.debug(`evaluating ${versions.length} versions`); - if (versions.length === 0) { - throw new Error("unable to get latest version"); - } - core.debug(`matched: ${versions[0]}`); - return "v" + versions[0]; - }); +async function computeVersion(version, includePreReleases, repoToken) { + // strip leading `v` char (will be re-added later) + if (version.startsWith("v")) { + version = version.slice(1, version.length); + } + // strip trailing .x chars + if (version.endsWith(".x")) { + version = version.slice(0, version.length - 2); + } + const allVersions = await fetchVersions(includePreReleases, repoToken); + const validVersions = allVersions.filter((v) => v.match(semverRegex)); + const possibleVersions = validVersions.filter((v) => v.startsWith(version)); + const versionMap = new Map(); + possibleVersions.forEach((v) => versionMap.set(normalizeVersion(v), v)); + const versions = Array.from(versionMap.keys()) + .sort(semver.rcompare) + .map((v) => versionMap.get(v)); + core.debug(`evaluating ${versions.length} versions`); + if (versions.length === 0) { + throw new Error("unable to get latest version"); + } + core.debug(`matched: ${versions[0]}`); + return "v" + versions[0]; } // Make partial versions semver compliant. function normalizeVersion(version) { @@ -290,50 +273,57 @@ var __importStar = (this && this.__importStar) || function (mod) { __setModuleDefault(result, mod); return result; }; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", ({ value: true })); const core = __importStar(__nccwpck_require__(7484)); const installer = __importStar(__nccwpck_require__(8328)); +const fs = __importStar(__nccwpck_require__(9896)); const axios_1 = __importStar(__nccwpck_require__(7269)); -function validateSubscription() { - var _a; - return __awaiter(this, void 0, void 0, function* () { - const API_URL = `https://agent.api.stepsecurity.io/v1/github/${process.env.GITHUB_REPOSITORY}/actions/subscription`; - try { - yield axios_1.default.get(API_URL, { timeout: 3000 }); - } - catch (error) { - if ((0, axios_1.isAxiosError)(error) && ((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) === 403) { - core.error("Subscription is not valid. Reach out to support@stepsecurity.io"); - process.exit(1); - } - else { - core.info("Timeout or API not reachable. Continuing to next step."); - } +async function validateSubscription() { + const eventPath = process.env.GITHUB_EVENT_PATH; + let repoPrivate; + if (eventPath && fs.existsSync(eventPath)) { + const eventData = JSON.parse(fs.readFileSync(eventPath, "utf8")); + repoPrivate = eventData?.repository?.private; + } + const upstream = "arduino/setup-protoc"; + const action = process.env.GITHUB_ACTION_REPOSITORY; + const docsUrl = "https://docs.stepsecurity.io/actions/stepsecurity-maintained-actions"; + core.info(""); + core.info("StepSecurity Maintained Action"); + core.info(`Secure drop-in replacement for ${upstream}`); + if (repoPrivate === false) + core.info("✓ Free for public repositories"); + core.info(`Learn more: ${docsUrl}`); + core.info(""); + if (repoPrivate === false) + return; + const serverUrl = process.env.GITHUB_SERVER_URL || "https://github.com"; + const body = { action: action || "" }; + if (serverUrl !== "https://github.com") + body.ghes_server = serverUrl; + try { + await axios_1.default.post(`https://agent.api.stepsecurity.io/v1/github/${process.env.GITHUB_REPOSITORY}/actions/maintained-actions-subscription`, body, { timeout: 3000 }); + } + catch (error) { + if ((0, axios_1.isAxiosError)(error) && error.response?.status === 403) { + core.error(`This action requires a StepSecurity subscription for private repositories.`); + core.error(`Learn how to enable a subscription: ${docsUrl}`); + process.exit(1); } - }); + core.info("Timeout or API not reachable. Continuing to next step."); + } } -function run() { - return __awaiter(this, void 0, void 0, function* () { - try { - yield validateSubscription(); - const version = core.getInput("version"); - const includePreReleases = convertToBoolean(core.getInput("include-pre-releases")); - const repoToken = core.getInput("repo-token"); - yield installer.getProtoc(version, includePreReleases, repoToken); - } - catch (error) { - core.setFailed(`${error}`); - } - }); +async function run() { + try { + await validateSubscription(); + const version = core.getInput("version"); + const includePreReleases = convertToBoolean(core.getInput("include-pre-releases")); + const repoToken = core.getInput("repo-token"); + await installer.getProtoc(version, includePreReleases, repoToken); + } + catch (error) { + core.setFailed(`${error}`); + } } run(); function convertToBoolean(input) { diff --git a/src/main.ts b/src/main.ts index dce156b..2fa5509 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,21 +1,49 @@ import * as core from "@actions/core"; import * as installer from "./installer"; +import * as fs from "fs"; import axios, { isAxiosError } from "axios"; async function validateSubscription(): Promise { - const API_URL = `https://agent.api.stepsecurity.io/v1/github/${process.env.GITHUB_REPOSITORY}/actions/subscription`; + const eventPath = process.env.GITHUB_EVENT_PATH; + let repoPrivate: boolean | undefined; + if (eventPath && fs.existsSync(eventPath)) { + const eventData = JSON.parse(fs.readFileSync(eventPath, "utf8")); + repoPrivate = eventData?.repository?.private; + } + + const upstream = "arduino/setup-protoc"; + const action = process.env.GITHUB_ACTION_REPOSITORY; + const docsUrl = + "https://docs.stepsecurity.io/actions/stepsecurity-maintained-actions"; + + core.info(""); + core.info("StepSecurity Maintained Action"); + core.info(`Secure drop-in replacement for ${upstream}`); + if (repoPrivate === false) core.info("✓ Free for public repositories"); + core.info(`Learn more: ${docsUrl}`); + core.info(""); + + if (repoPrivate === false) return; + + const serverUrl = process.env.GITHUB_SERVER_URL || "https://github.com"; + const body: Record = { action: action || "" }; + if (serverUrl !== "https://github.com") body.ghes_server = serverUrl; try { - await axios.get(API_URL, { timeout: 3000 }); + await axios.post( + `https://agent.api.stepsecurity.io/v1/github/${process.env.GITHUB_REPOSITORY}/actions/maintained-actions-subscription`, + body, + { timeout: 3000 }, + ); } catch (error) { if (isAxiosError(error) && error.response?.status === 403) { core.error( - "Subscription is not valid. Reach out to support@stepsecurity.io", + `This action requires a StepSecurity subscription for private repositories.`, ); + core.error(`Learn how to enable a subscription: ${docsUrl}`); process.exit(1); - } else { - core.info("Timeout or API not reachable. Continuing to next step."); } + core.info("Timeout or API not reachable. Continuing to next step."); } } diff --git a/tsconfig.json b/tsconfig.json index 74870b0..f533515 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { /* Basic Options */ // "incremental": true, /* Enable incremental compilation */ - "target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ + "target": "ES2022", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ // "allowJs": true, /* Allow javascript files to be compiled. */ // "checkJs": true, /* Report errors in .js files. */