Skip to content

Public Announce Releases to Slack #445

Public Announce Releases to Slack

Public Announce Releases to Slack #445

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 }}