From cd0c0ab416aea08b8889590c48eba21eb03cec7a Mon Sep 17 00:00:00 2001 From: zwitschi Date: Fri, 14 Nov 2025 19:21:48 +0100 Subject: [PATCH 1/4] fix(ci-build): update conditions for push permissions in CI workflow --- .gitea/workflows/ci-build.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.gitea/workflows/ci-build.yml b/.gitea/workflows/ci-build.yml index 99456a5..dac73a7 100644 --- a/.gitea/workflows/ci-build.yml +++ b/.gitea/workflows/ci-build.yml @@ -27,10 +27,10 @@ jobs: event_name="${GITHUB_EVENT_NAME:-}" sha="${GITHUB_SHA:-}" - if [ "$ref_name" = "${DEFAULT_BRANCH:-main}" ]; then - echo "on_default=true" >> "$GITHUB_OUTPUT" + if [ "$ref_name" = "${DEFAULT_BRANCH:-main}" ] && [ "$event_name" != "pull_request" ]; then + echo "allow_push=true" >> "$GITHUB_OUTPUT" else - echo "on_default=false" >> "$GITHUB_OUTPUT" + echo "allow_push=false" >> "$GITHUB_OUTPUT" fi echo "ref_name=$ref_name" >> "$GITHUB_OUTPUT" @@ -41,7 +41,7 @@ jobs: uses: docker/setup-buildx-action@v3 - name: Log in to gitea registry - if: ${{ steps.meta.outputs.on_default == 'true' }} + if: ${{ steps.meta.outputs.allow_push == 'true' }} uses: docker/login-action@v3 continue-on-error: true with: @@ -55,7 +55,7 @@ jobs: REGISTRY_URL: ${{ env.REGISTRY_URL }} REGISTRY_CONTAINER_NAME: ${{ env.REGISTRY_CONTAINER_NAME }} SHA_TAG: ${{ steps.meta.outputs.sha }} - PUSH_IMAGE: ${{ steps.meta.outputs.on_default == 'true' && steps.meta.outputs.event_name != 'pull_request' && env.REGISTRY_URL != '' && env.REGISTRY_USERNAME != '' && env.REGISTRY_PASSWORD != '' }} + PUSH_IMAGE: ${{ steps.meta.outputs.allow_push == 'true' && env.REGISTRY_URL != '' && env.REGISTRY_USERNAME != '' && env.REGISTRY_PASSWORD != '' }} run: | set -eo pipefail LOG_FILE=build.log -- 2.39.5 From fa8a0651383e5fa2144d3327e3c2b2ec8e0a21c7 Mon Sep 17 00:00:00 2001 From: zwitschi Date: Fri, 14 Nov 2025 19:55:06 +0100 Subject: [PATCH 2/4] feat(ci): enhance CI workflow with metadata outputs and add Coolify deployment workflow --- .gitea/workflows/ci-build.yml | 60 +++++++++++++--- .gitea/workflows/deploy-coolify.yml | 105 ++++++++++++++++++++++++++++ 2 files changed, 155 insertions(+), 10 deletions(-) create mode 100644 .gitea/workflows/deploy-coolify.yml diff --git a/.gitea/workflows/ci-build.yml b/.gitea/workflows/ci-build.yml index dac73a7..130ffff 100644 --- a/.gitea/workflows/ci-build.yml +++ b/.gitea/workflows/ci-build.yml @@ -6,6 +6,11 @@ on: jobs: build: + outputs: + allow_push: ${{ steps.meta.outputs.allow_push }} + ref_name: ${{ steps.meta.outputs.ref_name }} + event_name: ${{ steps.meta.outputs.event_name }} + sha: ${{ steps.meta.outputs.sha }} runs-on: ubuntu-latest env: DEFAULT_BRANCH: main @@ -23,9 +28,16 @@ jobs: env: DEFAULT_BRANCH: ${{ env.DEFAULT_BRANCH }} run: | - ref_name="${GITHUB_REF_NAME:-${GITHUB_REF##*/}}" - event_name="${GITHUB_EVENT_NAME:-}" - sha="${GITHUB_SHA:-}" + git_ref="${GITEA_REF:-${GITHUB_REF:-}}" + ref_name="${GITEA_REF_NAME:-${GITHUB_REF_NAME:-}}" + if [ -z "$ref_name" ] && [ -n "$git_ref" ]; then + ref_name="${git_ref##*/}" + fi + event_name="${GITEA_EVENT_NAME:-${GITHUB_EVENT_NAME:-}}" + sha="${GITEA_SHA:-${GITHUB_SHA:-}}" + if [ -z "$sha" ]; then + sha="$(git rev-parse HEAD)" + fi if [ "$ref_name" = "${DEFAULT_BRANCH:-main}" ] && [ "$event_name" != "pull_request" ]; then echo "allow_push=true" >> "$GITHUB_OUTPUT" @@ -37,6 +49,27 @@ jobs: echo "event_name=$event_name" >> "$GITHUB_OUTPUT" echo "sha=$sha" >> "$GITHUB_OUTPUT" + - name: Validate registry configuration + shell: bash + run: | + set -euo pipefail + if [ -z "${REGISTRY_URL}" ]; then + echo "::error::REGISTRY_URL secret not configured. Configure it with your Gitea container registry host." >&2 + exit 1 + fi + server_url="${GITEA_SERVER_URL:-${GITHUB_SERVER_URL:-}}" + server_host="${server_url#http://}" + server_host="${server_host#https://}" + server_host="${server_host%%/*}" + server_host="${server_host%%:*}" + registry_host="${REGISTRY_URL#http://}" + registry_host="${registry_host#https://}" + registry_host="${registry_host%%/*}" + registry_host="${registry_host%%:*}" + if [ -n "${server_host}" ] && ! printf '%s' "${registry_host}" | grep -qi "${server_host}"; then + echo "::warning::REGISTRY_URL (${REGISTRY_URL}) does not match current Gitea host (${server_host}). Ensure this registry endpoint is managed by Gitea." >&2 + fi + - name: Set up QEMU and Buildx uses: docker/setup-buildx-action@v3 @@ -83,7 +116,7 @@ jobs: deploy: needs: build - if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request' + if: needs.build.outputs.allow_push == 'true' runs-on: ubuntu-latest env: REGISTRY_URL: ${{ secrets.REGISTRY_URL }} @@ -95,22 +128,29 @@ jobs: - name: Checkout uses: actions/checkout@v4 + - name: Capture commit metadata + id: commit_meta + run: | + set -euo pipefail + message="$(git log -1 --pretty=%B | tr '\n' ' ')" + echo "message=$message" >> "$GITHUB_OUTPUT" + - name: Set up kubectl for staging - if: github.event.head_commit && contains(github.event.head_commit.message, '[deploy staging]') + if: contains(steps.commit_meta.outputs.message, '[deploy staging]') uses: azure/k8s-set-context@v3 with: method: kubeconfig kubeconfig: ${{ env.STAGING_KUBE_CONFIG }} - name: Set up kubectl for production - if: github.event.head_commit && contains(github.event.head_commit.message, '[deploy production]') + if: contains(steps.commit_meta.outputs.message, '[deploy production]') uses: azure/k8s-set-context@v3 with: method: kubeconfig kubeconfig: ${{ env.PROD_KUBE_CONFIG }} - name: Deploy to staging - if: github.event.head_commit && contains(github.event.head_commit.message, '[deploy staging]') + if: contains(steps.commit_meta.outputs.message, '[deploy staging]') run: | kubectl set image deployment/calminer-app calminer=${REGISTRY_URL}/allucanget/${REGISTRY_CONTAINER_NAME}:latest kubectl apply -f k8s/configmap.yaml @@ -118,7 +158,7 @@ jobs: kubectl rollout status deployment/calminer-app - name: Collect staging deployment logs - if: github.event.head_commit && contains(github.event.head_commit.message, '[deploy staging]') + if: contains(steps.commit_meta.outputs.message, '[deploy staging]') run: | mkdir -p logs/deployment/staging kubectl get pods -o wide > logs/deployment/staging/pods.txt @@ -126,7 +166,7 @@ jobs: kubectl logs deployment/calminer-app --all-containers=true --tail=500 > logs/deployment/staging/calminer-app.log - name: Deploy to production - if: github.event.head_commit && contains(github.event.head_commit.message, '[deploy production]') + if: contains(steps.commit_meta.outputs.message, '[deploy production]') run: | kubectl set image deployment/calminer-app calminer=${REGISTRY_URL}/allucanget/${REGISTRY_CONTAINER_NAME}:latest kubectl apply -f k8s/configmap.yaml @@ -134,7 +174,7 @@ jobs: kubectl rollout status deployment/calminer-app - name: Collect production deployment logs - if: github.event.head_commit && contains(github.event.head_commit.message, '[deploy production]') + if: contains(steps.commit_meta.outputs.message, '[deploy production]') run: | mkdir -p logs/deployment/production kubectl get pods -o wide > logs/deployment/production/pods.txt diff --git a/.gitea/workflows/deploy-coolify.yml b/.gitea/workflows/deploy-coolify.yml new file mode 100644 index 0000000..3f02ef0 --- /dev/null +++ b/.gitea/workflows/deploy-coolify.yml @@ -0,0 +1,105 @@ +name: Deploy - Coolify + +on: + push: + branches: + - main + workflow_dispatch: + +jobs: + deploy: + runs-on: ubuntu-latest + env: + COOLIFY_BASE_URL: ${{ secrets.COOLIFY_BASE_URL }} + COOLIFY_API_TOKEN: ${{ secrets.COOLIFY_API_TOKEN }} + COOLIFY_APPLICATION_ID: ${{ secrets.COOLIFY_APPLICATION_ID }} + COOLIFY_DEPLOY_ENV: ${{ secrets.COOLIFY_DEPLOY_ENV }} + DOCKER_COMPOSE_PATH: docker-compose.prod.yml + ENV_FILE_PATH: deploy/.env + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Capture deployment context + id: context + run: | + set -euo pipefail + repo="${GITEA_REPOSITORY:-${GITHUB_REPOSITORY:-}}" + if [ -z "$repo" ]; then + repo="$(git remote get-url origin | sed 's#.*/\(.*\)\.git#\1#')" + fi + ref_name="${GITEA_REF_NAME:-${GITHUB_REF_NAME:-}}" + full_ref="${GITEA_REF:-${GITHUB_REF:-}}" + if [ -z "$ref_name" ] && [ -n "$full_ref" ]; then + ref_name="${full_ref##*/}" + fi + if [ -z "$ref_name" ]; then + ref_name="$(git rev-parse --abbrev-ref HEAD)" + fi + sha="${GITEA_SHA:-${GITHUB_SHA:-}}" + if [ -z "$sha" ]; then + sha="$(git rev-parse HEAD)" + fi + + echo "repository=$repo" >> "$GITHUB_OUTPUT" + echo "ref=${ref_name:-main}" >> "$GITHUB_OUTPUT" + echo "sha=$sha" >> "$GITHUB_OUTPUT" + + - name: Prepare compose bundle + run: | + set -euo pipefail + mkdir -p deploy + cp "$DOCKER_COMPOSE_PATH" deploy/docker-compose.yml + if [ -n "$COOLIFY_DEPLOY_ENV" ]; then + printf '%s\n' "$COOLIFY_DEPLOY_ENV" > "$ENV_FILE_PATH" + elif [ ! -f "$ENV_FILE_PATH" ]; then + echo "::error::COOLIFY_DEPLOY_ENV secret not configured and deploy/.env missing" >&2 + exit 1 + fi + + - name: Validate Coolify secrets + run: | + set -euo pipefail + missing=0 + for var in COOLIFY_BASE_URL COOLIFY_API_TOKEN COOLIFY_APPLICATION_ID; do + if [ -z "${!var}" ]; then + echo "::error::Missing required secret: $var" + missing=1 + fi + done + if [ "$missing" -eq 1 ]; then + exit 1 + fi + + - name: Trigger deployment via Coolify API + env: + HEAD_SHA: ${{ steps.context.outputs.sha }} + run: | + set -euo pipefail + api_url="$COOLIFY_BASE_URL/api/v1/applications/${COOLIFY_APPLICATION_ID}/deploy" + payload=$(jq -n --arg sha "$HEAD_SHA" '{ commitSha: $sha }') + response=$(curl -sS -w '\n%{http_code}' \ + -X POST "$api_url" \ + -H "Authorization: Bearer $COOLIFY_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d "$payload") + body=$(echo "$response" | head -n -1) + status=$(echo "$response" | tail -n1) + echo "Deploy response status: $status" + echo "$body" + printf '%s' "$body" > deploy/coolify-response.json + if [ "$status" -ge 400 ]; then + echo "::error::Deployment request failed" + exit 1 + fi + + - name: Upload deployment bundle + if: always() + uses: actions/upload-artifact@v3 + with: + name: coolify-deploy-bundle + path: | + deploy/docker-compose.yml + deploy/.env + deploy/coolify-response.json + if-no-files-found: warn -- 2.39.5 From 839399363e6a1cb751012591ceb7faa0c34bb5f2 Mon Sep 17 00:00:00 2001 From: zwitschi Date: Fri, 14 Nov 2025 20:08:26 +0100 Subject: [PATCH 3/4] fix(ci): update registry handling and add image push step in CI workflow --- .gitea/workflows/ci-build.yml | 49 ++++++++++++++++++++++++++++------- docker-compose.prod.yml | 6 +---- 2 files changed, 41 insertions(+), 14 deletions(-) diff --git a/.gitea/workflows/ci-build.yml b/.gitea/workflows/ci-build.yml index 130ffff..73ed8ad 100644 --- a/.gitea/workflows/ci-build.yml +++ b/.gitea/workflows/ci-build.yml @@ -69,6 +69,9 @@ jobs: if [ -n "${server_host}" ] && ! printf '%s' "${registry_host}" | grep -qi "${server_host}"; then echo "::warning::REGISTRY_URL (${REGISTRY_URL}) does not match current Gitea host (${server_host}). Ensure this registry endpoint is managed by Gitea." >&2 fi + registry_repository="${registry_host}/allucanget/${REGISTRY_CONTAINER_NAME}" + echo "REGISTRY_HOST=${registry_host}" >> "$GITHUB_ENV" + echo "REGISTRY_REPOSITORY=${registry_repository}" >> "$GITHUB_ENV" - name: Set up QEMU and Buildx uses: docker/setup-buildx-action@v3 @@ -76,27 +79,26 @@ jobs: - name: Log in to gitea registry if: ${{ steps.meta.outputs.allow_push == 'true' }} uses: docker/login-action@v3 - continue-on-error: true with: - registry: ${{ env.REGISTRY_URL }} + registry: ${{ env.REGISTRY_HOST }} username: ${{ env.REGISTRY_USERNAME }} password: ${{ env.REGISTRY_PASSWORD }} - name: Build image id: build-image env: - REGISTRY_URL: ${{ env.REGISTRY_URL }} + REGISTRY_REPOSITORY: ${{ env.REGISTRY_REPOSITORY }} REGISTRY_CONTAINER_NAME: ${{ env.REGISTRY_CONTAINER_NAME }} SHA_TAG: ${{ steps.meta.outputs.sha }} - PUSH_IMAGE: ${{ steps.meta.outputs.allow_push == 'true' && env.REGISTRY_URL != '' && env.REGISTRY_USERNAME != '' && env.REGISTRY_PASSWORD != '' }} + PUSH_IMAGE: ${{ steps.meta.outputs.allow_push == 'true' && env.REGISTRY_HOST != '' && env.REGISTRY_USERNAME != '' && env.REGISTRY_PASSWORD != '' }} run: | set -eo pipefail LOG_FILE=build.log if [ "${PUSH_IMAGE}" = "true" ]; then docker buildx build \ - --push \ - --tag "${REGISTRY_URL}/allucanget/${REGISTRY_CONTAINER_NAME}:latest" \ - --tag "${REGISTRY_URL}/allucanget/${REGISTRY_CONTAINER_NAME}:${SHA_TAG}" \ + --load \ + --tag "${REGISTRY_REPOSITORY}:latest" \ + --tag "${REGISTRY_REPOSITORY}:${SHA_TAG}" \ --file Dockerfile \ . 2>&1 | tee "${LOG_FILE}" else @@ -107,6 +109,20 @@ jobs: . 2>&1 | tee "${LOG_FILE}" fi + - name: Push image + if: ${{ steps.meta.outputs.allow_push == 'true' }} + env: + REGISTRY_REPOSITORY: ${{ env.REGISTRY_REPOSITORY }} + SHA_TAG: ${{ steps.meta.outputs.sha }} + run: | + set -euo pipefail + if [ -z "${REGISTRY_REPOSITORY}" ]; then + echo "::error::REGISTRY_REPOSITORY not defined; cannot push image" >&2 + exit 1 + fi + docker push "${REGISTRY_REPOSITORY}:${SHA_TAG}" + docker push "${REGISTRY_REPOSITORY}:latest" + - name: Upload docker build logs if: failure() uses: actions/upload-artifact@v4 @@ -128,6 +144,21 @@ jobs: - name: Checkout uses: actions/checkout@v4 + - name: Resolve registry repository + run: | + set -euo pipefail + if [ -z "${REGISTRY_URL}" ]; then + echo "::error::REGISTRY_URL secret not configured. Configure it with your Gitea container registry host." >&2 + exit 1 + fi + registry_host="${REGISTRY_URL#http://}" + registry_host="${registry_host#https://}" + registry_host="${registry_host%%/*}" + registry_host="${registry_host%%:*}" + registry_repository="${registry_host}/allucanget/${REGISTRY_CONTAINER_NAME}" + echo "REGISTRY_HOST=${registry_host}" >> "$GITHUB_ENV" + echo "REGISTRY_REPOSITORY=${registry_repository}" >> "$GITHUB_ENV" + - name: Capture commit metadata id: commit_meta run: | @@ -152,7 +183,7 @@ jobs: - name: Deploy to staging if: contains(steps.commit_meta.outputs.message, '[deploy staging]') run: | - kubectl set image deployment/calminer-app calminer=${REGISTRY_URL}/allucanget/${REGISTRY_CONTAINER_NAME}:latest + kubectl set image deployment/calminer-app calminer=${REGISTRY_REPOSITORY}:latest kubectl apply -f k8s/configmap.yaml kubectl apply -f k8s/secret.yaml kubectl rollout status deployment/calminer-app @@ -168,7 +199,7 @@ jobs: - name: Deploy to production if: contains(steps.commit_meta.outputs.message, '[deploy production]') run: | - kubectl set image deployment/calminer-app calminer=${REGISTRY_URL}/allucanget/${REGISTRY_CONTAINER_NAME}:latest + kubectl set image deployment/calminer-app calminer=${REGISTRY_REPOSITORY}:latest kubectl apply -f k8s/configmap.yaml kubectl apply -f k8s/secret.yaml kubectl rollout status deployment/calminer-app diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index cd3e264..e7f0f57 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -2,11 +2,7 @@ version: "3.8" services: app: - build: - context: . - dockerfile: Dockerfile - args: - APT_CACHE_URL: ${APT_CACHE_URL:-} + image: git.allucanget.biz/allucanget/calminer:latest environment: - ENVIRONMENT=production - DEBUG=false -- 2.39.5 From dae3b59af9a58700d90e852c485c6fce32befc68 Mon Sep 17 00:00:00 2001 From: zwitschi Date: Fri, 14 Nov 2025 20:14:53 +0100 Subject: [PATCH 4/4] feat(ci): add Kubernetes deployment toggle and update conditions for deployment steps --- .gitea/workflows/ci-build.yml | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/.gitea/workflows/ci-build.yml b/.gitea/workflows/ci-build.yml index 73ed8ad..f1d4146 100644 --- a/.gitea/workflows/ci-build.yml +++ b/.gitea/workflows/ci-build.yml @@ -140,6 +140,7 @@ jobs: KUBE_CONFIG: ${{ secrets.KUBE_CONFIG }} STAGING_KUBE_CONFIG: ${{ secrets.STAGING_KUBE_CONFIG }} PROD_KUBE_CONFIG: ${{ secrets.PROD_KUBE_CONFIG }} + K8S_DEPLOY_ENABLED: ${{ secrets.K8S_DEPLOY_ENABLED }} steps: - name: Checkout uses: actions/checkout@v4 @@ -159,6 +160,16 @@ jobs: echo "REGISTRY_HOST=${registry_host}" >> "$GITHUB_ENV" echo "REGISTRY_REPOSITORY=${registry_repository}" >> "$GITHUB_ENV" + - name: Report Kubernetes deployment toggle + run: | + set -euo pipefail + enabled="${K8S_DEPLOY_ENABLED:-}" + if [ "${enabled}" = "true" ]; then + echo "Kubernetes deployment is enabled for this run." + else + echo "::notice::Kubernetes deployment steps are disabled (set secrets.K8S_DEPLOY_ENABLED to 'true' to enable)." + fi + - name: Capture commit metadata id: commit_meta run: | @@ -167,21 +178,21 @@ jobs: echo "message=$message" >> "$GITHUB_OUTPUT" - name: Set up kubectl for staging - if: contains(steps.commit_meta.outputs.message, '[deploy staging]') + if: env.K8S_DEPLOY_ENABLED == 'true' && contains(steps.commit_meta.outputs.message, '[deploy staging]') uses: azure/k8s-set-context@v3 with: method: kubeconfig kubeconfig: ${{ env.STAGING_KUBE_CONFIG }} - name: Set up kubectl for production - if: contains(steps.commit_meta.outputs.message, '[deploy production]') + if: env.K8S_DEPLOY_ENABLED == 'true' && contains(steps.commit_meta.outputs.message, '[deploy production]') uses: azure/k8s-set-context@v3 with: method: kubeconfig kubeconfig: ${{ env.PROD_KUBE_CONFIG }} - name: Deploy to staging - if: contains(steps.commit_meta.outputs.message, '[deploy staging]') + if: env.K8S_DEPLOY_ENABLED == 'true' && contains(steps.commit_meta.outputs.message, '[deploy staging]') run: | kubectl set image deployment/calminer-app calminer=${REGISTRY_REPOSITORY}:latest kubectl apply -f k8s/configmap.yaml @@ -189,7 +200,7 @@ jobs: kubectl rollout status deployment/calminer-app - name: Collect staging deployment logs - if: contains(steps.commit_meta.outputs.message, '[deploy staging]') + if: env.K8S_DEPLOY_ENABLED == 'true' && contains(steps.commit_meta.outputs.message, '[deploy staging]') run: | mkdir -p logs/deployment/staging kubectl get pods -o wide > logs/deployment/staging/pods.txt @@ -197,7 +208,7 @@ jobs: kubectl logs deployment/calminer-app --all-containers=true --tail=500 > logs/deployment/staging/calminer-app.log - name: Deploy to production - if: contains(steps.commit_meta.outputs.message, '[deploy production]') + if: env.K8S_DEPLOY_ENABLED == 'true' && contains(steps.commit_meta.outputs.message, '[deploy production]') run: | kubectl set image deployment/calminer-app calminer=${REGISTRY_REPOSITORY}:latest kubectl apply -f k8s/configmap.yaml @@ -205,7 +216,7 @@ jobs: kubectl rollout status deployment/calminer-app - name: Collect production deployment logs - if: contains(steps.commit_meta.outputs.message, '[deploy production]') + if: env.K8S_DEPLOY_ENABLED == 'true' && contains(steps.commit_meta.outputs.message, '[deploy production]') run: | mkdir -p logs/deployment/production kubectl get pods -o wide > logs/deployment/production/pods.txt -- 2.39.5