#!/bin/sh
# script/bootstrap: install all dependencies needed to build and develop
# this repo. Idempotent: every install is guarded by a check so already
# installed tools are skipped. Base tooling comes from nix, apt, brew,
# or apk (detected in that order); assumes nothing is present.
# golangci-lint is installed via `go install` pinned to the same version
# the Dockerfile lint stage uses (never "latest"), and is reinstalled
# whenever the installed version differs from that pin.
set -eu

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

# Pinned versions, 2026-08-07 (same version as the Dockerfile lint stage).
# This is the single source of truth for the linter version: the module
# ref below and the version comparison in main() are both derived from
# it, so a bump here cannot half-apply. Written without a leading "v",
# the way `golangci-lint --version` reports it.
GOLANGCI_LINT_VERSION="2.12.2"
GOLANGCI_LINT_MODULE="github.com/golangci/golangci-lint/v2/cmd/golangci-lint"
GOLANGCI_LINT_REF="$GOLANGCI_LINT_MODULE@v$GOLANGCI_LINT_VERSION"

PKGMGR=""
SUDO=""
APT_UPDATED=""

detect_pkgmgr() {
    [ -n "$PKGMGR" ] && return 0
    if command -v nix-env >/dev/null 2>&1; then
        PKGMGR="nix"
    elif command -v apt-get >/dev/null 2>&1; then
        PKGMGR="apt"
    elif command -v brew >/dev/null 2>&1; then
        PKGMGR="brew"
    elif command -v apk >/dev/null 2>&1; then
        PKGMGR="apk"
    else
        echo "bootstrap: no supported package manager (nix, apt, brew, apk)" >&2
        exit 1
    fi
    if [ "$PKGMGR" = "apt" ]; then
        export DEBIAN_FRONTEND=noninteractive
        if [ "$(id -u)" != "0" ]; then
            SUDO="sudo"
        fi
    fi
}

# pkg_install <nix-attr> <apt-pkg> <brew-formula> <apk-pkg>
pkg_install() {
    detect_pkgmgr
    case "$PKGMGR" in
        nix) nix-env -iA "nixpkgs.$1" ;;
        apt)
            if [ -z "$APT_UPDATED" ]; then
                $SUDO env DEBIAN_FRONTEND=noninteractive apt-get update
                APT_UPDATED=1
            fi
            $SUDO env DEBIAN_FRONTEND=noninteractive apt-get install -y "$2"
            ;;
        brew) brew install "$3" ;;
        apk) apk add --no-cache "$4" ;;
    esac
}

missing() {
    ! command -v "$1" >/dev/null 2>&1
}

# Echo the installed golangci-lint version, or nothing when the tool is
# absent. The binary reports e.g.
#   golangci-lint has version 2.12.2 built with go1.26.5 from abc1234 ...
# so the version is the field after the literal word "version", and it
# carries no leading "v" (the module ref does). Some builds do print a
# leading "v", so strip one if present and compare bare versions.
golangci_lint_version() {
    command -v golangci-lint >/dev/null 2>&1 || return 0
    golangci-lint --version 2>/dev/null | awk '
        {
            for (i = 1; i < NF; i++) {
                if ($i == "version") {
                    v = $(i + 1)
                    sub(/^v/, "", v)
                    print v
                    exit
                }
            }
        }
    '
}

main() {
    cd "$ROOT"

    # System tooling, deliberately unpinned: these come from the host
    # package manager and whatever version it ships is what the host
    # gets, so a presence check is the right check. The repo pins no
    # system toolchain versions — the Go language version is governed by
    # go.mod, and builds that must be reproducible run in the Docker
    # image, whose base images are pinned by digest.
    if missing git; then pkg_install git git git git; fi
    if missing make; then pkg_install gnumake make make make; fi
    if missing go; then pkg_install go golang go go; fi

    # Lint tooling, pinned via go install (installs into
    # "$(go env GOPATH)/bin"; ensure that is on your PATH). Unlike the
    # system tools above this is version-checked, not presence-checked:
    # the Dockerfile lint stage runs a digest-pinned linter, so a host
    # running any other version lints against different rules and
    # `make check` can go green on a commit CI then rejects. Any version
    # that is not the pin — older or newer — is reinstalled.
    installed="$(golangci_lint_version)"
    if [ "$installed" != "$GOLANGCI_LINT_VERSION" ]; then
        echo "bootstrap: golangci-lint ${installed:-absent or unparseable}," \
            "want $GOLANGCI_LINT_VERSION; installing"
        go install "$GOLANGCI_LINT_REF"
    fi

    go mod download

    echo "bootstrap complete"
}

main "$@"
