From e62eb714b231adb29395adf540a81255dc32cb00 Mon Sep 17 00:00:00 2001 From: yinlianghui-tw Date: Tue, 25 Aug 2026 03:02:25 +0000 Subject: [PATCH] fix(ci): check the Playwright version pipeline's status before it becomes a cache key Two byte-identical steps built the Playwright browser cache key out of a value whose exit status was discarded twice over: `echo` owned the step's status, and the pipe inside the substitution reported `jq`'s. A failing `pnpm list` or a missing field in its JSON therefore produced a *successful* step and a key degraded to `playwright-Linux-` or `playwright-Linux-null` -- a stable wrong bucket two Playwright versions can share. Both steps now run `set -eo pipefail`, read the version through the repo's existing checked-assignment convention (`if ! version=$(...)`, as `ci.yml` and `lint.yml` already do for `git diff`), and refuse an empty or `null` version with a named `::error::` before anything reaches the cache key. The two blocks are kept byte-identical so they stay greppable as a pair. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_019b5UBNMtTzKbVtZZGvFuxe --- .github/workflows/ci.yml | 22 +++++++++++++++++++++- .github/workflows/live-e2e.yml | 22 +++++++++++++++++++++- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c36a71b49a..3d8a4976a0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1007,7 +1007,27 @@ jobs: - name: Get Playwright version if: steps.relevant.outputs.should_run == 'true' id: playwright-version - run: echo "version=$(pnpm list @playwright/test --depth=0 --json | jq -r '.[0].devDependencies["@playwright/test"].version')" >> $GITHUB_OUTPUT + run: | + # This pipeline's exit status is load-bearing: the value it produces + # becomes the Playwright browser cache key in the next step. Without + # `pipefail` a failing `pnpm list` is masked by `jq`, and `jq -r` + # prints the string `null` and exits 0 when the field is missing — so + # either failure used to yield a *successful* step and a key that had + # silently degraded to `playwright-Linux-` / `playwright-Linux-null`. + # That wrong bucket is stable, so two Playwright versions can share + # one cache entry and restore a stale browser (objectui#6231). + # The same block is duplicated verbatim in `ci.yml` and `live-e2e.yml` + # — keep them byte-identical so they stay greppable as a pair. + set -eo pipefail + if ! version=$(pnpm list @playwright/test --depth=0 --json | jq -r '.[0].devDependencies["@playwright/test"].version'); then + echo "::error::Reading the @playwright/test version failed (pnpm list --json | jq). Refusing to write a Playwright browser cache key from it." + exit 1 + fi + if [ -z "$version" ] || [ "$version" = "null" ]; then + echo "::error::Could not resolve the @playwright/test version (got: '${version}'). Refusing to write an empty or null version into the Playwright browser cache key." + exit 1 + fi + echo "version=$version" >> "$GITHUB_OUTPUT" - name: Cache Playwright browsers if: steps.relevant.outputs.should_run == 'true' diff --git a/.github/workflows/live-e2e.yml b/.github/workflows/live-e2e.yml index 047993f304..d75bd009d1 100644 --- a/.github/workflows/live-e2e.yml +++ b/.github/workflows/live-e2e.yml @@ -137,7 +137,27 @@ jobs: - name: Get Playwright version id: playwright-version - run: echo "version=$(pnpm list @playwright/test --depth=0 --json | jq -r '.[0].devDependencies["@playwright/test"].version')" >> $GITHUB_OUTPUT + run: | + # This pipeline's exit status is load-bearing: the value it produces + # becomes the Playwright browser cache key in the next step. Without + # `pipefail` a failing `pnpm list` is masked by `jq`, and `jq -r` + # prints the string `null` and exits 0 when the field is missing — so + # either failure used to yield a *successful* step and a key that had + # silently degraded to `playwright-Linux-` / `playwright-Linux-null`. + # That wrong bucket is stable, so two Playwright versions can share + # one cache entry and restore a stale browser (objectui#6231). + # The same block is duplicated verbatim in `ci.yml` and `live-e2e.yml` + # — keep them byte-identical so they stay greppable as a pair. + set -eo pipefail + if ! version=$(pnpm list @playwright/test --depth=0 --json | jq -r '.[0].devDependencies["@playwright/test"].version'); then + echo "::error::Reading the @playwright/test version failed (pnpm list --json | jq). Refusing to write a Playwright browser cache key from it." + exit 1 + fi + if [ -z "$version" ] || [ "$version" = "null" ]; then + echo "::error::Could not resolve the @playwright/test version (got: '${version}'). Refusing to write an empty or null version into the Playwright browser cache key." + exit 1 + fi + echo "version=$version" >> "$GITHUB_OUTPUT" - name: Cache Playwright browsers uses: actions/cache@v6