From 576b26cf8103849d59e560c0d5f13d77cc0cbbde Mon Sep 17 00:00:00 2001 From: dhkatz Date: Fri, 30 Oct 2026 15:47:58 +0000 Subject: [PATCH] CI only tests again: Komodo builds and deploys MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Komodo now watches the t5de org (Gitea webhook → its t5de-push action, defined in t5de-infra): it builds and deploys a commit to staging once this CI passes on main, and to prod for vX.Y.Z tags. So the deploy job and .gitea/scripts/deploy.py go, and this repo no longer needs to know how it's deployed. Co-Authored-By: Claude Opus 5.5 --- .gitea/scripts/deploy.py | 97 ---------------------------------------- .gitea/workflows/ci.yml | 48 +------------------- 2 files changed, 1 insertion(+), 144 deletions(-) delete mode 100644 .gitea/scripts/deploy.py diff --git a/.gitea/scripts/deploy.py b/.gitea/scripts/deploy.py deleted file mode 100644 index f8eeebd..0000000 --- a/.gitea/scripts/deploy.py +++ /dev/null @@ -1,97 +0,0 @@ -"""Deploy a build with Komodo. Run by ci.yml after the images are pushed. - - deploy.py - -A vX.Y.Z tag deploys X.Y.Z to prod: the version is committed to t5de/t5de-infra (stacks//prod.env), then -Komodo deploys -prod from it. Pushes to main deploy -staging, which runs the :main image. - -Env: KOMODO_API_KEY and KOMODO_API_SECRET (a Komodo service user that can only deploy), INFRA_TOKEN (writes -t5de/t5de-infra; releases only), KOMODO_URL (default http://10.10.10.239:9120). -""" - -import base64 -import json -import os -import re -import sys -import time -import urllib.error -import urllib.request - -KOMODO_URL = os.environ.get("KOMODO_URL") or "http://10.10.10.239:9120" -GITEA_URL = os.environ.get("GITHUB_SERVER_URL", "https://git.t5de.dev").rstrip("/") -INFRA_REPO = "t5de/t5de-infra" -RELEASE_TAG = re.compile(r"v(\d+\.\d+\.\d+)") - - -def call(method: str, url: str, body: dict | None = None, headers: dict | None = None) -> dict: - data = json.dumps(body).encode() if body is not None else None - req = urllib.request.Request(url, data=data, method=method, headers={"Content-Type": "application/json", **(headers or {})}) - try: - with urllib.request.urlopen(req, timeout=60) as resp: - return json.loads(resp.read() or b"{}") - except urllib.error.HTTPError as e: - sys.exit(f"{method} {url}: {e.code} {e.read().decode(errors='replace')}") - - -def komodo(kind: str, type_: str, params: dict) -> dict: - keys = {"X-Api-Key": os.environ["KOMODO_API_KEY"], "X-Api-Secret": os.environ["KOMODO_API_SECRET"]} - return call("POST", f"{KOMODO_URL}/{kind}", {"type": type_, "params": params}, keys) - - -def pin_version(infra_dir: str, variable: str, version: str) -> None: - """Set = in the infra repo's prod.env, committing only if it changed.""" - path = f"stacks/{infra_dir}/prod.env" - url = f"{GITEA_URL}/api/v1/repos/{INFRA_REPO}/contents/{path}" - auth = {"Authorization": f"token {os.environ['INFRA_TOKEN']}"} - current = call("GET", f"{url}?ref=main", headers=auth) - text = base64.b64decode(current["content"]).decode() - updated, n = re.subn(rf"(?m)^{variable}=.*$", f"{variable}={version}", text) - if n != 1: - sys.exit(f"{path}: expected one {variable}= line, found {n}") - if updated == text: - print(f"{path} already has {variable}={version}") - return - release_url = f"{GITEA_URL}/{os.environ.get('GITHUB_REPOSITORY', '')}/releases/tag/v{version}" - call("PUT", url, { - "branch": "main", - "sha": current["sha"], - "content": base64.b64encode(updated.encode()).decode(), - "message": f"{infra_dir} {version} to prod\n\nFrom {release_url}", - }, auth) - print(f"Committed {variable}={version} to {INFRA_REPO}/{path}") - - -def deploy(stack: str) -> None: - update = komodo("execute", "DeployStack", {"stack": stack}) - update_id = update["_id"]["$oid"] - print(f"Deploying {stack} (Komodo update {update_id})") - deadline = time.time() + 15 * 60 - while time.time() < deadline: - update = komodo("read", "GetUpdate", {"id": update_id}) - if update.get("status") == "Complete": - for log in update.get("logs", []): - print(f"--- {log.get('stage')}: {'ok' if log.get('success') else 'FAILED'}") - if not log.get("success"): - print(log.get("stdout", ""), log.get("stderr", ""), sep="\n") - if not update.get("success"): - sys.exit(f"{stack}: deploy failed") - print(f"{stack}: deployed") - return - time.sleep(5) - sys.exit(f"{stack}: deploy didn't finish in 15 minutes") - - -def main() -> None: - stack, infra_dir, variable = sys.argv[1:4] - ref_name = os.environ.get("GITHUB_REF_NAME", "") - release = os.environ.get("GITHUB_REF_TYPE") == "tag" and RELEASE_TAG.fullmatch(ref_name) - if release: - pin_version(infra_dir, variable, release.group(1)) - deploy(f"{stack}-prod") - else: - deploy(f"{stack}-staging") - - -if __name__ == "__main__": - main() diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 2c3993b..d5b5aee 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -1,5 +1,4 @@ -# Tests the catalog, builds t5de-catalog for Linux (static) and its Docker image, then deploys it: main to staging, -# vX.Y.Z tags to prod. +# Tests the catalog and builds t5de-catalog for Linux (static) and its Docker image. name: CI on: @@ -36,48 +35,3 @@ jobs: -ldflags "-s -w -buildid= -X git.t5de.dev/t5de/t5de-catalog/internal/buildinfo.Version=${GITHUB_SHA::12}" \ -o dist/t5de-catalog ./cmd/t5de-catalog sha256sum dist/t5de-catalog - - # The image for Komodo, then the deploy: main → :main and :sha-, deployed to staging; vX.Y.Z → :X.Y.Z and - # :latest, pinned in t5de/t5de-infra and deployed to prod (.gitea/scripts/deploy.py). - deploy: - needs: build - if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Image tags - id: tags - run: | - if [ "$GITHUB_REF_TYPE" = tag ]; then - version=$(echo "$GITHUB_REF_NAME" | sed -nE 's/^v([0-9]+\.[0-9]+\.[0-9]+)$/\1/p') - [ -n "$version" ] || { echo "Not a vX.Y.Z tag: $GITHUB_REF_NAME"; exit 1; } - echo "tags=$version latest" >> "$GITHUB_OUTPUT" - echo "version=$version" >> "$GITHUB_OUTPUT" - else - echo "tags=main sha-${GITHUB_SHA::12}" >> "$GITHUB_OUTPUT" - echo "version=${GITHUB_SHA::12}" >> "$GITHUB_OUTPUT" - fi - - # The build fetches t5de-api from Gitea; --add-host keeps that on the LAN, as the runner does. - - name: Build and push - env: - TAGS: ${{ steps.tags.outputs.tags }} - VERSION: ${{ steps.tags.outputs.version }} - REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN || secrets.GITHUB_TOKEN }} - run: | - echo "$REGISTRY_TOKEN" | docker login git.t5de.dev -u "$GITHUB_ACTOR" --password-stdin - image=git.t5de.dev/t5de/t5de-catalog - args=(--build-arg "VERSION=$VERSION" --add-host git.t5de.dev:10.10.10.39 - --label "org.opencontainers.image.source=$GITHUB_SERVER_URL/$GITHUB_REPOSITORY" - --label "org.opencontainers.image.revision=$GITHUB_SHA") - for tag in $TAGS; do args+=(-t "$image:$tag"); done - docker build "${args[@]}" . - for tag in $TAGS; do docker push "$image:$tag"; done - - - name: Deploy - env: - KOMODO_API_KEY: ${{ secrets.KOMODO_API_KEY }} - KOMODO_API_SECRET: ${{ secrets.KOMODO_API_SECRET }} - INFRA_TOKEN: ${{ secrets.INFRA_TOKEN }} - run: python3 .gitea/scripts/deploy.py catalog t5de-catalog CATALOG_VERSION