diff --git a/.github/workflows/release-please-reusable.yml b/.github/workflows/release-please-reusable.yml index 8db1f94..28013a9 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,42 @@ 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: + # `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 + 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 +143,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' }}