pipeline {
  parameters {
    string(
      name: 'HARBOR_VERSION',
      defaultValue: '',
      description: 'Harbor tag to build (e.g. v2.14.1). Leave empty to build the latest release.'
    )
  }
  agent {
    kubernetes {
      label 'harbor-arm-build'
      defaultContainer 'builder'
      yaml """
apiVersion: v1
kind: Pod
spec:
  nodeSelector:
    kubernetes.io/arch: arm64
  containers:
  - name: builder
    image: quay.io/podman/stable:v5.2
    tty: true
    command: ["cat"]
    securityContext:
      privileged: true
    env:
    - name: DOCKER_CONFIG
      value: /root/.docker
    - name: REGISTRY_AUTH_FILE
      value: /root/.docker/config.json
    - name: XDG_RUNTIME_DIR
      value: /var/run/containers
    volumeMounts:
    - name: workspace-volume
      mountPath: /home/jenkins/agent
    - name: docker-config-secret
      mountPath: /docker-config
    - name: containers-storage
      mountPath: /var/lib/containers
    - name: podman-runtime
      mountPath: /var/run/containers
  volumes:
  - name: docker-config-secret
    secret:
      secretName: harbor-robot-pipeline
      items:
      - key: .dockerconfigjson
        path: config.json
  - name: workspace-volume
    emptyDir: {}
  - name: containers-storage
    emptyDir: {}
  - name: podman-runtime
    emptyDir: {}
"""
    }
  }
  environment {
    IMAGE_NAMESPACE = 'registry.bstein.dev/infra'
    TAG_SUFFIX      = '-arm64'
    REGISTRY_URL    = 'registry.bstein.dev'
    SUITE_NAME      = 'harbor-arm-build'
    PUSHGATEWAY_URL = 'http://platform-quality-gateway.monitoring.svc.cluster.local:9091'
  }
  options {
    disableConcurrentBuilds()
  }
  stages {
    stage('Checkout pipeline repo') {
      steps {
        git credentialsId: 'gitea-pat', url: 'https://scm.bstein.dev/bstein/harbor-arm-build.git'
      }
    }

    stage('Setup tooling') {
      steps {
        container('builder') {
          sh '''
            set -euo pipefail
            microdnf -y install git make curl tar gzip jq golang podman-docker python3
            mkdir -p "${DOCKER_CONFIG}"
            cp /docker-config/config.json "${DOCKER_CONFIG}/config.json"
            # Make the Docker CLI invoke podman (buildah backend).
            ln -sf /usr/bin/podman /usr/local/bin/docker
            # Harbor's build scripts parse `docker --version`; emulate Docker output so
            # environment checks pass while still delegating real commands to podman.
            cat > /usr/local/bin/docker <<'EOF'
#!/bin/sh
if [ "${1:-}" = "--version" ] || [ "${1:-}" = "version" ]; then
  echo "Docker version 24.0.0, build podman-shim"
  exit 0
fi
exec /usr/bin/podman "$@"
EOF
            chmod +x /usr/local/bin/docker
            mkdir -p "${XDG_RUNTIME_DIR}"
          '''
        }
      }
    }

    stage('Resolve version') {
      steps {
        container('builder') {
          script {
            def resolved = sh(
              label: 'pick version',
              returnStdout: true,
              script: '''
                set -euo pipefail
                if [ -n "${HARBOR_VERSION:-}" ]; then
                  echo "${HARBOR_VERSION}"
                  exit 0
                fi
                for attempt in 1 2 3; do
                  version=$(curl -fsSL \
                    -H 'Accept: application/vnd.github+json' \
                    -H 'User-Agent: harbor-arm-build' \
                    https://api.github.com/repos/goharbor/harbor/releases/latest \
                    | jq -r '.tag_name // empty') || version=""
                  if [ -n "${version}" ]; then
                    echo "${version}"
                    exit 0
                  fi
                  sleep $((attempt * 5))
                done
                echo "Failed to resolve Harbor version from GitHub; set HARBOR_VERSION manually." >&2
                exit 1
              '''
            ).trim()
            env.HARBOR_VERSION_RESOLVED = resolved
            env.HARBOR_SRC_DIR = "harbor-src/harbor-${resolved.startsWith('v') ? resolved.substring(1) : resolved}"
          }
        }
      }
    }

    stage('Fetch source') {
      steps {
        container('builder') {
          sh """
            set -euo pipefail
            rm -rf harbor-src
            mkdir -p harbor-src
            curl -sSL "https://github.com/goharbor/harbor/archive/refs/tags/${env.HARBOR_VERSION_RESOLVED}.tar.gz" | tar xz -C harbor-src
            ls -la harbor-src
          """
        }
      }
    }

    stage('Build & push (podman/buildah)') {
      steps {
        container('builder') {
          sh '''
            set -euo pipefail
            VERSION_TAG="${HARBOR_VERSION_RESOLVED}${TAG_SUFFIX}.${BUILD_NUMBER}"
            export VERSIONTAG="${VERSION_TAG}"
            export BASEIMAGETAG="${VERSION_TAG}"
            export IMAGENAMESPACE="${IMAGE_NAMESPACE}"
            export BASEIMAGENAMESPACE="${IMAGE_NAMESPACE}"
            export DOCKERNETWORK=host
            export DOCKER_DEFAULT_PLATFORM=linux/arm64
            export PULL_BASE_FROM_DOCKERHUB=false
            export BUILD_BASE=true
            export BUILDTRIVYADP=false
            export BUILD_INSTALLER=true
            export BUILDAH_ISOLATION=chroot
            export REGISTRY_AUTH_FILE="${DOCKER_CONFIG}/config.json"
            git config --global --add safe.directory '*'
            cd "${HARBOR_SRC_DIR}"

            make compile
            make \
              VERSIONTAG="${VERSION_TAG}" \
              BASEIMAGETAG="${VERSION_TAG}" \
              IMAGENAMESPACE="${IMAGE_NAMESPACE}" \
              BASEIMAGENAMESPACE="${IMAGE_NAMESPACE}" \
              DOCKERNETWORK=host \
              PULL_BASE_FROM_DOCKERHUB=false \
              BUILD_BASE=true \
              BUILDTRIVYADP=false \
              BUILD_INSTALLER=true \
              build

            for pair in \
              prepare:harbor-prepare \
              redis-photon:harbor-redis \
              nginx-photon:harbor-nginx \
              registry-photon:harbor-registry; do
              pair_src="${pair%%:*}"
              pair_dst="${pair##*:}"
              src="${IMAGE_NAMESPACE}/${pair_src}:${VERSION_TAG}"
              dst="${IMAGE_NAMESPACE}/${pair_dst}:${VERSION_TAG}"
              if podman image exists "${src}"; then
                podman tag "${src}" "${dst}" || true
              fi
            done

            podman images --format '{{.Repository}}:{{.Tag}}' \
              | awk -v ns="${IMAGE_NAMESPACE}/" -v tag="${VERSION_TAG}" 'index($0, ns)==1 && $0 ~ ":"tag"$"' \
              | sort -u \
              | while read -r img; do
                  echo "Pushing ${img}"
                  podman push "${img}"
                done
          '''
        }
      }
    }
  }
  post {
    success {
      container('builder') {
        sh '''
          set -euo pipefail
          suite="${SUITE_NAME}"
          gateway="${PUSHGATEWAY_URL}"
          fetch_counter() {
            status="$1"
            line="$(curl -fsS "${gateway}/metrics" 2>/dev/null | awk -v suite="${suite}" -v status="${status}" '
              /^platform_quality_gate_runs_total\{/ {
                if (index($0, "job=\\"platform-quality-ci\\"") && index($0, "suite=\\"" suite "\\"") && index($0, "status=\\"" status "\\"")) {
                  print $2
                  exit
                }
              }
            ' || true)"
            [ -n "${line}" ] && printf '%s\n' "${line}" || printf '0\n'
          }
          ok_count="$(fetch_counter ok)"
          failed_count="$(fetch_counter failed)"
          ok_count=$((ok_count + 1))
          cat <<METRICS | curl -fsS --data-binary @- "${gateway}/metrics/job/platform-quality-ci/suite/${suite}" >/dev/null
# TYPE platform_quality_gate_runs_total counter
platform_quality_gate_runs_total{suite="${suite}",status="ok"} ${ok_count}
platform_quality_gate_runs_total{suite="${suite}",status="failed"} ${failed_count}
METRICS
        '''
      }
    }
    failure {
      container('builder') {
        sh '''
          set -euo pipefail
          suite="${SUITE_NAME}"
          gateway="${PUSHGATEWAY_URL}"
          fetch_counter() {
            status="$1"
            line="$(curl -fsS "${gateway}/metrics" 2>/dev/null | awk -v suite="${suite}" -v status="${status}" '
              /^platform_quality_gate_runs_total\{/ {
                if (index($0, "job=\\"platform-quality-ci\\"") && index($0, "suite=\\"" suite "\\"") && index($0, "status=\\"" status "\\"")) {
                  print $2
                  exit
                }
              }
            ' || true)"
            [ -n "${line}" ] && printf '%s\n' "${line}" || printf '0\n'
          }
          ok_count="$(fetch_counter ok)"
          failed_count="$(fetch_counter failed)"
          failed_count=$((failed_count + 1))
          cat <<METRICS | curl -fsS --data-binary @- "${gateway}/metrics/job/platform-quality-ci/suite/${suite}" >/dev/null
# TYPE platform_quality_gate_runs_total counter
platform_quality_gate_runs_total{suite="${suite}",status="ok"} ${ok_count}
platform_quality_gate_runs_total{suite="${suite}",status="failed"} ${failed_count}
METRICS
        '''
      }
    }
    always {
      echo "done"
    }
  }
}
