|
| 1 | +# Build Docker Action |
| 2 | + |
| 3 | +A reusable GitHub Action for building and pushing multi-architecture Docker images with advanced features like caching, SemVer tagging, and support for multiple registries. |
| 4 | + |
| 5 | +## Features |
| 6 | + |
| 7 | +- ✅ **Multi-platform builds** using Docker Buildx (linux/amd64, linux/arm64) |
| 8 | +- 🏷️ **Automatic SemVer tagging** based on Git tags |
| 9 | +- 🚀 **Build caching** for faster rebuilds using GitHub Actions cache |
| 10 | +- 🐳 **Multiple registry support** (Docker Hub, GHCR, custom registries) |
| 11 | +- 🔄 **Conditional pushing** (build-only mode for testing) |
| 12 | +- 📋 **Rich metadata** with OpenContainer labels |
| 13 | +- 🎯 **Matrix builds** for multiple Dockerfiles |
| 14 | + |
| 15 | +## Usage |
| 16 | + |
| 17 | +### Basic Usage |
| 18 | + |
| 19 | +```yaml |
| 20 | +- name: Build and push Docker image |
| 21 | + uses: ./.github/actions/build-docker |
| 22 | + with: |
| 23 | + dockerfile: docker/Dockerfile |
| 24 | + image-name: myorg/myapp |
| 25 | + registry: docker.io |
| 26 | + username: ${{ secrets.DOCKER_USERNAME }} |
| 27 | + password: ${{ secrets.DOCKER_PASSWORD }} |
| 28 | + push: 'true' |
| 29 | +``` |
| 30 | +
|
| 31 | +### Advanced Usage with Matrix Strategy |
| 32 | +
|
| 33 | +```yaml |
| 34 | +jobs: |
| 35 | + docker: |
| 36 | + strategy: |
| 37 | + matrix: |
| 38 | + image: |
| 39 | + - {file: 'docker/Dockerfile', name: 'myorg/myapp'} |
| 40 | + - {file: 'docker/demo/Dockerfile', name: 'myorg/myapp-demo'} |
| 41 | + steps: |
| 42 | + - uses: actions/checkout@v4 |
| 43 | + |
| 44 | + - name: Build and push |
| 45 | + uses: ./.github/actions/build-docker |
| 46 | + with: |
| 47 | + dockerfile: ${{ matrix.image.file }} |
| 48 | + image-name: ${{ matrix.image.name }} |
| 49 | + registry: docker.io |
| 50 | + username: ${{ secrets.DOCKER_USERNAME }} |
| 51 | + password: ${{ secrets.DOCKER_PASSWORD }} |
| 52 | + push: ${{ github.ref_type == 'tag' }} |
| 53 | + build-args: | |
| 54 | + VERSION=${{ github.ref_name }} |
| 55 | + BUILD_DATE=${{ steps.date.outputs.date }} |
| 56 | +``` |
| 57 | +
|
| 58 | +## Inputs |
| 59 | +
|
| 60 | +| Input | Description | Required | Default | |
| 61 | +|-------|-------------|----------|---------| |
| 62 | +| `dockerfile` | Path to Dockerfile (relative to repo root) | ✅ | - | |
| 63 | +| `image-name` | Docker image name (without registry prefix) | ✅ | - | |
| 64 | +| `registry` | Docker registry (docker.io, ghcr.io, etc.) | ❌ | `docker.io` | |
| 65 | +| `username` | Docker registry username | ✅ | - | |
| 66 | +| `password` | Docker registry password/token | ✅ | - | |
| 67 | +| `platforms` | Target platforms for multi-arch build | ❌ | `linux/amd64,linux/arm64` | |
| 68 | +| `push` | Whether to push the image (true/false) | ❌ | `false` | |
| 69 | +| `build-args` | Build arguments (KEY=VALUE, one per line) | ❌ | - | |
| 70 | +| `cache-scope` | Cache scope for build cache | ❌ | `default` | |
| 71 | + |
| 72 | +## Outputs |
| 73 | + |
| 74 | +| Output | Description | |
| 75 | +|--------|-------------| |
| 76 | +| `image-id` | Image ID of the built image | |
| 77 | +| `digest` | Image digest of the built image | |
| 78 | +| `metadata` | Build result metadata | |
| 79 | + |
| 80 | +## Tag Strategy |
| 81 | + |
| 82 | +The action automatically applies tags based on the trigger: |
| 83 | + |
| 84 | +### For Git Tags (Releases) |
| 85 | + |
| 86 | +- `1.2.3` - Exact version |
| 87 | +- `1.2` - Minor version |
| 88 | +- `1` - Major version |
| 89 | +- `latest` - Latest stable release (excludes pre-releases) |
| 90 | + |
| 91 | +**Note**: Pre-release versions (e.g., `1.2.3-beta.1`, `2.0.0-rc.1`) will only get the exact version tag, not `latest` or major/minor tags. |
| 92 | + |
| 93 | +### For Branch Pushes |
| 94 | + |
| 95 | +- `main` or `master` → branch name (no `latest` tag) |
| 96 | +- Other branches → branch name as tag |
| 97 | + |
| 98 | +### For Pull Requests |
| 99 | + |
| 100 | +- `pr-123` - PR number |
| 101 | + |
| 102 | +### Always Applied |
| 103 | + |
| 104 | +- `sha-abc1234` - Short commit SHA |
| 105 | + |
| 106 | +## Registry Examples |
| 107 | + |
| 108 | +### Docker Hub |
| 109 | + |
| 110 | +```yaml |
| 111 | +with: |
| 112 | + registry: docker.io |
| 113 | + username: ${{ secrets.DOCKER_USERNAME }} |
| 114 | + password: ${{ secrets.DOCKER_PASSWORD }} |
| 115 | +``` |
| 116 | + |
| 117 | +### GitHub Container Registry (GHCR) |
| 118 | + |
| 119 | +```yaml |
| 120 | +with: |
| 121 | + registry: ghcr.io |
| 122 | + image-name: myorg/myapp # Same name as Docker Hub |
| 123 | + username: ${{ github.actor }} |
| 124 | + password: ${{ secrets.GITHUB_TOKEN }} |
| 125 | +``` |
| 126 | + |
| 127 | +**Note**: Use consistent image names across registries for better user experience. |
| 128 | + |
| 129 | +### Custom Registry |
| 130 | + |
| 131 | +```yaml |
| 132 | +with: |
| 133 | + registry: my-registry.com |
| 134 | + username: ${{ secrets.CUSTOM_REGISTRY_USER }} |
| 135 | + password: ${{ secrets.CUSTOM_REGISTRY_TOKEN }} |
| 136 | +``` |
| 137 | + |
| 138 | +## Build-Only Mode |
| 139 | + |
| 140 | +To test builds without pushing (useful in PR workflows): |
| 141 | + |
| 142 | +```yaml |
| 143 | +- name: Test Docker build |
| 144 | + uses: ./.github/actions/build-docker |
| 145 | + with: |
| 146 | + dockerfile: docker/Dockerfile |
| 147 | + image-name: myorg/myapp |
| 148 | + registry: docker.io |
| 149 | + username: dummy # Not used when push=false |
| 150 | + password: dummy # Not used when push=false |
| 151 | + push: 'false' |
| 152 | +``` |
| 153 | + |
| 154 | +## Build Arguments |
| 155 | + |
| 156 | +Pass build arguments to Docker: |
| 157 | + |
| 158 | +```yaml |
| 159 | +with: |
| 160 | + build-args: | |
| 161 | + VERSION=${{ github.ref_name }} |
| 162 | + GIT_HASH=${{ github.sha }} |
| 163 | + BUILD_DATE=${{ steps.date.outputs.date }} |
| 164 | + ENVIRONMENT=production |
| 165 | +``` |
| 166 | + |
| 167 | +## Caching |
| 168 | + |
| 169 | +The action uses GitHub Actions cache for Docker layer caching. Use different `cache-scope` values for different build contexts: |
| 170 | + |
| 171 | +```yaml |
| 172 | +with: |
| 173 | + cache-scope: main-app # For main application |
| 174 | + # vs |
| 175 | + cache-scope: demo-app # For demo application |
| 176 | +``` |
| 177 | + |
| 178 | +## Security Notes |
| 179 | + |
| 180 | +- Registry credentials are only used when `push: 'true'` |
| 181 | +- Build-only mode doesn't require valid credentials |
| 182 | +- Use repository secrets for sensitive data |
| 183 | +- GHCR automatically uses `GITHUB_TOKEN` which has appropriate permissions |
| 184 | + |
| 185 | +## Conditional Pushing |
| 186 | + |
| 187 | +Common patterns for when to push images: |
| 188 | + |
| 189 | +```yaml |
| 190 | +# Push only on tags (releases) |
| 191 | +push: ${{ github.ref_type == 'tag' }} |
| 192 | +
|
| 193 | +# Push on tags or main branch |
| 194 | +push: ${{ github.ref_type == 'tag' || github.ref_name == 'main' }} |
| 195 | +
|
| 196 | +# Push only on releases (not pre-releases) |
| 197 | +push: ${{ github.ref_type == 'tag' && !contains(github.ref_name, 'beta') }} |
| 198 | +``` |
| 199 | + |
| 200 | +## Workflow Integration |
| 201 | + |
| 202 | +### Avoiding Conflicts |
| 203 | + |
| 204 | +When using this action in multiple workflows, be careful about triggers to avoid conflicts: |
| 205 | + |
| 206 | +```yaml |
| 207 | +# ✅ Good: Release workflow (official releases) |
| 208 | +on: |
| 209 | + release: |
| 210 | + types: [created] |
| 211 | +# Uses: push: 'true' for Docker Hub |
| 212 | +
|
| 213 | +# ✅ Good: Build workflow (testing) |
| 214 | +on: |
| 215 | + pull_request: |
| 216 | +# Uses: push: 'false' (build-only) |
| 217 | +
|
| 218 | +# ❌ Bad: Both workflows triggering on same tag |
| 219 | +# Don't use both of these: |
| 220 | +on: { push: { tags: ['v*'] } } # Workflow 1 |
| 221 | +on: { release: { types: [created] } } # Workflow 2 (GitHub creates tag automatically) |
| 222 | +``` |
| 223 | + |
| 224 | +### Recommended Setup |
| 225 | + |
| 226 | +- **Release Workflow**: Handle official releases to Docker Hub |
| 227 | +- **Build Workflow**: Test builds on PRs (no push) |
| 228 | +- **Multi-Registry Workflow**: Manual builds for alternative registries |
0 commit comments