Files
SharedWorkflows/.gitea/workflows/notifications.yml
T
dresberandClaude Sonnet 5 602ff49f7b fix(notifications): use the run's actual API event, not gitea.event_name
notifications.yml is invoked via workflow_call from the caller
workflow, so inside this job gitea.event_name reports "workflow_call"
rather than the top-level event (push/schedule/...) that actually
started the run. Comparing that against the Actions API's per-run
event field (which reports the real trigger) never matched, so
previous_conclusion always stayed "unknown" and healed notifications
never fired. Now the current run's real event is captured from the API
response itself and used for the comparison, falling back to
gitea.event_name only if the current run isn't found in the scanned
history.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-08 21:08:25 +00:00

186 lines
7.1 KiB
YAML

name: Notification
on:
workflow_call:
inputs:
job_status:
required: true
type: string
job_name:
required: true
type: string
notify_on_release:
required: false
type: boolean
default: true
secrets:
NTFY_TOPIC: { required: true }
NTFY_TOKEN: { required: true }
NTFY_SERVER: { required: true }
API_GITEA_TOKEN: { required: true }
jobs:
notify:
runs-on: docker
steps:
- name: Detect previous workflow state
id: previous
shell: bash
env:
API_GITEA_TOKEN: ${{ secrets.API_GITEA_TOKEN }}
run: |
set -euo pipefail
python3 <<'PY'
import json
import os
import urllib.request
server = "${{ gitea.server_url }}"
repo = "${{ gitea.repository }}"
current_run_number = int("${{ gitea.run_number }}")
current_branch = "${{ gitea.ref_name }}"
# gitea.event_name is unreliable here: notifications.yml is itself called via
# workflow_call from the caller workflow, so inside this job it reports
# "workflow_call" rather than the top-level event (push/schedule/...) that
# actually started the run. The Actions API reports the real triggering event
# per run, so use that for the current run instead of trusting this context
# value. It is kept only as a last-resort fallback if the current run can't be
# found in the scanned history at all.
current_event = "${{ gitea.event_name }}"
token = os.environ["API_GITEA_TOKEN"]
print(f"Current run number: {current_run_number}")
print(f"Current branch: {current_branch}")
print(f"Current event (context, may be inaccurate): {current_event}")
previous = "unknown"
# Scheduled runs can push the prior run for this branch/event beyond the
# first page. Scan a bounded history so a succeeding push after a failure
# is still reported as healed.
for page in range(1, 11):
url = f"{server}/api/v1/repos/{repo}/actions/runs?page={page}&limit=100"
print(f"Fetching workflow runs page {page}: {url}")
req = urllib.request.Request(
url,
headers={
"Authorization": f"token {token}",
"Accept": "application/json",
},
)
with urllib.request.urlopen(req) as response:
data = json.loads(response.read().decode("utf-8"))
runs = data.get("workflow_runs", [])
if not isinstance(runs, list):
print("Received an invalid workflow-runs payload; stopping lookup.")
break
print(f"Received {len(runs)} workflow runs on page {page}")
if not runs:
break
for run in runs:
run_number = run.get("run_number")
status = run.get("status")
conclusion = run.get("conclusion")
branch = run.get("head_branch")
event = run.get("event")
print(
f"Inspecting run #{run_number}: "
f"status={status}, "
f"conclusion={conclusion}, "
f"branch={branch}, "
f"event={event}"
)
# aktuellen Run überspringen, aber dessen echtes Trigger-Event merken
if str(run_number) == str(current_run_number):
current_event = event or current_event
print(f" -> skipping current run (actual event={event})")
continue
# nur abgeschlossene Runs
if status != "completed":
print(" -> skipping non-completed run")
continue
# nur gleicher Branch
if branch != current_branch:
print(" -> skipping different branch")
continue
# nur gleiches Event
if event != current_event:
print(" -> skipping different event")
continue
previous = conclusion or "unknown"
print(f" -> selected previous conclusion: {previous}")
break
if previous != "unknown" or len(runs) < 100:
break
print(f"Previous conclusion final: {previous}")
with open(os.environ["GITEA_OUTPUT"], "a", encoding="utf-8") as f:
f.write(f"previous_conclusion={previous}\n")
PY
- name: Send Notification
shell: bash
run: |
set -euo pipefail
CURRENT_STATUS="${{ inputs.job_status }}"
PREVIOUS_STATUS="${{ steps.previous.outputs.previous_conclusion }}"
IS_RELEASE="false"
if [[ "${{ gitea.ref }}" == refs/tags/v* ]]; then
IS_RELEASE="true"
fi
echo "Current status: ${CURRENT_STATUS}"
echo "Previous status: ${PREVIOUS_STATUS}"
echo "Is release: ${IS_RELEASE}"
if [[ "$IS_RELEASE" == "true" && "$CURRENT_STATUS" == "success" ]]; then
ICON="🚀"
TITLE="New release available: ${{ gitea.ref_name }} for ${{ gitea.repository }}"
elif [[ "$CURRENT_STATUS" == "failure" ]]; then
ICON="❌"
TITLE="Failed: ${{ inputs.job_name }} for ${{ gitea.repository }}"
elif [[ "$CURRENT_STATUS" == "success" && "$PREVIOUS_STATUS" == "failure" ]]; then
ICON="✅"
TITLE="Healed: ${{ inputs.job_name }} for ${{ gitea.repository }}"
else
echo "No notification needed."
exit 0
fi
COMMIT_SUBJECT="$(curl -fsS \
-H "Authorization: Bearer ${{ secrets.API_GITEA_TOKEN }}" \
"${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}/git/commits/${{ gitea.sha }}" \
| python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('RepoCommit',{}).get('message','').split('\n')[0])" \
2>/dev/null || echo 'Commit info unavailable')"
RUN_URL="${{ gitea.server_url }}/${{ gitea.repository }}/actions/runs/${{ gitea.run_number }}"
cat <<EOF >/tmp/ntfy-payload.json
{
"topic": "${{ secrets.NTFY_TOPIC }}",
"title": "${ICON} ${TITLE}",
"message": "Ref: ${{ gitea.ref_name }}\nStatus: ${CURRENT_STATUS}\nPrevious: ${PREVIOUS_STATUS}\nCommit: ${COMMIT_SUBJECT}\n\nRun URL: ${RUN_URL}",
"click": "${RUN_URL}",
"actions": [
{ "action": "view", "label": "Open Run", "url": "${RUN_URL}" }
]
}
EOF
curl -fsS \
-H "Authorization: Bearer ${{ secrets.NTFY_TOKEN }}" \
-H "Content-Type: application/json" \
-d @/tmp/ntfy-payload.json \
"${{ secrets.NTFY_SERVER }}"