diff --git a/.gitea/workflows/deploy-compose.reusable.yml b/.gitea/workflows/deploy-compose.reusable.yml index f7f7326..e6499c8 100644 --- a/.gitea/workflows/deploy-compose.reusable.yml +++ b/.gitea/workflows/deploy-compose.reusable.yml @@ -16,6 +16,11 @@ name: Deploy (Compose) description: Image reference to deploy (registry/repo:tag or @sha256) required: true type: string + registry: + description: "Registry host for optional docker login (example: harbor.hcmc.online). If empty, login step is skipped." + required: false + type: string + default: "" workdir: description: Directory containing compose files on the target runner required: false @@ -26,16 +31,33 @@ name: Deploy (Compose) required: false type: string default: up -d --pull always --remove-orphans + secrets: + REGISTRY_USERNAME: + description: "Optional registry username for docker login (recommended for private registries)" + required: false + REGISTRY_PASSWORD: + description: "Optional registry password/token for docker login (recommended for private registries)" + required: false jobs: deploy: runs-on: ${{ inputs.runner_json != '' && fromJSON(inputs.runner_json) || inputs.runner }} + env: + REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }} + REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }} steps: - # Required for local action usage: `uses: ./.github/actions/...` - name: Checkout uses: actions/checkout@v4 + - name: Docker Login (Optional) + if: inputs.registry != '' && env.REGISTRY_USERNAME != '' && env.REGISTRY_PASSWORD != '' + uses: docker/login-action@v3 + with: + registry: ${{ inputs.registry }} + username: ${{ env.REGISTRY_USERNAME }} + password: ${{ env.REGISTRY_PASSWORD }} + - name: Show target server (Linux/macOS) if: runner.os != 'Windows' shell: bash @@ -50,9 +72,21 @@ jobs: Write-Output "Deploying on runner: ${{ inputs.runner }}" Write-Output "Image: ${{ inputs.image }}" - - name: Compose Up - uses: tienngo/ci-templates/.gitea/actions/compose-up@main - with: - image: ${{ inputs.image }} - workdir: ${{ inputs.workdir }} - compose_args: ${{ inputs.compose_args }} + - name: Compose Up (Linux/macOS) + if: runner.os != 'Windows' + shell: bash + working-directory: ${{ inputs.workdir }} + env: + DOCKER_IMAGE: ${{ inputs.image }} + run: | + set -euo pipefail + docker compose ${{ inputs.compose_args }} + + - name: Compose Up (Windows) + if: runner.os == 'Windows' + shell: pwsh + working-directory: ${{ inputs.workdir }} + env: + DOCKER_IMAGE: ${{ inputs.image }} + run: | + docker compose ${{ inputs.compose_args }} diff --git a/.gitea/workflows/deploy-direct.example.yml b/.gitea/workflows/deploy-direct.example.yml index efb9662..88838fd 100644 --- a/.gitea/workflows/deploy-direct.example.yml +++ b/.gitea/workflows/deploy-direct.example.yml @@ -16,11 +16,12 @@ jobs: runs-on: ${{ inputs.runner }} steps: + # Required for local action usage: `uses: ./.github/actions/...` - name: Checkout uses: actions/checkout@v4 - name: Compose Up - uses: tienngo/ci-templates/.gitea/actions/compose-up@main + uses: ./.github/actions/compose-up with: image: ${{ inputs.image }} workdir: . diff --git a/.gitea/workflows/deploy-template.yml b/.gitea/workflows/deploy-template.yml deleted file mode 100644 index c3a5993..0000000 --- a/.gitea/workflows/deploy-template.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: Deploy Template - -on: - workflow_call: - inputs: - runner: - required: true - type: string - image: - required: true - type: string - -jobs: - deploy: - runs-on: ${{ inputs.runner }} - - steps: - - name: Show target server - run: echo "Deploying on ${{ inputs.runner }}" - - # Force compose to use the exact image tag we built/pushed, regardless of - # what the server's compose file has hard-coded. - - name: Compose Up (Linux) - if: runner.os != 'Windows' - env: - DOCKER_IMAGE: ${{ inputs.image }} - run: | - docker compose up -d --pull always --remove-orphans - - - name: Compose Up (Windows) - if: runner.os == 'Windows' - env: - DOCKER_IMAGE: ${{ inputs.image }} - run: | - docker compose up -d --pull always --remove-orphans - diff --git a/.gitea/workflows/deploy-uses-reusable.example.yml b/.gitea/workflows/deploy-uses-reusable.example.yml index b7343bd..33a834f 100644 --- a/.gitea/workflows/deploy-uses-reusable.example.yml +++ b/.gitea/workflows/deploy-uses-reusable.example.yml @@ -1,4 +1,4 @@ -name: Deploy Using Reusable +name: Deploy Using Reusable (Example) "on": workflow_dispatch: @@ -13,7 +13,7 @@ name: Deploy Using Reusable jobs: deploy: - uses: tienngo/ci-templates/.gitea/workflows/deploy-compose.reusable.yml@main + uses: ./.github/workflows/deploy-compose.reusable.yml with: runner: ${{ inputs.runner }} - image: ${{ inputs.image }} \ No newline at end of file + image: ${{ inputs.image }} diff --git a/.gitea/workflows/docker-build-push.yml b/.gitea/workflows/docker-build-push.yml deleted file mode 100644 index fbcd491..0000000 --- a/.gitea/workflows/docker-build-push.yml +++ /dev/null @@ -1,59 +0,0 @@ -name: Docker Build & Push (Template) - -on: - workflow_call: - inputs: - image_name: - required: true - type: string - image_tag: - required: false - type: string - default: latest - dockerfile: - required: false - type: string - default: Dockerfile - context: - required: false - type: string - default: . - outputs: - image: - description: Fully-qualified image reference pushed to Docker Hub - value: ${{ jobs.build.outputs.image }} - -jobs: - build: - runs-on: ubuntu-latest - container: - image: catthehacker/ubuntu:act-latest - outputs: - image: ${{ steps.push.outputs.image }} - - steps: - - uses: actions/checkout@v3 - - - name: Build Docker image - run: | - docker build \ - -t "${{ inputs.image_name }}:${{ inputs.image_tag }}" \ - -f "${{ inputs.dockerfile }}" \ - "${{ inputs.context }}" - - - name: Log in to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ env.DOCKER_HUB_USERNAME }} - password: ${{ env.DOCKER_HUB_ACCESS_TOKEN }} - - - name: Push Docker image to Docker Hub - id: push - shell: bash - run: | - set -euo pipefail - REMOTE_IMAGE="${{ secrets.DOCKER_HUB_USERNAME }}/${{ inputs.image_name }}:${{ inputs.image_tag }}" - docker tag "${{ inputs.image_name }}:${{ inputs.image_tag }}" "$REMOTE_IMAGE" - docker push "$REMOTE_IMAGE" - echo "image=$REMOTE_IMAGE" >> "$GITHUB_OUTPUT" - diff --git a/.gitea/workflows/harbor-build-push-deploy.example.yml b/.gitea/workflows/harbor-build-push-deploy.example.yml new file mode 100644 index 0000000..4e02a82 --- /dev/null +++ b/.gitea/workflows/harbor-build-push-deploy.example.yml @@ -0,0 +1,32 @@ +name: Harbor Build+Push+Deploy (Example) + +"on": + workflow_dispatch: + inputs: + deploy_runner: + description: "Runner label for deploy (use self-hosted label for real deploy)" + required: true + default: self-hosted + tag: + description: "Optional tag (default: sha-)" + required: false + default: "" + +jobs: + deploy: + # Replace with your actual owner/repo and a pinned ref (tag or commit SHA). + # Example: your-org/registry-stack/.github/workflows/harbor-build-push-deploy.reusable.yml@v1 + uses: YOUR_ORG/registry-stack/.github/workflows/harbor-build-push-deploy.reusable.yml@v1 + with: + deploy_runner: ${{ inputs.deploy_runner }} + tag: ${{ inputs.tag }} + # Defaults: + # - harbor_registry: harbor.hcmc.online + # - harbor_project: ci + # - image_repo: + # - compose_workdir: . + secrets: + HARBOR_PUSH_USERNAME: ${{ secrets.HARBOR_PUSH_USERNAME }} + HARBOR_PUSH_PASSWORD: ${{ secrets.HARBOR_PUSH_PASSWORD }} + HARBOR_PULL_USERNAME: ${{ secrets.HARBOR_PULL_USERNAME }} + HARBOR_PULL_PASSWORD: ${{ secrets.HARBOR_PULL_PASSWORD }} diff --git a/.gitea/workflows/harbor-build-push-deploy.reusable.yml b/.gitea/workflows/harbor-build-push-deploy.reusable.yml new file mode 100644 index 0000000..1570be8 --- /dev/null +++ b/.gitea/workflows/harbor-build-push-deploy.reusable.yml @@ -0,0 +1,190 @@ +name: Harbor Build + Push + Deploy (Compose) + +"on": + workflow_call: + inputs: + # Build inputs + harbor_registry: + description: "Harbor registry hostname (example: harbor.hcmc.online)" + required: false + type: string + default: harbor.hcmc.online + harbor_project: + description: "Harbor project name (default: ci)" + required: false + type: string + default: ci + image_repo: + description: "Optional override for repo name under project (default: GitHub repository name)" + required: false + type: string + default: "" + context: + description: "Docker build context directory" + required: false + type: string + default: . + dockerfile: + description: "Dockerfile path (relative to repository root)" + required: false + type: string + default: Dockerfile + platforms: + description: "Build platforms for buildx (default: linux/amd64)" + required: false + type: string + default: linux/amd64 + tag: + description: "Optional tag to push (default: sha-)" + required: false + type: string + default: "" + + # Deploy inputs + deploy_runner: + description: "Single runner label for deploy (example: self-hosted)" + required: true + type: string + deploy_runner_json: + description: "Optional JSON array of runner labels (example: [\"self-hosted\",\"prod\"]). If set, overrides deploy_runner." + required: false + type: string + default: "" + compose_workdir: + description: "Directory containing compose files on the target runner" + required: false + type: string + default: . + compose_args: + description: "Arguments after `docker compose`" + required: false + type: string + default: up -d --pull always --remove-orphans + + secrets: + HARBOR_PUSH_USERNAME: + description: "Harbor robot/user with push rights to harbor_project" + required: true + HARBOR_PUSH_PASSWORD: + description: "Token/password for HARBOR_PUSH_USERNAME" + required: true + HARBOR_PULL_USERNAME: + description: "Harbor robot/user with pull rights to the deployed repositories" + required: true + HARBOR_PULL_PASSWORD: + description: "Token/password for HARBOR_PULL_USERNAME" + required: true + + outputs: + image_repo: + description: "Image repository (no tag/digest), e.g. harbor.hcmc.online/ci/myapp" + value: ${{ jobs.build.outputs.image_repo }} + image_digest: + description: "Content digest, e.g. sha256:..." + value: ${{ jobs.build.outputs.image_digest }} + image_ref: + description: "Immutable image ref, e.g. harbor.hcmc.online/ci/myapp@sha256:..." + value: ${{ jobs.build.outputs.image_ref }} + image_tag: + description: "Tag pushed alongside digest (for humans), e.g. sha-abc123" + value: ${{ jobs.build.outputs.image_tag }} + +jobs: + build: + runs-on: ubuntu-latest + 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 }} + image_tag: ${{ steps.meta.outputs.image_tag }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Buildx + uses: docker/setup-buildx-action@v3 + + - name: Compute Image Repo + Tag + id: meta + shell: bash + run: | + set -euo pipefail + repo_name="${{ inputs.image_repo }}" + if [[ -z "${repo_name}" ]]; then + repo_name="${{ github.event.repository.name }}" + fi + + tag="${{ inputs.tag }}" + if [[ -z "${tag}" ]]; then + short_sha="$(echo "${{ github.sha }}" | cut -c1-12)" + tag="sha-${short_sha}" + fi + + image_repo="${{ inputs.harbor_registry }}/${{ inputs.harbor_project }}/${repo_name}" + + echo "image_repo=${image_repo}" >> "${GITHUB_OUTPUT}" + echo "image_tag=${tag}" >> "${GITHUB_OUTPUT}" + + - name: Docker Login (Harbor Push) + uses: docker/login-action@v3 + with: + registry: ${{ inputs.harbor_registry }} + username: ${{ secrets.HARBOR_PUSH_USERNAME }} + password: ${{ secrets.HARBOR_PUSH_PASSWORD }} + + - name: Build + Push (Harbor) + 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.image_tag }} + + deploy: + needs: build + runs-on: ${{ inputs.deploy_runner_json != '' && fromJSON(inputs.deploy_runner_json) || inputs.deploy_runner }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Docker 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 deploy image + if: runner.os != 'Windows' + shell: bash + run: | + echo "Deploying: ${{ needs.build.outputs.image_repo }}@${{ needs.build.outputs.image_digest }}" + + - name: Show deploy image (Windows) + if: runner.os == 'Windows' + shell: pwsh + run: | + Write-Output "Deploying: ${{ needs.build.outputs.image_repo }}@${{ needs.build.outputs.image_digest }}" + + - name: Compose Up (Linux/macOS) + if: runner.os != 'Windows' + shell: bash + working-directory: ${{ inputs.compose_workdir }} + env: + DOCKER_IMAGE: ${{ needs.build.outputs.image_repo }}@${{ needs.build.outputs.image_digest }} + run: | + set -euo pipefail + docker compose ${{ inputs.compose_args }} + + - name: Compose Up (Windows) + if: runner.os == 'Windows' + shell: pwsh + working-directory: ${{ inputs.compose_workdir }} + env: + DOCKER_IMAGE: ${{ needs.build.outputs.image_repo }}@${{ needs.build.outputs.image_digest }} + run: | + docker compose ${{ inputs.compose_args }} + diff --git a/actions/compose-up/action.yml b/actions/compose-up/action.yml deleted file mode 100644 index 4d2acb5..0000000 --- a/actions/compose-up/action.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: compose-up -description: Run docker compose up using an explicit image (DOCKER_IMAGE) on Linux/Windows. - -inputs: - image: - description: Full image reference (registry/repo:tag or @sha256:...) - required: true - workdir: - description: Directory containing compose files on the runner - required: false - default: . - compose_args: - description: Arguments after `docker compose` (default runs up -d with pull) - required: false - default: up -d --pull always --remove-orphans - -runs: - using: composite - steps: - - name: Compose Up (Linux/macOS) - if: runner.os != 'Windows' - shell: bash - working-directory: ${{ inputs.workdir }} - env: - DOCKER_IMAGE: ${{ inputs.image }} - run: | - set -euo pipefail - docker compose ${{ inputs.compose_args }} - - - name: Compose Up (Windows) - if: runner.os == 'Windows' - shell: pwsh - working-directory: ${{ inputs.workdir }} - env: - DOCKER_IMAGE: ${{ inputs.image }} - run: | - docker compose ${{ inputs.compose_args }}