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
23 changes: 13 additions & 10 deletions .github/workflows/ca-template-changes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,21 @@ on:
release:
types: [published]

# Opens a pull request against the default branch. The repository default is
# read, so the writes are granted here and nowhere else.
permissions:
contents: write
pull-requests: write
# Every write is the app token's, which is also what makes the checks run on
# the pull request this opens.
permissions: {}

jobs:
update:
runs-on: ubuntu-latest
steps:
- name: Mint a token for the release app
id: app
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
app-id: ${{ secrets.RELEASE_APP_ID }}
private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}

# Branch from the default branch, not the tag the release points at: a
# tag checkout is detached and the push would go nowhere.
- name: Checkout default branch
Expand Down Expand Up @@ -41,20 +46,18 @@ jobs:
# push and is never left in the runner's git config.
#
# A pull request, never a direct push: the default branch requires one.
# Checks do not start on a pull request opened with GITHUB_TOKEN. Merging
# this one needs the ruleset bypass, or a re-run of the checks.
- name: Open a pull request
if: steps.changes.outputs.changed == 'true'
env:
VERSION: ${{ github.event.release.tag_name }}
BASE: ${{ github.event.repository.default_branch }}
REPO: ${{ github.repository }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_TOKEN: ${{ steps.app.outputs.token }}
run: |
set -euo pipefail
BRANCH="docs/ca-template-${VERSION}"
git config user.name 'github-actions[bot]'
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
git config user.name 'stackyard-release[bot]'
git config user.email '${{ steps.app.outputs.app-slug }}[bot]@users.noreply.github.com'
git checkout -b "${BRANCH}"
git add templates/stackyard.xml
git commit -m "Docs: describe ${VERSION} in the Community Applications template"
Expand Down
4 changes: 2 additions & 2 deletions api/test/release-automation.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ test('release prep opens a pull request rather than writing to the default branc
});

test('the tag and the release page are pushed with the app token', () => {
for (const f of ['release-tag.yml', 'release.yml']) {
for (const f of ['release-tag.yml', 'release.yml', 'ca-template-changes.yml']) {
const src = wf(f);
assert.ok(src.includes(APP_TOKEN), `${f} does not use the app token`);
assert.doesNotMatch(
Expand Down Expand Up @@ -53,7 +53,7 @@ test('the release page is published from the changelog, after the image', () =>
});

test('the release workflows grant the built-in token nothing', () => {
for (const f of ['release-prep.yml', 'release-tag.yml']) {
for (const f of ['release-prep.yml', 'release-tag.yml', 'ca-template-changes.yml']) {
const doc = yaml.load(wf(f));
assert.deepEqual(doc.permissions, {}, `${f} should leave GITHUB_TOKEN with no scope`);
}
Expand Down
4 changes: 0 additions & 4 deletions api/test/workflow-hardening.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,9 @@ test('no workflow grants a write scope it does not need', () => {
lets the job mint a short-lived OIDC token, which is what cosign exchanges
for a Sigstore certificate when it signs the released image. Keyless
signing needs it, and it is the reason there is no private key to hold. */
/* The CA template's release notes go to a branch and a pull request, which is
all those two scopes cover. The checkout still keeps no credentials: the
push authenticates on its own. */
const allowed = {
'workflows/release.yml': ['packages: write', 'id-token: write'],
'workflows/codeql.yml': ['security-events: write'],
'workflows/ca-template-changes.yml': ['contents: write', 'pull-requests: write'],
};
const bad = [];
for (const [f, src] of workflows) {
Expand Down
31 changes: 24 additions & 7 deletions docs/releasing.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,30 @@ keeps `latest`, the Docker Hub description and the demo on the last stable
release, and marks the release page as a pre-release. See
`scripts/is-prerelease.js`.

## Requirements

The prep, tagging and release-page steps authenticate as a GitHub App, through
the `RELEASE_APP_ID` and `RELEASE_APP_PRIVATE_KEY` secrets. The app needs
Contents and Pull requests write access. Without it the release cannot proceed:
the built-in token cannot push to the protected default branch, and a tag or a
release it creates starts no workflow run, so nothing would build.
## The release app

Every write in the release runs as a GitHub App. The built-in `GITHUB_TOKEN`
cannot push to the protected default branch, and a tag or a release it creates
starts no workflow run, so the image would never build and the Community
Applications template would never update.

Create it once, under **Settings > Developer settings > GitHub Apps > New GitHub
App** on the account that owns the repository:

1. Name it `stackyard-release`. Homepage URL can be the repository URL.
2. Clear **Webhook > Active**. The app receives nothing.
3. Repository permissions: **Contents: Read and write**, **Pull requests: Read
and write**. Nothing else. Metadata read-only is added automatically.
4. Create the app, then **Generate a private key** and keep the downloaded
`.pem`.
5. **Install App** on this repository only.
6. In the repository, add two secrets under **Settings > Secrets and variables >
Actions**: `RELEASE_APP_ID` is the app's App ID, and
`RELEASE_APP_PRIVATE_KEY` is the whole `.pem` file including the
`-----BEGIN` and `-----END` lines.

The app needs no ruleset bypass. It opens pull requests rather than pushing to
the default branch, and the ruleset covers branches, not tags.

## When a release build fails

Expand Down
Loading