mirror of
https://github.com/docker/build-push-action.git
synced 2025-06-14 07:07:12 +02:00
Compare commits
38 Commits
Author | SHA1 | Date | |
---|---|---|---|
6925f94b6b | |||
bf3d577ea5 | |||
1f1cc26e46 | |||
3c98919e7f | |||
eae00c3028 | |||
1471dfb80d | |||
9c13ff40b3 | |||
61a74b1e3a | |||
d3ddc4b4ef | |||
50caab8424 | |||
d971423a6f | |||
ae5ee4ca11 | |||
3c6bad5f82 | |||
6e1d94b6b3 | |||
11ca7847e4 | |||
35f1834293 | |||
d651be4597 | |||
8832f2902d | |||
b6150991af | |||
6b0583b656 | |||
f96d0fb6b7 | |||
9e2f4416f3 | |||
6efc2b01cb | |||
953dc85723 | |||
2e36e439bc | |||
6e7bd99c53 | |||
fa61d38ad8 | |||
41a004098f | |||
41b2c888ba | |||
72350a828e | |||
5f6cd6b99d | |||
bef45c0027 | |||
c8e09bfd16 | |||
b3b0ca3523 | |||
0307a522bb | |||
8616d520af | |||
21692b9878 | |||
fc7e9a2b38 |
1
.dockerignore
Normal file
1
.dockerignore
Normal file
@ -0,0 +1 @@
|
|||||||
|
node_modules
|
15
.github/CONTRIBUTING.md
vendored
15
.github/CONTRIBUTING.md
vendored
@ -15,6 +15,21 @@ Contributions to this project are [released](https://help.github.com/articles/gi
|
|||||||
7. Push to your fork and [submit a pull request](https://github.com/docker/build-push-action/compare)
|
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.
|
8. Pat your self on the back and wait for your pull request to be reviewed and merged.
|
||||||
|
|
||||||
|
## Container based developer flow
|
||||||
|
|
||||||
|
If you don't want to maintain a Node developer environment that fits this project you can use containerized commands instead of invoking yarn directly.
|
||||||
|
|
||||||
|
```
|
||||||
|
# format code and build javascript artifacts
|
||||||
|
docker buildx bake pre-checkin
|
||||||
|
|
||||||
|
# validate all code has correctly formatted and built
|
||||||
|
docker buildx bake validate
|
||||||
|
|
||||||
|
# run tests
|
||||||
|
docker buildx bake test
|
||||||
|
```
|
||||||
|
|
||||||
Here are a few things you can do that will increase the likelihood of your pull request being accepted:
|
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**.
|
- Make sure the `README.md` and any other relevant **documentation are kept up-to-date**.
|
||||||
|
36
.github/workflows/ci.yml
vendored
36
.github/workflows/ci.yml
vendored
@ -15,7 +15,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v2.3.3
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
path: action
|
path: action
|
||||||
-
|
-
|
||||||
@ -41,7 +41,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v2.3.3
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
path: action
|
path: action
|
||||||
-
|
-
|
||||||
@ -95,7 +95,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v2.3.3
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
path: action
|
path: action
|
||||||
-
|
-
|
||||||
@ -121,6 +121,14 @@ jobs:
|
|||||||
localhost:5000/name/app:1.0.0
|
localhost:5000/name/app:1.0.0
|
||||||
secrets: |
|
secrets: |
|
||||||
GIT_AUTH_TOKEN=${{ github.token }}
|
GIT_AUTH_TOKEN=${{ github.token }}
|
||||||
|
"MYSECRET=aaaaaaaa
|
||||||
|
bbbbbbb
|
||||||
|
ccccccccc"
|
||||||
|
FOO=bar
|
||||||
|
"EMPTYLINE=aaaa
|
||||||
|
|
||||||
|
bbbb
|
||||||
|
ccc"
|
||||||
-
|
-
|
||||||
name: Inspect
|
name: Inspect
|
||||||
run: |
|
run: |
|
||||||
@ -156,7 +164,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v2.3.3
|
uses: actions/checkout@v2
|
||||||
-
|
-
|
||||||
name: Set up QEMU
|
name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v1
|
uses: docker/setup-qemu-action@v1
|
||||||
@ -203,7 +211,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v2.3.3
|
uses: actions/checkout@v2
|
||||||
-
|
-
|
||||||
name: Set up QEMU
|
name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v1
|
uses: docker/setup-qemu-action@v1
|
||||||
@ -250,7 +258,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v2.3.3
|
uses: actions/checkout@v2
|
||||||
-
|
-
|
||||||
name: Build
|
name: Build
|
||||||
id: docker_build
|
id: docker_build
|
||||||
@ -282,7 +290,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v2.3.3
|
uses: actions/checkout@v2
|
||||||
-
|
-
|
||||||
name: Build
|
name: Build
|
||||||
uses: ./
|
uses: ./
|
||||||
@ -319,7 +327,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v2.3.3
|
uses: actions/checkout@v2
|
||||||
-
|
-
|
||||||
name: Set up QEMU
|
name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v1
|
uses: docker/setup-qemu-action@v1
|
||||||
@ -372,7 +380,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v2.3.3
|
uses: actions/checkout@v2
|
||||||
-
|
-
|
||||||
name: Set up QEMU
|
name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v1
|
uses: docker/setup-qemu-action@v1
|
||||||
@ -381,10 +389,8 @@ jobs:
|
|||||||
id: buildx
|
id: buildx
|
||||||
uses: docker/setup-buildx-action@v1
|
uses: docker/setup-buildx-action@v1
|
||||||
with:
|
with:
|
||||||
# TODO: Remove image=moby/buildkit:buildx-stable-1 when moby/buildkit#1727 fixed
|
|
||||||
driver-opts: |
|
driver-opts: |
|
||||||
network=host
|
network=host
|
||||||
image=moby/buildkit:buildx-stable-1
|
|
||||||
-
|
-
|
||||||
name: Build and push (1)
|
name: Build and push (1)
|
||||||
id: docker_build
|
id: docker_build
|
||||||
@ -472,7 +478,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v2.3.3
|
uses: actions/checkout@v2
|
||||||
-
|
-
|
||||||
name: Set up QEMU
|
name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v1
|
uses: docker/setup-qemu-action@v1
|
||||||
@ -481,10 +487,8 @@ jobs:
|
|||||||
id: buildx
|
id: buildx
|
||||||
uses: docker/setup-buildx-action@v1
|
uses: docker/setup-buildx-action@v1
|
||||||
with:
|
with:
|
||||||
# TODO: Remove image=moby/buildkit:buildx-stable-1 when moby/buildkit#1727 fixed
|
|
||||||
driver-opts: |
|
driver-opts: |
|
||||||
network=host
|
network=host
|
||||||
image=moby/buildkit:buildx-stable-1
|
|
||||||
-
|
-
|
||||||
name: Cache Docker layers
|
name: Cache Docker layers
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
@ -542,7 +546,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v2.3.3
|
uses: actions/checkout@v2
|
||||||
-
|
-
|
||||||
name: Set up QEMU
|
name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v1
|
uses: docker/setup-qemu-action@v1
|
||||||
@ -551,10 +555,8 @@ jobs:
|
|||||||
id: buildx
|
id: buildx
|
||||||
uses: docker/setup-buildx-action@v1
|
uses: docker/setup-buildx-action@v1
|
||||||
with:
|
with:
|
||||||
# TODO: Remove image=moby/buildkit:buildx-stable-1 when moby/buildkit#1727 fixed
|
|
||||||
driver-opts: |
|
driver-opts: |
|
||||||
network=host
|
network=host
|
||||||
image=moby/buildkit:buildx-stable-1
|
|
||||||
-
|
-
|
||||||
name: Cache Docker layers
|
name: Cache Docker layers
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
|
83
.github/workflows/e2e.yml
vendored
Normal file
83
.github/workflows/e2e.yml
vendored
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
name: e2e
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 10 * * *' # everyday at 10am
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
tags:
|
||||||
|
- v*
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
docker:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
-
|
||||||
|
registry: ''
|
||||||
|
slug: ghactionstest/ghactionstest
|
||||||
|
username_secret: DOCKERHUB_USERNAME
|
||||||
|
password_secret: DOCKERHUB_TOKEN
|
||||||
|
-
|
||||||
|
registry: ghcr.io
|
||||||
|
slug: ghcr.io/docker-ghactiontest/test
|
||||||
|
username_secret: GHCR_USERNAME
|
||||||
|
password_secret: GHCR_PAT
|
||||||
|
-
|
||||||
|
registry: registry.gitlab.com
|
||||||
|
slug: registry.gitlab.com/test1716/test
|
||||||
|
username_secret: GITLAB_USERNAME
|
||||||
|
password_secret: GITLAB_TOKEN
|
||||||
|
steps:
|
||||||
|
-
|
||||||
|
name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
-
|
||||||
|
name: Docker meta
|
||||||
|
id: docker_meta
|
||||||
|
uses: crazy-max/ghaction-docker-meta@v1
|
||||||
|
with:
|
||||||
|
images: ${{ matrix.slug }}
|
||||||
|
-
|
||||||
|
name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v1
|
||||||
|
-
|
||||||
|
name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v1
|
||||||
|
-
|
||||||
|
name: Login to Registry
|
||||||
|
if: github.event_name != 'pull_request'
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
registry: ${{ matrix.registry }}
|
||||||
|
username: ${{ secrets[matrix.username_secret] }}
|
||||||
|
password: ${{ secrets[matrix.password_secret] }}
|
||||||
|
-
|
||||||
|
name: Build and push
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
context: ./test
|
||||||
|
file: ./test/Dockerfile-multi
|
||||||
|
platforms: linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/ppc64le,linux/s390x
|
||||||
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
|
tags: ${{ steps.docker_meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.docker_meta.outputs.labels }}
|
||||||
|
-
|
||||||
|
name: Inspect image
|
||||||
|
if: github.event_name != 'pull_request'
|
||||||
|
run: |
|
||||||
|
docker pull ${{ matrix.slug }}:${{ steps.docker_meta.outputs.version }}
|
||||||
|
docker image inspect ${{ matrix.slug }}:${{ steps.docker_meta.outputs.version }}
|
||||||
|
-
|
||||||
|
name: Check manifest
|
||||||
|
if: github.event_name != 'pull_request'
|
||||||
|
run: |
|
||||||
|
docker buildx imagetools inspect ${{ matrix.slug }}:${{ steps.docker_meta.outputs.version }}
|
||||||
|
-
|
||||||
|
name: Dump context
|
||||||
|
if: always()
|
||||||
|
uses: crazy-max/ghaction-dump-context@v1
|
66
.github/workflows/example.yml
vendored
66
.github/workflows/example.yml
vendored
@ -1,5 +1,4 @@
|
|||||||
# This workflow is provided just as an usage example and not for repo testing/verification
|
# 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
|
name: example
|
||||||
|
|
||||||
on:
|
on:
|
||||||
@ -12,6 +11,9 @@ on:
|
|||||||
- 'v*.*.*'
|
- 'v*.*.*'
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
|
env:
|
||||||
|
DOCKER_IMAGE: localhost:5000/name/app
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
docker:
|
docker:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@ -23,36 +25,14 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v2.3.3
|
uses: actions/checkout@v2
|
||||||
-
|
-
|
||||||
name: Prepare
|
name: Docker meta
|
||||||
id: prep
|
id: docker_meta
|
||||||
run: |
|
uses: crazy-max/ghaction-docker-meta@v1
|
||||||
DOCKER_IMAGE=localhost:5000/name/app
|
with:
|
||||||
VERSION=noop
|
images: ${{ env.DOCKER_IMAGE }} # list of Docker images to use as base name for tags
|
||||||
if [ "${{ github.event_name }}" = "schedule" ]; then
|
tag-sha: true # add git short SHA as Docker tag
|
||||||
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
|
name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v1
|
uses: docker/setup-buildx-action@v1
|
||||||
@ -64,15 +44,9 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
context: ./test
|
context: ./test
|
||||||
file: ./test/Dockerfile
|
file: ./test/Dockerfile
|
||||||
outputs: type=docker
|
load: true
|
||||||
tags: ${{ steps.prep.outputs.tags }}
|
tags: ${{ steps.docker_meta.outputs.tags }}
|
||||||
labels: |
|
labels: ${{ steps.docker_meta.outputs.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
|
name: Build and push to local registry
|
||||||
uses: ./
|
uses: ./
|
||||||
@ -80,23 +54,17 @@ jobs:
|
|||||||
context: ./test
|
context: ./test
|
||||||
file: ./test/Dockerfile
|
file: ./test/Dockerfile
|
||||||
push: ${{ github.event_name != 'pull_request' }}
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
tags: ${{ steps.prep.outputs.tags }}
|
tags: ${{ steps.docker_meta.outputs.tags }}
|
||||||
labels: |
|
labels: ${{ steps.docker_meta.outputs.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
|
name: Inspect image
|
||||||
run: |
|
run: |
|
||||||
docker image inspect localhost:5000/name/app:${{ steps.prep.outputs.version }}
|
docker image inspect ${{ env.DOCKER_IMAGE }}:${{ steps.docker_meta.outputs.version }}
|
||||||
-
|
-
|
||||||
name: Check manifest
|
name: Check manifest
|
||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
run: |
|
run: |
|
||||||
docker buildx imagetools inspect localhost:5000/name/app:${{ steps.prep.outputs.version }}
|
docker buildx imagetools inspect ${{ env.DOCKER_IMAGE }}:${{ steps.docker_meta.outputs.version }}
|
||||||
-
|
-
|
||||||
name: Dump context
|
name: Dump context
|
||||||
if: always()
|
if: always()
|
||||||
|
4
.github/workflows/labels.yml
vendored
4
.github/workflows/labels.yml
vendored
@ -14,7 +14,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v2.3.3
|
uses: actions/checkout@v2
|
||||||
-
|
-
|
||||||
name: Run Labeler
|
name: Run Labeler
|
||||||
uses: crazy-max/ghaction-github-labeler@v3.1.0
|
uses: crazy-max/ghaction-github-labeler@v3
|
||||||
|
17
.github/workflows/test.yml
vendored
17
.github/workflows/test.yml
vendored
@ -9,12 +9,25 @@ on:
|
|||||||
- master
|
- master
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
test-containerized:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
-
|
||||||
|
name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
-
|
||||||
|
name: Validate
|
||||||
|
run: docker buildx bake validate
|
||||||
|
-
|
||||||
|
name: Test
|
||||||
|
run: docker buildx bake test
|
||||||
|
|
||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v2.3.3
|
uses: actions/checkout@v2
|
||||||
-
|
-
|
||||||
name: Install
|
name: Install
|
||||||
run: yarn install
|
run: yarn install
|
||||||
@ -23,7 +36,7 @@ jobs:
|
|||||||
run: yarn run test
|
run: yarn run test
|
||||||
-
|
-
|
||||||
name: Upload coverage
|
name: Upload coverage
|
||||||
uses: codecov/codecov-action@v1.0.14
|
uses: codecov/codecov-action@v1
|
||||||
if: success()
|
if: success()
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
52
Dockerfile
Normal file
52
Dockerfile
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#syntax=docker/dockerfile:1.1-experimental
|
||||||
|
|
||||||
|
FROM node:12 AS deps
|
||||||
|
WORKDIR /src
|
||||||
|
COPY package.json yarn.lock ./
|
||||||
|
RUN --mount=type=cache,target=/usr/local/share/.cache/yarn \
|
||||||
|
yarn install
|
||||||
|
|
||||||
|
FROM scratch AS update-yarn
|
||||||
|
COPY --from=deps /src/yarn.lock /
|
||||||
|
|
||||||
|
FROM deps AS validate-yarn
|
||||||
|
COPY .git .git
|
||||||
|
RUN status=$(git status --porcelain -- yarn.lock); if [ -n "$status" ]; then echo $status; exit 1; fi
|
||||||
|
|
||||||
|
FROM deps AS base
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
FROM base AS build
|
||||||
|
RUN yarn build
|
||||||
|
|
||||||
|
FROM deps AS test
|
||||||
|
COPY --from=docker /usr/local/bin/docker /usr/bin/
|
||||||
|
ARG TARGETOS
|
||||||
|
ARG TARGETARCH
|
||||||
|
ARG BUILDX_VERSION=v0.4.2
|
||||||
|
ENV RUNNER_TEMP=/tmp/github_runner
|
||||||
|
ENV RUNNER_TOOL_CACHE=/tmp/github_tool_cache
|
||||||
|
RUN mkdir -p /usr/local/lib/docker/cli-plugins && \
|
||||||
|
curl -fsSL https://github.com/docker/buildx/releases/download/$BUILDX_VERSION/buildx-$BUILDX_VERSION.$TARGETOS-$TARGETARCH > /usr/local/lib/docker/cli-plugins/docker-buildx && \
|
||||||
|
chmod +x /usr/local/lib/docker/cli-plugins/docker-buildx && \
|
||||||
|
docker buildx version
|
||||||
|
COPY . .
|
||||||
|
RUN yarn run test
|
||||||
|
|
||||||
|
FROM base AS run-format
|
||||||
|
RUN yarn run format
|
||||||
|
|
||||||
|
FROM scratch AS format
|
||||||
|
COPY --from=run-format /src/src/*.ts /src/
|
||||||
|
|
||||||
|
FROM base AS validate-format
|
||||||
|
RUN yarn run format-check
|
||||||
|
|
||||||
|
FROM scratch AS dist
|
||||||
|
COPY --from=build /src/dist/ /dist/
|
||||||
|
|
||||||
|
FROM build AS validate-build
|
||||||
|
RUN status=$(git status --porcelain -- dist); if [ -n "$status" ]; then echo $status; exit 1; fi
|
||||||
|
|
||||||
|
FROM base AS dev
|
||||||
|
ENTRYPOINT ["bash"]
|
111
README.md
111
README.md
@ -39,11 +39,13 @@ ___
|
|||||||
* [Local registry](#local-registry)
|
* [Local registry](#local-registry)
|
||||||
* [Export image to Docker](#export-image-to-docker)
|
* [Export image to Docker](#export-image-to-docker)
|
||||||
* [Leverage GitHub cache](#leverage-github-cache)
|
* [Leverage GitHub cache](#leverage-github-cache)
|
||||||
* [Complete workflow](#complete-workflow)
|
* [Handle tags and labels](#handle-tags-and-labels)
|
||||||
* [Update DockerHub repo description](#update-dockerhub-repo-description)
|
* [Update DockerHub repo description](#update-dockerhub-repo-description)
|
||||||
* [Customizing](#customizing)
|
* [Customizing](#customizing)
|
||||||
* [inputs](#inputs)
|
* [inputs](#inputs)
|
||||||
* [outputs](#outputs)
|
* [outputs](#outputs)
|
||||||
|
* [Notes](#notes)
|
||||||
|
* [Multi-line secret value](#multi-line-secret-value)
|
||||||
* [Troubleshooting](#troubleshooting)
|
* [Troubleshooting](#troubleshooting)
|
||||||
* [Keep up-to-date with GitHub Dependabot](#keep-up-to-date-with-github-dependabot)
|
* [Keep up-to-date with GitHub Dependabot](#keep-up-to-date-with-github-dependabot)
|
||||||
* [Limitation](#limitation)
|
* [Limitation](#limitation)
|
||||||
@ -472,17 +474,12 @@ using [actions/cache](https://github.com/actions/cache) with this action:
|
|||||||
> If you want to [export layers for all stages](https://github.com/docker/buildx#--cache-tonametypetypekeyvalue),
|
> 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`.
|
> you have to specify `mode=max` attribute in `cache-to`.
|
||||||
|
|
||||||
### Complete workflow
|
### Handle tags and labels
|
||||||
|
|
||||||
If you come from [`v1`](https://github.com/docker/build-push-action/tree/releases/v1#readme) and want an
|
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)
|
"automatic" tag management 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.
|
for labels, you can do it in a dedicated step. The following workflow will use the [Docker meta action](https://github.com/crazy-max/ghaction-docker-meta)
|
||||||
|
to handle tags and labels based on GitHub actions events and Git metadata.
|
||||||
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>
|
<details>
|
||||||
<summary><b>Show workflow</b></summary>
|
<summary><b>Show workflow</b></summary>
|
||||||
@ -508,42 +505,12 @@ to your needs:
|
|||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
-
|
-
|
||||||
name: Repo metadata
|
name: Docker meta
|
||||||
id: repo
|
id: docker_meta
|
||||||
uses: actions/github-script@v3
|
uses: crazy-max/ghaction-docker-meta@v1
|
||||||
with:
|
with:
|
||||||
script: |
|
images: name/app # list of Docker images to use as base name for tags
|
||||||
const repo = await github.repos.get(context.repo)
|
tag-sha: true # add git short SHA as Docker tag
|
||||||
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
|
name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v1
|
uses: docker/setup-qemu-action@v1
|
||||||
@ -566,28 +533,11 @@ to your needs:
|
|||||||
file: ./Dockerfile
|
file: ./Dockerfile
|
||||||
platforms: linux/amd64,linux/arm64,linux/386
|
platforms: linux/amd64,linux/arm64,linux/386
|
||||||
push: ${{ github.event_name != 'pull_request' }}
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
tags: ${{ steps.prep.outputs.tags }}
|
tags: ${{ steps.docker_meta.outputs.tags }}
|
||||||
labels: |
|
labels: ${{ steps.docker_meta.outputs.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>
|
</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
|
### Update DockerHub repo description
|
||||||
|
|
||||||
You can update the [DockerHub repository description](https://docs.docker.com/docker-hub/repos/) using
|
You can update the [DockerHub repository description](https://docs.docker.com/docker-hub/repos/) using
|
||||||
@ -658,7 +608,7 @@ Following inputs can be used as `step.with` keys
|
|||||||
|---------------------|----------|------------------------------------|
|
|---------------------|----------|------------------------------------|
|
||||||
| `builder` | String | Builder instance (see [setup-buildx](https://github.com/docker/setup-buildx-action) action) |
|
| `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)) |
|
| `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`) |
|
| `file` | String | Path to the Dockerfile (default `./Dockerfile`) |
|
||||||
| `build-args` | List | List of build-time variables |
|
| `build-args` | List | List of build-time variables |
|
||||||
| `labels` | List | List of metadata for an image |
|
| `labels` | List | List of metadata for an image |
|
||||||
| `tags` | List/CSV | List of tags |
|
| `tags` | List/CSV | List of tags |
|
||||||
@ -673,6 +623,7 @@ Following inputs can be used as `step.with` keys
|
|||||||
| `cache-from` | List | List of [external cache sources](https://github.com/docker/buildx#--cache-fromnametypetypekeyvalue) (eg. `type=local,src=path/to/dir`) |
|
| `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`) |
|
| `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`) |
|
| `secrets` | List | List of secrets to expose to the build (eg. `key=value`, `GIT_AUTH_TOKEN=mytoken`) |
|
||||||
|
| `ssh` | List | List of SSH agent socket or keys to expose to the build |
|
||||||
|
|
||||||
### outputs
|
### outputs
|
||||||
|
|
||||||
@ -682,6 +633,36 @@ Following outputs are available
|
|||||||
|---------------|---------|---------------------------------------|
|
|---------------|---------|---------------------------------------|
|
||||||
| `digest` | String | Image content-addressable identifier also called a digest |
|
| `digest` | String | Image content-addressable identifier also called a digest |
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
### Multi-line secret value
|
||||||
|
|
||||||
|
To handle multi-line value for a secret, you will need to place the key-value pair between quotes:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
secrets: |
|
||||||
|
"MYSECRET=${{ secrets.GPG_KEY }}"
|
||||||
|
GIT_AUTH_TOKEN=abcdefghi,jklmno=0123456789
|
||||||
|
"MYSECRET=aaaaaaaa
|
||||||
|
bbbbbbb
|
||||||
|
ccccccccc"
|
||||||
|
FOO=bar
|
||||||
|
"EMPTYLINE=aaaa
|
||||||
|
|
||||||
|
bbbb
|
||||||
|
ccc"
|
||||||
|
```
|
||||||
|
|
||||||
|
| Key | Value |
|
||||||
|
|--------------------|--------------------------------------------------|
|
||||||
|
| `MYSECRET` | `***********************` |
|
||||||
|
| `GIT_AUTH_TOKEN` | `abcdefghi,jklmno=0123456789` |
|
||||||
|
| `MYSECRET` | `aaaaaaaa\nbbbbbbb\nccccccccc` |
|
||||||
|
| `FOO` | `bar` |
|
||||||
|
| `EMPTYLINE` | `aaaa\n\nbbbb\nccc` |
|
||||||
|
|
||||||
|
> Note: all quote signs need to be doubled for escaping.
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
See [TROUBLESHOOTING.md](TROUBLESHOOTING.md)
|
See [TROUBLESHOOTING.md](TROUBLESHOOTING.md)
|
||||||
|
@ -1,6 +1,114 @@
|
|||||||
# Troubleshooting
|
# Troubleshooting
|
||||||
|
|
||||||
## Errors on pushing to registry
|
* [`auto-push is currently not implemented for docker driver`](#auto-push-is-currently-not-implemented-for-docker-driver)
|
||||||
|
* [Cannot push to a registry](#cannot-push-to-a-registry)
|
||||||
|
|
||||||
|
## `auto-push is currently not implemented for docker driver`
|
||||||
|
|
||||||
|
If you're using the default builder (which uses the docker driver) without using our `setup-buildx-action`, you may
|
||||||
|
encounter this error message if you try to push your image:
|
||||||
|
|
||||||
|
```
|
||||||
|
Run docker/build-push-action@v2
|
||||||
|
📣 Buildx version: 0.4.2
|
||||||
|
🏃 Starting build...
|
||||||
|
/usr/bin/docker buildx build --tag localhost:5000/name/app:latest --iidfile /tmp/docker-build-push-eYl8PB/iidfile --file ./test/Dockerfile --push ./test
|
||||||
|
auto-push is currently not implemented for docker driver
|
||||||
|
Error: buildx call failed with: auto-push is currently not implemented for docker driver
|
||||||
|
```
|
||||||
|
|
||||||
|
While waiting for an implementation to be done on buildx/buildkit, you have the following possibilities
|
||||||
|
to solve this atm:
|
||||||
|
|
||||||
|
### With `docker-container` driver and `setup-buildx`
|
||||||
|
|
||||||
|
> Recommended solution
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
-
|
||||||
|
name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
-
|
||||||
|
name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v1
|
||||||
|
-
|
||||||
|
name: Login
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ env.USER }}
|
||||||
|
password: ${{ secrets.PASSWORD }}
|
||||||
|
-
|
||||||
|
name: Build and push
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
tags: ${{ env.REGISTRY }}/myapp:latest
|
||||||
|
push: true
|
||||||
|
```
|
||||||
|
|
||||||
|
### With `docker` driver
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
-
|
||||||
|
name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
-
|
||||||
|
name: Login
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ env.USER }}
|
||||||
|
password: ${{ secrets.PASSWORD }}
|
||||||
|
-
|
||||||
|
name: Build
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
tags: ${{ env.REGISTRY }}/myapp:latest
|
||||||
|
load: true
|
||||||
|
-
|
||||||
|
name: Push
|
||||||
|
run: docker push ${{ env.REGISTRY }}/myapp:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
### With `docker` driver and `setup-buildx`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
-
|
||||||
|
name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
-
|
||||||
|
name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v1
|
||||||
|
with:
|
||||||
|
driver: docker
|
||||||
|
-
|
||||||
|
name: Login
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ env.USER }}
|
||||||
|
password: ${{ secrets.PASSWORD }}
|
||||||
|
-
|
||||||
|
name: Build
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
tags: ${{ env.REGISTRY }}/myapp:latest
|
||||||
|
load: true
|
||||||
|
-
|
||||||
|
name: Push
|
||||||
|
run: docker push ${{ env.REGISTRY }}/myapp:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
## Cannot push to a registry
|
||||||
|
|
||||||
While pushing to a registry, you may encounter these kinds of issues:
|
While pushing to a registry, you may encounter these kinds of issues:
|
||||||
|
|
||||||
@ -38,7 +146,7 @@ jobs:
|
|||||||
containerd:
|
containerd:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
-
|
-
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
* Add [`outputs`](https://github.com/docker/buildx#-o---outputpath-typetypekeyvalue) 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-from`](https://github.com/docker/buildx#--cache-fromnametypetypekeyvalue) input (`cache_froms` removed)
|
||||||
* Add [`cache-to`](https://github.com/docker/buildx#--cache-tonametypetypekeyvalue) input
|
* Add [`cache-to`](https://github.com/docker/buildx#--cache-tonametypetypekeyvalue) input
|
||||||
|
* Rename `build_args` input to `build-args` for consistency with other Docker build tools
|
||||||
* Add `secrets` input
|
* Add `secrets` input
|
||||||
* Review `tags` input
|
* Review `tags` input
|
||||||
* Remove `repository` input. See [Simple workflow](#simple-workflow) for migration
|
* Remove `repository` input. See [Simple workflow](#simple-workflow) for migration
|
||||||
@ -91,6 +92,7 @@ steps:
|
|||||||
push: ${{ github.event_name != 'pull_request' }}
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
tag_with_ref: true
|
tag_with_ref: true
|
||||||
tag_with_sha: true
|
tag_with_sha: true
|
||||||
|
add_git_labels: true
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
@ -138,7 +140,10 @@ steps:
|
|||||||
push: ${{ github.event_name != 'pull_request' }}
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
tags: ${{ steps.prep.outputs.tags }}
|
tags: ${{ steps.prep.outputs.tags }}
|
||||||
labels: |
|
labels: |
|
||||||
org.opencontainers.image.source=${{ github.event.repository.clone_url }}
|
org.opencontainers.image.source=${{ github.event.repository.html_url }}
|
||||||
org.opencontainers.image.created=${{ steps.prep.outputs.created }}
|
org.opencontainers.image.created=${{ steps.prep.outputs.created }}
|
||||||
org.opencontainers.image.revision=${{ github.sha }}
|
org.opencontainers.image.revision=${{ github.sha }}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> You can also use the [Docker meta action](https://github.com/crazy-max/ghaction-docker-meta) to handle tags and
|
||||||
|
> labels based on GitHub actions events and Git metadata. A workflow example is available in the [README](README.md#handle-tags-and-labels).
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as semver from 'semver';
|
import * as semver from 'semver';
|
||||||
import * as buildx from '../src/buildx';
|
|
||||||
import * as exec from '@actions/exec';
|
|
||||||
import * as context from '../src/context';
|
|
||||||
|
|
||||||
|
import * as buildx from '../src/buildx';
|
||||||
|
import * as context from '../src/context';
|
||||||
|
import * as docker from '../src/docker';
|
||||||
|
|
||||||
|
const tmpNameSync = path.join('/tmp/.docker-build-push-jest', '.tmpname-jest').split(path.sep).join(path.posix.sep);
|
||||||
const digest = 'sha256:bfb45ab72e46908183546477a08f8867fc40cebadd00af54b071b097aed127a9';
|
const digest = 'sha256:bfb45ab72e46908183546477a08f8867fc40cebadd00af54b071b097aed127a9';
|
||||||
|
|
||||||
jest.spyOn(context, 'tmpDir').mockImplementation((): string => {
|
jest.spyOn(context, 'tmpDir').mockImplementation((): string => {
|
||||||
@ -16,7 +18,7 @@ jest.spyOn(context, 'tmpDir').mockImplementation((): string => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
jest.spyOn(context, 'tmpNameSync').mockImplementation((): string => {
|
jest.spyOn(context, 'tmpNameSync').mockImplementation((): string => {
|
||||||
return path.join('/tmp/.docker-build-push-jest', '.tmpname-jest').split(path.sep).join(path.posix.sep);
|
return tmpNameSync;
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getImageID', () => {
|
describe('getImageID', () => {
|
||||||
@ -91,11 +93,18 @@ describe('isLocalOrTarExporter', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('getVersion', () => {
|
describe('getVersion', () => {
|
||||||
it('valid', async () => {
|
async function isDaemonRunning() {
|
||||||
const version = await buildx.getVersion();
|
return await docker.isDaemonRunning();
|
||||||
console.log(`version: ${version}`);
|
}
|
||||||
expect(semver.valid(version)).not.toBeNull();
|
(isDaemonRunning() ? it : it.skip)(
|
||||||
}, 100000);
|
'valid',
|
||||||
|
async () => {
|
||||||
|
const version = await buildx.getVersion();
|
||||||
|
console.log(`version: ${version}`);
|
||||||
|
expect(semver.valid(version)).not.toBeNull();
|
||||||
|
},
|
||||||
|
100000
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('parseVersion', () => {
|
describe('parseVersion', () => {
|
||||||
@ -107,3 +116,26 @@ describe('parseVersion', () => {
|
|||||||
expect(await buildx.parseVersion(stdout)).toEqual(expected);
|
expect(await buildx.parseVersion(stdout)).toEqual(expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getSecret', () => {
|
||||||
|
test.each([
|
||||||
|
['A_SECRET=abcdef0123456789', 'A_SECRET', 'abcdef0123456789', false],
|
||||||
|
['GIT_AUTH_TOKEN=abcdefghijklmno=0123456789', 'GIT_AUTH_TOKEN', 'abcdefghijklmno=0123456789', false],
|
||||||
|
['MY_KEY=c3RyaW5nLXdpdGgtZXF1YWxzCg==', 'MY_KEY', 'c3RyaW5nLXdpdGgtZXF1YWxzCg==', false],
|
||||||
|
['aaaaaaaa', '', '', true],
|
||||||
|
['aaaaaaaa=', '', '', true],
|
||||||
|
['=bbbbbbb', '', '', true]
|
||||||
|
])('given %p key and %p secret', async (kvp, key, secret, invalid) => {
|
||||||
|
try {
|
||||||
|
const secretArgs = await buildx.getSecret(kvp);
|
||||||
|
expect(true).toBe(!invalid);
|
||||||
|
console.log(`secretArgs: ${secretArgs}`);
|
||||||
|
expect(secretArgs).toEqual(`id=${key},src=${tmpNameSync}`);
|
||||||
|
const secretContent = await fs.readFileSync(tmpNameSync, 'utf-8');
|
||||||
|
console.log(`secretValue: ${secretContent}`);
|
||||||
|
expect(secretContent).toEqual(secret);
|
||||||
|
} catch (err) {
|
||||||
|
expect(true).toBe(invalid);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
@ -1,8 +1,115 @@
|
|||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as buildx from '../src/buildx';
|
|
||||||
import * as context from '../src/context';
|
import * as context from '../src/context';
|
||||||
|
|
||||||
|
const pgp = `-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||||
|
|
||||||
|
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
|
||||||
|
PejgXO0uIRolYQ3sz2tMGhx1MfBqH64=
|
||||||
|
=WbwB
|
||||||
|
-----END PGP PRIVATE KEY BLOCK-----`;
|
||||||
|
|
||||||
jest.spyOn(context, 'defaultContext').mockImplementation((): string => {
|
jest.spyOn(context, 'defaultContext').mockImplementation((): string => {
|
||||||
return 'https://github.com/docker/build-push-action.git#test-jest';
|
return 'https://github.com/docker/build-push-action.git#test-jest';
|
||||||
});
|
});
|
||||||
@ -107,7 +214,7 @@ describe('getArgs', () => {
|
|||||||
'0.4.2',
|
'0.4.2',
|
||||||
new Map<string, string>([
|
new Map<string, string>([
|
||||||
['context', '.'],
|
['context', '.'],
|
||||||
['secrets', 'GIT_AUTH_TOKEN=abcdefghijklmno0123456789'],
|
['secrets', 'GIT_AUTH_TOKEN=abcdefghijklmno=0123456789'],
|
||||||
]),
|
]),
|
||||||
[
|
[
|
||||||
'buildx',
|
'buildx',
|
||||||
@ -139,7 +246,7 @@ describe('getArgs', () => {
|
|||||||
['context', 'https://github.com/docker/build-push-action.git#heads/master'],
|
['context', 'https://github.com/docker/build-push-action.git#heads/master'],
|
||||||
['tag', 'localhost:5000/name/app:latest'],
|
['tag', 'localhost:5000/name/app:latest'],
|
||||||
['platforms', 'linux/amd64,linux/arm64'],
|
['platforms', 'linux/amd64,linux/arm64'],
|
||||||
['secrets', 'GIT_AUTH_TOKEN=abcdefghijklmno0123456789'],
|
['secrets', 'GIT_AUTH_TOKEN=abcdefghijklmno=0123456789'],
|
||||||
['file', './test/Dockerfile'],
|
['file', './test/Dockerfile'],
|
||||||
['builder', 'builder-git-context-2'],
|
['builder', 'builder-git-context-2'],
|
||||||
['push', 'true']
|
['push', 'true']
|
||||||
@ -155,6 +262,74 @@ describe('getArgs', () => {
|
|||||||
'--push',
|
'--push',
|
||||||
'https://github.com/docker/build-push-action.git#heads/master'
|
'https://github.com/docker/build-push-action.git#heads/master'
|
||||||
]
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'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=abcdefghi,jklmno=0123456789
|
||||||
|
"MYSECRET=aaaaaaaa
|
||||||
|
bbbbbbb
|
||||||
|
ccccccccc"
|
||||||
|
FOO=bar
|
||||||
|
"EMPTYLINE=aaaa
|
||||||
|
|
||||||
|
bbbb
|
||||||
|
ccc"`],
|
||||||
|
['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',
|
||||||
|
'--secret', 'id=MYSECRET,src=/tmp/.docker-build-push-jest/.tmpname-jest',
|
||||||
|
'--secret', 'id=FOO,src=/tmp/.docker-build-push-jest/.tmpname-jest',
|
||||||
|
'--secret', 'id=EMPTYLINE,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'
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'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=abcdefghi,jklmno=0123456789
|
||||||
|
MYSECRET=aaaaaaaa
|
||||||
|
bbbbbbb
|
||||||
|
ccccccccc
|
||||||
|
FOO=bar
|
||||||
|
EMPTYLINE=aaaa
|
||||||
|
|
||||||
|
bbbb
|
||||||
|
ccc`],
|
||||||
|
['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',
|
||||||
|
'--secret', 'id=MYSECRET,src=/tmp/.docker-build-push-jest/.tmpname-jest',
|
||||||
|
'--secret', 'id=FOO,src=/tmp/.docker-build-push-jest/.tmpname-jest',
|
||||||
|
'--secret', 'id=EMPTYLINE,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',
|
'given %p with %p as inputs, returns %p',
|
||||||
@ -173,68 +348,167 @@ describe('getArgs', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('getInputList', () => {
|
describe('getInputList', () => {
|
||||||
it('handles single line correctly', async () => {
|
it('single line correctly', async () => {
|
||||||
await setInput('foo', 'bar');
|
await setInput('foo', 'bar');
|
||||||
const res = await context.getInputList('foo');
|
const res = await context.getInputList('foo');
|
||||||
console.log(res);
|
console.log(res);
|
||||||
expect(res).toEqual(['bar']);
|
expect(res).toEqual(['bar']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles multiple lines correctly', async () => {
|
it('multiline correctly', async () => {
|
||||||
setInput('foo', 'bar\nbaz');
|
setInput('foo', 'bar\nbaz');
|
||||||
const res = await context.getInputList('foo');
|
const res = await context.getInputList('foo');
|
||||||
console.log(res);
|
console.log(res);
|
||||||
expect(res).toEqual(['bar', 'baz']);
|
expect(res).toEqual(['bar', 'baz']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('remove empty lines correctly', async () => {
|
it('empty lines correctly', async () => {
|
||||||
setInput('foo', 'bar\n\nbaz');
|
setInput('foo', 'bar\n\nbaz');
|
||||||
const res = await context.getInputList('foo');
|
const res = await context.getInputList('foo');
|
||||||
console.log(res);
|
console.log(res);
|
||||||
expect(res).toEqual(['bar', 'baz']);
|
expect(res).toEqual(['bar', 'baz']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles comma correctly', async () => {
|
it('comma correctly', async () => {
|
||||||
setInput('foo', 'bar,baz');
|
setInput('foo', 'bar,baz');
|
||||||
const res = await context.getInputList('foo');
|
const res = await context.getInputList('foo');
|
||||||
console.log(res);
|
console.log(res);
|
||||||
expect(res).toEqual(['bar', 'baz']);
|
expect(res).toEqual(['bar', 'baz']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('remove empty result correctly', async () => {
|
it('empty result correctly', async () => {
|
||||||
setInput('foo', 'bar,baz,');
|
setInput('foo', 'bar,baz,');
|
||||||
const res = await context.getInputList('foo');
|
const res = await context.getInputList('foo');
|
||||||
console.log(res);
|
console.log(res);
|
||||||
expect(res).toEqual(['bar', 'baz']);
|
expect(res).toEqual(['bar', 'baz']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles different new lines correctly', async () => {
|
it('different new lines correctly', async () => {
|
||||||
setInput('foo', 'bar\r\nbaz');
|
setInput('foo', 'bar\r\nbaz');
|
||||||
const res = await context.getInputList('foo');
|
const res = await context.getInputList('foo');
|
||||||
console.log(res);
|
console.log(res);
|
||||||
expect(res).toEqual(['bar', 'baz']);
|
expect(res).toEqual(['bar', 'baz']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles different new lines and comma correctly', async () => {
|
it('different new lines and comma correctly', async () => {
|
||||||
setInput('foo', 'bar\r\nbaz,bat');
|
setInput('foo', 'bar\r\nbaz,bat');
|
||||||
const res = await context.getInputList('foo');
|
const res = await context.getInputList('foo');
|
||||||
console.log(res);
|
console.log(res);
|
||||||
expect(res).toEqual(['bar', 'baz', 'bat']);
|
expect(res).toEqual(['bar', 'baz', 'bat']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles multiple lines and ignoring comma correctly', async () => {
|
it('multiline and ignoring comma correctly', async () => {
|
||||||
setInput('cache-from', 'user/app:cache\ntype=local,src=path/to/dir');
|
setInput('cache-from', 'user/app:cache\ntype=local,src=path/to/dir');
|
||||||
const res = await context.getInputList('cache-from', true);
|
const res = await context.getInputList('cache-from', true);
|
||||||
console.log(res);
|
console.log(res);
|
||||||
expect(res).toEqual(['user/app:cache', 'type=local,src=path/to/dir']);
|
expect(res).toEqual(['user/app:cache', 'type=local,src=path/to/dir']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles different new lines and ignoring comma correctly', async () => {
|
it('different new lines and ignoring comma correctly', async () => {
|
||||||
setInput('cache-from', 'user/app:cache\r\ntype=local,src=path/to/dir');
|
setInput('cache-from', 'user/app:cache\r\ntype=local,src=path/to/dir');
|
||||||
const res = await context.getInputList('cache-from', true);
|
const res = await context.getInputList('cache-from', true);
|
||||||
console.log(res);
|
console.log(res);
|
||||||
expect(res).toEqual(['user/app:cache', 'type=local,src=path/to/dir']);
|
expect(res).toEqual(['user/app:cache', 'type=local,src=path/to/dir']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('multiline values', async () => {
|
||||||
|
setInput(
|
||||||
|
'secrets',
|
||||||
|
`GIT_AUTH_TOKEN=abcdefgh,ijklmno=0123456789
|
||||||
|
"MYSECRET=aaaaaaaa
|
||||||
|
bbbbbbb
|
||||||
|
ccccccccc"
|
||||||
|
FOO=bar`
|
||||||
|
);
|
||||||
|
const res = await context.getInputList('secrets', true);
|
||||||
|
console.log(res);
|
||||||
|
expect(res).toEqual([
|
||||||
|
'GIT_AUTH_TOKEN=abcdefgh,ijklmno=0123456789',
|
||||||
|
`MYSECRET=aaaaaaaa
|
||||||
|
bbbbbbb
|
||||||
|
ccccccccc`,
|
||||||
|
'FOO=bar'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('multiline values with empty lines', async () => {
|
||||||
|
setInput(
|
||||||
|
'secrets',
|
||||||
|
`GIT_AUTH_TOKEN=abcdefgh,ijklmno=0123456789
|
||||||
|
"MYSECRET=aaaaaaaa
|
||||||
|
bbbbbbb
|
||||||
|
ccccccccc"
|
||||||
|
FOO=bar
|
||||||
|
"EMPTYLINE=aaaa
|
||||||
|
|
||||||
|
bbbb
|
||||||
|
ccc"`
|
||||||
|
);
|
||||||
|
const res = await context.getInputList('secrets', true);
|
||||||
|
console.log(res);
|
||||||
|
expect(res).toEqual([
|
||||||
|
'GIT_AUTH_TOKEN=abcdefgh,ijklmno=0123456789',
|
||||||
|
`MYSECRET=aaaaaaaa
|
||||||
|
bbbbbbb
|
||||||
|
ccccccccc`,
|
||||||
|
'FOO=bar',
|
||||||
|
`EMPTYLINE=aaaa
|
||||||
|
|
||||||
|
bbbb
|
||||||
|
ccc`
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('multiline values without quotes', async () => {
|
||||||
|
setInput(
|
||||||
|
'secrets',
|
||||||
|
`GIT_AUTH_TOKEN=abcdefgh,ijklmno=0123456789
|
||||||
|
MYSECRET=aaaaaaaa
|
||||||
|
bbbbbbb
|
||||||
|
ccccccccc
|
||||||
|
FOO=bar`
|
||||||
|
);
|
||||||
|
const res = await context.getInputList('secrets', true);
|
||||||
|
console.log(res);
|
||||||
|
expect(res).toEqual([
|
||||||
|
'GIT_AUTH_TOKEN=abcdefgh,ijklmno=0123456789',
|
||||||
|
'MYSECRET=aaaaaaaa',
|
||||||
|
'bbbbbbb',
|
||||||
|
'ccccccccc',
|
||||||
|
'FOO=bar'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('large multiline values', async () => {
|
||||||
|
setInput(
|
||||||
|
'secrets',
|
||||||
|
`"GPG_KEY=${pgp}"
|
||||||
|
FOO=bar`
|
||||||
|
);
|
||||||
|
const res = await context.getInputList('secrets', true);
|
||||||
|
console.log(res);
|
||||||
|
expect(res).toEqual([`GPG_KEY=${pgp}`, 'FOO=bar']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('multiline values escape quotes', async () => {
|
||||||
|
setInput(
|
||||||
|
'secrets',
|
||||||
|
`GIT_AUTH_TOKEN=abcdefgh,ijklmno=0123456789
|
||||||
|
"MYSECRET=aaaaaaaa
|
||||||
|
bbbb""bbb
|
||||||
|
ccccccccc"
|
||||||
|
FOO=bar`
|
||||||
|
);
|
||||||
|
const res = await context.getInputList('secrets', true);
|
||||||
|
console.log(res);
|
||||||
|
expect(res).toEqual([
|
||||||
|
'GIT_AUTH_TOKEN=abcdefgh,ijklmno=0123456789',
|
||||||
|
`MYSECRET=aaaaaaaa
|
||||||
|
bbbb\"bbb
|
||||||
|
ccccccccc`,
|
||||||
|
'FOO=bar'
|
||||||
|
]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('asyncForEach', () => {
|
describe('asyncForEach', () => {
|
||||||
|
@ -67,6 +67,9 @@ inputs:
|
|||||||
description: "GitHub Token used to authenticate against a repository for Git context"
|
description: "GitHub Token used to authenticate against a repository for Git context"
|
||||||
default: ${{ github.token }}
|
default: ${{ github.token }}
|
||||||
required: false
|
required: false
|
||||||
|
ssh:
|
||||||
|
description: "List of SSH agent socket or keys to expose to the build"
|
||||||
|
required: false
|
||||||
|
|
||||||
outputs:
|
outputs:
|
||||||
digest:
|
digest:
|
||||||
|
3681
dist/index.js
generated
vendored
3681
dist/index.js
generated
vendored
File diff suppressed because one or more lines are too long
42
docker-bake.hcl
Normal file
42
docker-bake.hcl
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
group "default" {
|
||||||
|
targets = ["build"]
|
||||||
|
}
|
||||||
|
|
||||||
|
group "pre-checkin" {
|
||||||
|
targets = ["update-yarn", "format", "build"]
|
||||||
|
}
|
||||||
|
|
||||||
|
group "validate" {
|
||||||
|
targets = ["validate-format", "validate-build", "validate-yarn"]
|
||||||
|
}
|
||||||
|
|
||||||
|
target "update-yarn" {
|
||||||
|
target = "update-yarn"
|
||||||
|
output = ["."]
|
||||||
|
}
|
||||||
|
|
||||||
|
target "build" {
|
||||||
|
target = "dist"
|
||||||
|
output = ["."]
|
||||||
|
}
|
||||||
|
|
||||||
|
target "test" {
|
||||||
|
target = "test"
|
||||||
|
}
|
||||||
|
|
||||||
|
target "format" {
|
||||||
|
target = "format"
|
||||||
|
output = ["."]
|
||||||
|
}
|
||||||
|
|
||||||
|
target "validate-format" {
|
||||||
|
target = "validate-format"
|
||||||
|
}
|
||||||
|
|
||||||
|
target "validate-build" {
|
||||||
|
target = "validate-build"
|
||||||
|
}
|
||||||
|
|
||||||
|
target "validate-yarn" {
|
||||||
|
target = "validate-yarn"
|
||||||
|
}
|
@ -31,7 +31,7 @@
|
|||||||
"@actions/core": "^1.2.6",
|
"@actions/core": "^1.2.6",
|
||||||
"@actions/exec": "^1.0.4",
|
"@actions/exec": "^1.0.4",
|
||||||
"@actions/github": "^4.0.0",
|
"@actions/github": "^4.0.0",
|
||||||
"csv-parse": "^4.12.0",
|
"csv-parse": "^4.14.1",
|
||||||
"semver": "^7.3.2",
|
"semver": "^7.3.2",
|
||||||
"tmp": "^0.2.1"
|
"tmp": "^0.2.1"
|
||||||
},
|
},
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
|
import csvparse from 'csv-parse/lib/sync';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import csvparse from 'csv-parse/lib/sync';
|
|
||||||
import * as semver from 'semver';
|
import * as semver from 'semver';
|
||||||
|
|
||||||
import * as context from './context';
|
import * as context from './context';
|
||||||
import * as exec from './exec';
|
import * as exec from './exec';
|
||||||
|
|
||||||
@ -18,7 +19,12 @@ export async function getImageID(): Promise<string | undefined> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getSecret(kvp: string): Promise<string> {
|
export async function getSecret(kvp: string): Promise<string> {
|
||||||
const [key, value] = kvp.split('=');
|
const delimiterIndex = kvp.indexOf('=');
|
||||||
|
const key = kvp.substring(0, delimiterIndex);
|
||||||
|
const value = kvp.substring(delimiterIndex + 1);
|
||||||
|
if (key.length == 0 || value.length == 0) {
|
||||||
|
throw new Error(`${kvp} is not a valid secret`);
|
||||||
|
}
|
||||||
const secretFile = context.tmpNameSync({
|
const secretFile = context.tmpNameSync({
|
||||||
tmpdir: context.tmpDir()
|
tmpdir: context.tmpDir()
|
||||||
});
|
});
|
||||||
@ -31,7 +37,7 @@ export function isLocalOrTarExporter(outputs: string[]): Boolean {
|
|||||||
delimiter: ',',
|
delimiter: ',',
|
||||||
trim: true,
|
trim: true,
|
||||||
columns: false,
|
columns: false,
|
||||||
relax_column_count: true
|
relaxColumnCount: true
|
||||||
})) {
|
})) {
|
||||||
// Local if no type is defined
|
// Local if no type is defined
|
||||||
// https://github.com/docker/buildx/blob/d2bf42f8b4784d83fde17acb3ed84703ddc2156b/build/output.go#L29-L43
|
// https://github.com/docker/buildx/blob/d2bf42f8b4784d83fde17acb3ed84703ddc2156b/build/output.go#L29-L43
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
|
import csvparse from 'csv-parse/lib/sync';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as semver from 'semver';
|
import * as semver from 'semver';
|
||||||
import * as tmp from 'tmp';
|
import * as tmp from 'tmp';
|
||||||
import * as buildx from './buildx';
|
|
||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
import * as github from '@actions/github';
|
import * as github from '@actions/github';
|
||||||
|
|
||||||
|
import * as buildx from './buildx';
|
||||||
|
|
||||||
let _defaultContext, _tmpDir: string;
|
let _defaultContext, _tmpDir: string;
|
||||||
|
|
||||||
export interface Inputs {
|
export interface Inputs {
|
||||||
@ -28,6 +31,7 @@ export interface Inputs {
|
|||||||
cacheTo: string[];
|
cacheTo: string[];
|
||||||
secrets: string[];
|
secrets: string[];
|
||||||
githubToken: string;
|
githubToken: string;
|
||||||
|
ssh: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function defaultContext(): string {
|
export function defaultContext(): string {
|
||||||
@ -69,7 +73,8 @@ export async function getInputs(defaultContext: string): Promise<Inputs> {
|
|||||||
cacheFrom: await getInputList('cache-from', true),
|
cacheFrom: await getInputList('cache-from', true),
|
||||||
cacheTo: await getInputList('cache-to', true),
|
cacheTo: await getInputList('cache-to', true),
|
||||||
secrets: await getInputList('secrets', true),
|
secrets: await getInputList('secrets', true),
|
||||||
githubToken: core.getInput('github-token')
|
githubToken: core.getInput('github-token'),
|
||||||
|
ssh: await getInputList('ssh')
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,11 +122,18 @@ async function getBuildArgs(inputs: Inputs, defaultContext: string, buildxVersio
|
|||||||
args.push('--cache-to', cacheTo);
|
args.push('--cache-to', cacheTo);
|
||||||
});
|
});
|
||||||
await asyncForEach(inputs.secrets, async secret => {
|
await asyncForEach(inputs.secrets, async secret => {
|
||||||
args.push('--secret', await buildx.getSecret(secret));
|
try {
|
||||||
|
args.push('--secret', await buildx.getSecret(secret));
|
||||||
|
} catch (err) {
|
||||||
|
core.warning(err.message);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
if (inputs.githubToken && !buildx.hasGitAuthToken(inputs.secrets) && inputs.context == defaultContext) {
|
if (inputs.githubToken && !buildx.hasGitAuthToken(inputs.secrets) && inputs.context == defaultContext) {
|
||||||
args.push('--secret', await buildx.getSecret(`GIT_AUTH_TOKEN=${inputs.githubToken}`));
|
args.push('--secret', await buildx.getSecret(`GIT_AUTH_TOKEN=${inputs.githubToken}`));
|
||||||
}
|
}
|
||||||
|
await asyncForEach(inputs.ssh, async ssh => {
|
||||||
|
args.push('--ssh', ssh);
|
||||||
|
});
|
||||||
if (inputs.file) {
|
if (inputs.file) {
|
||||||
args.push('--file', inputs.file);
|
args.push('--file', inputs.file);
|
||||||
}
|
}
|
||||||
@ -149,17 +161,29 @@ async function getCommonArgs(inputs: Inputs): Promise<Array<string>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getInputList(name: string, ignoreComma?: boolean): Promise<string[]> {
|
export async function getInputList(name: string, ignoreComma?: boolean): Promise<string[]> {
|
||||||
|
let res: Array<string> = [];
|
||||||
|
|
||||||
const items = core.getInput(name);
|
const items = core.getInput(name);
|
||||||
if (items == '') {
|
if (items == '') {
|
||||||
return [];
|
return res;
|
||||||
}
|
}
|
||||||
return items
|
|
||||||
.split(/\r?\n/)
|
for (let output of (await csvparse(items, {
|
||||||
.filter(x => x)
|
columns: false,
|
||||||
.reduce<string[]>(
|
relaxColumnCount: true,
|
||||||
(acc, line) => acc.concat(!ignoreComma ? line.split(',').filter(x => x) : line).map(pat => pat.trim()),
|
skipLinesWithEmptyValues: true
|
||||||
[]
|
})) as Array<string[]>) {
|
||||||
);
|
if (output.length == 1) {
|
||||||
|
res.push(output[0]);
|
||||||
|
continue;
|
||||||
|
} else if (!ignoreComma) {
|
||||||
|
res.push(...output);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
res.push(output.join(','));
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.filter(item => item);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const asyncForEach = async (array, callback) => {
|
export const asyncForEach = async (array, callback) => {
|
||||||
|
7
src/docker.ts
Normal file
7
src/docker.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import * as exec from './exec';
|
||||||
|
|
||||||
|
export async function isDaemonRunning(): Promise<boolean> {
|
||||||
|
return await exec.exec(`docker`, ['version', '--format', '{{.Server.Os}}'], true).then(res => {
|
||||||
|
return !res.stdout.includes(' ') && res.success;
|
||||||
|
});
|
||||||
|
}
|
@ -1236,10 +1236,10 @@ cssstyle@^2.2.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
cssom "~0.3.6"
|
cssom "~0.3.6"
|
||||||
|
|
||||||
csv-parse@*, csv-parse@^4.12.0:
|
csv-parse@*, csv-parse@^4.14.1:
|
||||||
version "4.12.0"
|
version "4.14.1"
|
||||||
resolved "https://registry.yarnpkg.com/csv-parse/-/csv-parse-4.12.0.tgz#fd42d6291bbaadd51d3009f6cadbb3e53b4ce026"
|
resolved "https://registry.yarnpkg.com/csv-parse/-/csv-parse-4.14.1.tgz#b6b3736508fb94682fa6d450fe1755237221d291"
|
||||||
integrity sha512-wPQl3H79vWLPI8cgKFcQXl0NBgYYEqVnT1i6/So7OjMpsI540oD7p93r3w6fDSyPvwkTepG05F69/7AViX2lXg==
|
integrity sha512-4wmcO7QbWtDAncGFaBwlWFPhEN4Akr64IbM4zvDwEOFekI8blLc04Nw7XjQjtSNy+3AUAgBgtUa9nWo5Cq89Xg==
|
||||||
|
|
||||||
dashdash@^1.12.0:
|
dashdash@^1.12.0:
|
||||||
version "1.14.1"
|
version "1.14.1"
|
||||||
|
Reference in New Issue
Block a user