Wire push deployment for all services
All checks were successful
deploy / deploy (push) Successful in 30s

Proof: Push-driven repo workflow now renders and applies the built image across all repo-owned deployments instead of resetting services to placeholder images or relying on a manual rollout list.

Assumptions: All repo-owned workloads that should roll on push carry app.kubernetes.io/part-of= in the manifests, and namespace bootstrap can happen before the image build without applying placeholder deployments.

Still fake: This turn fixes the repo deployment path in code, but I have not yet exercised the new Forgejo workflow end-to-end from a fresh push on the cluster.
This commit is contained in:
philipp 2026-04-08 21:47:51 +02:00
parent 3c1ad1dde4
commit deda0002ab
5 changed files with 92 additions and 15 deletions

View file

@ -14,30 +14,29 @@ jobs:
REGISTRY_HOST: ${{ vars.REGISTRY_HOST }}
PROJECT_NAME: ${{ vars.PROJECT_NAME || 'unrip' }}
PROJECT_NAMESPACE: ${{ vars.PROJECT_NAMESPACE || vars.PROJECT_NAME || 'unrip' }}
PROJECT_DEPLOYMENTS: ${{ vars.PROJECT_DEPLOYMENTS || 'near-intents-ingest,market-reference-ingest,liquidity-manager,inventory-sync,history-writer,strategy-engine,trade-executor' }}
PROJECT_REGISTRY_SECRET_NAME: ${{ vars.PROJECT_REGISTRY_SECRET_NAME || format('{0}-registry-creds', vars.PROJECT_NAME || 'unrip') }}
REPO_CLONE_URL: ${{ github.server_url }}/${{ github.repository }}.git
steps:
- name: Install tooling
run: |
if command -v git >/dev/null 2>&1 && command -v kubectl >/dev/null 2>&1; then
if command -v git >/dev/null 2>&1 && command -v kubectl >/dev/null 2>&1 && command -v python3 >/dev/null 2>&1; then
exit 0
fi
if command -v apk >/dev/null 2>&1; then
apk add --no-cache git kubectl
apk add --no-cache git kubectl python3
exit 0
fi
if command -v apt-get >/dev/null 2>&1; then
apt-get update
apt-get install -y git curl ca-certificates
apt-get install -y git curl ca-certificates python3
curl -fsSLo /usr/local/bin/kubectl "https://dl.k8s.io/release/$(curl -fsSL https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x /usr/local/bin/kubectl
exit 0
fi
echo "missing git/kubectl and no supported package manager found" >&2
echo "missing git/kubectl/python3 and no supported package manager found" >&2
exit 1
- name: Prepare workspace
@ -77,9 +76,9 @@ jobs:
echo "BUILD_JOB=$BUILD_JOB"
} >> "$GITHUB_ENV"
- name: Apply manifests
- name: Ensure namespace exists
run: |
kubectl apply -k "$WORKSPACE_DIR/deploy/k8s/base"
kubectl apply -f "$WORKSPACE_DIR/deploy/k8s/base/namespace.yaml"
- name: Build and push image in-cluster
env:
@ -144,12 +143,16 @@ jobs:
kubectl -n "$PROJECT_NAMESPACE" wait --for=condition=Complete --timeout=20m "job/$BUILD_JOB"
kubectl -n "$PROJECT_NAMESPACE" logs "job/$BUILD_JOB"
- name: Roll deployments to new image
- name: Apply release manifests and wait for rollout
run: |
printf '%s\n' "$PROJECT_DEPLOYMENTS" | tr ',' '\n' | while IFS= read -r deployment; do
deployment="$(echo "$deployment" | xargs)"
[ -n "$deployment" ] || continue
kubectl kustomize "$WORKSPACE_DIR/deploy/k8s/base" \
| python3 "$WORKSPACE_DIR/scripts/deploy/render_release_manifest.py" --image "$IMAGE" \
| kubectl apply -f -
kubectl -n "$PROJECT_NAMESPACE" set image "deployment/$deployment" app="$IMAGE"
kubectl -n "$PROJECT_NAMESPACE" rollout status "deployment/$deployment" --timeout=180s
kubectl -n "$PROJECT_NAMESPACE" get deployment \
-l "app.kubernetes.io/part-of=$PROJECT_NAME" \
-o name \
| while IFS= read -r deployment; do
[ -n "$deployment" ] || continue
kubectl -n "$PROJECT_NAMESPACE" rollout status "$deployment" --timeout=180s
done

View file

@ -52,6 +52,9 @@ Read:
- Do not expand scope beyond the active implementation proof or research charter.
- No backlog generation instead of implementation.
- No scaffolding ahead of demonstrated need.
- The repository must be fully wired so every service deploys automatically from a repo push.
- Manual deployment intervention is forbidden.
- Manual `kubectl` rollout, image patching, or other ad hoc production reconciliation is forbidden except when the user explicitly asks for emergency break-glass incident handling.
- Quote collection and analytics are first-class from day one. They are not a later add-on.
- Do not present scaffolding, dashboards, placeholders, or mock flows as product progress.
- State assumptions before coding when the environment, venue, chain, or source behavior is uncertain.
@ -129,6 +132,7 @@ Real progress means the repository can do more of the active proof with validate
- storing durable inventory, pricing, decision, and execution records
- producing a real decision from live data
- making a real execution attempt through repo-controlled code
- deploying all repo-owned services automatically from push-driven repo workflow without manual cluster intervention
- proving blocked-path safety with explicit evidence
Fake progress includes:

View file

@ -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}"
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_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}"

View file

@ -0,0 +1,28 @@
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import sys
PLACEHOLDER_IMAGE = "ghcr.io/example/unrip:bootstrap"
def render_release_manifest(manifest: str, image: str) -> str:
return manifest.replace(PLACEHOLDER_IMAGE, image)
def main() -> int:
parser = argparse.ArgumentParser(
description="Render a release manifest by replacing placeholder app images."
)
parser.add_argument("--image", required=True, help="Fully qualified image reference to deploy.")
args = parser.parse_args()
source = sys.stdin.read()
sys.stdout.write(render_release_manifest(source, args.image))
return 0
if __name__ == "__main__":
raise SystemExit(main())

View file

@ -0,0 +1,42 @@
import unittest
from scripts.deploy.render_release_manifest import render_release_manifest
class RenderReleaseManifestTest(unittest.TestCase):
def test_swaps_placeholder_images_for_release_image(self):
input_manifest = """
apiVersion: apps/v1
kind: Deployment
metadata:
name: operator-dashboard
spec:
template:
spec:
containers:
- name: app
image: ghcr.io/example/unrip:bootstrap
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: ops-sentinel
spec:
template:
spec:
containers:
- name: app
image: ghcr.io/example/unrip:bootstrap
"""
output = render_release_manifest(
input_manifest,
"registry.example/unrip:abc123",
)
self.assertIn("image: registry.example/unrip:abc123", output)
self.assertNotIn("ghcr.io/example/unrip:bootstrap", output)
if __name__ == "__main__":
unittest.main()