Merge pull request #92 from docker/v2-build-push

Build Push action v2
This commit is contained in:
Tõnis Tiigi 2020-09-04 09:47:46 -07:00 committed by GitHub
commit e01a38bb45
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 22626 additions and 292 deletions

2
.gitattributes vendored Normal file
View file

@ -0,0 +1,2 @@
/dist/** linguist-generated=true
/lib/** linguist-generated=true

1
.github/CODEOWNERS vendored Normal file
View file

@ -0,0 +1 @@
* @crazy-max

29
.github/CONTRIBUTING.md vendored Normal file
View file

@ -0,0 +1,29 @@
## Contributing
Hi there! We're thrilled that you'd like to contribute to this project. Your help is essential for keeping it great.
Contributions to this project are [released](https://help.github.com/articles/github-terms-of-service/#6-contributions-under-repository-license) to the public under the [project's open source license](LICENSE).
## Submitting a pull request
1. [Fork](https://github.com/docker/build-push-action/fork) and clone the repository
2. Configure and install the dependencies: `yarn install`
3. Make sure the tests pass on your machine: `yarn run test`
4. Create a new branch: `git checkout -b my-branch-name`
5. Make your change, add tests, and make sure the tests still pass
6. Run pre-checkin: `yarn run pre-checkin`
7. Push to your fork and [submit a pull request](https://github.com/docker/build-push-action/compare)
8. Pat your self on the back and wait for your pull request to be reviewed and merged.
Here are a few things you can do that will increase the likelihood of your pull request being accepted:
- Make sure the `README.md` and any other relevant **documentation are kept up-to-date**.
- We try to follow [SemVer v2.0.0](https://semver.org/). Randomly breaking public APIs is not an option.
- Keep your change as focused as possible. If there are multiple changes you would like to make that are not dependent upon each other, consider submitting them as **separate pull requests**.
- Write a [good commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
## Resources
- [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/)
- [Using Pull Requests](https://help.github.com/articles/about-pull-requests/)
- [GitHub Help](https://help.github.com)

33
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View file

@ -0,0 +1,33 @@
---
name: Bug report
about: Create a report to help us improve
---
### Behaviour
#### Steps to reproduce this issue
1.
2.
3.
#### Expected behaviour
> Tell us what should happen
#### Actual behaviour
> Tell us what happens instead
### Configuration
* Repository URL (if public):
* Build URL (if public):
```yml
# paste your YAML workflow file here and remove sensitive data
```
### Logs
> Download the [log file of your build](https://help.github.com/en/actions/configuring-and-managing-workflows/managing-a-workflow-run#downloading-logs) and [attach it](https://help.github.com/en/github/managing-your-work-on-github/file-attachments-on-issues-and-pull-requests) to this issue.

31
.github/SUPPORT.md vendored Normal file
View file

@ -0,0 +1,31 @@
# Support [![](https://isitmaintained.com/badge/resolution/docker/build-push-action.svg)](https://isitmaintained.com/project/docker/build-push-action)
First, [be a good guy](https://github.com/kossnocorp/etiquette/blob/master/README.md).
## Reporting an issue
Please do a search in [open issues](https://github.com/docker/build-push-action/issues?utf8=%E2%9C%93&q=) to see if the issue or feature request has already been filed.
If you find your issue already exists, make relevant comments and add your [reaction](https://github.com/blog/2119-add-reactions-to-pull-requests-issues-and-comments). Use a reaction in place of a "+1" comment.
:+1: - upvote
:-1: - downvote
If you cannot find an existing issue that describes your bug or feature, submit an issue using the guidelines below.
## Writing good bug reports and feature requests
File a single issue per problem and feature request.
* Do not enumerate multiple bugs or feature requests in the same issue.
* Do not add your issue as a comment to an existing issue unless it's for the identical input. Many issues look similar, but have different causes.
The more information you can provide, the more likely someone will be successful reproducing the issue and finding a fix.
You are now ready to [create a new issue](https://github.com/docker/build-push-action/issues/new/choose)!
## Closure policy
* Issues that don't have the information requested above (when applicable) will be closed immediately and the poster directed to the support guidelines.
* Issues that go a week without a response from original poster are subject to closure at our discretion.

BIN
.github/build-push-action.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

12
.github/dependabot.yml vendored Normal file
View file

@ -0,0 +1,12 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "daily"
allow:
- dependency-type: "production"

241
.github/workflows/ci.yml vendored Normal file
View file

@ -0,0 +1,241 @@
name: ci
on:
push:
branches:
- master
- v2-working-branch # remove when merged to master
pull_request:
branches:
- master
- v2-working-branch # remove when merged to master
jobs:
git-context:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
buildx-version:
- latest
steps:
-
name: Run local registry
run: |
docker run -d -p 5000:5000 registry:2
-
name: Checkout
uses: actions/checkout@v2.3.1
-
name: Set up QEMU
uses: docker/setup-qemu-action@master
with:
platforms: all
-
name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@master
with:
version: ${{ matrix.buildx-version }}
driver-opts: network=host
buildkitd-flags: --allow-insecure-entitlement security.insecure
-
name: Build and push
id: docker_build
uses: ./
with:
file: ./test/Dockerfile
builder: ${{ steps.buildx.outputs.name }}
platforms: linux/amd64,linux/arm64
allow: network.host,security.insecure
push: true
tags: |
localhost:5000/name/app:latest
localhost:5000/name/app:1.0.0
secrets: |
GIT_AUTH_TOKEN=${{ github.token }}
-
name: Inspect
run: |
docker buildx imagetools inspect localhost:5000/name/app:1.0.0
-
name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}
-
name: Dump context
if: always()
uses: crazy-max/ghaction-dump-context@v1
path-context:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
buildx-version:
- ""
- latest
steps:
-
name: Run local registry
run: |
docker run -d -p 5000:5000 registry:2
-
name: Checkout
uses: actions/checkout@v2.3.1
-
name: Set up QEMU
uses: docker/setup-qemu-action@master
with:
platforms: all
-
name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@master
with:
version: ${{ matrix.buildx-version }}
driver-opts: network=host
buildkitd-flags: --allow-insecure-entitlement security.insecure
-
name: Build and push
id: docker_build
uses: ./
with:
context: ./test
file: ./test/Dockerfile
builder: ${{ steps.buildx.outputs.name }}
allow: network.host,security.insecure
push: true
tags: |
localhost:5000/name/app:latest
localhost:5000/name/app:1.0.0
-
name: Inspect
run: |
docker buildx imagetools inspect localhost:5000/name/app:1.0.0
-
name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}
-
name: Dump context
if: always()
uses: crazy-max/ghaction-dump-context@v1
multi:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
buildx-version:
- ""
- latest
dockerfile:
- multi
- multi-sudo
steps:
-
name: Run local registry
run: |
docker run -d -p 5000:5000 registry:2
-
name: Checkout
uses: actions/checkout@v2.3.1
-
name: Set up QEMU
uses: docker/setup-qemu-action@master
with:
platforms: all
-
name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@master
with:
version: ${{ matrix.buildx-version }}
driver-opts: network=host
buildkitd-flags: --allow-insecure-entitlement security.insecure
-
name: Build and push
id: docker_build
uses: ./
with:
context: ./test
file: ./test/Dockerfile-${{ matrix.dockerfile }}
builder: ${{ steps.buildx.outputs.name }}
platforms: linux/amd64,linux/arm64
allow: network.host,security.insecure
push: true
tags: |
localhost:5000/name/app:latest
localhost:5000/name/app:1.0.0
-
name: Inspect
run: |
docker buildx imagetools inspect localhost:5000/name/app:1.0.0
-
name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}
-
name: Dump context
if: always()
uses: crazy-max/ghaction-dump-context@v1
github-cache:
runs-on: ubuntu-latest
steps:
-
name: Run local registry
run: |
docker run -d -p 5000:5000 registry:2
-
name: Checkout
uses: actions/checkout@v2.3.1
-
name: Set up QEMU
uses: docker/setup-qemu-action@master
with:
platforms: all
-
name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@master
with:
driver-opts: network=host
buildkitd-flags: --allow-insecure-entitlement security.insecure
-
name: Cache Docker layers
uses: actions/cache@v2
id: cache
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
-
name: Build and push
id: docker_build
uses: ./
with:
context: ./test
file: ./test/Dockerfile-multi-golang
builder: ${{ steps.buildx.outputs.name }}
platforms: linux/amd64,linux/arm64
allow: network.host,security.insecure
push: true
tags: |
localhost:5000/name/app:latest
localhost:5000/name/app:1.0.0
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache
-
name: Inspect
run: |
docker buildx imagetools inspect localhost:5000/name/app:1.0.0
-
name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}
-
name: Cache hit
run: echo ${{ steps.cache.outputs.cache-hit }}
-
name: Dump context
if: always()
uses: crazy-max/ghaction-dump-context@v1

View file

@ -1,37 +0,0 @@
name: CI
on:
push:
branches:
- master
tags:
- '*'
pull_request:
jobs:
build:
name: build
runs-on: ubuntu-latest
timeout-minutes: 3
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Run local registry
run: docker run -d -p 5000:5000 registry:2
- name: Build and push image
uses: ./
env:
DOCKER_BUILDKIT: 1
with:
registry: localhost:5000
repository: temp/workflow
tags: foo
- name: Remove local image
run: docker image rm localhost:5000/temp/workflow:foo
- name: Run image from registry
run: docker run localhost:5000/temp/workflow:foo

32
.github/workflows/test.yml vendored Normal file
View file

@ -0,0 +1,32 @@
name: test
on:
push:
branches:
- master
- v2-working-branch # remove when merged to master
pull_request:
branches:
- master
- v2-working-branch # remove when merged to master
jobs:
test:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v2.3.1
-
name: Install
run: yarn install
-
name: Test
run: yarn run test
# -
# name: Upload coverage
# uses: codecov/codecov-action@v1.0.7
# if: success()
# with:
# token: ${{ secrets.CODECOV_TOKEN }}
# file: ./coverage/clover.xml

95
.gitignore vendored Normal file
View file

@ -0,0 +1,95 @@
node_modules
lib
# Jetbrains
/.idea
/*.iml
# Rest of the file pulled from https://github.com/github/gitignore/blob/master/Node.gitignore
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
jspm_packages/
# TypeScript v1 declaration files
typings/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
# next.js build output
.next
# nuxt.js build output
.nuxt
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/

11
.prettierrc.json Normal file
View file

@ -0,0 +1,11 @@
{
"printWidth": 120,
"tabWidth": 2,
"useTabs": false,
"semi": true,
"singleQuote": true,
"trailingComma": "none",
"bracketSpacing": false,
"arrowParens": "avoid",
"parser": "typescript"
}

52
CHANGELOG.md Normal file
View file

@ -0,0 +1,52 @@
# Changelog
## 2.0.0 (2020/??/??)
v2 of Build Push action uses Docker [Buildx](https://github.com/docker/buildx). It's also rewritten as
a [typescript-action](https://github.com/actions/typescript-action/) to be as closed as possible of
the [GitHub Runner](https://github.com/actions/virtual-environments) during its execution (#71 #92).
* Rename `path` input to `context`
* Rename `dockerfile` input to `file`
* Rename `always_pull` input to `pull`
* Add `builder` input to be able to choose a builder instance through our [setup-buildx action](https://github.com/docker/setup-buildx-action)
* Add [`platforms`](https://github.com/docker/buildx#---platformvaluevalue) input
* Add [`allow`](https://github.com/docker/buildx#--allowentitlement) input
* Add [`load`](https://github.com/docker/buildx#--load) input
* Add [`outputs`](https://github.com/docker/buildx#-o---outputpath-typetypekeyvalue) input
* Add [`cache-from`](https://github.com/docker/buildx#--cache-fromnametypetypekeyvalue) input
* Add [`cache-to`](https://github.com/docker/buildx#--cache-tonametypetypekeyvalue) input
* Add `secrets` input
* Review `tags` input
* Remove `repository`, `username`, `password`, `registry`, `cache_froms` inputs
* Remove `tag_with_sha`, `tag_with_ref`, `add_git_labels` inputs
* Handle Git context
* Add `digest` output
* Login support moved to [docker/login-action](https://github.com/docker/login-action) repo
* Enhanced examples in README
* Tests and/or CI workflows
## 1.1.0 (2020/04/23)
* Add cache-from support fixing #7
* Add GCR example
## 1.0.1 (2020/03/23)
* Clarify dockerfile and path inputs
* Rename LICENCE to LICENSE
* Use v1 of docker/gihub-actions image
* Logs in before building image
## 1.0.0 (2020/03/18)
* Build and push Docker images to Docker Hub or your own private registry.
* Log in to Hub or private registry.
* Static tags and labels.
* Auto tagging by git ref.
* Auto tagging by git SHA.
* Auto labelling with opencontainers standards.
* Build arguments.
* Multi-stage build targets.
Backed by Docker image [docker/github-action:v1.0](https://hub.docker.com/repository/docker/docker/github-actions/)

View file

@ -1,3 +0,0 @@
FROM alpine:3
ENTRYPOINT ["echo", "docker github actions"]

573
README.md
View file

@ -1,243 +1,418 @@
# build-push-action
[![GitHub release](https://img.shields.io/github/release/docker/build-push-action.svg?style=flat-square)](https://github.com/docker/build-push-action/releases/latest)
[![GitHub marketplace](https://img.shields.io/badge/marketplace-docker--build--push-blue?logo=github&style=flat-square)](https://github.com/marketplace/actions/docker-build-push)
[![CI workflow](https://img.shields.io/github/workflow/status/docker/build-push-action/ci?label=ci&logo=github&style=flat-square)](https://github.com/docker/build-push-action/actions?workflow=ci)
[![Test workflow](https://img.shields.io/github/workflow/status/docker/build-push-action/test?label=test&logo=github&style=flat-square)](https://github.com/docker/build-push-action/actions?workflow=test)
Builds and pushes Docker images and will log in to a Docker registry if required.
## About
Suggestions and issues can be posted on the repositories [issues page](https://github.com/docker/build-push-action/issues).
GitHub Action to build and push Docker images with [Buildx](https://github.com/docker/buildx).
[Inputs](#Inputs)
* [repository](#repository)
* [username](#username)
* [password](#password)
* [registry](#registry)
* [tags](#tags)
* [tag_with_ref](#tag_with_ref)
* [tag_with_sha](#tag_with_sha)
* [path](#path)
* [dockerfile](#dockerfile)
* [target](#target)
* [always_pull](#always_pull)
* [build_args](#build_args)
* [cache_froms](#cache_froms)
* [labels](#labels)
* [add_git_labels](#add_git_labels)
* [push](#push)
> :bulb: See also:
> * [login](https://github.com/docker/login-action) action
> * [setup-buildx](https://github.com/docker/setup-buildx-action) action
> * [setup-qemu](https://github.com/docker/setup-qemu-action) action
[Example usage](#Example-usage)
![Screenshot](.github/build-push-action.png)
## Inputs
___
### `repository`
* [Usage](#usage)
* [Git context](#git-context)
* [Path context](#path-context)
* [Isolated builders](#isolated-builders)
* [Multi-platform image](#multi-platform-image)
* [Leverage GitHub cache](#leverage-github-cache)
* [Complete workflow](#complete-workflow)
* [Customizing](#customizing)
* [inputs](#inputs)
* [outputs](#outputs)
* [Keep up-to-date with GitHub Dependabot](#keep-up-to-date-with-github-dependabot)
* [Limitation](#limitation)
**Required** Docker repository to tag the image with.
## Usage
### `username`
This action uses our [setup-buildx](https://github.com/docker/setup-buildx-action) action that extends the
`docker build` command named [buildx](https://github.com/docker/buildx) with the full support of the features
provided by [Moby BuildKit](https://github.com/moby/buildkit) builder toolkit. This includes multi-arch build,
build-secrets, remote cache, etc. and different builder deployment/namespacing options.
Username used to log in to a Docker registry. If not set then no login will occur.
### Git context
### `password`
Password or personal access token used to log in to a Docker registry. If not set then no login will occur.
### `registry`
Server address of Docker registry. If not set then will default to Docker Hub.
### `tags`
Comma-delimited list of tags. These will be added to the registry/repository to form the image's tags.
Example:
The default behavior of this action is to use the Git context invoked by your workflow (`https://github.com/owner/repo#ref`).
```yaml
tags: tag1,tag2
name: ci
on:
push:
branches: master
jobs:
main:
runs-on: ubuntu-latest
steps:
-
name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@master
-
name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
-
name: Build and push
id: docker_build
uses: docker/build-push-action@v2
with:
builder: ${{ steps.buildx.outputs.name }}
push: true
tags: user/app:latest
-
name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}
```
### `tag_with_ref`
Boolean value. Defaults to `false`.
Automatically tags the built image with the git reference. The format of the tag depends on the type of git reference with all forward slashes replaced with `-`.
For pushes to a branch the reference will be `refs/heads/{branch-name}` and the tag will be `{branch-name}`. If `{branch-name}` is master then the tag will be `latest`.
For pull requests the reference will be `refs/pull/{pull-request}` and the tag will be `pr-{pull-request}`.
For git tags the reference will be `refs/tags/{git-tag}` and the tag will be `{git-tag}`.
Examples:
|Git Reference|Image tag|
|---|---|
|`refs/heads/master`|`latest`|
|`refs/heads/mybranch`|`mybranch`|
|`refs/heads/my/branch`|`my-branch`|
|`refs/pull/2/merge`|`pr-2-merge`|
|`refs/tags/v1.0.0`|`v1.0.0`|
### `tag_with_sha`
Boolean value. Defaults to `false`.
Automatically tags the built image with the git short SHA prefixed with `sha-`.
Example:
|Git SHA|Image tag|
|---|---|
|`676cae2f85471aeff6776463c72881ebd902dcf9`|`sha-676cae2`|
### `path`
Path to the build context. Defaults to `.`
### `dockerfile`
Path to the Dockerfile. Defaults to `{path}/Dockerfile`
Note when set this path is **not** relative to the `path` input but is instead relative to the current working directory.
### `target`
Sets the target stage to build.
### `always_pull`
Boolean value. Defaults to `false`.
Always attempt to pull a newer version of the image.
### `build_args`
Comma-delimited list of build-time variables.
Example:
If you use this action in a private repository, you have to pass the [GitHub Token](https://help.github.com/en/actions/configuring-and-managing-workflows/authenticating-with-the-github_token)
as a secret named `GIT_AUTH_TOKEN` to be able to authenticate against it with buildx:
```yaml
build_args: arg1=value1,arg2=value2
-
name: Build and push
id: docker_build
uses: docker/build-push-action@v2
with:
builder: ${{ steps.buildx.outputs.name }}
push: true
tags: user/app:latest
secrets: |
GIT_AUTH_TOKEN=${{ github.token }}
```
### `cache_froms`
### Path context
Comma-delimited list of images to consider as cache sources.
Example:
```yaml
cache_froms: myorg/baseimage:latest
```
### `labels`
Comma-delimited list of labels to add to the built image.
Example:
You can also use the `PATH` context alongside the [`actions/checkout`](https://github.com/actions/checkout/) action.
```yaml
labels: label_name_1=label_value_1,label_name_2=label_value_2
name: ci
on:
push:
branches: master
jobs:
path-context:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v2
-
name: Set up QEMU
uses: docker/setup-qemu-action@master
with:
platforms: all
-
name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@master
with:
version: latest
-
name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
-
name: Build and push
uses: docker/build-push-action@v2
with:
builder: ${{ steps.buildx.outputs.name }}
context: .
file: ./Dockerfile
platforms: linux/amd64,linux/arm64,linux/386
push: true
tags: user/app:latest
```
### `add_git_labels`
Boolean value. Defaults to `false`.
Adds labels with git repository information to the built image based on the standards set out in https://github.com/opencontainers/image-spec/blob/master/annotations.md.
The labels are:
|Label key|Example value|Description|
|---|---|---|
|`org.opencontainers.image.created`|`2020-03-06T23:00:00Z`|Date and time on which the image was built (string, date-time as defined by RFC 3339).|
|`org.opencontainers.image.source`|`https://github.com/myorg/myrepository`|URL to the GitHub repository.|
|`org.opencontainers.image.revision`|`676cae2f85471aeff6776463c72881ebd902dcf9`|The full git SHA of this commit.|
### `push`
Boolean value. Defaults to `true`.
Whether to push the built image.
## Example usage
The following will build the root Dockerfile, tag the image as `myorg/myrepository:latest`, log in to Docker Hub using GitHub secrets, and push the image to the Docker Hub repository `myorg/myrepository`:
### Isolated builders
```yaml
steps:
- name: Checkout code
uses: actions/checkout@v2
name: ci
- name: Build and push Docker images
uses: docker/build-push-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
repository: myorg/myrepository
tags: latest
on:
push:
branches: master
jobs:
multi-builders:
runs-on: ubuntu-latest
steps:
-
uses: docker/setup-buildx-action@master
id: builder1
-
uses: docker/setup-buildx-action@master
id: builder2
-
name: Builder 1 name
run: echo ${{ steps.builder1.outputs.name }}
-
name: Builder 2 name
run: echo ${{ steps.builder2.outputs.name }}
-
name: Build against builder1
uses: docker/build-push-action@v2
with:
builder: ${{ steps.builder1.outputs.name }}
target: mytarget1
-
name: Build against builder2
uses: docker/build-push-action@v2
with:
builder: ${{ steps.builder2.outputs.name }}
target: mytarget2
```
The following will build the root Dockerfile, tag the image with the git reference and SHA as described above, log in to Docker Hub using GitHub secrets, and push the image to the Docker Hub repository `myorg/myrepository`:
### Multi-platform image
```yaml
steps:
- name: Checkout code
uses: actions/checkout@v2
name: ci
- name: Build and push Docker images
uses: docker/build-push-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
repository: myorg/myrepository
tag_with_ref: true
tag_with_sha: true
on:
push:
branches: master
jobs:
multi:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v2
-
name: Set up QEMU
uses: docker/setup-qemu-action@master
with:
platforms: all
-
name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@master
-
name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
-
name: Build and push
uses: docker/build-push-action@v2
with:
builder: ${{ steps.buildx.outputs.name }}
context: .
file: ./Dockerfile
platforms: linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/ppc64le,linux/s390x
push: true
tags: |
user/app:latest
user/app:1.0.0
```
The following will only push the image when the event that kicked off the workflow was a push of a git tag:
### Leverage GitHub cache
You can leverage [GitHub cache](https://docs.github.com/en/actions/configuring-and-managing-workflows/caching-dependencies-to-speed-up-workflows)
using [@actions/cache](https://github.com/actions/cache) with this action.
```yaml
steps:
- name: Checkout code
uses: actions/checkout@v2
name: ci
- name: Build and push Docker images
uses: docker/build-push-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
repository: myorg/myrepository
tag_with_ref: true
push: ${{ startsWith(github.ref, 'refs/tags/') }}
on:
push:
branches: master
jobs:
github-cache:
runs-on: ubuntu-latest
steps:
-
name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@master
-
name: Cache Docker layers
uses: actions/cache@v2
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
-
name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
-
name: Build and push
id: docker_build
uses: docker/build-push-action@v2
with:
builder: ${{ steps.buildx.outputs.name }}
push: true
tags: user/app:latest
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache
-
name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}
```
The following builds the `mytarget` stage and pushes that:
### Complete workflow
* On `pull_request` event, Docker image `name/app:edge` is **built**.
* On `push` event, Docker image `name/app:edge` is **built** and **pushed** to DockerHub.
* On `schedule` event, Docker image `name/app:nightly` is **built** and **pushed** to DockerHub.
* On `push tags` event, Docker image `name/app:<version>` and `name/app:latest` is **built** and **pushed** to DockerHub.
```yaml
steps:
- name: Checkout code
uses: actions/checkout@v2
name: ci
- name: Build and push Docker images
uses: docker/build-push-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
repository: myorg/myrepository
tag_with_ref: true
target: mytarget
on:
schedule:
- cron: '0 10 * * *' # everyday at 10am
push:
branches: master
tags:
- 'v*.*.*'
pull_request:
branches: master
jobs:
docker:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v2
-
name: Prepare
id: prep
run: |
DOCKER_IMAGE=name/app
VERSION=edge
if [[ $GITHUB_REF == refs/tags/* ]]; then
VERSION=${GITHUB_REF#refs/tags/v}
fi
if [ "${{ github.event_name }}" = "schedule" ]; then
VERSION=nightly
fi
TAGS="${DOCKER_IMAGE}:${VERSION}"
if [[ $VERSION =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
TAGS="$TAGS,${DOCKER_IMAGE}:latest"
fi
echo ::set-output name=tags::${TAGS}
-
name: Set up QEMU
uses: docker/setup-qemu-action@master
with:
platforms: all
-
name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@master
-
name: Login to DockerHub
if: github.event_name != 'pull_request'
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
-
name: Build and push
id: docker_build
uses: docker/build-push-action@v2
with:
builder: ${{ steps.buildx.outputs.name }}
context: .
file: ./Dockerfile
platforms: linux/amd64,linux/arm64,linux/386
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.prep.outputs.tags }}
```
The following will build the root Dockerfile, tag the image as `myorg/myrepository:latest`, log in to Google Container Registry using GitHub secrets (where `DOCKER_PASSWORD` is a [JSON key](https://cloud.google.com/container-registry/docs/advanced-authentication#json-key)), and push the image to the GCR repository `myorg/myrepository`:
## Customizing
### inputs
Following inputs can be used as `step.with` keys
| Name | Type | Description |
|---------------------|---------|------------------------------------|
| `builder` | String | Builder instance (see [setup-buildx](https://github.com/docker/setup-buildx-action) action) |
| `context` | String | Build's context is the set of files located in the specified [`PATH` or `URL`](https://docs.docker.com/engine/reference/commandline/build/) (default [Git context](#git-context)) |
| `file` | String | Path to the Dockerfile (default `Dockerfile`) |
| `build-args` | List | List of build-time variables |
| `labels` | List | List of metadata for an image |
| `tags` | List | List of tags |
| `pull` | Bool | Always attempt to pull a newer version of the image (default `false`) |
| `target` | String | Sets the target stage to build |
| `allow` | List | List of [extra privileged entitlement](https://github.com/docker/buildx#--allowentitlement) (eg. `network.host,security.insecure`) |
| `no-cache` | Bool | Do not use cache when building the image (default `false`) |
| `platforms` | List | List of [target platforms](https://github.com/docker/buildx#---platformvaluevalue) for build |
| `load` | Bool | [Load](https://github.com/docker/buildx#--load) is a shorthand for `--output=type=docker` (default `false`) |
| `push` | Bool | [Push](https://github.com/docker/buildx#--push) is a shorthand for `--output=type=registry` (default `false`) |
| `outputs` | CSV | List of [output destinations](https://github.com/docker/buildx#-o---outputpath-typetypekeyvalue) (format: `type=local,dest=path`) |
| `cache-from` | CSV | List of [external cache sources](https://github.com/docker/buildx#--cache-fromnametypetypekeyvalue) (eg. `type=local,src=path/to/dir`) |
| `cache-to` | CSV | List of [cache export destinations](https://github.com/docker/buildx#--cache-tonametypetypekeyvalue) (eg. `type=local,dest=path/to/dir`) |
| `secrets` | CSV | List of secrets to expose to the build (eg. `key=value`, `GIT_AUTH_TOKEN=mytoken`) |
> `List` type can be a comma or newline-delimited string
> ```yaml
> tags: name/app:latest,name/app:1.0.0
> ```
> ```yaml
> tags: |
> name/app:latest
> name/app:1.0.0
> ```
> `CSV` type must be a newline-delimited string
> ```yaml
> cache-from: user/app:cache
> ```
> ```yaml
> cache-from: |
> user/app:cache
> type=local,src=path/to/dir
> ```
### outputs
Following outputs are available
| Name | Type | Description |
|---------------|---------|---------------------------------------|
| `digest` | String | Image content-addressable identifier also called a digest |
## Keep up-to-date with GitHub Dependabot
Since [Dependabot](https://docs.github.com/en/github/administering-a-repository/keeping-your-actions-up-to-date-with-github-dependabot)
has [native GitHub Actions support](https://docs.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates#package-ecosystem),
to enable it on your GitHub repo all you need to do is add the `.github/dependabot.yml` file:
```yaml
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Build and push Docker images
uses: docker/build-push-action@v1
with:
username: _json_key
password: ${{ secrets.DOCKER_PASSWORD }}
registry: gcr.io
repository: myorg/myrepository
tags: latest
version: 2
updates:
# Maintain dependencies for GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
```
## Limitation
This action is only available for Linux [virtual environments](https://help.github.com/en/articles/virtual-environments-for-github-actions#supported-virtual-environments-and-hardware-resources).

36
__tests__/buildx.test.ts Normal file
View file

@ -0,0 +1,36 @@
import fs from 'fs';
import * as semver from 'semver';
import * as buildx from '../src/buildx';
import * as exec from '@actions/exec';
const digest = 'sha256:bfb45ab72e46908183546477a08f8867fc40cebadd00af54b071b097aed127a9';
describe('getImageID', () => {
it('matches', async () => {
const imageIDFile = await buildx.getImageIDFile();
console.log(`imageIDFile: ${imageIDFile}`);
await fs.writeFileSync(imageIDFile, digest);
const imageID = await buildx.getImageID();
console.log(`imageID: ${imageID}`);
expect(imageID).toEqual(digest);
});
});
describe('getVersion', () => {
it('valid', async () => {
await exec.exec('docker', ['buildx', 'version']);
const version = await buildx.getVersion();
console.log(`version: ${version}`);
expect(semver.valid(version)).not.toBeNull();
}, 100000);
});
describe('parseVersion', () => {
test.each([
['github.com/docker/buildx 0.4.1+azure bda4882a65349ca359216b135896bddc1d92461c', '0.4.1'],
['github.com/docker/buildx v0.4.1 bda4882a65349ca359216b135896bddc1d92461c', '0.4.1'],
['github.com/docker/buildx v0.4.2 fb7b670b764764dc4716df3eba07ffdae4cc47b2', '0.4.2']
])('given %p', async (stdout, expected) => {
expect(await buildx.parseVersion(stdout)).toEqual(expected);
});
});

74
__tests__/context.test.ts Normal file
View file

@ -0,0 +1,74 @@
import * as context from '../src/context';
describe('getInputList', () => {
it('handles single line correctly', async () => {
await setInput('foo', 'bar');
const res = await context.getInputList('foo');
console.log(res);
expect(res).toEqual(['bar']);
});
it('handles multiple lines correctly', async () => {
setInput('foo', 'bar\nbaz');
const res = await context.getInputList('foo');
console.log(res);
expect(res).toEqual(['bar', 'baz']);
});
it('handles comma correctly', async () => {
setInput('foo', 'bar,baz');
const res = await context.getInputList('foo');
console.log(res);
expect(res).toEqual(['bar', 'baz']);
});
it('handles different new lines correctly', async () => {
setInput('foo', 'bar\r\nbaz');
const res = await context.getInputList('foo');
console.log(res);
expect(res).toEqual(['bar', 'baz']);
});
it('handles different new lines and comma correctly', async () => {
setInput('foo', 'bar\r\nbaz,bat');
const res = await context.getInputList('foo');
console.log(res);
expect(res).toEqual(['bar', 'baz', 'bat']);
});
it('handles multiple lines and ignoring comma correctly', async () => {
setInput('cache-from', 'user/app:cache\ntype=local,src=path/to/dir');
const res = await context.getInputList('cache-from', true);
console.log(res);
expect(res).toEqual(['user/app:cache', 'type=local,src=path/to/dir']);
});
it('handles different new lines and ignoring comma correctly', async () => {
setInput('cache-from', 'user/app:cache\r\ntype=local,src=path/to/dir');
const res = await context.getInputList('cache-from', true);
console.log(res);
expect(res).toEqual(['user/app:cache', 'type=local,src=path/to/dir']);
});
});
describe('asyncForEach', () => {
it('executes async tasks sequentially', async () => {
const testValues = [1, 2, 3, 4, 5];
const results: number[] = [];
await context.asyncForEach(testValues, async value => {
results.push(value);
});
expect(results).toEqual(testValues);
});
});
// See: https://github.com/actions/toolkit/blob/master/packages/core/src/core.ts#L67
function getInputName(name: string): string {
return `INPUT_${name.replace(/ /g, '_').toUpperCase()}`;
}
function setInput(name: string, value: string): void {
process.env[getInputName(name)] = value;
}

View file

@ -1,66 +1,75 @@
name: Build and push Docker images
description: Builds and pushes Docker images and will log in to a Docker registry if required
author: Docker
# https://help.github.com/en/articles/metadata-syntax-for-github-actions
name: Docker Build Push
description: Build and push Docker images with Buildx
author: docker
branding:
icon: 'anchor'
color: 'blue'
runs:
using: docker
image: docker://docker/github-actions:v1
args:
- build-push
inputs:
username:
description: Username used to log in to a Docker registry. If not set then no login will occur
builder:
description: "Builder instance"
required: false
password:
description: Password or personal access token used to log in to a Docker registry. If not set then no login will occur
context:
description: "Build's context is the set of files located in the specified PATH or URL"
required: false
registry:
description: Server address of Docker registry. If not set then will default to Docker Hub
default: '.'
file:
description: "Path to the Dockerfile"
required: false
repository:
description: Docker repository to tag the image with
required: true
tags:
description: Comma-delimited list of tags. These will be added to the registry/repository to form the image's tags
required: false
tag_with_ref:
description: Automatically tags the built image with the git reference as per the readme
required: false
default: false
tag_with_sha:
description: Automatically tags the built image with the git short SHA as per the readme
required: false
default: false
path:
description: Path to the build context
required: false
default: "."
dockerfile:
description: Path to the Dockerfile (Default is '{path}/Dockerfile')
required: false
target:
description: Sets the target stage to build
required: false
always_pull:
description: Always attempt to pull a newer version of the image
required: false
default: false
build_args:
description: Comma-delimited list of build-time variables
required: false
cache_froms:
description: Comma-delimited list of images to consider as cache sources
default: './Dockerfile'
build-args:
description: "List of build-time variables"
required: false
labels:
description: Comma-delimited list of labels to add to the built image
description: "List of metadata for an image"
required: false
add_git_labels:
description: Adds labels with git repository information to the built image
tags:
description: "List of tags"
required: false
default: false
pull:
description: "Always attempt to pull a newer version of the image"
required: false
default: 'false'
target:
description: "Sets the target stage to build"
required: false
allow:
description: "List of extra privileged entitlement (eg. network.host,security.insecure)"
required: false
no-cache:
description: "Do not use cache when building the image"
required: false
default: 'false'
platforms:
description: "List of target platforms for build"
required: false
load:
description: "Load is a shorthand for --output=type=docker"
required: false
default: 'false'
push:
description: Whether to push the image
description: "Push is a shorthand for --output=type=registry"
required: false
default: true
default: 'false'
outputs:
description: "List of output destinations (format: type=local,dest=path)"
required: false
cache-from:
description: "List of external cache sources for buildx (eg. user/app:cache, type=local,src=path/to/dir)"
required: false
cache-to:
description: "List of cache export destinations for buildx (eg. user/app:cache, type=local,dest=path/to/dir)"
required: false
secrets:
description: "List of secrets to expose to the build (eg. key=value, GIT_AUTH_TOKEN=mytoken)"
required: false
outputs:
digest:
description: 'Image content-addressable identifier also called a digest'
runs:
using: 'node12'
main: 'dist/index.js'
post: 'dist/index.js'

17195
dist/index.js generated vendored Normal file

File diff suppressed because one or more lines are too long

12
jest.config.js Normal file
View file

@ -0,0 +1,12 @@
module.exports = {
clearMocks: true,
moduleFileExtensions: ['js', 'ts'],
setupFiles: ["dotenv/config"],
testEnvironment: 'node',
testMatch: ['**/*.test.ts'],
testRunner: 'jest-circus/runner',
transform: {
'^.+\\.ts$': 'ts-jest'
},
verbose: false
}

