Skip to content

CI 5d5dc17e90ce409044a8c194650d700e37a2a563 #652

CI 5d5dc17e90ce409044a8c194650d700e37a2a563

CI 5d5dc17e90ce409044a8c194650d700e37a2a563 #652

Workflow file for this run

name: CI
run-name: "${{ github.event_name == 'pull_request' && format('CI PR #{0}', github.event.pull_request.number) || format('CI {0}', github.sha) }}"
on:
pull_request:
paths-ignore:
- README.md
- CHANGELOG.md
- docs/**
- .planning/**
- .github/**/*.md
push:
branches:
- main
paths-ignore:
- README.md
- CHANGELOG.md
- docs/**
- .planning/**
- .github/**/*.md
workflow_dispatch:
permissions:
actions: read
contents: read
pull-requests: read
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
env:
CARGO_TERM_COLOR: always
jobs:
decision:
name: CI decision
runs-on: ubuntu-22.04
timeout-minutes: 10
outputs:
run_full: ${{ steps.dedupe.outputs.run_full }}
steps:
- name: Deduplicate an already verified main tree
id: dedupe
shell: bash
env:
GH_TOKEN: ${{ github.token }}
run: |
set -uo pipefail
run_full=true
reason="this event requires full validation"
if [ "$GITHUB_EVENT_NAME" = "push" ]; then
reason="no exact successful pull request validation was found"
if ! pulls=$(gh api \
-H "Accept: application/vnd.github+json" \
"repos/$GITHUB_REPOSITORY/commits/$GITHUB_SHA/pulls?per_page=100"); then
echo "::warning::Could not resolve associated pull requests; running full CI."
else
pr=$(jq -c --arg sha "$GITHUB_SHA" \
'[.[] | select(.merged_at != null and .merge_commit_sha == $sha)] | sort_by(.number) | last // empty' \
<<<"$pulls")
if [ -n "$pr" ]; then
pr_number=$(jq -r .number <<<"$pr")
head_sha=$(jq -r .head.sha <<<"$pr")
if push_tree=$(gh api "repos/$GITHUB_REPOSITORY/git/commits/$GITHUB_SHA" --jq .tree.sha) \
&& head_tree=$(gh api "repos/$GITHUB_REPOSITORY/git/commits/$head_sha" --jq .tree.sha) \
&& runs=$(gh api \
"repos/$GITHUB_REPOSITORY/actions/workflows/ci.yml/runs?event=pull_request&head_sha=$head_sha&status=completed&per_page=20"); then
expected_title="CI PR #$pr_number"
latest_conclusion=$(jq -r --arg title "$expected_title" \
'[.workflow_runs[] | select(.display_title == $title)] | sort_by(.run_started_at) | last | .conclusion // ""' \
<<<"$runs")
if [ "$push_tree" = "$head_tree" ] && [ "$latest_conclusion" = "success" ]; then
run_full=false
reason="PR #$pr_number successfully validated the exact tree $push_tree"
elif [ "$push_tree" != "$head_tree" ]; then
reason="the merged tree differs from PR #$pr_number head"
else
reason="the latest CI run for PR #$pr_number is not successful"
fi
else
echo "::warning::Could not verify the associated PR tree and run; running full CI."
fi
fi
fi
fi
echo "run_full=$run_full" >> "$GITHUB_OUTPUT"
echo "CI decision: $reason"
if [ "$run_full" = "false" ]; then
echo "### CI deduplicated" >> "$GITHUB_STEP_SUMMARY"
echo "$reason. Expensive validation is skipped for this main push." >> "$GITHUB_STEP_SUMMARY"
fi
verify:
name: make verify
needs: decision
if: needs.decision.outputs.run_full == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Check patch and detect a release version change
id: changes
shell: bash
env:
BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.before }}
run: |
set -euo pipefail
echo "release=false" >> "$GITHUB_OUTPUT"
if [ -z "$BASE_SHA" ] || [[ "$BASE_SHA" =~ ^0+$ ]]; then
git diff --check
exit 0
fi
if ! git cat-file -e "$BASE_SHA^{commit}" 2>/dev/null; then
git fetch --no-tags --depth=1 origin "$BASE_SHA"
fi
git diff --check "$BASE_SHA" "$GITHUB_SHA"
base_version=$(git show "$BASE_SHA:package.json" \
| node -e 'let data=""; process.stdin.on("data", chunk => data += chunk); process.stdin.on("end", () => console.log(JSON.parse(data).version));')
current_version=$(node -p "require('./package.json').version")
if [ "$base_version" != "$current_version" ]; then
# The version band follows the active milestone (README, Release
# Process), so a bump can jump minor. It must never go backwards:
# a lower version overwrites latest.json and strands updaters.
newest=$(printf '%s\n%s\n' "$base_version" "$current_version" | sort -V | tail -1)
if [ "$newest" != "$current_version" ]; then
echo "::error::release version went backwards: $base_version -> $current_version"
exit 1
fi
echo "release=true" >> "$GITHUB_OUTPUT"
echo "Release version changed: $base_version -> $current_version"
fi
- name: Setup pnpm
uses: pnpm/action-setup@v6
with:
version: 9.15.0
- name: Setup Node
uses: actions/setup-node@v5
with:
node-version: 22.22.3
cache: pnpm
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache Rust build
uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri -> target
shared-key: ubuntu-22.04-validation
- name: Install Linux Tauri dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
libwebkit2gtk-4.1-dev \
libxdo-dev \
libssl-dev \
libayatana-appindicator3-dev \
librsvg2-dev
- name: Install frontend dependencies
run: pnpm install --frozen-lockfile
- name: Run verify (typecheck + version checks + guards + tests + frontend build)
if: steps.changes.outputs.release != 'true'
run: make verify
- name: Run verify and release-only checks
if: steps.changes.outputs.release == 'true'
run: make release-checks
e2e:
name: playwright e2e
needs: decision
if: needs.decision.outputs.run_full == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup pnpm
uses: pnpm/action-setup@v6
with:
version: 9.15.0
- name: Setup Node
uses: actions/setup-node@v5
with:
node-version: 22.22.3
cache: pnpm
- name: Install frontend dependencies
run: pnpm install --frozen-lockfile
# e2e used to run only in release-preflight, so a broken or flaky spec
# first surfaced on the release tag instead of on the PR that caused it.
- name: Install Playwright browsers
run: pnpm exec playwright install --with-deps chromium
- name: Run e2e
run: make test-e2e
- name: Upload e2e artifacts on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: |
playwright-report/
test-results/
retention-days: 7
# D-03: keep the native runner compiling on every PR without executing the
# suite - launching the real app per PR is rejected on cost and early-flake
# grounds (D-16). The gated suite lives in native-e2e.yml.
native-e2e-compile:
name: native e2e compile check
needs: decision
if: needs.decision.outputs.run_full == 'true'
runs-on: macos-14
timeout-minutes: 45
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup pnpm
uses: pnpm/action-setup@v6
with:
version: 9.15.0
- name: Setup Node
uses: actions/setup-node@v5
with:
node-version: 22.22.3
cache: pnpm
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache Rust build
uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri -> target
shared-key: macos-14-native-e2e-compile
- name: Install frontend dependencies
run: pnpm install --frozen-lockfile
- name: Typecheck (includes e2e-native/)
run: pnpm typecheck
- name: Lint (includes e2e-native/)
run: pnpm lint
# generate_context!() requires frontendDist (../dist) at compile time
# once native-e2e turns on tauri/custom-protocol; build it first.
- name: Build frontend for native-e2e
run: pnpm build:frontend:native-e2e
# --locked, not --offline: a hosted runner has no warm registry cache on
# a cold job, so --offline would fail for a reason unrelated to the code.
- name: Cargo check with the native-e2e feature on
run: cd src-tauri && cargo check --locked --features native-e2e