#!/bin/sh
# script/verify-linter-pin: fail unless a golangci-lint binary is exactly
# the version script/bootstrap pins. Takes the binary to check as its
# argument, defaulting to whatever PATH resolves. Our own extension to
# scripts-to-rule-them-all, not one of its entrypoints.
#
# The Dockerfile build stage runs this on the linter it copies out of the
# lint stage, before anything else runs there. Without it, drift between
# the two stages is silently absorbed: script/bootstrap reinstalls its
# pinned version from source, verifies that, and the build goes green
# with the lint stage having linted at one version and `make check`
# having run at another. Bumping the lint stage image alone is enough to
# produce that, and this is the check that turns it into a build failure
# naming both versions.
#
# The pin is read out of script/bootstrap rather than restated here.
# script/bootstrap is the single source of truth for the linter version,
# and a second hardcoded copy of it is exactly the drift this script
# exists to catch. A pin that cannot be read is therefore a hard failure
# and not a skip: silently comparing against an empty string would turn
# this check into the kind of unearned green it was written to stop.
set -eu

ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"

# Seconds to allow `golangci-lint --version` to run, so a wedged binary
# stops the build instead of hanging it. Bounded by timeout(1) where that
# exists; stock macOS has none, and there the call runs unbounded.
VERSION_TIMEOUT="30"

version_output() {
    if command -v timeout >/dev/null 2>&1; then
        timeout "$VERSION_TIMEOUT" "$1" --version
    else
        "$1" --version
    fi
}

main() {
    # Resolve the argument before changing directory, so a relative path
    # means what the caller meant by it.
    bin="${1:-golangci-lint}"
    resolved="$(command -v "$bin" 2>/dev/null || true)"

    cd "$ROOT"

    pin="$(
        sed -n 's/^GOLANGCI_LINT_VERSION="\([^"]*\)".*/\1/p' script/bootstrap
    )"
    if [ -z "$pin" ]; then
        echo "verify-linter-pin: no GOLANGCI_LINT_VERSION assignment found" \
            "in script/bootstrap; that file is the single source of truth" \
            "for the linter version and this check cannot run without it" >&2
        exit 1
    fi

    if [ -z "$resolved" ]; then
        echo "verify-linter-pin: $bin: not found (pin is $pin)" >&2
        exit 1
    fi

    # Same output shape script/bootstrap parses:
    #   golangci-lint has version X.Y.Z built with go1.26.5 from abc1234
    # so the version is the field after the literal word "version", with
    # any leading "v" stripped. stderr is left connected so a binary that
    # cannot execute (wrong architecture, missing shared library) says why
    # rather than being reported as merely unparseable.
    if ! out="$(version_output "$resolved")"; then
        echo "verify-linter-pin: $resolved --version failed; the binary" \
            "cannot be executed or timed out (pin is $pin)" >&2
        exit 1
    fi
    found="$(
        echo "$out" | awk '
            {
                for (i = 1; i < NF; i++) {
                    if ($i == "version") {
                        v = $(i + 1)
                        sub(/^v/, "", v)
                        print v
                        exit
                    }
                }
            }
        '
    )"

    if [ "$found" != "$pin" ]; then
        echo "verify-linter-pin: $resolved reports" \
            "${found:-no parseable version}, but script/bootstrap pins" \
            "$pin" >&2
        echo "verify-linter-pin: these must be the same version — bump the" \
            "Dockerfile lint stage image and GOLANGCI_LINT_VERSION in" \
            "script/bootstrap together" >&2
        exit 1
    fi

    echo "verify-linter-pin: $resolved is $found, matching the" \
        "script/bootstrap pin"
}

main "$@"
