Repository navigation
feat: one source-control panel, in every mode #84
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
| # Build and test on every push and PR; optionally deploy to Cloudflare Pages. | |
| # | |
| # Two ways to deploy, pick one: | |
| # | |
| # 1. Cloudflare Pages Git integration (dashboard-driven): connect the repo in | |
| # the Cloudflare dashboard with build command `npm run build` and output | |
| # directory `dist/feastdocs/browser`. This workflow then just provides CI | |
| # checks — the deploy job below stays skipped because the secrets are absent. | |
| # Caveat: Cloudflare's own builder may clone shallowly, which can blank out | |
| # the "last updated by" authors (they come from git history at build time). | |
| # | |
| # 2. Deploy from this workflow (recommended — guarantees full git history): | |
| # create a Pages project once (`wrangler pages project create feastdocs`), | |
| # then add two repository secrets: | |
| # CLOUDFLARE_API_TOKEN (API token with the "Cloudflare Pages — Edit" permission) | |
| # CLOUDFLARE_ACCOUNT_ID (dashboard → Workers & Pages → account ID) | |
| # The deploy job activates by itself once they exist. | |
| name: ci | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| jobs: | |
| build: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| # Full history — "last updated by" on every page is read from git log | |
| # at build time; a shallow clone would blank out most authors. | |
| fetch-depth: 0 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 22 | |
| cache: npm | |
| - run: npm ci | |
| - run: npm test | |
| - run: npm run build | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: site | |
| path: dist/feastdocs/browser | |
| retention-days: 7 | |
| deploy: | |
| needs: build | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/main' | |
| runs-on: ubuntu-latest | |
| steps: | |
| # Job-level `if` cannot read secrets, so the gate lives in a step: when | |
| # the Cloudflare secrets are not configured, every later step is skipped | |
| # and the job succeeds as a no-op. | |
| - name: Check Cloudflare secrets | |
| id: cf | |
| run: echo "configured=${{ secrets.CLOUDFLARE_API_TOKEN != '' && secrets.CLOUDFLARE_ACCOUNT_ID != '' }}" >> "$GITHUB_OUTPUT" | |
| - name: Download site | |
| if: steps.cf.outputs.configured == 'true' | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: site | |
| path: site | |
| # Functions (e.g. the GitHub OAuth exchange) deploy alongside the assets. | |
| - name: Include Pages Functions | |
| if: steps.cf.outputs.configured == 'true' | |
| uses: actions/checkout@v4 | |
| with: | |
| path: repo | |
| - name: Stage functions next to the site | |
| if: steps.cf.outputs.configured == 'true' | |
| run: cp -r repo/functions functions | |
| # Project name: repo variable CLOUDFLARE_PAGES_PROJECT, or "feastdocs". | |
| # Created on first run if it does not exist yet (harmless when it does). | |
| - name: Ensure the Pages project exists | |
| if: steps.cf.outputs.configured == 'true' | |
| continue-on-error: true | |
| uses: cloudflare/wrangler-action@v3 | |
| with: | |
| apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} | |
| accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} | |
| command: pages project create ${{ vars.CLOUDFLARE_PAGES_PROJECT || 'feastdocs' }} --production-branch=main | |
| # "Sign in with GitHub" needs two variables on the Pages project. Syncing | |
| # them from GitHub secrets guarantees they land on the right project and | |
| # environment — no dashboard hunting. Optional: skipped when unset. | |
| - name: Sync OAuth secrets to the Pages project | |
| if: steps.cf.outputs.configured == 'true' | |
| env: | |
| CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} | |
| CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} | |
| OAUTH_ID: ${{ secrets.OAUTH_GITHUB_CLIENT_ID }} | |
| OAUTH_SECRET: ${{ secrets.OAUTH_GITHUB_CLIENT_SECRET }} | |
| PROJECT: ${{ vars.CLOUDFLARE_PAGES_PROJECT || 'feastdocs' }} | |
| run: | | |
| if [ -n "$OAUTH_ID" ] && [ -n "$OAUTH_SECRET" ]; then | |
| printf '%s' "$OAUTH_ID" | npx --yes wrangler@4 pages secret put GITHUB_CLIENT_ID --project-name "$PROJECT" | |
| printf '%s' "$OAUTH_SECRET" | npx --yes wrangler@4 pages secret put GITHUB_CLIENT_SECRET --project-name "$PROJECT" | |
| else | |
| echo "OAuth secrets not set in GitHub — skipping (the dashboard route works too)." | |
| fi | |
| - name: Deploy to Cloudflare Pages | |
| if: steps.cf.outputs.configured == 'true' | |
| uses: cloudflare/wrangler-action@v3 | |
| with: | |
| apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} | |
| accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} | |
| command: pages deploy site --project-name=${{ vars.CLOUDFLARE_PAGES_PROJECT || 'feastdocs' }} --branch=main | |
| # Keeps the starter template in step with this repository. The template is a | |
| # derived artifact — every feature that lands here is in it on the next push, | |
| # so nobody has to remember to port anything. | |
| # | |
| # Needs a repository secret TEMPLATE_SYNC_TOKEN: a fine-grained PAT with | |
| # Contents: Read and write on the template repository. The automatic | |
| # GITHUB_TOKEN cannot push to another repository. Skipped when absent. | |
| template: | |
| runs-on: ubuntu-latest | |
| needs: build | |
| if: github.ref == 'refs/heads/main' && github.event_name == 'push' | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Sync the starter template | |
| env: | |
| TOKEN: ${{ secrets.TEMPLATE_SYNC_TOKEN }} | |
| TARGET: ${{ vars.TEMPLATE_REPO || 'Mindfeast/feastdocs-template' }} | |
| run: | | |
| if [ -z "$TOKEN" ]; then | |
| echo "TEMPLATE_SYNC_TOKEN not set — skipping template sync." | |
| exit 0 | |
| fi | |
| git config --global user.name "github-actions[bot]" | |
| git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| git clone --quiet "https://x-access-token:$TOKEN@github.com/$TARGET.git" /tmp/template | |
| node tools/sync-template.mjs /tmp/template --push |