diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f629d5d..09fd964 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -8,6 +8,10 @@ # # 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: @@ -139,7 +143,8 @@ package: dotenv: build.env expire_in: 90 days -# --- GitLab release (v* tags): generic package registry + release with asset links ------------- +# --- 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//... release: stage: release image: @@ -155,15 +160,15 @@ release: - 'DEB=$(basename dist/*.deb)' - >- release-cli create --name "monky-deployd $CI_COMMIT_TAG" --tag-name "$CI_COMMIT_TAG" - --description "See CHANGELOG.md. Public install assets are on the Gitea mirror: https://gitea.cbs.tikali.net/mdella/monky-deployd/releases/tag/$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 PUBLIC download the installer uses) ------------------------------------------ -# The GitLab project is private, so install.sh fetches from the Gitea mirror -# https://gitea.cbs.tikali.net/mdella/monky-deployd/releases/download/v/... . This job -# waits for the pull-mirror to carry the tag, creates the release and uploads the assets. +# --- Gitea release (the off-estate alternative: install.sh --source gitea) ----------------------- +# Same assets at https://gitea.cbs.tikali.net/mdella/monky-deployd/releases/download/v/... . +# 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: diff --git a/CHANGELOG.md b/CHANGELOG.md index e88f00a..3b65de7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,15 @@ # Changelog +## v0.1.1 — 2026-09-05 + +- `install.sh` / ansible role / README: the **primary download is the public GitLab project's generic + package registry on scm.tikali.ai** (`/api/v4/projects/69/packages/generic/monky-deployd//…`); + the one-liner fetches the script from `scm.tikali.ai/…/-/raw/main/packaging/install.sh`. Inside the + estate `gitea.cbs.tikali.net` is split-horizon to jump1's RED EIP (no HTTP ingress), so backend + boxes could not fetch the artefacts (cbs/iac#102). The Gitea release stays the off-estate + alternative (`--source gitea` / `MONKY_DEPLOYD_SOURCE=gitea`). No agent behaviour change. + ## v0.1.0 — 2026-09-05 First release (MONKY-ADR-0028 §D, Reconciliation v2, Gate 1 v2). diff --git a/CLAUDE.md b/CLAUDE.md index 6464cde..9e5cf05 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -75,8 +75,9 @@ systemd-analyze verify packaging/systemd/*.service # when available Bump `monky_deployd.__version__` + `pyproject.toml` + `packaging/install.sh` `DEFAULT_VERSION` + `CHANGELOG.md`, merge to `main`, tag `vX.Y.Z` (protected `v*`). The tag pipeline builds the `.deb`, -publishes the GitLab release and — with `GITEA_TOKEN` — the **Gitea release** the installer -downloads from (`docs/OPERATIONS.md` has the manual recipe). `lint` refuses a tag whose version +publishes the **GitLab generic package registry + release** the installer downloads from (public +project; the Gitea name is split-horizon inside the estate, cbs/iac#102) and — with `GITEA_TOKEN` — +the **Gitea release** (`install.sh --source gitea`, off-estate; `docs/OPERATIONS.md` has the manual recipe). `lint` refuses a tag whose version differs from `__version__`. ## What NOT to do diff --git a/README.md b/README.md index f9ea013..7a6c49f 100644 --- a/README.md +++ b/README.md @@ -21,17 +21,22 @@ An admin reveals the kit once in the console (`GET /v1/backends/{id}/agent/insta you the enrolment JWT and a one-time **bootstrap deploy grant**. On the box: ```sh -curl -fsSL https://gitea.cbs.tikali.net/mdella/monky-deployd/raw/branch/main/packaging/install.sh \ +curl -fsSL https://scm.tikali.ai/tikali/applications/monky/monky-deployd/-/raw/main/packaging/install.sh \ | sudo bash -s -- --env env-qa-02 --site cbs --enrol-jwt ./monky-host.env-qa-02.jwt < bootstrap.jwt -# [--transport sdk|proxy|system] [--version 0.1.0] [--laptop] [--bao-ca openbao-ca.pem] +# [--transport sdk|proxy|system] [--version 0.1.1] [--laptop] [--bao-ca openbao-ca.pem] [--source gitlab|gitea] ``` `install.sh` installs `ziti-edge-tunnel` (OpenZiti `jammy` suite) and `docker-compose-plugin` if -absent, downloads the pinned `.deb` + `.sha256` from the [Gitea release](https://gitea.cbs.tikali.net/mdella/monky-deployd/releases), +absent, downloads the pinned `.deb` + `.sha256` from the [scm.tikali.ai generic package registry](https://scm.tikali.ai/tikali/applications/monky/monky-deployd/-/packages) +(`https://scm.tikali.ai/api/v4/projects/69/packages/generic/monky-deployd//monky-deployd__amd64.deb`), enrols `monky-host.` if the identity is missing, switches the tunneler to `run-host`, writes `/etc/monky-deployd/config.yaml`, grants the agent read access to the identity (ACL), stages the bootstrap grant (0600), enables `monky-deployd.timer`, runs one tick and deletes the -JWT. The GitLab project is private, so **the public download is the Gitea mirror**. +JWT. The GitLab project is **public**, so anonymous downloads work from scm.tikali.ai. Why not the +Gitea mirror: inside the estate `gitea.cbs.tikali.net` is split-horizon to jump1's RED EIP +(`10.10.0.175`), which has no HTTP ingress, so backend boxes cannot reach it (cbs/iac#102); +`--source gitea` (or `MONKY_DEPLOYD_SOURCE=gitea`) keeps the +[Gitea release](https://gitea.cbs.tikali.net/mdella/monky-deployd/releases) as the off-estate alternative. ## Transports @@ -140,9 +145,10 @@ systemd-analyze verify packaging/systemd/*.service # where systemd is availabl egress to github.com and pypi.org) and `package` builds the `.deb` with `nfpm` (binary from GitHub releases, goreleaser apt repo as fallback). Both were proven on the v0.1.0 tag pipeline and are blocking on `main`/tags (manual on MRs); the `.deb` always ships the SDK wheel. On a `v*` tag -`release` uploads to the GitLab generic package registry + release, and `release:gitea` publishes -the same assets on the public Gitea mirror (automatic when `GITEA_TOKEN` is set, manual otherwise -— see `docs/OPERATIONS.md` for the by-hand recipe). +`release` uploads to the GitLab generic package registry + release (**the primary download**, public +project), and `release:gitea` publishes the same assets on the Gitea mirror (the `--source gitea` +alternative; automatic when `GITEA_TOKEN` is set, manual otherwise — see `docs/OPERATIONS.md` for +the by-hand recipe). Both locations keep being published. ## See also diff --git a/ansible/roles/monky_deployd/README.md b/ansible/roles/monky_deployd/README.md index 1f6e7d9..be89cd4 100644 --- a/ansible/roles/monky_deployd/README.md +++ b/ansible/roles/monky_deployd/README.md @@ -4,7 +4,7 @@ Installs and configures [monky-deployd](https://scm.tikali.ai/tikali/application (the Monky backend pull agent, MONKY-ADR-0028) on a docker host that already carries an enrolled host identity (`roles/ziti_tunneler`, run-host mode). Skeleton for **osg1-07**; copy it there. -What it does: pin + download the `.deb` from the Gitea release (sha256 verified) → ACL +What it does: pin + download the `.deb` from the scm.tikali.ai package registry (sha256 verified; `monky_deployd_base_url`/`_deb_url` switch to the Gitea release off-estate) → ACL `u:monky-deployd:r` on the identity (`rx` on the dir) → `/etc/monky-deployd/config.yaml` from the template → optional openbao-ca PEM → optional `monky-deployd-proxy.service` (`transport: proxy`) → the **one-time bootstrap deploy grant** from a vault var (0600, `no_log`) → `monky-deployd.timer` @@ -14,7 +14,7 @@ template → optional openbao-ca PEM → optional `monky-deployd-proxy.service` | var | note | |---|---| -| `monky_deployd_version` | pinned release, e.g. `0.1.0` | +| `monky_deployd_version` | pinned release, e.g. `0.1.1` | | `monky_deployd_env_id` / `_site` / `_transport` | per host (`env-dev-06`, `cbs`, `sdk`) | | `monky_deployd_bootstrap_grant` | tenancy-minted deploy grant (1 h) — `ansible-vault` or a lookup at play time; empty keeps the existing token | | `monky_deployd_bao_ca_pem` | the `openbao-ca` certificate (PEM) | diff --git a/ansible/roles/monky_deployd/defaults/main.yml b/ansible/roles/monky_deployd/defaults/main.yml index 657dd19..08190ea 100644 --- a/ansible/roles/monky_deployd/defaults/main.yml +++ b/ansible/roles/monky_deployd/defaults/main.yml @@ -1,10 +1,15 @@ --- # monky_deployd — install and configure the Monky backend pull agent (MONKY-ADR-0028 §D). # Copy this role into osg1-07 (roles/monky_deployd) and roll to env-dev-06..09 after the pilot. -monky_deployd_version: "0.1.0" -monky_deployd_base_url: "https://gitea.cbs.tikali.net/mdella/monky-deployd" +monky_deployd_version: "0.1.1" monky_deployd_deb: "monky-deployd_{{ monky_deployd_version }}_amd64.deb" -monky_deployd_deb_url: "{{ monky_deployd_base_url }}/releases/download/v{{ monky_deployd_version }}/{{ monky_deployd_deb }}" +# PRIMARY download = the public GitLab project's generic package registry on scm.tikali.ai. Inside the +# estate gitea.cbs.tikali.net is split-horizon to jump1's RED EIP (10.10.0.175, no HTTP ingress), so +# backend boxes cannot fetch from the Gitea mirror (cbs/iac#102). Off-estate alternative (Gitea release): +# monky_deployd_base_url: "https://gitea.cbs.tikali.net/mdella/monky-deployd" +# monky_deployd_deb_url: "{{ monky_deployd_base_url }}/releases/download/v{{ monky_deployd_version }}/{{ monky_deployd_deb }}" +monky_deployd_base_url: "https://scm.tikali.ai/api/v4/projects/69/packages/generic/monky-deployd" +monky_deployd_deb_url: "{{ monky_deployd_base_url }}/{{ monky_deployd_version }}/{{ monky_deployd_deb }}" monky_deployd_deb_sha256_url: "{{ monky_deployd_deb_url }}.sha256" # per host (inventory / host_vars) diff --git a/ansible/roles/monky_deployd/tasks/main.yml b/ansible/roles/monky_deployd/tasks/main.yml index 46f00d0..9564354 100644 --- a/ansible/roles/monky_deployd/tasks/main.yml +++ b/ansible/roles/monky_deployd/tasks/main.yml @@ -22,7 +22,7 @@ changed_when: false failed_when: false -- name: monky_deployd | download .deb + sha256 from the Gitea release +- name: monky_deployd | download .deb + sha256 from the scm.tikali.ai package registry when: monky_deployd_installed.stdout != monky_deployd_version block: - name: monky_deployd | fetch sha256 diff --git a/config.example.yaml b/config.example.yaml index 0e49a2b..157ec1e 100644 --- a/config.example.yaml +++ b/config.example.yaml @@ -1,4 +1,4 @@ -# /etc/monky-deployd/config.yaml — monky-deployd v0.1.0 (MONKY-ADR-0028 §D) +# /etc/monky-deployd/config.yaml — monky-deployd v0.1.1 (MONKY-ADR-0028 §D) # Written by packaging/install.sh (or the ansible role monky_deployd). YAML *subset*: maps, scalars, # simple lists, comments. Keys not listed here are a config error. diff --git a/docs/OPERATIONS.md b/docs/OPERATIONS.md index 19515c6..6da59a1 100644 --- a/docs/OPERATIONS.md +++ b/docs/OPERATIONS.md @@ -86,6 +86,17 @@ the picker, nothing is retired automatically. `docker image prune -f` after a successful apply. The state dir and the token survive `apt remove`; `apt purge` deletes them. +## Where the installer downloads from + +The primary source is the **scm.tikali.ai generic package registry** of this (public) project: +`https://scm.tikali.ai/api/v4/projects/69/packages/generic/monky-deployd//` for +`monky-deployd__amd64.deb`, `.sha256` and `install.sh`; the one-liner fetches the script from +`https://scm.tikali.ai/tikali/applications/monky/monky-deployd/-/raw/main/packaging/install.sh`. +Inside the estate `gitea.cbs.tikali.net` is split-horizon to jump1's RED EIP (`10.10.0.175`), which +has no HTTP ingress, so backend boxes cannot fetch from the Gitea mirror (cbs/iac#102). Off-estate, +`install.sh --source gitea` (ansible: `monky_deployd_base_url`/`_deb_url`) uses the Gitea release +instead. The tag pipeline publishes to both (`release`, `release:gitea`). + ## Publishing a release to Gitea by hand When `release:gitea` is manual (no `GITEA_TOKEN` in CI) — from jump1 with the Gitea API token: @@ -100,10 +111,10 @@ for f in monky-deployd_0.1.0_amd64.deb monky-deployd_0.1.0_amd64.deb.sha256 inst ``` The assets are then at `https://gitea.cbs.tikali.net/mdella/monky-deployd/releases/download/v0.1.0/`, -which is what `install.sh` fetches (GitLab artifacts from the tag pipeline's `package` job). +which is what `install.sh --source gitea` fetches (GitLab artifacts from the tag pipeline's `package` job). ## Ansible (osg1-07) -`ansible/roles/monky_deployd/` is the role skeleton to copy into osg1-07: `.deb` from the Gitea -release (sha256-verified), config template, bootstrap grant from a vault var, ACL on the identity, +`ansible/roles/monky_deployd/` is the role skeleton to copy into osg1-07: `.deb` from the +scm.tikali.ai package registry (sha256-verified), config template, bootstrap grant from a vault var, ACL on the identity, timer, one tick. Rolled to env-dev-06..09 after the env-qa-02 pilot; env-dev-01 last. diff --git a/monky_deployd/__init__.py b/monky_deployd/__init__.py index 27c4775..c98d5e6 100644 --- a/monky_deployd/__init__.py +++ b/monky_deployd/__init__.py @@ -4,4 +4,4 @@ Dials monky-tenancy over the mesh with the box's host identity, fetches the rend leases a deploy grant, logs in to OpenBao, reads its own secrets, runs `docker compose`, reports. Stdlib only; the optional `openziti` SDK is the `sdk` transport.""" -__version__ = "0.1.0" +__version__ = "0.1.1" diff --git a/packaging/install.sh b/packaging/install.sh index a526233..2295c9f 100755 --- a/packaging/install.sh +++ b/packaging/install.sh @@ -1,9 +1,9 @@ #!/usr/bin/env bash # monky-deployd installer — Ubuntu 26.04 (verified target). # -# curl -fsSL https://gitea.cbs.tikali.net/mdella/monky-deployd/raw/branch/main/packaging/install.sh \ -# | sudo bash -s -- --env env-qa-02 --site cbs [--transport sdk|proxy|system] [--version 0.1.0] \ -# [--enrol-jwt /path/monky-host.env-qa-02.jwt] [--laptop] < bootstrap.jwt +# curl -fsSL https://scm.tikali.ai/tikali/applications/monky/monky-deployd/-/raw/main/packaging/install.sh \ +# | sudo bash -s -- --env env-qa-02 --site cbs [--transport sdk|proxy|system] [--version 0.1.1] \ +# [--enrol-jwt /path/monky-host.env-qa-02.jwt] [--laptop] [--source gitlab|gitea] < bootstrap.jwt # # stdin (or --bootstrap-file): the ONE-TIME bootstrap deploy grant from the install kit # (GET /v1/backends/{id}/agent/install). The enrolment JWT is read from --enrol-jwt or @@ -11,7 +11,8 @@ # # What it does (idempotent): # 1. apt: ziti-edge-tunnel (OpenZiti `jammy` suite) if absent, docker-compose-plugin, acl -# 2. downloads the pinned monky-deployd__amd64.deb + .sha256 from the Gitea release, verifies, installs +# 2. downloads the pinned monky-deployd__amd64.deb + .sha256 from the scm.tikali.ai package registry +# (--source gitea: the Gitea release, off-estate), verifies, installs # 3. enrols /opt/openziti/etc/identities/monky-host..json if absent (ziti-edge-tunnel enroll), # chown ziti:ziti 0600, switches ziti-edge-tunnel.service to `run-host` via a drop-in # 4. writes /etc/monky-deployd/config.yaml, ACLs so user monky-deployd can read the identity, @@ -20,8 +21,16 @@ set -euo pipefail umask 077 -DEFAULT_VERSION="0.1.0" -BASE_URL="${MONKY_DEPLOYD_BASE_URL:-https://gitea.cbs.tikali.net/mdella/monky-deployd}" +DEFAULT_VERSION="0.1.1" +# Download source. PRIMARY is the public GitLab project's generic package registry on scm.tikali.ai: +# inside the estate gitea.cbs.tikali.net is split-horizon to jump1's RED EIP (10.10.0.175), which has +# no HTTP ingress, so backend boxes cannot reach the Gitea mirror (cbs/iac#102); scm.tikali.ai they can. +# `--source gitea` keeps the Gitea release as the off-estate alternative. --base-url / MONKY_DEPLOYD_BASE_URL +# override the base for whichever layout is selected. +GITLAB_BASE_URL="https://scm.tikali.ai/api/v4/projects/69/packages/generic/monky-deployd" +GITEA_BASE_URL="https://gitea.cbs.tikali.net/mdella/monky-deployd" +SOURCE="${MONKY_DEPLOYD_SOURCE:-gitlab}" +BASE_URL="${MONKY_DEPLOYD_BASE_URL:-}" OPENZITI_SUITE="${OPENZITI_SUITE:-jammy}" IDENTITY_DIR="/opt/openziti/etc/identities" ETC="/etc/monky-deployd" @@ -41,6 +50,7 @@ while [ $# -gt 0 ]; do --bootstrap-file) BOOTSTRAP_FILE="$2"; shift 2 ;; --bao-ca) BAO_CA="$2"; shift 2 ;; --base-url) BASE_URL="$2"; shift 2 ;; + --source) SOURCE="$2"; shift 2 ;; --laptop) LAPTOP="true"; shift ;; --no-run) NO_RUN=1; shift ;; --force-config) FORCE_CONFIG=1; shift ;; @@ -56,6 +66,7 @@ done SITE="${SITE,,}" [[ "$SITE" =~ ^(cbs|pdx)$ ]] || die "site must be cbs|pdx" [[ "$TRANSPORT" =~ ^(sdk|proxy|system)$ ]] || die "transport must be sdk|proxy|system" +[[ "$SOURCE" =~ ^(gitlab|gitea)$ ]] || die "source must be gitlab|gitea" IDENTITY="$IDENTITY_DIR/monky-host.$ENV_ID.json" export DEBIAN_FRONTEND=noninteractive @@ -98,7 +109,11 @@ if [ "$installed" = "$VERSION" ]; then else tmp="$(mktemp -d)"; trap 'rm -rf "$tmp"' EXIT deb="monky-deployd_${VERSION}_amd64.deb" - url="$BASE_URL/releases/download/v${VERSION}" + if [ "$SOURCE" = gitlab ]; then + url="${BASE_URL:-$GITLAB_BASE_URL}/${VERSION}" + else + url="${BASE_URL:-$GITEA_BASE_URL}/releases/download/v${VERSION}" + fi log "downloading $deb from $url" curl -fsSL -o "$tmp/$deb" "$url/$deb" curl -fsSL -o "$tmp/$deb.sha256" "$url/$deb.sha256" diff --git a/pyproject.toml b/pyproject.toml index 2a9a1ce..beaef8f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "monky-deployd" -version = "0.1.0" +version = "0.1.1" description = "Monky backend pull agent: checkin -> bundle -> lease -> OpenBao -> docker compose -> report, over the ziti mesh (MONKY-ADR-0028)" readme = "README.md" requires-python = ">=3.12" diff --git a/tests/test_agent.py b/tests/test_agent.py index 847ba1d..6ed4b4f 100644 --- a/tests/test_agent.py +++ b/tests/test_agent.py @@ -7,6 +7,7 @@ import os import time from pathlib import Path +from monky_deployd import __version__ from monky_deployd import state as statemod from monky_deployd.agent import EX_ENV_MISMATCH, EX_FAIL, EX_OK, EX_TEMPFAIL, Agent from monky_deployd.bundle import bundle_sha @@ -24,7 +25,7 @@ def test_first_tick_bootstraps_applies_and_reports(bootstrapped, tenancy, bao, f assert not Path(cfg.bootstrap_path).exists() assert cfg.token_path.exists() and (cfg.token_path.stat().st_mode & 0o777) == 0o600 # protocol: checkin -> bundle -> lease -> report applied - assert tenancy.checkins[0]["env_id"] == ENV and tenancy.checkins[0]["agent_version"] == "0.1.0" + assert tenancy.checkins[0]["env_id"] == ENV and tenancy.checkins[0]["agent_version"] == __version__ assert tenancy.checkins[0]["host"]["docker"] == "28.3.0" assert len(tenancy.leases) == 1 and tenancy.leases[0]["reason"] == "apply" assert [r["result"] for r in tenancy.reports] == ["applied"] diff --git a/tests/test_cli.py b/tests/test_cli.py index efe4dad..759c6cf 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1,7 +1,7 @@ import json from pathlib import Path -from monky_deployd import cli +from monky_deployd import __version__, cli from monky_deployd.agent import EX_OK @@ -49,7 +49,7 @@ def test_bootstrap_command(bootstrapped, bao, capsys): def test_version_and_bad_config(capsys, tmp_path): assert cli.main(["version"]) == 0 - assert capsys.readouterr().out.strip() == "0.1.0" + assert capsys.readouterr().out.strip() == __version__ bad = tmp_path / "c.yaml" bad.write_text("env_id: nope\nsite: cbs\n") assert cli.main(["-c", str(bad), "status"]) == 78