From ab62afc4674527f64b9dffbbc094132b47aea09b Mon Sep 17 00:00:00 2001 From: "Michael A. Smith" Date: Fri, 15 May 2026 16:20:36 -0400 Subject: [PATCH 1/2] feat(release-please): accept App credentials and mint token in-job Adds optional secrets.app-client-id and secrets.app-private-key inputs. When both are provided, the workflow mints a short-lived GitHub App installation token inside the release-please job and uses it for checkout and the release-please action. Why in-job: GitHub masks secret values when they cross job boundaries via needs.outputs, so a sibling mint-token job would deliver "***" to the reusable workflow and the App identity would be lost. Minting in the same job that uses the token sidesteps this entirely. Falls back to the existing secrets.token, then GITHUB_TOKEN, when App credentials are not provided -- existing callers keep working unchanged. The recommended caller stanza in the file header is updated to show the App-credential pattern. --- .github/workflows/release-please-reusable.yml | 63 +++++++++++++++++-- 1 file changed, 59 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release-please-reusable.yml b/.github/workflows/release-please-reusable.yml index 8db1f94..b9dd179 100644 --- a/.github/workflows/release-please-reusable.yml +++ b/.github/workflows/release-please-reusable.yml @@ -1,7 +1,8 @@ # Release Please reusable workflow. Wraps the openCoreEMR release-please-action # fork to open release PRs and cut annotated tags from conventional commits. # -# Recommended caller stanza: +# Recommended caller stanza (mints a token from a GitHub App so release PRs +# trigger pull_request workflows): # # name: Release Please # on: @@ -15,6 +16,13 @@ # permissions: # contents: write # pull-requests: write +# secrets: +# app-client-id: ${{ vars.RELEASE_PLEASE_CLIENT_ID }} +# app-private-key: ${{ secrets.RELEASE_PLEASE_PRIVATE_KEY }} +# +# Without app-client-id/app-private-key the workflow falls back to the +# default GITHUB_TOKEN, which still works but means release PRs do not +# trigger pull_request workflows (GitHub anti-recursion). name: Release Please (reusable) on: @@ -38,7 +46,19 @@ on: default: 0 secrets: token: - description: Token for checkout and release-please. Falls back to GITHUB_TOKEN. + description: | + Pre-minted token for checkout and release-please. Falls back to + GITHUB_TOKEN. Prefer app-client-id/app-private-key so the token is + minted inside this job (cross-job secret masking would zero it out). + required: false + app-client-id: + description: | + GitHub App Client ID. When provided together with app-private-key, + the workflow mints a short-lived installation token inside this job + and uses it for checkout and release-please. Overrides `token`. + required: false + app-private-key: + description: GitHub App private key (PEM). Paired with app-client-id. required: false outputs: releases_created: @@ -64,6 +84,14 @@ jobs: permissions: contents: write pull-requests: write + # Hoist credentials to job env so step-level `if:` can read them. + # GitHub allows the `env` context in step `if:` only when env is defined + # at the workflow or job level; the `secrets` context is not available + # in step `if:` at all. + env: + APP_CLIENT_ID: ${{ secrets.app-client-id }} + APP_PRIVATE_KEY: ${{ secrets.app-private-key }} + PASSED_TOKEN: ${{ secrets.token }} outputs: releases_created: ${{ steps.release.outputs.releases_created }} release_created: ${{ steps.release.outputs.release_created }} @@ -72,11 +100,38 @@ jobs: paths_released: ${{ steps.release.outputs.paths_released }} steps: + # Mint an App installation token in-job. GitHub masks secret values when + # they cross job boundaries via needs.outputs, so a sibling mint job + # would deliver "***" instead of a usable token. Doing it here keeps the + # token in the same job and reaching the release-please action intact. + - name: Mint App token + id: app-token + if: ${{ env.APP_CLIENT_ID != '' && env.APP_PRIVATE_KEY != '' }} + uses: actions/create-github-app-token@v3 + with: + client-id: ${{ env.APP_CLIENT_ID }} + private-key: ${{ env.APP_PRIVATE_KEY }} + + - name: Resolve effective token + id: resolve-token + env: + APP_TOKEN: ${{ steps.app-token.outputs.token }} + run: | + if [[ -n "${APP_TOKEN}" ]]; then + echo "::add-mask::${APP_TOKEN}" + printf 'token=%s\n' "${APP_TOKEN}" >> "$GITHUB_OUTPUT" + elif [[ -n "${PASSED_TOKEN}" ]]; then + echo "::add-mask::${PASSED_TOKEN}" + printf 'token=%s\n' "${PASSED_TOKEN}" >> "$GITHUB_OUTPUT" + else + printf 'token=%s\n' "${{ github.token }}" >> "$GITHUB_OUTPUT" + fi + - name: Checkout uses: actions/checkout@v6 with: fetch-depth: ${{ inputs.checkout-fetch-depth }} - token: ${{ secrets.token || github.token }} + token: ${{ steps.resolve-token.outputs.token }} - name: Run Release Please id: release @@ -84,7 +139,7 @@ jobs: with: config-file: ${{ inputs.config-file }} manifest-file: ${{ inputs.manifest-file }} - token: ${{ secrets.token || github.token }} + token: ${{ steps.resolve-token.outputs.token }} - name: Summary if: ${{ steps.release.outputs.releases_created == 'true' }} From 151aceca5334fee47fb23f1dfcecafcba01e49fc Mon Sep 17 00:00:00 2001 From: "Michael A. Smith" Date: Fri, 15 May 2026 16:29:37 -0400 Subject: [PATCH 2/2] ci: stay on app-id until actionlint metadata learns client-id actions/create-github-app-token@v3 deprecated app-id in favor of client-id at runtime, but the actionlint v1.7.12 bundled action database does not know client-id yet and hard-fails the workflow. The Client ID value still works as app-id (the action accepts both), the runtime emits a soft deprecation warning, and CI passes. Revisit when rhysd/actionlint ships a newer snapshot. --- .github/workflows/release-please-reusable.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release-please-reusable.yml b/.github/workflows/release-please-reusable.yml index b9dd179..28013a9 100644 --- a/.github/workflows/release-please-reusable.yml +++ b/.github/workflows/release-please-reusable.yml @@ -109,7 +109,11 @@ jobs: if: ${{ env.APP_CLIENT_ID != '' && env.APP_PRIVATE_KEY != '' }} uses: actions/create-github-app-token@v3 with: - client-id: ${{ env.APP_CLIENT_ID }} + # `app-id` accepts a Client ID as well; the action emits a runtime + # deprecation warning preferring `client-id`, but the bundled + # actionlint database doesn't know `client-id` yet and fails the + # workflow. Stay on `app-id` until actionlint catches up. + app-id: ${{ env.APP_CLIENT_ID }} private-key: ${{ env.APP_PRIVATE_KEY }} - name: Resolve effective token