feat(plugins-home): pre-baked hover-pan preview clips for the gallery… #5
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: 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 |