Skip to content

releng: replace the user-owned release tag deploy key with a GitHub App so release runs are not attributed to a person #698

Description

Symptom

Every unbounded release (release.yaml) run reports the same actor regardless
of who cut the release. Two runs from 2026-09-02:

Release build actor triggering_actor Who actually cut it
33655638847 (v0.5.0) plombardi89 plombardi89 on attempt 1 cchildress, via prepare run 33655577293 dispatched 36s earlier
33667392561 (v0.6.0) plombardi89 plombardi89 cchildress, via prepare run 33667308864 dispatched 49s earlier

The v0.6.0 case is the clean one: cchildress dispatched, and both actor and
triggering_actor came back plombardi89 on attempt 1. cchildress appears
nowhere on the run.

This holds for every release since 2026-06-23. Correlating all 80 release.yaml
runs against all 52 release-prepare runs: every release build with a matching
prepare run reports plombardi89, including ones cut by bcho, vpatelsj,
jwilder, jveski and cchildress. The only builds with a truthful actor are
the handful with no prepare run at all (v0.1.24-rc.11 through rc.18,
v0.2.0-beta.4, v0.2.3-alpha.0 through alpha.2), which were tags pushed by
hand.

Cause

release-prepare pushes the tag over SSH with RELEASE_TAG_DEPLOY_KEY rather
than GITHUB_TOKEN, because GitHub suppresses workflow triggers for tags pushed
with the default token:

https://github.com/Azure/unbounded/blob/main/.github/workflows/release-prepare.yaml#L110-L116

GitHub attributes a deploy-key push to the account that registered the key.
That key was added by an individual maintainer when 6c8d83d (#328) introduced
this mechanism, so the actor on every workflow-cut release is that person,
permanently.

triggering_actor cannot substitute: on a push it equals actor, and on a
re-run it names whoever pressed re-run. Neither field can name the cutter.

Why this is worth fixing rather than just documenting

  • Audit trail. The release history names one person for releases they had
    nothing to do with. For a release pipeline that signs artifacts, that is the
    wrong property.
  • Bus factor. The push credential is owned by an individual account. If that
    account is offboarded or the key is removed, release-prepare starts failing
    at the push, and the failure mode is the ugly one: a tag can land with no
    build behind it (RELEASING.md, "A tag was pushed but no build started").
  • Ownership. It is a repository write credential held by a person rather
    than by the org.

Fix

Replace the user-owned deploy key with an org-owned identity. Either:

  1. GitHub App installation token, minted per run with
    actions/create-github-app-token, scoped to this repo with contents: write.
    Pushes made with an App token do trigger workflows, so the reason the deploy
    key exists at all is preserved. Runs would then be attributed to the App's bot
    identity.
  2. Deploy key re-issued from a machine account, which is cheaper but keeps a
    long-lived key and moves the ownership problem rather than removing it.

Option 1 is preferred.

Notes

  • Needs a repo admin. GET /repos/Azure/unbounded/keys returns 404 for a
    non-admin token, so confirming the current key's owner and doing the swap
    cannot be done from outside admin.
  • relctl now works around the symptom: relctl watch <tag> reports a Cut by:
    line and relctl status a BY column, both derived by correlating the build
    against the release-prepare run whose execution window contains the tag push.
    That is a mitigation, not a fix, and it can only report unknown for builds
    older than its correlation window. It should be simplified once the underlying
    attribution is correct.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestgithub_actionsPull requests that update GitHub Actions codereleng

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions