Skip to content

Commit

Permalink
Adds support for multi-platform builds (#119) (#107)
Browse files Browse the repository at this point in the history
  • Loading branch information
mr-smithers-excellent authored Mar 3, 2023
1 parent 0e68818 commit a6df1cf
Show file tree
Hide file tree
Showing 10 changed files with 249 additions and 41 deletions.
3 changes: 3 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
"node": true,
"jest": true
},
"parserOptions": {
"ecmaVersion": 2021
},
"rules": {
"prettier/prettier": [
"error",
Expand Down
8 changes: 8 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
version: 2
updates:
- package-ecosystem: npm
directory: "/"
schedule:
interval: daily
open-pull-requests-limit: 10
target-branch: 'master'
13 changes: 11 additions & 2 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ jobs:
addLatest: true
labels: org.opencontainers.image.description="A Hello World image used for e2e tests"

- name: Multi-platform e2e
image: mrsmithers/hello-world
dockerfile: ./e2e/Dockerfile
registry: docker.io
username: DOCKERHUB_USERNAME
password: DOCKERHUB_PASSWORD
multiPlatform: true
platform: linux/amd64,linux/arm64,linux/arm/v7

- name: GCR e2e
image: orbital-bank-301021/hello-world
dockerfile: ./e2e/Dockerfile
Expand Down Expand Up @@ -70,7 +79,7 @@ jobs:

- name: Create check run
if: ${{ inputs.pr-trigger }}
uses: actions/github-script@v5
uses: actions/github-script@v6
env:
name: ${{ matrix.name }}
number: ${{ github.event.client_payload.pull_request.number }}
Expand Down Expand Up @@ -117,7 +126,7 @@ jobs:
- name: Update check run
if: ${{ inputs.pr-trigger }}
uses: actions/github-script@v5
uses: actions/github-script@v6
env:
number: ${{ github.event.client_payload.pull_request.number }}
job: ${{ github.job }}
Expand Down
104 changes: 92 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,23 @@ Builds a Docker image and pushes it to the private registry of your choosing.
- AWS Elastic Container Registry (ECR)
- GitHub Docker Registry

## Features

- [Auto-tagging with GitOps](#auto-tagging-with-gitops)
- [BuildKit support](#buildkit-support)
- [Multi-platform builds](#multi-platform-builds)

## Breaking changes

If you're experiencing issues, be sure you are using the [latest stable release](https://github.com/mr-smithers-excellent/docker-build-push/releases/latest) (currently v5). The AWS ECR login command became deprecated between v4 and v5. Additionally, support for multiple tags was added between v4 and v5.
If you're experiencing issues, be sure you are using the [latest stable release](https://github.com/mr-smithers-excellent/docker-build-push/releases/latest) (currently v6).

### v6
- Multi-platform builds now supported

### v5
- AWS ECR [get-login command](https://docs.aws.amazon.com/cli/latest/reference/ecr/get-login.html) became deprecated, migrated to [get-login-password command](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ecr/get-login-password.html)
- Support for multiple tags added
- BuildKit support added

## Basic usage

Expand All @@ -25,10 +39,10 @@ If you're experiencing issues, be sure you are using the [latest stable release]

```yaml
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
name: Check out code

- uses: mr-smithers-excellent/docker-build-push@v5
- uses: mr-smithers-excellent/docker-build-push@v6
name: Build & push Docker image
with:
image: repo/image
Expand All @@ -42,7 +56,7 @@ steps:
## Inputs
| Name | Description | Required | Type |
| -------------- | -------------------------------------------------------------------------------------------------------- | -------- | ------- |
|----------------|----------------------------------------------------------------------------------------------------------|----------|---------|
| image | Docker image name | Yes | String |
| tags | Comma separated docker image tags (see [Tagging the image with GitOps](#tagging-the-image-using-gitops)) | No | List |
| addLatest | Adds the `latest` tag to the GitOps-generated tags | No | Boolean |
Expand All @@ -58,12 +72,14 @@ steps:
| password | Docker registry password or token | No | String |
| githubOrg | GitHub organization to push image to (if not current) | No | String |
| enableBuildKit | Enables Docker BuildKit support | No | Boolean |
| multiPlatform | Enables Docker buildx support | No | Boolean |
| overrideDriver | Disables setting up docker-container driver (if `true`, alternative docker driver must be set up) | No | Boolean |
| pushImage | Flag for disabling the login & push steps, set to `true` by default | No | Boolean |

## Outputs

| Name | Description | Format |
| ------------- | -------------------------------------------------- | ---------------------- |
|---------------|----------------------------------------------------|------------------------|
| imageFullName | Full name of the Docker image with registry prefix | `registry/owner/image` |
| imageName | Name of the Docker image with owner prefix | `owner/image` |
| tags | Tags for the Docker image | `v1,latest` |
Expand All @@ -82,7 +98,7 @@ There is a distinction between secrets at the [repository](https://docs.github.c
- Modify sample below and include in your workflow `.github/workflows/*.yml` file

```yaml
uses: mr-smithers-excellent/docker-build-push@v5
uses: mr-smithers-excellent/docker-build-push@v6
with:
image: docker-hub-repo/image-name
registry: docker.io
Expand All @@ -99,7 +115,7 @@ with:
- Ensure you set the username to `_json_key`

```yaml
uses: mr-smithers-excellent/docker-build-push@v5
uses: mr-smithers-excellent/docker-build-push@v6
with:
image: gcp-project/image-name
registry: gcr.io
Expand All @@ -116,7 +132,7 @@ with:
- Modify sample below and include in your workflow `.github/workflows/*.yml` file

```yaml
uses: mr-smithers-excellent/docker-build-push@v5
uses: mr-smithers-excellent/docker-build-push@v6
with:
image: image-name
registry: [aws-account-number].dkr.ecr.[region].amazonaws.com
Expand All @@ -137,7 +153,7 @@ env:
#### New ghcr.io

```yaml
uses: mr-smithers-excellent/docker-build-push@v5
uses: mr-smithers-excellent/docker-build-push@v6
with:
image: image-name
registry: ghcr.io
Expand All @@ -149,20 +165,20 @@ with:
#### Legacy docker.pkg.github.com

```yaml
uses: mr-smithers-excellent/docker-build-push@v5
uses: mr-smithers-excellent/docker-build-push@v6
with:
image: github-repo/image-name
registry: docker.pkg.github.com
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
```

## Tagging the image using GitOps
## Auto-tagging with GitOps

By default, if you do not pass a `tags` input this action will use an algorithm based on the state of your git repo to determine the Docker image tag(s). This is designed to enable developers to more easily use [GitOps](https://www.weave.works/technologies/gitops/) in their CI/CD pipelines. Below is a table detailing how the GitHub trigger (branch or tag) determines the Docker tag(s).

| Trigger | Commit SHA | addLatest | addTimestamp | Docker Tag(s) |
| ------------------------ | ---------- | --------- | ------------ | -------------------------------------- |
|--------------------------|------------|-----------|--------------|----------------------------------------|
| /refs/tags/v1.0 | N/A | false | N/A | v1.0 |
| /refs/tags/v1.0 | N/A | true | N/A | v1.0,latest |
| /refs/heads/dev | 1234567 | false | true | dev-1234567-2021-09-01.195027 |
Expand All @@ -171,3 +187,67 @@ By default, if you do not pass a `tags` input this action will use an algorithm
| /refs/heads/main | 1234567 | true | false | main-1234567,latest |
| /refs/heads/SOME-feature | 1234567 | false | true | some-feature-1234567-2021-09-01.195027 |
| /refs/heads/SOME-feature | 1234567 | true | false | some-feature-1234567,latest |

## BuildKit support

Enables [Docker BuildKit](https://docs.docker.com/build/buildkit/)

```yaml
steps:
- uses: actions/checkout@v3
name: Check out code
- uses: mr-smithers-excellent/docker-build-push@v6
name: Build & push Docker image
with:
image: repo/image
registry: docker.io
enableBuildKit: true
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
```

## Multi-platform builds

Enables [multi-platform builds](https://docs.docker.com/build/building/multi-platform/) with the default [docker-container driver](https://docs.docker.com/build/drivers/docker-container/)

```yaml
steps:
- uses: actions/checkout@v3
name: Check out code
- uses: mr-smithers-excellent/docker-build-push@v6
name: Build & push Docker image
with:
image: repo/image
registry: docker.io
multiPlatform: true
platform: linux/amd64,linux/arm64,linux/arm/v7
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
```

Enables [multi-platform builds](https://docs.docker.com/build/building/multi-platform/) with custom driver

```yaml
steps:
- uses: actions/checkout@v3
name: Check out code
# Required when overrideDriver is set to true
- uses: docker/setup-buildx-action@v2
name: Customize Docker driver
with:
driver-opts: image=moby/buildkit:v0.11.0
- uses: mr-smithers-excellent/docker-build-push@v6
name: Build & push Docker image
with:
image: repo/image
registry: docker.io
multiPlatform: true
platform: linux/amd64,linux/arm64,linux/arm/v7
overrideDriver: true
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
```
8 changes: 8 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ inputs:
description: "Enables Docker BuildKit support"
required: false
default: "false"
multiPlatform:
description: "Builds image with buildx to support multiple platforms"
required: false
default: "false"
overrideDriver:
description: "Disables setting up docker-container driver"
required: false
default: "false"
pushImage:
description: "Flag for disabling the login & push steps, set to true by default"
required: false
Expand Down
48 changes: 36 additions & 12 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9455,11 +9455,26 @@ const buildOpts = {
labels: undefined,
target: undefined,
buildDir: undefined,
multiPlatform: false,
overrideDriver: false,
enableBuildKit: false,
platform: undefined,
skipPush: false
};

const setBuildOpts = (addLatest, addTimestamp) => {
buildOpts.tags = parseArray(core.getInput('tags')) || docker.createTags(addLatest, addTimestamp);
buildOpts.multiPlatform = core.getInput('multiPlatform') === 'true';
buildOpts.overrideDriver = core.getInput('overrideDriver') === 'true';
buildOpts.buildArgs = parseArray(core.getInput('buildArgs'));
buildOpts.labels = parseArray(core.getInput('labels'));
buildOpts.target = core.getInput('target');
buildOpts.buildDir = core.getInput('directory') || '.';
buildOpts.enableBuildKit = core.getInput('enableBuildKit') === 'true';
buildOpts.platform = core.getInput('platform');
buildOpts.skipPush = core.getInput('pushImage') === 'false';
};

const run = () => {
try {
// Capture action inputs
Expand All @@ -9471,14 +9486,7 @@ const run = () => {
const githubOwner = core.getInput('githubOrg') || github.getDefaultOwner();
const addLatest = core.getInput('addLatest') === 'true';
const addTimestamp = core.getInput('addTimestamp') === 'true';
buildOpts.tags = parseArray(core.getInput('tags')) || docker.createTags(addLatest, addTimestamp);
buildOpts.buildArgs = parseArray(core.getInput('buildArgs'));
buildOpts.labels = parseArray(core.getInput('labels'));
buildOpts.target = core.getInput('target');
buildOpts.buildDir = core.getInput('directory') || '.';
buildOpts.enableBuildKit = core.getInput('enableBuildKit') === 'true';
buildOpts.platform = core.getInput('platform');
buildOpts.skipPush = core.getInput('pushImage') === 'false';
setBuildOpts(addLatest, addTimestamp);

// Create the Docker image name
const imageFullName = docker.createFullImageName(registry, image, githubOwner);
Expand All @@ -9487,7 +9495,7 @@ const run = () => {
// Log in, build & push the Docker image
docker.login(username, password, registry, buildOpts.skipPush);
docker.build(imageFullName, dockerfile, buildOpts);
docker.push(imageFullName, buildOpts.tags, buildOpts.skipPush);
docker.push(imageFullName, buildOpts.tags, buildOpts);

// Capture outputs
core.setOutput('imageFullName', imageFullName);
Expand Down Expand Up @@ -9563,7 +9571,9 @@ const createTags = (addLatest, addTimestamp) => {
// Dynamically create 'docker build' command based on inputs provided
const createBuildCommand = (imageName, dockerfile, buildOpts) => {
const tagsSuffix = buildOpts.tags.map(tag => `-t ${imageName}:${tag}`).join(' ');
let buildCommandPrefix = `docker build -f ${dockerfile} ${tagsSuffix}`;
const builder = buildOpts.multiPlatform ? 'buildx build' : 'build';

let buildCommandPrefix = `docker ${builder} -f ${dockerfile} ${tagsSuffix}`;

if (buildOpts.buildArgs) {
const argsSuffix = buildOpts.buildArgs.map(arg => `--build-arg ${arg}`).join(' ');
Expand All @@ -9583,9 +9593,14 @@ const createBuildCommand = (imageName, dockerfile, buildOpts) => {
buildCommandPrefix = `${buildCommandPrefix} --platform ${buildOpts.platform}`;
}

if (buildOpts.multiPlatform && !buildOpts.skipPush) {
buildCommandPrefix = `${buildCommandPrefix} --push`;
}

if (buildOpts.enableBuildKit) {
buildCommandPrefix = `DOCKER_BUILDKIT=1 ${buildCommandPrefix}`;
}
core.info(`BuildCommand ${buildCommandPrefix} ${buildOpts.buildDir}`);

return `${buildCommandPrefix} ${buildOpts.buildDir}`;
};
Expand All @@ -9596,6 +9611,11 @@ const build = (imageName, dockerfile, buildOpts) => {
core.setFailed(`Dockerfile does not exist in location ${dockerfile}`);
}

// Setup buildx driver
if (buildOpts.multiPlatform && !buildOpts.overrideDriver) {
cp.execSync('docker buildx create --use');
}

core.info(`Building Docker image ${imageName} with tags ${buildOpts.tags}...`);
cp.execSync(createBuildCommand(imageName, dockerfile, buildOpts), cpOptions);
};
Expand Down Expand Up @@ -9629,8 +9649,12 @@ const login = (username, password, registry, skipPush) => {
};

// Push Docker image & all tags
const push = (imageName, tags, skipPush) => {
if (skipPush) {
const push = (imageName, tags, buildOpts) => {
if (buildOpts?.multiPlatform) {
core.info('Input multiPlatform is set to true, skipping Docker push step...');
return;
}
if (buildOpts?.skipPush) {
core.info('Input skipPush is set to true, skipping Docker push step...');
return;
}
Expand Down
Loading

0 comments on commit a6df1cf

Please sign in to comment.