diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 68ba080..458c9ce 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,32 +36,44 @@ jobs: - name: Install dependencies run: bun install - - name: Check proposal matches PR number + - name: Check RFC number matches PR number run: | BASE_SHA="${{ github.event.pull_request.base.sha }}" HEAD_SHA="${{ github.event.pull_request.head.sha }}" PR_NUMBER="${{ github.event.pull_request.number }}" - CHANGED_PROPOSALS=$(git diff --name-only "$BASE_SHA" "$HEAD_SHA" -- 'proposals/' \ - | grep -v '^proposals/0000-template\.md$' || true) + # Grandfathered RFCs that predate the PR number convention + GRANDFATHERED="5 15 23 24 27 29 33" - COUNT=$(echo "$CHANGED_PROPOSALS" | grep -c . || true) + # Only check newly added RFC files, not moved/renamed ones + CHANGED_RFCS=$(git diff --name-only --diff-filter=A "$BASE_SHA" "$HEAD_SHA" -- 'rfcs/' \ + | grep -v '^rfcs/0000-template\.md$' || true) + + COUNT=$(echo "$CHANGED_RFCS" | grep -c . || true) if [ "$COUNT" -gt 1 ]; then - echo "::error::PR touches $COUNT proposals. Only one proposal per PR is allowed." - echo "$CHANGED_PROPOSALS" + echo "::error::PR touches $COUNT RFCs. Only one RFC per PR is allowed." + echo "$CHANGED_RFCS" exit 1 fi if [ "$COUNT" -eq 1 ]; then - FILE=$(echo "$CHANGED_PROPOSALS" | head -1) + FILE=$(echo "$CHANGED_RFCS" | head -1) FILENAME=$(basename "$FILE") FILE_NUMBER=$(echo "$FILENAME" | grep -oE '^[0-9]+' || true) # Strip leading zeros for comparison FILE_NUM=$((10#$FILE_NUMBER)) + # Skip check for grandfathered RFCs + for GF in $GRANDFATHERED; do + if [ "$FILE_NUM" -eq "$GF" ]; then + echo "RFC $FILE_NUM is grandfathered, skipping PR number check." + exit 0 + fi + done + if [ "$FILE_NUM" -ne "$PR_NUMBER" ]; then - echo "::error::Proposal number ($FILE_NUM from $FILENAME) does not match PR number ($PR_NUMBER)." + echo "::error::RFC number ($FILE_NUM from $FILENAME) does not match PR number ($PR_NUMBER)." exit 1 fi fi diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 5b31b10..5dc87d1 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -4,6 +4,10 @@ on: push: branches: - develop + pull_request: + types: [opened, closed, reopened] + branches: + - develop permissions: contents: read @@ -23,6 +27,7 @@ jobs: - name: Checkout uses: actions/checkout@v4 with: + ref: develop fetch-depth: 0 - name: Setup Bun diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml new file mode 100644 index 0000000..1ace045 --- /dev/null +++ b/.github/workflows/preview.yml @@ -0,0 +1,41 @@ +name: RFC Preview + +on: + pull_request: + branches: + - develop + +permissions: + contents: read + deployments: write + +jobs: + preview: + env: + GH_TOKEN: ${{ github.token }} + runs-on: ubuntu-latest + environment: + name: rfc-preview + url: ${{ steps.upload.outputs.artifact-url }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + + - name: Install dependencies + run: bun install + + - name: Build preview + run: bun run index.ts --preview + + - name: Upload RFC preview + id: upload + uses: actions/upload-artifact@v7 + with: + name: rfc-preview-pr-${{ github.event.pull_request.number }} + path: ./dist/preview.html + archive: false diff --git a/CLAUDE.md b/CLAUDE.md index 5fd294c..c983765 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -7,14 +7,12 @@ Static site generator for Vortex RFC proposals built with Bun. ``` index.ts - Main build script and dev server styles.css - Site styling (light/dark themes) -proposed/ - RFC markdown files in proposed state -accepted/ - RFC markdown files in accepted state -completed/ - RFC markdown files in completed state +rfcs/ - RFC markdown files (merged to develop = accepted) dist/ - Build output (gitignored) ``` -RFC filenames follow the format `NNNN-slug.md` (e.g., `0001-galp-patches.md`). -Numbering is global across all states - no duplicates allowed. +RFC filenames follow the format `NNNN-slug.md` (e.g., `0027-patches-format.md`). +The RFC number must match the PR number used to propose it. No duplicate numbers allowed. ## Commands @@ -26,29 +24,24 @@ bun run clean # Remove dist/ ## How the Build Works -1. Scans `proposed/`, `accepted/`, `completed/` for RFC files +1. Scans `rfcs/` for RFC markdown files 2. Parses RFC number from filename (e.g., `0002-foo.md` → RFC 0002) -3. Determines state from containing folder -4. Extracts title from first `# ` heading -5. Converts markdown to HTML using `Bun.markdown.html()` -6. Generates `dist/index.html` (table of contents with filter UI) -7. Generates `dist/rfc/{number}.html` for each RFC +3. Extracts title from first `# ` heading +4. Converts markdown to HTML using `Bun.markdown.html()` +5. Generates `dist/index.html` (table of contents) +6. Generates `dist/rfc/{number}.html` for each RFC ## Dev Server - Uses `Bun.serve()` to serve static files from `dist/` -- Watches `proposed/`, `accepted/`, `completed/`, and `styles.css` for changes +- Watches `rfcs/` and `styles.css` for changes - SSE endpoint at `/__reload` for live reload -## RFC States +## RFC Workflow -RFCs progress through three states by moving files between folders: - -- **proposed**: New RFCs under discussion -- **accepted**: Approved RFCs ready for implementation -- **completed**: Fully implemented RFCs - -The index page shows a state pill for each RFC and supports filtering by state. +1. Open a PR with a new file `rfcs/NNNN-slug.md` where NNNN matches the PR number +2. PR builds a preview artifact for reviewers +3. Merging the PR to `develop` accepts the RFC ## Styling diff --git a/accepted/.keep b/accepted/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/completed/.keep b/completed/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/index.ts b/index.ts index d825752..a04293d 100644 --- a/index.ts +++ b/index.ts @@ -3,6 +3,7 @@ import { watch } from "fs"; import { createHighlighter, type Highlighter } from "shiki"; const isDev = process.argv.includes("--dev"); +const isPreview = process.argv.includes("--preview"); const PORT = 3000; interface GitCommit { @@ -22,17 +23,20 @@ interface RFCGitInfo { author: GitHubAuthor | null; } -type RFCState = "proposed" | "accepted" | "completed"; - -const RFC_STATES: RFCState[] = ["proposed", "accepted", "completed"]; - interface RFC { number: string; title: string; filename: string; html: string; git: RFCGitInfo; - state: RFCState; +} + +interface ProposedRFC { + number: string; + title: string; + prNumber: number; + prUrl: string; + author: GitHubAuthor | null; } const THEME_SCRIPT = ` @@ -71,33 +75,6 @@ function updateToggleIcon() { document.addEventListener('DOMContentLoaded', updateToggleIcon); `; -const FILTER_SCRIPT = ` -function filterRFCs(state) { - const items = document.querySelectorAll('.rfc-list li'); - const buttons = document.querySelectorAll('.filter-btn'); - - buttons.forEach(btn => { - btn.classList.toggle('active', btn.dataset.state === state); - }); - - items.forEach(item => { - if (state === 'all' || item.dataset.state === state) { - item.style.display = ''; - } else { - item.style.display = 'none'; - } - }); - - // Save filter preference - localStorage.setItem('rfc-filter', state); -} - -document.addEventListener('DOMContentLoaded', function() { - const saved = localStorage.getItem('rfc-filter') || 'all'; - filterRFCs(saved); -}); -`; - const LIVE_RELOAD_SCRIPT = ` (function() { const evtSource = new EventSource('/__reload'); @@ -160,12 +137,9 @@ function escapeHTML(str: string): string { .replace(/"/g, """); } -function stateLabel(state: RFCState): string { - return state.charAt(0).toUpperCase() + state.slice(1); -} - function indexPage( rfcs: RFC[], + proposed: ProposedRFC[], repoUrl: string | null, liveReload: boolean = false, ): string { @@ -189,10 +163,9 @@ function indexPage( } return ` -
Technical proposals for the Vortex file format.
-${filterButtons} +${proposedSection} +