From 01f6f06ee12a0947070a1f9e789407713a26aacd Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Thu, 1 Oct 2026 01:07:08 -0400 Subject: [PATCH] Add API docs preview workflow for pull requests A pull request with the `api-docs-preview` label gets a build of the ember-api-docs app with the docs of that pull request. The build is an artifact, and a comment on the pull request gives the link and the commands to view it. Two workflows do the work. The build runs the code of the pull request, so it runs on `pull_request` with a read-only token. The comment needs write access, so it runs on `workflow_run` and runs no code from the pull request. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../workflows/api-docs-preview-comment.yml | 77 +++++++++++++ .github/workflows/api-docs-preview.yml | 108 ++++++++++++++++++ CONTRIBUTING.md | 4 + 3 files changed, 189 insertions(+) create mode 100644 .github/workflows/api-docs-preview-comment.yml create mode 100644 .github/workflows/api-docs-preview.yml diff --git a/.github/workflows/api-docs-preview-comment.yml b/.github/workflows/api-docs-preview-comment.yml new file mode 100644 index 00000000000..e6d58597594 --- /dev/null +++ b/.github/workflows/api-docs-preview-comment.yml @@ -0,0 +1,77 @@ +name: API Docs Preview Comment + +on: + workflow_run: # This workflow has permissions on the repo, do NOT run code from PRs in this workflow. See https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ # zizmor: ignore[dangerous-triggers] we know what we are doing here + workflows: ["API Docs Preview"] + types: [completed] + +permissions: {} + +jobs: + comment: + name: Comment on PR with the preview link + if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' + runs-on: ubuntu-latest + permissions: + actions: read + pull-requests: write + steps: + - name: Find the PR and the artifact + id: find + run: | + # The event has no PR number for a PR from a fork. + # GitHub supplies the head values, so the code of a PR cannot change them. + number=$( + gh api -X GET "repos/${REPO}/pulls" -f state=open -f head="${HEAD_OWNER}:${HEAD_BRANCH}" | + jq --arg sha "${HEAD_SHA}" 'first(.[] | select(.head.sha == $sha) | .number)' + ) + artifact=$( + gh api "repos/${REPO}/actions/runs/${RUN_ID}/artifacts" \ + --jq '[.artifacts[] | select(.name == "api-docs-preview")] | sort_by(.created_at) | last | .id // empty' + ) + + if [ -z "${number}" ] || [ -z "${artifact}" ]; then + echo "::notice::No open PR at ${HEAD_SHA} or no artifact. No comment." + exit 0 + fi + + echo "number=${number}" >> "$GITHUB_OUTPUT" + # gh does not extract over the files of an older preview, so each commit gets a folder. + echo "dir=api-docs-preview-${HEAD_SHA:0:7}" >> "$GITHUB_OUTPUT" + echo "artifact-url=${GITHUB_SERVER_URL}/${REPO}/actions/runs/${RUN_ID}/artifacts/${artifact}" >> "$GITHUB_OUTPUT" + env: + GH_TOKEN: ${{ github.token }} + REPO: ${{ github.repository }} + RUN_ID: ${{ github.event.workflow_run.id }} + HEAD_SHA: ${{ github.event.workflow_run.head_sha }} + HEAD_OWNER: ${{ github.event.workflow_run.head_repository.owner.login }} + HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }} + + - name: Create or update the comment + if: steps.find.outputs.number != '' + uses: marocchino/sticky-pull-request-comment@5770ad5eb8f42dd2c4f34da00c94c5381e49af88 # v3.0.5 + with: + header: api-docs-preview + number_force: ${{ steps.find.outputs.number }} + message: | + ### API docs preview + + The preview shows the API docs of ${{ github.event.workflow_run.head_sha }}, as https://api.emberjs.com will show them. + + 1. Download and extract the artifact. + + ```sh + gh run download ${{ github.event.workflow_run.id }} --repo ${{ github.repository }} --name api-docs-preview --dir ${{ steps.find.outputs.dir }} + ``` + + Without `gh`, download [api-docs-preview.zip](${{ steps.find.outputs.artifact-url }}) and extract it into `${{ steps.find.outputs.dir }}`. The download needs a GitHub login. + + 2. Start a server. + + ```sh + npx http-server ${{ steps.find.outputs.dir }} -c-1 + ``` + + 3. Open http://localhost:8080. + + The preview has only the version of this PR. The search box shows results from the released docs. diff --git a/.github/workflows/api-docs-preview.yml b/.github/workflows/api-docs-preview.yml new file mode 100644 index 00000000000..d9154b3a84e --- /dev/null +++ b/.github/workflows/api-docs-preview.yml @@ -0,0 +1,108 @@ +name: API Docs Preview + +on: + pull_request: + types: [labeled, synchronize] + +permissions: + contents: read + +jobs: + build: + name: Build API docs preview + if: >- + contains(github.event.pull_request.labels.*.name, 'api-docs-preview') && + (github.event.action != 'labeled' || github.event.label.name == 'api-docs-preview') + runs-on: ubuntu-latest + timeout-minutes: 15 + concurrency: + group: api-docs-preview-${{ github.event.pull_request.number }} + cancel-in-progress: true + steps: + - name: Checkout ember.js + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + path: ember.js + persist-credentials: false + + - name: Checkout ember-jsonapi-docs + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + repository: kategengler/ember-jsonapi-docs + path: ember-jsonapi-docs + persist-credentials: false + + - name: Checkout ember-api-docs-data + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + repository: ember-learn/ember-api-docs-data + path: ember-api-docs-data + persist-credentials: false + # Only the scripts at the root. The preview shows the docs of the PR and no released version. + sparse-checkout: | + /* + !/*/ + sparse-checkout-cone-mode: false + + - name: Checkout ember-api-docs + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + repository: ember-learn/ember-api-docs + path: ember-api-docs + persist-credentials: false + + - uses: pnpm/action-setup@91ab88e2619ed1f46221f0ba42d1492c02baf788 # v6.0.6 + name: Install pnpm + with: + cache: false + version: 10.33.2 + run_install: false + + - name: Set up Node.js + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + with: + node-version: "22" + package-manager-cache: false + + - name: Install dependencies for ember-jsonapi-docs + run: pnpm install + working-directory: ember-jsonapi-docs + + - name: Install dependencies for ember-api-docs-data + run: npm install + working-directory: ember-api-docs-data + + - name: Install dependencies for ember-api-docs + run: pnpm install + working-directory: ember-api-docs + + - name: Get Ember version from package.json + id: pkg + run: | + echo "version=$(jq -r .version package.json)" >> "$GITHUB_OUTPUT" + working-directory: ember.js + + - name: Generate API docs + run: pnpm run gen --project ember --version "${STEPS_PKG_OUTPUTS_VERSION}" + working-directory: ember-jsonapi-docs + env: + STEPS_PKG_OUTPUTS_VERSION: ${{ steps.pkg.outputs.version }} + + - name: Fix docs + run: npm run fix:files + working-directory: ember-api-docs-data + + - name: Build ember-api-docs + run: | + ln -s ../ember-api-docs-data ember-api-docs-data + pnpm exec ember build --environment=production + # A static server sends 404.html for a path that is not a file. The app then shows the route. + cp dist/index.html dist/404.html + working-directory: ember-api-docs + + - name: Upload preview + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: api-docs-preview + path: ember-api-docs/dist + if-no-files-found: error diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0e52f727747..7a1cca29b51 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -211,6 +211,10 @@ Code words are: - `true`, `false`, `null`, `undefined` (when referring to programming values) - references to other properties/methods +To see your documentation as https://api.emberjs.com will show it, ask a +maintainer to add the `api-docs-preview` label to your pull request. A comment +on the pull request then tells you how to download and view the preview. + And in case we didn't emphasize it enough: we love tests! NOTE: Partially copied from https://raw.github.com/thoughtbot/factory_girl_rails/master/CONTRIBUTING.md