{"id":50607289,"url":"https://github.com/brettdavies/.github","last_synced_at":"2026-06-06T00:04:11.630Z","repository":{"id":345364711,"uuid":"1185585669","full_name":"brettdavies/.github","owner":"brettdavies","description":"Reusable GitHub Actions workflows for brettdavies Rust CLI tools","archived":false,"fork":false,"pushed_at":"2026-06-04T22:10:21.000Z","size":178,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-04T22:14:55.104Z","etag":null,"topics":["automation","ci-cd","github-actions","release-automation","reusable-workflows","rust","workflow-templates"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/brettdavies.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-18T18:31:16.000Z","updated_at":"2026-06-04T22:10:52.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/brettdavies/.github","commit_stats":null,"previous_names":["brettdavies/.github"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/brettdavies/.github","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brettdavies%2F.github","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brettdavies%2F.github/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brettdavies%2F.github/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brettdavies%2F.github/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/brettdavies","download_url":"https://codeload.github.com/brettdavies/.github/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brettdavies%2F.github/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33964367,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-05T02:00:06.157Z","response_time":120,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["automation","ci-cd","github-actions","release-automation","reusable-workflows","rust","workflow-templates"],"created_at":"2026-06-06T00:04:11.026Z","updated_at":"2026-06-06T00:04:11.622Z","avatar_url":"https://github.com/brettdavies.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# brettdavies/.github\n\nReusable GitHub Actions workflows for all brettdavies Rust CLI tools.\n\n## Why\n\nEvery SHA bump, runner update, or new feature requires exactly one PR to this repo instead of N PRs across N consumer\nrepos.\n\n## Directory structure\n\nGitHub requires reusable workflows in `.github/workflows/`. Since this repo *is* named `.github`, the on-disk path is:\n\n```text\n.github/                    # repo root\n  .github/                  # GitHub's special directory\n    workflows/\n      rust-ci.yml           # reusable CI workflow\n      rust-release.yml      # reusable release workflow\n      rust-finalize-release.yml  # reusable finalize workflow\n      lint.yml              # internal: actionlint on push/PR\n```\n\n## Reusable workflows\n\n### `rust-ci.yml`\n\nCI for Rust CLI tools: fmt, clippy, test, security audit, package check.\n\n|                                 |                                         |\n| ------------------------------- | --------------------------------------- |\n| **Trigger**                     | `workflow_call` (no inputs, no secrets) |\n| **Required caller permissions** | `contents: read`                        |\n\n**Caller example:**\n\n```yaml\nname: CI\non:\n  push:\n    branches: [main]\n  pull_request:\n    branches: [main]\npermissions:\n  contents: read\njobs:\n  ci:\n    uses: brettdavies/.github/.github/workflows/rust-ci.yml@main\n```\n\n### `rust-release.yml`\n\nFull release pipeline: version check, audit, cross-platform build (7 targets — 5 hard-required, 2 linux-musl rows\nsoft-fail by default), crates.io publish (Trusted Publishing OIDC), draft GitHub Release (notes extracted from\nCHANGELOG.md), Homebrew dispatch.\n\n|                                 |                                                                                                                                                                             |\n| ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **Trigger**                     | `workflow_call`                                                                                                                                                             |\n| **Inputs**                      | `crate` (string, required), `bin` (string, required), `linux_musl_required` (bool, optional, default `false`), `linux_musl_verify_alpine` (bool, optional, default `false`) |\n| **Secrets**                     | `CI_RELEASE_TOKEN` (required, explicit — not inherited)                                                                                                                     |\n| **Required caller permissions** | `contents: write`, `id-token: write`                                                                                                                                        |\n\n**Caller example:**\n\n```yaml\nname: Release\non:\n  push:\n    tags: ['v[0-9]+.[0-9]+.[0-9]+']\npermissions:\n  contents: write\n  id-token: write\njobs:\n  pipeline:\n    uses: brettdavies/.github/.github/workflows/rust-release.yml@main\n    with:\n      crate: bird\n      bin: bird\n    secrets:\n      CI_RELEASE_TOKEN: ${{ secrets.CI_RELEASE_TOKEN }}\n```\n\n### `rust-finalize-release.yml`\n\nPublishes a draft GitHub Release after Homebrew bottles are uploaded.\n\n|                                 |                                                       |\n| ------------------------------- | ----------------------------------------------------- |\n| **Trigger**                     | `workflow_call` (no inputs)                           |\n| **Required caller permissions** | `contents: write`                                     |\n| **Secrets**                     | None (only `GITHUB_TOKEN`, which flows automatically) |\n\n**Caller example:**\n\n```yaml\nname: Finalize Release\non:\n  repository_dispatch:\n    types: [finalize-release]\npermissions:\n  contents: write\njobs:\n  finalize:\n    uses: brettdavies/.github/.github/workflows/rust-finalize-release.yml@main\n```\n\n### `guard-main-docs.yml`\n\nBlocks engineering docs (`docs/architecture/`, `docs/brainstorms/`, `docs/ideation/`, `docs/plans/`, `docs/research/`,\n`docs/reviews/`, `docs/solutions/`) from reaching main.\n\n|                                 |                             |\n| ------------------------------- | --------------------------- |\n| **Trigger**                     | `workflow_call` (no inputs) |\n| **Required caller permissions** | `pull-requests: read`       |\n\n**Caller example:**\n\n```yaml\nname: Guard main from engineering docs\non:\n  pull_request:\n    branches: [main]\npermissions:\n  pull-requests: read\njobs:\n  guard-docs:\n    uses: brettdavies/.github/.github/workflows/guard-main-docs.yml@main\n```\n\n### `guard-main-provenance.yml`\n\nVerifies that every non-exempt commit in a PR to `main` carries a `(#N)` PR reference, indicating it was squash-merged\nfrom a feature PR to `dev`. Catches direct-pushes to `dev` or release branches that bypass the PR-review boundary.\n\nSkipped automatically for `release/*` head branches — cherry-picks from dev inherently lose their PR references, and the\nrelease PR itself is the review gate.\n\nExempt commit-message prefixes: `docs:`, `chore:`, `ci:`, `style:`, `build:` (housekeeping commits authored directly on\nthe release branch — version bumps, changelogs, CI tweaks). `test:` is **not** exempt — tests are code and must go\nthrough PRs.\n\n|                                 |                             |\n| ------------------------------- | --------------------------- |\n| **Trigger**                     | `workflow_call` (no inputs) |\n| **Required caller permissions** | `pull-requests: read`       |\n\n**Caller example:**\n\n```yaml\nname: Guard main commit provenance\non:\n  pull_request:\n    branches: [main]\npermissions:\n  pull-requests: read\njobs:\n  guard-provenance:\n    uses: brettdavies/.github/.github/workflows/guard-main-provenance.yml@main\n```\n\n### `guard-release-branch.yml`\n\nRejects PRs to main whose head branch doesn't start with `release/`. Enforces the release-branch pattern so that `dev`\nis never a PR head (which keeps `deleteBranchOnMerge: true` compatible with a forever `dev` branch).\n\n|                                 |                                                                   |\n| ------------------------------- | ----------------------------------------------------------------- |\n| **Trigger**                     | `workflow_call` with optional `prefix` input (default `release/`) |\n| **Required caller permissions** | `pull-requests: read`                                             |\n\n**Caller example:**\n\n```yaml\nname: Guard release branch pattern\non:\n  pull_request:\n    branches: [main]\npermissions:\n  pull-requests: read\njobs:\n  guard-release:\n    uses: brettdavies/.github/.github/workflows/guard-release-branch.yml@main\n```\n\n## Ruleset templates\n\nStarting points for GitHub branch protection, committed under `.github/rulesets/`. Consumer repos copy these into their\nown `.github/rulesets/` and extend the `required_status_checks` list with repo-specific checks.\n\n| Template            | Purpose                                                                                                                                                                                                                                     |\n| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `protect-main.json` | Squash-only PR merge, linear history, required signatures, `actionlint` required. Add repo-specific checks (`ci / \u003cjob\u003e`, `guard-docs / check-forbidden-docs`, `Guard release branch pattern / check-release-branch-name`) before applying. |\n| `protect-dev.json`  | Dev forever-branch protection: no deletion, no non-fast-forward, required signatures. No PR requirement at the ruleset level (enforced by convention + `guard-release-branch` on the main side).                                            |\n\nApply with `gh api`:\n\n```bash\ngh api -X POST repos/\u003cowner\u003e/\u003crepo\u003e/rulesets --input .github/rulesets/protect-dev.json\ngh api -X PUT  repos/\u003cowner\u003e/\u003crepo\u003e/rulesets/\u003cid\u003e --input .github/rulesets/protect-main.json\n```\n\n## Security\n\n- All third-party actions are SHA-pinned (except `dtolnay/rust-toolchain@stable`)\n- No `secrets: inherit` — secrets are passed explicitly\n- All `${{ }}` expressions in `run:` blocks use `env:` indirection (zero direct interpolation)\n- Input validation: `crate` and `bin` are validated with `[a-zA-Z0-9_-]+` regex\n- Tag format validation in finalize-release (`^v[0-9]+\\.[0-9]+\\.[0-9]+$`)\n- Per-job permission narrowing inside reusable workflows\n\n## Ref pinning\n\nConsumer repos reference these workflows via `@main`. Rationale:\n\n- Same owner controls all repos (no supply chain risk)\n- `actionlint` CI + branch protection catches errors before propagation\n- Rollback: revert one commit in this repo (faster than updating N consumers)\n\nMigrate to `@v1` semver tags when a third-party contributor or third+ consumer arrives.\n\n## Naming convention\n\n- `rust-*` prefix: language-specific reusable workflows\n- Unprefixed (`lint.yml`): repo-internal infrastructure\n- Caller workflows stay unprefixed (`ci.yml`, `release.yml`) — they describe intent\n\n## Naming coupling\n\nThe Homebrew dispatch chain assumes `formula name == crate name == repo name`. If a future tool breaks this coupling,\nadd an optional `formula` input to `rust-release.yml` and update `homebrew-tap/publish.yml`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrettdavies%2F.github","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbrettdavies%2F.github","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrettdavies%2F.github/lists"}