From 28a4a7ea6cfccdd35c342f16e2c521dc54bf27c5 Mon Sep 17 00:00:00 2001 From: philipp Date: Wed, 8 Apr 2026 22:33:59 +0200 Subject: [PATCH] Make repo deployment list authoritative Proof: Automatic rollout now reconciles a repo-owned deployment list that explicitly includes operator-dashboard, instead of depending on mutable Forgejo variables or deployment metadata labels. Assumptions: Repo-owned application deployments are the set enumerated in scripts/deploy/repo_deployments.py and each deployment still uses container name app for image updates. Still fake: Forgejo still shows older workflow behavior on prior runs, so this commit must be validated by one more push-driven deployment cycle. --- .forgejo/workflows/deploy.yml | 7 +++--- scripts/deploy/bootstrap.sh | 2 +- scripts/deploy/repo_deployments.py | 39 ++++++++++++++++++++++++++++++ test/repo_deployments_test.py | 16 ++++++++++++ 4 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 scripts/deploy/repo_deployments.py create mode 100644 test/repo_deployments_test.py diff --git a/.forgejo/workflows/deploy.yml b/.forgejo/workflows/deploy.yml index a82d762..d03a1fd 100644 --- a/.forgejo/workflows/deploy.yml +++ b/.forgejo/workflows/deploy.yml @@ -150,10 +150,9 @@ jobs: | python3 "$WORKSPACE_DIR/scripts/deploy/render_release_manifest.py" --image "$IMAGE" \ | kubectl apply -f - - kubectl -n "$PROJECT_NAMESPACE" get deployment \ - -l "app.kubernetes.io/part-of=$PROJECT_NAME" \ - -o name \ + python3 "$WORKSPACE_DIR/scripts/deploy/repo_deployments.py" --format lines \ | while IFS= read -r deployment; do [ -n "$deployment" ] || continue - kubectl -n "$PROJECT_NAMESPACE" rollout status "$deployment" --timeout=180s + kubectl -n "$PROJECT_NAMESPACE" set image "deployment/$deployment" app="$IMAGE" + kubectl -n "$PROJECT_NAMESPACE" rollout status "deployment/$deployment" --timeout=180s done diff --git a/scripts/deploy/bootstrap.sh b/scripts/deploy/bootstrap.sh index e6b6978..9039f79 100755 --- a/scripts/deploy/bootstrap.sh +++ b/scripts/deploy/bootstrap.sh @@ -10,7 +10,7 @@ FORGEJO_REMOTE_NAME="${FORGEJO_REMOTE_NAME:-forgejo}" PROJECT_NAME="${PROJECT_NAME:-unrip}" PROJECT_NAMESPACE="${PROJECT_NAMESPACE:-$PROJECT_NAME}" -PROJECT_DEPLOYMENTS="${PROJECT_DEPLOYMENTS:-near-intents-ingest,market-reference-ingest,liquidity-manager,inventory-sync,history-writer,ops-sentinel,strategy-engine,trade-executor,operator-dashboard}" +PROJECT_DEPLOYMENTS="${PROJECT_DEPLOYMENTS:-$(python3 "$ROOT_DIR/scripts/deploy/repo_deployments.py" --format csv)}" PROJECT_REGISTRY_SECRET_NAME="${PROJECT_REGISTRY_SECRET_NAME:-${PROJECT_NAME}-registry-creds}" APP_SECRET_NAME="${APP_SECRET_NAME:-${PROJECT_NAME}-secrets}" SYNC_FORGEJO_REMOTE="${SYNC_FORGEJO_REMOTE:-1}" diff --git a/scripts/deploy/repo_deployments.py b/scripts/deploy/repo_deployments.py new file mode 100644 index 0000000..5034fde --- /dev/null +++ b/scripts/deploy/repo_deployments.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse + + +REPO_DEPLOYMENTS = [ + "near-intents-ingest", + "market-reference-ingest", + "liquidity-manager", + "inventory-sync", + "history-writer", + "ops-sentinel", + "strategy-engine", + "trade-executor", + "operator-dashboard", +] + + +def main() -> int: + parser = argparse.ArgumentParser(description="Print repo-owned application deployments.") + parser.add_argument( + "--format", + choices=("lines", "csv"), + default="lines", + help="Output format for deployment names.", + ) + args = parser.parse_args() + + if args.format == "csv": + print(",".join(REPO_DEPLOYMENTS)) + else: + for deployment in REPO_DEPLOYMENTS: + print(deployment) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/test/repo_deployments_test.py b/test/repo_deployments_test.py new file mode 100644 index 0000000..1b77a26 --- /dev/null +++ b/test/repo_deployments_test.py @@ -0,0 +1,16 @@ +import unittest + +from scripts.deploy.repo_deployments import REPO_DEPLOYMENTS + + +class RepoDeploymentsTest(unittest.TestCase): + def test_repo_deployments_include_runtime_authority_and_dashboard(self): + self.assertIn("ops-sentinel", REPO_DEPLOYMENTS) + self.assertIn("operator-dashboard", REPO_DEPLOYMENTS) + + def test_repo_deployments_are_unique(self): + self.assertEqual(len(REPO_DEPLOYMENTS), len(set(REPO_DEPLOYMENTS))) + + +if __name__ == "__main__": + unittest.main()