name: CI - Build on: workflow_call: workflow_dispatch: 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 REGISTRY_URL: ${{ secrets.REGISTRY_URL }} REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }} REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }} REGISTRY_CONTAINER_NAME: calminer steps: - name: Checkout uses: actions/checkout@v4 - name: Collect workflow metadata id: meta shell: bash env: DEFAULT_BRANCH: ${{ env.DEFAULT_BRANCH }} run: | 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" else echo "allow_push=false" >> "$GITHUB_OUTPUT" fi echo "ref_name=$ref_name" >> "$GITHUB_OUTPUT" 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 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 - name: Log in to gitea registry if: ${{ steps.meta.outputs.allow_push == 'true' }} uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY_HOST }} username: ${{ env.REGISTRY_USERNAME }} password: ${{ env.REGISTRY_PASSWORD }} - name: Build image id: build-image env: 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_HOST != '' && env.REGISTRY_USERNAME != '' && env.REGISTRY_PASSWORD != '' }} run: | set -eo pipefail LOG_FILE=build.log if [ "${PUSH_IMAGE}" = "true" ]; then docker buildx build \ --load \ --tag "${REGISTRY_REPOSITORY}:latest" \ --tag "${REGISTRY_REPOSITORY}:${SHA_TAG}" \ --file Dockerfile \ . 2>&1 | tee "${LOG_FILE}" else docker buildx build \ --load \ --tag "${REGISTRY_CONTAINER_NAME}:ci" \ --file Dockerfile \ . 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 with: name: docker-build-logs path: build.log deploy: needs: build if: needs.build.outputs.allow_push == 'true' runs-on: ubuntu-latest env: REGISTRY_URL: ${{ secrets.REGISTRY_URL }} REGISTRY_CONTAINER_NAME: calminer 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 - 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: 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: | set -euo pipefail message="$(git log -1 --pretty=%B | tr '\n' ' ')" echo "message=$message" >> "$GITHUB_OUTPUT" - name: Set up kubectl for 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: 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: 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 kubectl apply -f k8s/secret.yaml kubectl rollout status deployment/calminer-app - name: Collect staging deployment logs 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 kubectl get deployment calminer-app -o yaml > logs/deployment/staging/deployment.yaml kubectl logs deployment/calminer-app --all-containers=true --tail=500 > logs/deployment/staging/calminer-app.log - name: Deploy to 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 kubectl apply -f k8s/secret.yaml kubectl rollout status deployment/calminer-app - name: Collect production deployment logs 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 kubectl get deployment calminer-app -o yaml > logs/deployment/production/deployment.yaml kubectl logs deployment/calminer-app --all-containers=true --tail=500 > logs/deployment/production/calminer-app.log - name: Upload deployment logs if: always() uses: actions/upload-artifact@v4 with: name: deployment-logs path: logs/deployment if-no-files-found: ignore