diff --git a/.gitea/workflows/harbor-build-deploy.yml b/.gitea/workflows/harbor-build-deploy.yml new file mode 100644 index 0000000..380a2ba --- /dev/null +++ b/.gitea/workflows/harbor-build-deploy.yml @@ -0,0 +1,148 @@ +name: Harbor Build Once → Deploy Many (Compose) + +on: + workflow_call: + inputs: + harbor_registry: + type: string + default: harbor.hcmc.online + harbor_project: + type: string + default: ci + image_repo: + type: string + required: true + + context: + type: string + default: . + dockerfile: + type: string + default: Dockerfile + platforms: + type: string + default: linux/amd64,linux/arm64 + + # ⭐ IMPORTANT CHANGE + deploy_runners_json: + description: 'JSON array of runner labels' + type: string + required: true + + compose_workdir: + type: string + default: . + compose_args: + type: string + default: up -d --pull always --remove-orphans + + secrets: + HARBOR_PUSH_USERNAME: { required: true } + HARBOR_PUSH_PASSWORD: { required: true } + HARBOR_PULL_USERNAME: { required: true } + HARBOR_PULL_PASSWORD: { required: true } + +jobs: + +# ========================================================== +# 1️⃣ BUILD ONCE (single powerful runner) +# ========================================================== + build_and_push: + name: 🏗 Build & Push Image + runs-on: devsg-atlantic + + outputs: + image_repo: ${{ steps.meta.outputs.image_repo }} + image_digest: ${{ steps.build.outputs.digest }} + image_ref: ${{ steps.meta.outputs.image_repo }}@${{ steps.build.outputs.digest }} + + steps: + - uses: actions/checkout@v4 + - uses: docker/setup-buildx-action@v3 + + - name: Compute image metadata + id: meta + shell: bash + run: | + set -euo pipefail + IMAGE="${{ inputs.harbor_registry }}/${{ inputs.harbor_project }}/${{ inputs.image_repo }}" + SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-12) + BRANCH="${{ github.ref_name }}" + + echo "image_repo=$IMAGE" >> $GITHUB_OUTPUT + echo "sha_tag=sha-$SHORT_SHA" >> $GITHUB_OUTPUT + echo "branch_tag=$BRANCH" >> $GITHUB_OUTPUT + + - name: Login Harbor (push) + uses: docker/login-action@v3 + with: + registry: ${{ inputs.harbor_registry }} + username: ${{ secrets.HARBOR_PUSH_USERNAME }} + password: ${{ secrets.HARBOR_PUSH_PASSWORD }} + + # 🚀 Cached multi-platform build + - name: Build & Push Image + id: build + uses: docker/build-push-action@v6 + with: + context: ${{ inputs.context }} + file: ${{ inputs.dockerfile }} + platforms: ${{ inputs.platforms }} + push: true + + tags: | + ${{ steps.meta.outputs.image_repo }}:${{ steps.meta.outputs.sha_tag }} + ${{ steps.meta.outputs.image_repo }}:${{ steps.meta.outputs.branch_tag }} + ${{ steps.meta.outputs.image_repo }}:latest + + cache-from: type=registry,ref=${{ steps.meta.outputs.image_repo }}:buildcache + cache-to: type=registry,ref=${{ steps.meta.outputs.image_repo }}:buildcache,mode=max + + +# ========================================================== +# 2️⃣ DEPLOY MANY (fan-out runners) +# ========================================================== + deploy: + name: 🚀 Deploy to Fleet + needs: build_and_push + + strategy: + fail-fast: false + matrix: + runner: ${{ fromJSON(inputs.deploy_runners_json) }} + + runs-on: ${{ matrix.runner }} + + steps: + - uses: actions/checkout@v4 + + - name: Login Harbor (pull) + uses: docker/login-action@v3 + with: + registry: ${{ inputs.harbor_registry }} + username: ${{ secrets.HARBOR_PULL_USERNAME }} + password: ${{ secrets.HARBOR_PULL_PASSWORD }} + + - name: Show image + run: echo "Deploying ${{ needs.build_and_push.outputs.image_ref }}" + + # Linux/macOS + - name: Compose Up (Unix) + if: runner.os != 'Windows' + shell: bash + working-directory: ${{ inputs.compose_workdir }} + env: + DOCKER_IMAGE: ${{ needs.build_and_push.outputs.image_ref }} + run: | + set -euo pipefail + docker compose ${{ inputs.compose_args }} + + # Windows runners support + - name: Compose Up (Windows) + if: runner.os == 'Windows' + shell: pwsh + working-directory: ${{ inputs.compose_workdir }} + env: + DOCKER_IMAGE: ${{ needs.build_and_push.outputs.image_ref }} + run: | + docker compose ${{ inputs.compose_args }} \ No newline at end of file