51
package.json Normal file
View file

@ -0,0 +1,51 @@
{
"name": "docker-build-push",
"description": "Build and push Docker images",
"main": "lib/main.js",
"scripts": {
"build": "tsc && ncc build",
"format": "prettier --write **/*.ts",
"format-check": "prettier --check **/*.ts",
"test": "jest --coverage",
"pre-checkin": "yarn run format && yarn run build"
},
"repository": {
"type": "git",
"url": "git+https://github.com/docker/build-push-action.git"
},
"keywords": [
"actions",
"docker",
"build",
"push"
],
"author": "Docker",
"contributors": [
{
"name": "CrazyMax",
"url": "https://crazymax.dev"
}
],
"license": "Apache-2.0",
"dependencies": {
"@actions/core": "^1.2.4",
"@actions/exec": "^1.0.4",
"@actions/github": "^4.0.0",
"semver": "^7.3.2",
"tmp": "^0.2.1"
},
"devDependencies": {
"@types/jest": "^26.0.3",
"@types/node": "^14.0.14",
"@types/tmp": "^0.2.0",
"@vercel/ncc": "^0.23.0",
"dotenv": "^8.2.0",
"jest": "^26.1.0",
"jest-circus": "^26.1.0",
"jest-runtime": "^26.1.0",
"prettier": "^2.0.5",
"ts-jest": "^26.1.1",
"typescript": "^3.9.5",
"typescript-formatter": "^7.2.2"
}
}

