Skip to content

fix: let the SDK join an app instead of auditioning for one #265

fix: let the SDK join an app instead of auditioning for one

fix: let the SDK join an app instead of auditioning for one #265

Workflow file for this run

name: Deploy Slidev Presentations to GitHub Pages
on:
push:
branches:
- main
paths:
- "slides/**"
- ".github/workflows/deploy-slides.yml"
workflow_dispatch:
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
cancel-in-progress: false
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v5
- name: Restore previous build cache
uses: actions/cache@v5
with:
path: ./slides/dist
key: slides-dist-${{ github.sha }}
restore-keys: |
slides-dist-
- name: Setup Node.js
uses: actions/setup-node@v5
with:
node-version: "24"
cache: npm
cache-dependency-path: slides/package-lock.json
- name: Install dependencies
working-directory: ./slides
run: npm ci
- name: Build slides
working-directory: ./slides
shell: bash {0}
run: |
mkdir -p dist/workshop dist/tech-talks dist/exec-talks dist/.build-logs
MAX_JOBS=4
PIDS=()
LABELS=()
FAILED=0
SKIPPED=0
# Shared slide infrastructure changes must rebuild every deck, even if
# individual frontmatter dates did not change.
SHARED_FINGERPRINT=$({
printf '%s\0' style.css vite.config.ts package.json package-lock.json global-top.vue global-bottom.vue
find components layouts setup -type f -print0
find tech-talks -maxdepth 1 -type f \( -name 'global-*.vue' -o -name 'sdp-logo.png' \) -print0
find exec-talks -maxdepth 1 -type f \( -name 'global-*.vue' -o -name 'sdp-logo.png' \) -print0
} | xargs -0 sha256sum | sha256sum | cut -d' ' -f1)
echo "🧩 Shared fingerprint: ${SHARED_FINGERPRINT}"
# Extract 'updated' date from frontmatter (first 20 lines)
get_updated() {
head -20 "$1" | sed -n 's/^updated: *//p'
}
# Load previous build manifest
declare -A PREV_UPDATED
PREV_SHARED_FINGERPRINT=""
if [ -f "dist/.build-manifest.json" ]; then
while IFS='=' read -r key val; do
if [ "$key" = "__shared_fingerprint" ]; then
PREV_SHARED_FINGERPRINT="$val"
else
PREV_UPDATED["$key"]="$val"
fi
done < <(python3 -c "
import json
with open('dist/.build-manifest.json') as f:
m = json.load(f)
for k, v in m.items():
print(f'{k}={v}')
" 2>/dev/null || true)
echo "πŸ“‹ Loaded manifest with ${#PREV_UPDATED[@]} entries"
if [ "$SHARED_FINGERPRINT" != "$PREV_SHARED_FINGERPRINT" ]; then
echo "πŸ” Shared slide infrastructure changed β€” forcing rebuild of all decks"
fi
else
echo "πŸ“‹ No manifest β€” building all slides"
fi
# Decide if a slide needs building: compare date@content-hash stamp vs manifest
needs_build() {
local KEY="$1"
local CURRENT_STAMP="$2"
[ "$SHARED_FINGERPRINT" != "$PREV_SHARED_FINGERPRINT" ] && return 0 # shared dependency changed
[ ! -d "dist/${KEY}" ] && return 0 # No dist output
[ -z "${PREV_UPDATED[$KEY]+x}" ] && return 0 # Not in manifest
[ "$CURRENT_STAMP" != "${PREV_UPDATED[$KEY]}" ] # Date or content changed
}
# Build with retry
build_slide() {
local SLIDE_FILE="$1"
local CATEGORY=$(dirname "$SLIDE_FILE")
local BASENAME=$(basename "$SLIDE_FILE" .md)
local LOG_FILE="dist/.build-logs/${CATEGORY}--${BASENAME}.log"
for ATTEMPT in 1 2; do
[ $ATTEMPT -eq 1 ] && echo "πŸ”¨ Building ${CATEGORY}/${BASENAME}..." \
|| echo "πŸ”„ Retrying ${CATEGORY}/${BASENAME} (attempt 2)..."
if npx slidev build "$SLIDE_FILE" \
--base "/CopilotTraining/${CATEGORY}/${BASENAME}/" \
--out "$(pwd)/dist/${CATEGORY}/${BASENAME}" \
> "$LOG_FILE" 2>&1; then
return 0
fi
done
echo "::error::Build failed for ${CATEGORY}/${BASENAME}. Log (head):"
head -40 "$LOG_FILE"
echo "--- (tail) ---"
tail -10 "$LOG_FILE"
return 1
}
# Wait for a parallel slot to free up
wait_for_slot() {
while [ ${#PIDS[@]} -ge $MAX_JOBS ]; do
NEW_PIDS=(); NEW_LABELS=()
for i in "${!PIDS[@]}"; do
if kill -0 "${PIDS[$i]}" 2>/dev/null; then
NEW_PIDS+=("${PIDS[$i]}"); NEW_LABELS+=("${LABELS[$i]}")
else
wait "${PIDS[$i]}" && echo "βœ… ${LABELS[$i]} built successfully" \
|| { echo "❌ ${LABELS[$i]} FAILED"; FAILED=1; }
fi
done
PIDS=("${NEW_PIDS[@]}"); LABELS=("${NEW_LABELS[@]}")
[ ${#PIDS[@]} -ge $MAX_JOBS ] && sleep 1
done
}
# Iterate all slides β€” build only what's needed
declare -A NEW_MANIFEST
NEW_MANIFEST["__shared_fingerprint"]="$SHARED_FINGERPRINT"
for SLIDE_FILE in workshop/*.md tech-talks/*.md exec-talks/*.md; do
[ -f "$SLIDE_FILE" ] || continue
CATEGORY=$(dirname "$SLIDE_FILE")
BASENAME=$(basename "$SLIDE_FILE" .md)
KEY="${CATEGORY}/${BASENAME}"
CURRENT_UPDATED=$(get_updated "$SLIDE_FILE")
# Date plus content hash, so same-day edits still rebuild
CURRENT_STAMP="${CURRENT_UPDATED}@$(sha256sum "$SLIDE_FILE" | cut -c1-12)"
# Always track in manifest
NEW_MANIFEST["$KEY"]="$CURRENT_STAMP"
# Skip if date and content match and dist exists
if ! needs_build "$KEY" "$CURRENT_STAMP"; then
echo "⏭️ ${KEY} (updated: ${CURRENT_UPDATED}) β€” up to date"
SKIPPED=$((SKIPPED + 1))
continue
fi
wait_for_slot
build_slide "$SLIDE_FILE" &
PIDS+=($!)
LABELS+=("${KEY}")
done
# Wait for remaining builds
for i in "${!PIDS[@]}"; do
wait "${PIDS[$i]}" && echo "βœ… ${LABELS[$i]} built successfully" \
|| { echo "❌ ${LABELS[$i]} FAILED"; FAILED=1; }
done
[ $FAILED -eq 1 ] && { echo "❌ Some builds failed"; exit 1; }
# Write manifest for next run
python3 -c "
import json, sys
manifest = {}
for line in sys.stdin:
if '=' in line:
k, v = line.strip().split('=', 1)
manifest[k] = v
with open('dist/.build-manifest.json', 'w') as f:
json.dump(manifest, f, indent=2, sort_keys=True)
" <<< "$(for key in "${!NEW_MANIFEST[@]}"; do echo "${key}=${NEW_MANIFEST[$key]}"; done)"
# Copy index, 404, and shared assets
cp index-custom.html dist/index.html
cp 404.html dist/404.html
mkdir -p dist/tech-talks
cp tech-talks/sdp-logo.png dist/tech-talks/sdp-logo.png
# Ship pre-inspected companions (agenda.html / agenda.md / deck.pdf).
# Do NOT generate agendas or PDFs in CI β€” authors build those locally
# via the Deploy agent / npm run generate-agendas + export-pdf.
# Companions must be committed under slides/companions/ to ship.
node scripts/copy-companions.mjs
# Soft report only (CI cannot regenerate PDFs if stale).
node scripts/check-companions.mjs || true
# Hard check: every file under companions/ must land in dist/.
node scripts/verify-companions-dist.mjs
BUILT=${#LABELS[@]}
echo "πŸ“Š ${BUILT} built, ${SKIPPED} skipped (up to date)"
- name: Upload artifact
uses: actions/upload-pages-artifact@v5
with:
path: ./slides/dist
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v5