Skip to content

feat(plugins-home): pre-baked hover-pan preview clips for the gallery… #5

feat(plugins-home): pre-baked hover-pan preview clips for the gallery…

feat(plugins-home): pre-baked hover-pan preview clips for the gallery… #5

name: bake-plugin-previews
# Pre-render the home gallery's plugin previews to small H.264 clips + posters.
# We bake ALL plugins every run; the script's content-hash skip (manifest hash +
# BAKE_VERSION) makes that cheap — only plugins whose page actually changed are
# re-rendered and re-uploaded. Runs post-merge (so fork contributions are covered
# with full secrets) and nightly as a self-healing sweep. Until a plugin is baked
# the gallery falls back to its live example.html iframe, so there is no gap.
on:
push:
branches: [main]
paths:
- 'plugins/_official/**'
- 'scripts/bake-plugin-previews.mjs'
schedule:
- cron: '0 18 * * *' # nightly self-healing full sweep
workflow_dispatch: {}
permissions:
contents: write # push the manifest branch
pull-requests: write # open the review PR (gh pr create needs this; unset perms default to none)
concurrency:
group: bake-plugin-previews
cancel-in-progress: false
jobs:
bake:
name: Bake plugin previews
runs-on: ubuntu-24.04
timeout-minutes: 90
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
- name: Setup workspace
uses: ./.github/actions/setup-workspace
- name: Install ffmpeg + puppeteer-core
run: |
sudo apt-get update
sudo apt-get install -y ffmpeg
# npm chokes on the workspace:* root manifest; use pnpm to drop
# puppeteer-core into the workspace node_modules (CI-only, not committed).
pnpm add -w puppeteer-core
- name: Build daemon + tools
run: |
pnpm --filter "./packages/**" build
pnpm --filter @open-design/daemon build
pnpm --filter @open-design/tools-dev build
- name: Start daemon
run: |
pnpm tools-dev start daemon --namespace ci --daemon-port 17470
for _ in $(seq 1 60); do
if curl -sf -o /dev/null http://127.0.0.1:17470/api/plugins; then
echo "daemon ready"; exit 0
fi
sleep 2
done
echo "daemon did not become ready" >&2; exit 1
- name: Bake (content-hash skip reuses unchanged plugins)
env:
BASE_URL: http://127.0.0.1:17470
# The CI clips live on R2, not on disk, so trust the manifest hash.
PREVIEW_REMOTE: '1'
run: |
CHROME="$(which google-chrome || which google-chrome-stable || which chromium-browser)"
export CHROME
echo "resolved CHROME=$CHROME"
mkdir -p .tmp/plugin-previews
# Seed with the committed manifest so the hash skip can reuse entries.
cp data/plugin-previews/manifest.json .tmp/plugin-previews/manifest.json
node scripts/bake-plugin-previews.mjs --out .tmp/plugin-previews
- name: Upload clips to R2
if: ${{ github.ref == 'refs/heads/main' }} # only publish from main
# Publishes to the repository-assets R2 bucket; the deployed daemon must
# set OD_PLUGIN_PREVIEWS_BASE_URL to
# https://repo-assets.open-design.ai/plugin-previews so its bakedPreview
# URLs point here (CLOUDFLARE_R2_REPOSITORY_ASSETS_PUBLIC_ORIGIN).
env:
AWS_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_R2_REPOSITORY_ASSETS_AK }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_R2_REPOSITORY_ASSETS_SK }}
AWS_DEFAULT_REGION: auto
AWS_EC2_METADATA_DISABLED: 'true'
R2_BUCKET: ${{ secrets.CLOUDFLARE_R2_REPOSITORY_ASSETS_BUCKET }}
R2_ENDPOINT: ${{ secrets.CLOUDFLARE_R2_REPOSITORY_ASSETS_URL }}
run: |
endpoint="${R2_ENDPOINT%/}"
# cp (not sync --delete) so untouched clips already on R2 stay put.
aws s3 cp .tmp/plugin-previews "s3://$R2_BUCKET/plugin-previews/" \
--recursive --no-progress \
--endpoint-url "$endpoint" \
--exclude manifest.json
- name: Open manifest PR for review
if: ${{ github.ref == 'refs/heads/main' }} # branch debug runs skip publishing
# The clips are already on R2; the manifest is version-pinned (it ships
# with the build), so it must land on main through a reviewed PR rather
# than a direct push to protected main. Fires only when a plugin actually
# changed (hash skip + diff guard), so this is quiet in steady state.
#
# NOTE: a GITHUB_TOKEN-authored PR does not trigger pull_request CI; set a
# PAT/app token secret PREVIEW_BAKE_TOKEN so the PR runs checks + can be
# merged through the queue. Falls back to GITHUB_TOKEN otherwise.
env:
GH_TOKEN: ${{ secrets.PREVIEW_BAKE_TOKEN || secrets.GITHUB_TOKEN }}
run: |
cp .tmp/plugin-previews/manifest.json data/plugin-previews/manifest.json
if git diff --quiet -- data/plugin-previews/manifest.json; then
echo "manifest unchanged — nothing to review"; exit 0
fi
BRANCH="chore/plugin-previews-${{ github.run_id }}"
git config user.name "open-design-bot"
git config user.email "bot@open-design.ai"
git checkout -b "$BRANCH"
git add data/plugin-previews/manifest.json
git commit -m "chore(plugin-previews): refresh baked preview manifest"
git push origin "$BRANCH"
gh pr create --base main --head "$BRANCH" \
--title "chore(plugin-previews): refresh baked preview manifest" \
--reviewer lefarcen \
--body "Automated by the bake-plugin-previews workflow: preview clips were re-rendered and uploaded to R2; this updates data/plugin-previews/manifest.json to match (the clips live on R2 and are not committed). @lefarcen please review and merge."
- name: Stop daemon
if: always()
run: pnpm tools-dev stop --namespace ci || true