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