From 894d3cbce9245f28a9596640e729e3b948f3b27b Mon Sep 17 00:00:00 2001 From: Valentin Popov Date: Thu, 10 Sep 2026 19:35:14 +0400 Subject: [PATCH] ci: test and publish Docker images to Gitea --- .gitea/workflows/ci.yml | 105 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 .gitea/workflows/ci.yml diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 0000000..f25fba0 --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -0,0 +1,105 @@ +name: CI + +on: + push: + branches: ['**'] + tags: ['v*'] + pull_request: + workflow_dispatch: + +concurrency: + group: ci-${{ gitea.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + ci: + name: Test and publish + runs-on: ubuntu-latest + timeout-minutes: 30 + env: + IMAGE: code.popov.link/packager/vacationplanner2ics + TEST_IMAGE: vacationplanner2ics-test:ci-${{ gitea.run_id }} + SMOKE_CONTAINER: vacationplanner2ics-ci-${{ gitea.run_id }} + REVISION: ${{ gitea.sha }} + REF: ${{ gitea.ref }} + DOCKER_BUILDKIT: '1' + steps: + - uses: actions/checkout@v7 + with: + persist-credentials: false + + - name: Check Docker + run: docker version + + - name: Check formatting, lint and build the release binary + run: docker build --target test --tag "$TEST_IMAGE" . + + - name: Run E2E tests without network access + run: >- + docker run --rm --network none --read-only + --cap-drop ALL --security-opt no-new-privileges + --memory 256m --cpus 2 --pids-limit 128 "$TEST_IMAGE" + + - name: Build the runtime image from the tested binary + run: >- + docker build --target runtime + --label "org.opencontainers.image.revision=$REVISION" + --tag "$IMAGE:sha-$REVISION" . + + - name: Smoke-test the runtime image + run: | + set -eu + docker run -d --name "$SMOKE_CONTAINER" --network none --read-only \ + --cap-drop ALL --security-opt no-new-privileges \ + --memory 32m --cpus 1 --pids-limit 64 "$IMAGE:sha-$REVISION" + docker run --rm --network "container:$SMOKE_CONTAINER" --read-only \ + --cap-drop ALL --security-opt no-new-privileges "$TEST_IMAGE" python -c ' + import time, urllib.request + for attempt in range(50): + try: + with urllib.request.urlopen("http://127.0.0.1:8080/healthz", timeout=1) as response: + assert response.status == 200 and response.read() == b"ok" + break + except OSError: + if attempt == 49: + raise + time.sleep(0.1) + print("Runtime HTTP smoke test passed")' + docker image inspect --format 'Image size: {{.Size}} bytes' "$IMAGE:sha-$REVISION" + docker stats --no-stream --format 'Memory: {{.MemUsage}}; CPU: {{.CPUPerc}}' "$SMOKE_CONTAINER" + docker stop --time 5 "$SMOKE_CONTAINER" + test "$(docker inspect --format '{{.State.ExitCode}}' "$SMOKE_CONTAINER")" = 0 + + - name: Publish to Gitea Container Registry + if: >- + gitea.event_name != 'pull_request' && + (gitea.ref == 'refs/heads/master' || startsWith(gitea.ref, 'refs/tags/v')) + env: + REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }} + run: | + set -eu + if [ -z "$REGISTRY_TOKEN" ]; then + echo 'Add the REGISTRY_TOKEN Actions secret: a packager PAT with write:package.' >&2 + exit 1 + fi + export DOCKER_CONFIG + DOCKER_CONFIG="$(mktemp -d)" + trap 'rm -rf "$DOCKER_CONFIG"' EXIT + printf '%s' "$REGISTRY_TOKEN" | docker login code.popov.link --username packager --password-stdin + docker push "$IMAGE:sha-$REVISION" + if [ "$REF" = refs/heads/master ]; then + docker tag "$IMAGE:sha-$REVISION" "$IMAGE:latest" + docker push "$IMAGE:latest" + else + docker tag "$IMAGE:sha-$REVISION" "$IMAGE:${REF#refs/tags/}" + docker push "$IMAGE:${REF#refs/tags/}" + fi + + - name: Remove temporary containers and image tags + if: always() + run: | + docker rm -f "$SMOKE_CONTAINER" 2>/dev/null || true + docker image rm "$TEST_IMAGE" "$IMAGE:sha-$REVISION" 2>/dev/null || true