Composite GitHub Action that aggregates the six upstream maintenance syncs behind a single
action. Every consumer runs one job instead of one job per kind. Each kind is opt-out: it runs
by default and is disabled by setting its input to false. The kinds are:
agents-rules→agents-rules-sync— sync the stack-appropriaterules/<stack>.mdintoAGENTS.md, withCLAUDE.mdas a symlink to it.code-review→code-review-sync— sync the AI code-review workflow.repomix→repomix-sync— sync therepomix-packworkflow andrepomix.config.json.release→release-sync— sync the release pipeline workflows (create, publish, auto-merge).licenses→licenses-sync— sync the licenses workflow.contributing→contributing-sync— syncCONTRIBUTING.md,CODE_OF_CONDUCT.md,LICENSE.md,SECURITY.md, and the contributing, auto-label, and validate-actions workflows.
Each kind is a gated step that delegates to its child *-sync action, which in turn delegates the
diff and PR mechanics to files-sync. New sync kinds added upstream
propagate to every consumer automatically through this action's input defaults — no consumer
workflow edit is required. The action does not require actions/checkout and never touches the
runner's working tree.
name: Upstream
on:
schedule:
- cron: "0 * * * *"
workflow_dispatch:
concurrency:
group: upstream-${{ github.workflow }}
cancel-in-progress: false
permissions:
contents: write
pull-requests: write
jobs:
sync:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: awinogradov/code-assistants/.github/actions/upstream-sync@main
with:
bot_token: ${{ secrets.BOT_TOKEN }}
bot_username: ${{ vars.BOT_USERNAME }}
# Every kind runs by default. Opt out by setting a kind to false, e.g.:
# release: false| Input | Required | Default | Description |
|---|---|---|---|
bot_token |
yes | — | PAT or GitHub App installation token with contents: write + pull-requests: write on this repo. The workflow's default GITHUB_TOKEN is not supported — see Permissions. |
bot_username |
no | github-actions[bot] |
Git author/committer login for the sync commits. Pass ${{ vars.BOT_USERNAME }}. The PRs are opened by the bot_token owner. |
agents-rules |
no | true |
When true, sync the stack-appropriate rules file into AGENTS.md and publish CLAUDE.md as a symlink to it. Set to false to opt out. |
code-review |
no | true |
When true, sync the AI code-review workflow. Set to false to opt out. |
repomix |
no | true |
When true, sync the repomix-pack workflow and repomix.config.json. Set to false to opt out. |
release |
no | true |
When true, sync the release pipeline workflows. Set to false to opt out. |
licenses |
no | true |
When true, sync the licenses workflow. Set to false to opt out. |
contributing |
no | true |
When true, sync the contributing artefacts and workflows. Set to false to opt out. |
The kind inputs are booleans expressed as strings — use true / false, not YAML's yes / on
(which serialize to "yes" / "on" and would silently disable the kind, since the gate is
== 'true').
This action exposes no outputs. Each child *-sync action still surfaces its own
changed-files / pr-number / pr-url when invoked directly.
The bot_token input is required. The workflow's default GITHUB_TOKEN is not supported, because creating pull requests with it can fail with an opaque 403 (GitHub Actions is not permitted to create or approve pull requests) whenever this repo or its org disables the Settings → Actions → General → Workflow permissions → "Allow GitHub Actions to create and approve pull requests" toggle.
Pass one of the following:
- A classic Personal Access Token with the
reposcope. - A fine-grained Personal Access Token scoped to this repository with
Contents: Read and writeandPull requests: Read and write. For private source repositories, the same token also needsContents: Readon the source repo. - A GitHub App installation token with the same
contents: write+pull-requests: writepermissions. (App tokens are not subject to the workflow-permissions toggle above — that toggle gatesGITHUB_TOKENonly.)
Store the token in a secret (e.g., BOT_TOKEN) and pass it via bot_token: ${{ secrets.BOT_TOKEN }}. Optionally set a BOT_USERNAME repository variable to control the commit author identity.
The repomix kind additionally requires the bot_token identity to be a bypass actor on the default-branch ruleset — the synced repomix-pack.yml pushes the regenerated pack directly to the default branch. See repomix-sync for details.
- Runs the six child syncs as steps in a single job, each gated by its
<kind>input (if: inputs['<kind>'] == 'true'). A disabled kind is skipped. - Each enabled kind delegates to its
*-syncaction, which opens or force-updates exactly one PR on its fixedmaintenance-sync-<kind>branch. Branch names, titles, and commit messages are owned by the child actions and are not configurable here. - Steps run serially (one job, one runner). Each child is API-only, so the run completes well within the workflow's timeout.
- Failure isolation: every kind runs with
continue-on-error: true, and a final aggregation step fails the job if any enabled kind failed. A skipped (disabled) kind never fails the job. The::error::sync failed for: <kinds>annotation names every kind that failed. - The child actions reference upstream actions via
@main; those action directories are not vendored into consumers.
Reference the action by tag of the autopilot repo once a release is cut, e.g.:
uses: awinogradov/code-assistants/.github/actions/upstream-sync@v1The canonical upstream.yml template references it via @main so consumers always run the latest aggregator.