Docker images #23
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Three Alpine-based images: raw (uvicorn), nginx (nginx+mkcert+uvicorn), granian (granian+mkcert). | |
| # Triggers: | |
| # - workflow_call: CI (main/testing) and release → always build, test, push (parallel jobs). | |
| # - workflow_dispatch: manual — test-only (no Hub) or full publish (parallel jobs, same as call). | |
| name: Docker images | |
| on: | |
| workflow_call: | |
| inputs: | |
| source_ref: | |
| description: Branch, tag, or SHA to build | |
| type: string | |
| required: false | |
| default: "" | |
| mode: | |
| description: >- | |
| build-and-test-only — amd64 build + in-container tests, no Docker Hub. | |
| publish — build, smoke-test, and push raw + nginx + granian images. | |
| type: string | |
| required: false | |
| default: publish | |
| workflow_dispatch: | |
| inputs: | |
| source_ref: | |
| description: Branch, tag, or SHA to build | |
| type: string | |
| required: true | |
| default: main | |
| mode: | |
| description: >- | |
| build-and-test-only — amd64 build + in-container tests, no Docker Hub. | |
| publish — same as CI/release (push raw + nginx + granian, multi-arch). | |
| type: choice | |
| required: true | |
| options: | |
| - build-and-test-only | |
| - publish | |
| default: build-and-test-only | |
| permissions: | |
| contents: read | |
| env: | |
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true | |
| # Required since v0.0.10: proxbox-api refuses to start without an | |
| # encryption key (or PROXBOX_ALLOW_PLAINTEXT_CREDENTIALS=1). Use a | |
| # constant CI-only key so smoke containers go through the same code | |
| # path operators use in production. | |
| PROXBOX_ENCRYPTION_KEY: proxbox-ci-only-encryption-key-not-a-secret | |
| jobs: | |
| # Sequential local test (manual); no registry login or push. | |
| docker-manual: | |
| if: inputs.mode == 'build-and-test-only' | |
| name: Docker — build and test only (manual) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Resolve checkout ref | |
| id: checkout_source | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| SOURCE_REF: ${{ inputs.source_ref != '' && inputs.source_ref || github.sha }} | |
| run: | | |
| set -euo pipefail | |
| encoded_ref="$(python3 - <<'PY' | |
| import os | |
| from urllib.parse import quote | |
| print(quote(os.environ["SOURCE_REF"], safe="")) | |
| PY | |
| )" | |
| sha="$(gh api "repos/${{ github.repository }}/commits/$encoded_ref" --jq .sha)" | |
| echo "sha=$sha" >> "$GITHUB_OUTPUT" | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ steps.checkout_source.outputs.sha }} | |
| - name: Validate pyproject version vs tag ref | |
| id: version | |
| env: | |
| GIT_REF: ${{ inputs.source_ref != '' && inputs.source_ref || github.sha }} | |
| run: | | |
| python3 <<'PY' | |
| import os | |
| import pathlib | |
| import tomllib | |
| git_ref = os.environ["GIT_REF"].strip() | |
| version = tomllib.loads( | |
| pathlib.Path("pyproject.toml").read_text(encoding="utf-8") | |
| )["project"]["version"] | |
| expected_tag = f"v{version}" | |
| if git_ref.startswith("v") and git_ref != expected_tag: | |
| raise SystemExit( | |
| f"git_ref/version mismatch: ref={git_ref!r}, expected {expected_tag!r}" | |
| ) | |
| with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as fh: | |
| fh.write(f"tag={version}\n") | |
| PY | |
| - name: Keep image tag in env | |
| run: echo "IMAGE_TAG=${{ steps.version.outputs.tag }}" >> $GITHUB_ENV | |
| - name: Create Docker test script | |
| run: | | |
| cat <<'EOF' > /tmp/docker-tests.sh | |
| #!/bin/sh | |
| set -eux | |
| # tests/ is bind-mounted read-only; keep bytecode out of the tree | |
| export PYTHONPYCACHEPREFIX=/tmp/pycache | |
| mkdir -p "$PYTHONPYCACHEPREFIX" | |
| export PROXBOX_SKIP_NETBOX_BOOTSTRAP=1 | |
| uv sync --frozen --extra test --group dev | |
| uv run ruff check . | |
| uv run ruff format --check . | |
| uv run ty check proxbox_api/types proxbox_api/utils/retry.py proxbox_api/schemas/sync.py | |
| uv run python -m compileall proxbox_api scripts tests | |
| uv run python -c "import proxbox_api.main" | |
| uv run python -c "from proxbox_api.proxmox_to_netbox.proxmox_schema import load_proxmox_generated_openapi; assert load_proxmox_generated_openapi().get('paths')" | |
| uv run pytest --cov=proxbox_api --cov-report=term-missing --cov-report=xml tests --ignore=tests/e2e \ | |
| --deselect=tests/test_qemu_guest_agent_sync.py::test_vm_sync_prefers_guest_agent_ip \ | |
| --deselect=tests/test_qemu_guest_agent_sync.py::test_vm_sync_falls_back_to_config_when_guest_agent_unavailable \ | |
| --deselect=tests/test_qemu_guest_agent_sync.py::test_vm_sync_skips_guest_agent_call_when_disabled \ | |
| --deselect=tests/test_qemu_guest_agent_sync.py::test_vm_sync_ignore_ipv6_link_local_false_includes_fe80 | |
| EOF | |
| chmod +x /tmp/docker-tests.sh | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| with: | |
| driver: docker-container | |
| # --- raw image --- | |
| - name: Build amd64 raw image for in-container tests | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| target: raw | |
| platforms: linux/amd64 | |
| push: false | |
| load: true | |
| tags: emersonfelipesp/proxbox-api:${{ env.IMAGE_TAG }}-raw-pre | |
| cache-from: type=gha,scope=raw | |
| cache-to: type=gha,mode=max,scope=raw | |
| - name: Run tests in raw image | |
| run: | | |
| docker run --rm \ | |
| -e PROXBOX_ENCRYPTION_KEY \ | |
| -w /workspace \ | |
| -v /tmp/docker-tests.sh:/tmp/docker-tests.sh \ | |
| -v "${{ github.workspace }}:/workspace" \ | |
| emersonfelipesp/proxbox-api:${{ env.IMAGE_TAG }}-raw-pre \ | |
| /tmp/docker-tests.sh | |
| - name: Smoke-test raw image startup | |
| run: | | |
| set -eux | |
| docker rm -f proxbox-smoke-raw 2>/dev/null || true | |
| docker run -d --name proxbox-smoke-raw \ | |
| -p 127.0.0.1:18080:8000 \ | |
| -e PROXBOX_SKIP_NETBOX_BOOTSTRAP=1 \ | |
| -e PROXBOX_ENCRYPTION_KEY \ | |
| emersonfelipesp/proxbox-api:${{ env.IMAGE_TAG }}-raw-pre | |
| for i in $(seq 1 40); do | |
| if curl -fsS --max-time 5 http://127.0.0.1:18080/ >/dev/null 2>&1; then | |
| docker rm -f proxbox-smoke-raw | |
| exit 0 | |
| fi | |
| sleep 2 | |
| done | |
| docker logs proxbox-smoke-raw | |
| docker rm -f proxbox-smoke-raw | |
| exit 1 | |
| # --- nginx image --- | |
| - name: Build amd64 nginx image for in-container tests | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| target: nginx | |
| platforms: linux/amd64 | |
| push: false | |
| load: true | |
| tags: emersonfelipesp/proxbox-api:${{ env.IMAGE_TAG }}-nginx-pre | |
| cache-from: type=gha,scope=nginx | |
| cache-to: type=gha,mode=max,scope=nginx | |
| - name: Run tests in nginx image | |
| run: | | |
| docker run --rm \ | |
| -e PROXBOX_ENCRYPTION_KEY \ | |
| -w /workspace \ | |
| -v /tmp/docker-tests.sh:/tmp/docker-tests.sh \ | |
| -v "${{ github.workspace }}:/workspace" \ | |
| emersonfelipesp/proxbox-api:${{ env.IMAGE_TAG }}-nginx-pre \ | |
| /tmp/docker-tests.sh | |
| - name: Smoke-test nginx image startup | |
| run: | | |
| set -eux | |
| docker rm -f proxbox-smoke-nginx 2>/dev/null || true | |
| docker run -d --name proxbox-smoke-nginx \ | |
| -p 127.0.0.1:18443:8000 \ | |
| -e PROXBOX_SKIP_NETBOX_BOOTSTRAP=1 \ | |
| -e PROXBOX_ENCRYPTION_KEY \ | |
| emersonfelipesp/proxbox-api:${{ env.IMAGE_TAG }}-nginx-pre | |
| for i in $(seq 1 40); do | |
| if curl -kfsS --max-time 5 https://127.0.0.1:18443/ >/dev/null 2>&1; then | |
| docker rm -f proxbox-smoke-nginx | |
| exit 0 | |
| fi | |
| sleep 2 | |
| done | |
| docker logs proxbox-smoke-nginx | |
| docker rm -f proxbox-smoke-nginx | |
| exit 1 | |
| # --- granian image --- | |
| - name: Build amd64 granian image for in-container tests | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| target: granian | |
| platforms: linux/amd64 | |
| push: false | |
| load: true | |
| tags: emersonfelipesp/proxbox-api:${{ env.IMAGE_TAG }}-granian-pre | |
| cache-from: type=gha,scope=granian | |
| cache-to: type=gha,mode=max,scope=granian | |
| - name: Run tests in granian image | |
| run: | | |
| docker run --rm \ | |
| -e PROXBOX_ENCRYPTION_KEY \ | |
| -w /workspace \ | |
| -v /tmp/docker-tests.sh:/tmp/docker-tests.sh \ | |
| -v "${{ github.workspace }}:/workspace" \ | |
| emersonfelipesp/proxbox-api:${{ env.IMAGE_TAG }}-granian-pre \ | |
| /tmp/docker-tests.sh | |
| - name: Smoke-test granian image startup | |
| run: | | |
| set -eux | |
| docker rm -f proxbox-smoke-granian 2>/dev/null || true | |
| docker run -d --name proxbox-smoke-granian \ | |
| -p 127.0.0.1:18444:8000 \ | |
| -e PROXBOX_SKIP_NETBOX_BOOTSTRAP=1 \ | |
| -e PROXBOX_ENCRYPTION_KEY \ | |
| emersonfelipesp/proxbox-api:${{ env.IMAGE_TAG }}-granian-pre | |
| for i in $(seq 1 40); do | |
| if curl -kfsS --max-time 5 https://127.0.0.1:18444/ >/dev/null 2>&1; then | |
| docker rm -f proxbox-smoke-granian | |
| exit 0 | |
| fi | |
| sleep 2 | |
| done | |
| docker logs proxbox-smoke-granian | |
| docker rm -f proxbox-smoke-granian | |
| exit 1 | |
| # Parallel publish jobs — one per image variant. | |
| docker-raw: | |
| if: inputs.mode == 'publish' | |
| name: Docker Hub — raw (uvicorn only, HTTP) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Resolve checkout ref | |
| id: checkout_source | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| SOURCE_REF: ${{ inputs.source_ref != '' && inputs.source_ref || github.sha }} | |
| run: | | |
| set -euo pipefail | |
| encoded_ref="$(python3 - <<'PY' | |
| import os | |
| from urllib.parse import quote | |
| print(quote(os.environ["SOURCE_REF"], safe="")) | |
| PY | |
| )" | |
| sha="$(gh api "repos/${{ github.repository }}/commits/$encoded_ref" --jq .sha)" | |
| echo "sha=$sha" >> "$GITHUB_OUTPUT" | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ steps.checkout_source.outputs.sha }} | |
| - name: Resolve source revision | |
| id: source | |
| run: echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" | |
| - name: Set up QEMU | |
| uses: docker/setup-qemu-action@v3 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| with: | |
| driver: docker-container | |
| - name: Log in to Docker Hub | |
| uses: docker/login-action@v3 | |
| with: | |
| username: ${{ secrets.DOCKERHUB_USERNAME }} | |
| password: ${{ secrets.DOCKERHUB_TOKEN }} | |
| - name: Resolve image version from pyproject | |
| id: version | |
| env: | |
| GIT_REF: ${{ inputs.source_ref != '' && inputs.source_ref || github.ref_name }} | |
| run: | | |
| python3 <<'PY' | |
| import os | |
| import pathlib | |
| import tomllib | |
| data = tomllib.loads(pathlib.Path("pyproject.toml").read_text(encoding="utf-8")) | |
| version = data["project"]["version"] | |
| expected_tag = f"v{version}" | |
| git_ref = os.environ["GIT_REF"].strip() | |
| if git_ref.startswith("v") and git_ref != expected_tag: | |
| raise SystemExit( | |
| f"git ref / pyproject mismatch: ref={git_ref!r}, expected {expected_tag!r}" | |
| ) | |
| with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as fh: | |
| fh.write(f"tag={version}\n") | |
| PY | |
| - name: Keep image tag in env | |
| run: echo "IMAGE_TAG=${{ steps.version.outputs.tag }}" >> $GITHUB_ENV | |
| - name: Build amd64 raw image for pre-release tests | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| target: raw | |
| platforms: linux/amd64 | |
| push: false | |
| load: true | |
| tags: emersonfelipesp/proxbox-api:${{ env.IMAGE_TAG }}-raw-pre | |
| cache-from: type=gha,scope=raw | |
| cache-to: type=gha,mode=max,scope=raw | |
| - name: Smoke-test raw image before push | |
| run: | | |
| set -eux | |
| docker rm -f proxbox-smoke-raw 2>/dev/null || true | |
| docker run -d --name proxbox-smoke-raw \ | |
| -p 127.0.0.1:18080:8000 \ | |
| -e PROXBOX_SKIP_NETBOX_BOOTSTRAP=1 \ | |
| -e PROXBOX_ENCRYPTION_KEY \ | |
| emersonfelipesp/proxbox-api:${{ env.IMAGE_TAG }}-raw-pre | |
| for i in $(seq 1 40); do | |
| if curl -fsS --max-time 5 http://127.0.0.1:18080/ >/dev/null 2>&1; then | |
| docker rm -f proxbox-smoke-raw | |
| exit 0 | |
| fi | |
| sleep 2 | |
| done | |
| docker logs proxbox-smoke-raw | |
| docker rm -f proxbox-smoke-raw | |
| exit 1 | |
| - name: Build and push raw Docker image | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| target: raw | |
| platforms: linux/amd64,linux/arm64 | |
| push: true | |
| tags: | | |
| emersonfelipesp/proxbox-api:${{ env.IMAGE_TAG }} | |
| emersonfelipesp/proxbox-api:latest | |
| emersonfelipesp/proxbox-api:sha-${{ steps.source.outputs.sha }} | |
| cache-from: type=gha,scope=raw | |
| cache-to: type=gha,mode=max,scope=raw | |
| - name: Smoke-test pushed raw images | |
| run: | | |
| set -eux | |
| for TAG in "${{ env.IMAGE_TAG }}" latest "sha-${{ steps.source.outputs.sha }}"; do | |
| NAME=$(printf 'proxbox-smoke-%s' "$TAG" | tr '.:' '-') | |
| docker rm -f "$NAME" 2>/dev/null || true | |
| docker run -d --name "$NAME" \ | |
| -p 127.0.0.1:18080:8000 \ | |
| -e PROXBOX_SKIP_NETBOX_BOOTSTRAP=1 \ | |
| -e PROXBOX_ENCRYPTION_KEY \ | |
| emersonfelipesp/proxbox-api:"$TAG" | |
| ok=0 | |
| for i in $(seq 1 40); do | |
| if curl -fsS --max-time 5 http://127.0.0.1:18080/ >/dev/null 2>&1; then | |
| ok=1 | |
| break | |
| fi | |
| sleep 2 | |
| done | |
| if [ "$ok" -ne 1 ]; then | |
| docker logs "$NAME" | |
| docker rm -f "$NAME" | |
| exit 1 | |
| fi | |
| docker rm -f "$NAME" | |
| done | |
| docker-nginx: | |
| if: inputs.mode == 'publish' | |
| name: Docker Hub — nginx (nginx + mkcert HTTPS + uvicorn) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Resolve checkout ref | |
| id: checkout_source | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| SOURCE_REF: ${{ inputs.source_ref != '' && inputs.source_ref || github.sha }} | |
| run: | | |
| set -euo pipefail | |
| encoded_ref="$(python3 - <<'PY' | |
| import os | |
| from urllib.parse import quote | |
| print(quote(os.environ["SOURCE_REF"], safe="")) | |
| PY | |
| )" | |
| sha="$(gh api "repos/${{ github.repository }}/commits/$encoded_ref" --jq .sha)" | |
| echo "sha=$sha" >> "$GITHUB_OUTPUT" | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ steps.checkout_source.outputs.sha }} | |
| - name: Resolve source revision | |
| id: source | |
| run: echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" | |
| - name: Set up QEMU | |
| uses: docker/setup-qemu-action@v3 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| with: | |
| driver: docker-container | |
| - name: Log in to Docker Hub | |
| uses: docker/login-action@v3 | |
| with: | |
| username: ${{ secrets.DOCKERHUB_USERNAME }} | |
| password: ${{ secrets.DOCKERHUB_TOKEN }} | |
| - name: Resolve image version from pyproject | |
| id: version | |
| env: | |
| GIT_REF: ${{ inputs.source_ref != '' && inputs.source_ref || github.ref_name }} | |
| run: | | |
| python3 <<'PY' | |
| import os | |
| import pathlib | |
| import tomllib | |
| data = tomllib.loads(pathlib.Path("pyproject.toml").read_text(encoding="utf-8")) | |
| version = data["project"]["version"] | |
| expected_tag = f"v{version}" | |
| git_ref = os.environ["GIT_REF"].strip() | |
| if git_ref.startswith("v") and git_ref != expected_tag: | |
| raise SystemExit( | |
| f"git ref / pyproject mismatch: ref={git_ref!r}, expected {expected_tag!r}" | |
| ) | |
| with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as fh: | |
| fh.write(f"tag={version}\n") | |
| PY | |
| - name: Keep image tag in env | |
| run: echo "IMAGE_TAG=${{ steps.version.outputs.tag }}" >> $GITHUB_ENV | |
| - name: Build amd64 nginx image for pre-release tests | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| target: nginx | |
| platforms: linux/amd64 | |
| push: false | |
| load: true | |
| tags: emersonfelipesp/proxbox-api:${{ env.IMAGE_TAG }}-nginx-pre | |
| cache-from: type=gha,scope=nginx | |
| cache-to: type=gha,mode=max,scope=nginx | |
| - name: Smoke-test nginx image before push | |
| run: | | |
| set -eux | |
| docker rm -f proxbox-smoke-nginx 2>/dev/null || true | |
| docker run -d --name proxbox-smoke-nginx \ | |
| -p 127.0.0.1:18443:8000 \ | |
| -e PROXBOX_SKIP_NETBOX_BOOTSTRAP=1 \ | |
| -e PROXBOX_ENCRYPTION_KEY \ | |
| emersonfelipesp/proxbox-api:${{ env.IMAGE_TAG }}-nginx-pre | |
| for i in $(seq 1 40); do | |
| if curl -kfsS --max-time 5 https://127.0.0.1:18443/ >/dev/null 2>&1; then | |
| docker rm -f proxbox-smoke-nginx | |
| exit 0 | |
| fi | |
| sleep 2 | |
| done | |
| docker logs proxbox-smoke-nginx | |
| docker rm -f proxbox-smoke-nginx | |
| exit 1 | |
| - name: Build and push nginx Docker image | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| target: nginx | |
| platforms: linux/amd64,linux/arm64 | |
| push: true | |
| tags: | | |
| emersonfelipesp/proxbox-api:${{ env.IMAGE_TAG }}-nginx | |
| emersonfelipesp/proxbox-api:latest-nginx | |
| emersonfelipesp/proxbox-api:sha-${{ steps.source.outputs.sha }}-nginx | |
| cache-from: type=gha,scope=nginx | |
| cache-to: type=gha,mode=max,scope=nginx | |
| - name: Smoke-test pushed nginx images | |
| run: | | |
| set -eux | |
| for TAG in "${{ env.IMAGE_TAG }}-nginx" latest-nginx "sha-${{ steps.source.outputs.sha }}-nginx"; do | |
| NAME=$(printf 'proxbox-smoke-%s' "$TAG" | tr '.:' '-') | |
| docker rm -f "$NAME" 2>/dev/null || true | |
| docker run -d --name "$NAME" \ | |
| -p 127.0.0.1:18443:8000 \ | |
| -e PROXBOX_SKIP_NETBOX_BOOTSTRAP=1 \ | |
| -e PROXBOX_ENCRYPTION_KEY \ | |
| emersonfelipesp/proxbox-api:"$TAG" | |
| ok=0 | |
| for i in $(seq 1 40); do | |
| if curl -kfsS --max-time 5 https://127.0.0.1:18443/ >/dev/null 2>&1; then | |
| ok=1 | |
| break | |
| fi | |
| sleep 2 | |
| done | |
| if [ "$ok" -ne 1 ]; then | |
| docker logs "$NAME" | |
| docker rm -f "$NAME" | |
| exit 1 | |
| fi | |
| docker rm -f "$NAME" | |
| done | |
| docker-granian: | |
| if: inputs.mode == 'publish' | |
| name: Docker Hub — granian (granian + mkcert HTTPS) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Resolve checkout ref | |
| id: checkout_source | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| SOURCE_REF: ${{ inputs.source_ref != '' && inputs.source_ref || github.sha }} | |
| run: | | |
| set -euo pipefail | |
| encoded_ref="$(python3 - <<'PY' | |
| import os | |
| from urllib.parse import quote | |
| print(quote(os.environ["SOURCE_REF"], safe="")) | |
| PY | |
| )" | |
| sha="$(gh api "repos/${{ github.repository }}/commits/$encoded_ref" --jq .sha)" | |
| echo "sha=$sha" >> "$GITHUB_OUTPUT" | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ steps.checkout_source.outputs.sha }} | |
| - name: Resolve source revision | |
| id: source | |
| run: echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" | |
| - name: Set up QEMU | |
| uses: docker/setup-qemu-action@v3 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| with: | |
| driver: docker-container | |
| - name: Log in to Docker Hub | |
| uses: docker/login-action@v3 | |
| with: | |
| username: ${{ secrets.DOCKERHUB_USERNAME }} | |
| password: ${{ secrets.DOCKERHUB_TOKEN }} | |
| - name: Resolve image version from pyproject | |
| id: version | |
| env: | |
| GIT_REF: ${{ inputs.source_ref != '' && inputs.source_ref || github.ref_name }} | |
| run: | | |
| python3 <<'PY' | |
| import os | |
| import pathlib | |
| import tomllib | |
| data = tomllib.loads(pathlib.Path("pyproject.toml").read_text(encoding="utf-8")) | |
| version = data["project"]["version"] | |
| expected_tag = f"v{version}" | |
| git_ref = os.environ["GIT_REF"].strip() | |
| if git_ref.startswith("v") and git_ref != expected_tag: | |
| raise SystemExit( | |
| f"git ref / pyproject mismatch: ref={git_ref!r}, expected {expected_tag!r}" | |
| ) | |
| with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as fh: | |
| fh.write(f"tag={version}\n") | |
| PY | |
| - name: Keep image tag in env | |
| run: echo "IMAGE_TAG=${{ steps.version.outputs.tag }}" >> $GITHUB_ENV | |
| - name: Build amd64 granian image for pre-release tests | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| target: granian | |
| platforms: linux/amd64 | |
| push: false | |
| load: true | |
| tags: emersonfelipesp/proxbox-api:${{ env.IMAGE_TAG }}-granian-pre | |
| cache-from: type=gha,scope=granian | |
| cache-to: type=gha,mode=max,scope=granian | |
| - name: Smoke-test granian image before push | |
| run: | | |
| set -eux | |
| docker rm -f proxbox-smoke-granian 2>/dev/null || true | |
| docker run -d --name proxbox-smoke-granian \ | |
| -p 127.0.0.1:18444:8000 \ | |
| -e PROXBOX_SKIP_NETBOX_BOOTSTRAP=1 \ | |
| -e PROXBOX_ENCRYPTION_KEY \ | |
| emersonfelipesp/proxbox-api:${{ env.IMAGE_TAG }}-granian-pre | |
| for i in $(seq 1 40); do | |
| if curl -kfsS --max-time 5 https://127.0.0.1:18444/ >/dev/null 2>&1; then | |
| docker rm -f proxbox-smoke-granian | |
| exit 0 | |
| fi | |
| sleep 2 | |
| done | |
| docker logs proxbox-smoke-granian | |
| docker rm -f proxbox-smoke-granian | |
| exit 1 | |
| - name: Build and push granian Docker image | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| target: granian | |
| platforms: linux/amd64,linux/arm64 | |
| push: true | |
| tags: | | |
| emersonfelipesp/proxbox-api:${{ env.IMAGE_TAG }}-granian | |
| emersonfelipesp/proxbox-api:latest-granian | |
| emersonfelipesp/proxbox-api:sha-${{ steps.source.outputs.sha }}-granian | |
| cache-from: type=gha,scope=granian | |
| cache-to: type=gha,mode=max,scope=granian | |
| - name: Smoke-test pushed granian images | |
| run: | | |
| set -eux | |
| for TAG in "${{ env.IMAGE_TAG }}-granian" latest-granian "sha-${{ steps.source.outputs.sha }}-granian"; do | |
| NAME=$(printf 'proxbox-smoke-%s' "$TAG" | tr '.:' '-') | |
| docker rm -f "$NAME" 2>/dev/null || true | |
| docker run -d --name "$NAME" \ | |
| -p 127.0.0.1:18444:8000 \ | |
| -e PROXBOX_SKIP_NETBOX_BOOTSTRAP=1 \ | |
| -e PROXBOX_ENCRYPTION_KEY \ | |
| emersonfelipesp/proxbox-api:"$TAG" | |
| ok=0 | |
| for i in $(seq 1 40); do | |
| if curl -kfsS --max-time 5 https://127.0.0.1:18444/ >/dev/null 2>&1; then | |
| ok=1 | |
| break | |
| fi | |
| sleep 2 | |
| done | |
| if [ "$ok" -ne 1 ]; then | |
| docker logs "$NAME" | |
| docker rm -f "$NAME" | |
| exit 1 | |
| fi | |
| docker rm -f "$NAME" | |
| done |