release publish: A6Gpm7Na #56
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
| # This workflow publishes a release by creating a version tag. | |
| # It is intended to be run with workflow_dispatch event by the automation. | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| release_handle: | |
| description: 'Release handle JSON string containing release information' | |
| required: true | |
| type: string | |
| workflow_uuid: | |
| description: 'Optional UUID to identify this workflow run' | |
| required: false | |
| pr_to_official_library: | |
| description: 'Open a PR to official library repo' | |
| type: boolean | |
| default: false | |
| slack_channel_id: | |
| description: 'Slack channel ID to duplicate links to announcement messages to' | |
| required: false | |
| type: string | |
| default: '' | |
| slack_thread_ts: | |
| description: 'Thread timestamp to post message as a reply (optional)' | |
| required: false | |
| type: string | |
| default: '' | |
| env: | |
| TARGET_OFFICIAL_IMAGES_REPO: docker-library/official-images | |
| FORKED_OFFICIAL_IMAGES_REPO: redis-developer/docker-library-official-images | |
| PR_USER_MENTIONS: "@yossigo @dagansandler @Peter-Sh @AngelYanev @dariaguy @kalinstaykov @hilabarkai @gonen-red" | |
| # UUID is used to help automation to identify workflow run in the list of workflow runs. | |
| run-name: "release publish${{ github.event.inputs.workflow_uuid && format(': {0}', github.event.inputs.workflow_uuid) || '' }}" | |
| jobs: | |
| publish-release: | |
| runs-on: ["ubuntu-latest"] | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Parse release handle and validate | |
| id: parse-release | |
| shell: bash | |
| run: | | |
| # Parse the JSON input | |
| RELEASE_HANDLE='${{ github.event.inputs.release_handle }}' | |
| echo "Parsing release handle JSON:" | |
| echo "$RELEASE_HANDLE" | jq . | |
| # Extract release_commit_sha | |
| RELEASE_COMMIT_SHA=$(echo "$RELEASE_HANDLE" | jq -r '.release_commit_sha // empty') | |
| # Validate that release_commit_sha exists and is not empty | |
| if [ -z "$RELEASE_COMMIT_SHA" ] || [ "$RELEASE_COMMIT_SHA" = "null" ]; then | |
| echo "Error: release_commit_sha is missing or empty in release_handle" | |
| echo "Release handle content: $RELEASE_HANDLE" | |
| exit 1 | |
| fi | |
| # Extract release_version for tag creation | |
| RELEASE_VERSION=$(echo "$RELEASE_HANDLE" | jq -r '.release_version // empty') | |
| if [ -z "$RELEASE_VERSION" ] || [ "$RELEASE_VERSION" = "null" ]; then | |
| echo "Error: release_version is missing or empty in release_handle" | |
| echo "Release handle content: $RELEASE_HANDLE" | |
| exit 1 | |
| fi | |
| echo "Successfully parsed release handle:" | |
| echo " release_commit_sha: $RELEASE_COMMIT_SHA" | |
| echo " release_version: $RELEASE_VERSION" | |
| # Set outputs for next steps | |
| echo "release_commit_sha=$RELEASE_COMMIT_SHA" >> $GITHUB_OUTPUT | |
| echo "release_version=$RELEASE_VERSION" >> $GITHUB_OUTPUT | |
| - name: Create version tag | |
| uses: redis-developer/redis-oss-release-automation/.github/actions/create-tag-verified@main | |
| with: | |
| tag: v${{ steps.parse-release.outputs.release_version }} | |
| ref: ${{ steps.parse-release.outputs.release_commit_sha }} | |
| gh_token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Sync forked official-images repo | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ secrets.GH_TOKEN_FOR_PR_2 }} | |
| run: | | |
| echo "Syncing fork ${{ env.FORKED_OFFICIAL_IMAGES_REPO }} with upstream ${{ env.TARGET_OFFICIAL_IMAGES_REPO }}" | |
| # Use GitHub API to sync the fork with upstream | |
| # POST /repos/{owner}/{repo}/merge-upstream | |
| response=$(curl -L \ | |
| -X POST \ | |
| -H "Accept: application/vnd.github+json" \ | |
| -H "Authorization: Bearer $GH_TOKEN" \ | |
| -H "X-GitHub-Api-Version: 2022-11-28" \ | |
| "https://api.github.com/repos/${{ env.FORKED_OFFICIAL_IMAGES_REPO }}/merge-upstream" \ | |
| -d '{"branch":"master"}') | |
| echo "API Response:" | |
| echo "$response" | jq '.' | |
| # Check if sync was successful | |
| if echo "$response" | jq -e '.message' > /dev/null 2>&1; then | |
| message=$(echo "$response" | jq -r '.message') | |
| if [ "$message" = "This branch is not behind the upstream docker-library:master" ]; then | |
| echo "Fork is already up to date" | |
| elif echo "$message" | grep -q "Successfully fetched and fast-forwarded"; then | |
| echo "Successfully synced fork with upstream" | |
| else | |
| echo "Warning: Unexpected response message: $message" | |
| fi | |
| elif echo "$response" | jq -e '.merge_type' > /dev/null 2>&1; then | |
| merge_type=$(echo "$response" | jq -r '.merge_type') | |
| echo "Successfully synced fork (merge type: $merge_type)" | |
| else | |
| echo "Error: Failed to sync fork" | |
| echo "$response" | |
| exit 1 | |
| fi | |
| - name: Checkout official-images repo | |
| uses: actions/checkout@v4 | |
| with: | |
| path: official-images | |
| repository: ${{ github.event.inputs.pr_to_official_library == 'true' && env.TARGET_OFFICIAL_IMAGES_REPO || env.FORKED_OFFICIAL_IMAGES_REPO }} | |
| - name: Install release automation dependencies | |
| uses: ./.github/actions/release-automation-install | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Log in to Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Apply Docker image tags to multi-arch images | |
| if: ${{ !contains(steps.parse-release.outputs.release_version, '-') }} | |
| shell: bash | |
| run: | | |
| REGISTRY=$(echo "ghcr.io/${{ github.repository }}" | tr '[:upper:]' '[:lower:]') | |
| RELEASE_VERSION="${{ steps.parse-release.outputs.release_version }}" | |
| for DISTRIBUTION in debian alpine; do | |
| SOURCE_TAG="${REGISTRY}:${RELEASE_VERSION}-${DISTRIBUTION}" | |
| echo "Processing ${SOURCE_TAG}" | |
| TAGS=$(uv --directory release-automation run release-automation \ | |
| generate-image-tags "${RELEASE_VERSION}" "${DISTRIBUTION}" 2>/dev/null) | |
| if [ -z "$TAGS" ]; then | |
| echo "No tags generated for ${RELEASE_VERSION} ${DISTRIBUTION}, skipping" | |
| continue | |
| fi | |
| echo "Generated tags: ${TAGS}" | |
| IFS=',' read -ra TAG_ARRAY <<< "$TAGS" | |
| TAG_ARGS="" | |
| for tag in "${TAG_ARRAY[@]}"; do | |
| tag=$(echo "$tag" | xargs) | |
| TAG_ARGS="${TAG_ARGS} -t ${REGISTRY}:${tag}" | |
| done | |
| docker buildx imagetools create ${TAG_ARGS} "${SOURCE_TAG}" | |
| done | |
| - name: Generate stackbrew library content | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| # Extract major version from release version (e.g., "8.2.1" -> "8") | |
| MAJOR_VERSION=$(uv --directory release-automation run release-automation redis-version major "${{ steps.parse-release.outputs.release_version }}") | |
| echo "Major version: $MAJOR_VERSION" | |
| # Generate updated stackbrew content using uv | |
| uv --directory release-automation run release-automation update-stackbrew-file $MAJOR_VERSION --input ../official-images/library/redis --output ../official-images/library/redis | |
| cd official-images && git diff --color | |
| cd - | |
| - name: Create pull request to official-images | |
| id: create-pr | |
| uses: peter-evans/create-pull-request@v7 | |
| with: | |
| token: ${{ secrets.GH_TOKEN_FOR_PR_2 }} | |
| draft: true | |
| push-to-fork: ${{ github.event.inputs.pr_to_official_library == 'true' && env.FORKED_OFFICIAL_IMAGES_REPO || '' }} | |
| path: official-images | |
| branch: redis-${{ steps.parse-release.outputs.release_version }} | |
| commit-message: "Redis: Update to ${{ steps.parse-release.outputs.release_version }}" | |
| title: "Redis: Update to ${{ steps.parse-release.outputs.release_version }}" | |
| body: | | |
| Automated update for Redis ${{ steps.parse-release.outputs.release_version }} | |
| Release commit: ${{ steps.parse-release.outputs.release_commit_sha }} | |
| Release tag: v${{ steps.parse-release.outputs.release_version }} | |
| Compare: ${{ github.server_url }}/${{ github.repository }}/compare/v${{ steps.parse-release.outputs.release_version }}^1...v${{ steps.parse-release.outputs.release_version }} | |
| ${{ env.PR_USER_MENTIONS }} | |
| - name: Validate PR creation | |
| env: | |
| PULL_REQUEST_NUMBER: ${{ steps.create-pr.outputs.pull-request-number }} | |
| shell: bash | |
| run: | | |
| if [ -z "$PULL_REQUEST_NUMBER" ]; then | |
| echo "::error::Failed to create PR, pull-request-number is empty" | |
| exit 1 | |
| fi | |
| # Since publish is only for public releases, always send notifications to the announcement channel first | |
| - name: Send Success Slack notification | |
| id: slack-success | |
| continue-on-error: true | |
| uses: ./.github/actions/slack-notification | |
| with: | |
| slack_func: slack_format_docker_PR_message | |
| slack_channel_id: ${{ vars.SLACK_CHANNEL_ID }} | |
| release_tag: v${{ steps.parse-release.outputs.release_version }} | |
| pr_url: ${{ steps.create-pr.outputs.pull-request-url }} | |
| SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} | |
| # Post link to message in the announcement channel to the slack thread if requested | |
| # This makes user to see the link to the "official" message if he started the release in the thread | |
| - name: Post announcement link to Slack | |
| continue-on-error: true | |
| if: | | |
| github.event.inputs.slack_channel_id != '' && | |
| steps.slack-success.outputs.slack_url != '' | |
| uses: ./.github/actions/slack-notification | |
| with: | |
| slack_func: slack_format_simple_msg | |
| slack_channel_id: ${{ github.event.inputs.slack_channel_id }} | |
| slack_thread_ts: ${{ github.event.inputs.slack_thread_ts }} | |
| message: ${{ steps.slack-success.outputs.slack_url }} | |
| SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} | |
| - name: Create Release info | |
| id: create-release-info | |
| shell: bash | |
| run: | | |
| # Create release_info.json with all fields, then filter out empty ones | |
| jq -n \ | |
| --arg release_version "${{ steps.parse-release.outputs.release_version }}" \ | |
| --arg pull_request_number "${{ steps.create-pr.outputs.pull-request-number }}" \ | |
| --arg pull_request_url "${{ steps.create-pr.outputs.pull-request-url }}" \ | |
| --argjson run_id "${{ github.run_id }}" \ | |
| --arg slack_ts "${{ steps.slack-success.outputs.slack_ts }}" \ | |
| --arg slack_channel_id "${{ github.event.inputs.slack_channel_id || vars.SLACK_CHANNEL_ID }}" \ | |
| --arg slack_message_url "${{ steps.slack-success.outputs.slack_url }}" \ | |
| '{ | |
| release_version: $release_version, | |
| pull_request_number: $pull_request_number, | |
| pull_request_url: $pull_request_url, | |
| run_id: $run_id, | |
| slack_ts: $slack_ts, | |
| slack_channel_id: $slack_channel_id, | |
| slack_message_url: $slack_message_url | |
| } | with_entries(select(.value != ""))' > result.json | |
| echo "Created result.json for release_info:" | |
| cat result.json | jq '.' | |
| - name: Upload release info artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: release_info | |
| path: result.json | |
| retention-days: 400 | |
| - name: Send Failure Slack notification | |
| id: slack-failure | |
| if: failure() | |
| uses: ./.github/actions/slack-notification | |
| with: | |
| slack_func: slack_format_failure_message | |
| slack_channel_id: ${{ vars.SLACK_CHANNEL_ID }} | |
| release_tag: v${{ steps.parse-release.outputs.release_version }} | |
| message: "Docker PR failed" | |
| SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} | |
| # Post link to message in the announcement channel to the slack thread if requested | |
| # This makes user to see the link to the "official" message if he started the release in the thread | |
| - name: Post failure notification link to Slack | |
| continue-on-error: true | |
| if: | | |
| failure() && | |
| github.event.inputs.slack_channel_id != '' && | |
| steps.slack-failure.outputs.slack_url != '' | |
| uses: ./.github/actions/slack-notification | |
| with: | |
| slack_func: slack_format_simple_msg | |
| slack_channel_id: ${{ github.event.inputs.slack_channel_id }} | |
| slack_thread_ts: ${{ github.event.inputs.slack_thread_ts }} | |
| message: ${{ steps.slack-failure.outputs.slack_url }} | |
| SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} |