From e0e44f1633e05792311a5980dbaacea0fb275659 Mon Sep 17 00:00:00 2001 From: JacobPEvans <20714140+JacobPEvans-personal@users.noreply.github.com> Date: Sun, 31 May 2026 16:12:20 -0400 Subject: [PATCH] feat(release-please): central scheduled hub for org-wide releases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cut releases for every repo that carries a release-please config from one place — no per-repo workflow files. Ruleset required-workflows run only on pull_request/merge_group (never push), so release-please cannot be forced org-wide via a ruleset; instead this runs the release-please CLI against each repo via --repo-url using an org App token. Runs once daily overnight (cron 37 7 * * *) + on demand. Org App creds live only here (GH_ACTION_RELEASE_PLEASE_APP_ID var + GH_ACTION_RELEASE_PLEASE_PRIVATE_KEY secret). Repos opt in by adding release-please-config.json + manifest. Per-repo failures are non-fatal. Actions digest-pinned to current-latest (create-github-app-token v3.2.0, setup-node v6.4.0); release-please CLI pinned to 17.6.1. actionlint + shellcheck clean. Refs: dryvist/terraform-github#6 Assisted-by: Claude:claude-opus-4-8 --- .github/scripts/release-please-hub.sh | 47 ++++++++++++++++++++++ .github/workflows/release-please-hub.yml | 51 ++++++++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 .github/scripts/release-please-hub.sh create mode 100644 .github/workflows/release-please-hub.yml diff --git a/.github/scripts/release-please-hub.sh b/.github/scripts/release-please-hub.sh new file mode 100644 index 0000000..d0ae031 --- /dev/null +++ b/.github/scripts/release-please-hub.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +# Central release-please hub. +# +# Cuts releases for every org repo that carries a release-please config, from +# this one place — no per-repo workflow files. For each repo the org App is +# installed on, this runs the release-please CLI remotely via --repo-url. +# +# Opt in: add release-please-config.json + .release-please-manifest.json to a +# repo's default branch. +# Opt out: remove them. +# +# Environment: +# GH_TOKEN — org App installation token (contents + pull-requests write). +# Also used by `gh` to enumerate the installation's repositories. +set -euo pipefail + +# Repos the App is installed on (authoritative for an installation token), +# excluding archived repos. +mapfile -t repos < <( + gh api --paginate /installation/repositories \ + --jq '.repositories[] | select(.archived == false) | .full_name' +) + +echo "Discovered ${#repos[@]} non-archived repo(s) in the installation." + +for repo in "${repos[@]}"; do + # Opt-in marker: a release config on the default branch. + if ! gh api "repos/${repo}/contents/release-please-config.json" >/dev/null 2>&1; then + continue + fi + + echo "::group::release-please ${repo}" + npx --yes release-please@17.6.1 release-pr \ + --token="${GH_TOKEN}" \ + --repo-url="${repo}" \ + --config-file=release-please-config.json \ + --manifest-file=.release-please-manifest.json \ + || echo "::warning::release-pr failed for ${repo}" + + npx --yes release-please@17.6.1 github-release \ + --token="${GH_TOKEN}" \ + --repo-url="${repo}" \ + --config-file=release-please-config.json \ + --manifest-file=.release-please-manifest.json \ + || echo "::warning::github-release failed for ${repo}" + echo "::endgroup::" +done diff --git a/.github/workflows/release-please-hub.yml b/.github/workflows/release-please-hub.yml new file mode 100644 index 0000000..bf5f6cb --- /dev/null +++ b/.github/workflows/release-please-hub.yml @@ -0,0 +1,51 @@ +# Central release-please hub for the whole org. +# +# One place cuts releases for every repo that carries a release-please config — +# no per-repo workflow files. Required-workflow rulesets can't host this (they +# run on pull_request/merge_group only, never push), so instead of forcing a +# per-repo workflow this runs the release-please CLI against each repo via +# --repo-url using an org App token. Repos opt in by adding +# release-please-config.json + .release-please-manifest.json to their default +# branch. +# +# Org App creds live ONLY here: app id in the GH_ACTION_RELEASE_PLEASE_APP_ID +# org variable, private key in the GH_ACTION_RELEASE_PLEASE_PRIVATE_KEY org +# secret. The App must be installed on the org with Contents + Pull requests +# write. Action refs are digest-pinned to their current-latest releases. +name: release-please-hub + +on: + schedule: + # Once daily, overnight (≈ 03:37 America/Indiana/Indianapolis). Off the + # top-of-hour to avoid GitHub's cron congestion. + - cron: "37 7 * * *" + workflow_dispatch: + +permissions: {} + +concurrency: + group: release-please-hub + cancel-in-progress: false + +jobs: + release: + runs-on: ubuntu-latest + permissions: {} + steps: + - name: Generate org App token + id: app-token + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 + with: + app-id: ${{ vars.GH_ACTION_RELEASE_PLEASE_APP_ID }} + private-key: ${{ secrets.GH_ACTION_RELEASE_PLEASE_PRIVATE_KEY }} + owner: ${{ github.repository_owner }} + + - name: Set up Node + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version: lts/* + + - name: Run release-please for every configured repo + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + run: bash .github/scripts/release-please-hub.sh