Skip to content

Add commit-level actions, graph filtering, demo mode, and headless screenshot capture #24

Add commit-level actions, graph filtering, demo mode, and headless screenshot capture

Add commit-level actions, graph filtering, demo mode, and headless screenshot capture #24

Workflow file for this run

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',
'',
`![GitPilot app screenshot](${imageUrl})`,
'',
`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"