Add commit-level actions, graph filtering, demo mode, and headless screenshot capture #24
Workflow file for this run
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 | |
| on: | |
| push: | |
| branches: [main, master] | |
| pull_request: | |
| branches: [main, master] | |
| workflow_dispatch: | |
| permissions: | |
| contents: write | |
| issues: write | |
| pull-requests: write | |
| env: | |
| CARGO_HTTP_MULTIPLEXING: "false" | |
| CARGO_HTTP_TIMEOUT: "120" | |
| CARGO_NET_RETRY: "10" | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| quality: | |
| name: Build and check | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v5 | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v5 | |
| with: | |
| node-version: 24 | |
| - name: Install frontend dependencies | |
| run: npm ci | |
| - name: Type-check and build frontend | |
| run: npm run build | |
| - name: Install Linux dependencies for Tauri | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y --no-install-recommends \ | |
| libwebkit2gtk-4.1-dev \ | |
| libayatana-appindicator3-dev \ | |
| librsvg2-dev \ | |
| patchelf \ | |
| dbus-x11 \ | |
| xvfb \ | |
| xdotool \ | |
| imagemagick | |
| - name: Install Rust stable | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Cache Rust dependencies | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| workspaces: src-tauri -> target | |
| - name: Check Tauri backend | |
| run: | | |
| for attempt in 1 2 3; do | |
| echo "cargo check attempt ${attempt}/3" | |
| if cargo check --manifest-path src-tauri/Cargo.toml --locked; then | |
| exit 0 | |
| fi | |
| if [ "$attempt" = "3" ]; then | |
| exit 1 | |
| fi | |
| sleep $((attempt * 15)) | |
| done | |
| - name: Build Tauri app | |
| run: | | |
| for attempt in 1 2 3; do | |
| echo "Tauri build attempt ${attempt}/3" | |
| if npm run tauri -- build; then | |
| exit 0 | |
| fi | |
| if [ "$attempt" = "3" ]; then | |
| exit 1 | |
| fi | |
| sleep $((attempt * 15)) | |
| done | |
| - name: Capture browser screenshot | |
| timeout-minutes: 3 | |
| run: | | |
| set -euo pipefail | |
| mkdir -p screenshots | |
| browser_bin="$(command -v google-chrome || command -v chromium || command -v chromium-browser || true)" | |
| if [ -z "$browser_bin" ]; then | |
| echo "No Chrome/Chromium browser found on the runner." >&2 | |
| exit 1 | |
| fi | |
| echo "Using browser: $browser_bin" | |
| npm run preview -- --host 127.0.0.1 --port 4173 > screenshots/gitpilot.log 2>&1 & | |
| preview_pid=$! | |
| trap 'kill "$preview_pid" 2>/dev/null || true' EXIT | |
| ready=false | |
| for _ in {1..30}; do | |
| if curl -fsS http://127.0.0.1:4173 >/dev/null; then | |
| ready=true | |
| break | |
| fi | |
| sleep 1 | |
| done | |
| if [ "$ready" != "true" ]; then | |
| echo "Timed out waiting for Vite preview" >&2 | |
| cat screenshots/gitpilot.log >&2 || true | |
| exit 1 | |
| fi | |
| timeout 75s node scripts/capture-screenshot.mjs \ | |
| --browser "$browser_bin" \ | |
| --url http://127.0.0.1:4173 \ | |
| --output screenshots/gitpilot.png \ | |
| --width 1440 \ | |
| --height 920 | |
| test -s screenshots/gitpilot.png | |
| - name: Upload app screenshot | |
| id: upload-screenshot | |
| if: always() | |
| uses: actions/upload-artifact@v5 | |
| with: | |
| name: gitpilot-app-screenshot | |
| path: | | |
| screenshots/gitpilot.png | |
| screenshots/gitpilot.log | |
| if-no-files-found: warn | |
| - name: Publish screenshot for inline preview | |
| id: publish-screenshot | |
| if: success() && github.event_name == 'pull_request' && hashFiles('screenshots/gitpilot.png') != '' | |
| continue-on-error: true | |
| run: | | |
| set -euo pipefail | |
| pr_number="${{ github.event.pull_request.number }}" | |
| image_path="/tmp/gitpilot-screenshot-${pr_number}.png" | |
| cp screenshots/gitpilot.png "$image_path" | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| if git ls-remote --exit-code --heads origin ci-screenshots >/dev/null 2>&1; then | |
| git fetch origin ci-screenshots:ci-screenshots | |
| git checkout ci-screenshots | |
| else | |
| git checkout --orphan ci-screenshots | |
| git rm -rf . | |
| fi | |
| mkdir -p "pr-${pr_number}" | |
| cp "$image_path" "pr-${pr_number}/gitpilot.png" | |
| git add "pr-${pr_number}/gitpilot.png" | |
| if git diff --cached --quiet; then | |
| echo "Screenshot branch already has the latest image." | |
| else | |
| git commit -m "Update GitPilot screenshot for PR #${pr_number}" | |
| git push origin HEAD:ci-screenshots | |
| fi | |
| echo "image-url=https://raw.githubusercontent.com/${{ github.repository }}/ci-screenshots/pr-${pr_number}/gitpilot.png?run=${{ github.run_id }}" >> "$GITHUB_OUTPUT" | |
| - name: Comment screenshot on pull request | |
| if: success() && github.event_name == 'pull_request' && steps.upload-screenshot.outputs.artifact-url != '' | |
| uses: actions/github-script@v8 | |
| continue-on-error: true | |
| with: | |
| script: | | |
| const marker = '<!-- gitpilot-app-screenshot -->'; | |
| const artifactUrl = '${{ steps.upload-screenshot.outputs.artifact-url }}'; | |
| const imageUrl = '${{ steps.publish-screenshot.outputs.image-url }}' || artifactUrl; | |
| const body = [ | |
| marker, | |
| '### GitPilot app screenshot', | |
| '', | |
| ``, | |
| '', | |
| `Download all screenshot artifacts: ${artifactUrl}`, | |
| ].join('\n'); | |
| const { owner, repo } = context.repo; | |
| const issue_number = context.issue.number; | |
| try { | |
| const comments = await github.paginate(github.rest.issues.listComments, { | |
| owner, | |
| repo, | |
| issue_number, | |
| per_page: 100, | |
| }); | |
| const existing = comments.find((comment) => | |
| comment.user?.type === 'Bot' && comment.body?.includes(marker) | |
| ); | |
| if (existing) { | |
| await github.rest.issues.updateComment({ | |
| owner, | |
| repo, | |
| comment_id: existing.id, | |
| body, | |
| }); | |
| } else { | |
| await github.rest.issues.createComment({ | |
| owner, | |
| repo, | |
| issue_number, | |
| body, | |
| }); | |
| } | |
| } catch (error) { | |
| if (error.status === 403) { | |
| core.warning(`Skipping PR screenshot comment because the workflow token cannot write comments: ${error.message}`); | |
| } else { | |
| throw error; | |
| } | |
| } | |
| - name: Summarize screenshot artifact | |
| if: always() | |
| run: | | |
| { | |
| echo "### GitPilot app screenshot" | |
| echo | |
| echo "The workflow builds the app, opens browser demo mode, captures it through Chrome DevTools with hard timeouts, and uploads the screenshot as the \`gitpilot-app-screenshot\` artifact." | |
| echo | |
| echo "Open this workflow run from the pull request checks to download the screenshot artifact." | |
| } >> "$GITHUB_STEP_SUMMARY" |