Public Announce Releases to Slack #445
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
| name: Public Announce Releases to Slack | |
| on: | |
| workflow_run: | |
| workflows: ["Public Release"] | |
| types: | |
| - completed | |
| jobs: | |
| announce-to-slack: | |
| if: github.repository == 'oxy-hq/oxy' && github.event.workflow_run.conclusion == 'success' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| ref: ${{ github.event.workflow_run.head_sha }} | |
| - name: Determine build type and channel | |
| id: build_info | |
| run: | | |
| EVENT="${{ github.event.workflow_run.event }}" | |
| BRANCH="${{ github.event.workflow_run.head_branch }}" | |
| SHA="${{ github.event.workflow_run.head_sha }}" | |
| echo "event=$EVENT branch=$BRANCH sha=$SHA" | |
| # Stable: push event where head_branch is a semver tag | |
| if [[ "$EVENT" == "push" && "$BRANCH" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then | |
| { | |
| echo "type=stable" | |
| echo "channel=product-releases-prod" | |
| echo "emoji=:rocket:" | |
| echo "version=$BRANCH" | |
| echo "release_repo=oxy-hq/oxy" | |
| } >> "$GITHUB_OUTPUT" | |
| # Edge: scheduled builds or pushes to main | |
| elif [[ "$EVENT" == "schedule" || ( "$EVENT" == "push" && "$BRANCH" == "main" ) ]]; then | |
| { | |
| echo "type=edge" | |
| echo "channel=product-releases-dev" | |
| echo "emoji=:zap:" | |
| echo "version=edge-${SHA:0:7}" | |
| echo "release_repo=oxy-hq/oxy-nightly" | |
| } >> "$GITHUB_OUTPUT" | |
| else | |
| echo "type=skip" >> "$GITHUB_OUTPUT" | |
| echo "Unrecognized trigger (event=$EVENT, branch=$BRANCH) — skipping." | |
| fi | |
| - name: Verify edge Docker image was published | |
| id: edge_check | |
| if: steps.build_info.outputs.type == 'edge' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| # The release workflow reports `success` even when every build job | |
| # was skipped (e.g. main push with no oxy/web-app/docker changeset). | |
| # `Create Deployment Summary` only runs when the multi-arch manifest | |
| # was actually pushed, so use it as the marker for "edge image built". | |
| RUN_ID="${{ github.event.workflow_run.id }}" | |
| PUBLISHED=$(gh api --paginate \ | |
| "/repos/${{ github.repository }}/actions/runs/${RUN_ID}/jobs" \ | |
| --jq '[.jobs[] | select(.name == "Create Deployment Summary") | .conclusion] | any(. == "success")') | |
| echo "published=$PUBLISHED" >> "$GITHUB_OUTPUT" | |
| if [[ "$PUBLISHED" != "true" ]]; then | |
| echo "::notice::No edge Docker image published in run $RUN_ID — skipping announcement." | |
| fi | |
| - name: Resolve previous reference | |
| id: prev | |
| if: >- | |
| steps.build_info.outputs.type != 'skip' | |
| && (steps.build_info.outputs.type != 'edge' | |
| || steps.edge_check.outputs.published == 'true') | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| TYPE="${{ steps.build_info.outputs.type }}" | |
| VERSION="${{ steps.build_info.outputs.version }}" | |
| CURRENT_SHA="${{ github.event.workflow_run.head_sha }}" | |
| CURRENT_RUN_ID="${{ github.event.workflow_run.id }}" | |
| git fetch --tags --force --quiet | |
| PREV_LABEL="" | |
| PREV_SHA="" | |
| if [[ "$TYPE" == "stable" ]]; then | |
| # Most recent stable tag that isn't the current one | |
| PREV_LABEL=$(git tag --list '[0-9]*.[0-9]*.[0-9]*' --sort=-v:refname \ | |
| | grep -v "^${VERSION}$" | head -1 || true) | |
| if [[ -n "$PREV_LABEL" ]]; then | |
| PREV_SHA=$(git rev-parse "$PREV_LABEL") | |
| fi | |
| else | |
| # Candidate SHAs from recent successful Public Release runs on main, | |
| # newest-first, excluding this run. workflow_run events can complete out | |
| # of order (a fast docs-only run may finish before an earlier build-heavy | |
| # run), so "most recent success" is NOT necessarily an ancestor of | |
| # CURRENT_SHA. Pick the newest candidate that is a strict ancestor — | |
| # otherwise the `PREV..CURRENT` diff inverts and reports 0 commits. | |
| mapfile -t CANDIDATES < <(gh api \ | |
| "/repos/${{ github.repository }}/actions/workflows/public-release.yaml/runs?status=success&per_page=30" \ | |
| --jq "[.workflow_runs[] | |
| | select(.id != ${CURRENT_RUN_ID}) | |
| | select(.event == \"schedule\" or (.event == \"push\" and .head_branch == \"main\")) | |
| | .head_sha] | .[]") | |
| for sha in "${CANDIDATES[@]}"; do | |
| [[ "$sha" == "$CURRENT_SHA" ]] && continue | |
| git cat-file -e "$sha" 2>/dev/null || continue | |
| if git merge-base --is-ancestor "$sha" "$CURRENT_SHA"; then | |
| PREV_SHA="$sha" | |
| PREV_LABEL="${PREV_SHA:0:7}" | |
| break | |
| fi | |
| done | |
| fi | |
| { | |
| echo "label=$PREV_LABEL" | |
| echo "sha=$PREV_SHA" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Install git-cliff (stable only) | |
| if: steps.build_info.outputs.type == 'stable' | |
| uses: taiki-e/install-action@v2 | |
| with: | |
| tool: git-cliff | |
| - name: Build changelog | |
| id: changelog | |
| if: >- | |
| steps.build_info.outputs.type != 'skip' | |
| && (steps.build_info.outputs.type != 'edge' | |
| || steps.edge_check.outputs.published == 'true') | |
| run: | | |
| TYPE="${{ steps.build_info.outputs.type }}" | |
| VERSION="${{ steps.build_info.outputs.version }}" | |
| CURRENT_SHA="${{ github.event.workflow_run.head_sha }}" | |
| PREV_SHA="${{ steps.prev.outputs.sha }}" | |
| PREV_LABEL="${{ steps.prev.outputs.label }}" | |
| REPO_URL="https://github.com/${{ github.repository }}" | |
| CHANGELOG="" | |
| COMMIT_COUNT=0 | |
| CONTRIBUTORS="" | |
| BREAKING="false" | |
| LAST_COMMIT_AT="" | |
| COMPARE_URL="" | |
| LAST_COMMIT_AT=$(git log -1 --pretty=format:'%cI' "$CURRENT_SHA" 2>/dev/null || echo "") | |
| echo "last_commit_at=$LAST_COMMIT_AT" >> "$GITHUB_OUTPUT" | |
| if [[ -z "$PREV_SHA" ]]; then | |
| DELIM="EOF_$(uuidgen)" | |
| { | |
| echo "commit_count=0" | |
| echo "compare_url=" | |
| echo "contributors=" | |
| echo "breaking=false" | |
| echo "body<<${DELIM}" | |
| echo "_No previous reference found — cannot compute diff._" | |
| echo "${DELIM}" | |
| } >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| COMMIT_COUNT=$(git rev-list --count "${PREV_SHA}..${CURRENT_SHA}" 2>/dev/null || echo 0) | |
| echo "commit_count=$COMMIT_COUNT" >> "$GITHUB_OUTPUT" | |
| COMPARE_URL="${REPO_URL}/compare/${PREV_LABEL}...${VERSION}" | |
| echo "compare_url=$COMPARE_URL" >> "$GITHUB_OUTPUT" | |
| CONTRIBUTORS=$(git log --pretty=format:'%an' "${PREV_SHA}..${CURRENT_SHA}" \ | |
| | sort -u | paste -sd ', ' -) | |
| echo "contributors=$CONTRIBUTORS" >> "$GITHUB_OUTPUT" | |
| if git log --format='%B' "${PREV_SHA}..${CURRENT_SHA}" \ | |
| | grep -qE '^BREAKING CHANGE:|^[a-z]+(\([^)]*\))?!:'; then | |
| BREAKING="true" | |
| fi | |
| echo "breaking=$BREAKING" >> "$GITHUB_OUTPUT" | |
| if [[ "$TYPE" == "stable" ]]; then | |
| # Full categorized changelog from git-cliff | |
| BODY=$(git cliff "${PREV_LABEL}..${VERSION}" --strip all 2>/dev/null || echo "") | |
| if [[ -z "$BODY" ]]; then | |
| BODY=$(git log --no-merges \ | |
| --pretty=format:"• <${REPO_URL}/commit/%H|%h> %s — _%an_" \ | |
| "${PREV_SHA}..${CURRENT_SHA}" | head -30) | |
| fi | |
| else | |
| # Flat list for edge — most recent first, up to 20 | |
| BODY=$(git log --no-merges \ | |
| --pretty=format:"• <${REPO_URL}/commit/%H|%h> %s — _%an_" \ | |
| "${PREV_SHA}..${CURRENT_SHA}" | head -20) | |
| if [[ "$COMMIT_COUNT" -gt 20 ]]; then | |
| BODY="${BODY}"$'\n'"_...and $((COMMIT_COUNT - 20)) more commits — see compare link below_" | |
| fi | |
| fi | |
| if [[ -z "$BODY" ]]; then | |
| BODY="_No new commits_" | |
| fi | |
| # Turn (#1234) into clickable PR links (use @ delimiter since replacement contains |) | |
| BODY=$(printf '%s' "$BODY" | sed -E "s@\(#([0-9]+)\)@(<${REPO_URL}/pull/\1|#\1>)@g") | |
| DELIM="EOF_$(uuidgen)" | |
| { | |
| echo "body<<${DELIM}" | |
| echo "$BODY" | |
| echo "${DELIM}" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Build Slack message | |
| id: message | |
| if: >- | |
| steps.build_info.outputs.type != 'skip' | |
| && (steps.build_info.outputs.type != 'edge' | |
| || steps.edge_check.outputs.published == 'true') | |
| run: | | |
| TYPE="${{ steps.build_info.outputs.type }}" | |
| EMOJI="${{ steps.build_info.outputs.emoji }}" | |
| VERSION="${{ steps.build_info.outputs.version }}" | |
| RELEASE_REPO="${{ steps.build_info.outputs.release_repo }}" | |
| SHA="${{ github.event.workflow_run.head_sha }}" | |
| SHORT_SHA="${SHA:0:7}" | |
| CUT_AT="${{ github.event.workflow_run.updated_at }}" | |
| LAST_COMMIT_AT="${{ steps.changelog.outputs.last_commit_at }}" | |
| WORKFLOW_URL="${{ github.event.workflow_run.html_url }}" | |
| COMMIT_URL="https://github.com/${{ github.repository }}/commit/${SHA}" | |
| PREV_LABEL="${{ steps.prev.outputs.label }}" | |
| COMMIT_COUNT="${{ steps.changelog.outputs.commit_count }}" | |
| CONTRIBUTORS="${{ steps.changelog.outputs.contributors }}" | |
| BREAKING="${{ steps.changelog.outputs.breaking }}" | |
| COMPARE_URL="${{ steps.changelog.outputs.compare_url }}" | |
| CHANGELOG="${{ steps.changelog.outputs.body }}" | |
| PREV_SUFFIX="" | |
| if [[ -n "$PREV_LABEL" ]]; then | |
| PREV_SUFFIX=" (previous: \`$PREV_LABEL\`)" | |
| fi | |
| BREAKING_LINE="" | |
| if [[ "$BREAKING" == "true" ]]; then | |
| BREAKING_LINE=":warning: *This release contains breaking changes.* | |
| " | |
| fi | |
| COMPARE_LINE="" | |
| if [[ -n "$COMPARE_URL" ]]; then | |
| COMPARE_LINE="• <${COMPARE_URL}|Full diff (\`${PREV_LABEL}...${VERSION}\`)> | |
| " | |
| fi | |
| CONTRIB_LINE="" | |
| if [[ -n "$CONTRIBUTORS" ]]; then | |
| CONTRIB_LINE="*Contributors:* $CONTRIBUTORS | |
| " | |
| fi | |
| if [[ "$TYPE" == "stable" ]]; then | |
| TITLE="$EMOJI \`$VERSION\` is live${PREV_SUFFIX}" | |
| MESSAGE="${BREAKING_LINE}${CHANGELOG} | |
| ${CONTRIB_LINE} | |
| <${COMMIT_URL}|${SHORT_SHA}>${COMPARE_URL:+ · <${COMPARE_URL}|diff>} · <https://github.com/${RELEASE_REPO}/releases/tag/${VERSION}|release> · <${WORKFLOW_URL}|build> | |
| \`bash <(curl --proto '=https' --tlsv1.2 -LsSf https://get.oxy.tech)\` · \`ghcr.io/oxy-hq/oxy:${VERSION}\`" | |
| else | |
| TITLE="$EMOJI \`$VERSION\`${PREV_SUFFIX}" | |
| if [[ "$COMMIT_COUNT" -eq 0 ]]; then | |
| CHANGES_LINE="_No new commits_" | |
| else | |
| CHANGES_LINE="${CHANGELOG}" | |
| fi | |
| MESSAGE="${BREAKING_LINE}${CHANGES_LINE} | |
| ${CONTRIB_LINE} | |
| <${COMMIT_URL}|${SHORT_SHA}>${COMPARE_URL:+ · <${COMPARE_URL}|diff>} · <https://github.com/${RELEASE_REPO}/releases/tag/${VERSION}|release> · <${WORKFLOW_URL}|build> | |
| \`bash <(curl --proto '=https' --tlsv1.2 -LsSf https://nightly.oxy.tech)\` · \`ghcr.io/oxy-hq/oxy:${VERSION}\`" | |
| fi | |
| DELIM_T="EOF_$(uuidgen)" | |
| DELIM_B="EOF_$(uuidgen)" | |
| { | |
| echo "title<<${DELIM_T}" | |
| echo "$TITLE" | |
| echo "${DELIM_T}" | |
| echo "body<<${DELIM_B}" | |
| echo "$MESSAGE" | |
| echo "${DELIM_B}" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Send Slack notification | |
| if: >- | |
| steps.build_info.outputs.type != 'skip' | |
| && (steps.build_info.outputs.type == 'stable' | |
| || steps.edge_check.outputs.published == 'true') | |
| uses: rtCamp/action-slack-notify@v2 | |
| env: | |
| SLACK_USERNAME: "Release Notifier" | |
| MSG_MINIMAL: ref,commit | |
| SLACK_ICON_EMOJI: ${{ steps.build_info.outputs.emoji }} | |
| SLACK_LINK_NAMES: true | |
| SLACK_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} | |
| SLACK_CHANNEL: ${{ steps.build_info.outputs.channel }} | |
| SLACK_FOOTER: "" | |
| SLACK_TITLE: ${{ steps.message.outputs.title }} | |
| SLACK_MESSAGE: ${{ steps.message.outputs.body }} |