Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 103 additions & 0 deletions .github/workflows/visual-capture.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
name: FoundLab Visual Capture

on:
workflow_call:
inputs:
url:
description: "Base production or preview URL to capture."
required: true
type: string
routes:
description: "Comma-separated routes to capture. Example: /,/login,/pricing"
required: false
default: ""
type: string
artifact-name:
description: "GitHub Actions artifact name."
required: false
default: "visual-capture"
type: string
wait-ms:
description: "Extra stabilization wait after navigation and readiness checks."
required: false
default: "2500"
type: string
full-page:
description: "Whether to capture full-page screenshots."
required: false
default: "true"
type: string
ready-selector:
description: "Optional selector that must be visible before screenshot capture."
required: false
default: ""
type: string
viewports-json:
description: "Optional JSON array of viewport targets."
required: false
default: ""
type: string
fail-fast:
description: "Stop after first failed capture."
required: false
default: "false"
type: string
kit-ref:
description: "Ref of FoundLab-PoweredByGoogleCloud/.github to checkout. Use main, a protected tag like v1, or a commit SHA."
required: false
default: "main"
type: string

jobs:
capture:
name: Capture visual evidence
runs-on: ubuntu-latest

permissions:
contents: read
actions: read

defaults:
run:
working-directory: visual-capture-kit

steps:
- name: Checkout organizational automation repository
uses: actions/checkout@v4
with:
repository: FoundLab-PoweredByGoogleCloud/.github
ref: ${{ inputs.kit-ref }}

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "22"

- name: Install dependencies
run: npm install

- name: Install Playwright Chromium
run: npx playwright install --with-deps chromium

- name: Typecheck capture runner
run: npm run typecheck

- name: Capture screenshots
env:
CAPTURE_URL: ${{ inputs.url }}
CAPTURE_ROUTES: ${{ inputs.routes }}
CAPTURE_DIR: artifacts/screenshots
CAPTURE_WAIT_MS: ${{ inputs.wait-ms }}
CAPTURE_FULL_PAGE: ${{ inputs.full-page }}
CAPTURE_READY_SELECTOR: ${{ inputs.ready-selector }}
CAPTURE_VIEWPORTS_JSON: ${{ inputs.viewports-json }}
CAPTURE_FAIL_FAST: ${{ inputs.fail-fast }}
run: npm run capture

- name: Upload screenshots and manifest
uses: actions/upload-artifact@v4
if: always()
with:
name: ${{ inputs.artifact-name }}
path: visual-capture-kit/artifacts/screenshots
if-no-files-found: error
2 changes: 2 additions & 0 deletions visual-capture-kit/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
artifacts/
node_modules/
160 changes: 160 additions & 0 deletions visual-capture-kit/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
# FoundLab Visual Capture Kit

Organizational visual evidence runner for production and preview UIs.

This kit captures deterministic desktop/mobile screenshots with Playwright and uploads the result as a GitHub Actions artifact.

## What it provides

- Reusable GitHub Actions workflow.
- Chromium-based screenshots via Playwright.
- Desktop and mobile default viewports.
- Multi-route capture.
- Optional readiness selector.
- Artifact upload containing PNG files and `manifest.json`.
- Structured JSON logs.

## Reusable workflow

Consumer repositories should call:

```yaml
jobs:
visual-capture:
uses: FoundLab-PoweredByGoogleCloud/.github/.github/workflows/visual-capture.yml@main
with:
url: https://example.com
routes: /,/login,/pricing
artifact-name: production-visual-capture
```

For production hardening, pin the workflow to a protected tag or commit SHA instead of `@main`.

## Inputs

| Input | Required | Default | Description |
| --- | --- | --- | --- |
| `url` | yes | — | Base URL to capture. |
| `routes` | no | empty | Comma-separated routes. Empty captures only the base URL. |
| `artifact-name` | no | `visual-capture` | GitHub artifact name. |
| `wait-ms` | no | `2500` | Extra stabilization wait before screenshot. |
| `full-page` | no | `true` | Capture full page instead of viewport only. |
| `ready-selector` | no | empty | CSS selector that must be visible before capture. |
| `viewports-json` | no | desktop/mobile | JSON array of custom viewport targets. |
| `fail-fast` | no | `false` | Stop on first failed capture. |

## Default outputs

```txt
artifacts/screenshots/root-desktop.png
artifacts/screenshots/root-mobile.png
artifacts/screenshots/manifest.json
```

For multiple routes, the output pattern is:

```txt
artifacts/screenshots/<route>-<viewport>.png
```

Example:

```txt
artifacts/screenshots/login-desktop.png
artifacts/screenshots/login-mobile.png
```

## Manifest contract

```json
{
"schema": "foundlab.visual_capture.v1",
"generatedAt": "2026-01-01T00:00:00.000Z",
"input": {
"captureUrl": "https://example.com",
"routes": ["/", "/login"],
"viewports": [
{
"label": "desktop",
"viewport": {
"width": 1440,
"height": 900
}
}
],
"fullPage": true,
"waitMs": 2500,
"readySelector": null
},
"summary": {
"total": 2,
"passed": 2,
"failed": 0
},
"results": []
}
```

## Production deploy example

```yaml
name: Deploy Production

on:
push:
branches:
- main

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm run deploy

visual-capture:
needs: deploy
uses: FoundLab-PoweredByGoogleCloud/.github/.github/workflows/visual-capture.yml@main
with:
url: https://app.example.com
routes: /,/login,/dashboard
artifact-name: production-screenshots
ready-selector: "[data-ready='app']"
```

## Local execution

```bash
cd visual-capture-kit
npm install
npx playwright install chromium
CAPTURE_URL=http://localhost:3000 npm run capture
```

Optional variables:

```bash
CAPTURE_URL=http://localhost:3000 \
CAPTURE_ROUTES=/,/login,/pricing \
CAPTURE_DIR=artifacts/screenshots \
CAPTURE_WAIT_MS=2500 \
CAPTURE_FULL_PAGE=true \
CAPTURE_READY_SELECTOR="[data-ready='app']" \
npm run capture
```

## Recommended versioning

- Development: `@main`.
- Production: protected tag like `@v1`.
- High-assurance production: commit SHA.

Avoid calling mutable branches from critical release workflows unless the branch is protected.

## Roadmap

- v1: screenshots and manifest artifacts.
- v2: authenticated capture via Playwright storage state.
- v3: visual diff against baseline.
- v4: release evidence bundle with signed provenance.
17 changes: 17 additions & 0 deletions visual-capture-kit/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "@foundlab/visual-capture-kit",
"version": "1.0.0",
"private": true,
"type": "module",
"description": "FoundLab organizational visual capture kit for production and preview UI evidence.",
"scripts": {
"capture": "tsx src/capture.ts",
"typecheck": "tsc --noEmit"
},
"devDependencies": {
"@types/node": "latest",
"playwright": "latest",
"tsx": "latest",
"typescript": "latest"
}
}
Loading