Files
actual/.github/workflows/vrt-update-generate.yml
625e18e20b [AI] Use noble Playwright image for the desktop VRT job to compile against Electron 43 headers (#8770)
The /update-vrt workflow always runs from the default branch, so this
container fix must land there for the desktop shard to build native modules
against Electron 43's V8 headers, which gcc 11/12 (jammy) cannot parse.
Matches the change to the functional-desktop-app job in e2e-test.yml on the
Electron 43 upgrade branch (#8769).

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-21 22:58:39 +00:00

338 lines
12 KiB
YAML

name: VRT Update - Generate
# SECURITY: This workflow runs in untrusted fork context with no write permissions.
# It only generates VRT patch artifacts that are later applied by vrt-update-apply.yml
# Triggered by commenting "/update-vrt" on a pull request.
on:
issue_comment:
types: [created]
permissions:
contents: read
pull-requests: read
concurrency:
group: ${{ github.workflow }}-${{ github.event.issue.number }}
cancel-in-progress: false
jobs:
add-reaction:
name: Add 👀 Reaction
runs-on: depot-ubuntu-latest
# Only run on PR comments containing /update-vrt
if: >
github.event.issue.pull_request &&
startsWith(github.event.comment.body, '/update-vrt')
permissions:
pull-requests: write
steps:
- name: Add 👀 reaction to comment
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
await github.rest.reactions.createForIssueComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: context.payload.comment.id,
content: 'eyes'
});
get-pr:
name: Resolve PR details
runs-on: depot-ubuntu-latest
if: >
github.event.issue.pull_request &&
startsWith(github.event.comment.body, '/update-vrt')
outputs:
head_sha: ${{ steps.pr.outputs.head_sha }}
head_ref: ${{ steps.pr.outputs.head_ref }}
head_repo: ${{ steps.pr.outputs.head_repo }}
steps:
- name: Get PR details
id: pr
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const { data: pr } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number
});
core.setOutput('head_sha', pr.head.sha);
core.setOutput('head_ref', pr.head.ref);
core.setOutput('head_repo', pr.head.repo.full_name);
build-web:
name: Build web bundle
runs-on: depot-ubuntu-latest
needs: get-pr
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ needs.get-pr.outputs.head_sha }}
persist-credentials: false
- name: Set up environment
uses: ./.github/actions/setup
with:
download-translations: 'false'
- name: Build browser bundle
# REACT_APP_NETLIFY=true flips isNonProductionEnvironment() on in the
# bundle so the "Create test file" button (used by every e2e beforeEach
# via ConfigurationPage.createTestFile()) is still rendered in a
# production build. Without it, e2e tests would time out waiting for
# a button that was tree-shaken out.
# --skip-translations keeps VRT screenshots deterministic by rendering
# source-code English instead of upstream Weblate en.json (which can
# drift between snapshot capture and test runs).
env:
REACT_APP_NETLIFY: 'true'
run: yarn build:browser --skip-translations
- name: Upload build artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: desktop-client-build
path: packages/desktop-client/build/
retention-days: 1
overwrite: true
browser-vrt:
name: Browser VRT (shard ${{ matrix.shard }}/3)
runs-on: depot-ubuntu-latest
needs: [get-pr, build-web]
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3]
container:
image: mcr.microsoft.com/playwright:v1.61.1-jammy
env:
E2E_USE_BUILD: '1'
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ needs.get-pr.outputs.head_sha }}
persist-credentials: false
- name: Trust workspace directory
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
shell: bash
- name: Set up environment
uses: ./.github/actions/setup
with:
download-translations: 'false'
- name: Download web build
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: desktop-client-build
path: packages/desktop-client/build/
- name: Run VRT Tests
continue-on-error: true
run: yarn vrt --update-snapshots --shard=${{ matrix.shard }}/3
- name: Create shard patch with PNG changes only
id: create-patch
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git add "**/*.png"
if git diff --staged --quiet; then
echo "has_changes=false" >> "$GITHUB_OUTPUT"
echo "No VRT changes in this shard"
exit 0
fi
echo "has_changes=true" >> "$GITHUB_OUTPUT"
git commit -m "Update VRT screenshots (browser shard ${{ matrix.shard }})"
git format-patch -1 HEAD --stdout > vrt-shard.patch
# Validate patch only contains PNG files. `git format-patch` emits a
# `GIT binary patch` block for PNGs (no +++/--- lines), so check
# `diff --git` headers — those are present for both text and binary.
if grep -E '^diff --git ' vrt-shard.patch \
| grep -vE '^diff --git a/[^[:space:]]+\.png b/[^[:space:]]+\.png$'; then
echo "ERROR: Shard patch contains non-PNG files!"
exit 1
fi
- name: Upload shard patch
if: steps.create-patch.outputs.has_changes == 'true'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: vrt-shard-browser-${{ matrix.shard }}
path: vrt-shard.patch
retention-days: 1
overwrite: true
desktop-vrt:
name: Desktop VRT
runs-on: depot-ubuntu-latest
needs: get-pr
container:
# noble (gcc 13) is required to compile native modules against Electron 43+ headers;
# jammy's gcc 11/12 cannot parse them. Keep in sync with the functional-desktop-app job in e2e-test.yml.
image: mcr.microsoft.com/playwright:v1.61.1-noble
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ needs.get-pr.outputs.head_sha }}
persist-credentials: false
- name: Trust workspace directory
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
shell: bash
- name: Set up environment
uses: ./.github/actions/setup
with:
download-translations: 'false'
# Build tools are needed to rebuild native modules like better-sqlite3 used by the Desktop app, which is required to run VRT tests on the Desktop app and generate updated snapshots.
- name: Install build tools
run: apt-get update && apt-get install -y build-essential python3
- name: Run Desktop VRT Tests
continue-on-error: true
run: |
yarn rebuild-electron
xvfb-run --auto-servernum --server-args="-screen 0 1920x1080x24" -- yarn e2e:desktop --update-snapshots
- name: Create shard patch with PNG changes only
id: create-patch
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git add "**/*.png"
if git diff --staged --quiet; then
echo "has_changes=false" >> "$GITHUB_OUTPUT"
echo "No VRT changes in desktop shard"
exit 0
fi
echo "has_changes=true" >> "$GITHUB_OUTPUT"
git commit -m "Update VRT screenshots (desktop)"
git format-patch -1 HEAD --stdout > vrt-shard.patch
# See validation note in browser-vrt above.
if grep -E '^diff --git ' vrt-shard.patch \
| grep -vE '^diff --git a/[^[:space:]]+\.png b/[^[:space:]]+\.png$'; then
echo "ERROR: Desktop shard patch contains non-PNG files!"
exit 1
fi
- name: Upload shard patch
if: steps.create-patch.outputs.has_changes == 'true'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: vrt-shard-desktop
path: vrt-shard.patch
retention-days: 1
overwrite: true
merge-patch:
name: Merge VRT Patches
runs-on: depot-ubuntu-latest
needs: [get-pr, browser-vrt, desktop-vrt]
if: ${{ !cancelled() && needs.get-pr.result == 'success' }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ needs.get-pr.outputs.head_sha }}
persist-credentials: false
- name: Download all shard patches
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
path: /tmp/shard-patches
pattern: vrt-shard-*
- name: Merge shard patches
id: create-patch
shell: bash
run: |
git config --global --add safe.directory "$GITHUB_WORKSPACE"
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
# actions/download-artifact puts a lone matched artifact directly in
# `path` but gives each of several its own `path/<name>/` subdir, so
# recurse instead of globbing `*/vrt-shard.patch` (which would miss
# the common single-shard case).
mapfile -t patches < <(find /tmp/shard-patches -type f -name 'vrt-shard.patch' | sort)
if [ ${#patches[@]} -eq 0 ]; then
echo "has_changes=false" >> "$GITHUB_OUTPUT"
echo "No shard patches to merge"
exit 0
fi
# Defense in depth: re-validate every shard patch before applying.
# See validation note in browser-vrt above for why we match
# `diff --git` headers instead of +++/--- lines.
for patch in "${patches[@]}"; do
echo "Validating $patch"
if grep -E '^diff --git ' "$patch" \
| grep -vE '^diff --git a/[^[:space:]]+\.png b/[^[:space:]]+\.png$'; then
echo "ERROR: $patch contains non-PNG files!"
exit 1
fi
done
# Apply each shard patch. Shards touch disjoint PNG files so
# order does not matter. --index stages the applied changes.
for patch in "${patches[@]}"; do
echo "Applying $patch"
git apply --index "$patch"
done
if git diff --staged --quiet; then
echo "has_changes=false" >> "$GITHUB_OUTPUT"
echo "No VRT changes after merge"
exit 0
fi
echo "has_changes=true" >> "$GITHUB_OUTPUT"
git commit -m "Update VRT screenshots"
git format-patch -1 HEAD --stdout > vrt-update.patch
# Final guard on the combined patch.
if grep -E '^diff --git ' vrt-update.patch \
| grep -vE '^diff --git a/[^[:space:]]+\.png b/[^[:space:]]+\.png$'; then
echo "ERROR: Merged patch contains non-PNG files!"
exit 1
fi
echo "Merged patch created successfully with PNG changes only"
- name: Upload patch artifact
if: steps.create-patch.outputs.has_changes == 'true'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: vrt-patch-${{ github.event.issue.number }}
path: vrt-update.patch
retention-days: 5
- name: Save PR metadata
if: steps.create-patch.outputs.has_changes == 'true'
run: |
mkdir -p pr-metadata
echo "${{ github.event.issue.number }}" > pr-metadata/pr-number.txt
echo "${NEEDS_GET_PR_OUTPUTS_HEAD_REF}" > pr-metadata/head-ref.txt
echo "${NEEDS_GET_PR_OUTPUTS_HEAD_REPO}" > pr-metadata/head-repo.txt
env:
NEEDS_GET_PR_OUTPUTS_HEAD_REF: ${{ needs.get-pr.outputs.head_ref }}
NEEDS_GET_PR_OUTPUTS_HEAD_REPO: ${{ needs.get-pr.outputs.head_repo }}
- name: Upload PR metadata
if: steps.create-patch.outputs.has_changes == 'true'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: vrt-metadata-${{ github.event.issue.number }}
path: pr-metadata/
retention-days: 5