CI 5d5dc17e90ce409044a8c194650d700e37a2a563 #652
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |