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
18 changes: 17 additions & 1 deletion .github/workflows/build-pdf.yml
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,12 @@ jobs:
permissions:
contents: write
pull-requests: write
# `secrets` cannot be read from a step's `if:` -- GitHub's context
# availability table allows it in `env`, `with` and `run`, but not in
# `jobs.<id>.steps.if`. It IS allowed in `jobs.<id>.env`, so hoist the
# "is an App configured?" test to the job and let the gate read `env`.
env:
HAS_APP: ${{ secrets.TEMPLATE_SYNC_APP_CLIENT_ID != '' }}
steps:
- name: Checkout main
uses: actions/checkout@v7
Expand Down Expand Up @@ -321,11 +327,21 @@ jobs:
fi
echo "changed=true" >> "$GITHUB_OUTPUT"

# Signed commits -- see the matching step in template-sync.yml.
- name: Mint GitHub App token
id: app-token
if: steps.stamp.outputs.changed == 'true' && env.HAS_APP == 'true'
uses: actions/create-github-app-token@v3
with:
client-id: ${{ secrets.TEMPLATE_SYNC_APP_CLIENT_ID }}
private-key: ${{ secrets.TEMPLATE_SYNC_PRIVATE_KEY }}

- name: Open site version stamp PR
if: steps.stamp.outputs.changed == 'true'
uses: peter-evans/create-pull-request@v8
with:
token: ${{ secrets.GHTOKEN || secrets.GITHUB_TOKEN }}
token: ${{ steps.app-token.outputs.token || secrets.GHTOKEN || secrets.GITHUB_TOKEN }}
sign-commits: ${{ steps.app-token.outputs.token != '' }}
base: main
# Signed off as the dispatching user, for the DCO check -- see the
# matching note in version-bot.yml's milestone PR step.
Expand Down
29 changes: 27 additions & 2 deletions .github/workflows/template-sync.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ jobs:
sync:
runs-on: ubuntu-latest
if: github.repository != 'riscv/docs-spec-template'
# `secrets` cannot be read from a step's `if:` -- GitHub's context
# availability table allows it in `env`, `with` and `run`, but not in
# `jobs.<id>.steps.if`. It IS allowed in `jobs.<id>.env`, so hoist the
# "is an App configured?" test to the job and let the gate read `env`.
env:
HAS_APP: ${{ secrets.TEMPLATE_SYNC_APP_CLIENT_ID != '' }}
steps:
- name: Checkout repository
uses: actions/checkout@v7
Expand All @@ -42,14 +48,18 @@ jobs:
GHTOKEN: ${{ secrets.GHTOKEN }}
run: |
set -euo pipefail
if [[ -z "$GHTOKEN" ]]; then
if [[ "$HAS_APP" == "true" ]]; then
echo "Using the GitHub App token (TEMPLATE_SYNC_APP_CLIENT_ID); commits will be signed."
elif [[ -z "$GHTOKEN" ]]; then
# Unlike milestone-pr in version-bot.yml, this is not just a
# "checks stall" warning: GITHUB_TOKEN is hard-blocked by GitHub
# from pushing any change under .github/workflows/, which this
# workflow always touches. Without GHTOKEN (a PAT with the
# `workflow` scope) the create-pull-request step below will fail
# outright.
echo "::warning::GHTOKEN is not set. This workflow updates files under .github/workflows/, and GITHUB_TOKEN cannot push those -- the pull request step below will fail. Set a GHTOKEN repository secret (a PAT with the 'workflow' scope). See README.adoc, 'Recommended repository secret'."
else
echo "::warning::No GitHub App configured (TEMPLATE_SYNC_APP_CLIENT_ID), so the sync commit will be pushed with GHTOKEN and will not be signed. If main requires signed commits, the PR cannot merge until a maintainer re-signs it. See README.adoc, 'Recommended repository secret'."
fi

- name: Add template remote
Expand Down Expand Up @@ -212,11 +222,26 @@ jobs:
EOF
git add .template-version

# See README.adoc, 'Recommended repository secret'. create-pull-request
# signs commits (sign-commits) only with a bot token -- with a PAT such as
# GHTOKEN it pushes them unsigned, which a "require signed commits" rule
# on main blocks. An App token lets GitHub sign them as the App's bot.
- name: Mint GitHub App token
id: app-token
if: steps.refs.outputs.skip == 'false' && env.HAS_APP == 'true'
uses: actions/create-github-app-token@v3
with:
client-id: ${{ secrets.TEMPLATE_SYNC_APP_CLIENT_ID }}
private-key: ${{ secrets.TEMPLATE_SYNC_PRIVATE_KEY }}

- name: Create pull request
if: steps.refs.outputs.skip == 'false'
uses: peter-evans/create-pull-request@v8
with:
token: ${{ secrets.GHTOKEN || secrets.GITHUB_TOKEN }}
token: ${{ steps.app-token.outputs.token || secrets.GHTOKEN || secrets.GITHUB_TOKEN }}
# With an App token the commit is authored by the App's bot, which the
# DCO check skips; the trailer below then only records who ran it.
sign-commits: ${{ steps.app-token.outputs.token != '' }}
# Same reason as milestone-pr in version-bot.yml: the DCO check that
# RISC-V spec repos run requires a Signed-off-by matching the
# commit's author. create-pull-request authors the commit as the user
Expand Down
18 changes: 17 additions & 1 deletion .github/workflows/version-bot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,12 @@ jobs:
# meaningless for a doc-mode repo, which has no milestones. See
# scripts/release-info.sh's .docmode / "mode" output above.
if: needs.tag-version.outputs.milestone_transition == 'true' && needs.tag-version.outputs.mode == 'spec'
# `secrets` cannot be read from a step's `if:` -- GitHub's context
# availability table allows it in `env`, `with` and `run`, but not in
# `jobs.<id>.steps.if`. It IS allowed in `jobs.<id>.env`, so hoist the
# "is an App configured?" test to the job and let the gate read `env`.
env:
HAS_APP: ${{ secrets.TEMPLATE_SYNC_APP_CLIENT_ID != '' }}

steps:
- name: Checkout repository
Expand All @@ -292,10 +298,20 @@ jobs:
set -euo pipefail
./scripts/update-spec-state.sh "${{ needs.tag-version.outputs.next_version }}" "$(date +%Y-%m-%d)"

# Signed commits -- see the matching step in template-sync.yml.
- name: Mint GitHub App token
id: app-token
if: env.HAS_APP == 'true'
uses: actions/create-github-app-token@v3
with:
client-id: ${{ secrets.TEMPLATE_SYNC_APP_CLIENT_ID }}
private-key: ${{ secrets.TEMPLATE_SYNC_PRIVATE_KEY }}

- name: Create milestone review PR
uses: peter-evans/create-pull-request@v8
with:
token: ${{ secrets.GHTOKEN || secrets.GITHUB_TOKEN }}
token: ${{ steps.app-token.outputs.token || secrets.GHTOKEN || secrets.GITHUB_TOKEN }}
sign-commits: ${{ steps.app-token.outputs.token != '' }}
# The DCO check that RISC-V spec repos run requires a Signed-off-by
# matching the commit's author. create-pull-request authors the commit
# as the user who dispatched this workflow and adds no trailer, so the
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
**_NOTE:_** PROJECTS BUILT USING THE TEMPLATE SHOULD UPDATE THE BELOW SECTIONS AS-NEEDED.

## [Unreleased]
- Sign bot commits so their PRs can merge where `main` requires signed commits.
`template-sync.yml`, `version-bot.yml` (milestone PR) and `build-pdf.yml`
(site version stamp PR) pushed with `GHTOKEN`, a personal access token, which
`create-pull-request` cannot sign, so under the `riscv` organization's ruleset
every bot PR was blocked even with DCO passing. When a GitHub App is configured
(`TEMPLATE_SYNC_APP_CLIENT_ID` and `TEMPLATE_SYNC_PRIVATE_KEY` secrets), each workflow
now mints its token and passes `sign-commits: true`; otherwise it falls back
to `GHTOKEN` as before. See README.adoc, "Signed commits: use a GitHub App".
- Keep the version stamp on the release tag when `main` refuses the bot's
push. The `riscv` organization ruleset requires signed commits and a passing
DCO check on the default branch, so `version-bot.yml`'s pre-tag push of the
Expand Down
7 changes: 7 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,13 @@ earlier.
secrets count). The workflow writes files under `.github/workflows/`, which
`GITHUB_TOKEN` cannot push regardless of the *Allow GitHub Actions to
create and approve pull requests* setting.
- [ ] If `main` requires signed commits (the `riscv` organization's ruleset
does), confirm the `TEMPLATE_SYNC_APP_CLIENT_ID` and
`TEMPLATE_SYNC_PRIVATE_KEY` secrets are visible and the App is installed
on the repository. Without
them the sync commit is pushed with `GHTOKEN` unsigned, and the PR is
blocked until someone re-signs it. See README.adoc, *Signed commits: use a
GitHub App*.
- [ ] Run it once from *Actions → Template Sync → Run workflow*, rather than
waiting for the Monday schedule.
- [ ] While you are in *Actions*, run **Repository Setup** too. It applies the
Expand Down
29 changes: 26 additions & 3 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -249,10 +249,33 @@ PDF build, `pre-commit`, Vale, and the Antora content-source validation -- is
created in the `action_required` state and waits for a maintainer to press
"Approve and run". The PR itself is still correct; only its checks stall.

`GHTOKEN` may be either:
`GHTOKEN` is a fine-grained personal access token scoped to the repository with `Contents: read and write`, `Pull requests: read and write`, and `Workflows: read and write`. It is tied to one person's account and expires.

* A fine-grained personal access token scoped to the repository with `Contents: read and write`, `Pull requests: read and write`, and `Workflows: read and write`. Simplest to set up; tied to one person's account and expires.
* A GitHub App installation token minted per run with `actions/create-github-app-token`. Preferred for repositories with more than one maintainer, since it is not tied to an individual.
==== Signed commits: use a GitHub App

Commits pushed with `GHTOKEN` are *not signed*. If `main` requires signed
commits (the `riscv` organization's ruleset does), every bot PR -- template
sync, milestone, site version stamp -- is blocked with "Commits must have
verified signatures" even though DCO passes, until a maintainer re-signs the
commit by hand. `create-pull-request` signs commits only with a bot token, never
with a personal access token.

To have GitHub sign them, configure a GitHub App. The three workflows that open
PRs mint a token for it per run with `actions/create-github-app-token` and pass
`sign-commits: true`; when it is not configured they fall back to `GHTOKEN`.

* *Secret* `TEMPLATE_SYNC_APP_CLIENT_ID`: the App's client ID.
* *Secret* `TEMPLATE_SYNC_PRIVATE_KEY`: a private key generated for the App.
* App permissions: `Contents`, `Pull requests` and `Workflows`, all read and write. Install it on each repository.

Both can be set at the organization level. Commits are then authored by the
App's bot, which the DCO check skips. An App is also not tied to one person's
account, and its tokens don't need rotating by hand.

The client ID is not sensitive, but it is stored as a secret rather than a
variable so that both values live in one place. Because `secrets` cannot be
read from a step's `if:`, each job hoists the "is an App configured?" test into
a job-level `env` (`HAS_APP`) that the gate reads instead.

=== Manual builds

Expand Down
Loading