#!/bin/bash
# BOSH packaging script for the pve_cpi binary.
# Runs inside a BOSH compilation VM (Linux/amd64).  All compilation
# happens vendor-only (no network) and produces a linux/amd64 ELF binary.
set -euo pipefail

# ---------------------------------------------------------------------------
# 1. Locate Go toolchain
# ---------------------------------------------------------------------------
GOROOT=/var/vcap/packages/golang-1.27
if [ ! -d "${GOROOT}" ]; then
  # Relative path used by `bosh create-env` local compilation.
  GOROOT="$(dirname "${BOSH_INSTALL_TARGET}")/golang-1.27"
fi

# Packaged Go is a Linux ELF; on a macOS operator host running `bosh
# create-env` it cannot execute.  Fall back to the host `go` binary in
# that case, but WARN loudly when the host and target platforms differ.
if ! "${GOROOT}/bin/go" version >/dev/null 2>&1; then
  if command -v go >/dev/null 2>&1; then
    GOROOT="$(go env GOROOT)"
    HOST_GOOS="$(go env GOOS)"
    HOST_GOARCH="$(go env GOARCH)"
    if [ "${HOST_GOOS}" != "linux" ] || [ "${HOST_GOARCH}" != "amd64" ]; then
      echo "WARNING: packaged Go (linux/amd64 ELF) cannot execute on this host" >&2
      echo "WARNING: falling back to host go (${HOST_GOOS}/${HOST_GOARCH})" >&2
      echo "WARNING: cross-compilation is required — ensure the host toolchain" >&2
      echo "WARNING: supports GOOS=linux GOARCH=amd64 or this binary will not" >&2
      echo "WARNING: run on the target Linux VM." >&2
    fi
  else
    echo "ERROR: packaged Go cannot execute on this host and no host 'go' found in PATH" >&2
    exit 1
  fi
fi

export GOROOT
export PATH="${GOROOT}/bin:${PATH}"

# ---------------------------------------------------------------------------
# 2. Compilation environment
# ---------------------------------------------------------------------------
export GOCACHE="${BOSH_COMPILE_TARGET}/.gocache"
export GOMODCACHE="${BOSH_COMPILE_TARGET}/.gomodcache"

# Force vendor mode globally so no stray `go` sub-invocation hits the network.
export GOFLAGS="-mod=vendor"

# Pin the build to the packaged toolchain. Without this, a go.mod `go`
# directive newer than the blob makes `go build` try to DOWNLOAD the newer
# toolchain from the module proxy — a network fetch that hermetic compile VMs
# must never attempt (and which fails there anyway: no HOME, no proxy access).
# With `local`, the same mismatch fails immediately with a clear
# "go.mod requires go >= X" error, and the fix is to bump the golang blob.
export GOTOOLCHAIN=local

# Target platform selection.
#
# Inside a real BOSH compilation VM the host is always linux/amd64 and the
# packaged Go runs natively, so this script's job there is to produce a
# Linux ELF for the director or deployment VMs to execute.
#
# `bosh create-env` invokes this same packaging script locally on the operator
# host (macOS, linux, …) to build a CPI binary it will then `exec` ITSELF on
# that operator host to bring up the director VM. If we cross-compile to
# linux/amd64 unconditionally, create-env on a non-linux/amd64 operator (this
# Mac, an arm64 Linux laptop, etc.) gets a Linux ELF it cannot exec — bosh
# create-env then fails with "cannot execute binary file".
#
# Detect which path we are on by inspecting the active toolchain. If `go env`
# reports linux/amd64 the build target matches the deploy target — either a
# real compilation VM or a linux/amd64 operator host; pin GOOS/GOARCH for
# clarity. Otherwise we are on the host-fallback path on a non-linux/amd64
# operator; build host-native so create-env can run the binary, and warn that
# the resulting release tarball is operator-local (re-package on linux/amd64
# before uploading to a director or pushing to other deploy VMs).
if ! go version >/dev/null 2>&1; then
  echo "ERROR: go binary at ${GOROOT}/bin/go is not executable" >&2
  exit 1
fi
HOST_GO_GOOS="$(go env GOOS)"
HOST_GO_GOARCH="$(go env GOARCH)"

