#!/bin/sh
# script/ci-mark-superseded: record an honest status on commits whose CI
# run Gitea cancelled because a newer commit landed on the same branch.
# Gitea writes `failure` / "Has been cancelled" for such a run, which
# reads as a test result on a commit nothing ever tested. Cancellation is
# unconditional server-side for push events, so the superseding run
# rewrites those statuses to `failure` with a description that says the
# commit was never tested. `skipped` cannot be used: Gitea's combined
# status folds `skipped` into `success`, so a never-tested commit would
# report green. Genuine failures and successes are never touched.
#
# Called by the Gitea Actions workflow, which supplies GITHUB_API_URL,
# GITHUB_REPOSITORY, GITHUB_SHA, GITHUB_WORKFLOW, GITHUB_JOB,
# GITHUB_EVENT_NAME and GITEA_TOKEN. ANCESTOR_LIMIT (default 20) caps how
# far back the walk looks.
set -eu

SUPERSEDED_DESC='Superseded by a newer commit; never tested'

# Gitea builds the commit-status context as
# "<workflow name> / <job name> (<event>)", the same three values the
# runner exports, so derive it rather than hardcoding the result.
context() {
    printf '%s / %s (%s)' \
        "$GITHUB_WORKFLOW" "$GITHUB_JOB" "$GITHUB_EVENT_NAME"
}

# The status Gitea created for this very job proves which context string
# it uses. If the derived one is missing, the workflow or the job was
# renamed and the match below would silently stop firing, restoring the
# false-red bug with no signal. Fail loudly instead.
require_own_context() {
    if ! _body="$(curl -sf --retry 3 --retry-delay 2 --max-time 30 \
        "${1}/commits/${GITHUB_SHA}/status")"; then
        echo "cannot read commit statuses for ${GITHUB_SHA}" >&2
        return 1
    fi
    _found="$(printf '%s' "$_body" | jq -r '(.statuses // [])[].context')"
    if printf '%s\n' "$_found" | grep -qxF "$2"; then
        return 0
    fi
    echo "no commit status with context '${2}' on ${GITHUB_SHA}:" >&2
    echo "workflow or job renamed? contexts present:" >&2
    printf '%s\n' "$_found" >&2
    return 1
}

# Latest status for our context on a commit, as "state|description".
status_of() {
    curl -sf "${1}/commits/${2}/status" | jq -r --arg c "$3" \
        '[(.statuses // [])[] | select(.context == $c)][0] // empty
         | "\(.status)|\(.description)"'
}

mark_superseded() {
    curl -sf -X POST "${1}/statuses/${2}" \
        -H "Authorization: token ${GITEA_TOKEN}" \
        -H 'Content-Type: application/json' \
        -d "$(jq -nc --arg c "$3" --arg d "$SUPERSEDED_DESC" \
            '{context: $c, state: "failure", description: $d}')" \
        >/dev/null
}

main() {
    _api="${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}"
    _ctx="$(context)"

    require_own_context "$_api" "$_ctx"

    _walk="$(git rev-list \
        --max-count="${ANCESTOR_LIMIT:-20}" "${GITHUB_SHA}^" || true)"

    for _sha in $_walk; do
        _latest="$(status_of "$_api" "$_sha" "$_ctx")"
        # A run that was cancelled, or one an earlier revision of this
        # script laundered into `skipped`. Anything else stands.
        case "$_latest" in
        'failure|Has been cancelled' | "skipped|${SUPERSEDED_DESC}") ;;
        *) continue ;;
        esac
        mark_superseded "$_api" "$_sha" "$_ctx"
        echo "marked superseded: ${_sha}"
    done
}

main "$@"
