https://github.com/guibranco/github-screenshot-action
📸 A reusable GitHub Action to capture, monitor, and version website screenshots from a JSON list with parallel execution, retries, and cron-based change detection
https://github.com/guibranco/github-screenshot-action
gha github-actions githubactions screenshot
Last synced: about 1 month ago
JSON representation
📸 A reusable GitHub Action to capture, monitor, and version website screenshots from a JSON list with parallel execution, retries, and cron-based change detection
- Host: GitHub
- URL: https://github.com/guibranco/github-screenshot-action
- Owner: guibranco
- License: mit
- Created: 2026-03-19T16:02:39.000Z (3 months ago)
- Default Branch: main
- Last Pushed: 2026-04-20T05:35:27.000Z (about 2 months ago)
- Last Synced: 2026-04-20T07:37:35.014Z (about 2 months ago)
- Topics: gha, github-actions, githubactions, screenshot
- Language: TypeScript
- Homepage: http://guilherme.stracini.com.br/github-screenshot-action/
- Size: 442 KB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# 📸 github-screenshot-action
**Capture, monitor, and version website screenshots — automatically.**
A reusable GitHub Action that takes screenshots of websites from a JSON list, with parallel execution, retry logic, cron scheduling, and automated PR creation.
[](https://github.com/guibranco/github-screenshot-action/releases)
[](LICENSE)
[](https://ghcr.io/guibranco/github-screenshot-action)
[](https://github.com/guibranco/github-screenshot-action)
---
## ✨ Features
| Feature | Description |
|---|---|
| 📄 **JSON-driven** | Define all your target sites in a simple JSON file — no code changes needed |
| ⚡ **Parallel execution** | Configurable concurrency to capture multiple sites simultaneously |
| 🔁 **Retry & timeout** | Automatically retries failed captures with configurable limits |
| 🕐 **Cron scheduling** | Run on any schedule to monitor visual changes over time |
| 🌿 **Branch isolation** | Screenshots are committed to a dedicated branch, keeping `main` clean |
| 🔀 **Automated PRs** | Optionally open a pull request automatically after each monitoring run |
| 🐳 **Pre-built Docker image** | No cold build — uses a pre-published image from GHCR for fast startup |
| 🔐 **Chromium-based** | Full Puppeteer + Chromium stack for accurate, real-browser rendering |
| ⏳ **Wait strategies** | Wait for full page load, network idle, or DOM ready before capturing |
| 🟥 **Square mode** | Optionally clip output to a square for thumbnails or social previews |
---
## 🚀 Quick Start
### 1. Create your sites file
Add a `sites.json` file to your repository:
```json
[
{ "name": "homepage", "url": "https://example.com" },
{ "name": "dashboard", "url": "https://app.example.com/dashboard" },
{ "name": "pricing", "url": "https://example.com/pricing" }
]
```
Each entry requires a `url` and a `name`. The `name` becomes the screenshot filename (e.g. `homepage.png`).
### 2. Create your workflow
```yaml
name: Website monitoring
on:
schedule:
- cron: "0 */6 * * *" # every 6 hours
workflow_dispatch:
jobs:
monitor:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run screenshot monitoring
uses: guibranco/github-screenshot-action@v2.0.17
with:
json_file: "sites.json"
output_dir: "screenshots/"
concurrency: "5"
retries: "2"
timeout_ms: "20000"
create_pr: "true"
branch_name: "monitor/screenshots"
wait_until: "networkidle0"
square: "false"
viewport_width: "1280"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```
---
## ⚙️ Inputs
| Input | Required | Default | Description |
|---|---|---|---|
| `json_file` | ✅ | `screenshots.json` | Path to the JSON file listing sites to capture |
| `output_dir` | ✅ | `screenshots` | Directory where `.png` files will be saved |
| `concurrency` | ❌ | `3` | Number of screenshots to take in parallel |
| `retries` | ❌ | `2` | How many times to retry a failed screenshot |
| `timeout_ms` | ❌ | `30000` | Page load timeout per site in milliseconds |
| `create_pr` | ❌ | `false` | If `true`, opens a PR after committing screenshots |
| `branch_name` | ❌ | `monitor/screenshots` | Branch to commit screenshots to |
| `wait_until` | ❌ | `load` | Page load event to wait for before capturing. See [Wait Strategies](#wait-strategies) |
| `square` | ❌ | `false` | If `true`, clips the output to a square using `viewport_width` as the side length |
| `viewport_width` | ❌ | `1280` | Viewport width in pixels. Also controls the square size when `square` is `true` |
---
## 📁 JSON File Format
```json
[
{
"name": "landing-page",
"url": "https://example.com"
},
{
"name": "login",
"url": "https://example.com/login"
}
]
```
- **`name`** — used as the output filename (`name.png`). Use lowercase, hyphen-separated values for clean filenames.
- **`url`** — full URL to capture. If the protocol is omitted, `https://` is prepended automatically.
---
## 🔀 PR Automation
When `create_pr: "true"`, the action will:
1. Check out (or create) the branch specified in `branch_name`
2. Capture all screenshots and write them to `output_dir`
3. Commit any changed or new `.png` files with `[skip ci]` to avoid re-triggering workflows
4. Force-push the branch
5. Open a pull request against `main` — or skip silently if a PR already exists
This gives you a clean, reviewable diff of visual changes over time.
> **Note:** The workflow needs `GITHUB_TOKEN` passed via `env` for PR creation and branch push to work.
```yaml
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```
---
## ⏳ Wait Strategies
The `wait_until` input controls when Puppeteer considers the page ready to capture. Choose based on how dynamic the target site is:
| Value | Waits until... | Best for |
|---|---|---|
| `domcontentloaded` | HTML is parsed, no assets waited on | Simple static pages |
| `load` | All resources loaded (default) | Most standard websites |
| `networkidle2` | No more than 2 requests in flight for 500ms | Pages with background polling |
| `networkidle0` | Zero network requests for 500ms | SPAs and lazy-loaded content |
> **Tip:** `networkidle0` produces the most complete captures but is the slowest. If a site has continuous background requests (analytics, websockets), use `networkidle2` to avoid hitting the timeout.
---
## 🟥 Square Mode
When `square: "true"`, the action sets a square viewport and clips the output to `viewport_width × viewport_width` pixels. Full-page scrolling is disabled in this mode.
```yaml
square: "true"
viewport_width: "1280" # produces a 1280×1280 PNG
```
This is useful for generating consistent thumbnails, social preview images, or monitoring dashboards where uniform dimensions are required.
> **Note:** `square` and full-page capture are mutually exclusive. When `square` is enabled, only the top portion of the page visible within the square viewport is captured.
---
## 🗓️ Scheduling Examples
```yaml
# Every 6 hours
- cron: "0 */6 * * *"
# Once a day at midnight UTC
- cron: "0 0 * * *"
# Every Monday at 8am UTC
- cron: "0 8 * * 1"
# Every hour during business hours (9–17) on weekdays
- cron: "0 9-17 * * 1-5"
```
---
## 🏗️ Architecture
```
sites.json
│
▼
parser.ts ──► loadItems()
│
▼
screenshot.ts ──► Puppeteer + pLimit (parallel)
│ │
│ setViewport(width, height)
│ goto(url, { waitUntil })
│ screenshot({ fullPage | clip })
│
▼
output_dir/*.png
│
▼
git.ts ──► checkout branch
│ git add -f / commit
│ git push --force
│ GitHub API PR (optional)
▼
Pull Request / Branch
```
---
## 🐳 Docker Image
This action uses a **pre-built Docker image** published to GitHub Container Registry, so there is no build step at runtime.
```
ghcr.io/guibranco/github-screenshot-action:
```
The image includes:
- `node:24-slim` base
- Chromium and all required system dependencies
- Pre-compiled TypeScript output in `dist/`
New images are published automatically on every release via the `release.yml` workflow.
---
## 🔒 Permissions
Your workflow needs the following permissions for full functionality:
```yaml
permissions:
contents: write # to push the screenshot branch
pull-requests: write # to open PRs
```
If you are using a classic `GITHUB_TOKEN` without an explicit `permissions` block, make sure your repository's **Actions settings** allow workflows to create pull requests.
---
## 🛠️ Development
### Prerequisites
- Node.js 24+
- npm
### Setup
```bash
git clone https://github.com/guibranco/github-screenshot-action.git
cd github-screenshot-action
npm install
```
### Build
```bash
npm run build
```
Output is written to `dist/`.
### Project Structure
```
├── src/
│ ├── main.ts # Entry point — orchestrates the full run
│ ├── parser.ts # Reads and validates sites.json
│ ├── screenshot.ts # Puppeteer screenshot logic with concurrency
│ ├── git.ts # Git operations: commit, branch, PR creation
│ └── logger.ts # Emoji-prefixed console logger
├── Dockerfile # Pre-built image definition
├── entrypoint.sh # Docker entrypoint
├── action.yml # Action metadata
└── sites.json # Example sites file
```
### Publishing a new version
1. Merge your changes to `main`
2. The `release.yml` workflow automatically determines the next version via GitVersion
3. It builds and pushes the Docker image to GHCR tagged as `vX.Y.Z` and `latest`
4. It opens a PR updating the `image:` tag in `action.yml` — merge it to complete the release
---
## 📄 License
MIT © [Guilherme Branco Stracini](https://github.com/guibranco)
---
Made with ❤️ and ☕ — contributions welcome via [issues](https://github.com/guibranco/github-screenshot-action/issues) and [pull requests](https://github.com/guibranco/github-screenshot-action/pulls).