# Uses the estate's tier-2 container-build runner, a dedicated repository-read
# FORGEJO_SOURCE_TOKEN, and package-write REGISTRY_USER / REGISTRY_TOKEN.
name: Build and publish policy-nexus image

on:
  push:
    branches:
      - main
    paths:
      - ".forgejo/workflows/publish-image.yaml"
      - "Containerfile"
      - "deploy/**"
      - "publication.json"
      - "source-inventory.config.json"
      - "source-inventory.json"
      - "tests/**"
      - "tools/**"
  schedule:
    # Pull-based freshness audit. A failed source checkout, inventory drift, or
    # overdue document is visible as a failed scheduled Forgejo Actions run.
    - cron: "17 04 * * *"
  workflow_dispatch:

concurrency:
  group: policy-nexus-publication
  cancel-in-progress: false

permissions:
  contents: read

env:
  REGISTRY: forgejo.coulomb.social
  FORGEJO_ORIGIN: https://forgejo.coulomb.social
  IMAGE_NAME: coulomb/policy-nexus
  DOCKER_HOST: tcp://127.0.0.1:2375

jobs:
  build-and-push:
    runs-on: container-build
    steps:
      - name: Build, verify, and publish immutable policy artifact
        env:
          FORGEJO_SOURCE_TOKEN: ${{ secrets.FORGEJO_SOURCE_TOKEN }}
          REGISTRY_USER: ${{ secrets.REGISTRY_USER }}
          REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
        run: |
          set -eu
          REF="${GITHUB_SHA:-}"
          test "${#REF}" -eq 40
          if [ -z "${FORGEJO_SOURCE_TOKEN:-}" ]; then
            echo "FORGEJO_SOURCE_TOKEN is required for private source repositories" >&2
            exit 2
          fi
          SHORT="${REF:0:7}"
          BUILD_ROOT="$(mktemp -d)"
          PYTHON_CONTAINER=""
          cleanup() {
            if [ -n "${PYTHON_CONTAINER}" ] && [ -x "${HOME}/bin/docker" ]; then
              "${HOME}/bin/docker" rm -f "${PYTHON_CONTAINER}" >/dev/null 2>&1 || true
            fi
            rm -rf "${BUILD_ROOT}"
          }
          trap cleanup EXIT
          BUILD_CONTEXT="${BUILD_ROOT}/buildctx"
          mkdir -p "${BUILD_CONTEXT}" "${HOME}/bin"

          wget -qO "${BUILD_ROOT}/policy-nexus.tar.gz" \
            --header="Authorization: token ${FORGEJO_SOURCE_TOKEN}" \
            "${FORGEJO_ORIGIN}/${GITHUB_REPOSITORY}/archive/${REF}.tar.gz"
          tar xzf "${BUILD_ROOT}/policy-nexus.tar.gz" \
            -C "${BUILD_CONTEXT}" --strip-components=1

          wget -qO- https://download.docker.com/linux/static/stable/x86_64/docker-27.3.1.tgz \
            | tar xz --strip-components=1 -C "${HOME}/bin" docker/docker
          export PATH="${HOME}/bin:${PATH}"
          docker version
          PYTHON_IMAGE="docker.io/library/python@sha256:d09d15e60962ca365d1cd544a48773bac9d33f2fb1b00f2aa0deec78ade7dc31"
          PYTHON_CONTAINER="policy-source-fetch-${SHORT}-${GITHUB_RUN_ID:-0}"
          docker create --name "${PYTHON_CONTAINER}" "${PYTHON_IMAGE}" sleep 300
          docker cp "${BUILD_CONTEXT}/." "${PYTHON_CONTAINER}:/workspace"
          docker start "${PYTHON_CONTAINER}"
          docker exec --workdir /workspace \
            --env FORGEJO_SOURCE_TOKEN "${PYTHON_CONTAINER}" \
            python3 tools/fetch_sources.py \
            --config source-inventory.config.json \
            --destination _sources \
            --policy-revision "${REF}" \
            --token-env FORGEJO_SOURCE_TOKEN \
            --token-origin "${FORGEJO_ORIGIN}"
          SOURCE_SET_DIGEST="$(docker exec "${PYTHON_CONTAINER}" python3 -c 'import json; print(json.load(open("/workspace/_sources/source-lock.json"))["source_set_digest"])')"
          docker cp "${PYTHON_CONTAINER}:/workspace/_sources" "${BUILD_CONTEXT}/"
          docker rm -f "${PYTHON_CONTAINER}"
          PYTHON_CONTAINER=""
          SOURCE_TAG="source-${SOURCE_SET_DIGEST}"
          REVISION_TAG="git-${REF}-sources-${SOURCE_SET_DIGEST:0:16}"

          echo "${REGISTRY_TOKEN}" | docker login "${REGISTRY}" \
            -u "${REGISTRY_USER}" --password-stdin

          IMAGE="${REGISTRY}/${IMAGE_NAME}"
          docker build \
            --file "${BUILD_CONTEXT}/Containerfile" \
            --build-arg "VCS_REVISION=${REF}" \
            --build-arg "SOURCE_SET_DIGEST=${SOURCE_SET_DIGEST}" \
            --tag "${IMAGE}:${SOURCE_TAG}" \
            --tag "${IMAGE}:${REVISION_TAG}" \
            --tag "${IMAGE}:main" \
            "${BUILD_CONTEXT}"
          PUSH_OUTPUT="$(docker push "${IMAGE}:${SOURCE_TAG}")"
          printf '%s\n' "${PUSH_OUTPUT}"
          IMAGE_DIGEST="$(printf '%s\n' "${PUSH_OUTPUT}" | tr '\r' '\n' | awk '{for (i=1; i<=NF; i++) if ($i ~ /^sha256:[a-f0-9]{64}$/) print $i}' | tail -1)"
          printf '%s' "${IMAGE_DIGEST}" | grep -Eq '^sha256:[a-f0-9]{64}$'
          docker push "${IMAGE}:${REVISION_TAG}"
          docker push "${IMAGE}:main"

          PUBLICATION_DIGEST=$(docker run --rm --entrypoint sha256sum \
            "${IMAGE}:${SOURCE_TAG}" /usr/share/nginx/html/publication-manifest.json \
            | awk '{print $1}')
          SOURCE_INVENTORY_DIGEST=$(docker run --rm --entrypoint sha256sum \
            "${IMAGE}:${SOURCE_TAG}" /usr/share/nginx/html/source-inventory.json \
            | awk '{print $1}')
          echo "published=${IMAGE}:${SOURCE_TAG}"
          echo "image_digest=${IMAGE_DIGEST}"
          echo "publication_manifest_digest=${PUBLICATION_DIGEST}"
          echo "source_inventory_digest=${SOURCE_INVENTORY_DIGEST}"
          echo "source_set_digest=${SOURCE_SET_DIGEST}"
          if [ -n "${GITHUB_STEP_SUMMARY:-}" ]; then
            {
              echo "## policy-nexus release candidate"
              echo
              echo "The scheduled pull only publishes an immutable candidate; it does not deploy."
              echo
              echo "- image: \`${IMAGE}@${IMAGE_DIGEST}\`"
              echo "- publication manifest: \`${PUBLICATION_DIGEST}\`"
              echo "- source inventory: \`${SOURCE_INVENTORY_DIGEST}\`"
              echo "- source set: \`${SOURCE_SET_DIGEST}\`"
              echo
              echo "Promotion requires an explicit paired digest update in rapp-policy-nexus and railiance-apps."
            } >> "${GITHUB_STEP_SUMMARY}"
          fi
