Build static API #732
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: Build static API | |
| permissions: | |
| contents: read | |
| pages: write | |
| id-token: write | |
| concurrency: | |
| group: static-api-${{ github.ref }} | |
| cancel-in-progress: true | |
| on: | |
| push: | |
| branches: [main, master] | |
| paths: | |
| - 'src/**' | |
| - 'data/**' | |
| - 'public/**' | |
| - 'docs/**' | |
| - 'mkdocs.yml' | |
| - 'requirements.txt' | |
| - 'package.json' | |
| - '.github/workflows/build.yml' | |
| workflow_dispatch: | |
| inputs: | |
| force: | |
| description: 'Force rebuild (ignore auto flags)' | |
| required: false | |
| default: 'false' | |
| schedule: | |
| - cron: '0 */6 * * *' | |
| jobs: | |
| build: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| pages: write | |
| id-token: write | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Sync to latest branch tip | |
| env: | |
| BRANCH_NAME: ${{ github.ref_name }} | |
| run: | | |
| git fetch origin "$BRANCH_NAME" | |
| git checkout "$BRANCH_NAME" || git switch -c "$BRANCH_NAME" | |
| git reset --hard "origin/$BRANCH_NAME" | |
| - name: Debounce to batch multiple merges | |
| if: ${{ github.event_name == 'push' }} | |
| env: | |
| DEBOUNCE_SECONDS: ${{ vars.BUILD_DEBOUNCE_SECONDS }} | |
| run: | | |
| SLEEP=${DEBOUNCE_SECONDS:-30} | |
| echo "Debouncing for ${SLEEP}s to batch merges..." | |
| sleep "${SLEEP}" | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| - name: Setup Python | |
| uses: actions/setup-python@v4 | |
| with: | |
| python-version: '3.x' | |
| - name: Install MkDocs and dependencies | |
| run: | | |
| pip install -r requirements.txt | |
| - name: Install Node dependencies | |
| run: | | |
| npm ci | |
| - name: Build (check changes) | |
| id: check | |
| run: | | |
| npm run check || echo "CHANGED=$?" >> $GITHUB_OUTPUT | |
| shell: bash | |
| - name: Generate docs markdown from API | |
| if: ${{ steps.check.outputs.CHANGED == '2' || github.event_name == 'workflow_dispatch' || github.event_name == 'push' }} | |
| run: | | |
| npm run docs-md-only | |
| - name: Build MkDocs documentation | |
| if: ${{ steps.check.outputs.CHANGED == '2' || github.event_name == 'workflow_dispatch' || github.event_name == 'push' }} | |
| run: | | |
| mkdocs build -d dist | |
| - name: Build API data | |
| if: ${{ steps.check.outputs.CHANGED == '2' || github.event_name == 'workflow_dispatch' || github.event_name == 'push' }} | |
| run: | | |
| if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ "${{ inputs.force }}" = "true" ]; then | |
| npm run api-only -- --force | |
| else | |
| npm run api-only | |
| fi | |
| - name: Commit and push changes | |
| if: ${{ steps.check.outputs.CHANGED == '2' || github.event_name == 'workflow_dispatch' || github.event_name == 'push' }} | |
| id: commit | |
| env: | |
| BRANCH_NAME: ${{ github.ref_name }} | |
| run: | | |
| if [ -n "$(git status --porcelain)" ]; then | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| git add dist | |
| DT=$(date -u +"%Y-%m-%dT%H:%M:%SZ") | |
| if [ "${{ github.event_name }}" = "push" ]; then | |
| TRIGGER_MSG="triggered by push to ${{ github.ref_name }}" | |
| elif [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | |
| if [ "${{ inputs.force }}" = "true" ]; then | |
| TRIGGER_MSG="triggered by manual dispatch (force rebuild)" | |
| else | |
| TRIGGER_MSG="triggered by manual dispatch" | |
| fi | |
| elif [ "${{ github.event_name }}" = "schedule" ]; then | |
| TRIGGER_MSG="triggered by scheduled run" | |
| else | |
| TRIGGER_MSG="triggered by ${{ github.event_name }}" | |
| fi | |
| COMMIT_MSG="📦 chore(dist): update static API - ${TRIGGER_MSG} [${DT}]" | |
| git commit -m "${COMMIT_MSG}" | |
| # Ensure we are rebased on the latest remote tip to avoid non-ff | |
| git pull --rebase origin "$BRANCH_NAME" || true | |
| # Push fast-forward | |
| git push origin HEAD:"$BRANCH_NAME" | |
| echo "COMMITTED=true" >> $GITHUB_OUTPUT | |
| echo "COMMIT_MESSAGE=${COMMIT_MSG}" >> $GITHUB_OUTPUT | |
| else | |
| echo "No changes to commit" | |
| echo "COMMITTED=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Check for release trigger in commit message | |
| if: ${{ github.event_name == 'push' }} | |
| id: release_check | |
| run: | | |
| # Trigger release only if messages in THIS push contain 'release' (case-insensitive) | |
| BEFORE_SHA="${{ github.event.before }}" | |
| AFTER_SHA="${{ github.event.after }}" | |
| echo "Scanning push range ${BEFORE_SHA}..${AFTER_SHA} for 'release'..." | |
| RECENT_MSGS=$(git log --pretty=format:"%B" ${BEFORE_SHA}..${AFTER_SHA}) | |
| echo "$RECENT_MSGS" | sed -n '1,5p' | |
| if echo "$RECENT_MSGS" | grep -i 'release' > /dev/null; then | |
| echo "Release trigger found" | |
| echo "SHOULD_RELEASE=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "No release trigger found" | |
| echo "SHOULD_RELEASE=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Generate version and create release | |
| if: ${{ github.event_name == 'push' && steps.release_check.outputs.SHOULD_RELEASE == 'true' }} | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| # Generate timestamp-based version number | |
| VERSION="v$(date -u +"%Y.%m.%d.%H%M")" | |
| echo "Generated version: $VERSION" | |
| # Tag the user commit (not the bot commit) | |
| BEFORE_SHA="${{ github.event.before }}" | |
| AFTER_SHA="${{ github.event.after }}" | |
| git tag $VERSION ${AFTER_SHA} | |
| git push origin $VERSION | |
| # Build release notes from last version to this version, exclude github-actions[bot] | |
| # Get the previous version tag | |
| PREV_VERSION=$(git tag --sort=-version:refname | grep -E "^v[0-9]{4}\.[0-9]{2}\.[0-9]{2}\.[0-9]{4}$" | grep -v "^$VERSION$" | head -1) | |
| if [ -n "$PREV_VERSION" ]; then | |
| echo "Previous version found: $PREV_VERSION" | |
| RECENT_COMMITS=$(git log --pretty=format:"%an%x09%s" ${PREV_VERSION}..${AFTER_SHA} | awk -F'\t' '$1!="github-actions[bot]" {print "- "$2}') | |
| else | |
| echo "No previous version found, using recent commits" | |
| RECENT_COMMITS=$(git log --pretty=format:"%an%x09%s" --max-count=50 ${AFTER_SHA} | awk -F'\t' '$1!="github-actions[bot]" {print "- "$2}') | |
| fi | |
| # Fallback when no commits were found in the computed range | |
| if [ -z "$RECENT_COMMITS" ]; then | |
| RECENT_COMMITS="- No changes found" | |
| fi | |
| # Create release notes | |
| cat > release_notes.md << EOF | |
| ## 🚀 Automated Release $VERSION | |
| **Build Time:** $(date -u +"%Y-%m-%d %H:%M:%S UTC") | |
| **Triggered By:** ${{ github.event_name }} | |
| ### 📋 Recent Changes | |
| $RECENT_COMMITS | |
| ### 🔗 Usage | |
| For complete API documentation and endpoints, visit: https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }} | |
| > This version is automatically built and released via GitHub Actions | |
| EOF | |
| # Create release using GitHub CLI | |
| gh release create $VERSION \ | |
| --title "$VERSION" \ | |
| --notes-file release_notes.md \ | |
| --latest | |
| - name: Upload Pages artifact | |
| uses: actions/upload-pages-artifact@v3 | |
| with: | |
| path: dist | |
| deploy: | |
| needs: build | |
| runs-on: ubuntu-latest | |
| concurrency: | |
| group: pages | |
| cancel-in-progress: true | |
| environment: | |
| name: github-pages | |
| url: ${{ steps.deployment.outputs.page_url }} | |
| permissions: | |
| pages: write | |
| id-token: write | |
| steps: | |
| - name: Deploy to GitHub Pages | |
| id: deployment | |
| uses: actions/deploy-pages@v4 |