4.9 KiB · text History 4b6fe3b
# Builds both deployment variants from a single source tree:
#
#   * standalone — site_encrypt provisions and renews ACME certs in-process.
#                  Suitable for VPS / bare-metal where the IRC node has direct
#                  access to ports 80/443.
#   * kubernetes — site_encrypt is compiled out; certs come from a mounted
#                  Secret (typically managed by cert-manager). Suitable for
#                  k8s deployments behind an ingress.
#
# Each variant produces its own image tag so deployments can pin to one
# explicitly.

.build:
  retry: 1
  stage: build
  image:
    name: gcr.io/kaniko-project/executor:v1.23.2-debug
    entrypoint: [""]
  only:
    - master
  # Not on the nightly audit's schedule: that pipeline runs on master too,
  # and would otherwise rebuild and republish both images every night.
  except:
    - schedules
  script:
    - |
      echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
    - |
      /kaniko/executor \
        --force \
        --context $CI_PROJECT_DIR \
        --dockerfile $CI_PROJECT_DIR/Dockerfile \
        --build-arg TLS_MODE=$TLS_MODE \
        --destination "$CI_REGISTRY_IMAGE:${TLS_MODE}-${CI_COMMIT_SHORT_SHA}" \
        --destination "$CI_REGISTRY_IMAGE:${TLS_MODE}-master" \
        --registry-mirror us0.regi.kube.gmp.io

build-standalone:
  extends: .build
  variables:
    TLS_MODE: standalone
  # Also publish standalone as `:latest` and `:master` since most VPS-style
  # deploys want the in-process ACME path.
  script:
    - |
      echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
    - |
      /kaniko/executor \
        --force \
        --context $CI_PROJECT_DIR \
        --dockerfile $CI_PROJECT_DIR/Dockerfile \
        --build-arg TLS_MODE=standalone \
        --destination "$CI_REGISTRY_IMAGE:standalone-${CI_COMMIT_SHORT_SHA}" \
        --destination "$CI_REGISTRY_IMAGE:standalone-master" \
        --destination "$CI_REGISTRY_IMAGE:master" \
        --destination "$CI_REGISTRY_IMAGE:latest" \
        --registry-mirror us0.regi.kube.gmp.io

build-kubernetes:
  extends: .build
  variables:
    TLS_MODE: kubernetes

# Known vulnerabilities in what the ircd is built from: its mix.lock
# checked against the OSV database (https://osv.dev). A finding fails the
# job, which is how it gets noticed. Nightly, since advisories land
# without anyone pushing — from a pipeline schedule on master, which has
# to be created by hand (Build > Pipeline schedules; any time works, the
# Forgejo repos use "17 6 * * *") — and from "Run pipeline" on any branch.
#
# On a merge request, audit-mr compares the lockfiles the merge request
# changes with the same files at its merge base: it fails only on a
# vulnerability the merge request brings in, and says what it fixed and
# what's still there (scripts/osv-diff). A security update goes green for
# what it fixes, not red for what it couldn't.

.audit:
  stage: test
  needs: []
  image: python:3.13-trixie
  # Pinned, and checked against the release's published checksum before
  # it runs: a scanner is a strange thing to take on trust. To move it
  # on, change both together, from
  # https://github.com/google/osv-scanner/releases (…_SHA256SUMS).
  variables:
    OSV_VERSION: v2.6.0
    OSV_SHA256: ca69b3d3cd08f889a49dc0a383122f71cc528b83803671df5fd874d97485b108
  before_script:
    - |
      curl -sSfL -o /tmp/osv-scanner \
        "https://github.com/google/osv-scanner/releases/download/${OSV_VERSION}/osv-scanner_linux_amd64"
      echo "${OSV_SHA256}  /tmp/osv-scanner" | sha256sum -c -
      chmod +x /tmp/osv-scanner

# Exits 1 when anything is vulnerable, so the job fails; the table names
# each package, its version, and the version that fixes it.
audit:
  extends: .audit
  only:
    - schedules
    - web
  script:
    - /tmp/osv-scanner scan source --recursive .

audit-mr:
  extends: .audit
  only:
    - merge_requests
  variables:
    # The merge base has to be in the clone to read its lockfiles.
    GIT_DEPTH: "0"
  script:
    - |
      BASE="$CI_MERGE_REQUEST_DIFF_BASE_SHA"
      changed=$(git diff --name-only "$BASE" HEAD | grep -E '(^|/)mix\.lock$' || true)
      if [ -z "$changed" ]; then
        echo "No lockfile changed."
        exit 0
      fi
      mkdir -p /tmp/base
      for f in $changed; do
        mkdir -p "/tmp/base/$(dirname "$f")"
        git show "$BASE:$f" > "/tmp/base/$f" 2>/dev/null || true
      done
      # 0 and 1 (found some) are both answers; with nothing to scan, none.
      scan() {
        if [ $# -eq 0 ]; then echo '{}'; return; fi
        /tmp/osv-scanner scan source --format json $(printf -- '--lockfile %s ' "$@") || [ $? -eq 1 ]
      }
      (cd /tmp/base && scan $(for f in $changed; do [ -s "$f" ] && echo "$f"; done)) > /tmp/base.json
      scan $changed > /tmp/head.json
      python3 scripts/osv-diff /tmp/base.json /tmp/head.json