Repository navigation
fix: let the SDK join an app instead of auditioning for one #265
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: 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 |