53
src/buildx.ts Normal file
View file

@ -0,0 +1,53 @@
import fs from 'fs';
import path from 'path';
import tmp from 'tmp';
import * as semver from 'semver';
import * as context from './context';
import * as exec from './exec';
export async function getImageIDFile(): Promise<string> {
return path.join(context.tmpDir, 'iidfile');
}
export async function getImageID(): Promise<string | undefined> {
const iidFile = await getImageIDFile();
if (!fs.existsSync(iidFile)) {
return undefined;
}
return fs.readFileSync(iidFile, {encoding: 'utf-8'});
}
export async function getSecret(kvp: string): Promise<string> {
const [key, value] = kvp.split('=');
const secretFile = tmp.tmpNameSync({
tmpdir: context.tmpDir
});
await fs.writeFileSync(secretFile, value);
return `id=${key},src=${secretFile}`;
}
export async function isAvailable(): Promise<Boolean> {
return await exec.exec(`docker`, ['buildx'], true).then(res => {
if (res.stderr != '' && !res.success) {
return false;
}
return res.success;
});
}
export async function getVersion(): Promise<string> {
return await exec.exec(`docker`, ['buildx', 'version'], true).then(res => {
if (res.stderr != '' && !res.success) {
throw new Error(res.stderr);
}
return parseVersion(res.stdout);
});
}
export async function parseVersion(stdout: string): Promise<string> {
const matches = /\sv?([0-9.]+)/.exec(stdout);
if (!matches) {
throw new Error(`Cannot parse Buildx version`);
}
return semver.clean(matches[1]);
}

