7.3 KiB
Plan: Auto-deploy qbit-maid to Komodo from Gitea Actions
Status: proposal — no workflow changes made yet.
Branch: dev-komodo-deploy
Goal
When a commit lands on main (or a dev* branch), the image is built and pushed
to git.jonb.io/jblu/qbit-maid, and then the matching Komodo resource is
redeployed automatically — with no manual click in the Komodo UI.
Current state
Two Gitea Actions workflows exist, both build-and-push only:
| File | Trigger | Tag pushed |
|---|---|---|
.gitea/workflows/docker.yml |
push to main |
git.jonb.io/jblu/qbit-maid:latest |
.gitea/workflows/docker-dev.yml |
push to dev* |
git.jonb.io/jblu/qbit-maid:dev |
Neither workflow talks to Komodo. There are currently no Komodo references anywhere in the repo, so the Komodo side (stack/deployment resource, registry credentials) is assumed to be configured out-of-band in the Komodo UI.
Design options
| # | Approach | Ordering | CI change | Notes |
|---|---|---|---|---|
| A | Gitea Actions step calls Komodo API (POST /execute/DeployStack) |
✅ deploy strictly after image push | small | Needs API key/secret as Gitea secrets; runner must reach Komodo |
| B | Gitea repo webhook → Komodo /listener/github/stack/<name>/deploy |
❌ fires on push, races the image build | none | Komodo may pull the old :latest; needs KOMODO_WEBHOOK_SECRET |
| C | Komodo Build resource builds from the Gitea repo, deploy attached | ✅ | removes CI build | Moves building off Gitea Actions; bigger change |
| D | Komodo Auto Update (poll registry for new :latest digest) |
⚠️ up to poll interval late | none | Zero-effort safety net, not "on push" |
Recommendation: A, with D enabled as a backstop.
A gives deterministic ordering — the deploy can only start once the push step has
succeeded. D costs nothing and catches any deploy that A misses (e.g. a manual
docker push, or a failed API call).
B is the tempting "no code" option but it is genuinely racy: the webhook fires on
git push, which is before the image exists in the registry. Komodo would
redeploy the previous :latest. Only pick B if you also switch to C.
Implementation
1. Komodo-side prerequisites (manual, in the Komodo UI)
- Confirm the resource type and name — a Stack (
DeployStack) or a Deployment (Deploy). The plan below assumes a Stack namedqbit-maid. - Make sure the Komodo server can pull from
git.jonb.io. Add the registry credentials under Settings → Docker Registries (or the stack'sdocker_account), otherwise the redeploy will fail ondocker pull. - Create an API key: Settings → API Keys → New. Scope it to a user that has
Execute permission on the
qbit-maidstack. Save the key + secret. - Optional backstop: set the stack's update mode to Auto Update.
2. Gitea secrets / variables
Repo → Settings → Actions → Secrets:
| Name | Kind | Value |
|---|---|---|
KOMODO_URL |
secret | https://komo.example.com (no trailing slash) |
KOMODO_API_KEY |
secret | from step 1.3 |
KOMODO_API_SECRET |
secret | from step 1.3 |
KOMODO_STACK |
variable | qbit-maid |
KOMODO_STACK is a variable, not a secret — it is not sensitive and being able
to see it in the UI makes the workflow easier to read.
3. Workflow change
Append one step to each workflow, after Build and push. For docker.yml:
-
name: Trigger Komodo deploy
env:
KOMODO_URL: ${{ secrets.KOMODO_URL }}
KOMODO_API_KEY: ${{ secrets.KOMODO_API_KEY }}
KOMODO_API_SECRET: ${{ secrets.KOMODO_API_SECRET }}
KOMODO_STACK: ${{ vars.KOMODO_STACK }}
run: |
set -euo pipefail
response=$(curl -sS -w '\n%{http_code}' \
-X POST "${KOMODO_URL}/execute/DeployStack" \
-H 'Content-Type: application/json' \
-H "x-api-key: ${KOMODO_API_KEY}" \
-H "x-api-secret: ${KOMODO_API_SECRET}" \
-d "{\"stack\": \"${KOMODO_STACK}\"}")
body=$(printf '%s' "$response" | sed '$d')
code=$(printf '%s' "$response" | tail -n1)
echo "Komodo responded HTTP ${code}"
echo "$body"
[ "$code" = "200" ] || exit 1
docker-dev.yml is identical except KOMODO_STACK points at the dev stack
(e.g. qbit-maid-dev), so dev pushes never touch production.
Notes on the API call:
- Endpoint is
POST /execute/DeployStackwith the params object as the body. The equivalent generic form isPOST /executewith{"type": "DeployStack", "params": {"stack": "..."}}. - Auth headers are
x-api-keyandx-api-secret. DeployStackaccepts{"stack": "<id or name>", "services": [], "stop_time": null}. Use the resource id rather than the name if the name is likely to change.- The call returns immediately with an
Updateobject; the deploy runs asynchronously. A200means "queued", not "deployed". See step 4.
4. Optional: wait for the deploy to actually finish
If you want the workflow to go red when the deploy fails, poll
POST /read/GetUpdate with {"update_id": "<id>"} until status is Complete,
then check success. Sketch:
update_id=$(printf '%s' "$body" | python3 -c 'import sys,json; print(json.load(sys.stdin)["_id"]["$oid"])')
for _ in $(seq 1 60); do
sleep 5
u=$(curl -sS -X POST "${KOMODO_URL}/read/GetUpdate" \
-H 'Content-Type: application/json' \
-H "x-api-key: ${KOMODO_API_KEY}" \
-H "x-api-secret: ${KOMODO_API_SECRET}" \
-d "{\"update_id\": \"${update_id}\"}")
status=$(printf '%s' "$u" | python3 -c 'import sys,json; print(json.load(sys.stdin)["status"])')
[ "$status" = "Complete" ] || continue
printf '%s' "$u" | python3 -c 'import sys,json; sys.exit(0 if json.load(sys.stdin)["success"] else 1)'
done
echo "Timed out waiting for deploy"; exit 1
Recommendation: ship step 3 first, add step 4 only if you find yourself wanting the CI signal. Polling adds up to 5 minutes of runner time per deploy.
5. Testing
- Push
dev-komodo-deploy— this matchesdev*, sodocker-dev.ymlruns and exercises the new step against the dev stack. Confirm the Komodo UI shows a newDeployupdate with operator = your API key's user. - Verify the running container actually picked up the new image
(
docker inspectthe image digest, or check the app's log line). - Merge to
mainand confirm the production stack redeploys. - Negative test: temporarily set
KOMODO_API_SECRETto a bad value and confirm the step fails loudly rather than silently passing.
6. Rollback
The change is additive — delete the Trigger Komodo deploy step and the
workflows are back to build-and-push only. No Komodo state is modified by the
workflow beyond triggering a deploy.
Open questions
These need answers before the workflow change is written:
- Stack or Deployment? Determines
DeployStackvsDeploy. - Resource name(s) for prod and dev.
- Is Komodo reachable from the Gitea runner? If Komodo is on a private network and the runner is not, option A is out and we fall back to B+C or D.
- Does Komodo already have registry credentials for
git.jonb.io? If not, the first automated deploy will fail at pull time. - Should dev deploys be automatic? Some people prefer dev to stay manual.