Skip to content

feat: apply-repo-settings action (in-workflow rulesets sync) - #23

Merged
nsheaps merged 4 commits into
mainfrom
claude/practical-shannon-a2n2d
May 28, 2026
Merged

nsheaps merged 4 commits into
mainfrom
claude/practical-shannon-a2n2d

Conversation

@jack-nsheaps

@jack-nsheaps jack-nsheaps Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

Summary

  • New composite action at .github/actions/apply-repo-settings/ — an ephemeral, in-workflow alternative to the repository-settings GitHub App.
  • Covers the two sections we actually use today: repository: config + rulesets:. Other sections (labels/collaborators/teams/environments/legacy branches) are out of scope — they have separate sync mechanisms in nsheaps/.github.
  • ~170 lines of bash + gh api. Non-destructive: never deletes rulesets that aren't in the YAML.
  • Includes docs/setup.html — a single-file GitHub App manifest helper that builds the app with exactly the permissions this action needs (administration:write, contents:read, metadata:read) and exchanges the manifest code for credentials client-side. Five accordion sections cover hosting (GH Pages recipe), troubleshooting, manual setup, and key rotation.

Why a fresh impl, not a fork of the upstream app

The upstream is a Probot webhook server — adapting it to one-shot in-workflow runs is more invasive than rewriting the subset we need. README documents the choice.

Test plan

  • Confirm action.yml + action.sh lint clean (existing mise run check covers bash -n).
  • Validate docs/setup.html renders + the manifest submit flow ends at ?code= on a return.
  • Dry-run the action against nsheaps/.github (see consumer PR in nsheaps/.github).

Consumer

nsheaps/.github PR for the workflow. Land this one first; that one pins @main.

