From e0b4a3d6b1d0743dc04eae1c631d6310a75b9971 Mon Sep 17 00:00:00 2001 From: Joe Corall Date: Tue, 7 Jan 2025 13:15:09 -0500 Subject: [PATCH] Automatically run renovate on a schedule (#366) * Run renovate on a schedule * don't even need to check out the repo * Use a GitHub App * Update README.md * add check to access ci script * Make JWT generation more legible --- .github/workflows/renovate.yml | 32 +++++++++++++ .github/workflows/validate-renovate.yml | 2 +- README.md | 30 ++++++++++-- ci/fetch-app-token.sh | 64 +++++++++++++++++++++++++ 4 files changed, 122 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/renovate.yml create mode 100755 ci/fetch-app-token.sh diff --git a/.github/workflows/renovate.yml b/.github/workflows/renovate.yml new file mode 100644 index 00000000..3e879abf --- /dev/null +++ b/.github/workflows/renovate.yml @@ -0,0 +1,32 @@ +name: run renovate + +on: + workflow_dispatch: + # Monday mornings + schedule: + - cron: '15 1 * * 1' + +env: + LOG_LEVEL: debug + RENOVATE_REPOSITORIES: islandora-devops/isle-buildkit + RENOVATE_ALLOWED_POST_UPGRADE_COMMANDS: '["bash ci/update-sha.sh \"{{{depName}}}\" \"{{{currentVersion}}}\" \"{{{newVersion}}}\" \"{{{newDigest}}}\""]' +jobs: + run: + runs-on: ubuntu-24.04 + timeout-minutes: 10 + + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + + - uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4 + with: + node-version: 20 + + - name: run renovate + run: | + # fetch GitHub App token for this repo + echo "${{ secrets.GH_APP_PRIV_KEY }}" | base64 -d > private-key.pem + export RENOVATE_TOKEN=$(./ci/fetch-app-token.sh ${{ secrets.GH_APP_ID }} ${{ secrets.GH_APP_INSTALLATION_ID }} private-key.pem) + + # run renovate with our token + npx renovate --platform=github diff --git a/.github/workflows/validate-renovate.yml b/.github/workflows/validate-renovate.yml index 4958b154..e7db9f87 100644 --- a/.github/workflows/validate-renovate.yml +++ b/.github/workflows/validate-renovate.yml @@ -8,7 +8,7 @@ env: jobs: renovate-config-validator: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 timeout-minutes: 10 steps: diff --git a/README.md b/README.md index 8c178c84..cda8dfb5 100644 --- a/README.md +++ b/README.md @@ -521,9 +521,10 @@ shasum -a 256 ${ALPACA_FILE} #### Renovate -Several dependencies in this repo can be automatically updating using [renovate](https://www.mend.io/renovate/). +Several dependencies in this repo can be automatically updated using [renovate](https://www.mend.io/renovate/). Most dependencies are managed using [advanced capture](https://docs.renovatebot.com/modules/manager/regex/#advanced-capture) in the Dockerfile. -Currently these docker images have some depenencies managed by renovate: + +Currently these docker images have some dependencies managed by renovate: ``` activemq @@ -542,7 +543,28 @@ tomcat Since renovate does not natively support the ability to extract a sha256 from a file, we need [a custom shell script](./ci/update-sha.sh) in the [postUpgradeTasks](https://docs.renovatebot.com/configuration-options/#postupgradetasks) to calculate the sha256 of our files and update our Dockerfile accordingly. -Post upgrade tasks can only run on self-hosted Renovate instances, so this forces us to run renovate on a properly configured runner (instead of using mend.io's free GitHub app to manage our dependencies). Getting renovate setup locally looks like +Post upgrade tasks can only run on self-hosted Renovate instances, so this forces us to run renovate on a properly configured runner (instead of using mend.io's free GitHub app to manage our dependencies). + +##### Running renovate (automated) + +We have [a GitHub Action](./.github/workflows/renovate.yml) that runs on a schedule (or can be triggered manually) to automate generating renovate updates. + +That action requires a GitHub App (`isle-buildkit-renovate`) to be installed in the Islandora-Devops GitHub org. This app is needed to generate a GitHub access token to allow renovate to create PRs for us in the GitHub workflow. During installation, the app was restricted to only this repo. This is all configurable in the GitHub UI by Islandora-Devops admins. + +The action requires three secrets. + +- The `GH_APP_INSTALLATION_ID` secret is the number found in the URL on [the GitHub Apps installation page for the Islandora-Devops org](https://github.com/organizations/Islandora-Devops/settings/installations) for the `isle-buildkit-renovate` app + +- The two other secrets `GH_APP_ID` and `GH_APP_PRIV_KEY` can be found on [the GitHub App settings](https://github.com/organizations/Islandora-Devops/settings/apps/isle-buildkit-renovate) + - The value of `GH_APP_ID` is shown at the top of the page at the above URL + - The value of `GH_APP_PRIV_KEY` is a base64 encoded string of a private key created at the bottom of the page in the above UI (i.e. app settings). Creating a new key will download the key to your local machine. You can then generate the value needed by the GitHub Action by running e.g. `base64 -i ~/Downloads/isle-buildkit-renovate.2025-01-06.private-key.pem` and pasting that value into https://github.com/Islandora-Devops/isle-buildkit/settings/secrets/actions/GH_APP_PRIV_KEY. + +> [!IMPORTANT] +> The `GH_APP_PRIV_KEY` value is the only truly secret value out of the three secrets needed by the action. If this value is ever exposed it must be rotated by deleting the existing private key and generating a new key. + +##### Running renovate (manually for debugging) + +First, generate a GitHub token. Instructions can be found at https://docs.renovatebot.com/modules/platform/github/#running-using-a-fine-grained-token ``` npm install -g renovate @@ -555,8 +577,6 @@ export RENOVATE_ALLOWED_POST_UPGRADE_COMMANDS='["bash ci/update-sha.sh \"{{{depN renovate --platform=github ``` -Versions listed in GitHub tags or releases can use [advanced capture](https://docs.renovatebot.com/modules/manager/regex/#advanced-capture) in the Dockerfile to update the pinned version. - #### Updating Composer diff --git a/ci/fetch-app-token.sh b/ci/fetch-app-token.sh new file mode 100755 index 00000000..d9b36e0f --- /dev/null +++ b/ci/fetch-app-token.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env bash + +set -eou pipefail + +if [[ $# -ne 3 ]]; then + echo "Usage: $0 " + echo "e.g. ./ci/fetch-app-token.sh 123 456 /path/to/priv.pem" + exit 1 +fi + +APP_ID="$1" +INSTALL_ID="$2" +PRIVATE_KEY_FILE="$3" + +if [[ ! -f "$PRIVATE_KEY_FILE" ]]; then + echo "Error: Private key file not found: $PRIVATE_KEY_FILE" + exit 1 +fi + +b64url_encode() { + base64 -w 0 | tr -d '=' | tr '/+' '_-' | tr -d '\n' +} + +JWT_HEADER=$(jq -n \ + '{ + "alg": "RS256", + "typ": "JWT" + }' | b64url_encode +) + +NOW=$(date +%s) +EXPIRATION=$((NOW + 300)) # 5m +JWT_PAYLOAD=$(jq -n \ + --argjson iat "$NOW" \ + --argjson exp "$EXPIRATION" \ + --arg iss "$APP_ID" \ + '{ + "iat": $iat, + "exp": $exp, + "iss": $iss + }' | b64url_encode +) + +JWT_SIGNATURE=$(echo -n "${JWT_HEADER}.${JWT_PAYLOAD}" \ + | openssl dgst -sha256 -sign "$PRIVATE_KEY_FILE" \ + | b64url_encode +) + +JWT="${JWT_HEADER}.${JWT_PAYLOAD}.${JWT_SIGNATURE}" + +RESPONSE=$(curl -s -X POST \ + "https://api.github.com/app/installations/${INSTALL_ID}/access_tokens" \ + -H "Authorization: Bearer $JWT" \ + -H "Accept: application/vnd.github+json") + +if echo "$RESPONSE" | jq -e '.token' > /dev/null; then + # this token has a TTL of 60m + # see https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-an-installation-access-token-for-a-github-app#generating-an-installation-access-token + echo "$RESPONSE" | jq -r '.token' +else + echo "Error:" >&2 + echo "$RESPONSE" | jq >&2 + exit 1 +fi