Skip to content

ci: push auto-fixes through securefix instead of a GitHub App - #2860

Draft
toiroakr wants to merge 4 commits into
mainfrom
ci/securefix-without-github-app
Draft

toiroakr wants to merge 4 commits into
mainfrom
ci/securefix-without-github-app

Conversation

@toiroakr

@toiroakr toiroakr commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Stop using the TAILOR_GITHUB_APP_* credentials in the workflows. Pushes to a pull request branch go through securefix, and the two workflows that open pull requests use GITHUB_TOKEN.
  • The securefix client holds a key that can only create issue labels. A server in another repository pushes the commit with its own GitHub App token, so CI starts on the new head by itself.
  • The server only accepts a request from a client workflow named securefix, so every fix that is pushed to a pull request branch now lives in securefix.yml. The workflows that check the same files keep only the checks.

What changes per workflow

Workflow Before After
securefix.yml (new) - Regenerates files for same-repository pull requests and pushes them through the securefix client
ci.yml auto-fix-lint-format job pushed lint/format fixes Job removed; the ci job is unchanged
generate.yml commit-action + App key Fails when generated files differ; the path filter is shared with securefix.yml
skills-sync-check.yml commit-action + App key Fails when skills/ differs; the copy lives in .github/scripts/sync-skills.sh
add-renovate-changeset.yml commit-action + App key Removed; the logic moved to securefix.yml
update_aqua_checksum.yml reusable aqua workflow + App key Removed; aqua update-checksum -deep -prune runs in securefix.yml
lockfile-audit-fix.yml App token for create-signed-pr GITHUB_TOKEN (contents: write, pull-requests: write on the job)
release.yml App token for changesets/action and the release-PR fixup script GITHUB_TOKEN (pull-requests: write added to the job)

renovate.json: the securefix server bot is added to gitIgnoredAuthors, next to the existing bot, so Renovate does not treat its commits as human edits. actions.lock is regenerated with pnpm run actions:lock:update.

How securefix.yml works

  • regenerate runs the pull request's dependencies and tools with no secret and hands over a patch as an artifact. push holds the client key, installs and runs nothing from the branch, and applies the patch only if every changed path is allowed.
  • Lint and format fixes can rewrite any source file, so the check is a denylist: workflows, hooks, agent settings, changesets (except renovate-<number>.md), package manager and tool settings, and package.json files are rejected. A fix that needs one of them has to be committed by hand.
  • Pull requests from forks get no secrets, so they are skipped. generate.yml and skills-sync-check.yml now fail on them when files differ, instead of passing silently.

Behavior to know

  • When the client pushes to the branch it runs on, it fails the step after sending the request. The failed securefix run is the superseded one; the run on the new head replaces it.
  • Lint and format fixes now run on every Renovate pull request, not only after ci reports a lint or format failure, because a separate workflow cannot read that result.
  • Pull requests opened or updated with GITHUB_TOKEN start their checks after someone with write access selects "Approve workflows to run". The required checks (ci, deploy-result, generate, test-summary, generate-summary) need that click on the release PR and on the lockfile PR.

To confirm by running

  • release.yml pushes a follow-up commit to the release PR branch through the Contents API. GitHub's documentation does not say whether this push, made with GITHUB_TOKEN, also asks for approval.
  • The repository setting "Allow GitHub Actions to create and approve pull requests" must be on for changesets/action and create-signed-pr to open pull requests with GITHUB_TOKEN.
  • The securefix server App must be installed on this repository, and the client App on the server repository and this repository.
  • The first Renovate pull request that needs a fix shows whether the server accepts the securefix workflow and pushes the commit.

Not in this PR

Removing the TAILOR_GITHUB_APP_ID and TAILOR_GITHUB_APP_PRIVATE_KEY repository secrets, once this has merged and the workflows have run.

Five workflows pushed their fixes to the pull request branch with the
TAILOR_GITHUB_APP credentials, whose private key sits in repository
secrets that workflows on any branch of the repository can read.
Request the commit from a securefix server instead: the client only
holds a key that can create issue labels, and the server pushes the
commit with its own GitHub App token, so the checks on the new head
still start by themselves.

When the client pushes to the branch it runs on, it fails the step after
sending the request. That marks only the superseded run, and it is what
commit-action did by default; fail_on_self_push: false has no securefix
counterpart, so ci.yml and add-renovate-changeset.yml fail the same way.

Pull requests from forks get no secrets, so the steps and jobs skip them.
skills-sync-check.yml also skips pushes to main, where securefix does not
push. update_aqua_checksum.yml runs aqua update-checksum directly instead
of the reusable workflow that was given the App credentials.

Renovate must not treat the server commits as human edits, so add the
securefix server bot to gitIgnoredAuthors.
Both workflows minted a GitHub App token only to open a pull request,
which needs just contents: write and pull-requests: write on the job.
Use the job's GITHUB_TOKEN, so the App credentials are no longer read
here.

Checks on a pull request opened or updated with GITHUB_TOKEN start only
after someone with write access selects "Approve workflows to run", so
the required checks need that click on the release PR and on the
lockfile PR. release.yml also pushes a follow-up commit to the release
PR branch through the Contents API; GitHub's documentation does not say
whether that push asks for approval as well.
@changeset-bot

changeset-bot Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 13f8e97

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

The securefix server only accepts a request from a client workflow named
securefix and rejects any other name, so the five workflows that pushed
fixes under their own names would have failed on every request.

Move the fixes (generate, expected fixtures, lint and format on Renovate
pull requests, skills sync, aqua checksums, Renovate changesets) into
securefix.yml. Its regenerate job runs the branch's tooling without a
secret and hands over a patch; its push job installs nothing from the
branch, checks the patch against a denylist and sends it through the
client. The generate and skills checks now fail on a difference instead
of pushing, so pull requests from forks and pushes to main no longer
pass with stale files.

aqua update-checksum runs with -deep again, as the reusable workflow did.
The patch comes from a job that runs the pull request branch's own
dependencies, and the push job commits it with the client's token. The
denylist missed files that change how installs, editors, agents or the
formatter behave, so a compromised dependency could have slipped one of
them into the commit.

Also reject .pnpmfile.*, .mcp.json, .vscode/, .devcontainer/, version
manager files, CLAUDE.md, AGENTS.md, the oxfmt and oxlint configs and
tsconfig files. A fix that needs one of them has to be committed by hand.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant