Skip to content

fix(ci): bound every job, skip smoke on manifest-only pushes, deploy … #572

fix(ci): bound every job, skip smoke on manifest-only pushes, deploy …

fix(ci): bound every job, skip smoke on manifest-only pushes, deploy … #572

Workflow file for this run

name: Deploy GitHub Pages
# Builds the landing page (docs/index.html, via the locally vendored template at
# scripts/site/ — originally scaffolded from Developer-Tools-Directory's site-template,
# now owned and evolved by this repo) AND the local examples gallery
# (docs/gallery/index.html, generated from examples/gallery.json by scripts/build_gallery.py).
# Both ship in the single docs/ artifact; the landing build only writes docs/index.html +
# docs/fonts/ + docs/assets/, so it never clobbers docs/gallery/.
on:
push:
branches: [main]
paths:
- "skills/**"
- "rules/**"
- "snippets/**" # the landing's snippet list and What's new strip
- "site.json"
- ".cursor-plugin/plugin.json"
- "assets/**"
- "examples/**"
- "showcase/**"
- "docs/gallery/**"
- "scripts/build_gallery.py"
- "scripts/site/**"
- "CHANGELOG.md"
- ".github/workflows/pages.yml"
workflow_dispatch:
permissions:
pages: write
id-token: write
concurrency:
group: pages
cancel-in-progress: true
jobs:
# A push that will cut a release gets its deploy from release.yml, which dispatches
# this workflow after committing the version bump. Deploying on the push too only
# built a page that the dispatch then cancelled (8 of 9 push runs measured, #215).
# Both workflows call .github/scripts/bump-kind.sh, so the decision cannot drift. If that
# release fails, redeploy by hand: Actions > Deploy GitHub Pages > Run workflow.
gate:
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
deploy: ${{ steps.decide.outputs.deploy }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
sparse-checkout: |
.github/scripts
scripts/build_plugin_dist.py
- id: decide
env:
EVENT: ${{ github.event_name }}
COMMITS: ${{ toJSON(github.event.commits) }}
run: |
deploy=true
if [ "$EVENT" = "push" ] && [ "$(echo "$COMMITS" \
| jq -j '.[].message + "\u0000"' \
| bash .github/scripts/bump-kind.sh)" != "none" ]; then
deploy=false
echo "Release-worthy push: release.yml dispatches Pages after the version bump."
elif [ "$EVENT" = "push" ]; then
# A push that touches plugin content also releases (a patch, #350),
# and that release dispatches Pages too, so deploying here would be
# the duplicate (#495). Shipped paths come from the dist build.
shipped=$(python3 scripts/build_plugin_dist.py --list-paths)
while IFS= read -r f; do
f="${f%$'\r'}"
while IFS= read -r p; do
p="${p%$'\r'}"
case "$f" in "$p"|"$p"/*)
deploy=false
echo "Plugin content changed ($f): release.yml dispatches Pages after the patch release." ;;
esac
done <<< "$shipped"
[ "$deploy" = false ] && break
done < <(echo "$COMMITS" | jq -r '.[] | (.added + .modified + .removed)[]')
fi
echo "deploy=$deploy" >> "$GITHUB_OUTPUT"
echo "deploy=$deploy"
build-and-deploy:
needs: gate
if: needs.gate.outputs.deploy == 'true'
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
# Full history: the landing page's "Recently added" dates come from
# the commit that first added each example (a shallow clone hides it).
fetch-depth: 0
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
with:
python-version: "3.12"
cache: pip
cache-dependency-path: scripts/site/requirements.txt
- run: pip install -r scripts/site/requirements.txt
- name: Build landing page (vendored template)
run: python scripts/site/build_site.py --repo-root . --out docs
- name: Build local examples gallery (from examples/gallery.json)
# Stdlib-only; regenerates docs/gallery/index.html so the committed page can never
# drift from gallery.json. The committed docs/gallery/assets/*.webp are untouched.
run: python scripts/build_gallery.py
- name: Stage the public site (leave out internal docs and unlinked sheets)
run: python scripts/site/stage_public.py --src docs --out _site
- name: Check every internal link, anchor and image alt (on the staged tree)
# Checked after staging so a link to a stripped file fails here, not in production.
run: python tests/check_site_links.py --root _site
- uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6
- uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5
with:
path: _site
- uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5
id: deployment