138
src/context.ts Normal file
View file

@ -0,0 +1,138 @@
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import * as semver from 'semver';
import * as buildx from './buildx';
import * as core from '@actions/core';
import * as github from '@actions/github';
export const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'docker-build-push-'));
export interface Inputs {
context: string;
file: string;
buildArgs: string[];
labels: string[];
tags: string[];
pull: boolean;
target: string;
allow: string[];
noCache: boolean;
builder: string;
platforms: string[];
load: boolean;
push: boolean;
outputs: string[];
cacheFrom: string[];
cacheTo: string[];
secrets: string[];
}
export async function getInputs(): Promise<Inputs> {
return {
context:
core.getInput('context') ||
`https://github.com/${github.context.repo.owner}/${github.context.repo.repo}#${github.context.ref}`,
file: core.getInput('file') || 'Dockerfile',
buildArgs: await getInputList('build-args'),
labels: await getInputList('labels'),
tags: await getInputList('tags'),
pull: /true/i.test(core.getInput('pull')),
target: core.getInput('target'),
allow: await getInputList('allow'),
noCache: /true/i.test(core.getInput('no-cache')),
builder: core.getInput('builder'),
platforms: await getInputList('platforms'),
load: /true/i.test(core.getInput('load')),
push: /true/i.test(core.getInput('push')),
outputs: await getInputList('outputs', true),
cacheFrom: await getInputList('cache-from', true),
cacheTo: await getInputList('cache-to', true),
secrets: await getInputList('secrets', true)
};
}
export async function getArgs(inputs: Inputs, buildxVersion: string): Promise<Array<string>> {
let args: Array<string> = ['buildx'];
args.push.apply(args, await getBuildArgs(inputs, buildxVersion));
args.push.apply(args, await getCommonArgs(inputs));
args.push(inputs.context);
return args;
}
async function getBuildArgs(inputs: Inputs, buildxVersion: string): Promise<Array<string>> {
let args: Array<string> = ['build'];
await asyncForEach(inputs.buildArgs, async buildArg => {
args.push('--build-arg', buildArg);
});
await asyncForEach(inputs.labels, async label => {
args.push('--label', label);
});
await asyncForEach(inputs.tags, async tag => {
args.push('--tag', tag);
});
if (inputs.target) {
args.push('--target', inputs.target);
}
if (inputs.allow.length > 0) {
args.push('--allow', inputs.allow.join(','));
}
if (inputs.platforms.length > 0) {
args.push('--platform', inputs.platforms.join(','));
}
if (inputs.platforms.length == 0 || semver.satisfies(buildxVersion, '>=0.4.2')) {
args.push('--iidfile', await buildx.getImageIDFile());
}
await asyncForEach(inputs.outputs, async output => {
args.push('--output', output);
});
await asyncForEach(inputs.cacheFrom, async cacheFrom => {
args.push('--cache-from', cacheFrom);
});
await asyncForEach(inputs.cacheTo, async cacheTo => {
args.push('--cache-to', cacheTo);
});
await asyncForEach(inputs.secrets, async secret => {
args.push('--secret', await buildx.getSecret(secret));
});
if (inputs.file) {
args.push('--file', inputs.file);
}
return args;
}
async function getCommonArgs(inputs: Inputs): Promise<Array<string>> {
let args: Array<string> = [];
if (inputs.noCache) {
args.push('--no-cache');
}
if (inputs.builder) {
args.push('--builder', inputs.builder);
}
if (inputs.pull) {
args.push('--pull');
}
if (inputs.load) {
args.push('--load');
}
if (inputs.push) {
args.push('--push');
}
return args;
}
export async function getInputList(name: string, ignoreComma?: boolean): Promise<string[]> {
const items = core.getInput(name);
if (items == '') {
return [];
}
return items
.split(/\r?\n/)
.reduce<string[]>((acc, line) => acc.concat(!ignoreComma ? line.split(',') : line).map(pat => pat.trim()), []);
}
export const asyncForEach = async (array, callback) => {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array);
}
};

34
src/exec.ts Normal file
View file

@ -0,0 +1,34 @@
import * as aexec from '@actions/exec';
import {ExecOptions} from '@actions/exec';
export interface ExecResult {
success: boolean;
stdout: string;
stderr: string;
}
export const exec = async (command: string, args: string[] = [], silent: boolean): Promise<ExecResult> => {
let stdout: string = '';
let stderr: string = '';
const options: ExecOptions = {
silent: silent,
ignoreReturnCode: true
};
options.listeners = {
stdout: (data: Buffer) => {
stdout += data.toString();
},
stderr: (data: Buffer) => {
stderr += data.toString();
}
};
const returnCode: number = await aexec.exec(command, args, options);
return {
success: returnCode === 0,
stdout: stdout.trim(),
stderr: stderr.trim()
};
};

53
src/main.ts Normal file
View file

@ -0,0 +1,53 @@
import * as fs from 'fs';
import * as os from 'os';
import * as buildx from './buildx';
import * as context from './context';
import * as stateHelper from './state-helper';
import * as core from '@actions/core';
import * as exec from '@actions/exec';
async function run(): Promise<void> {
try {
if (os.platform() !== 'linux') {
core.setFailed('Only supported on linux platform');
return;
}
if (!(await buildx.isAvailable())) {
core.setFailed(`Buildx is required. See https://github.com/docker/setup-buildx-action to set up buildx.`);
return;
}
stateHelper.setTmpDir(context.tmpDir);
const buildxVersion = await buildx.getVersion();
core.info(`📣 Buildx version: ${buildxVersion}`);
let inputs: context.Inputs = await context.getInputs();
core.info(`🏃 Starting build...`);
const args: string[] = await context.getArgs(inputs, buildxVersion);
await exec.exec('docker', args);
const imageID = await buildx.getImageID();
if (imageID) {
core.info('🛒 Extracting digest...');
core.info(`${imageID}`);
core.setOutput('digest', imageID);
}
} catch (error) {
core.setFailed(error.message);
}
}
async function cleanup(): Promise<void> {
if (stateHelper.tmpDir.length > 0) {
core.info(`🚿 Removing temp folder ${stateHelper.tmpDir}`);
fs.rmdirSync(stateHelper.tmpDir, {recursive: true});
}
}
if (!stateHelper.IsPost) {
run();
} else {
cleanup();
}

12
src/state-helper.ts Normal file
View file

@ -0,0 +1,12 @@
import * as core from '@actions/core';
export const IsPost = !!process.env['STATE_isPost'];
export const tmpDir = process.env['STATE_tmpDir'] || '';
export function setTmpDir(tmpDir: string) {
core.saveState('tmpDir', tmpDir);
}
if (!IsPost) {
core.saveState('isPost', 'true');
}

3
test/Dockerfile Normal file
View file

@ -0,0 +1,3 @@
FROM alpine
RUN echo "Hello world!"

8
test/Dockerfile-multi Normal file
View file

@ -0,0 +1,8 @@
FROM --platform=$BUILDPLATFORM golang:alpine AS build
ARG TARGETPLATFORM
ARG BUILDPLATFORM
RUN echo "I am running on $BUILDPLATFORM, building for $TARGETPLATFORM" > /log
FROM alpine
COPY --from=build /log /log

View file

@ -0,0 +1,30 @@
FROM --platform=${BUILDPLATFORM:-linux/amd64} tonistiigi/xx:golang AS xgo
FROM --platform=${BUILDPLATFORM:-linux/amd64} golang:1.13-alpine AS builder
ENV CGO_ENABLED 0
ENV GO111MODULE on
ENV GOPROXY https://goproxy.io
COPY --from=xgo / /
ARG TARGETPLATFORM
RUN go env
RUN apk --update --no-cache add \
build-base \
gcc \
git \
&& rm -rf /tmp/* /var/cache/apk/*
WORKDIR /app
ENV DIUN_VERSION="v4.4.0"
RUN git clone --branch ${DIUN_VERSION} https://github.com/crazy-max/diun .
RUN go mod download
RUN go build -ldflags "-w -s -X 'main.version=test'" -v -o diun cmd/main.go
FROM --platform=${TARGETPLATFORM:-linux/amd64} alpine:latest
COPY --from=builder /app/diun /usr/local/bin/diun
COPY --from=builder /usr/local/go/lib/time/zoneinfo.zip /usr/local/go/lib/time/zoneinfo.zip
RUN diun --version

View file

@ -0,0 +1,22 @@
FROM --platform=$BUILDPLATFORM golang:alpine AS build
ARG TARGETPLATFORM
ARG BUILDPLATFORM
RUN echo "I am running on $BUILDPLATFORM, building for $TARGETPLATFORM" > /log
RUN apk --update --no-cache add \
shadow \
sudo \
&& addgroup -g 1200 buildx \
&& adduser -u 1200 -G buildx -s /sbin/nologin -D buildx \
&& echo 'buildx ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers \
&& rm -rf /tmp/* /var/cache/apk/*
USER buildx
RUN sudo chown buildx. /log
USER root
FROM alpine
COPY --from=build /log /log
RUN ls -al /log

21
tsconfig.json Normal file
View file

@ -0,0 +1,21 @@
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"lib": [
"es6",
"dom"
],
"newLine": "lf",
"outDir": "./lib",
"rootDir": "./src",
"strict": true,
"noImplicitAny": false,
"esModuleInterop": true,
"sourceMap": true
},
"exclude": [
"node_modules",
"**/*.test.ts"
]
}

3909
yarn.lock Normal file

File diff suppressed because it is too large Load diff