diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml new file mode 100644 index 0000000..6235601 --- /dev/null +++ b/.github/workflows/docker-build.yml @@ -0,0 +1,168 @@ +name: Docker Build and Push + +on: + workflow_call: + inputs: + image-name: + description: "Docker image name (e.g., aligent/my-app)" + type: string + required: true + registry: + description: "Docker registry" + type: string + required: false + default: "docker.io" + context: + description: "Build context path" + type: string + required: false + default: "." + dockerfile: + description: "Path to Dockerfile" + type: string + required: false + default: "Dockerfile" + build-args: + description: "Build arguments (newline-separated KEY=value pairs)" + type: string + required: false + default: "" + platforms: + description: "Target platforms (e.g., linux/amd64,linux/arm64)" + type: string + required: false + default: "" + push: + description: "Push image to registry" + type: boolean + required: false + default: true + tags: + description: "Custom tags configuration for docker/metadata-action" + type: string + required: false + default: | + type=raw,value=latest,enable={{is_default_branch}} + type=sha,prefix= + no-cache: + description: "Disable build cache" + type: boolean + required: false + default: false + cache-from: + description: "Cache source (e.g., type=gha)" + type: string + required: false + default: "type=gha" + cache-to: + description: "Cache destination (e.g., type=gha,mode=max)" + type: string + required: false + default: "type=gha,mode=max" + provenance: + description: "Generate provenance attestation" + type: boolean + required: false + default: false + timeout-minutes: + description: "Job timeout in minutes" + type: number + required: false + default: 60 + dockerhub-username: + description: "Docker Hub username (from vars). Required when push: true (default)." + type: string + required: false + + secrets: + dockerhub-token: + description: "Docker Hub access token (from secrets). Required when push: true (default)." + required: false + build-secrets: + description: "Docker BuildKit secrets (newline-separated KEY=value pairs)" + required: false + build-args-secrets: + description: "Secret build arguments appended to build-args (newline-separated KEY=value pairs)" + required: false + +jobs: + build-and-push: + runs-on: ubuntu-latest + timeout-minutes: ${{ inputs.timeout-minutes }} + permissions: + contents: read + + steps: + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + + - name: Set up QEMU + if: inputs.platforms != '' + uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0 + + - name: Log in to Docker Hub + if: inputs.push + uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0 + with: + registry: ${{ inputs.registry }} + username: ${{ inputs.dockerhub-username }} + password: ${{ secrets.dockerhub-token }} + + - name: Extract metadata for Docker + id: meta + uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0 + with: + images: ${{ inputs.registry }}/${{ inputs.image-name }} + tags: ${{ inputs.tags }} + + - name: Prepare build args + id: build-args + run: | + { + echo 'args<> "$GITHUB_OUTPUT" + env: + BUILD_ARGS: ${{ inputs.build-args }} + BUILD_ARGS_SECRETS: ${{ secrets.build-args-secrets }} + + - name: Build Docker image + uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 + with: + context: ${{ inputs.context }} + file: ${{ inputs.context }}/${{ inputs.dockerfile }} + push: false + load: ${{ inputs.platforms == '' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + build-args: ${{ steps.build-args.outputs.args }} + platforms: ${{ inputs.platforms || '' }} + no-cache: ${{ inputs.no-cache }} + cache-from: ${{ !inputs.no-cache && inputs.cache-from || '' }} + cache-to: ${{ inputs.cache-to }} + provenance: ${{ inputs.provenance }} + secrets: ${{ secrets.build-secrets }} + + - name: Refresh Docker Hub login + if: inputs.push + uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0 + with: + registry: ${{ inputs.registry }} + username: ${{ inputs.dockerhub-username }} + password: ${{ secrets.dockerhub-token }} + + - name: Push Docker image + if: inputs.push + run: | + echo "$TAGS" | tr ',' '\n' | while read -r tag; do + [ -n "$tag" ] && docker push "$tag" + done + env: + TAGS: ${{ steps.meta.outputs.tags }} diff --git a/README.md b/README.md index 9798086..dcc0ff0 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ A collection of GitHub action workflows. Built using the [reusable workflows](ht | Workflow | Description | |----------|-------------| | [AIO App Deployment](docs/aio-app-deployment.md) | Adobe I/O App Builder deployment for standalone apps and NX monorepos | +| [Docker Build and Push](docs/docker-build.md) | Build and push Docker images to Docker Hub with caching, multi-platform, and BuildKit secrets support | | [AIO Mesh Deployment](docs/aio-mesh-deployment.md) | Adobe I/O API Mesh create/update with provisioning polling | | [AWS CDK](docs/aws-cdk.md) | Multi-environment infrastructure synthesis, diffs and deployments with automatic package manager detection | | [Changeset Check](docs/changeset-check.md) | Advisory PR comments when changesets are missing for affected packages | diff --git a/docs/docker-build.md b/docs/docker-build.md new file mode 100644 index 0000000..8725019 --- /dev/null +++ b/docs/docker-build.md @@ -0,0 +1,102 @@ +# Docker Build and Push + +Build and push Docker images to Docker Hub with support for multi-platform builds, caching, and BuildKit secrets. + +#### **Inputs** +| Name | Required | Type | Default | Description | +|-------------------|----------|---------|------------------------------------------------------|--------------------------------------------------| +| image-name | ✅ | string | | Docker image name (e.g., `aligent/my-app`) | +| registry | ❌ | string | `docker.io` | Docker registry | +| context | ❌ | string | `.` | Build context path | +| dockerfile | ❌ | string | `Dockerfile` | Path to Dockerfile | +| build-args | ❌ | string | | Build arguments (newline-separated KEY=value) | +| platforms | ❌ | string | | Target platforms (e.g., `linux/amd64,linux/arm64`) | +| push | ❌ | boolean | `true` | Push image to registry | +| tags | ❌ | string | `type=raw,value=latest,enable={{is_default_branch}}`
`type=sha,prefix=` | Custom tags for docker/metadata-action | +| no-cache | ❌ | boolean | `false` | Disable build cache | +| cache-from | ❌ | string | `type=gha` | Cache source | +| cache-to | ❌ | string | `type=gha,mode=max` | Cache destination | +| provenance | ❌ | boolean | `false` | Generate provenance attestation | +| timeout-minutes | ❌ | number | `60` | Job timeout in minutes | +| dockerhub-username| ✅ | string | | Docker Hub username (from vars) | + +#### **Secrets** +| Name | Required | Description | +|-------------------|----------|------------------------------------------------------| +| dockerhub-token | ✅ | Docker Hub access token | +| build-secrets | ❌ | Docker BuildKit secrets (newline-separated KEY=value)| +| build-args-secrets| ❌ | Secret build arguments appended to build-args | + +#### **Features** + +- **Split build and push**: Build and push are separate steps with a fresh Docker Hub login before push, preventing token timeout for long-running builds. +- **Multi-platform support**: Optional QEMU setup for cross-platform builds. +- **GitHub Actions cache**: Uses `type=gha` caching by default for faster builds. +- **BuildKit secrets**: Securely pass secrets during build without exposing them in logs. +- **Flexible tagging**: Uses docker/metadata-action for automatic tag generation. + +#### Example Usage + +```yaml +name: Build and Push Docker Image + +on: + push: + branches: [main] + schedule: + - cron: '0 2 * * *' # Daily at 2am UTC + workflow_dispatch: + +jobs: + build-and-push: + uses: aligent/workflows/.github/workflows/docker-build.yml@main + with: + image-name: aligent/my-app + dockerhub-username: ${{ vars.DOCKERHUB_USERNAME }} + build-args: | + BUILD_ENV=production + secrets: + dockerhub-token: ${{ secrets.DOCKERHUB_TOKEN }} +``` + +#### Example with BuildKit Secrets + +For builds requiring sensitive data (e.g., API keys during build): + +```yaml +jobs: + build-and-push: + uses: aligent/workflows/.github/workflows/docker-build.yml@main + with: + image-name: aligent/my-app + dockerhub-username: ${{ vars.DOCKERHUB_USERNAME }} + timeout-minutes: 360 + build-args: | + UPDATE_DB=true + secrets: + dockerhub-token: ${{ secrets.DOCKERHUB_TOKEN }} + build-secrets: | + NVD_API_KEY=${{ secrets.NVD_API_KEY }} +``` + +In your Dockerfile, access the secret: + +```dockerfile +RUN --mount=type=secret,id=NVD_API_KEY,mode=0444 \ + SECRET_VALUE=$(cat /run/secrets/NVD_API_KEY) && \ + # use SECRET_VALUE... +``` + +#### Example with Multi-Platform Build + +```yaml +jobs: + build-and-push: + uses: aligent/workflows/.github/workflows/docker-build.yml@main + with: + image-name: aligent/my-app + dockerhub-username: ${{ vars.DOCKERHUB_USERNAME }} + platforms: linux/amd64,linux/arm64 + secrets: + dockerhub-token: ${{ secrets.DOCKERHUB_TOKEN }} +```