release-keycloak-upgrade #8
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| # desc: create a GitHub release when Keycloak versions are upgraded by Renovate | |
| name: release-keycloak-upgrade | |
| on: | |
| workflow_dispatch: | |
| schedule: | |
| - cron: 0 8 * * 1 | |
| # Scheduled workflows run on the default branch only. | |
| # See: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: false | |
| permissions: | |
| contents: write | |
| jobs: | |
| check-changes: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| has_changes: ${{ steps.check.outputs.has_changes }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| with: | |
| fetch-depth: 0 | |
| - name: Check for relevant changes since last release | |
| id: check | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| set -euo pipefail | |
| # Get the latest release tag | |
| last_tag=$(gh release list --limit 1 --json tagName --jq '.[0].tagName // empty') | |
| echo "Checking changes since tag: ${last_tag}" | |
| changed_files=$(git diff --name-only "${last_tag}..HEAD") | |
| # Check if any keycloak bases.yml or Dockerfile* files were changed | |
| if echo "$changed_files" | grep -qE '^keycloak-[0-9]+/(bases\.yml|Dockerfile)'; then | |
| echo "has_changes=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "has_changes=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| release: | |
| runs-on: ubuntu-latest | |
| needs: | |
| - check-changes | |
| if: needs.check-changes.outputs.has_changes == 'true' | |
| steps: | |
| - name: Generate token for GitHub | |
| id: generate-github-token | |
| uses: camunda/infra-global-github-actions/generate-github-app-token-from-vault-secrets@f5807d2242ed5d143402f3ff092901ce920b01d0 # main | |
| with: | |
| github-app-id-vault-key: GITHUB_APP_ID | |
| github-app-id-vault-path: secret/data/products/infrastructure-experience/ci/common | |
| github-app-private-key-vault-key: GITHUB_APP_PRIVATE_KEY | |
| github-app-private-key-vault-path: secret/data/products/infrastructure-experience/ci/common | |
| vault-auth-method: approle | |
| vault-auth-role-id: ${{ secrets.VAULT_ROLE_ID }} | |
| vault-auth-secret-id: ${{ secrets.VAULT_SECRET_ID }} | |
| vault-url: ${{ secrets.VAULT_ADDR }} | |
| - name: Checkout | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| with: | |
| fetch-depth: 0 | |
| token: ${{ steps.generate-github-token.outputs.token }} | |
| - name: Create GitHub Release | |
| env: | |
| GH_TOKEN: ${{ steps.generate-github-token.outputs.token }} | |
| run: | | |
| set -euo pipefail | |
| today="$(date -u +'%Y-%m-%d')" | |
| # Find the next unused tag for today, considering both releases and remote tags | |
| next_number=1 | |
| # Account for existing releases | |
| last_release_number=$(gh release list --json tagName,createdAt --jq \ | |
| "[.[] | select(.createdAt | startswith(\"${today}\")) | .tagName | ltrimstr(\"${today}-\") | tonumber] | max // 0") | |
| if [ "${last_release_number}" -ge "${next_number}" ]; then | |
| next_number=$((last_release_number + 1)) | |
| fi | |
| # Account for existing remote tags (e.g. from partial failures that pushed a tag but didn't create a release) | |
| existing_tags=$(gh api "repos/${{ github.repository }}/git/matching-refs/tags/${today}-" --jq '.[].ref' | sed 's|refs/tags/||') | |
| for tag in ${existing_tags}; do | |
| tag_number=$(echo "${tag}" | sed "s/${today}-//" | sed 's/^0*//' ) | |
| tag_number=${tag_number:-0} | |
| if [ "${tag_number}" -ge "${next_number}" ]; then | |
| next_number=$((tag_number + 1)) | |
| fi | |
| done | |
| release_tag="${today}-$(printf '%03d' "${next_number}")" | |
| # Verify there are commits since the last release | |
| last_tag=$(gh release list --limit 1 --json tagName --jq '.[0].tagName // empty') | |
| if [ -n "${last_tag}" ]; then | |
| if ! gh api "repos/${{ github.repository }}/git/ref/tags/${last_tag}" --jq '.object.sha' >/dev/null 2>&1; then | |
| echo "::error::Last release tag ${last_tag} not found via API." | |
| exit 1 | |
| fi | |
| commit_count=$(gh api "repos/${{ github.repository }}/compare/${last_tag}...${{ github.sha }}" --jq '.total_commits') | |
| if [ "${commit_count}" -eq 0 ]; then | |
| echo "No new commits since last release ${last_tag}, skipping." | |
| exit 0 | |
| fi | |
| fi | |
| # If the tag already exists without a release (e.g. partial failure rerun), verify it points to the expected commit | |
| if remote_tag_sha=$(gh api "repos/${{ github.repository }}/git/ref/tags/${release_tag}" --jq '.object.sha' 2>/dev/null); then | |
| if [ "${remote_tag_sha}" != "${{ github.sha }}" ]; then | |
| echo "::error::Tag ${release_tag} exists remotely but points to ${remote_tag_sha}, expected ${{ github.sha }}." | |
| exit 1 | |
| fi | |
| echo "Tag ${release_tag} already exists remotely and points to the expected commit, reusing it." | |
| # Create the release from the existing tag | |
| gh release create "${release_tag}" \ | |
| --title "${release_tag}" \ | |
| --generate-notes \ | |
| --verify-tag | |
| else | |
| # Let gh release create handle tag creation via the Releases API | |
| gh release create "${release_tag}" \ | |
| --title "${release_tag}" \ | |
| --generate-notes \ | |
| --target "${{ github.sha }}" | |
| fi | |
| report-failure: | |
| name: Report failures | |
| if: failure() | |
| runs-on: ubuntu-latest | |
| needs: | |
| - check-changes | |
| - release | |
| steps: | |
| - name: Notify in Slack in case of failure | |
| id: slack-notification | |
| uses: camunda/infraex-common-config/.github/actions/report-failure-on-slack@193a21e1e56c9a65517a822224ac3b4ffa4d6ae4 # 1.5.9 | |
| with: | |
| vault_addr: ${{ secrets.VAULT_ADDR }} | |
| vault_role_id: ${{ secrets.VAULT_ROLE_ID }} | |
| vault_secret_id: ${{ secrets.VAULT_SECRET_ID }} |