[WIP] feat!: upgrade to Node.js 24, refresh security dependencies, SHA-pin GitHub Actions, and make backports strategy-independent and conflict-safe - #3
Draft
chandler-solo wants to merge 2 commits into
Conversation
…s, tooling, workflows, and security automation ## Summary This PR upgrades the action from the deprecated Node.js 16 runtime to Node.js 24 and comprehensively modernizes its runtime dependencies, development toolchain, generated distribution, tests, and GitHub Actions workflows. The upgrade also removes deprecated packages, resolves all currently reported npm security vulnerabilities, and adds automated tracking for future GitHub Actions dependency updates. ## Motivation The action currently declares `runs.using: node16`, which is end-of-life and no longer supported by current GitHub Actions runners. The repository also relies on substantially outdated versions of the GitHub Actions Toolkit, TypeScript, Jest, ESLint, Prettier, and associated tooling. Several transitive dependencies in the old lockfile had known security advisories. This update moves the project onto the supported Node.js 24 runtime and refreshes the complete dependency graph. ## Changes ### Node.js 24 runtime - Changed the action runtime from `node16` to `node24`. - Added `engines.node: ">=24"` to `package.json`. - Updated CI and release workflows to install and test with Node.js 24 explicitly. - Enabled `FORCE_JAVASCRIPT_ACTIONS_TO_NODE24` in workflows that still invoke pinned or legacy third-party JavaScript actions. - Updated README examples to use `actions/checkout@v6`. ### Runtime dependency upgrades Updated the GitHub Actions Toolkit packages: - `@actions/core`: `^1.9.0` → `^3.0.1` - `@actions/exec`: `^1.1.1` → `^3.0.0` - `@actions/github`: `^5.1.1` → `^9.1.1` - `@actions/io`: `^1.1.2` → `^3.0.2` The current Toolkit releases are ESM-only, so the TypeScript build configuration now preserves ES modules for NCC to bundle correctly. ### Octokit type migration - Removed deprecated `@octokit/webhooks-definitions`. - Added supported `@octokit/webhooks-types`. - Converted webhook type imports to type-only imports. - Normalized nullable pull request bodies to `undefined` when constructing the Octokit request. ### Development toolchain upgrades Updated the development stack: - Node.js types: Node 18 → Node 24 - TypeScript: `4.9` → `6.0` - Jest: `26` → `30` - ts-jest: `26` → `29` - ESLint: `8` → `9` - TypeScript ESLint: `5` → `8` - Prettier: `2` → `3` - `@vercel/ncc`: `0.36` → `0.44` - `eslint-plugin-github`: `4` → `6` Unused direct dependencies such as `jest-circus`, `js-yaml`, and `eslint-plugin-jest` were removed where their functionality is now built in or no longer required. ### TypeScript and bundling configuration Updated the TypeScript compiler configuration to: - Target ES2022. - Preserve ES modules with `module: "ESNext"`. - Use `moduleResolution: "Bundler"`. This is required to bundle the ESM-only Actions Toolkit packages. Compiling them as CommonJS would otherwise produce unresolved runtime module references. The checked-in `dist/index.js` bundle was regenerated with NCC and verified to contain the runtime dependencies without missing-module stubs. The generated bundle is larger than the previous artifact because the complete modern ESM-based Actions Toolkit and Octokit dependency graph is now embedded in the standalone action bundle. ### ESLint migration - Replaced the legacy `.eslintrc.json` configuration with `eslint.config.mjs`. - Removed the obsolete `.eslintignore`. - Migrated existing GitHub and TypeScript linting rules to ESLint’s flat configuration format. - Preserved the existing ignored paths and project-aware TypeScript linting. - Removed rules that no longer exist in current TypeScript ESLint releases. - Cleaned up an unused constant and catch binding exposed by the upgraded lint configuration. ### Jest 30 compatibility - Removed the explicit `jest-circus` runner configuration because it is now Jest’s default. - Imported Jest globals explicitly from `@jest/globals`. - Updated removed matcher aliases such as `toBeCalledTimes`. - Added typed virtual mocks for the ESM-only Actions Toolkit packages. - Added the missing `@actions/io` mock. - Updated the pull request fixture with the `head.ref` property used by the implementation. - Corrected the expected number of Git executions to match the implementation. ### Workflow modernization #### Build workflow - Upgraded `actions/checkout` from v2 to v6. - Added `actions/setup-node@v6`. - Configured Node.js 24 and npm caching. - Replaced `npm install` with deterministic `npm ci`. #### Publish workflow - Upgraded `actions/checkout` and `actions/setup-node` to v6. - Configured Node.js 24 and npm caching. - Forced JavaScript actions in the job to execute using Node.js 24. #### Dependabot labeling - Replaced the third-party `actions-ecosystem/action-add-labels` action with the official `actions/github-script@v9`. - Added explicit least-privilege permissions: - `issues: write` - `pull-requests: read` - Preserved automatic application of the `maintenance` label to Dependabot pull requests. #### Release management - Upgraded Release Drafter from v5 to v7. - Forced the pinned label-verification action to execute under Node.js 24. #### Dependency automation - Retained daily npm dependency checks. - Added weekly Dependabot checks for GitHub Actions dependencies. ## Security impact The dependency lockfile was completely regenerated against the upgraded package set. Audit results: - Production dependencies: **0 vulnerabilities** - Complete dependency graph: **0 vulnerabilities** The update also removes the deprecated Octokit webhook definitions package and eliminates an unnecessary third-party labeling action. ## Compatibility and operational notes - Consumers must use a GitHub-hosted runner or a sufficiently recent self-hosted Actions runner with Node.js 24 action support. - Local development now requires Node.js 24 or newer. - No action inputs or outputs were added, removed, or renamed. - The action remains distributed as a self-contained CommonJS `dist/index.js` bundle, despite its dependencies being authored as ESM. - Existing cherry-pick and pull-request creation behavior is preserved. ## Validation The following checks completed successfully: - Clean dependency installation with `npm ci` - Full repository validation with `npm run all` - Jest unit tests - Prettier formatting - ESLint - TypeScript compilation - NCC packaging - Jest tests executed explicitly under Node.js 24 - TypeScript type-check executed explicitly under Node.js 24 - Standalone `dist/index.js` load under Node.js 24 - Verification that the bundle contains no unresolved Actions Toolkit module stubs - Production-only `npm audit` - Full `npm audit` - GitHub Actions and action metadata YAML parsing - `git diff --check` All tests pass and both npm audits report zero vulnerabilities. ## Reviewer checklist - [ ] Confirm the Node.js 24 runtime requirement is acceptable for supported runners. - [ ] Review the major GitHub Actions Toolkit and Octokit upgrades. - [ ] Review the ESLint flat-config migration. - [ ] Confirm workflow permission changes remain appropriately scoped. - [ ] Confirm the regenerated `dist/index.js` artifact is included in the merge. - [ ] Confirm the increased bundle size is acceptable. Signed-off-by: David L. Chandler <david.chandler@solo.io>
chandler-solo
force-pushed
the
chandler/node24
branch
from
August 6, 2026 19:44
d82f6f3 to
575fa55
Compare
…GitHub Actions, and make backports strategy-independent and conflict-safe
## Summary
This PR modernizes and secures the GitHub Cherry Pick Action by upgrading its runtime to Node.js 24, refreshing its dependency stack, pinning every GitHub Actions dependency to an immutable commit SHA, and correcting several issues discovered during a repository-wide code review.
The backport implementation has also been redesigned so it reliably handles pull requests merged through merge commits, squash merges, or rebase merges. Conflicting or empty backports now fail safely without committing unresolved files or pushing partial results.
## Motivation
The existing implementation had several correctness and security problems:
- The action attempted to pass `|| true` as arguments to `git cherry-pick`; `@actions/exec` does not invoke a shell, so these were treated as invalid Git arguments.
- The generated branch name included the complete author identity, producing invalid branch names containing spaces and angle brackets.
- Backporting `merge_commit_sha` did not work consistently across GitHub merge strategies.
- Cherry-pick failures were ignored, after which every working-tree file was staged and committed. This could commit unresolved conflict markers.
- Author and committer fields were crossed, and some configured identity fields were ignored.
- The action token was used for GitHub API calls but not Git fetch and push operations.
- The `team-reviewers` metadata name did not match the input name read by the implementation.
- Workflow permissions were incomplete.
- The Dependabot labeling workflow could not reliably write labels when triggered by Dependabot pull requests.
- Tests mocked every Git command as successful and did not verify the generated commands.
- CI rebuilt the bundled action but did not detect an uncommitted `dist/index.js`.
- Several metadata and README examples were incomplete or inaccurate.
## Runtime and dependency modernization
- Upgrade the action runtime in `action.yml` to Node.js 24.
- Require Node.js 24 or newer through the package engine declaration.
- Configure CI and publishing workflows to use Node.js 24.
- Refresh production and development dependencies to current compatible releases.
- Keep Node type definitions aligned with the Node.js 24 runtime.
- Retain the latest compatible TypeScript and ESLint versions where newer majors are not yet supported by the surrounding toolchain.
- Regenerate `package-lock.json`.
- Rebuild the checked-in `dist/index.js` bundle.
## Backport correctness
- Require a merged pull-request event before attempting a backport.
- Validate the source, target, and generated branch names with `git check-ref-format`.
- Replace the invalid author-based default branch with:
`cherry-pick-{sanitized target branch}-{source PR number}`
- Fetch the target branch, source base branch, and pull-request head explicitly.
- Detect shallow checkouts and unshallow the repository when required.
- Generate the complete binary pull-request diff from the event’s base SHA to the pull-request head.
- Apply the diff with `git apply --3way --index`.
- Preserve binary changes and full object identifiers in generated patches.
- Refuse to continue when the pull request is empty, already applied, or cannot be merged cleanly.
- Create one non-interactive backport commit containing the complete pull-request change set.
- Use the configured author name and email as the Git author.
- Use the configured committer name and email as the Git committer.
- Use `--force-with-lease` instead of an unconditional force push.
- Fetch an existing backport branch before using force-with-lease.
This makes the backport behavior independent of whether the source PR was merged with a merge commit, squash merge, or rebase merge.
## Authentication and API handling
- Use the action’s `token` input for both Git operations and GitHub API requests.
- Mark the token and derived Git authorization values as secrets.
- Store Git identity and authentication configuration locally instead of modifying global runner configuration.
- Correctly read the hyphenated `team-reviewers` input.
- Parse boolean inputs through the Actions toolkit’s strict boolean parser.
- Reject malformed repository context instead of returning an undefined pull-request response.
- Replace every title and body template placeholder, not only the first occurrence.
- Deduplicate inherited and explicitly configured labels without mutating input arrays.
- Skip the source PR author when requesting reviewers.
- Combine user and team reviewer requests into one API operation.
- Avoid logging untrusted pull-request titles and bodies.
## Workflow and supply-chain hardening
- Pin every GitHub Action used by repository workflows to a full 40-character commit SHA.
- Retain version comments beside SHA pins for maintainability.
- Add explicit read-only permissions to the test workflow.
- Add the required `contents: write` permission to the publishing workflow.
- Add explicit label and pull-request read permissions to the label verification workflow.
- Remove the Dependabot autolabel workflow, whose token could be read-only for Dependabot-authored pull requests.
- Configure Dependabot itself to apply the `maintenance` label.
- Add a generated-bundle check so CI detects when `dist/index.js` is not committed after a source change.
- Ensure lint and aggregate verification scripts do not silently rewrite repository files.
## Metadata and documentation
- Declare the action’s `data`, `number`, and `html_url` outputs.
- Correct the inverted `inherit_labels` description.
- Document the new conflict-safe aggregate-diff behavior.
- Document required workflow permissions.
- Document token forwarding for Git and API operations.
- Correct the output example’s YAML structure.
- Document fork and `pull_request_target` security considerations.
- Explain the approval behavior of pull requests created with `GITHUB_TOKEN`.
- Correct package name, description, keywords, and repository metadata.
## Test coverage
The test suite now verifies:
- Exact Git argument ordering
- Valid default branch generation
- Custom branch handling
- Hyphenated team-reviewer input handling
- Force-with-lease behavior
- Patch data being forwarded through standard input
- Correct author and committer identities
- Safe failure when a patch cannot be applied
- Refusal to process unmerged pull requests
- Strict boolean parsing
- Array input parsing
- Branch-component sanitization
- Author identity validation
- Pull-request title and body template replacement
- Label inheritance and deduplication
- Input immutability
- Assignee handling
- Reviewer filtering
- Invalid repository context handling
## Breaking and operational changes
- Node.js 24 is now required.
- The default backport branch naming convention has changed.
- Backport conflicts now fail the action instead of creating a potentially invalid pull request.
- `force: true` now uses force-with-lease.
- Workflows using the default token must grant `contents: write`, `pull-requests: write`, and `issues: write` when all optional PR configuration features are used.
- Consumers requiring automatically started downstream workflows may need a GitHub App token or personal access token.
## Validation
Validated under Node.js `v24.19.0`:
- `npm ci`
- 3 test suites and 12 tests passing
- TypeScript type-check
- ESLint, including source and tests
- Prettier format check
- NCC production bundle generation
- Bundled JavaScript syntax check
- GitHub Actions workflow validation with actionlint
- Complete dependency-tree validation
- Full and production-only npm security audits
- `git diff --check`
Both npm audits report zero known vulnerabilities.
Signed-off-by: David L. Chandler <david.chandler@solo.io>
chandler-solo
force-pushed
the
chandler/node24
branch
from
August 7, 2026 19:14
222dae2 to
7b5d47c
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR modernizes and secures the GitHub Cherry Pick Action by upgrading its runtime to Node.js 24, refreshing its dependency stack, pinning every GitHub Actions dependency to an immutable commit SHA, and correcting several issues discovered during a repository-wide code review.
The backport implementation has also been redesigned so it reliably handles pull requests merged through merge commits, squash merges, or rebase merges. Conflicting or empty backports now fail safely without committing unresolved files or pushing partial results.
Motivation
The existing implementation had several correctness and security problems:
|| trueas arguments togit cherry-pick;@actions/execdoes not invoke a shell, so these were treated as invalid Git arguments.merge_commit_shadid not work consistently across GitHub merge strategies.team-reviewersmetadata name did not match the input name read by the implementation.dist/index.js.Runtime and dependency modernization
action.ymlto Node.js 24.package-lock.json.dist/index.jsbundle.Backport correctness
Require a merged pull-request event before attempting a backport.
Validate the source, target, and generated branch names with
git check-ref-format.Replace the invalid author-based default branch with:
cherry-pick-{sanitized target branch}-{source PR number}Fetch the target branch, source base branch, and pull-request head explicitly.
Detect shallow checkouts and unshallow the repository when required.
Generate the complete binary pull-request diff from the event’s base SHA to the pull-request head.
Apply the diff with
git apply --3way --index.Preserve binary changes and full object identifiers in generated patches.
Refuse to continue when the pull request is empty, already applied, or cannot be merged cleanly.
Create one non-interactive backport commit containing the complete pull-request change set.
Use the configured author name and email as the Git author.
Use the configured committer name and email as the Git committer.
Use
--force-with-leaseinstead of an unconditional force push.Fetch an existing backport branch before using force-with-lease.
This makes the backport behavior independent of whether the source PR was merged with a merge commit, squash merge, or rebase merge.
Authentication and API handling
tokeninput for both Git operations and GitHub API requests.team-reviewersinput.Workflow and supply-chain hardening
contents: writepermission to the publishing workflow.maintenancelabel.dist/index.jsis not committed after a source change.Metadata and documentation
data,number, andhtml_urloutputs.inherit_labelsdescription.pull_request_targetsecurity considerations.GITHUB_TOKEN.Test coverage
The test suite now verifies:
Breaking and operational changes
force: truenow uses force-with-lease.contents: write,pull-requests: write, andissues: writewhen all optional PR configuration features are used.Validation
Validated under Node.js
v24.19.0:npm cigit diff --checkBoth npm audits report zero known vulnerabilities.