if [ "${HOST_GO_GOOS}" = "linux" ] && [ "${HOST_GO_GOARCH}" = "amd64" ]; then
  export GOOS=linux
  export GOARCH=amd64
else
  echo "==> Host-native build: go is ${HOST_GO_GOOS}/${HOST_GO_GOARCH};" >&2
  echo "==> producing a binary for THIS host so 'bosh create-env' can exec it." >&2
  echo "==> Re-package on linux/amd64 before publishing the release tarball" >&2
  echo "==> for use by a remote director or any deployed VM." >&2
  unset GOOS
  unset GOARCH
fi

# ---------------------------------------------------------------------------
# 3. Sanitize BOSH_PACKAGE_VERSION for use in ldflags.
#
# The raw value may contain characters that are not safe inside Go -ldflags
# (e.g. embedded spaces from dirty git tags). Strip everything except
# alphanumerics, dots, dashes, and plus signs.
#
# Notes:
#   - Avoid `printf '%s' ...`: BOSH's package-compilation pipeline runs the
#     packaging script through Go text/template before the agent executes
#     it, and the literal '%s' renders as '%!s(MISSING)' (no template arg
#     supplied), corrupting printf's format string.
#   - Avoid `tr -cd '[:alnum:].-+'`: tr interprets `.-+` as a character range
#     and aborts with "range-endpoints in reverse collating sequence order".
#     Putting `-` last (`'[:alnum:].+-'`) keeps `-` literal.
# ---------------------------------------------------------------------------
RAW_VERSION="${BOSH_PACKAGE_VERSION:-0.0.0+dev}"
BOSH_PACKAGE_VERSION_SANITIZED="$(echo -n "${RAW_VERSION}" | tr -cd '[:alnum:].+-')"
if [ -z "${BOSH_PACKAGE_VERSION_SANITIZED}" ]; then
  BOSH_PACKAGE_VERSION_SANITIZED="0.0.0+dev"
fi

# ---------------------------------------------------------------------------
# 4. Build
# ---------------------------------------------------------------------------
mkdir -p "${BOSH_INSTALL_TARGET}/bin"

echo "==> Building pve_cpi (${GOOS:-${HOST_GO_GOOS}}/${GOARCH:-${HOST_GO_GOARCH}}) version=${BOSH_PACKAGE_VERSION_SANITIZED}"
pushd "${BOSH_COMPILE_TARGET}/pve_cpi" >/dev/null

  go build \
    -trimpath \
    -ldflags "-s -w \
      -X github.com/fivetwenty-io/bosh-proxmox-cpi/internal/version.Version=${BOSH_PACKAGE_VERSION_SANITIZED} \
      -X github.com/fivetwenty-io/bosh-proxmox-cpi/internal/version.BuildDate=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
    -o "${BOSH_INSTALL_TARGET}/bin/cpi" \
    ./cmd/cpi

  # pve-cid: read-only operator CLI for decoding/locating CPI CIDs (see
  # src/pve_cpi/cmd/pve-cid). Ships alongside the cpi binary in this same
  # package — same module, same vendor tree, no extra spec dependency (the
  # package spec's "pve_cpi/cmd/**/*.go" glob already covers this
  # subdirectory).
  go build \
    -trimpath \
    -ldflags "-s -w -X main.cliVersion=${BOSH_PACKAGE_VERSION_SANITIZED}" \
    -o "${BOSH_INSTALL_TARGET}/bin/pve-cid" \
    ./cmd/pve-cid

popd >/dev/null

# Confirm each binary is a Linux ELF (sanity check on the host-fallback path).
if command -v file >/dev/null 2>&1; then
  for bin in cpi pve-cid; do
    BIN_TYPE="$(file -b "${BOSH_INSTALL_TARGET}/bin/${bin}")"
    case "${BIN_TYPE}" in
      ELF*) ;;
      *)
        echo "WARNING: produced binary ${bin} does not appear to be an ELF: ${BIN_TYPE}" >&2
        echo "WARNING: the binary may not execute on the Linux target VM." >&2
        ;;
    esac
  done
fi

echo "==> pve_cpi built at ${BOSH_INSTALL_TARGET}/bin/cpi"
echo "==> pve-cid built at ${BOSH_INSTALL_TARGET}/bin/pve-cid"
