From 971b36b1fc8f831e0b629488862eb13df552bd90 Mon Sep 17 00:00:00 2001 From: Quinton Jason Date: Fri, 15 May 2026 09:00:17 -0500 Subject: [PATCH 1/6] ci: add Chromatic workflow and clarify release cadence MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wire up Chromatic visual regression on PR and push to main/next. The `@chromatic-com/storybook` addon was already configured in libs/core/.storybook/main.js but no CI workflow ran against it. Fork PRs are skipped because repository secrets are unavailable. A `CHROMATIC_PROJECT_TOKEN` secret must be added by maintainers. Also clarify (in CONTRIBUTING.md and schedule-release.yml comments) that releases today run manually via workflow_dispatch — there is no weekly cron yet — so contributors know where to look when they ask. --- .github/workflows/chromatic.yml | 52 ++++++++++++++++++++++++++ .github/workflows/schedule-release.yml | 10 ++++- CONTRIBUTING.md | 10 +++++ 3 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/chromatic.yml diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml new file mode 100644 index 000000000..416c2ff58 --- /dev/null +++ b/.github/workflows/chromatic.yml @@ -0,0 +1,52 @@ +name: Chromatic + +on: + push: + branches: [main, next] + pull_request: + branches: [main, next] + +concurrency: + group: chromatic-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + publish: + runs-on: ubuntu-latest + # Fork PRs cannot use repository secrets; skip to avoid failing checks. + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + permissions: + contents: read + pull-requests: write + steps: + - name: Check out latest + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + cache: npm + cache-dependency-path: package-lock.json + + - name: Install dependencies + run: npm ci + + - name: Build doc components + run: npx nx run @pine-ds/doc-components:build + + - name: Build Stencil (Storybook consumes dist) + run: npx nx run @pine-ds/core:build + + - name: Build Storybook static + run: npx nx run @pine-ds/core:build.storybook + + - name: Publish to Chromatic + uses: chromaui/action@v16.9.1 + with: + projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} + workingDir: libs/core + storybookBuildDir: storybook-static + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/schedule-release.yml b/.github/workflows/schedule-release.yml index ec8c02d87..609d74f16 100644 --- a/.github/workflows/schedule-release.yml +++ b/.github/workflows/schedule-release.yml @@ -6,9 +6,15 @@ permissions: contents: read on: - ## We will uncomment this when we settle on a deployment schedule + # Release cadence today is **manual / on-demand** via `workflow_dispatch` (and `workflow_call` + # from other automation). There is no weekly cron publish yet. + # + # When the team wants a fixed cadence (for example weekly npm publishes), uncomment and adjust: # schedule: - # - cron: '00 04 * * 1' # 4 am UTC (9 am CST) Monday + # - cron: '00 04 * * 1' # example: 4 am UTC Mondays + # + # Until then, maintainers cut versions from the default branch using the dispatch form in GitHub + # Actions for this workflow ("Pine Continuous Deployment"). workflow_call: inputs: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a05a2bbbf..7ac85150c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -78,6 +78,16 @@ npm run size When a budget fails, the CLI prints each tracked file with its gzipped size and limit. +### Visual regression (Chromatic) + +Pull requests and pushes to `main` / `next` run [Chromatic](https://www.chromatic.com/) via [`.github/workflows/chromatic.yml`](.github/workflows/chromatic.yml). The workflow publishes the static Storybook build from `libs/core/storybook-static`. + +Repository maintainers must add a **`CHROMATIC_PROJECT_TOKEN`** secret (from the Chromatic project for this Storybook) so the job can authenticate. Forked pull requests skip Chromatic because secrets are not available to them. + +### Releases + +`Pine Continuous Deployment` ([`.github/workflows/schedule-release.yml`](.github/workflows/schedule-release.yml)) is triggered **manually** with `workflow_dispatch` (or by another workflow via `workflow_call`). There is no scheduled npm publish cron enabled in-repo yet; see the comment block at the top of that workflow when enabling a cadence. + ### Submitting a Pull Request Once the desired changes have been made, add and commit the necessary files. We use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/), so please be sure that your commit messages adhere to these standards. From d89235230afba8708889276513161a7ccb9c83cd Mon Sep 17 00:00:00 2001 From: Quinton Jason Date: Fri, 15 May 2026 09:20:24 -0500 Subject: [PATCH 2/6] ci(chromatic): set exitZeroOnChanges and autoAcceptChanges policy Document and apply the per-branch policy for visual regressions: - PRs use exitZeroOnChanges so the workflow stays green; Chromatic's own PR status check is what blocks merge when diffs are unapproved. This keeps unrelated required checks visible. - main (and by extension next via the workflow trigger) auto-accept visual diffs into the baseline so post-merge re-approval is not required for code that has already passed review. --- .github/workflows/chromatic.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml index 416c2ff58..ba65b9ad6 100644 --- a/.github/workflows/chromatic.yml +++ b/.github/workflows/chromatic.yml @@ -50,3 +50,13 @@ jobs: workingDir: libs/core storybookBuildDir: storybook-static token: ${{ secrets.GITHUB_TOKEN }} + # PRs: do not fail the workflow on visual changes — Chromatic + # posts its own PR status check that blocks merge when changes + # are unapproved. This keeps the workflow itself green so other + # required checks can render their state independently. + exitZeroOnChanges: true + # Main (and next): accept visual diffs into the baseline + # automatically. By the time something is on main it has been + # reviewed, so re-approving in Chromatic adds friction without + # catching anything new. + autoAcceptChanges: main From 44609fd6baaa9cb84f38f1157f2d539f7e531212 Mon Sep 17 00:00:00 2001 From: Quinton Jason Date: Tue, 26 May 2026 12:35:43 -0500 Subject: [PATCH 3/6] ci(chromatic): skip workflow when project token is not configured Avoid failing PR checks until CHROMATIC_PROJECT_TOKEN is added to repo secrets. --- .github/workflows/chromatic.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml index ba65b9ad6..2e6a13ec8 100644 --- a/.github/workflows/chromatic.yml +++ b/.github/workflows/chromatic.yml @@ -13,8 +13,12 @@ concurrency: jobs: publish: runs-on: ubuntu-latest - # Fork PRs cannot use repository secrets; skip to avoid failing checks. - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + # Fork PRs cannot use repository secrets; skip when the token is not configured yet. + if: >- + ${{ + secrets.CHROMATIC_PROJECT_TOKEN != '' && + (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) + }} permissions: contents: read pull-requests: write From fe44c288ade8facbcab8d4903a35d3e43560f522 Mon Sep 17 00:00:00 2001 From: Quinton Jason Date: Tue, 26 May 2026 12:43:42 -0500 Subject: [PATCH 4/6] ci(chromatic): pin action to SHA and bump checkout to v6 Address Aikido supply-chain finding on floating third-party action tag. --- .github/workflows/chromatic.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml index 2e6a13ec8..96678a79e 100644 --- a/.github/workflows/chromatic.yml +++ b/.github/workflows/chromatic.yml @@ -24,7 +24,7 @@ jobs: pull-requests: write steps: - name: Check out latest - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 @@ -48,7 +48,8 @@ jobs: run: npx nx run @pine-ds/core:build.storybook - name: Publish to Chromatic - uses: chromaui/action@v16.9.1 + # Pin to full SHA (v16.9.1) for supply-chain security + uses: chromaui/action@c231f99a49d72bfcf68eca3f1ef3187f471e352b with: projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} workingDir: libs/core From 3c4ba4690e62e8803a62618bfccd89a05899fbce Mon Sep 17 00:00:00 2001 From: Quinton Jason Date: Wed, 27 May 2026 09:53:03 -0500 Subject: [PATCH 5/6] ci(chromatic): address Bugbot feedback on checkout, npm, and auto-accept Check out PR head SHA, use pinned npm setup composite, and auto-accept visual changes on both main and next. --- .github/workflows/chromatic.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml index 96678a79e..0983f5142 100644 --- a/.github/workflows/chromatic.yml +++ b/.github/workflows/chromatic.yml @@ -27,13 +27,12 @@ jobs: uses: actions/checkout@v6 with: fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha }} - - name: Set up Node.js - uses: actions/setup-node@v4 + - name: Set up Node.js (pinned npm) + uses: ./.github/workflows/actions/setup-node-with-pinned-npm with: node-version-file: '.nvmrc' - cache: npm - cache-dependency-path: package-lock.json - name: Install dependencies run: npm ci @@ -64,4 +63,4 @@ jobs: # automatically. By the time something is on main it has been # reviewed, so re-approving in Chromatic adds friction without # catching anything new. - autoAcceptChanges: main + autoAcceptChanges: 'main|next' From 119d545b47544569020abf7edc04b55eaf6bb517 Mon Sep 17 00:00:00 2001 From: Quinton Jason Date: Wed, 27 May 2026 09:58:50 -0500 Subject: [PATCH 6/6] ci(chromatic): fix secret gate and autoAcceptChanges branch glob Move CHROMATIC_PROJECT_TOKEN check to a step-level gate because secrets are unavailable in job-level if expressions, and use picomatch extglob @(main|next) for autoAcceptChanges. --- .github/workflows/chromatic.yml | 36 ++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml index 0983f5142..533f609d2 100644 --- a/.github/workflows/chromatic.yml +++ b/.github/workflows/chromatic.yml @@ -13,40 +13,57 @@ concurrency: jobs: publish: runs-on: ubuntu-latest - # Fork PRs cannot use repository secrets; skip when the token is not configured yet. + # Fork PRs cannot use repository secrets. if: >- - ${{ - secrets.CHROMATIC_PROJECT_TOKEN != '' && - (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) - }} + github.event_name != 'pull_request' || + github.event.pull_request.head.repo.full_name == github.repository permissions: contents: read pull-requests: write steps: + - name: Check Chromatic token is configured + id: chromatic-token + env: + CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} + run: | + if [ -n "$CHROMATIC_PROJECT_TOKEN" ]; then + echo "configured=true" >> "$GITHUB_OUTPUT" + else + echo "configured=false" >> "$GITHUB_OUTPUT" + echo "CHROMATIC_PROJECT_TOKEN is not configured; skipping Chromatic publish." + fi + - name: Check out latest + if: steps.chromatic-token.outputs.configured == 'true' uses: actions/checkout@v6 with: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} - name: Set up Node.js (pinned npm) + if: steps.chromatic-token.outputs.configured == 'true' uses: ./.github/workflows/actions/setup-node-with-pinned-npm with: node-version-file: '.nvmrc' - name: Install dependencies + if: steps.chromatic-token.outputs.configured == 'true' run: npm ci - name: Build doc components + if: steps.chromatic-token.outputs.configured == 'true' run: npx nx run @pine-ds/doc-components:build - name: Build Stencil (Storybook consumes dist) + if: steps.chromatic-token.outputs.configured == 'true' run: npx nx run @pine-ds/core:build - name: Build Storybook static + if: steps.chromatic-token.outputs.configured == 'true' run: npx nx run @pine-ds/core:build.storybook - name: Publish to Chromatic + if: steps.chromatic-token.outputs.configured == 'true' # Pin to full SHA (v16.9.1) for supply-chain security uses: chromaui/action@c231f99a49d72bfcf68eca3f1ef3187f471e352b with: @@ -59,8 +76,7 @@ jobs: # are unapproved. This keeps the workflow itself green so other # required checks can render their state independently. exitZeroOnChanges: true - # Main (and next): accept visual diffs into the baseline - # automatically. By the time something is on main it has been - # reviewed, so re-approving in Chromatic adds friction without - # catching anything new. - autoAcceptChanges: 'main|next' + # Main and next: accept visual diffs into the baseline automatically. + # By the time something is on main or next it has been reviewed, so + # re-approving in Chromatic adds friction without catching anything new. + autoAcceptChanges: '@(main|next)'