Skip to content

[INL-3675] feat: add dev release workflow for PR-based npm publishing#4233

Open
Jack Waller (Jack-Waller) wants to merge 3 commits intomainfrom
feature/INL-3675-add-dev-release-workflow
Open

[INL-3675] feat: add dev release workflow for PR-based npm publishing#4233
Jack Waller (Jack-Waller) wants to merge 3 commits intomainfrom
feature/INL-3675-add-dev-release-workflow

Conversation

@Jack-Waller
Copy link
Member

@Jack-Waller Jack Waller (Jack-Waller) commented Feb 12, 2026

What

Adds a workflow_dispatch dev release workflow that publishes pre-release npm packages from open pull requests. This enables contributors to test unreleased changes in downstream projects before merging.

Took heavy inspiration from Global Components' dev release workflow and backpack's release workflow.

New files

  • .github/workflows/dev-release.yml — Orchestrates the full pipeline: security validation, build, npm publish, and PR comment reporting.
  • .github/actions/pr-security-validation/action.yml — Composite action that validates the PR is open, from the same repository (not a fork), and the triggering user has write access.
  • .github/actions/upsert-pr-comment/action.yml — Composite action that creates or updates a single PR comment identified by a hidden HTML marker, keeping comment threads tidy.

Why

Currently there is no way to test a Backpack change in a consuming application without merging and releasing. A dev release workflow lets engineers publish a tagged pre-release (x.y.z-dev-v<run>.<attempt>) from any open PR, significantly shortening the feedback loop.

We need this to safely roll out a potential backpack fix, #4231, which is needed for INL-3675.

Security considerations

  • Fork PRs are rejected — the workflow only runs for branches in the same repository.
  • User permissions are verified via the GitHub API (must have write access).
  • All ${{ }} expressions in bash steps are passed via env blocks to prevent script injection.
  • Workflow permissions follow least-privilege (contents: read, pull-requests: write).
  • npm publishing uses a scoped Publishing environment with NPM_TOKEN from repository secrets.

References

Checklist

  • No component changes — CI, workflow, and action files only
  • skip-changelog label appropriate (no consumer-facing changes)

Adds a workflow_dispatch workflow that validates PR security, builds the
package from a PR branch, and publishes a dev release to npm. Includes
composite actions for PR security validation and idempotent PR commenting.

INL-3675

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
run: |
cd dist
npm version $DEV_VERSION --no-git-tag-version
npm publish --tag dev

Check notice

Code scanning / zizmor

prefer trusted publishing for authentication Note

prefer trusted publishing for authentication
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm happy accepting this now, as setting up OIDC for backpack publishing is beyond the scope of this incident.

@skyscanner-backpack-bot
Copy link

Visit https://backpack.github.io/storybook-prs/4233 to see this build running in a browser.

@Jack-Waller Jack Waller (Jack-Waller) added minor Non breaking change and removed minor Non breaking change labels Feb 12, 2026
@skyscanner-backpack-bot
Copy link

Visit https://backpack.github.io/storybook-prs/4233 to see this build running in a browser.

CACHE_NAME: node-modules-cache
PR_COMMENT_MARKER: "<!-- backpack-dev-release -->"

permissions: {}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Top-level permissions: {} revokes all default token permissions at the workflow level. Each job then re-declares only what it needs (e.g. contents: read, pull-requests: write), following least-privilege.

- name: Determine dev version
id: version
run: |
BASE_VERSION=$(git describe --tags --abbrev=0 --exclude='*-*' HEAD)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Version format: x.y.z-dev-v<run_id>.<attempt>

git describe --exclude='*-*' skips any existing pre-release tags and finds the latest stable release tag. The run ID + attempt number guarantees uniqueness even across retries of the same workflow run.

- name: Get PR data
id: get-pr-data
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
env:
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All ${{ }} expressions are passed via env blocks rather than inlined in run: scripts. This is a security best practice — it prevents script injection if any output value contains shell metacharacters or malicious payloads. The JS steps already use process.env for the same reason.

description: The pull request number to comment on
required: true
marker:
description: HTML comment marker used to identify and update existing comments
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The marker is a hidden HTML comment embedded in the body. On subsequent runs, the action searches for an existing comment containing this marker and updates it in-place rather than creating duplicates. This keeps PR comment threads clean across retries and re-runs.

DevRelease:
name: Build and publish dev release
runs-on: ubuntu-latest
environment: Publishing
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Publishing environment gates this job behind environment protection rules. The NPM_TOKEN secret is scoped to this environment, so it's never exposed to the security-check or reporting jobs.

steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ needs.SecurityCheck.outputs.pr_sha }}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checkout at the validated SHA (needs.SecurityCheck.outputs.pr_sha) rather than the branch ref. This prevents a TOCTOU race where the branch could be force-pushed between the security check and the build.

with:
ref: ${{ needs.SecurityCheck.outputs.pr_sha }}
persist-credentials: false
fetch-depth: 0
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fetch-depth: 0 is needed for git describe in the version step.

name: Report results on PR
runs-on: ubuntu-latest
needs: [SecurityCheck, DevRelease]
if: always() && needs.SecurityCheck.result == 'success'
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

always() ensures this job runs even if DevRelease fails, but the needs.SecurityCheck.result == 'success' condition skips it if the PR itself failed validation (no point commenting if we already posted a failure comment in SecurityCheck).

uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
sparse-checkout: .github/actions
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sparse checkout — only .github/actions is checked out here. This job doesn't need the full repo; it only needs the composite actions to run the security validation. Keeps the checkout fast and avoids exposing source code in a job that doesn't need it.

pr_number: ${{ inputs.pr_number }}

- name: Add or update failure comment on PR
if: failure()
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Failure comment on security check. The if: failure() condition means this only runs when the validation step fails. It posts a comment to the PR so the author gets feedback without having to dig through workflow logs.

@Jack-Waller Jack Waller (Jack-Waller) marked this pull request as ready for review February 12, 2026 17:14
Copilot AI review requested due to automatic review settings February 12, 2026 17:14
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a dev release workflow that enables publishing pre-release npm packages from open pull requests, allowing contributors to test unreleased changes in downstream projects before merging.

Changes:

  • New workflow_dispatch workflow for PR-based npm publishing with security validation
  • Composite action for PR security validation (same-repo branches, user permissions)
  • Composite action for creating/updating PR comments with workflow status

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
.github/workflows/dev-release.yml Main workflow orchestrating security checks, build, npm publish, and PR comment reporting
.github/actions/pr-security-validation/action.yml Validates PR is open, from same repository, and user has write access
.github/actions/upsert-pr-comment/action.yml Creates or updates a single PR comment identified by HTML marker

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants