From 6dc8000dc8933439e0872b4a7fb44bf821893ce9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Feb 2026 21:42:30 +0000 Subject: [PATCH 1/9] Initial plan From 5634cc2b5dd16b8480ccbb79e4040f0b3d64ae85 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Feb 2026 21:45:16 +0000 Subject: [PATCH 2/9] Add reusable GitHub Actions workflows for ARM, AMD, and multiarch image builds Co-authored-by: alexfricker <67075196+alexfricker@users.noreply.github.com> --- .github/workflows/build-amd-image.yml | 121 ++++++++ .github/workflows/build-arm-image.yml | 121 ++++++++ .github/workflows/build-multiarch-image.yml | 127 ++++++++ .yamllint | 11 + README.md | 314 +++++++++++++++++++- 5 files changed, 692 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/build-amd-image.yml create mode 100644 .github/workflows/build-arm-image.yml create mode 100644 .github/workflows/build-multiarch-image.yml create mode 100644 .yamllint diff --git a/.github/workflows/build-amd-image.yml b/.github/workflows/build-amd-image.yml new file mode 100644 index 0000000..20d6b23 --- /dev/null +++ b/.github/workflows/build-amd-image.yml @@ -0,0 +1,121 @@ +name: Build AMD Image + +on: + workflow_call: + inputs: + image_name: + description: 'Name of the Docker image to build' + required: true + type: string + dockerfile_path: + description: 'Path to the Dockerfile' + required: false + type: string + default: './Dockerfile' + context: + description: 'Build context path' + required: false + type: string + default: '.' + build_args: + description: 'Build arguments (comma-separated KEY=VALUE pairs)' + required: false + type: string + default: '' + push: + description: 'Whether to push the image to registry' + required: false + type: boolean + default: false + tags: + description: 'Additional tags for the image (newline-separated)' + required: false + type: string + default: '' + secrets: + registry_username: + description: 'Docker registry username' + required: false + registry_password: + description: 'Docker registry password' + required: false + outputs: + image_tag: + description: 'Full image tag that was built' + value: ${{ jobs.build-amd.outputs.image_tag }} + digest: + description: 'Image digest' + value: ${{ jobs.build-amd.outputs.digest }} + +jobs: + build-amd: + runs-on: ubuntu-latest + outputs: + image_tag: ${{ steps.build.outputs.image_tag }} + digest: ${{ steps.build.outputs.digest }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + with: + platforms: linux/amd64 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Docker Registry + if: inputs.push == true && secrets.registry_username != '' && secrets.registry_password != '' + uses: docker/login-action@v3 + with: + username: ${{ secrets.registry_username }} + password: ${{ secrets.registry_password }} + + - name: Prepare build args + id: prepare-args + run: | + if [ -n "${{ inputs.build_args }}" ]; then + echo "build_args<> $GITHUB_OUTPUT + echo "${{ inputs.build_args }}" | tr ',' '\n' >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + else + echo "build_args=" >> $GITHUB_OUTPUT + fi + + - name: Prepare tags + id: prepare-tags + run: | + TAGS="${{ inputs.image_name }}:amd64-${{ github.sha }}" + if [ -n "${{ inputs.tags }}" ]; then + while IFS= read -r tag; do + if [ -n "$tag" ]; then + TAGS="$TAGS,${{ inputs.image_name }}:$tag-amd64" + fi + done <<< "${{ inputs.tags }}" + fi + echo "tags=$TAGS" >> $GITHUB_OUTPUT + echo "Built tags: $TAGS" + + - name: Build and push AMD image + id: build + uses: docker/build-push-action@v5 + with: + context: ${{ inputs.context }} + file: ${{ inputs.dockerfile_path }} + platforms: linux/amd64 + push: ${{ inputs.push }} + tags: ${{ steps.prepare-tags.outputs.tags }} + build-args: ${{ steps.prepare-args.outputs.build_args }} + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Output image information + id: output + run: | + echo "image_tag=${{ inputs.image_name }}:amd64-${{ github.sha }}" >> $GITHUB_OUTPUT + echo "digest=${{ steps.build.outputs.digest }}" >> $GITHUB_OUTPUT + echo "✅ AMD64 image built successfully" + echo "Image: ${{ inputs.image_name }}:amd64-${{ github.sha }}" + echo "Digest: ${{ steps.build.outputs.digest }}" diff --git a/.github/workflows/build-arm-image.yml b/.github/workflows/build-arm-image.yml new file mode 100644 index 0000000..801a93f --- /dev/null +++ b/.github/workflows/build-arm-image.yml @@ -0,0 +1,121 @@ +name: Build ARM Image + +on: + workflow_call: + inputs: + image_name: + description: 'Name of the Docker image to build' + required: true + type: string + dockerfile_path: + description: 'Path to the Dockerfile' + required: false + type: string + default: './Dockerfile' + context: + description: 'Build context path' + required: false + type: string + default: '.' + build_args: + description: 'Build arguments (comma-separated KEY=VALUE pairs)' + required: false + type: string + default: '' + push: + description: 'Whether to push the image to registry' + required: false + type: boolean + default: false + tags: + description: 'Additional tags for the image (newline-separated)' + required: false + type: string + default: '' + secrets: + registry_username: + description: 'Docker registry username' + required: false + registry_password: + description: 'Docker registry password' + required: false + outputs: + image_tag: + description: 'Full image tag that was built' + value: ${{ jobs.build-arm.outputs.image_tag }} + digest: + description: 'Image digest' + value: ${{ jobs.build-arm.outputs.digest }} + +jobs: + build-arm: + runs-on: ubuntu-latest + outputs: + image_tag: ${{ steps.build.outputs.image_tag }} + digest: ${{ steps.build.outputs.digest }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + with: + platforms: linux/arm64 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Docker Registry + if: inputs.push == true && secrets.registry_username != '' && secrets.registry_password != '' + uses: docker/login-action@v3 + with: + username: ${{ secrets.registry_username }} + password: ${{ secrets.registry_password }} + + - name: Prepare build args + id: prepare-args + run: | + if [ -n "${{ inputs.build_args }}" ]; then + echo "build_args<> $GITHUB_OUTPUT + echo "${{ inputs.build_args }}" | tr ',' '\n' >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + else + echo "build_args=" >> $GITHUB_OUTPUT + fi + + - name: Prepare tags + id: prepare-tags + run: | + TAGS="${{ inputs.image_name }}:arm64-${{ github.sha }}" + if [ -n "${{ inputs.tags }}" ]; then + while IFS= read -r tag; do + if [ -n "$tag" ]; then + TAGS="$TAGS,${{ inputs.image_name }}:$tag-arm64" + fi + done <<< "${{ inputs.tags }}" + fi + echo "tags=$TAGS" >> $GITHUB_OUTPUT + echo "Built tags: $TAGS" + + - name: Build and push ARM image + id: build + uses: docker/build-push-action@v5 + with: + context: ${{ inputs.context }} + file: ${{ inputs.dockerfile_path }} + platforms: linux/arm64 + push: ${{ inputs.push }} + tags: ${{ steps.prepare-tags.outputs.tags }} + build-args: ${{ steps.prepare-args.outputs.build_args }} + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Output image information + id: output + run: | + echo "image_tag=${{ inputs.image_name }}:arm64-${{ github.sha }}" >> $GITHUB_OUTPUT + echo "digest=${{ steps.build.outputs.digest }}" >> $GITHUB_OUTPUT + echo "✅ ARM64 image built successfully" + echo "Image: ${{ inputs.image_name }}:arm64-${{ github.sha }}" + echo "Digest: ${{ steps.build.outputs.digest }}" diff --git a/.github/workflows/build-multiarch-image.yml b/.github/workflows/build-multiarch-image.yml new file mode 100644 index 0000000..5200aab --- /dev/null +++ b/.github/workflows/build-multiarch-image.yml @@ -0,0 +1,127 @@ +name: Build Multiarch Image + +on: + workflow_call: + inputs: + image_name: + description: 'Name of the Docker image to build' + required: true + type: string + dockerfile_path: + description: 'Path to the Dockerfile' + required: false + type: string + default: './Dockerfile' + context: + description: 'Build context path' + required: false + type: string + default: '.' + platforms: + description: 'Target platforms (comma-separated)' + required: false + type: string + default: 'linux/amd64,linux/arm64' + build_args: + description: 'Build arguments (comma-separated KEY=VALUE pairs)' + required: false + type: string + default: '' + push: + description: 'Whether to push the image to registry' + required: false + type: boolean + default: false + tags: + description: 'Additional tags for the image (newline-separated)' + required: false + type: string + default: '' + secrets: + registry_username: + description: 'Docker registry username' + required: false + registry_password: + description: 'Docker registry password' + required: false + outputs: + image_tag: + description: 'Full image tag that was built' + value: ${{ jobs.build-multiarch.outputs.image_tag }} + digest: + description: 'Image digest' + value: ${{ jobs.build-multiarch.outputs.digest }} + +jobs: + build-multiarch: + runs-on: ubuntu-latest + outputs: + image_tag: ${{ steps.build.outputs.image_tag }} + digest: ${{ steps.build.outputs.digest }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + with: + platforms: all + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Docker Registry + if: inputs.push == true && secrets.registry_username != '' && secrets.registry_password != '' + uses: docker/login-action@v3 + with: + username: ${{ secrets.registry_username }} + password: ${{ secrets.registry_password }} + + - name: Prepare build args + id: prepare-args + run: | + if [ -n "${{ inputs.build_args }}" ]; then + echo "build_args<> $GITHUB_OUTPUT + echo "${{ inputs.build_args }}" | tr ',' '\n' >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + else + echo "build_args=" >> $GITHUB_OUTPUT + fi + + - name: Prepare tags + id: prepare-tags + run: | + TAGS="${{ inputs.image_name }}:${{ github.sha }}" + if [ -n "${{ inputs.tags }}" ]; then + while IFS= read -r tag; do + if [ -n "$tag" ]; then + TAGS="$TAGS,${{ inputs.image_name }}:$tag" + fi + done <<< "${{ inputs.tags }}" + fi + echo "tags=$TAGS" >> $GITHUB_OUTPUT + echo "Built tags: $TAGS" + + - name: Build and push multiarch image + id: build + uses: docker/build-push-action@v5 + with: + context: ${{ inputs.context }} + file: ${{ inputs.dockerfile_path }} + platforms: ${{ inputs.platforms }} + push: ${{ inputs.push }} + tags: ${{ steps.prepare-tags.outputs.tags }} + build-args: ${{ steps.prepare-args.outputs.build_args }} + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Output image information + id: output + run: | + echo "image_tag=${{ inputs.image_name }}:${{ github.sha }}" >> $GITHUB_OUTPUT + echo "digest=${{ steps.build.outputs.digest }}" >> $GITHUB_OUTPUT + echo "✅ Multiarch image built successfully" + echo "Platforms: ${{ inputs.platforms }}" + echo "Image: ${{ inputs.image_name }}:${{ github.sha }}" + echo "Digest: ${{ steps.build.outputs.digest }}" diff --git a/.yamllint b/.yamllint new file mode 100644 index 0000000..e1e7a82 --- /dev/null +++ b/.yamllint @@ -0,0 +1,11 @@ +--- +extends: default + +rules: + document-start: disable + line-length: + max: 120 + level: warning + truthy: + allowed-values: ['true', 'false', 'on'] + trailing-spaces: enable diff --git a/README.md b/README.md index 0b08081..dd9f4c1 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,312 @@ -# github-actions -Reusable Actions/Workflows +# GitHub Actions - Reusable Workflows + +This repository contains reusable GitHub Actions workflows for building Docker images across different architectures. + +## Available Workflows + +### 1. Build ARM Image (`build-arm-image.yml`) +Builds a Docker image specifically for ARM64 architecture (linux/arm64). + +### 2. Build AMD Image (`build-amd-image.yml`) +Builds a Docker image specifically for AMD64 architecture (linux/amd64). + +### 3. Build Multiarch Image (`build-multiarch-image.yml`) +Builds a Docker image for multiple architectures in a single manifest (default: linux/amd64 and linux/arm64). + +## Quick Start + +To use these reusable workflows in your repository, reference them in your workflow files: + +```yaml +name: Build My Docker Image + +on: + push: + branches: [main] + +jobs: + build-multiarch: + uses: fricker-studios/github-actions/.github/workflows/build-multiarch-image.yml@main + with: + image_name: myorganization/myapp + push: true + tags: | + latest + v1.0.0 + secrets: + registry_username: ${{ secrets.DOCKER_USERNAME }} + registry_password: ${{ secrets.DOCKER_PASSWORD }} +``` + +## Workflow Inputs + +All three workflows share the following common inputs: + +| Input | Description | Required | Default | +|-------|-------------|----------|---------| +| `image_name` | Name of the Docker image to build (e.g., `myorg/myapp`) | Yes | - | +| `dockerfile_path` | Path to the Dockerfile | No | `./Dockerfile` | +| `context` | Build context path | No | `.` | +| `build_args` | Build arguments (comma-separated KEY=VALUE pairs) | No | `''` | +| `push` | Whether to push the image to registry | No | `false` | +| `tags` | Additional tags for the image (newline-separated) | No | `''` | + +**Additional input for multiarch workflow:** + +| Input | Description | Required | Default | +|-------|-------------|----------|---------| +| `platforms` | Target platforms (comma-separated) | No | `linux/amd64,linux/arm64` | + +## Workflow Secrets + +| Secret | Description | Required | +|--------|-------------|----------| +| `registry_username` | Docker registry username | No* | +| `registry_password` | Docker registry password | No* | + +*Required only if `push: true` is set. + +## Workflow Outputs + +All workflows provide the following outputs: + +| Output | Description | +|--------|-------------| +| `image_tag` | Full image tag that was built | +| `digest` | Image digest | + +## Usage Examples + +### Example 1: Build ARM Image Only + +```yaml +name: Build ARM Image + +on: + push: + branches: [main] + +jobs: + build-arm: + uses: fricker-studios/github-actions/.github/workflows/build-arm-image.yml@main + with: + image_name: myorg/myapp + dockerfile_path: ./docker/Dockerfile + context: . + push: false +``` + +### Example 2: Build AMD Image with Custom Tags + +```yaml +name: Build AMD Image + +on: + release: + types: [published] + +jobs: + build-amd: + uses: fricker-studios/github-actions/.github/workflows/build-amd-image.yml@main + with: + image_name: myorg/myapp + push: true + tags: | + latest + ${{ github.ref_name }} + secrets: + registry_username: ${{ secrets.DOCKER_USERNAME }} + registry_password: ${{ secrets.DOCKER_PASSWORD }} +``` + +### Example 3: Build Multiarch Image with Build Arguments + +```yaml +name: Build Multiarch Image + +on: + workflow_dispatch: + inputs: + version: + description: 'Version tag' + required: true + +jobs: + build-multiarch: + uses: fricker-studios/github-actions/.github/workflows/build-multiarch-image.yml@main + with: + image_name: myorg/myapp + platforms: linux/amd64,linux/arm64,linux/arm/v7 + build_args: VERSION=${{ github.event.inputs.version }},BUILD_DATE=${{ github.run_number }} + push: true + tags: | + latest + ${{ github.event.inputs.version }} + secrets: + registry_username: ${{ secrets.DOCKER_USERNAME }} + registry_password: ${{ secrets.DOCKER_PASSWORD }} +``` + +### Example 4: Build All Architectures in Parallel + +```yaml +name: Build All Architectures + +on: + push: + branches: [main] + +jobs: + build-arm: + uses: fricker-studios/github-actions/.github/workflows/build-arm-image.yml@main + with: + image_name: myorg/myapp + push: true + tags: latest + secrets: + registry_username: ${{ secrets.DOCKER_USERNAME }} + registry_password: ${{ secrets.DOCKER_PASSWORD }} + + build-amd: + uses: fricker-studios/github-actions/.github/workflows/build-amd-image.yml@main + with: + image_name: myorg/myapp + push: true + tags: latest + secrets: + registry_username: ${{ secrets.DOCKER_USERNAME }} + registry_password: ${{ secrets.DOCKER_PASSWORD }} +``` + +### Example 5: Using Workflow Outputs + +```yaml +name: Build and Deploy + +on: + push: + branches: [main] + +jobs: + build: + uses: fricker-studios/github-actions/.github/workflows/build-multiarch-image.yml@main + with: + image_name: myorg/myapp + push: true + tags: latest + secrets: + registry_username: ${{ secrets.DOCKER_USERNAME }} + registry_password: ${{ secrets.DOCKER_PASSWORD }} + + deploy: + needs: build + runs-on: ubuntu-latest + steps: + - name: Deploy with built image + run: | + echo "Deploying image: ${{ needs.build.outputs.image_tag }}" + echo "Image digest: ${{ needs.build.outputs.digest }}" + # Add your deployment commands here +``` + +### Example 6: Building from a Subdirectory + +```yaml +name: Build from Subdirectory + +on: + push: + branches: [main] + +jobs: + build: + uses: fricker-studios/github-actions/.github/workflows/build-multiarch-image.yml@main + with: + image_name: myorg/myapp + dockerfile_path: ./services/api/Dockerfile + context: ./services/api + push: true + secrets: + registry_username: ${{ secrets.DOCKER_USERNAME }} + registry_password: ${{ secrets.DOCKER_PASSWORD }} +``` + +## Docker Registry Support + +These workflows support any Docker registry that uses standard authentication: + +- **Docker Hub**: Use your Docker Hub username and personal access token +- **GitHub Container Registry (ghcr.io)**: Use `${{ github.actor }}` and `${{ secrets.GITHUB_TOKEN }}` +- **AWS ECR**: Use AWS credentials configured for ECR +- **Google Container Registry (GCR)**: Use service account credentials +- **Azure Container Registry (ACR)**: Use service principal credentials + +### Example: Using GitHub Container Registry + +```yaml +jobs: + build: + uses: fricker-studios/github-actions/.github/workflows/build-multiarch-image.yml@main + with: + image_name: ghcr.io/${{ github.repository }} + push: true + tags: | + latest + ${{ github.sha }} + secrets: + registry_username: ${{ github.actor }} + registry_password: ${{ secrets.GITHUB_TOKEN }} +``` + +## Features + +- ✅ **Multi-architecture support**: Build for ARM64, AMD64, or both +- ✅ **Caching**: Automatic GitHub Actions cache for faster builds +- ✅ **Flexible tagging**: Support for multiple tags per build +- ✅ **Build arguments**: Pass custom build arguments to Docker +- ✅ **Registry agnostic**: Works with any Docker registry +- ✅ **Output support**: Access image tags and digests in dependent jobs +- ✅ **QEMU support**: Cross-platform builds using QEMU emulation +- ✅ **Buildx integration**: Uses Docker Buildx for advanced features + +## Prerequisites + +To use these workflows, your repository needs: + +1. A valid `Dockerfile` in your repository +2. Docker registry credentials configured as GitHub secrets (if pushing images) +3. Appropriate permissions for the GitHub token (if using GHCR) + +## Troubleshooting + +### Build fails with "unauthorized" error +- Verify your registry credentials are correct +- Check that the secrets are properly configured in your repository settings +- Ensure the `push` input is set to `true` if you want to push images + +### Build is slow or times out +- Consider using build caching more effectively +- Split multiarch builds into separate jobs if needed +- Optimize your Dockerfile for layer caching + +### Platform not supported +- Check that your base image supports the target platform +- Verify QEMU is properly set up (handled automatically by the workflow) + +## Contributing + +To contribute to these reusable workflows: + +1. Fork this repository +2. Create a feature branch +3. Make your changes +4. Test the workflows in your fork +5. Submit a pull request + +## License + +This project is open source and available under the MIT License. + +## Support + +For issues, questions, or contributions, please open an issue in this repository. From 577e9568aa768be3f29513c20e0b7dc55e98b091 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Feb 2026 21:46:16 +0000 Subject: [PATCH 3/9] Fix job output references to use correct step IDs Co-authored-by: alexfricker <67075196+alexfricker@users.noreply.github.com> --- .github/workflows/build-amd-image.yml | 2 +- .github/workflows/build-arm-image.yml | 2 +- .github/workflows/build-multiarch-image.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-amd-image.yml b/.github/workflows/build-amd-image.yml index 20d6b23..45efe18 100644 --- a/.github/workflows/build-amd-image.yml +++ b/.github/workflows/build-amd-image.yml @@ -51,7 +51,7 @@ jobs: build-amd: runs-on: ubuntu-latest outputs: - image_tag: ${{ steps.build.outputs.image_tag }} + image_tag: ${{ steps.output.outputs.image_tag }} digest: ${{ steps.build.outputs.digest }} steps: diff --git a/.github/workflows/build-arm-image.yml b/.github/workflows/build-arm-image.yml index 801a93f..3ba53ad 100644 --- a/.github/workflows/build-arm-image.yml +++ b/.github/workflows/build-arm-image.yml @@ -51,7 +51,7 @@ jobs: build-arm: runs-on: ubuntu-latest outputs: - image_tag: ${{ steps.build.outputs.image_tag }} + image_tag: ${{ steps.output.outputs.image_tag }} digest: ${{ steps.build.outputs.digest }} steps: diff --git a/.github/workflows/build-multiarch-image.yml b/.github/workflows/build-multiarch-image.yml index 5200aab..17707e2 100644 --- a/.github/workflows/build-multiarch-image.yml +++ b/.github/workflows/build-multiarch-image.yml @@ -56,7 +56,7 @@ jobs: build-multiarch: runs-on: ubuntu-latest outputs: - image_tag: ${{ steps.build.outputs.image_tag }} + image_tag: ${{ steps.output.outputs.image_tag }} digest: ${{ steps.build.outputs.digest }} steps: From ba72a10ac7eced2724291b4746acd36def2ac654 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Feb 2026 21:47:10 +0000 Subject: [PATCH 4/9] Standardize tag naming convention to use consistent suffix format Co-authored-by: alexfricker <67075196+alexfricker@users.noreply.github.com> --- .github/workflows/build-amd-image.yml | 6 +++--- .github/workflows/build-arm-image.yml | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-amd-image.yml b/.github/workflows/build-amd-image.yml index 45efe18..6ad4e95 100644 --- a/.github/workflows/build-amd-image.yml +++ b/.github/workflows/build-amd-image.yml @@ -87,7 +87,7 @@ jobs: - name: Prepare tags id: prepare-tags run: | - TAGS="${{ inputs.image_name }}:amd64-${{ github.sha }}" + TAGS="${{ inputs.image_name }}:${{ github.sha }}-amd64" if [ -n "${{ inputs.tags }}" ]; then while IFS= read -r tag; do if [ -n "$tag" ]; then @@ -114,8 +114,8 @@ jobs: - name: Output image information id: output run: | - echo "image_tag=${{ inputs.image_name }}:amd64-${{ github.sha }}" >> $GITHUB_OUTPUT + echo "image_tag=${{ inputs.image_name }}:${{ github.sha }}-amd64" >> $GITHUB_OUTPUT echo "digest=${{ steps.build.outputs.digest }}" >> $GITHUB_OUTPUT echo "✅ AMD64 image built successfully" - echo "Image: ${{ inputs.image_name }}:amd64-${{ github.sha }}" + echo "Image: ${{ inputs.image_name }}:${{ github.sha }}-amd64" echo "Digest: ${{ steps.build.outputs.digest }}" diff --git a/.github/workflows/build-arm-image.yml b/.github/workflows/build-arm-image.yml index 3ba53ad..3cb40e7 100644 --- a/.github/workflows/build-arm-image.yml +++ b/.github/workflows/build-arm-image.yml @@ -87,7 +87,7 @@ jobs: - name: Prepare tags id: prepare-tags run: | - TAGS="${{ inputs.image_name }}:arm64-${{ github.sha }}" + TAGS="${{ inputs.image_name }}:${{ github.sha }}-arm64" if [ -n "${{ inputs.tags }}" ]; then while IFS= read -r tag; do if [ -n "$tag" ]; then @@ -114,8 +114,8 @@ jobs: - name: Output image information id: output run: | - echo "image_tag=${{ inputs.image_name }}:arm64-${{ github.sha }}" >> $GITHUB_OUTPUT + echo "image_tag=${{ inputs.image_name }}:${{ github.sha }}-arm64" >> $GITHUB_OUTPUT echo "digest=${{ steps.build.outputs.digest }}" >> $GITHUB_OUTPUT echo "✅ ARM64 image built successfully" - echo "Image: ${{ inputs.image_name }}:arm64-${{ github.sha }}" + echo "Image: ${{ inputs.image_name }}:${{ github.sha }}-arm64" echo "Digest: ${{ steps.build.outputs.digest }}" From 642cf654521efdf4d680fd46e0536dfa4c7e2b9b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Feb 2026 21:55:43 +0000 Subject: [PATCH 5/9] Convert workflows to use self-hosted runners instead of QEMU Co-authored-by: alexfricker <67075196+alexfricker@users.noreply.github.com> --- .github/workflows/build-amd-image.yml | 11 +- .github/workflows/build-arm-image.yml | 11 +- .github/workflows/build-multiarch-image.yml | 131 ++++++++++++-------- README.md | 60 +++++---- 4 files changed, 121 insertions(+), 92 deletions(-) diff --git a/.github/workflows/build-amd-image.yml b/.github/workflows/build-amd-image.yml index 6ad4e95..c4220a0 100644 --- a/.github/workflows/build-amd-image.yml +++ b/.github/workflows/build-amd-image.yml @@ -49,7 +49,7 @@ on: jobs: build-amd: - runs-on: ubuntu-latest + runs-on: [self-hosted, X64] outputs: image_tag: ${{ steps.output.outputs.image_tag }} digest: ${{ steps.build.outputs.digest }} @@ -58,14 +58,6 @@ jobs: - name: Checkout code uses: actions/checkout@v4 - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - with: - platforms: linux/amd64 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - name: Login to Docker Registry if: inputs.push == true && secrets.registry_username != '' && secrets.registry_password != '' uses: docker/login-action@v3 @@ -104,7 +96,6 @@ jobs: with: context: ${{ inputs.context }} file: ${{ inputs.dockerfile_path }} - platforms: linux/amd64 push: ${{ inputs.push }} tags: ${{ steps.prepare-tags.outputs.tags }} build-args: ${{ steps.prepare-args.outputs.build_args }} diff --git a/.github/workflows/build-arm-image.yml b/.github/workflows/build-arm-image.yml index 3cb40e7..98fc7d1 100644 --- a/.github/workflows/build-arm-image.yml +++ b/.github/workflows/build-arm-image.yml @@ -49,7 +49,7 @@ on: jobs: build-arm: - runs-on: ubuntu-latest + runs-on: [self-hosted, ARM64] outputs: image_tag: ${{ steps.output.outputs.image_tag }} digest: ${{ steps.build.outputs.digest }} @@ -58,14 +58,6 @@ jobs: - name: Checkout code uses: actions/checkout@v4 - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - with: - platforms: linux/arm64 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - name: Login to Docker Registry if: inputs.push == true && secrets.registry_username != '' && secrets.registry_password != '' uses: docker/login-action@v3 @@ -104,7 +96,6 @@ jobs: with: context: ${{ inputs.context }} file: ${{ inputs.dockerfile_path }} - platforms: linux/arm64 push: ${{ inputs.push }} tags: ${{ steps.prepare-tags.outputs.tags }} build-args: ${{ steps.prepare-args.outputs.build_args }} diff --git a/.github/workflows/build-multiarch-image.yml b/.github/workflows/build-multiarch-image.yml index 17707e2..fc956fc 100644 --- a/.github/workflows/build-multiarch-image.yml +++ b/.github/workflows/build-multiarch-image.yml @@ -17,11 +17,6 @@ on: required: false type: string default: '.' - platforms: - description: 'Target platforms (comma-separated)' - required: false - type: string - default: 'linux/amd64,linux/arm64' build_args: description: 'Build arguments (comma-separated KEY=VALUE pairs)' required: false @@ -47,30 +42,46 @@ on: outputs: image_tag: description: 'Full image tag that was built' - value: ${{ jobs.build-multiarch.outputs.image_tag }} + value: ${{ jobs.create-manifest.outputs.image_tag }} digest: description: 'Image digest' - value: ${{ jobs.build-multiarch.outputs.digest }} + value: ${{ jobs.create-manifest.outputs.digest }} jobs: - build-multiarch: + build-arm: + uses: ./.github/workflows/build-arm-image.yml + with: + image_name: ${{ inputs.image_name }} + dockerfile_path: ${{ inputs.dockerfile_path }} + context: ${{ inputs.context }} + build_args: ${{ inputs.build_args }} + push: ${{ inputs.push }} + tags: ${{ inputs.tags }} + secrets: + registry_username: ${{ secrets.registry_username }} + registry_password: ${{ secrets.registry_password }} + + build-amd: + uses: ./.github/workflows/build-amd-image.yml + with: + image_name: ${{ inputs.image_name }} + dockerfile_path: ${{ inputs.dockerfile_path }} + context: ${{ inputs.context }} + build_args: ${{ inputs.build_args }} + push: ${{ inputs.push }} + tags: ${{ inputs.tags }} + secrets: + registry_username: ${{ secrets.registry_username }} + registry_password: ${{ secrets.registry_password }} + + create-manifest: + needs: [build-arm, build-amd] runs-on: ubuntu-latest outputs: image_tag: ${{ steps.output.outputs.image_tag }} - digest: ${{ steps.build.outputs.digest }} + digest: ${{ steps.manifest.outputs.digest }} steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - with: - platforms: all - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - name: Login to Docker Registry if: inputs.push == true && secrets.registry_username != '' && secrets.registry_password != '' uses: docker/login-action@v3 @@ -78,50 +89,70 @@ jobs: username: ${{ secrets.registry_username }} password: ${{ secrets.registry_password }} - - name: Prepare build args - id: prepare-args + - name: Prepare manifest tags + id: prepare-tags run: | - if [ -n "${{ inputs.build_args }}" ]; then - echo "build_args<> $GITHUB_OUTPUT - echo "${{ inputs.build_args }}" | tr ',' '\n' >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT - else - echo "build_args=" >> $GITHUB_OUTPUT + MANIFEST_TAGS="${{ inputs.image_name }}:${{ github.sha }}" + if [ -n "${{ inputs.tags }}" ]; then + while IFS= read -r tag; do + if [ -n "$tag" ]; then + MANIFEST_TAGS="$MANIFEST_TAGS ${{ inputs.image_name }}:$tag" + fi + done <<< "${{ inputs.tags }}" fi + echo "manifest_tags=$MANIFEST_TAGS" >> $GITHUB_OUTPUT + echo "Manifest tags: $MANIFEST_TAGS" - - name: Prepare tags - id: prepare-tags + - name: Create and push multiarch manifest + id: manifest + if: inputs.push == true run: | - TAGS="${{ inputs.image_name }}:${{ github.sha }}" + # Get the first manifest tag as the primary tag + PRIMARY_TAG="${{ inputs.image_name }}:${{ github.sha }}" + + # Create manifest with ARM and AMD images + docker manifest create $PRIMARY_TAG \ + ${{ needs.build-arm.outputs.image_tag }} \ + ${{ needs.build-amd.outputs.image_tag }} + + # Push the manifest + docker manifest push $PRIMARY_TAG + + # Get the digest + DIGEST=$(docker manifest inspect $PRIMARY_TAG | grep -oP '"digest":\s*"\K[^"]+' | head -1) + echo "digest=$DIGEST" >> $GITHUB_OUTPUT + + # Create and push additional tags if provided if [ -n "${{ inputs.tags }}" ]; then while IFS= read -r tag; do if [ -n "$tag" ]; then - TAGS="$TAGS,${{ inputs.image_name }}:$tag" + TAG="${{ inputs.image_name }}:$tag" + docker manifest create $TAG \ + ${{ needs.build-arm.outputs.image_tag }} \ + ${{ needs.build-amd.outputs.image_tag }} + docker manifest push $TAG + echo "✅ Pushed manifest: $TAG" fi done <<< "${{ inputs.tags }}" fi - echo "tags=$TAGS" >> $GITHUB_OUTPUT - echo "Built tags: $TAGS" - - name: Build and push multiarch image - id: build - uses: docker/build-push-action@v5 - with: - context: ${{ inputs.context }} - file: ${{ inputs.dockerfile_path }} - platforms: ${{ inputs.platforms }} - push: ${{ inputs.push }} - tags: ${{ steps.prepare-tags.outputs.tags }} - build-args: ${{ steps.prepare-args.outputs.build_args }} - cache-from: type=gha - cache-to: type=gha,mode=max + echo "✅ Multiarch manifest created and pushed successfully" + echo "Primary tag: $PRIMARY_TAG" + echo "Digest: $DIGEST" + + - name: Create local manifest (no push) + if: inputs.push == false + run: | + echo "⚠️ Manifest creation skipped (push=false)" + echo "Would create manifest from:" + echo " - ARM: ${{ needs.build-arm.outputs.image_tag }}" + echo " - AMD: ${{ needs.build-amd.outputs.image_tag }}" - name: Output image information id: output run: | echo "image_tag=${{ inputs.image_name }}:${{ github.sha }}" >> $GITHUB_OUTPUT - echo "digest=${{ steps.build.outputs.digest }}" >> $GITHUB_OUTPUT - echo "✅ Multiarch image built successfully" - echo "Platforms: ${{ inputs.platforms }}" + echo "✅ Multiarch workflow completed" echo "Image: ${{ inputs.image_name }}:${{ github.sha }}" - echo "Digest: ${{ steps.build.outputs.digest }}" + echo "ARM digest: ${{ needs.build-arm.outputs.digest }}" + echo "AMD digest: ${{ needs.build-amd.outputs.digest }}" diff --git a/README.md b/README.md index dd9f4c1..9bc2780 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,25 @@ # GitHub Actions - Reusable Workflows -This repository contains reusable GitHub Actions workflows for building Docker images across different architectures. +This repository contains reusable GitHub Actions workflows for building Docker images across different architectures using self-hosted runners. ## Available Workflows ### 1. Build ARM Image (`build-arm-image.yml`) -Builds a Docker image specifically for ARM64 architecture (linux/arm64). +Builds a Docker image specifically for ARM64 architecture (linux/arm64) on self-hosted ARM64 runners. ### 2. Build AMD Image (`build-amd-image.yml`) -Builds a Docker image specifically for AMD64 architecture (linux/amd64). +Builds a Docker image specifically for AMD64 architecture (linux/amd64) on self-hosted X64 runners. ### 3. Build Multiarch Image (`build-multiarch-image.yml`) -Builds a Docker image for multiple architectures in a single manifest (default: linux/amd64 and linux/arm64). +Builds ARM64 and AMD64 images in parallel using the ARM and AMD workflows, then creates a multiarch manifest combining both architectures. + +## Prerequisites + +⚠️ **Important**: These workflows require self-hosted runners with the following labels: +- **ARM64**: Self-hosted runners tagged with `ARM64` label for ARM64 builds +- **X64**: Self-hosted runners tagged with `X64` label for AMD64 builds + +Make sure your self-hosted runners have Docker installed and properly configured before using these workflows. ## Quick Start @@ -51,12 +59,6 @@ All three workflows share the following common inputs: | `push` | Whether to push the image to registry | No | `false` | | `tags` | Additional tags for the image (newline-separated) | No | `''` | -**Additional input for multiarch workflow:** - -| Input | Description | Required | Default | -|-------|-------------|----------|---------| -| `platforms` | Target platforms (comma-separated) | No | `linux/amd64,linux/arm64` | - ## Workflow Secrets | Secret | Description | Required | @@ -136,7 +138,6 @@ jobs: uses: fricker-studios/github-actions/.github/workflows/build-multiarch-image.yml@main with: image_name: myorg/myapp - platforms: linux/amd64,linux/arm64,linux/arm/v7 build_args: VERSION=${{ github.event.inputs.version }},BUILD_DATE=${{ github.run_number }} push: true tags: | @@ -147,7 +148,11 @@ jobs: registry_password: ${{ secrets.DOCKER_PASSWORD }} ``` -### Example 4: Build All Architectures in Parallel +Note: The multiarch workflow automatically builds for both AMD64 and ARM64 architectures using separate self-hosted runners in parallel. + +### Example 4: Build All Architectures in Parallel (Manual Control) + +If you need to manually control individual architecture builds: ```yaml name: Build All Architectures @@ -261,24 +266,34 @@ jobs: ## Features - ✅ **Multi-architecture support**: Build for ARM64, AMD64, or both +- ✅ **Native architecture builds**: Uses self-hosted runners (no QEMU emulation overhead) +- ✅ **Parallel builds**: ARM and AMD builds run simultaneously for faster execution - ✅ **Caching**: Automatic GitHub Actions cache for faster builds - ✅ **Flexible tagging**: Support for multiple tags per build - ✅ **Build arguments**: Pass custom build arguments to Docker - ✅ **Registry agnostic**: Works with any Docker registry - ✅ **Output support**: Access image tags and digests in dependent jobs -- ✅ **QEMU support**: Cross-platform builds using QEMU emulation -- ✅ **Buildx integration**: Uses Docker Buildx for advanced features +- ✅ **Manifest creation**: Multiarch workflow automatically creates and pushes Docker manifests ## Prerequisites -To use these workflows, your repository needs: +To use these workflows, you need: -1. A valid `Dockerfile` in your repository -2. Docker registry credentials configured as GitHub secrets (if pushing images) -3. Appropriate permissions for the GitHub token (if using GHCR) +1. **Self-hosted runners** with the following labels: + - `ARM64` label for ARM64 builds + - `X64` label for AMD64 builds + - Docker installed and properly configured on all runners +2. A valid `Dockerfile` in your repository +3. Docker registry credentials configured as GitHub secrets (if pushing images) +4. Appropriate permissions for the GitHub token (if using GHCR) ## Troubleshooting +### Build fails with "No runner matching the specified labels" +- Ensure you have self-hosted runners configured with `ARM64` and `X64` labels +- Verify the runners are online and available +- Check that Docker is installed on the runners + ### Build fails with "unauthorized" error - Verify your registry credentials are correct - Check that the secrets are properly configured in your repository settings @@ -286,12 +301,13 @@ To use these workflows, your repository needs: ### Build is slow or times out - Consider using build caching more effectively -- Split multiarch builds into separate jobs if needed +- Ensure your self-hosted runners have adequate resources - Optimize your Dockerfile for layer caching -### Platform not supported -- Check that your base image supports the target platform -- Verify QEMU is properly set up (handled automatically by the workflow) +### Manifest creation fails +- Ensure both ARM and AMD builds completed successfully +- Verify the images were pushed to the registry (push must be true) +- Check that the registry supports Docker manifests ## Contributing From a412dff9d8ebcbc7543e4ad651b919b1d53c28a6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Feb 2026 21:56:46 +0000 Subject: [PATCH 6/9] Improve manifest creation with better JSON parsing and reduced duplication Co-authored-by: alexfricker <67075196+alexfricker@users.noreply.github.com> --- .github/workflows/build-multiarch-image.yml | 52 +++++++++++---------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/.github/workflows/build-multiarch-image.yml b/.github/workflows/build-multiarch-image.yml index fc956fc..25302eb 100644 --- a/.github/workflows/build-multiarch-image.yml +++ b/.github/workflows/build-multiarch-image.yml @@ -107,38 +107,40 @@ jobs: id: manifest if: inputs.push == true run: | - # Get the first manifest tag as the primary tag - PRIMARY_TAG="${{ inputs.image_name }}:${{ github.sha }}" - - # Create manifest with ARM and AMD images - docker manifest create $PRIMARY_TAG \ - ${{ needs.build-arm.outputs.image_tag }} \ - ${{ needs.build-amd.outputs.image_tag }} - - # Push the manifest - docker manifest push $PRIMARY_TAG - - # Get the digest - DIGEST=$(docker manifest inspect $PRIMARY_TAG | grep -oP '"digest":\s*"\K[^"]+' | head -1) - echo "digest=$DIGEST" >> $GITHUB_OUTPUT - - # Create and push additional tags if provided + # Create list of all tags to manifest + TAGS=("${{ inputs.image_name }}:${{ github.sha }}") if [ -n "${{ inputs.tags }}" ]; then while IFS= read -r tag; do if [ -n "$tag" ]; then - TAG="${{ inputs.image_name }}:$tag" - docker manifest create $TAG \ - ${{ needs.build-arm.outputs.image_tag }} \ - ${{ needs.build-amd.outputs.image_tag }} - docker manifest push $TAG - echo "✅ Pushed manifest: $TAG" + TAGS+=("${{ inputs.image_name }}:$tag") fi done <<< "${{ inputs.tags }}" fi - echo "✅ Multiarch manifest created and pushed successfully" - echo "Primary tag: $PRIMARY_TAG" - echo "Digest: $DIGEST" + # Create and push manifest for each tag + DIGEST="" + for TAG in "${TAGS[@]}"; do + echo "Creating manifest for: $TAG" + docker manifest create "$TAG" \ + ${{ needs.build-arm.outputs.image_tag }} \ + ${{ needs.build-amd.outputs.image_tag }} + docker manifest push "$TAG" + + # Get the digest from the first tag + if [ -z "$DIGEST" ]; then + if command -v jq &> /dev/null; then + DIGEST=$(docker manifest inspect "$TAG" | jq -r '.manifests[0].digest') + else + DIGEST=$(docker manifest inspect "$TAG" | grep -oP '"digest":\s*"\K[^"]+' | head -1) + fi + fi + echo "✅ Pushed manifest: $TAG" + done + + echo "digest=$DIGEST" >> $GITHUB_OUTPUT + echo "✅ All multiarch manifests created and pushed successfully" + echo "Primary tag: ${{ inputs.image_name }}:${{ github.sha }}" + echo "Manifest digest: $DIGEST" - name: Create local manifest (no push) if: inputs.push == false From 49708a45f6796b8bda5ea29b6846420679009b8b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Feb 2026 22:14:10 +0000 Subject: [PATCH 7/9] Add logging to show ARM and AMD image tags used in manifest creation Co-authored-by: alexfricker <67075196+alexfricker@users.noreply.github.com> --- .github/workflows/build-multiarch-image.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/build-multiarch-image.yml b/.github/workflows/build-multiarch-image.yml index 25302eb..8bfee32 100644 --- a/.github/workflows/build-multiarch-image.yml +++ b/.github/workflows/build-multiarch-image.yml @@ -107,6 +107,10 @@ jobs: id: manifest if: inputs.push == true run: | + echo "Using ARM image: ${{ needs.build-arm.outputs.image_tag }}" + echo "Using AMD image: ${{ needs.build-amd.outputs.image_tag }}" + echo "" + # Create list of all tags to manifest TAGS=("${{ inputs.image_name }}:${{ github.sha }}") if [ -n "${{ inputs.tags }}" ]; then From ac23b1e4466f7e8f496ca9b112d759017ea415c7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Feb 2026 22:29:01 +0000 Subject: [PATCH 8/9] Add architecture-specific parameters to multiarch workflow Co-authored-by: alexfricker <67075196+alexfricker@users.noreply.github.com> --- .github/workflows/build-multiarch-image.yml | 48 +++++++++++++++++---- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build-multiarch-image.yml b/.github/workflows/build-multiarch-image.yml index 8bfee32..95d560d 100644 --- a/.github/workflows/build-multiarch-image.yml +++ b/.github/workflows/build-multiarch-image.yml @@ -8,17 +8,47 @@ on: required: true type: string dockerfile_path: - description: 'Path to the Dockerfile' + description: 'Path to the Dockerfile (used for both architectures unless overridden)' required: false type: string default: './Dockerfile' context: - description: 'Build context path' + description: 'Build context path (used for both architectures unless overridden)' required: false type: string default: '.' build_args: - description: 'Build arguments (comma-separated KEY=VALUE pairs)' + description: 'Build arguments (comma-separated KEY=VALUE pairs, used for both architectures unless overridden)' + required: false + type: string + default: '' + arm_dockerfile_path: + description: 'Path to the Dockerfile for ARM build (overrides dockerfile_path for ARM)' + required: false + type: string + default: '' + arm_context: + description: 'Build context path for ARM build (overrides context for ARM)' + required: false + type: string + default: '' + arm_build_args: + description: 'Build arguments for ARM build (overrides build_args for ARM)' + required: false + type: string + default: '' + amd_dockerfile_path: + description: 'Path to the Dockerfile for AMD build (overrides dockerfile_path for AMD)' + required: false + type: string + default: '' + amd_context: + description: 'Build context path for AMD build (overrides context for AMD)' + required: false + type: string + default: '' + amd_build_args: + description: 'Build arguments for AMD build (overrides build_args for AMD)' required: false type: string default: '' @@ -52,9 +82,9 @@ jobs: uses: ./.github/workflows/build-arm-image.yml with: image_name: ${{ inputs.image_name }} - dockerfile_path: ${{ inputs.dockerfile_path }} - context: ${{ inputs.context }} - build_args: ${{ inputs.build_args }} + dockerfile_path: ${{ inputs.arm_dockerfile_path != '' && inputs.arm_dockerfile_path || inputs.dockerfile_path }} + context: ${{ inputs.arm_context != '' && inputs.arm_context || inputs.context }} + build_args: ${{ inputs.arm_build_args != '' && inputs.arm_build_args || inputs.build_args }} push: ${{ inputs.push }} tags: ${{ inputs.tags }} secrets: @@ -65,9 +95,9 @@ jobs: uses: ./.github/workflows/build-amd-image.yml with: image_name: ${{ inputs.image_name }} - dockerfile_path: ${{ inputs.dockerfile_path }} - context: ${{ inputs.context }} - build_args: ${{ inputs.build_args }} + dockerfile_path: ${{ inputs.amd_dockerfile_path != '' && inputs.amd_dockerfile_path || inputs.dockerfile_path }} + context: ${{ inputs.amd_context != '' && inputs.amd_context || inputs.context }} + build_args: ${{ inputs.amd_build_args != '' && inputs.amd_build_args || inputs.build_args }} push: ${{ inputs.push }} tags: ${{ inputs.tags }} secrets: From e3944633da6620908a55f4bfb189d09445c19657 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Feb 2026 22:29:59 +0000 Subject: [PATCH 9/9] Document architecture-specific parameters in README Co-authored-by: alexfricker <67075196+alexfricker@users.noreply.github.com> --- README.md | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 61 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9bc2780..52ba6ec 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,8 @@ jobs: ## Workflow Inputs +### Common Inputs (All Workflows) + All three workflows share the following common inputs: | Input | Description | Required | Default | @@ -59,6 +61,21 @@ All three workflows share the following common inputs: | `push` | Whether to push the image to registry | No | `false` | | `tags` | Additional tags for the image (newline-separated) | No | `''` | +### Architecture-Specific Inputs (Multiarch Workflow Only) + +The multiarch workflow supports optional architecture-specific parameters that override the common parameters for each architecture: + +| Input | Description | Required | Default | +|-------|-------------|----------|---------| +| `arm_dockerfile_path` | Path to the Dockerfile for ARM build (overrides `dockerfile_path`) | No | `''` | +| `arm_context` | Build context path for ARM build (overrides `context`) | No | `''` | +| `arm_build_args` | Build arguments for ARM build (overrides `build_args`) | No | `''` | +| `amd_dockerfile_path` | Path to the Dockerfile for AMD build (overrides `dockerfile_path`) | No | `''` | +| `amd_context` | Build context path for AMD build (overrides `context`) | No | `''` | +| `amd_build_args` | Build arguments for AMD build (overrides `build_args`) | No | `''` | + +**Note**: If architecture-specific parameters are not provided (empty string), the workflow will use the common parameters (`dockerfile_path`, `context`, `build_args`) for both architectures. + ## Workflow Secrets | Secret | Description | Required | @@ -150,7 +167,48 @@ jobs: Note: The multiarch workflow automatically builds for both AMD64 and ARM64 architectures using separate self-hosted runners in parallel. -### Example 4: Build All Architectures in Parallel (Manual Control) +### Example 4: Build Multiarch Image with Architecture-Specific Dockerfiles + +If you need different Dockerfiles or build contexts for ARM and AMD: + +```yaml +name: Build Multiarch Image with Different Dockerfiles + +on: + push: + branches: [main] + +jobs: + build-multiarch: + uses: fricker-studios/github-actions/.github/workflows/build-multiarch-image.yml@main + with: + image_name: myorg/myapp + # Common parameters (used if architecture-specific ones aren't provided) + dockerfile_path: ./Dockerfile + context: . + # ARM-specific overrides + arm_dockerfile_path: ./Dockerfile.arm64 + arm_context: ./arm + arm_build_args: ARCH=arm64,OPTIMIZATION=neon + # AMD-specific overrides + amd_dockerfile_path: ./Dockerfile.amd64 + amd_context: ./amd + amd_build_args: ARCH=amd64,OPTIMIZATION=avx2 + push: true + tags: | + latest + v1.0.0 + secrets: + registry_username: ${{ secrets.DOCKER_USERNAME }} + registry_password: ${{ secrets.DOCKER_PASSWORD }} +``` + +This allows you to optimize each architecture build separately, for example: +- Using architecture-specific optimizations in build arguments +- Different Dockerfiles that install architecture-specific dependencies +- Separate build contexts with architecture-specific files + +### Example 5: Build All Architectures in Parallel (Manual Control) If you need to manually control individual architecture builds: @@ -183,7 +241,7 @@ jobs: registry_password: ${{ secrets.DOCKER_PASSWORD }} ``` -### Example 5: Using Workflow Outputs +### Example 6: Using Workflow Outputs ```yaml name: Build and Deploy @@ -214,7 +272,7 @@ jobs: # Add your deployment commands here ``` -### Example 6: Building from a Subdirectory +### Example 7: Building from a Subdirectory ```yaml name: Build from Subdirectory