From 158be47c8d2654781631765bc7a3f86f5140c74f Mon Sep 17 00:00:00 2001 From: Marcus Burghardt Date: Mon, 2 Jun 2025 16:20:47 +0200 Subject: [PATCH 1/4] ci: run release-please with an installation token release-please creates a PR bumping versions and drafting a changelog. By default the PR is owned by github-actions and therefore CI tests from other workflows are not executed to prevent loop. This commit makes release-please to use a GH App token. Signed-off-by: Marcus Burghardt --- .github/workflows/release.yml | 75 ++++++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 55744857..88645d5a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,4 +10,77 @@ jobs: contents: write pull-requests: write steps: - - uses: googleapis/release-please-action@a02a34c4d625f9be7cb89156071d8567266a2445 # 4.2.0 + - name: Install Dependencies for Tokens + run: | + pip install PyJWT cryptography requests + + - name: Generate GitHub App JWT + id: generate_jwt + run: | + import jwt + import time + import os + + # Ensure the environment variables are set + app_id = os.environ["GH_APP_ID"] + private_key = os.environ["GH_APP_PRIVATE_KEY"] + + # Expiration time is set to 2 minutes + payload = { + "iat": int(time.time()) - 60, + "exp": int(time.time()) + (2 * 60), + "iss": app_id + } + + encoded_jwt = jwt.encode( + payload, + private_key, + algorithm="RS256" + ) + + # Mask JWT in logs + print(f"::add-mask::{encoded_jwt}") + + # Pass it to next step via GITHUB_OUTPUT + with open(os.environ["GITHUB_OUTPUT"], "a") as f: + f.write(f"jwt={encoded_jwt}\n") + env: + GH_APP_ID: ${{ secrets.GH_APP_ID }} + GH_APP_PRIVATE_KEY: ${{ secrets.GH_APP_PRIVATE_KEY }} + shell: python + + - name: Get Installation Token + id: get_installation_token + run: | + import requests + import os + + # Ensure the environment variables are set + jwt_token = os.environ["GH_APP_JWT"] + installation_id = os.environ["GH_APP_INSTALLATION_ID"] + + headers = { + "Authorization": f"Bearer {jwt_token}", + "Accept": "application/vnd.github+json" + } + + url = f"https://api.github.com/app/installations/{installation_id}/access_tokens" + response = requests.post(url, headers=headers) + response.raise_for_status() + token = response.json()["token"] + + # Mask token in logs + print(f"::add-mask::{token}") + + # Pass it to next step via GITHUB_OUTPUT + with open(os.environ["GITHUB_OUTPUT"], "a") as f: + f.write(f"token={token}\n") + env: + GH_APP_INSTALLATION_ID: ${{ secrets.GH_APP_INSTALLATION_ID }} + GH_APP_JWT: ${{ steps.generate_jwt.outputs.jwt }} + shell: python + + - name: Run release-please + uses: googleapis/release-please-action@a02a34c4d625f9be7cb89156071d8567266a2445 # 4.2.0 + with: + token: ${{ steps.get_installation_token.outputs.token }} From 2d410687d836b67d5728b49577b840a4bcc8569e Mon Sep 17 00:00:00 2001 From: Marcus Burghardt Date: Mon, 2 Jun 2025 18:46:55 +0200 Subject: [PATCH 2/4] chore: address the kics warning for leakage Signed-off-by: Marcus Burghardt --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 88645d5a..cd26a659 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -23,7 +23,7 @@ jobs: # Ensure the environment variables are set app_id = os.environ["GH_APP_ID"] - private_key = os.environ["GH_APP_PRIVATE_KEY"] + app_pem = os.environ["GH_APP_PRIVATE_KEY"] # kics-suppress: securely loaded from GitHub Actions secret # Expiration time is set to 2 minutes payload = { @@ -34,7 +34,7 @@ jobs: encoded_jwt = jwt.encode( payload, - private_key, + app_pem, algorithm="RS256" ) From 0e711747f2a46b48a9f7d320b46ae47d5e499f15 Mon Sep 17 00:00:00 2001 From: Marcus Burghardt Date: Wed, 4 Jun 2025 09:12:25 +0200 Subject: [PATCH 3/4] ci: simplify app token management Replaced the python code by actions/create-github-app-token to achieve the same outcome in a much simpler way. This action already ensures the token is masked and expiration is tied to the job. Explicit permissions are redundant, but it is intentional to make them explicit. Signed-off-by: Marcus Burghardt --- .github/workflows/release.yml | 77 ++++------------------------------- 1 file changed, 8 insertions(+), 69 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cd26a659..72ee5c12 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,77 +10,16 @@ jobs: contents: write pull-requests: write steps: - - name: Install Dependencies for Tokens - run: | - pip install PyJWT cryptography requests - - - name: Generate GitHub App JWT - id: generate_jwt - run: | - import jwt - import time - import os - - # Ensure the environment variables are set - app_id = os.environ["GH_APP_ID"] - app_pem = os.environ["GH_APP_PRIVATE_KEY"] # kics-suppress: securely loaded from GitHub Actions secret - - # Expiration time is set to 2 minutes - payload = { - "iat": int(time.time()) - 60, - "exp": int(time.time()) + (2 * 60), - "iss": app_id - } - - encoded_jwt = jwt.encode( - payload, - app_pem, - algorithm="RS256" - ) - - # Mask JWT in logs - print(f"::add-mask::{encoded_jwt}") - - # Pass it to next step via GITHUB_OUTPUT - with open(os.environ["GITHUB_OUTPUT"], "a") as f: - f.write(f"jwt={encoded_jwt}\n") - env: - GH_APP_ID: ${{ secrets.GH_APP_ID }} - GH_APP_PRIVATE_KEY: ${{ secrets.GH_APP_PRIVATE_KEY }} - shell: python - - name: Get Installation Token - id: get_installation_token - run: | - import requests - import os - - # Ensure the environment variables are set - jwt_token = os.environ["GH_APP_JWT"] - installation_id = os.environ["GH_APP_INSTALLATION_ID"] - - headers = { - "Authorization": f"Bearer {jwt_token}", - "Accept": "application/vnd.github+json" - } - - url = f"https://api.github.com/app/installations/{installation_id}/access_tokens" - response = requests.post(url, headers=headers) - response.raise_for_status() - token = response.json()["token"] - - # Mask token in logs - print(f"::add-mask::{token}") - - # Pass it to next step via GITHUB_OUTPUT - with open(os.environ["GITHUB_OUTPUT"], "a") as f: - f.write(f"token={token}\n") - env: - GH_APP_INSTALLATION_ID: ${{ secrets.GH_APP_INSTALLATION_ID }} - GH_APP_JWT: ${{ steps.generate_jwt.outputs.jwt }} - shell: python + uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6 + id: app_token + with: + app-id: ${{ secrets.GH_APP_ID }} + private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} + permission-contents: write + permission-pull-requests: write - name: Run release-please uses: googleapis/release-please-action@a02a34c4d625f9be7cb89156071d8567266a2445 # 4.2.0 with: - token: ${{ steps.get_installation_token.outputs.token }} + token: ${{ steps.app_token.outputs.token }} From b2df98659359d923d5025e78446687146353993a Mon Sep 17 00:00:00 2001 From: Marcus Burghardt Date: Wed, 4 Jun 2025 14:11:29 +0200 Subject: [PATCH 4/4] chore: remove unnecessary persmissions These permissions were defined for GITHUB_TOKEN and are no longer necessary since release-please is now using an installation token. Signed-off-by: Marcus Burghardt --- .github/workflows/release.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 72ee5c12..9ae48092 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,9 +6,6 @@ on: jobs: release-please: runs-on: ubuntu-latest - permissions: - contents: write - pull-requests: write steps: - name: Get Installation Token uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6