mirror of
https://github.com/docker/build-push-action.git
synced 2025-06-13 06:37:12 +02:00
Compare commits
219 Commits
Author | SHA1 | Date | |
---|---|---|---|
c58c6870a2 | |||
25af97b442 | |||
5b4307de80 | |||
28262c0ab5 | |||
99ba0e6cbf | |||
5d3938fa17 | |||
4d8e3c6c3b | |||
de804a56b7 | |||
07d2c320de | |||
5af5c5fa9d | |||
29cf8ab8dc | |||
999f006917 | |||
71d586a48c | |||
24a0b9628d | |||
53401988c7 | |||
84306df16c | |||
6751eb6bd5 | |||
bf051e6237 | |||
695ef9e5a5 | |||
75727aa23f | |||
fb848139a7 | |||
5ab22b2b60 | |||
08566ae0e1 | |||
64fe20b36e | |||
d90dfadeb5 | |||
7d32c994b7 | |||
3149cc2fd7 | |||
5b56278dbc | |||
2d55d887e4 | |||
2fb322285c | |||
6f2047153d | |||
fb65f04b1e | |||
a2e29921b7 | |||
3da3f281a0 | |||
818fbc8101 | |||
9e2936f9ed | |||
5538ea42ec | |||
499091e46b | |||
ecc23e5785 | |||
a94448a773 | |||
c5789b4eb9 | |||
c24a92baa4 | |||
b1b7db3498 | |||
fe024b2b06 | |||
4513bf8897 | |||
9b234cb12f | |||
f76e1de265 | |||
0b294e37ce | |||
4dd58f369e | |||
2b832c5b7e | |||
ff640d300f | |||
e2249b968c | |||
b072fc7fae | |||
a2578e544b | |||
e952699f4d | |||
32351da9d7 | |||
86856eb412 | |||
3a24abd18b | |||
65a1a173ac | |||
5281740ad2 | |||
1f11648765 | |||
9d763a7ae2 | |||
ab651b3b16 | |||
d60f174305 | |||
cc21f01876 | |||
6a3a81765c | |||
c750a99048 | |||
a368cdd67a | |||
f45dc4a7b8 | |||
c32afc2f2d | |||
6fd2a708bd | |||
9a445780ad | |||
371daebc6f | |||
dbdfd86c1d | |||
28d7281ebf | |||
83b2aeab47 | |||
4b42944e12 | |||
1f13e8e2d4 | |||
f38eb7faec | |||
f5974ebd2b | |||
c27a49bd68 | |||
c78f7c3517 | |||
e8ee3d70c8 | |||
520adf89c3 | |||
1f582c9fe5 | |||
3eedc039be | |||
4f52913403 | |||
725bbce13f | |||
894de2a378 | |||
6c053f55dc | |||
4253125b47 | |||
64c27ea551 | |||
6493cd066b | |||
865abb5a44 | |||
b2462f3ff0 | |||
b4bae2181a | |||
ebfdda24ef | |||
c9c37b8060 | |||
562df850e8 | |||
b0acf4a8d6 | |||
bd69f76d77 | |||
85d408708d | |||
90d197d06b | |||
e01a38bb45 | |||
ff910a3b64 | |||
cf9301c704 | |||
b563df09ad | |||
f295fbf080 | |||
bd6a01893d | |||
8954ded19b | |||
c124ff0226 | |||
fbd19d2677 | |||
d72873cd8f | |||
d8e8fa4354 | |||
616a7811e4 | |||
6810721b4e | |||
2a794d5efe | |||
1baa07036c | |||
e13f2c26a1 | |||
995fe3c38a | |||
e8186e990f | |||
3e57a3300a | |||
f11192a27b | |||
595e5e8d62 | |||
1c21811610 | |||
aac8809ede | |||
a2059ed789 | |||
a1114bb0a0 | |||
65e18bfb3c | |||
ea664df512 | |||
52a6ac2d7a | |||
c92227abdc | |||
25fd13d2a5 | |||
38c1f188ca | |||
44d05b9191 | |||
2962fe9789 | |||
e7964906a6 | |||
8413351ed4 | |||
ff03fa50a4 | |||
e0b127751c | |||
1110973d9c | |||
363c8ed24f | |||
512e4e994b | |||
fbd34eda32 | |||
068cb8e418 | |||
1ae53ef326 | |||
bf8b1bf979 | |||
6ede7e932d | |||
bd7ed33c45 | |||
1ca381eec1 | |||
444d7e9909 | |||
22acf7cb32 | |||
5003e0df3d | |||
1c3fedb652 | |||
9c473659de | |||
10b9d7ec22 | |||
7086e3777e | |||
4bbd4aa946 | |||
8913dd9900 | |||
8be103ff82 | |||
b07bd1f9df | |||
01bd5c1fa9 | |||
82c243c14d | |||
78a1e0d9a3 | |||
807389ab0d | |||
6aa6546aaa | |||
585c2ae3b7 | |||
e9cb922263 | |||
25daa691dd | |||
f7cac3b071 | |||
ac03ceb5e6 | |||
6b0b71d882 | |||
1af8829102 | |||
23410eee4e | |||
52102ad62f | |||
320acb8414 | |||
b15ad02902 | |||
ea0c8ed90e | |||
f0ffce7351 | |||
ba9a9528cd | |||
99bea387ee | |||
3f08c86128 | |||
4e201a2f03 | |||
33f14d658e | |||
ee3946b77e | |||
46bf3297bf | |||
8f0f662386 | |||
c3b8f61f90 | |||
e53a3da260 | |||
26b3a3b0cd | |||
ec2628bb21 | |||
0cc5e42c7a | |||
b72149bbe7 | |||
1ef57147e1 | |||
5bb3f5433b | |||
15c6a86f2d | |||
d0f7649930 | |||
5cec6ce786 | |||
4bde7b156d | |||
8e7bb4ab28 | |||
1886d9322f | |||
4522dd93f5 | |||
2ad38f5d19 | |||
2f906a9cb7 | |||
674ff6ada6 | |||
3f39bb96b5 | |||
a1eb1729d4 | |||
66ba91a185 | |||
e3626b5cb1 | |||
b943609390 | |||
7e439a0132 | |||
8d34fc9a8e | |||
5290010a6f | |||
925fee3dc9 | |||
f443d57e89 | |||
87d5297de8 | |||
64d53ac2cf | |||
36c90bc3ab | |||
6df1822dc3 |
15
.editorconfig
Normal file
15
.editorconfig
Normal file
@ -0,0 +1,15 @@
|
||||
# This file is for unifying the coding style for different editors and IDEs.
|
||||
# More information at http://editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/dist/** linguist-generated=true
|
||||
/lib/** linguist-generated=true
|
1
.github/CODEOWNERS
vendored
Normal file
1
.github/CODEOWNERS
vendored
Normal file
@ -0,0 +1 @@
|
||||
* @crazy-max
|
29
.github/CONTRIBUTING.md
vendored
Normal file
29
.github/CONTRIBUTING.md
vendored
Normal 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)
|
37
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
37
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
---
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
Before sumbitting a bug report please read the [Troubleshooting doc](https://github.com/docker/build-push-action/blob/master/TROUBLESHOOTING.md).
|
||||
|
||||
### 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
31
.github/SUPPORT.md
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
# Support [](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
BIN
.github/build-push-action.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
18
.github/dependabot.yml
vendored
Normal file
18
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
labels:
|
||||
- ":game_die: dependencies"
|
||||
- ":robot: bot"
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
allow:
|
||||
- dependency-type: "production"
|
||||
labels:
|
||||
- ":game_die: dependencies"
|
||||
- ":robot: bot"
|
79
.github/labels.yml
vendored
Normal file
79
.github/labels.yml
vendored
Normal file
@ -0,0 +1,79 @@
|
||||
## more info https://github.com/crazy-max/ghaction-github-labeler
|
||||
- # automerge
|
||||
name: ":bell: automerge"
|
||||
color: "8f4fbc"
|
||||
description: ""
|
||||
- # bot
|
||||
name: ":robot: bot"
|
||||
color: "69cde9"
|
||||
description: ""
|
||||
- # bug
|
||||
name: ":bug: bug"
|
||||
color: "b60205"
|
||||
description: ""
|
||||
- # dependencies
|
||||
name: ":game_die: dependencies"
|
||||
color: "0366d6"
|
||||
description: ""
|
||||
from_name: "dependencies"
|
||||
- # documentation
|
||||
name: ":memo: documentation"
|
||||
color: "c5def5"
|
||||
description: ""
|
||||
- # duplicate
|
||||
name: ":busts_in_silhouette: duplicate"
|
||||
color: "cccccc"
|
||||
description: ""
|
||||
- # enhancement
|
||||
name: ":sparkles: enhancement"
|
||||
color: "0054ca"
|
||||
description: ""
|
||||
- # feature request
|
||||
name: ":bulb: feature request"
|
||||
color: "0e8a16"
|
||||
description: ""
|
||||
- # feedback
|
||||
name: ":mega: feedback"
|
||||
color: "03a9f4"
|
||||
description: ""
|
||||
- # future maybe
|
||||
name: ":rocket: future maybe"
|
||||
color: "fef2c0"
|
||||
description: ""
|
||||
- # good first issue
|
||||
name: ":hatching_chick: good first issue"
|
||||
color: "7057ff"
|
||||
description: ""
|
||||
- # help wanted
|
||||
name: ":pray: help wanted"
|
||||
color: "4caf50"
|
||||
description: ""
|
||||
- # hold
|
||||
name: ":hand: hold"
|
||||
color: "24292f"
|
||||
description: ""
|
||||
- # invalid
|
||||
name: ":no_entry_sign: invalid"
|
||||
color: "e6e6e6"
|
||||
description: ""
|
||||
- # maybe bug
|
||||
name: ":interrobang: maybe bug"
|
||||
color: "ff5722"
|
||||
description: ""
|
||||
- # needs more info
|
||||
name: ":thinking: needs more info"
|
||||
color: "795548"
|
||||
description: ""
|
||||
- # question
|
||||
name: ":question: question"
|
||||
color: "3f51b5"
|
||||
description: ""
|
||||
from_name: "question"
|
||||
- # upstream
|
||||
name: ":eyes: upstream"
|
||||
color: "fbca04"
|
||||
description: ""
|
||||
- # wontfix
|
||||
name: ":coffin: wontfix"
|
||||
color: "ffffff"
|
||||
description: ""
|
610
.github/workflows/ci.yml
vendored
Normal file
610
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,610 @@
|
||||
name: ci
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
minimal:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
with:
|
||||
path: action
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
-
|
||||
name: Build
|
||||
uses: ./action
|
||||
with:
|
||||
file: ./test/Dockerfile
|
||||
-
|
||||
name: Dump context
|
||||
if: always()
|
||||
uses: crazy-max/ghaction-dump-context@v1
|
||||
|
||||
git-context:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
registry:
|
||||
image: registry:2
|
||||
ports:
|
||||
- 5000:5000
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
with:
|
||||
path: action
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
with:
|
||||
version: latest
|
||||
driver-opts: network=host
|
||||
-
|
||||
name: Build and push
|
||||
id: docker_build
|
||||
uses: ./action
|
||||
with:
|
||||
file: ./test/Dockerfile
|
||||
builder: ${{ steps.buildx.outputs.name }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
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: Check digest
|
||||
run: |
|
||||
if [ -z "${{ steps.docker_build.outputs.digest }}" ]; then
|
||||
echo "::error::Digest should not be empty"
|
||||
exit 1
|
||||
fi
|
||||
-
|
||||
name: Dump context
|
||||
if: always()
|
||||
uses: crazy-max/ghaction-dump-context@v1
|
||||
|
||||
git-context-secret:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
registry:
|
||||
image: registry:2
|
||||
ports:
|
||||
- 5000:5000
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
with:
|
||||
path: action
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
with:
|
||||
driver-opts: network=host
|
||||
-
|
||||
name: Build and push
|
||||
id: docker_build
|
||||
uses: ./action
|
||||
with:
|
||||
file: ./test/Dockerfile
|
||||
builder: ${{ steps.buildx.outputs.name }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
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: Check digest
|
||||
run: |
|
||||
if [ -z "${{ steps.docker_build.outputs.digest }}" ]; then
|
||||
echo "::error::Digest should not be empty"
|
||||
exit 1
|
||||
fi
|
||||
-
|
||||
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
|
||||
services:
|
||||
registry:
|
||||
image: registry:2
|
||||
ports:
|
||||
- 5000:5000
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
with:
|
||||
version: ${{ matrix.buildx-version }}
|
||||
driver-opts: network=host
|
||||
-
|
||||
name: Build and push
|
||||
id: docker_build
|
||||
uses: ./
|
||||
with:
|
||||
context: ./test
|
||||
file: ./test/Dockerfile
|
||||
builder: ${{ steps.buildx.outputs.name }}
|
||||
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: Check digest
|
||||
run: |
|
||||
if [ -z "${{ steps.docker_build.outputs.digest }}" ]; then
|
||||
echo "::error::Digest should not be empty"
|
||||
exit 1
|
||||
fi
|
||||
-
|
||||
name: Dump context
|
||||
if: always()
|
||||
uses: crazy-max/ghaction-dump-context@v1
|
||||
|
||||
error:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
-
|
||||
name: Build
|
||||
id: docker_build
|
||||
continue-on-error: true
|
||||
uses: ./
|
||||
with:
|
||||
context: ./test
|
||||
file: ./test/Dockerfile
|
||||
platforms: linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/ppc64le,linux/s390x
|
||||
push: true
|
||||
tags: localhost:5000/name/app:latest
|
||||
-
|
||||
name: Check
|
||||
run: |
|
||||
echo "${{ toJson(steps.docker_build) }}"
|
||||
if [ "${{ steps.docker_build.outcome }}" != "failure" ] || [ "${{ steps.docker_build.conclusion }}" != "success" ]; then
|
||||
echo "::error::Should have failed"
|
||||
exit 1
|
||||
fi
|
||||
-
|
||||
name: Dump context
|
||||
if: always()
|
||||
uses: crazy-max/ghaction-dump-context@v1
|
||||
|
||||
docker-driver:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
push:
|
||||
- true
|
||||
- false
|
||||
services:
|
||||
registry:
|
||||
image: registry:2
|
||||
ports:
|
||||
- 5000:5000
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
-
|
||||
name: Build
|
||||
id: docker_build
|
||||
continue-on-error: ${{ matrix.push }}
|
||||
uses: ./
|
||||
with:
|
||||
context: ./test
|
||||
file: ./test/Dockerfile
|
||||
push: ${{ matrix.push }}
|
||||
tags: localhost:5000/name/app:latest
|
||||
-
|
||||
name: Check
|
||||
run: |
|
||||
echo "${{ toJson(steps.docker_build) }}"
|
||||
if [ "${{ matrix.push }}" = "false" ]; then
|
||||
exit 0
|
||||
fi
|
||||
if [ "${{ steps.docker_build.outcome }}" != "failure" ] || [ "${{ steps.docker_build.conclusion }}" != "success" ]; then
|
||||
echo "::error::Should have failed"
|
||||
exit 1
|
||||
fi
|
||||
-
|
||||
name: Dump context
|
||||
if: always()
|
||||
uses: crazy-max/ghaction-dump-context@v1
|
||||
|
||||
export-docker:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
-
|
||||
name: Build
|
||||
uses: ./
|
||||
with:
|
||||
context: ./test
|
||||
file: ./test/Dockerfile
|
||||
load: true
|
||||
tags: myimage:latest
|
||||
-
|
||||
name: Inspect
|
||||
run: |
|
||||
docker image inspect myimage:latest
|
||||
-
|
||||
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
|
||||
services:
|
||||
registry:
|
||||
image: registry:2
|
||||
ports:
|
||||
- 5000:5000
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
with:
|
||||
version: ${{ matrix.buildx-version }}
|
||||
driver-opts: network=host
|
||||
-
|
||||
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
|
||||
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: Check digest
|
||||
run: |
|
||||
if [ -z "${{ steps.docker_build.outputs.digest }}" ]; then
|
||||
echo "::error::Digest should not be empty"
|
||||
exit 1
|
||||
fi
|
||||
-
|
||||
name: Dump context
|
||||
if: always()
|
||||
uses: crazy-max/ghaction-dump-context@v1
|
||||
|
||||
registry-cache:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
registry:
|
||||
image: registry:2
|
||||
ports:
|
||||
- 5000:5000
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
with:
|
||||
# TODO: Remove image=moby/buildkit:buildx-stable-1 when moby/buildkit#1727 fixed
|
||||
driver-opts: |
|
||||
network=host
|
||||
image=moby/buildkit:buildx-stable-1
|
||||
-
|
||||
name: Build and push (1)
|
||||
id: docker_build
|
||||
uses: ./
|
||||
with:
|
||||
context: ./test
|
||||
file: ./test/Dockerfile-multi
|
||||
builder: ${{ steps.buildx.outputs.name }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: |
|
||||
localhost:5000/name/app:latest
|
||||
localhost:5000/name/app:1.0.0
|
||||
cache-from: type=registry,ref=localhost:5000/name/app
|
||||
cache-to: type=inline
|
||||
-
|
||||
name: Inspect (1)
|
||||
run: |
|
||||
docker buildx imagetools inspect localhost:5000/name/app:latest
|
||||
-
|
||||
name: Image digest (1)
|
||||
run: echo ${{ steps.docker_build.outputs.digest }}
|
||||
-
|
||||
name: Check digest (1)
|
||||
run: |
|
||||
if [ -z "${{ steps.docker_build.outputs.digest }}" ]; then
|
||||
echo "::error::Digest should not be empty"
|
||||
exit 1
|
||||
fi
|
||||
-
|
||||
name: Prune
|
||||
run: |
|
||||
docker buildx prune -a -f --verbose
|
||||
-
|
||||
name: Build and push (2)
|
||||
id: docker_build2
|
||||
uses: ./
|
||||
with:
|
||||
context: ./test
|
||||
file: ./test/Dockerfile-multi
|
||||
builder: ${{ steps.buildx.outputs.name }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: |
|
||||
localhost:5000/name/app:latest
|
||||
localhost:5000/name/app:1.0.0
|
||||
cache-from: type=registry,ref=localhost:5000/name/app
|
||||
cache-to: type=inline
|
||||
-
|
||||
name: Inspect (2)
|
||||
run: |
|
||||
docker buildx imagetools inspect localhost:5000/name/app:latest
|
||||
-
|
||||
name: Image digest (2)
|
||||
run: echo ${{ steps.docker_build2.outputs.digest }}
|
||||
-
|
||||
name: Check digest (2)
|
||||
run: |
|
||||
if [ -z "${{ steps.docker_build2.outputs.digest }}" ]; then
|
||||
echo "::error::Digest should not be empty"
|
||||
exit 1
|
||||
fi
|
||||
-
|
||||
name: Compare digests
|
||||
run: |
|
||||
echo Compare "${{ steps.docker_build.outputs.digest }}" with "${{ steps.docker_build2.outputs.digest }}"
|
||||
if [ "${{ steps.docker_build.outputs.digest }}" != "${{ steps.docker_build2.outputs.digest }}" ]; then
|
||||
echo "::error::Digests should be identical"
|
||||
exit 1
|
||||
fi
|
||||
-
|
||||
name: Dump context
|
||||
if: always()
|
||||
uses: crazy-max/ghaction-dump-context@v1
|
||||
|
||||
github-cache-first:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
digest: ${{ steps.docker_build.outputs.digest }}
|
||||
services:
|
||||
registry:
|
||||
image: registry:2
|
||||
ports:
|
||||
- 5000:5000
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
with:
|
||||
# TODO: Remove image=moby/buildkit:buildx-stable-1 when moby/buildkit#1727 fixed
|
||||
driver-opts: |
|
||||
network=host
|
||||
image=moby/buildkit:buildx-stable-1
|
||||
-
|
||||
name: Cache Docker layers
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
key: ${{ runner.os }}-buildx-ghcache-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-buildx-ghcache-
|
||||
-
|
||||
name: Erase cache
|
||||
run: |
|
||||
rm -rf /tmp/.buildx-cache/*
|
||||
-
|
||||
name: Build and push
|
||||
id: docker_build
|
||||
uses: ./
|
||||
with:
|
||||
context: ./test
|
||||
file: ./test/Dockerfile-multi
|
||||
builder: ${{ steps.buildx.outputs.name }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
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: Check digest
|
||||
run: |
|
||||
if [ -z "${{ steps.docker_build.outputs.digest }}" ]; then
|
||||
echo "::error::Digest should not be empty"
|
||||
exit 1
|
||||
fi
|
||||
-
|
||||
name: Dump context
|
||||
if: always()
|
||||
uses: crazy-max/ghaction-dump-context@v1
|
||||
|
||||
github-cache-hit:
|
||||
runs-on: ubuntu-latest
|
||||
needs: github-cache-first
|
||||
services:
|
||||
registry:
|
||||
image: registry:2
|
||||
ports:
|
||||
- 5000:5000
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
with:
|
||||
# TODO: Remove image=moby/buildkit:buildx-stable-1 when moby/buildkit#1727 fixed
|
||||
driver-opts: |
|
||||
network=host
|
||||
image=moby/buildkit:buildx-stable-1
|
||||
-
|
||||
name: Cache Docker layers
|
||||
uses: actions/cache@v2
|
||||
id: cache
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
key: ${{ runner.os }}-buildx-ghcache-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-buildx-ghcache-
|
||||
-
|
||||
name: Build and push
|
||||
id: docker_build
|
||||
uses: ./
|
||||
with:
|
||||
context: ./test
|
||||
file: ./test/Dockerfile-multi
|
||||
builder: ${{ steps.buildx.outputs.name }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
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: Check digest
|
||||
run: |
|
||||
if [ -z "${{ steps.docker_build.outputs.digest }}" ]; then
|
||||
echo "::error::Digest should not be empty"
|
||||
exit 1
|
||||
fi
|
||||
-
|
||||
name: Compare digests
|
||||
run: |
|
||||
echo Compare "${{ needs.github-cache-first.outputs.digest }}" with "${{ steps.docker_build.outputs.digest }}"
|
||||
if [ "${{ needs.github-cache-first.outputs.digest }}" != "${{ steps.docker_build.outputs.digest }}" ]; then
|
||||
echo "::error::Digests should be identical"
|
||||
exit 1
|
||||
fi
|
||||
-
|
||||
name: Cache hit
|
||||
run: echo ${{ steps.cache.outputs.cache-hit }}
|
||||
-
|
||||
name: Dump context
|
||||
if: always()
|
||||
uses: crazy-max/ghaction-dump-context@v1
|
103
.github/workflows/example.yml
vendored
Normal file
103
.github/workflows/example.yml
vendored
Normal file
@ -0,0 +1,103 @@
|
||||
# This workflow is provided just as an usage example and not for repo testing/verification
|
||||
# See https://github.com/docker/build-push-action#complete-workflow
|
||||
name: example
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 10 * * 0' # everyday sunday at 10am
|
||||
push:
|
||||
branches:
|
||||
- '**'
|
||||
tags:
|
||||
- 'v*.*.*'
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
registry:
|
||||
image: registry:2
|
||||
ports:
|
||||
- 5000:5000
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
-
|
||||
name: Prepare
|
||||
id: prep
|
||||
run: |
|
||||
DOCKER_IMAGE=localhost:5000/name/app
|
||||
VERSION=noop
|
||||
if [ "${{ github.event_name }}" = "schedule" ]; then
|
||||
VERSION=nightly
|
||||
elif [[ $GITHUB_REF == refs/tags/* ]]; then
|
||||
VERSION=${GITHUB_REF#refs/tags/}
|
||||
elif [[ $GITHUB_REF == refs/heads/* ]]; then
|
||||
VERSION=$(echo ${GITHUB_REF#refs/heads/} | sed -r 's#/+#-#g')
|
||||
if [ "${{ github.event.repository.default_branch }}" = "$VERSION" ]; then
|
||||
VERSION=edge
|
||||
fi
|
||||
elif [[ $GITHUB_REF == refs/pull/* ]]; then
|
||||
VERSION=pr-${{ github.event.number }}
|
||||
fi
|
||||
TAGS="${DOCKER_IMAGE}:${VERSION}"
|
||||
if [[ $VERSION =~ ^v[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
|
||||
MINOR=${VERSION%.*}
|
||||
MAJOR=${MINOR%.*}
|
||||
TAGS="$TAGS,${DOCKER_IMAGE}:${MINOR},${DOCKER_IMAGE}:${MAJOR},${DOCKER_IMAGE}:latest"
|
||||
elif [ "${{ github.event_name }}" = "push" ]; then
|
||||
TAGS="$TAGS,${DOCKER_IMAGE}:sha-${GITHUB_SHA::8}"
|
||||
fi
|
||||
echo ::set-output name=version::${VERSION}
|
||||
echo ::set-output name=tags::${TAGS}
|
||||
echo ::set-output name=created::$(date -u +'%Y-%m-%dT%H:%M:%SZ')
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
with:
|
||||
driver-opts: network=host
|
||||
-
|
||||
name: Build and export to Docker client
|
||||
uses: ./
|
||||
with:
|
||||
context: ./test
|
||||
file: ./test/Dockerfile
|
||||
outputs: type=docker
|
||||
tags: ${{ steps.prep.outputs.tags }}
|
||||
labels: |
|
||||
org.opencontainers.image.created=${{ steps.prep.outputs.created }}
|
||||
org.opencontainers.image.url=${{ github.event.repository.html_url }}
|
||||
org.opencontainers.image.source=${{ github.event.repository.clone_url }}
|
||||
org.opencontainers.image.version=${{ steps.prep.outputs.version }}
|
||||
org.opencontainers.image.revision=${{ github.sha }}
|
||||
org.opencontainers.image.licenses=${{ github.event.repository.license.spdx_id }}
|
||||
-
|
||||
name: Build and push to local registry
|
||||
uses: ./
|
||||
with:
|
||||
context: ./test
|
||||
file: ./test/Dockerfile
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.prep.outputs.tags }}
|
||||
labels: |
|
||||
org.opencontainers.image.created=${{ steps.prep.outputs.created }}
|
||||
org.opencontainers.image.url=${{ github.event.repository.html_url }}
|
||||
org.opencontainers.image.source=${{ github.event.repository.clone_url }}
|
||||
org.opencontainers.image.version=${{ steps.prep.outputs.version }}
|
||||
org.opencontainers.image.revision=${{ github.sha }}
|
||||
org.opencontainers.image.licenses=${{ github.event.repository.license.spdx_id }}
|
||||
-
|
||||
name: Inspect image
|
||||
run: |
|
||||
docker image inspect localhost:5000/name/app:${{ steps.prep.outputs.version }}
|
||||
-
|
||||
name: Check manifest
|
||||
if: github.event_name != 'pull_request'
|
||||
run: |
|
||||
docker buildx imagetools inspect localhost:5000/name/app:${{ steps.prep.outputs.version }}
|
||||
-
|
||||
name: Dump context
|
||||
if: always()
|
||||
uses: crazy-max/ghaction-dump-context@v1
|
20
.github/workflows/labels.yml
vendored
Normal file
20
.github/workflows/labels.yml
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
name: labels
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
paths:
|
||||
- '.github/labels.yml'
|
||||
- '.github/workflows/labels.yml'
|
||||
|
||||
jobs:
|
||||
labeler:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
-
|
||||
name: Run Labeler
|
||||
uses: crazy-max/ghaction-github-labeler@v3.1.0
|
37
.github/workflows/main.yml
vendored
37
.github/workflows/main.yml
vendored
@ -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
|
30
.github/workflows/test.yml
vendored
Normal file
30
.github/workflows/test.yml
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
name: test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
-
|
||||
name: Install
|
||||
run: yarn install
|
||||
-
|
||||
name: Test
|
||||
run: yarn run test
|
||||
-
|
||||
name: Upload coverage
|
||||
uses: codecov/codecov-action@v1.0.14
|
||||
if: success()
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
file: ./coverage/clover.xml
|
95
.gitignore
vendored
Normal file
95
.gitignore
vendored
Normal 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
11
.prettierrc.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"printWidth": 120,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none",
|
||||
"bracketSpacing": false,
|
||||
"arrowParens": "avoid",
|
||||
"parser": "typescript"
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
FROM alpine:3
|
||||
|
||||
ENTRYPOINT ["echo", "docker github actions"]
|
864
README.md
864
README.md
@ -1,243 +1,707 @@
|
||||
# build-push-action
|
||||
[](https://github.com/docker/build-push-action/releases/latest)
|
||||
[](https://github.com/marketplace/actions/build-and-push-docker-images)
|
||||
[](https://github.com/docker/build-push-action/actions?workflow=ci)
|
||||
[](https://github.com/docker/build-push-action/actions?workflow=test)
|
||||
[](https://codecov.io/gh/docker/build-push-action)
|
||||
|
||||
Builds and pushes Docker images and will log in to a Docker registry if required.
|
||||
## Upgrade from v1
|
||||
|
||||
Suggestions and issues can be posted on the repositories [issues page](https://github.com/docker/build-push-action/issues).
|
||||
`v2` of this action includes significant updates and now uses Docker [Buildx](https://github.com/docker/buildx). It
|
||||
works with 3 new actions ([login](https://github.com/docker/login-action), [setup-buildx](https://github.com/docker/setup-buildx-action)
|
||||
and [setup-qemu](https://github.com/docker/setup-qemu-action)) that we have created. 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.
|
||||
|
||||
[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)
|
||||
[Upgrade notes](UPGRADE.md) and many [usage examples](#usage) have been added to handle most use cases but `v1` is
|
||||
still available through [`releases/v1` branch](https://github.com/docker/build-push-action/tree/releases/v1).
|
||||
|
||||
[Example usage](#Example-usage)
|
||||
## About
|
||||
|
||||
## Inputs
|
||||
GitHub Action to build and push Docker images with [Buildx](https://github.com/docker/buildx).
|
||||
|
||||
### `repository`
|
||||
> :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
|
||||
|
||||
**Required** Docker repository to tag the image with.
|
||||

|
||||
|
||||
### `username`
|
||||
___
|
||||
|
||||
Username used to log in to a Docker registry. If not set then no login will occur.
|
||||
* [Usage](#usage)
|
||||
* [Git context](#git-context)
|
||||
* [Path context](#path-context)
|
||||
* [Isolated builders](#isolated-builders)
|
||||
* [Multi-platform image](#multi-platform-image)
|
||||
* [Advanced usage](#advanced-usage)
|
||||
* [Push to multi-registries](#push-to-multi-registries)
|
||||
* [Cache to registry](#push-to-multi-registries)
|
||||
* [Local registry](#local-registry)
|
||||
* [Export image to Docker](#export-image-to-docker)
|
||||
* [Leverage GitHub cache](#leverage-github-cache)
|
||||
* [Complete workflow](#complete-workflow)
|
||||
* [Update DockerHub repo description](#update-dockerhub-repo-description)
|
||||
* [Customizing](#customizing)
|
||||
* [inputs](#inputs)
|
||||
* [outputs](#outputs)
|
||||
* [Troubleshooting](#troubleshooting)
|
||||
* [Keep up-to-date with GitHub Dependabot](#keep-up-to-date-with-github-dependabot)
|
||||
* [Limitation](#limitation)
|
||||
|
||||
### `password`
|
||||
## Usage
|
||||
|
||||
Password or personal access token used to log in to a Docker registry. If not set then no login will occur.
|
||||
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.
|
||||
|
||||
### `registry`
|
||||
### Git context
|
||||
|
||||
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](https://github.com/docker/build-push-action/blob/master/src/context.ts#L31-L35)
|
||||
by your workflow.
|
||||
|
||||
```yaml
|
||||
tags: tag1,tag2
|
||||
name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: master
|
||||
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
-
|
||||
name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
-
|
||||
name: Build and push
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
push: true
|
||||
tags: user/app:latest
|
||||
build-args: |
|
||||
arg1=value1
|
||||
arg2=value2
|
||||
-
|
||||
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:
|
||||
Building from current repository automatically uses the [GitHub Token](https://help.github.com/en/actions/configuring-and-managing-workflows/authenticating-with-the-github_token)
|
||||
as provided by `secrets` so it does not need to be passed. But if you want to authenticate against another private
|
||||
repository, you have to use 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:
|
||||
push: true
|
||||
tags: user/app:latest
|
||||
secrets: |
|
||||
GIT_AUTH_TOKEN=${{ secrets.MYTOKEN }}
|
||||
```
|
||||
|
||||
### `cache_froms`
|
||||
> :warning: Subdir for Git context is [not yet supported](https://github.com/docker/build-push-action/issues/120).
|
||||
> For the moment you can use the [path context](#path-context).
|
||||
|
||||
Comma-delimited list of images to consider as cache sources.
|
||||
> More info: https://docs.docker.com/engine/reference/commandline/build/#git-repositories
|
||||
|
||||
Example:
|
||||
```yaml
|
||||
cache_froms: myorg/baseimage:latest
|
||||
```
|
||||
### Path context
|
||||
|
||||
### `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@v1
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
-
|
||||
name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
-
|
||||
name: Build and push
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
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@v1
|
||||
id: builder1
|
||||
-
|
||||
uses: docker/setup-buildx-action@v1
|
||||
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@v1
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
-
|
||||
name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
-
|
||||
name: Build and push
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
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:
|
||||
## Advanced usage
|
||||
|
||||
### Push to multi-registries
|
||||
|
||||
The following workflow will connect you to [DockerHub](https://github.com/docker/login-action#dockerhub)
|
||||
and [GitHub Container Registry](https://github.com/docker/login-action#github-container-registry) and push the
|
||||
image to these registries.
|
||||
|
||||
<details>
|
||||
<summary><b>Show workflow</b></summary>
|
||||
|
||||
```yaml
|
||||
name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: master
|
||||
|
||||
jobs:
|
||||
multi-registries:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
-
|
||||
name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
-
|
||||
name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.CR_PAT }}
|
||||
-
|
||||
name: Build and push
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
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
|
||||
ghcr.io/user/app:latest
|
||||
ghcr.io/user/app:1.0.0
|
||||
```
|
||||
</details>
|
||||
|
||||
### Cache to registry
|
||||
|
||||
You can import/export cache from a cache manifest or (special) image configuration on the registry.
|
||||
|
||||
<details>
|
||||
<summary><b>Show workflow</b></summary>
|
||||
|
||||
```yaml
|
||||
name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: master
|
||||
|
||||
jobs:
|
||||
registry-cache:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
-
|
||||
name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
-
|
||||
name: Build and push
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
push: true
|
||||
tags: user/app:latest
|
||||
cache-from: type=registry,ref=user/app:latest
|
||||
cache-to: type=inline
|
||||
```
|
||||
</details>
|
||||
|
||||
### Local registry
|
||||
|
||||
For testing purposes you may need to create a [local registry](https://hub.docker.com/_/registry) to push images into:
|
||||
|
||||
<details>
|
||||
<summary><b>Show workflow</b></summary>
|
||||
|
||||
```yaml
|
||||
name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: master
|
||||
|
||||
jobs:
|
||||
local-registry:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
registry:
|
||||
image: registry:2
|
||||
ports:
|
||||
- 5000:5000
|
||||
steps:
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
with:
|
||||
driver-opts: network=host
|
||||
-
|
||||
name: Build and push to local registry
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
push: true
|
||||
tags: localhost:5000/name/app:latest
|
||||
-
|
||||
name: Inspect
|
||||
run: |
|
||||
docker buildx imagetools inspect localhost:5000/name/app:latest
|
||||
```
|
||||
</details>
|
||||
|
||||
### Export image to Docker
|
||||
|
||||
You may want your build result to be available in the Docker client through `docker images` to be able to use it
|
||||
in another step of your workflow:
|
||||
|
||||
<details>
|
||||
<summary><b>Show workflow</b></summary>
|
||||
|
||||
```yaml
|
||||
name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: master
|
||||
|
||||
jobs:
|
||||
export-docker:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
-
|
||||
name: Build
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
load: true
|
||||
tags: myimage:latest
|
||||
-
|
||||
name: Inspect
|
||||
run: |
|
||||
docker image inspect myimage:latest
|
||||
```
|
||||
</details>
|
||||
|
||||
### 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:
|
||||
|
||||
<details>
|
||||
<summary><b>Show workflow</b></summary>
|
||||
|
||||
```yaml
|
||||
name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: master
|
||||
|
||||
jobs:
|
||||
github-cache:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
-
|
||||
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.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
-
|
||||
name: Build and push
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
push: true
|
||||
tags: user/app:latest
|
||||
cache-from: type=local,src=/tmp/.buildx-cache
|
||||
cache-to: type=local,dest=/tmp/.buildx-cache
|
||||
```
|
||||
</details>
|
||||
|
||||
> If you want to [export layers for all stages](https://github.com/docker/buildx#--cache-tonametypetypekeyvalue),
|
||||
> you have to specify `mode=max` attribute in `cache-to`.
|
||||
|
||||
### Complete workflow
|
||||
|
||||
If you come from [`v1`](https://github.com/docker/build-push-action/tree/releases/v1#readme) and want an
|
||||
"automatic" tag management through Git reference and [OCI Image Format Specification](https://github.com/opencontainers/image-spec/blob/master/annotations.md)
|
||||
for labels, you will have to do it in a dedicated step.
|
||||
|
||||
The following workflow with the `Prepare` step will generate some [outputs](https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjobs_idoutputs)
|
||||
to handle tags and labels based on GitHub actions events.
|
||||
|
||||
This is just an example to show many cases that you might want to use and that you will have to adapt according
|
||||
to your needs:
|
||||
|
||||
<details>
|
||||
<summary><b>Show workflow</b></summary>
|
||||
|
||||
```yaml
|
||||
name: ci
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 10 * * *' # everyday at 10am
|
||||
push:
|
||||
branches:
|
||||
- '**'
|
||||
tags:
|
||||
- 'v*.*.*'
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
-
|
||||
name: Repo metadata
|
||||
id: repo
|
||||
uses: actions/github-script@v3
|
||||
with:
|
||||
script: |
|
||||
const repo = await github.repos.get(context.repo)
|
||||
return repo.data
|
||||
-
|
||||
name: Prepare
|
||||
id: prep
|
||||
run: |
|
||||
DOCKER_IMAGE=name/app
|
||||
VERSION=noop
|
||||
if [ "${{ github.event_name }}" = "schedule" ]; then
|
||||
VERSION=nightly
|
||||
elif [[ $GITHUB_REF == refs/tags/* ]]; then
|
||||
VERSION=${GITHUB_REF#refs/tags/}
|
||||
elif [[ $GITHUB_REF == refs/heads/* ]]; then
|
||||
VERSION=$(echo ${GITHUB_REF#refs/heads/} | sed -r 's#/+#-#g')
|
||||
if [ "${{ github.event.repository.default_branch }}" = "$VERSION" ]; then
|
||||
VERSION=edge
|
||||
fi
|
||||
elif [[ $GITHUB_REF == refs/pull/* ]]; then
|
||||
VERSION=pr-${{ github.event.number }}
|
||||
fi
|
||||
TAGS="${DOCKER_IMAGE}:${VERSION}"
|
||||
if [[ $VERSION =~ ^v[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
|
||||
MINOR=${VERSION%.*}
|
||||
MAJOR=${MINOR%.*}
|
||||
TAGS="$TAGS,${DOCKER_IMAGE}:${MINOR},${DOCKER_IMAGE}:${MAJOR},${DOCKER_IMAGE}:latest"
|
||||
elif [ "${{ github.event_name }}" = "push" ]; then
|
||||
TAGS="$TAGS,${DOCKER_IMAGE}:sha-${GITHUB_SHA::8}"
|
||||
fi
|
||||
echo ::set-output name=version::${VERSION}
|
||||
echo ::set-output name=tags::${TAGS}
|
||||
echo ::set-output name=created::$(date -u +'%Y-%m-%dT%H:%M:%SZ')
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
-
|
||||
name: Login to DockerHub
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
-
|
||||
name: Build and push
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
platforms: linux/amd64,linux/arm64,linux/386
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.prep.outputs.tags }}
|
||||
labels: |
|
||||
org.opencontainers.image.title=${{ fromJson(steps.repo.outputs.result).name }}
|
||||
org.opencontainers.image.description=${{ fromJson(steps.repo.outputs.result).description }}
|
||||
org.opencontainers.image.url=${{ fromJson(steps.repo.outputs.result).html_url }}
|
||||
org.opencontainers.image.source=${{ fromJson(steps.repo.outputs.result).clone_url }}
|
||||
org.opencontainers.image.version=${{ steps.prep.outputs.version }}
|
||||
org.opencontainers.image.created=${{ steps.prep.outputs.created }}
|
||||
org.opencontainers.image.revision=${{ github.sha }}
|
||||
org.opencontainers.image.licenses=${{ fromJson(steps.repo.outputs.result).license.spdx_id }}
|
||||
```
|
||||
</details>
|
||||
|
||||
| Event | Ref | Commit SHA | Docker Tag | Pushed |
|
||||
|-----------------|-------------------------------|------------|------------------------------------|--------|
|
||||
| `schedule` | | | `nightly` | Yes |
|
||||
| `pull_request` | `refs/pull/2/merge` | `a123b57` | `pr-2` | No |
|
||||
| `push` | `refs/heads/<default_branch>` | `676cae2` | `sha-676cae2`, `edge` | Yes |
|
||||
| `push` | `refs/heads/dev` | `cf20257` | `sha-cf20257`, `dev` | Yes |
|
||||
| `push` | `refs/heads/my/branch` | `a5df687` | `sha-a5df687`, `my-branch` | Yes |
|
||||
| `push tag` | `refs/tags/v1.2.3` | | `v1.2.3`, `v1.2`, `v1`, `latest` | Yes |
|
||||
|
||||
### Update DockerHub repo description
|
||||
|
||||
You can update the [DockerHub repository description](https://docs.docker.com/docker-hub/repos/) using
|
||||
a third-party action called [DockerHub Description](https://github.com/peter-evans/dockerhub-description)
|
||||
with this action:
|
||||
|
||||
<details>
|
||||
<summary><b>Show workflow</b></summary>
|
||||
|
||||
```yaml
|
||||
name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: master
|
||||
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
-
|
||||
name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
-
|
||||
name: Build and push
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
push: true
|
||||
tags: user/app:latest
|
||||
-
|
||||
name: Update repo description
|
||||
uses: peter-evans/dockerhub-description@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||
repository: user/app
|
||||
```
|
||||
</details>
|
||||
|
||||
## Customizing
|
||||
|
||||
### inputs
|
||||
|
||||
Following inputs can be used as `step.with` keys
|
||||
|
||||
> `List` type is a newline-delimited string
|
||||
> ```yaml
|
||||
> cache-from: |
|
||||
> user/app:cache
|
||||
> type=local,src=path/to/dir
|
||||
> ```
|
||||
|
||||
> `CSV` type is a comma-delimited string
|
||||
> ```yaml
|
||||
> tags: name/app:latest,name/app:1.0.0
|
||||
> ```
|
||||
|
||||
| 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/CSV | 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/CSV | 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/CSV | 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` | List | List of [output destinations](https://github.com/docker/buildx#-o---outputpath-typetypekeyvalue) (format: `type=local,dest=path`) |
|
||||
| `cache-from` | List | List of [external cache sources](https://github.com/docker/buildx#--cache-fromnametypetypekeyvalue) (eg. `type=local,src=path/to/dir`) |
|
||||
| `cache-to` | List | List of [cache export destinations](https://github.com/docker/buildx#--cache-tonametypetypekeyvalue) (eg. `type=local,dest=path/to/dir`) |
|
||||
| `secrets` | List | List of secrets to expose to the build (eg. `key=value`, `GIT_AUTH_TOKEN=mytoken`) |
|
||||
|
||||
### outputs
|
||||
|
||||
Following outputs are available
|
||||
|
||||
| Name | Type | Description |
|
||||
|---------------|---------|---------------------------------------|
|
||||
| `digest` | String | Image content-addressable identifier also called a digest |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
See [TROUBLESHOOTING.md](TROUBLESHOOTING.md)
|
||||
|
||||
## 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: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
repository: myorg/myrepository
|
||||
tag_with_ref: true
|
||||
push: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
version: 2
|
||||
updates:
|
||||
# Maintain dependencies for GitHub Actions
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
```
|
||||
|
||||
The following builds the `mytarget` stage and pushes that:
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
## Limitation
|
||||
|
||||
- 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
|
||||
```
|
||||
|
||||
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`:
|
||||
|
||||
```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
|
||||
```
|
||||
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).
|
||||
|
72
TROUBLESHOOTING.md
Normal file
72
TROUBLESHOOTING.md
Normal file
@ -0,0 +1,72 @@
|
||||
# Troubleshooting
|
||||
|
||||
## Errors on pushing to registry
|
||||
|
||||
While pushing to a registry, you may encounter these kinds of issues:
|
||||
|
||||
* `failed commit on ref "layer-sha256:...": invalid content digest in response: invalid checksum digest format`
|
||||
* `failed commit on ref "layer-sha256:...": no response`
|
||||
* `failed commit on ref "manifest-sha256:...": unexpected status: 400 Bad Request`
|
||||
* `failed commit on ref "manifest-sha256:...": unexpected status: 401 Unauthorized`
|
||||
* `unexpected response: 401 Unauthorized`
|
||||
|
||||
These issues are not directly related to this action but are rather linked to [buildx](https://github.com/docker/buildx),
|
||||
[buildkit](https://github.com/moby/buildkit), [containerd](https://github.com/containerd/containerd) or the registry
|
||||
on which you're pushing your image. The quality of error message depends on the registry and are usually not very informative.
|
||||
|
||||
To help you solve this, you should first enable debugging in the
|
||||
[setup-buildx action step](https://github.com/docker/setup-buildx-action):
|
||||
|
||||
```yaml
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
with:
|
||||
buildkitd-flags: --debug
|
||||
```
|
||||
|
||||
Next you can test pushing with [containerd action](https://github.com/crazy-max/ghaction-setup-containerd) using the
|
||||
following workflow. If it works then open an issue on [buildkit](https://github.com/moby/buildkit) repository.
|
||||
|
||||
```yaml
|
||||
name: containerd
|
||||
|
||||
on:
|
||||
push:
|
||||
|
||||
jobs:
|
||||
containerd:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
with:
|
||||
buildkitd-flags: --debug
|
||||
-
|
||||
name: Set up containerd
|
||||
uses: crazy-max/ghaction-setup-containerd@v1
|
||||
-
|
||||
name: Build Docker image
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
platforms: linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/ppc64le,linux/s390x
|
||||
tags: docker.io/user/app:latest
|
||||
outputs: type=oci,dest=/tmp/image.tar
|
||||
-
|
||||
name: Import image in containerd
|
||||
run: |
|
||||
sudo ctr i import --base-name docker.io/user/app --digests --all-platforms /tmp/image.tar
|
||||
-
|
||||
name: Push image with containerd
|
||||
run: |
|
||||
sudo ctr --debug i push --user "${{ secrets.DOCKER_USERNAME }}:${{ secrets.DOCKER_PASSWORD }}" docker.io/user/app:latest
|
||||
```
|
144
UPGRADE.md
Normal file
144
UPGRADE.md
Normal file
@ -0,0 +1,144 @@
|
||||
# Upgrade notes
|
||||
|
||||
## v1 to v2
|
||||
|
||||
* Input `path` is now called `context` for consistency with other Docker build tools
|
||||
* `path` defaults to current git repository so checkout action is not required in a workflow
|
||||
* Rename `dockerfile` input to `file` for consistency with other Docker build tools
|
||||
* Rename `always_pull` input to `pull` for consistency with other Docker build tools
|
||||
* 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 to support multi-platform builds
|
||||
* 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 (`cache_froms` removed)
|
||||
* Add [`cache-to`](https://github.com/docker/buildx#--cache-tonametypetypekeyvalue) input
|
||||
* Add `secrets` input
|
||||
* Review `tags` input
|
||||
* Remove `repository` input. See [Simple workflow](#simple-workflow) for migration
|
||||
* Remove `username`, `password` and `registry` inputs. Login support moved to [docker/login-action](https://github.com/docker/login-action) repo
|
||||
* Remove `tag_with_sha`, `tag_with_ref`, `add_git_labels` inputs. See [Tags with ref and Git labels](#tags-with-ref-and-git-labels) for migration
|
||||
* Handle Git context
|
||||
* Add `digest` output
|
||||
|
||||
### Simple workflow
|
||||
|
||||
```yaml
|
||||
# v1
|
||||
steps:
|
||||
-
|
||||
name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
-
|
||||
name: Build and push Docker images
|
||||
uses: docker/build-push-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
repository: myorg/myrepository
|
||||
always_pull: true
|
||||
build_args: arg1=value1,arg2=value2
|
||||
cache_froms: myorg/myrepository:latest
|
||||
tags: latest
|
||||
```
|
||||
|
||||
```yaml
|
||||
# v2
|
||||
steps:
|
||||
-
|
||||
name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
-
|
||||
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:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
pull: true
|
||||
push: true
|
||||
build-args: |
|
||||
arg1=value1
|
||||
arg2=value2
|
||||
cache-from: type=registry,ref=myorg/myrepository:latest
|
||||
cache-to: type=inline
|
||||
tags: myorg/myrepository:latest
|
||||
```
|
||||
|
||||
### Tags with ref and Git labels
|
||||
|
||||
```yaml
|
||||
# v1
|
||||
steps:
|
||||
-
|
||||
name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
-
|
||||
name: Build and push Docker images
|
||||
uses: docker/build-push-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
repository: myorg/myrepository
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tag_with_ref: true
|
||||
tag_with_sha: true
|
||||
```
|
||||
|
||||
```yaml
|
||||
# v2
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
-
|
||||
name: Prepare
|
||||
id: prep
|
||||
run: |
|
||||
DOCKER_IMAGE=myorg/myrepository
|
||||
VERSION=edge
|
||||
if [[ $GITHUB_REF == refs/tags/* ]]; then
|
||||
VERSION=${GITHUB_REF#refs/tags/}
|
||||
elif [[ $GITHUB_REF == refs/heads/* ]]; then
|
||||
VERSION=$(echo ${GITHUB_REF#refs/heads/} | sed -r 's#/+#-#g')
|
||||
elif [[ $GITHUB_REF == refs/pull/* ]]; then
|
||||
VERSION=pr-${{ github.event.number }}
|
||||
fi
|
||||
TAGS="${DOCKER_IMAGE}:${VERSION}"
|
||||
if [ "${{ github.event_name }}" = "push" ]; then
|
||||
TAGS="$TAGS,${DOCKER_IMAGE}:sha-${GITHUB_SHA::8}"
|
||||
fi
|
||||
echo ::set-output name=version::${VERSION}
|
||||
echo ::set-output name=tags::${TAGS}
|
||||
echo ::set-output name=created::$(date -u +'%Y-%m-%dT%H:%M:%SZ')
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
-
|
||||
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
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.prep.outputs.tags }}
|
||||
labels: |
|
||||
org.opencontainers.image.source=${{ github.event.repository.clone_url }}
|
||||
org.opencontainers.image.created=${{ steps.prep.outputs.created }}
|
||||
org.opencontainers.image.revision=${{ github.sha }}
|
||||
```
|
109
__tests__/buildx.test.ts
Normal file
109
__tests__/buildx.test.ts
Normal file
@ -0,0 +1,109 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as semver from 'semver';
|
||||
import * as buildx from '../src/buildx';
|
||||
import * as exec from '@actions/exec';
|
||||
import * as context from '../src/context';
|
||||
|
||||
const digest = 'sha256:bfb45ab72e46908183546477a08f8867fc40cebadd00af54b071b097aed127a9';
|
||||
|
||||
jest.spyOn(context, 'tmpDir').mockImplementation((): string => {
|
||||
const tmpDir = path.join('/tmp/.docker-build-push-jest').split(path.sep).join(path.posix.sep);
|
||||
if (!fs.existsSync(tmpDir)) {
|
||||
fs.mkdirSync(tmpDir, {recursive: true});
|
||||
}
|
||||
return tmpDir;
|
||||
});
|
||||
|
||||
jest.spyOn(context, 'tmpNameSync').mockImplementation((): string => {
|
||||
return path.join('/tmp/.docker-build-push-jest', '.tmpname-jest').split(path.sep).join(path.posix.sep);
|
||||
});
|
||||
|
||||
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('isLocalOrTarExporter', () => {
|
||||
// prettier-ignore
|
||||
test.each([
|
||||
[
|
||||
[
|
||||
'type=registry,ref=user/app',
|
||||
],
|
||||
false
|
||||
],
|
||||
[
|
||||
[
|
||||
'type=docker',
|
||||
],
|
||||
false
|
||||
],
|
||||
[
|
||||
[
|
||||
'type=local,dest=./release-out'
|
||||
],
|
||||
true
|
||||
],
|
||||
[
|
||||
[
|
||||
'type=tar,dest=/tmp/image.tar'
|
||||
],
|
||||
true
|
||||
],
|
||||
[
|
||||
[
|
||||
'type=docker',
|
||||
'type=tar,dest=/tmp/image.tar'
|
||||
],
|
||||
true
|
||||
],
|
||||
[
|
||||
[
|
||||
'"type=tar","dest=/tmp/image.tar"'
|
||||
],
|
||||
true
|
||||
],
|
||||
[
|
||||
[
|
||||
'" type= local" , dest=./release-out'
|
||||
],
|
||||
true
|
||||
],
|
||||
[
|
||||
[
|
||||
'.'
|
||||
],
|
||||
true
|
||||
],
|
||||
])(
|
||||
'given %p returns %p',
|
||||
async (outputs: Array<string>, expected: boolean) => {
|
||||
expect(buildx.isLocalOrTarExporter(outputs)).toEqual(expected);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
describe('getVersion', () => {
|
||||
it('valid', async () => {
|
||||
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);
|
||||
});
|
||||
});
|
260
__tests__/context.test.ts
Normal file
260
__tests__/context.test.ts
Normal file
@ -0,0 +1,260 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as buildx from '../src/buildx';
|
||||
import * as context from '../src/context';
|
||||
|
||||
jest.spyOn(context, 'defaultContext').mockImplementation((): string => {
|
||||
return 'https://github.com/docker/build-push-action.git#test-jest';
|
||||
});
|
||||
|
||||
jest.spyOn(context, 'tmpDir').mockImplementation((): string => {
|
||||
const tmpDir = path.join('/tmp/.docker-build-push-jest').split(path.sep).join(path.posix.sep);
|
||||
if (!fs.existsSync(tmpDir)) {
|
||||
fs.mkdirSync(tmpDir, {recursive: true});
|
||||
}
|
||||
return tmpDir;
|
||||
});
|
||||
|
||||
jest.spyOn(context, 'tmpNameSync').mockImplementation((): string => {
|
||||
return path.join('/tmp/.docker-build-push-jest', '.tmpname-jest').split(path.sep).join(path.posix.sep);
|
||||
});
|
||||
|
||||
describe('getArgs', () => {
|
||||
beforeEach(() => {
|
||||
process.env = Object.keys(process.env).reduce((object, key) => {
|
||||
if (!key.startsWith('INPUT_')) {
|
||||
object[key] = process.env[key];
|
||||
}
|
||||
return object;
|
||||
}, {});
|
||||
});
|
||||
|
||||
// prettier-ignore
|
||||
test.each([
|
||||
[
|
||||
'0.4.1',
|
||||
new Map<string, string>([
|
||||
['context', '.'],
|
||||
]),
|
||||
[
|
||||
'buildx',
|
||||
'build',
|
||||
'--iidfile', '/tmp/.docker-build-push-jest/iidfile',
|
||||
'--file', 'Dockerfile',
|
||||
'.'
|
||||
]
|
||||
],
|
||||
[
|
||||
'0.4.2',
|
||||
new Map<string, string>([
|
||||
['build-args', 'MY_ARG=val1,val2,val3\nARG=val'],
|
||||
]),
|
||||
[
|
||||
'buildx',
|
||||
'build',
|
||||
'--build-arg', 'MY_ARG=val1,val2,val3',
|
||||
'--build-arg', 'ARG=val',
|
||||
'--iidfile', '/tmp/.docker-build-push-jest/iidfile',
|
||||
'--file', 'Dockerfile',
|
||||
'https://github.com/docker/build-push-action.git#test-jest'
|
||||
]
|
||||
],
|
||||
[
|
||||
'0.4.2',
|
||||
new Map<string, string>([
|
||||
['context', '.'],
|
||||
['labels', 'org.opencontainers.image.title=buildkit\norg.opencontainers.image.description=concurrent, cache-efficient, and Dockerfile-agnostic builder toolkit'],
|
||||
['outputs', 'type=local,dest=./release-out']
|
||||
]),
|
||||
[
|
||||
'buildx',
|
||||
'build',
|
||||
'--label', 'org.opencontainers.image.title=buildkit',
|
||||
'--label', 'org.opencontainers.image.description=concurrent, cache-efficient, and Dockerfile-agnostic builder toolkit',
|
||||
'--output', 'type=local,dest=./release-out',
|
||||
'--file', 'Dockerfile',
|
||||
'.'
|
||||
]
|
||||
],
|
||||
[
|
||||
'0.4.1',
|
||||
new Map<string, string>([
|
||||
['context', '.'],
|
||||
['platforms', 'linux/amd64,linux/arm64']
|
||||
]),
|
||||
[
|
||||
'buildx',
|
||||
'build',
|
||||
'--platform', 'linux/amd64,linux/arm64',
|
||||
'--file', 'Dockerfile',
|
||||
'.'
|
||||
]
|
||||
],
|
||||
[
|
||||
'0.4.1',
|
||||
new Map<string, string>([
|
||||
['context', '.']
|
||||
]),
|
||||
[
|
||||
'buildx',
|
||||
'build',
|
||||
'--iidfile', '/tmp/.docker-build-push-jest/iidfile',
|
||||
'--file', 'Dockerfile',
|
||||
'.'
|
||||
]
|
||||
],
|
||||
[
|
||||
'0.4.2',
|
||||
new Map<string, string>([
|
||||
['context', '.'],
|
||||
['secrets', 'GIT_AUTH_TOKEN=abcdefghijklmno0123456789'],
|
||||
]),
|
||||
[
|
||||
'buildx',
|
||||
'build',
|
||||
'--iidfile', '/tmp/.docker-build-push-jest/iidfile',
|
||||
'--secret', 'id=GIT_AUTH_TOKEN,src=/tmp/.docker-build-push-jest/.tmpname-jest',
|
||||
'--file', 'Dockerfile',
|
||||
'.'
|
||||
]
|
||||
],
|
||||
[
|
||||
'0.4.2',
|
||||
new Map<string, string>([
|
||||
['github-token', 'abcdefghijklmno0123456789'],
|
||||
['outputs', '.']
|
||||
]),
|
||||
[
|
||||
'buildx',
|
||||
'build',
|
||||
'--output', '.',
|
||||
'--secret', 'id=GIT_AUTH_TOKEN,src=/tmp/.docker-build-push-jest/.tmpname-jest',
|
||||
'--file', 'Dockerfile',
|
||||
'https://github.com/docker/build-push-action.git#test-jest'
|
||||
]
|
||||
],
|
||||
[
|
||||
'0.4.2',
|
||||
new Map<string, string>([
|
||||
['context', 'https://github.com/docker/build-push-action.git#heads/master'],
|
||||
['tag', 'localhost:5000/name/app:latest'],
|
||||
['platforms', 'linux/amd64,linux/arm64'],
|
||||
['secrets', 'GIT_AUTH_TOKEN=abcdefghijklmno0123456789'],
|
||||
['file', './test/Dockerfile'],
|
||||
['builder', 'builder-git-context-2'],
|
||||
['push', 'true']
|
||||
]),
|
||||
[
|
||||
'buildx',
|
||||
'build',
|
||||
'--platform', 'linux/amd64,linux/arm64',
|
||||
'--iidfile', '/tmp/.docker-build-push-jest/iidfile',
|
||||
'--secret', 'id=GIT_AUTH_TOKEN,src=/tmp/.docker-build-push-jest/.tmpname-jest',
|
||||
'--file', './test/Dockerfile',
|
||||
'--builder', 'builder-git-context-2',
|
||||
'--push',
|
||||
'https://github.com/docker/build-push-action.git#heads/master'
|
||||
]
|
||||
]
|
||||
])(
|
||||
'given %p with %p as inputs, returns %p',
|
||||
async (buildxVersion: string, inputs: Map<string, any>, expected: Array<string>) => {
|
||||
await inputs.forEach((value: string, name: string) => {
|
||||
setInput(name, value);
|
||||
});
|
||||
const defContext = context.defaultContext();
|
||||
const inp = await context.getInputs(defContext);
|
||||
console.log(inp);
|
||||
const res = await context.getArgs(inp, defContext, buildxVersion);
|
||||
console.log(res);
|
||||
expect(res).toEqual(expected);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
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('remove empty lines correctly', async () => {
|
||||
setInput('foo', 'bar\n\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('remove empty result 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;
|
||||
}
|
117
action.yml
117
action.yml
@ -1,67 +1,78 @@
|
||||
# https://help.github.com/en/articles/metadata-syntax-for-github-actions
|
||||
name: Build and push Docker images
|
||||
description: Builds and pushes Docker images and will log in to a Docker registry if required
|
||||
author: Docker
|
||||
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
|
||||
file:
|
||||
description: "Path to the Dockerfile"
|
||||
required: false
|
||||
repository:
|
||||
deprecationMessage: 'v2 is now available through docker/build-push-action@v2'
|
||||
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
|
||||
github-token:
|
||||
description: "GitHub Token used to authenticate against a repository for Git context"
|
||||
default: ${{ github.token }}
|
||||
required: false
|
||||
|
||||
outputs:
|
||||
digest:
|
||||
description: 'Image content-addressable identifier also called a digest'
|
||||
|
||||
runs:
|
||||
using: 'node12'
|
||||
main: 'dist/index.js'
|
||||
post: 'dist/index.js'
|
||||
|
18573
dist/index.js
generated
vendored
Normal file
18573
dist/index.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
12
jest.config.js
Normal file
12
jest.config.js
Normal file
@ -0,0 +1,12 @@
|
||||
module.exports = {
|
||||
clearMocks: false,
|
||||
moduleFileExtensions: ['js', 'ts'],
|
||||
setupFiles: ["dotenv/config"],
|
||||
testEnvironment: 'node',
|
||||
testMatch: ['**/*.test.ts'],
|
||||
testRunner: 'jest-circus/runner',
|
||||
transform: {
|
||||
'^.+\\.ts$': 'ts-jest'
|
||||
},
|
||||
verbose: false
|
||||
}
|
53
package.json
Normal file
53
package.json
Normal file
@ -0,0 +1,53 @@
|
||||
{
|
||||
"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.6",
|
||||
"@actions/exec": "^1.0.4",
|
||||
"@actions/github": "^4.0.0",
|
||||
"csv-parse": "^4.12.0",
|
||||
"semver": "^7.3.2",
|
||||
"tmp": "^0.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/csv-parse": "^1.2.2",
|
||||
"@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"
|
||||
}
|
||||
}
|
83
src/buildx.ts
Normal file
83
src/buildx.ts
Normal file
@ -0,0 +1,83 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import csvparse from 'csv-parse/lib/sync';
|
||||
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').split(path.sep).join(path.posix.sep);
|
||||
}
|
||||
|
||||
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 = context.tmpNameSync({
|
||||
tmpdir: context.tmpDir()
|
||||
});
|
||||
await fs.writeFileSync(secretFile, value);
|
||||
return `id=${key},src=${secretFile}`;
|
||||
}
|
||||
|
||||
export function isLocalOrTarExporter(outputs: string[]): Boolean {
|
||||
for (let output of csvparse(outputs.join(`\n`), {
|
||||
delimiter: ',',
|
||||
trim: true,
|
||||
columns: false,
|
||||
relax_column_count: true
|
||||
})) {
|
||||
// Local if no type is defined
|
||||
// https://github.com/docker/buildx/blob/d2bf42f8b4784d83fde17acb3ed84703ddc2156b/build/output.go#L29-L43
|
||||
if (output.length == 1 && !output[0].startsWith('type=')) {
|
||||
return true;
|
||||
}
|
||||
for (let [key, value] of output.map(chunk => chunk.split('=').map(item => item.trim()))) {
|
||||
if (key == 'type' && (value == 'local' || value == 'tar')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function hasGitAuthToken(secrets: string[]): Boolean {
|
||||
for (let secret of secrets) {
|
||||
if (secret.startsWith('GIT_AUTH_TOKEN=')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
169
src/context.ts
Normal file
169
src/context.ts
Normal file
@ -0,0 +1,169 @@
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import * as semver from 'semver';
|
||||
import * as tmp from 'tmp';
|
||||
import * as buildx from './buildx';
|
||||
import * as core from '@actions/core';
|
||||
import * as github from '@actions/github';
|
||||
|
||||
let _defaultContext, _tmpDir: string;
|
||||
|
||||
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[];
|
||||
githubToken: string;
|
||||
}
|
||||
|
||||
export function defaultContext(): string {
|
||||
if (!_defaultContext) {
|
||||
_defaultContext = `https://github.com/${github.context.repo.owner}/${
|
||||
github.context.repo.repo
|
||||
}.git#${github.context?.ref?.replace(/^refs\//, '')}`;
|
||||
}
|
||||
return _defaultContext;
|
||||
}
|
||||
|
||||
export function tmpDir(): string {
|
||||
if (!_tmpDir) {
|
||||
_tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'docker-build-push-')).split(path.sep).join(path.posix.sep);
|
||||
}
|
||||
return _tmpDir;
|
||||
}
|
||||
|
||||
export function tmpNameSync(options?: tmp.TmpNameOptions): string {
|
||||
return tmp.tmpNameSync(options);
|
||||
}
|
||||
|
||||
export async function getInputs(defaultContext: string): Promise<Inputs> {
|
||||
return {
|
||||
context: core.getInput('context') || defaultContext,
|
||||
file: core.getInput('file') || 'Dockerfile',
|
||||
buildArgs: await getInputList('build-args', true),
|
||||
labels: await getInputList('labels', true),
|
||||
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),
|
||||
githubToken: core.getInput('github-token')
|
||||
};
|
||||
}
|
||||
|
||||
export async function getArgs(inputs: Inputs, defaultContext: string, buildxVersion: string): Promise<Array<string>> {
|
||||
let args: Array<string> = ['buildx'];
|
||||
args.push.apply(args, await getBuildArgs(inputs, defaultContext, buildxVersion));
|
||||
args.push.apply(args, await getCommonArgs(inputs));
|
||||
args.push(inputs.context);
|
||||
return args;
|
||||
}
|
||||
|
||||
async function getBuildArgs(inputs: Inputs, defaultContext: string, 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(','));
|
||||
}
|
||||
await asyncForEach(inputs.outputs, async output => {
|
||||
args.push('--output', output);
|
||||
});
|
||||
if (
|
||||
!buildx.isLocalOrTarExporter(inputs.outputs) &&
|
||||
(inputs.platforms.length == 0 || semver.satisfies(buildxVersion, '>=0.4.2'))
|
||||
) {
|
||||
args.push('--iidfile', await buildx.getImageIDFile());
|
||||
}
|
||||
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.githubToken && !buildx.hasGitAuthToken(inputs.secrets) && inputs.context == defaultContext) {
|
||||
args.push('--secret', await buildx.getSecret(`GIT_AUTH_TOKEN=${inputs.githubToken}`));
|
||||
}
|
||||
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/)
|
||||
.filter(x => x)
|
||||
.reduce<string[]>(
|
||||
(acc, line) => acc.concat(!ignoreComma ? line.split(',').filter(x => x) : 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
34
src/exec.ts
Normal 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()
|
||||
};
|
||||
};
|
56
src/main.ts
Normal file
56
src/main.ts
Normal file
@ -0,0 +1,56 @@
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as buildx from './buildx';
|
||||
import * as context from './context';
|
||||
import * as exec from './exec';
|
||||
import * as stateHelper from './state-helper';
|
||||
import * as core from '@actions/core';
|
||||
|
||||
async function run(): Promise<void> {
|
||||
try {
|
||||
if (os.platform() !== 'linux') {
|
||||
throw new Error(`Only supported on linux platform`);
|
||||
}
|
||||
|
||||
if (!(await buildx.isAvailable())) {
|
||||
throw new Error(`Buildx is required. See https://github.com/docker/setup-buildx-action to set up buildx.`);
|
||||
}
|
||||
stateHelper.setTmpDir(context.tmpDir());
|
||||
|
||||
const buildxVersion = await buildx.getVersion();
|
||||
core.info(`📣 Buildx version: ${buildxVersion}`);
|
||||
|
||||
const defContext = context.defaultContext();
|
||||
let inputs: context.Inputs = await context.getInputs(defContext);
|
||||
|
||||
core.info(`🏃 Starting build...`);
|
||||
const args: string[] = await context.getArgs(inputs, defContext, buildxVersion);
|
||||
await exec.exec('docker', args).then(res => {
|
||||
if (res.stderr != '' && !res.success) {
|
||||
throw new Error(`buildx call failed with: ${res.stderr.match(/(.*)\s*$/)![0]}`);
|
||||
}
|
||||
});
|
||||
|
||||
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
12
src/state-helper.ts
Normal 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
3
test/Dockerfile
Normal file
@ -0,0 +1,3 @@
|
||||
FROM alpine
|
||||
|
||||
RUN echo "Hello world!"
|
8
test/Dockerfile-multi
Normal file
8
test/Dockerfile-multi
Normal 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
|
22
test/Dockerfile-multi-sudo
Normal file
22
test/Dockerfile-multi-sudo
Normal 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
21
tsconfig.json
Normal 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"
|
||||
]
|
||||
}
|
Reference in New Issue
Block a user