Skip to content

fix(ci): prevent pull request cache writes #7172

fix(ci): prevent pull request cache writes

fix(ci): prevent pull request cache writes #7172

Workflow file for this run

name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
# Cancel superseded runs on a PR (a new push obsoletes the in-flight one).
# Pushes to main are never canceled in-flight — though GitHub keeps only the
# newest PENDING run per group, so rapid pushes can skip an intermediate
# commit's run.
concurrency:
# Keyed by PR number (not head_ref): two different fork PRs can share a
# head branch name (most commonly `main`), and a name-keyed group would let
# one contributor's push cancel or queue behind another's unrelated run.
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
# Turbo Remote Cache: first-party PRs can read shared artifacts, but only
# trusted push runs can write them. Fork PRs use the local cache only.
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
TURBO_CACHE: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && 'local:rw,remote:r' || github.event_name == 'pull_request' && 'local:rw' || 'local:rw,remote:rw' }}
jobs:
test:
runs-on: ${{ matrix.os }}
timeout-minutes: 20
# Fold the Node version AND the OS into turbo's `test` task hash
# (turbo.json) so a cached result from one matrix leg is never replayed for
# another — turbo does not hash the platform by default, so without the OS
# dimension the Windows/macOS legs (same Node as an ubuntu leg) would hit
# the remote cache and replay ubuntu's test logs instead of running.
env:
MATRIX_NODE_VERSION: ${{ matrix.node-version }}
MATRIX_OS: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
node-version: ["20.19.0", "22.18.0", "24", "25", "26"]
include:
- os: windows-latest
node-version: "22.18.0"
- os: blacksmith-6vcpu-macos-latest
node-version: "22.18.0"
steps:
- uses: actions/checkout@v5
with:
persist-credentials: false
- uses: pnpm/action-setup@v5
- uses: actions/setup-node@v5
with:
node-version: ${{ matrix.node-version }}
cache: pnpm
- run: pnpm install --frozen-lockfile --prefer-offline
- name: Run tests
if: ${{ matrix.os != 'windows-latest' && matrix.node-version != '20.19.0' }}
run: pnpm test
- name: Run Node 20 tests serially
if: ${{ matrix.node-version == '20.19.0' }}
run: pnpm test --concurrency=1
- name: Run Windows tests serially
if: ${{ matrix.os == 'windows-latest' }}
run: pnpm test --concurrency=1
# lint + typecheck run as dedicated jobs in code-quality.yml; don't pay for
# them again here.
- name: Check formatting
if: ${{ matrix.os == 'ubuntu-latest' && matrix.node-version == '22.18.0' }}
shell: bash
run: |
format_output_file="$(mktemp)"
set +e
pnpm format:check 2>&1 | tee "$format_output_file"
format_status=${PIPESTATUS[0]}
set -e
if [ "$format_status" -ne 0 ]; then
while IFS= read -r format_line; do
format_file="${format_line%% (*}"
if [ "$format_file" = "$format_line" ] || [ ! -f "$format_file" ]; then
continue
fi
format_annotation_file="${format_file//'%'/'%25'}"
format_annotation_file="${format_annotation_file//$'\r'/'%0D'}"
format_annotation_file="${format_annotation_file//$'\n'/'%0A'}"
format_annotation_file="${format_annotation_file//':'/'%3A'}"
format_annotation_file="${format_annotation_file//','/'%2C'}"
echo "::error file=$format_annotation_file::File is not formatted. Run pnpm format."
done < "$format_output_file"
fi
rm -f "$format_output_file"
exit "$format_status"
- name: Smoke test built CLI
if: ${{ matrix.os == 'ubuntu-latest' && matrix.node-version == '22.18.0' }}
run: |
cd packages/react-doctor
pnpm build
BUILT_VERSION=$(node bin/react-doctor.js --version)
echo "Built CLI reports version: $BUILT_VERSION"
if [ -z "$BUILT_VERSION" ] || [ "$BUILT_VERSION" = "0.0.0" ]; then
echo "Built CLI version is missing or 0.0.0; build env did not inject VERSION"
exit 1
fi
- name: Smoke test JSON report shape
if: ${{ matrix.os == 'ubuntu-latest' && matrix.node-version == '22.18.0' }}
run: pnpm smoke:json-report
# Regression guard for #629: a published package's built output must not
# import a package that isn't in its `dependencies` (such phantom imports
# only resolve when a sibling dependency happens to supply them, and crash
# consumers on strict installs). Builds every package, then audits dist.
- name: Check published packages declare their runtime dependencies
if: ${{ matrix.os == 'ubuntu-latest' && matrix.node-version == '22.18.0' }}
run: |
pnpm build
pnpm check:published-deps
- name: Smoke test packed CLI install
if: ${{ matrix.node-version == '22.18.0' && (matrix.os == 'ubuntu-latest' || matrix.os == 'windows-latest') }}
run: |
pnpm build
pnpm smoke:packed-cli-install
# Allocates a real pseudo-terminal (`pty.openpty()`) so the CLI sees an
# interactive TTY and renders the multiselect prompt, then asserts the
# process stays alive waiting for input instead of exiting by itself
# (regression guard for #576: unref'd stdin killed interactive prompts).
- name: Smoke test interactive TTY prompt
if: ${{ matrix.os == 'ubuntu-latest' && matrix.node-version == '22.18.0' }}
run: pnpm smoke:tty-prompt