claude and others added 4 commits May 28, 2026 05:32
An ephemeral alternative to the repository-settings GitHub App
(https://github.com/repository-settings/app). The upstream app is a
Probot webhook server — adapting it for one-shot in-workflow runs is
more invasive than building a minimal applier from scratch.

Why a fresh implementation: this action covers the two sections we
actually use today (`repository:` config and `rulesets:`) in ~170
lines of bash + `gh api`. Other sections (labels/collaborators/teams/
environments/legacy-branches) aren't implemented — they have separate
sync mechanisms in nsheaps/.github already.

Contents:
  action.yml          composite action interface (app-id + private-key
                      required; owner/repo/settings-file/dry-run/
                      sections all optional with sensible defaults).
                      Outputs a JSON summary of changes.
  action.sh           the applier:
                      - PATCH /repos/{owner}/{repo} with .repository
                      - For each ruleset: list existing, diff against
                        desired, POST/PUT as needed. Never deletes
                        rulesets that aren't in the YAML.
  docs/setup.html     static HTML helper that builds a GitHub App
                      manifest with the exact permissions this action
                      needs (administration:write + contents:read +
                      metadata:read), submits to
                      github.com/.../apps/new, then exchanges the
                      returned code for credentials via the manifest
                      conversion endpoint. Accordion sections cover
                      hosting choices and a manual-setup walkthrough.
  README.md           inputs/outputs reference + example workflow that
                      triggers on workflow_dispatch / repository_dispatch
                      / push to settings.yml on main.

Permissions intentionally narrow: this app needs Administration:write
to manage rulesets and repo config, plus Contents:read to read the
settings file. No Issues/PR/Workflow write — labels are managed by
sync-labels today.

Reuses the existing actions/create-github-app-token pin
(29824e69f54612133e76f7eaac726eef6c875baf, v2) used by
github-app-auth elsewhere in this repo.
Extends the apply-repo-settings setup helper into a reusable page that
can register any GitHub App via the manifest flow, then deploys it to
GitHub Pages so the hosted redirect URL is stable.

Changes:

- pages/index.html (moved from .github/actions/apply-repo-settings/
  docs/setup.html and substantially restructured):
    * App preset dropdown driven by a single PRESETS registry. Adding
      a new app = one entry in the registry; no other code changes.
    * apply-repo-settings is the seed preset; manifest permissions,
      secret names, and post-creation next-steps move into the entry.
    * Generic ?qparam=value form prefill — any input/select/textarea
      with a `name` matching a query param gets prefilled at load.
      Supports text/url/select/textarea/checkbox+radio. Field hints
      under each label name the qparam.
    * Round-trip state preserved via `&state=preset=...` on the
      manifest submit, with sessionStorage + ?preset fallback on
      return, so the right preset's secret names / next-steps render
      after the GitHub redirect-back.
    * Redirect URL defaults to the current page (sans code/state).
    * New "How do query-param prefills work?" accordion documents
      the mechanism; hosting + manual-setup accordions retained.

- .github/workflows/pages.yaml: deploys pages/ via the standard
  actions/configure-pages + upload-pages-artifact + deploy-pages
  trio. Triggers on push to main with paths in pages/ or this
  workflow, plus workflow_dispatch. concurrency: pages, serial.
  One-time enablement still required at repo Settings → Pages
  (Source: GitHub Actions) — noted in the workflow comment.

- .github/actions/apply-repo-settings/README.md:
    * "Setting up the GitHub App" section now points at the hosted
      page with the preset preselected:
        https://nsheaps.github.io/github-actions/?preset=apply-repo-settings
    * Cross-references pages/index.html + the deploy workflow.

Validation: JS parses clean via `node --check`. Preset block contains
the documented permissions and secret names. (Headless browser smoke
test not run — chromium not installed in this environment.)

@henry-nsheaps henry-nsheaps Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: Correctness 92% | Security 88% | Simplicity 90%

Scope: 4 files added (action.sh, action.yml, pages/index.html, .github/workflows/pages.yaml). 2 chore-format auto-commits. Draft ✅
CI: Format ✅ Security ✅

action.sh — shell correctness

  • set -euo pipefail ✅ — proper shell safety
  • Required env var guards ${VAR:?required}
  • want_section() correctly uses [[ ",$SECTIONS," == *",$target,"* ]] — handles edge cases ✅
  • yq -o=json '.repository // {}' — correct null/empty handling ✅
  • gh api --paginate for listing existing rulesets — handles repos with many rulesets ✅
  • Ruleset comparison normalizes both sides with jq -S '.' (sorted keys) and extracts only the diffable fields (name, target, enforcement, conditions, rules, bypass_actors) — correct approach for idempotency ✅
  • Non-destructive: never deletes rulesets absent from YAML — documented and intentional ✅
  • Summary generation with ${CREATED[@]:-} + jq -s 'map(select(length>0))' correctly produces [] for empty arrays ✅
  • create-github-app-token pinned to SHA (29824e69f54612133e76f7eaac726eef6c875baf) ✅

action.yml

  • SETTINGS_FILE: ${{ inputs.settings-file }} passed as env var and quoted in action.sh — not injection-prone ✅
  • GH_TOKEN scoped only to the apply step ✅

pages/index.html

  • Client-side manifest flow is GitHub's documented approach — correct ✅
  • &state=preset=... roundtrip is safe — preset name only, no credentials ✅
  • JS parses clean per commit message ✅

Follow-ups:

  • P2: action.yml installs yq from https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64latest is a moving target. Pin to a specific version (e.g. v4.44.3) to avoid silent breaking changes on new releases.
  • P2: PR has no labels applied.

Reviewed by henry-nsheaps[bot] · nsheaps/github-actions#23

@nsheaps
nsheaps marked this pull request as ready for review May 28, 2026 21:05
@nsheaps
nsheaps merged commit 5215aa5 into main May 28, 2026
2 checks passed
@nsheaps
nsheaps deleted the claude/practical-shannon-a2n2d branch May 28, 2026 21:06
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.

2 participants