New to the words here? The workflow token, GitHub App,
preset and restricted run are explained in the glossary.
Why split the tokens?
The simplest set-up gives smartcloud one strong token and lets it do everything with it. That works, but it means every run, including the run for a pull request whose workflow file the pull request itself can change, holds a key that can change repository settings, push workflow files and, if the token reaches it, write to your organisation’s shared.github repository.
Splitting the tokens means each run holds only the keys it needs:
- A pull request run never holds the strong token, so a changed workflow on a pull request cannot use it.
- The strong token reaches only the repository it runs in, so a leaf repository can never write to your shared
.githubrepository. - Reading your house preset needs only a read-only key to
.github, so that key is all a pull request run gets.
The three tokens
When a run has no app token (every pull request run, in the house workflow), smartcloud runs restricted: it skips settings, and it skips sync’s scheduled proposal, and says so in the job summary. With a house token, the sync edit check on a pull request still runs, because it only reads.
Set it up
1
Create a GitHub App
Follow Create a GitHub App. Install it on each repository that runs smartcloud and on your organisation’s
.github repository. Store its ID as the variable SMARTCLOUD_APP_ID and its private key as the secret SMARTCLOUD_APP_PRIVATE_KEY, at organisation level so every repository can use them.2
Mint the read-only house token
Add a step that asks the app for a token limited to reading
.github:permission-contents: read is what makes it read-only: the token gets that one permission, whatever the app itself may do.3
Mint the app token only where it is needed
Add a second step, limited to the repository itself, to runs from the default branch and to merged pull requests (for backports):With neither
owner nor repositories, the token reaches only the repository the workflow runs in. If your config extends a preset in another private repository that is not .github, add owner and list that repository in repositories too.4
Pass all three to smartcloud
workflowToken is left out on purpose: it defaults to github.token. The || github.token fallback means a run with no app token acts with the workflow token and runs restricted instead of failing.Why backport needs the app token
A backport pushes a branch and opens a new pull request. GitHub never starts workflows for a pull request opened with the workflow token, so a backport opened that way would sit with no CI, and its required checks would never pass. So backport acts with the app token too. The app token is minted for a pull request run only when the pull request has been merged and came from the repository itself. By then its code is on the default branch, so the run is trusted: nobody can change that run’s workflow file any more. Every other pull request run, and every comment run, still acts with the workflow token. If the app token is missing on that run (the mint failed, or the pull request came from a fork), the job’s workflow token hascontents: read and cannot push. smartcloud then opens nothing and reports a warning, “#7 was not backported to v1: the token cannot push …”, in the job summary instead of claiming a backport it did not make.
A complete example
.github/workflows/smartcloud.yml
What you will see
- On a pull request: the checks, comment and labels come from
github-actions[bot], the workflow token’s identity. The job summary has a notice, “ran with restricted access (the workflow token, without an app or access token)”, and lists settings as skipped. That is expected: settings never runs on pull requests anyway. Your house preset is read with the house token, so its rules are checked. - On a push, schedule or manual run: settings and sync act as your app (for example
my-app[bot]); the check run and labels still come fromgithub-actions[bot]. - When a pull request with a
backport <branch>label is merged: the backport pull request is opened by your app, so your CI runs on it. - On a pull request from a fork or Dependabot: no app token of either kind is minted, and the house token input is ignored even if one is passed. smartcloud acts with the workflow token alone, as before.
Options
Older workflows that pass one strong token as
GITHUB_TOKEN and no houseToken keep working: the strong token then reads presets and runs settings and sync, and the workflow token still does everything in the repository.
Common problems
A pull request run warns that the house preset was left out (access.config-skipped)
A pull request run warns that the house preset was left out (access.config-skipped)
The run had no house token that could read
.github. Check that the app is installed on .github, that the house
token step ran (it is skipped for forks and Dependabot, which is expected), and that houseToken is passed to
smartcloud.Settings or sync is skipped on a schedule or push run
Settings or sync is skipped on a schedule or push run
The app token was not minted. Open the job log: the “Mint the app token” step either was skipped (check its
if) or
failed (check the variable and secret, and that the app is installed on the repository). A failed mint falls back to
the workflow token rather than failing the run.Labels, comments or check runs fail with forbidden
Labels, comments or check runs fail with forbidden
These act with the workflow token now, so the job’s
permissions must grant them: checks: write, issues: write,
pull-requests: write and statuses: read.A workflow does not start on labels smartcloud adds
A workflow does not start on labels smartcloud adds
GitHub does not start workflows for events made with the workflow token. Labels and comments now come from the
workflow token, so a workflow that listened for smartcloud’s labels needs another trigger, such as
pull_request
with synchronize.