Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 17 additions & 7 deletions .github/workflows/export-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# This repo is the origin of truth for shared docs content — the generated
# files downstream (docs/generating-coverage-reports.md, docs/INSTALLATION.md)
# must never be edited directly. See README.md § Docs export pipeline for the
# COVERAGE_TRACKER_SYNC_TOKEN setup.
# docs-sync GitHub App setup (DOCS_SYNC_APP_ID / DOCS_SYNC_APP_PRIVATE_KEY).
name: Export docs

on:
Expand All @@ -17,32 +17,42 @@ on:
workflow_dispatch:

permissions:
id-token: write
contents: write
pull-requests: write

jobs:
export:
runs-on: ubuntu-latest
steps:
# Mint an installation token for the docs-sync GitHub App. A bot token is
# required for commit signing: create-pull-request's sign-commits builds
# commits via the GitHub API, which only signs them (as the app bot) when
# authenticated with a GitHub App token — a PAT leaves them unsigned.
# owner/repositories are mandatory to scope the token to the cross-org target repo.
- name: Generate GitHub App token
id: app-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ secrets.DOCS_SYNC_APP_ID }}
private-key: ${{ secrets.DOCS_SYNC_APP_PRIVATE_KEY }}
owner: CoverageTracker
repositories: coverage-tracker

- name: Checkout coveragetracker.dev
uses: actions/checkout@v4

- name: Checkout coverage-tracker
uses: actions/checkout@v4
with:
repository: CoverageTracker/coverage-tracker
token: ${{ secrets.COVERAGE_TRACKER_SYNC_TOKEN }}
token: ${{ steps.app-token.outputs.token }}
path: target-repo

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 22

- name: Setup Gitsign
uses: chainguard-dev/actions/setup-gitsign@f0be69916b439d0fcced2451b23d0f27cd46d545 # v1.6.26

- name: Export docs
run: node --experimental-strip-types scripts/export-docs.ts target-repo

Expand All @@ -54,7 +64,7 @@ jobs:
path: target-repo
branch: docs-sync
sign-commits: true
token: ${{ secrets.COVERAGE_TRACKER_SYNC_TOKEN }}
token: ${{ steps.app-token.outputs.token }}
commit-message: 'docs: sync generated docs from coveragetracker.dev'
title: 'docs: sync generated docs from coveragetracker.dev'
body: |
Expand Down
44 changes: 27 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,23 +98,33 @@ Run it locally against a sibling checkout:
pnpm export-docs ../coverage-tracker
```

### Provisioning the sync token

The workflow authenticates with a fine-grained PAT stored as the repo secret
`COVERAGE_TRACKER_SYNC_TOKEN`. The product GitHub App's permissions are deliberately
**not** widened for this docs plumbing. To (re)provision the token — after expiry, a new
fork, or a maintainer change:

1. GitHub → **Settings → Developer settings → Personal access tokens → Fine-grained
tokens → Generate new token**
2. Resource owner: the **CoverageTracker** org; Repository access: **Only select
repositories** → `coverage-tracker`
3. Repository permissions: **Contents → Read and write**, **Pull requests → Read and
write**; everything else: No access
4. Set an expiration and note it somewhere you'll see it — when the token lapses, the
workflow fails with an auth error on the checkout or PR step
5. Save the token in **this repo** as the Actions secret `COVERAGE_TRACKER_SYNC_TOKEN`
(Settings → Secrets and variables → Actions)
### Provisioning the sync credentials

The target repo requires **signed commits** on PRs, so the workflow authenticates with a
dedicated **GitHub App** installation token rather than a PAT. This is deliberate:
`create-pull-request`'s `sign-commits` builds commits through the GitHub API, and the API
only signs them (as the app bot, shown *Verified*) when the token is a bot token — a PAT
leaves the commits **unsigned** and branch protection rejects the PR. The product GitHub
App's permissions are deliberately **not** widened for this docs plumbing, so this uses a
separate minimal App.

To (re)provision — a new fork, a rotated key, or a maintainer change:

1. **CoverageTracker** org → **Settings → Developer settings → GitHub Apps → New GitHub
App**. Name it e.g. `coveragetracker-docs-sync`, give any valid homepage URL, and
uncheck **Webhook → Active**.
2. Repository permissions: **Contents → Read and write**, **Pull requests → Read and
write**; everything else: No access. Create the App.
3. App settings → **Private keys → Generate a private key** and download the `.pem`.
4. **Install** the App on the org, scoped to **Only select repositories → `coverage-tracker`**
(installation is required — creating the App alone does nothing).
5. In **this repo** → Settings → Secrets and variables → Actions, add two secrets:
- `DOCS_SYNC_APP_ID` — the App's numeric **App ID**
- `DOCS_SYNC_APP_PRIVATE_KEY` — the full contents of the downloaded `.pem`

The App has no expiry to track; installation tokens are minted per run by
`actions/create-github-app-token`. If commits ever land **Unverified**, check that the App
is still installed on `coverage-tracker`.

## Theming

Expand Down
Loading