Skip to content

Add post-merge PR comment with merge-specific run links. #1

Add post-merge PR comment with merge-specific run links.

Add post-merge PR comment with merge-specific run links. #1

name: Comment Post-Merge Run Links
on:
pull_request_target:
types:
- closed
permissions:
contents: read
pull-requests: write
jobs:
comment-run-links:
name: Comment links after merge
if: github.event.pull_request.merged == true && (github.event.pull_request.base.ref == 'main' || github.event.pull_request.base.ref == 'master')
runs-on: ${{ fromJSON(vars.GHA_RUNS_ON_JSON || '["self-hosted","linux"]') }}
steps:
- name: Resolve run links for this merge commit
id: resolve-links
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CIRCLECI_TOKEN: ${{ secrets.CIRCLECI_TOKEN }}
REPO_OWNER: ${{ github.repository_owner }}
REPO_NAME: ${{ github.event.repository.name }}
RAW_MERGE_SHA: ${{ github.event.pull_request.merge_commit_sha }}
RAW_BASE_REF: ${{ github.event.pull_request.base.ref }}
MAX_ATTEMPTS: "36"
SLEEP_SECONDS: "10"
WORKFLOW_NAME_REGEX: ${{ vars.WORKFLOW_NAME_REGEX || 'pulumi|deploy|build' }}
WORKFLOW_OWNER_REPO: ${{ vars.WORKFLOW_OWNER_REPO || github.repository }}
CIRCLECI_PROJECT_SLUG: ${{ vars.CIRCLECI_PROJECT_SLUG || '' }}
run: |
set -euo pipefail
echo "workflow_url=" >> "$GITHUB_OUTPUT"
echo "circleci_url=" >> "$GITHUB_OUTPUT"
echo "notes=" >> "$GITHUB_OUTPUT"
echo "merge_sha=n/a" >> "$GITHUB_OUTPUT"
echo "base_ref=n/a" >> "$GITHUB_OUTPUT"
# Validate event values before using them in API requests.
if [[ "${RAW_BASE_REF}" != "main" && "${RAW_BASE_REF}" != "master" ]]; then
echo "notes=Unsupported base branch; expected main/master." >> "$GITHUB_OUTPUT"
exit 0
fi
if [[ ! "${RAW_MERGE_SHA}" =~ ^[0-9a-f]{40}$ ]]; then
echo "notes=No valid merge commit SHA available on this PR event." >> "$GITHUB_OUTPUT"
exit 0
fi
BASE_REF="${RAW_BASE_REF}"
MERGE_SHA="${RAW_MERGE_SHA}"
echo "merge_sha=${MERGE_SHA}" >> "$GITHUB_OUTPUT"
echo "base_ref=${BASE_REF}" >> "$GITHUB_OUTPUT"
attempt=1
workflow_url=""
while [[ $attempt -le ${MAX_ATTEMPTS} ]]; do
runs_json="$(gh api "/repos/${WORKFLOW_OWNER_REPO}/actions/runs?event=push&head_sha=${MERGE_SHA}&per_page=50" 2>/dev/null || true)"
workflow_url="$(RUNS_JSON="${runs_json}" BASE_REF="${BASE_REF}" WORKFLOW_NAME_REGEX="${WORKFLOW_NAME_REGEX}" python3 - <<'PY_INNER'
import json

Check failure on line 61 in .github/workflows/comment-post-merge-run-links.yml

View workflow run for this annotation

GitHub Actions / .github/workflows/comment-post-merge-run-links.yml

Invalid workflow file

You have an error in your yaml syntax on line 61
import os
import re
raw = os.environ.get("RUNS_JSON") or "{}"
base_ref = os.environ.get("BASE_REF") or ""
name_re = os.environ.get("WORKFLOW_NAME_REGEX") or "pulumi|deploy|build"
try:
data = json.loads(raw)
except Exception:
print("")
raise SystemExit(0)
runs = data.get("workflow_runs") or []
if not runs:
print("")
raise SystemExit(0)
def branch_ok(run):
branch = (run.get("head_branch") or "")
return (not base_ref) or (branch == base_ref)
regex = re.compile(name_re, re.IGNORECASE)
filtered = [r for r in runs if branch_ok(r)]
preferred = [r for r in filtered if regex.search(r.get("name") or "")]
candidate = None
if preferred:
candidate = sorted(preferred, key=lambda x: x.get("run_number", 0), reverse=True)[0]
elif filtered:
candidate = sorted(filtered, key=lambda x: x.get("run_number", 0), reverse=True)[0]
print((candidate or {}).get("html_url", ""))
PY_INNER
)"
if [[ -n "${workflow_url}" ]]; then
break
fi
sleep "${SLEEP_SECONDS}"
attempt=$(( attempt + 1 ))
done
if [[ -n "${workflow_url}" ]]; then
echo "workflow_url=${workflow_url}" >> "$GITHUB_OUTPUT"
fi
circleci_slug="${CIRCLECI_PROJECT_SLUG}"
if [[ -z "${circleci_slug}" ]]; then
circleci_slug="${REPO_OWNER}/${REPO_NAME}"
fi
if [[ -z "${CIRCLECI_TOKEN:-}" ]]; then
echo "notes=CircleCI token not configured; skipping CircleCI run lookup." >> "$GITHUB_OUTPUT"
exit 0
fi
attempt=1
circleci_url=""
while [[ $attempt -le ${MAX_ATTEMPTS} ]]; do
pipelines_json="$(curl -fsSL -H "Circle-Token: ${CIRCLECI_TOKEN}" "https://circleci.com/api/v2/project/gh/${circleci_slug}/pipeline?branch=${BASE_REF}" 2>/dev/null || true)"
circleci_url="$(PIPELINES_JSON="${pipelines_json}" CIRCLECI_SLUG="${circleci_slug}" MERGE_SHA="${MERGE_SHA}" python3 - <<'PY_INNER'
import json
import os
raw = os.environ.get("PIPELINES_JSON") or "{}"
slug = os.environ.get("CIRCLECI_SLUG") or ""
merge_sha = os.environ.get("MERGE_SHA") or ""
try:
data = json.loads(raw)
except Exception:
print("")
raise SystemExit(0)
for item in data.get("items") or []:
vcs = item.get("vcs") or {}
if (vcs.get("revision") or "") == merge_sha:
number = item.get("number")
if number is not None:
print(f"https://app.circleci.com/pipelines/github/{slug}/{number}")
raise SystemExit(0)
print("")
PY_INNER
)"
if [[ -n "${circleci_url}" ]]; then
break
fi
sleep "${SLEEP_SECONDS}"
attempt=$(( attempt + 1 ))
done
if [[ -n "${circleci_url}" ]]; then
echo "circleci_url=${circleci_url}" >> "$GITHUB_OUTPUT"
fi
- name: Add run links to merged PR
uses: peter-evans/create-or-update-comment@v4
with:
issue-number: ${{ github.event.pull_request.number }}
body: |
Merged to `${{ steps.resolve-links.outputs.base_ref }}`.
Follow-on runs for merge commit `${{ steps.resolve-links.outputs.merge_sha }}`:
- CircleCI: ${{ steps.resolve-links.outputs.circleci_url || 'Not found within polling window.' }}
- GitHub Actions workflow: ${{ steps.resolve-links.outputs.workflow_url || 'Not found within polling window.' }}
${{ steps.resolve-links.outputs.notes }}