Add GHA build + Woodpecker deploy pipelines
Context ------- Matches the pattern used by claude-memory-mcp (infra CLAUDE.md §CI/CD). GHA is cheap and parallel — build+push happens there. Woodpecker runs in-cluster and has kubectl access, so it owns the `kubectl set image` step. This change ----------- - `.github/workflows/ci.yml` — push to main runs ruff + mypy strict + pytest, then builds `viktorbarzin/broker-sync:<8-char-sha>` + `:latest` and pushes to DockerHub, then triggers Woodpecker. - `.woodpecker/deploy.yml` — kubectl set image on all 5 CronJobs in the broker-sync namespace (version probe + 4 sync jobs), then spawns a one-shot Job from the version CronJob as a smoke test and waits for completion. - Woodpecker repo ID is `TBD` — needs filling in once the repo is registered with Woodpecker (see infra CLAUDE.md Repo IDs list). The workflow skips deploy cleanly if still TBD, so this doesn't block green builds. Test plan --------- ## Automated Nothing to run locally — CI is verified by pushing and watching the run on GitHub. ## Manual Verification 1. Push this branch to GitHub, confirm `test` job runs and passes. 2. Push to `main`, confirm `build` job produces `viktorbarzin/broker-sync:<sha>` on DockerHub. 3. Register repo with Woodpecker, note the numeric repo ID, replace `TBD` in ci.yml, push again. 4. Confirm Woodpecker deploy pipeline runs `kubectl set image` on all 5 CronJobs and the smoke-test job returns `broker-sync 0.1.0`.
This commit is contained in:
parent
43d2251159
commit
18d8241c85
2 changed files with 115 additions and 0 deletions
85
.github/workflows/ci.yml
vendored
Normal file
85
.github/workflows/ci.yml
vendored
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
env:
|
||||
IMAGE_NAME: broker-sync
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.12"
|
||||
- name: Install Poetry
|
||||
run: pipx install poetry==1.8.4
|
||||
- name: Install deps
|
||||
run: poetry install --no-interaction
|
||||
- run: poetry run ruff check .
|
||||
- run: poetry run mypy broker_sync tests
|
||||
- run: poetry run pytest -q
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
needs: test
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||
outputs:
|
||||
image_tag: ${{ steps.meta.outputs.sha }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
- uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- id: meta
|
||||
run: echo "sha=$(echo ${{ github.sha }} | cut -c1-8)" >> $GITHUB_OUTPUT
|
||||
- uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile
|
||||
push: true
|
||||
platforms: linux/amd64
|
||||
tags: |
|
||||
viktorbarzin/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.sha }}
|
||||
viktorbarzin/${{ env.IMAGE_NAME }}:latest
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
deploy:
|
||||
needs: build
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Trigger Woodpecker deploy
|
||||
env:
|
||||
# TODO: replace WOODPECKER_REPO_ID with the numeric id assigned when the
|
||||
# broker-sync repo is registered with Woodpecker. See infra CLAUDE.md
|
||||
# "Woodpecker API uses numeric repo IDs".
|
||||
WOODPECKER_REPO_ID: "TBD"
|
||||
run: |
|
||||
if [ "$WOODPECKER_REPO_ID" = "TBD" ]; then
|
||||
echo "Woodpecker repo not yet registered — skipping deploy trigger."
|
||||
exit 0
|
||||
fi
|
||||
for attempt in 1 2 3; do
|
||||
STATUS=$(curl -s -o /dev/null -w "%{http_code}" -X POST \
|
||||
"https://ci.viktorbarzin.me/api/repos/${WOODPECKER_REPO_ID}/pipelines" \
|
||||
-H "Authorization: Bearer ${{ secrets.WOODPECKER_TOKEN }}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"branch":"main","variables":{"IMAGE_TAG":"${{ needs.build.outputs.image_tag }}"}}')
|
||||
if [ "$STATUS" -ge 200 ] && [ "$STATUS" -lt 300 ]; then
|
||||
echo "Woodpecker deploy triggered (HTTP $STATUS)"
|
||||
exit 0
|
||||
fi
|
||||
echo "Attempt $attempt failed (HTTP $STATUS), retrying in 30s..."
|
||||
sleep 30
|
||||
done
|
||||
echo "Failed to trigger Woodpecker deploy after 3 attempts"
|
||||
exit 1
|
||||
30
.woodpecker/deploy.yml
Normal file
30
.woodpecker/deploy.yml
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
when:
|
||||
- event: [manual, push]
|
||||
|
||||
steps:
|
||||
- name: check-vars
|
||||
image: alpine
|
||||
commands:
|
||||
- "[ -n \"$IMAGE_TAG\" ] || (echo 'IMAGE_TAG not set, skipping deploy'; exit 78)"
|
||||
|
||||
- name: deploy-cronjobs
|
||||
image: bitnami/kubectl:latest
|
||||
commands:
|
||||
# broker-sync runs as CronJobs, not a Deployment. Update each CronJob's image
|
||||
# so the next scheduled run picks it up. Running CronJob pods are untouched
|
||||
# (Kubernetes doesn't do rolling updates on CronJob templates).
|
||||
- "kubectl -n broker-sync set image cronjob/broker-sync-version broker-sync=viktorbarzin/broker-sync:${IMAGE_TAG}"
|
||||
- "kubectl -n broker-sync set image cronjob/broker-sync-trading212 broker-sync=viktorbarzin/broker-sync:${IMAGE_TAG}"
|
||||
- "kubectl -n broker-sync set image cronjob/broker-sync-imap broker-sync=viktorbarzin/broker-sync:${IMAGE_TAG}"
|
||||
- "kubectl -n broker-sync set image cronjob/broker-sync-csv broker-sync=viktorbarzin/broker-sync:${IMAGE_TAG}"
|
||||
- "kubectl -n broker-sync set image cronjob/broker-sync-fx-reconcile broker-sync=viktorbarzin/broker-sync:${IMAGE_TAG}"
|
||||
|
||||
- name: smoke-test
|
||||
image: bitnami/kubectl:latest
|
||||
commands:
|
||||
# Spawn a one-shot Job from the version CronJob with the new image to prove
|
||||
# the image is pullable and the CLI entrypoint works end-to-end.
|
||||
- "kubectl -n broker-sync create job --from=cronjob/broker-sync-version broker-sync-smoke-${CI_COMMIT_SHA:0:8}"
|
||||
- "kubectl -n broker-sync wait --for=condition=complete --timeout=300s job/broker-sync-smoke-${CI_COMMIT_SHA:0:8}"
|
||||
- "kubectl -n broker-sync logs job/broker-sync-smoke-${CI_COMMIT_SHA:0:8}"
|
||||
- "kubectl -n broker-sync delete job broker-sync-smoke-${CI_COMMIT_SHA:0:8}"
|
||||
Loading…
Add table
Add a link
Reference in a new issue