Files
monky-deployd/.gitlab-ci.yml
T
mdella c966450d8e fix(install): fetch from scm.tikali.ai (public project) — Gitea name is split-horizon inside the estate
Inside the estate gitea.cbs.tikali.net resolves to jump1's RED EIP (10.10.0.175),
which has no HTTP ingress, so backend boxes could not download the install
artefacts from the Gitea mirror (cbs/iac#102). scm.tikali.ai is reachable from
those boxes and the project is now public, so the GitLab generic package
registry becomes the PRIMARY source:

- packaging/install.sh: default source = scm.tikali.ai generic package registry
  (projects/69/packages/generic/monky-deployd/<ver>/...); `--source gitea` /
  MONKY_DEPLOYD_SOURCE=gitea keeps the Gitea release as the off-estate
  alternative; --base-url / MONKY_DEPLOYD_BASE_URL still override the base.
- ansible role defaults: monky_deployd_base_url/_deb_url point at the registry,
  Gitea layout kept as a commented alternative.
- README / docs/OPERATIONS.md / CLAUDE.md / CI comments + release description:
  both locations keep being published (release + release:gitea).
- Version 0.1.1 (the tag gate refuses v* tags whose version != __version__);
  tests compare against __version__ instead of a literal. No agent change.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KLB7jieMNRkTsJ2epr4Ds1
2026-09-05 17:57:27 +00:00

198 lines
9.8 KiB
YAML

# monky-deployd CI/CD (mirrors monky-tenancy's conventions)
#
# lint -> test -> build (openziti wheel) -> package (.deb via nfpm) -> release (v* tags) -> docs
#
# Every script line is single-quoted (a bare ": " turns the line into a YAML map and silently
# yields a 0-job pipeline). Runner egress to github.com + pypi.org was PROVEN on the v0.1.0 tag
# pipeline (6999: openziti 1.7.1 wheel built, nfpm .deb packaged), so wheel/package are blocking.
#
# CI/CD variables (project or group level):
# GITEA_TOKEN — Gitea API token (write:repository) for `release:gitea`; without it the job is manual
#
# Release assets are published to BOTH the GitLab generic package registry (the installer's primary
# source — the project is public; gitea.cbs.tikali.net is split-horizon inside the estate, cbs/iac#102)
# and the Gitea mirror release (`install.sh --source gitea`, off-estate).
# GITLAB_TRANSLATE_TOKEN — used by the translate-docs component (group level)
include:
# doc-translator via the CI/CD component; PARTIAL PIN @11.3 tracks 11.3.x (no `v`).
- component: $CI_SERVER_FQDN/tikali/platform/doc-translator/translate-docs@11.3
inputs:
docs_stage: docs
stages: [lint, test, build, package, release, docs]
# One pipeline per change: MR pipeline for merge requests, branch pipeline otherwise, never both.
workflow:
rules:
- if: '$CI_COMMIT_TAG'
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- if: '$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS && $CI_PIPELINE_SOURCE == "push"'
when: never
- if: '$CI_COMMIT_BRANCH'
variables:
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
OPENZITI_VERSION: "1.7.1"
NFPM_VERSION: "2.43.0"
cache:
key: "$CI_JOB_NAME"
paths: [.cache/pip]
.run_rules:
rules:
- if: '$CI_COMMIT_TAG'
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- if: '$CI_COMMIT_BRANCH'
lint:
extends: [.run_rules]
stage: lint
image: python:3.12-slim
before_script:
- 'pip install -q "ruff>=0.16,<0.17"'
script:
- 'ruff check .'
- 'ruff format --check .'
- 'bash -n packaging/install.sh'
- 'bash -n packaging/scripts/postinstall.sh packaging/scripts/preremove.sh packaging/scripts/postremove.sh'
- 'python -c "import ast,sys; [ast.parse(open(f).read()) for f in sys.argv[1:]]" tests/fakebin/docker'
# the release version must match the tag when there is one
- 'if [ -n "$CI_COMMIT_TAG" ]; then v=$(python -c "import monky_deployd;print(monky_deployd.__version__)"); [ "v$v" = "$CI_COMMIT_TAG" ] || { echo "tag $CI_COMMIT_TAG != __version__ $v"; exit 1; }; fi'
test:
extends: [.run_rules]
stage: test
image: python:3.12-slim
before_script:
- 'pip install -q pytest'
script:
- 'chmod +x tests/fakebin/docker'
- 'pytest --junitxml=report.xml'
artifacts:
when: always
reports:
junit: report.xml
# --- the openziti wheel ---------------------------------------------------------------------
# PyPI ships `openziti` as an sdist whose build fetches ziti-sdk-c (+ prebuilt tlsuv/uv-mbed
# via cmake FetchContent) from github.com at install time. Building it here on ubuntu:26.04
# (the target OS; python3 = the target's python3) gives us a wheel to vendor into the venv.
# NEEDS runner egress to github.com + pypi.org (proven 2026-09-05, pipeline 6999).
wheel:
stage: build
image: ubuntu:26.04
needs: ["test"]
rules:
- if: '$CI_COMMIT_TAG'
- if: '$CI_COMMIT_BRANCH == "main"'
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
when: manual
allow_failure: true
variables:
DEBIAN_FRONTEND: noninteractive
script:
- 'apt-get update -qq && apt-get install -y -qq --no-install-recommends python3 python3-venv python3-dev build-essential cmake ninja-build git pkg-config libssl-dev zlib1g-dev ca-certificates curl >/dev/null'
- 'python3 -m venv /tmp/wb && /tmp/wb/bin/pip install -q --upgrade pip wheel setuptools'
- 'mkdir -p vendor'
- '/tmp/wb/bin/pip wheel --no-deps --no-binary openziti "openziti==${OPENZITI_VERSION}" -w vendor/'
- 'ls -l vendor/'
artifacts:
paths: [vendor/*.whl]
expire_in: 30 days
# --- the .deb --------------------------------------------------------------------------------------
# venv at its final path (/opt/monky-deployd/venv is where the .deb puts it; venvs are not
# relocatable) + nfpm. nfpm comes from GitHub releases (egress) with the goreleaser apt repo as
# fallback (proven 2026-09-05, pipeline 6999). The wheel is required: a .deb without it would
# silently ship a broken `transport: sdk`.
package:
stage: package
image: ubuntu:26.04
needs:
- job: test
- job: wheel
rules:
- if: '$CI_COMMIT_TAG'
- if: '$CI_COMMIT_BRANCH == "main"'
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
when: manual
allow_failure: true
variables:
DEBIAN_FRONTEND: noninteractive
script:
- 'apt-get update -qq && apt-get install -y -qq --no-install-recommends python3 python3-venv python3-pip ca-certificates curl gnupg >/dev/null'
- 'VERSION=$(python3 -c "import monky_deployd;print(monky_deployd.__version__)"); echo "VERSION=$VERSION" | tee build.env'
- 'python3 -m venv /opt/monky-deployd/venv'
- '/opt/monky-deployd/venv/bin/pip install -q --upgrade pip'
- '/opt/monky-deployd/venv/bin/pip install -q .'
- 'ls vendor/*.whl >/dev/null 2>&1 || { echo "no vendored openziti wheel (wheel job artifact missing)"; exit 1; }'
- '/opt/monky-deployd/venv/bin/pip install -q vendor/*.whl'
- '/opt/monky-deployd/venv/bin/python -c "import openziti; print(\"openziti import ok\")"'
- '/opt/monky-deployd/venv/bin/python -m monky_deployd version'
- 'mkdir -p build dist && cp -a /opt/monky-deployd/venv build/venv'
# nfpm: GitHub release .deb, else the goreleaser apt repo
- 'curl -fsSL -o /tmp/nfpm.deb "https://github.com/goreleaser/nfpm/releases/download/v${NFPM_VERSION}/nfpm_${NFPM_VERSION}_amd64.deb" && apt-get install -y -qq /tmp/nfpm.deb >/dev/null || { echo "deb [trusted=yes] https://repo.goreleaser.com/apt/ /" > /etc/apt/sources.list.d/goreleaser.list; apt-get update -qq; apt-get install -y -qq nfpm; }'
- 'VERSION=$VERSION nfpm package --config packaging/nfpm.yaml --packager deb --target dist/'
- 'cd dist && for f in *.deb; do sha256sum "$f" > "$f.sha256"; done && ls -l && cd ..'
- 'cp packaging/install.sh dist/install.sh'
artifacts:
paths: [dist/]
reports:
dotenv: build.env
expire_in: 90 days
# --- GitLab release (v* tags): generic package registry (the PRIMARY download) + release ---------
# install.sh / the ansible role fetch ${CI_API_V4_URL}/projects/69/packages/generic/monky-deployd/<ver>/...
release:
stage: release
image:
name: registry.gitlab.com/gitlab-org/release-cli:latest
entrypoint: [""]
needs: ["package"]
rules:
- if: '$CI_COMMIT_TAG =~ /^v/'
script:
- 'apk add --no-cache curl >/dev/null 2>&1 || true'
- 'VERSION=${CI_COMMIT_TAG#v}; PKG="${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/monky-deployd/${VERSION}"'
- 'for f in dist/*.deb dist/*.sha256 dist/install.sh; do curl -fsS --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file "$f" "$PKG/$(basename "$f")"; echo; done'
- 'DEB=$(basename dist/*.deb)'
- >-
release-cli create --name "monky-deployd $CI_COMMIT_TAG" --tag-name "$CI_COMMIT_TAG"
--description "See CHANGELOG.md. Install assets: this release's package registry links (primary; the install.sh default) and the Gitea mirror for off-estate use: https://gitea.cbs.tikali.net/mdella/monky-deployd/releases/tag/$CI_COMMIT_TAG"
--assets-link "{\"name\":\"$DEB\",\"url\":\"$PKG/$DEB\",\"link_type\":\"package\"}"
--assets-link "{\"name\":\"$DEB.sha256\",\"url\":\"$PKG/$DEB.sha256\",\"link_type\":\"other\"}"
--assets-link "{\"name\":\"install.sh\",\"url\":\"$PKG/install.sh\",\"link_type\":\"other\"}"
# --- Gitea release (the off-estate alternative: install.sh --source gitea) -----------------------
# Same assets at https://gitea.cbs.tikali.net/mdella/monky-deployd/releases/download/v<ver>/... .
# Not the default: inside the estate that name is split-horizon to jump1's RED EIP (cbs/iac#102).
# This job waits for the pull-mirror to carry the tag, creates the release and uploads the assets.
# Automatic when GITEA_TOKEN is set; otherwise a manual, non-blocking job (docs/OPERATIONS.md
# has the by-hand recipe).
release:gitea:
stage: release
image:
name: alpine:3.20
entrypoint: [""]
needs: ["package"]
rules:
- if: '$CI_COMMIT_TAG =~ /^v/ && $GITEA_TOKEN'
- if: '$CI_COMMIT_TAG =~ /^v/'
when: manual
allow_failure: true
variables:
GITEA_API: "https://gitea.cbs.tikali.net/api/v1/repos/mdella/monky-deployd"
script:
- 'apk add --no-cache curl jq >/dev/null'
- '[ -n "$GITEA_TOKEN" ] || { echo "GITEA_TOKEN is not set"; exit 1; }'
- 'H="Authorization: token $GITEA_TOKEN"'
- 'curl -fsS -X POST -H "$H" "$GITEA_API/mirror-sync" >/dev/null || echo "mirror-sync trigger failed; polling anyway"'
- 'for i in $(seq 1 30); do curl -fsS -H "$H" "$GITEA_API/tags/$CI_COMMIT_TAG" >/dev/null 2>&1 && break; echo "waiting for the mirror to carry $CI_COMMIT_TAG ($i)"; sleep 10; done'
- 'curl -fsS -H "$H" "$GITEA_API/tags/$CI_COMMIT_TAG" >/dev/null || { echo "tag not on the mirror yet"; exit 1; }'
- 'RID=$(curl -fsS -H "$H" "$GITEA_API/releases/tags/$CI_COMMIT_TAG" 2>/dev/null | jq -r .id || true)'
- 'if [ -z "$RID" ] || [ "$RID" = "null" ]; then RID=$(curl -fsS -X POST -H "$H" -H "Content-Type: application/json" "$GITEA_API/releases" -d "{\"tag_name\":\"$CI_COMMIT_TAG\",\"name\":\"monky-deployd $CI_COMMIT_TAG\",\"body\":\"See CHANGELOG.md\",\"draft\":false,\"prerelease\":false}" | jq -r .id); fi'
- 'echo "release id $RID"'
- 'for f in dist/*.deb dist/*.sha256 dist/install.sh; do n=$(basename "$f"); curl -fsS -X POST -H "$H" -F "attachment=@$f" "$GITEA_API/releases/$RID/assets?name=$n" >/dev/null && echo "uploaded $n"; done'