├── .dockerignore ├── .editorconfig ├── .eslintignore ├── .eslintrc.json ├── .gitattributes ├── .github ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── bug.yml │ ├── config.yml │ └── feature.yml ├── SECURITY.md ├── build-push-action.png ├── build-push-summary.png ├── dependabot.yml ├── e2e │ ├── distribution │ │ ├── env │ │ └── install.sh │ ├── harbor │ │ ├── env │ │ └── install.sh │ └── nexus │ │ ├── docker-compose.yml │ │ ├── env │ │ └── install.sh └── workflows │ ├── .e2e-run.yml │ ├── ci.yml │ ├── e2e.yml │ ├── pr-assign-author.yml │ ├── publish.yml │ ├── test.yml │ └── validate.yml ├── .gitignore ├── .prettierignore ├── .prettierrc.json ├── .yarn └── plugins │ └── @yarnpkg │ └── plugin-interactive-tools.cjs ├── .yarnrc.yml ├── LICENSE ├── README.md ├── TROUBLESHOOTING.md ├── __mocks__ └── @actions │ └── github.ts ├── __tests__ ├── context.test.ts └── fixtures │ ├── github-repo.json │ └── secret.txt ├── action.yml ├── codecov.yml ├── dev.Dockerfile ├── dist ├── index.js ├── index.js.map ├── licenses.txt └── sourcemap-register.js ├── docker-bake.hcl ├── jest.config.ts ├── package.json ├── src ├── context.ts ├── main.ts └── state-helper.ts ├── test ├── Dockerfile ├── addhost.Dockerfile ├── cgroup.Dockerfile ├── go │ ├── Dockerfile │ ├── go.mod │ └── main.go ├── lint.Dockerfile ├── multi-sudo.Dockerfile ├── multi.Dockerfile ├── named-context-base.Dockerfile ├── named-context.Dockerfile ├── nocachefilter.Dockerfile ├── proxy.Dockerfile ├── secret.Dockerfile ├── shmsize.Dockerfile └── ulimit.Dockerfile ├── tsconfig.json └── yarn.lock /.dockerignore: -------------------------------------------------------------------------------- 1 | /coverage 2 | 3 | # Dependency directories 4 | node_modules/ 5 | jspm_packages/ 6 | 7 | # yarn v2 8 | .yarn/cache 9 | .yarn/unplugged 10 | .yarn/build-state.yml 11 | .yarn/install-state.gz 12 | .pnp.* 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # This file is for unifying the coding style for different editors and IDEs. 2 | # More information at http://editorconfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | indent_style = space 8 | indent_size = 2 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true 13 | 14 | [*.md] 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /dist/** 2 | /coverage/** 3 | /node_modules/** 4 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true, 4 | "es6": true, 5 | "jest": true 6 | }, 7 | "extends": [ 8 | "eslint:recommended", 9 | "plugin:@typescript-eslint/eslint-recommended", 10 | "plugin:@typescript-eslint/recommended", 11 | "plugin:jest/recommended", 12 | "plugin:prettier/recommended" 13 | ], 14 | "parser": "@typescript-eslint/parser", 15 | "parserOptions": { 16 | "ecmaVersion": 2023, 17 | "sourceType": "module" 18 | }, 19 | "plugins": [ 20 | "@typescript-eslint", 21 | "jest", 22 | "prettier" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | /.yarn/releases/** binary 2 | /.yarn/plugins/** binary 3 | /dist/** linguist-generated=true 4 | /lib/** linguist-generated=true 5 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of conduct 2 | 3 | - [Moby community guidelines](https://github.com/moby/moby/blob/master/CONTRIBUTING.md#moby-community-guidelines) 4 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing 2 | 3 | Hi there! We're thrilled that you'd like to contribute to this project. Your help is essential for keeping it great. 4 | 5 | Contributions to this project are [released](https://docs.github.com/en/github/site-policy/github-terms-of-service#6-contributions-under-repository-license) 6 | to the public under the [project's open source license](LICENSE). 7 | 8 | ## Submitting a pull request 9 | 10 | 1. [Fork](https://github.com/docker/build-push-action/fork) and clone the repository 11 | 2. Configure and install the dependencies: `yarn install` 12 | 3. Create a new branch: `git checkout -b my-branch-name` 13 | 4. Make your changes 14 | 5. Make sure the tests pass: `docker buildx bake test` 15 | 6. Format code and build javascript artifacts: `docker buildx bake pre-checkin` 16 | 7. Validate all code has correctly formatted and built: `docker buildx bake validate` 17 | 8. Push to your fork and [submit a pull request](https://github.com/docker/build-push-action/compare) 18 | 9. Pat your self on the back and wait for your pull request to be reviewed and merged. 19 | 20 | Here are a few things you can do that will increase the likelihood of your pull request being accepted: 21 | 22 | - Make sure the `README.md` and any other relevant **documentation are kept up-to-date**. 23 | - We try to follow [SemVer v2.0.0](https://semver.org/). Randomly breaking public APIs is not an option. 24 | - 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**. 25 | - Write a [good commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html). 26 | 27 | ## Resources 28 | 29 | - [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/) 30 | - [Using Pull Requests](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests) 31 | - [GitHub Help](https://docs.github.com/en) 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.yml: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema 2 | name: Bug Report 3 | description: Report a bug 4 | labels: 5 | - status/triage 6 | 7 | body: 8 | - type: markdown 9 | attributes: 10 | value: | 11 | Thank you for taking the time to report a bug! 12 | If this is a security issue please report it to the [Docker Security team](mailto:security@docker.com). 13 | Before submitting a bug report, check out the [Troubleshooting doc](https://github.com/docker/build-push-action/blob/master/TROUBLESHOOTING.md). 14 | 15 | - type: checkboxes 16 | attributes: 17 | label: Contributing guidelines 18 | description: > 19 | Make sure you've read the contributing guidelines before proceeding. 20 | options: 21 | - label: I've read the [contributing guidelines](https://github.com/docker/build-push-action/blob/master/.github/CONTRIBUTING.md) and wholeheartedly agree 22 | required: true 23 | 24 | - type: checkboxes 25 | attributes: 26 | label: "I've found a bug, and:" 27 | description: | 28 | Make sure that your request fulfills all of the following requirements. 29 | If one requirement cannot be satisfied, explain in detail why. 30 | options: 31 | - label: The documentation does not mention anything about my problem 32 | - label: There are no open or closed issues that are related to my problem 33 | 34 | - type: textarea 35 | attributes: 36 | label: Description 37 | description: > 38 | Provide a brief description of the bug in 1-2 sentences. 39 | validations: 40 | required: true 41 | 42 | - type: textarea 43 | attributes: 44 | label: Expected behaviour 45 | description: > 46 | Describe precisely what you'd expect to happen. 47 | validations: 48 | required: true 49 | 50 | - type: textarea 51 | attributes: 52 | label: Actual behaviour 53 | description: > 54 | Describe precisely what is actually happening. 55 | validations: 56 | required: true 57 | 58 | - type: input 59 | attributes: 60 | label: Repository URL 61 | description: > 62 | Enter the URL of the repository where you are experiencing the 63 | issue. If your repository is private, provide a link to a minimal 64 | repository that reproduces the issue. 65 | 66 | - type: input 67 | attributes: 68 | label: Workflow run URL 69 | description: > 70 | Enter the URL of the GitHub Action workflow run, if public. 71 | 72 | - type: textarea 73 | attributes: 74 | label: YAML workflow 75 | description: | 76 | Provide the YAML of the workflow that's causing the issue. 77 | Make sure to remove any sensitive information. 78 | render: yaml 79 | validations: 80 | required: true 81 | 82 | - type: textarea 83 | attributes: 84 | label: Workflow logs 85 | description: > 86 | [Attach](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/attaching-files) 87 | the [log file of your workflow run](https://docs.github.com/en/actions/managing-workflow-runs/using-workflow-run-logs#downloading-logs) 88 | and make sure to remove any sensitive information. 89 | 90 | - type: textarea 91 | attributes: 92 | label: BuildKit logs 93 | description: > 94 | If applicable, provide the [BuildKit container logs](https://docs.docker.com/build/ci/github-actions/configure-builder/#buildkit-container-logs) 95 | render: text 96 | 97 | - type: textarea 98 | attributes: 99 | label: Additional info 100 | description: | 101 | Provide any additional information that could be useful. 102 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository#configuring-the-template-chooser 2 | blank_issues_enabled: true 3 | contact_links: 4 | - name: Questions and Discussions 5 | url: https://github.com/docker/build-push-action/discussions/new 6 | about: Use Github Discussions to ask questions and/or open discussion topics. 7 | - name: Documentation 8 | url: https://docs.docker.com/build/ci/github-actions/ 9 | about: Read the documentation. 10 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature.yml: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema 2 | name: Feature request 3 | description: Missing functionality? Come tell us about it! 4 | labels: 5 | - kind/enhancement 6 | - status/triage 7 | 8 | body: 9 | - type: textarea 10 | id: description 11 | attributes: 12 | label: Description 13 | description: What is the feature you want to see? 14 | validations: 15 | required: true 16 | -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Reporting security issues 2 | 3 | The project maintainers take security seriously. If you discover a security 4 | issue, please bring it to their attention right away! 5 | 6 | **Please _DO NOT_ file a public issue**, instead send your report privately to 7 | [security@docker.com](mailto:security@docker.com). 8 | 9 | Security reports are greatly appreciated, and we will publicly thank you for it. 10 | We also like to send gifts—if you'd like Docker swag, make sure to let 11 | us know. We currently do not offer a paid security bounty program, but are not 12 | ruling it out in the future. 13 | -------------------------------------------------------------------------------- /.github/build-push-action.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/build-push-action/263435318d21b8e681c14492fe198d362a7d2c83/.github/build-push-action.png -------------------------------------------------------------------------------- /.github/build-push-summary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/build-push-action/263435318d21b8e681c14492fe198d362a7d2c83/.github/build-push-summary.png -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | labels: 8 | - "dependencies" 9 | - "bot" 10 | - package-ecosystem: "npm" 11 | directory: "/" 12 | schedule: 13 | interval: "daily" 14 | versioning-strategy: "increase" 15 | allow: 16 | - dependency-type: "production" 17 | labels: 18 | - "dependencies" 19 | - "bot" 20 | -------------------------------------------------------------------------------- /.github/e2e/distribution/env: -------------------------------------------------------------------------------- 1 | REGISTRY_FQDN=localhost:8080 2 | REGISTRY_SLUG=localhost:8080/test-docker-action 3 | 4 | DISTRIBUTION_HOST=localhost 5 | DISTRIBUTION_PORT=8080 -------------------------------------------------------------------------------- /.github/e2e/distribution/install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eu 3 | 4 | : "${DISTRIBUTION_VERSION:=2}" 5 | : "${DISTRIBUTION_HOST:=localhost}" 6 | : "${DISTRIBUTION_PORT:=8080}" 7 | 8 | echo "::group::Starting registry:${DISTRIBUTION_VERSION}" 9 | ( 10 | set -x 11 | docker run -d --name registry -p "${DISTRIBUTION_PORT}:5000" "registry:${DISTRIBUTION_VERSION}" 12 | ) 13 | echo "::endgroup::" -------------------------------------------------------------------------------- /.github/e2e/harbor/env: -------------------------------------------------------------------------------- 1 | REGISTRY_FQDN=localhost:8081 2 | REGISTRY_USER=admin 3 | REGISTRY_PASSWORD=Harbor12345 4 | REGISTRY_SLUG=localhost:8081/test-docker-action/test-docker-action 5 | 6 | HARBOR_HOST=localhost 7 | HARBOR_PORT=8081 8 | HARBOR_PROJECT=test-docker-action 9 | -------------------------------------------------------------------------------- /.github/e2e/harbor/install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eu 3 | 4 | : "${HARBOR_VERSION:=v2.7.0}" 5 | : "${HARBOR_HOST:=localhost}" 6 | : "${HARBOR_PORT:=49154}" 7 | : "${REGISTRY_USER:=admin}" 8 | : "${REGISTRY_PASSWORD:=Harbor12345}" 9 | 10 | : "${HARBOR_PROJECT:=test-docker-action}" 11 | 12 | project_post_data() { 13 | cat </dev/null 41 | yq --no-colors harbor.yml 42 | ) 43 | echo "::endgroup::" 44 | 45 | # install and start 46 | echo "::group::Installing Harbor" 47 | ( 48 | cd /tmp/harbor 49 | set -x 50 | ./install.sh 51 | sleep 10 52 | netstat -aptn 53 | ) 54 | echo "::endgroup::" 55 | 56 | # compose config 57 | echo "::group::Compose config" 58 | ( 59 | cd /tmp/harbor 60 | set -x 61 | docker compose config 62 | ) 63 | echo "::endgroup::" 64 | 65 | # create project 66 | echo "::group::Creating project" 67 | ( 68 | set -x 69 | curl --fail -v -k --max-time 10 -u "$REGISTRY_USER:$REGISTRY_PASSWORD" -X POST -H "Content-Type: application/json" -d "$(project_post_data)" "http://$HARBOR_HOST:$HARBOR_PORT/api/v2.0/projects" 70 | ) 71 | echo "::endgroup::" 72 | 73 | # list projects 74 | echo "::group::List projects" 75 | ( 76 | set -x 77 | curl --fail -s -k --max-time 10 -u "$REGISTRY_USER:$REGISTRY_PASSWORD" -H "Content-Type: application/json" "http://$HARBOR_HOST:$HARBOR_PORT/api/v2.0/projects" | jq 78 | ) 79 | echo "::endgroup::" 80 | -------------------------------------------------------------------------------- /.github/e2e/nexus/docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | nexus: 3 | image: sonatype/nexus3:${NEXUS_VERSION:-latest} 4 | volumes: 5 | - "./data:/nexus-data" 6 | ports: 7 | - "8081:8081" 8 | - "8082:8082" 9 | -------------------------------------------------------------------------------- /.github/e2e/nexus/env: -------------------------------------------------------------------------------- 1 | REGISTRY_FQDN=localhost:8082 2 | REGISTRY_USER=admin 3 | REGISTRY_PASSWORD=Nexus12345 4 | REGISTRY_SLUG=localhost:8082/test-docker-action 5 | 6 | NEXUS_HOST=localhost 7 | NEXUS_PORT=8081 8 | NEXUS_REGISTRY_PORT=8082 9 | NEXUS_REPO=test-docker-action 10 | -------------------------------------------------------------------------------- /.github/e2e/nexus/install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eu 3 | 4 | SCRIPT_DIR=$(cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd) 5 | 6 | : "${NEXUS_VERSION:=3.47.1}" 7 | : "${NEXUS_HOST:=localhost}" 8 | : "${NEXUS_PORT:=8081}" 9 | : "${NEXUS_REGISTRY_PORT:=8082}" 10 | : "${REGISTRY_USER:=admin}" 11 | : "${REGISTRY_PASSWORD:=Nexus12345}" 12 | 13 | : "${NEXUS_REPO:=test-docker-action}" 14 | 15 | createrepo_post_data() { 16 | cat <> $GITHUB_ENV 59 | - 60 | name: Set up BuildKit config 61 | run: | 62 | touch /tmp/buildkitd.toml 63 | if [ "${{ inputs.type }}" = "local" ]; then 64 | echo -e "[registry.\"${{ env.REGISTRY_FQDN }}\"]\nhttp = true\ninsecure = true" > /tmp/buildkitd.toml 65 | fi 66 | - 67 | name: Set up Docker daemon 68 | if: inputs.type == 'local' 69 | run: | 70 | if [ ! -e /etc/docker/daemon.json ]; then 71 | echo '{}' | sudo tee /etc/docker/daemon.json >/dev/null 72 | fi 73 | DOCKERD_CONFIG=$(jq '.+{"insecure-registries":["http://${{ env.REGISTRY_FQDN }}"]}' /etc/docker/daemon.json) 74 | sudo tee /etc/docker/daemon.json <<<"$DOCKERD_CONFIG" >/dev/null 75 | cat /etc/docker/daemon.json 76 | sudo service docker restart 77 | - 78 | name: Install ${{ inputs.name }} 79 | if: inputs.type == 'local' 80 | run: | 81 | sudo -E bash ./.github/e2e/${{ inputs.id }}/install.sh 82 | sudo chown $(id -u):$(id -g) -R ~/.docker 83 | - 84 | name: Docker meta 85 | id: meta 86 | uses: docker/metadata-action@v5 87 | with: 88 | images: ${{ env.REGISTRY_SLUG || inputs.slug }} 89 | tags: | 90 | type=ref,event=branch,enable=${{ matrix.buildx_version == 'latest' && matrix.buildkit_image == 'moby/buildkit:buildx-stable-1' }} 91 | type=ref,event=tag,enable=${{ matrix.buildx_version == 'latest' && matrix.buildkit_image == 'moby/buildkit:buildx-stable-1' }} 92 | type=raw,gh-runid-${{ github.run_id }} 93 | - 94 | name: Set up QEMU 95 | uses: docker/setup-qemu-action@v3 96 | - 97 | name: Set up Docker Buildx 98 | uses: docker/setup-buildx-action@v3 99 | with: 100 | version: ${{ matrix.buildx_version }} 101 | buildkitd-config: /tmp/buildkitd.toml 102 | buildkitd-flags: --debug --allow-insecure-entitlement security.insecure --allow-insecure-entitlement network.host 103 | driver-opts: | 104 | image=${{ matrix.buildkit_image }} 105 | network=host 106 | - 107 | name: Login to Registry 108 | if: github.event_name != 'pull_request' && (env.REGISTRY_USER || inputs.username_secret) != '' 109 | uses: docker/login-action@v3 110 | with: 111 | registry: ${{ env.REGISTRY_FQDN || inputs.registry }} 112 | username: ${{ env.REGISTRY_USER || secrets[inputs.username_secret] }} 113 | password: ${{ env.REGISTRY_PASSWORD || secrets[inputs.password_secret] }} 114 | - 115 | name: Build and push 116 | uses: ./ 117 | with: 118 | context: ./test 119 | file: ./test/multi.Dockerfile 120 | platforms: linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/ppc64le,linux/s390x 121 | push: ${{ github.event_name != 'pull_request' }} 122 | tags: ${{ steps.meta.outputs.tags }} 123 | labels: ${{ steps.meta.outputs.labels }} 124 | cache-from: type=registry,ref=${{ env.REGISTRY_SLUG || inputs.slug }}:master 125 | cache-to: type=inline 126 | - 127 | name: Inspect image 128 | run: | 129 | docker pull ${{ env.REGISTRY_SLUG || inputs.slug }}:${{ steps.meta.outputs.version }} 130 | docker image inspect ${{ env.REGISTRY_SLUG || inputs.slug }}:${{ steps.meta.outputs.version }} 131 | - 132 | name: Check manifest 133 | run: | 134 | docker buildx imagetools inspect ${{ env.REGISTRY_SLUG || inputs.slug }}:${{ steps.meta.outputs.version }} --format '{{json .}}' 135 | -------------------------------------------------------------------------------- /.github/workflows/e2e.yml: -------------------------------------------------------------------------------- 1 | name: e2e 2 | 3 | concurrency: 4 | group: ${{ github.workflow }}-${{ github.ref }} 5 | cancel-in-progress: true 6 | 7 | on: 8 | workflow_dispatch: 9 | schedule: 10 | - cron: '0 10 * * *' 11 | push: 12 | branches: 13 | - 'master' 14 | tags: 15 | - 'v*' 16 | 17 | jobs: 18 | build: 19 | uses: ./.github/workflows/.e2e-run.yml 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | include: 24 | - 25 | name: Distribution 26 | id: distribution 27 | type: local 28 | - 29 | name: Docker Hub 30 | registry: '' 31 | slug: ghactionstest/ghactionstest 32 | username_secret: DOCKERHUB_USERNAME 33 | password_secret: DOCKERHUB_TOKEN 34 | type: remote 35 | - 36 | name: GitHub 37 | registry: ghcr.io 38 | slug: ghcr.io/docker-ghactiontest/test 39 | username_secret: GHCR_USERNAME 40 | password_secret: GHCR_PAT 41 | type: remote 42 | - 43 | name: GitLab 44 | registry: registry.gitlab.com 45 | slug: registry.gitlab.com/test1716/test 46 | username_secret: GITLAB_USERNAME 47 | password_secret: GITLAB_TOKEN 48 | type: remote 49 | - 50 | name: AWS ECR 51 | registry: 175142243308.dkr.ecr.us-east-2.amazonaws.com 52 | slug: 175142243308.dkr.ecr.us-east-2.amazonaws.com/sandbox/test-docker-action 53 | username_secret: AWS_ACCESS_KEY_ID 54 | password_secret: AWS_SECRET_ACCESS_KEY 55 | type: remote 56 | - 57 | name: AWS ECR Public 58 | registry: public.ecr.aws 59 | slug: public.ecr.aws/q3b5f1u4/test-docker-action 60 | username_secret: AWS_ACCESS_KEY_ID 61 | password_secret: AWS_SECRET_ACCESS_KEY 62 | type: remote 63 | - 64 | name: Google Artifact Registry 65 | registry: us-east4-docker.pkg.dev 66 | slug: us-east4-docker.pkg.dev/sandbox-298914/docker-official-github-actions/test-docker-action 67 | username_secret: GAR_USERNAME 68 | password_secret: GAR_JSON_KEY 69 | type: remote 70 | - 71 | name: Azure Container Registry 72 | registry: officialgithubactions.azurecr.io 73 | slug: officialgithubactions.azurecr.io/test-docker-action 74 | username_secret: AZURE_CLIENT_ID 75 | password_secret: AZURE_CLIENT_SECRET 76 | type: remote 77 | - 78 | name: Quay 79 | registry: quay.io 80 | slug: quay.io/docker_build_team/ghactiontest 81 | username_secret: QUAY_USERNAME 82 | password_secret: QUAY_TOKEN 83 | type: remote 84 | - 85 | name: Artifactory 86 | registry: infradock.jfrog.io 87 | slug: infradock.jfrog.io/test-ghaction/build-push-action 88 | username_secret: ARTIFACTORY_USERNAME 89 | password_secret: ARTIFACTORY_TOKEN 90 | type: remote 91 | - 92 | name: Harbor 93 | id: harbor 94 | type: local 95 | - 96 | name: Nexus 97 | id: nexus 98 | type: local 99 | with: 100 | id: ${{ matrix.id }} 101 | type: ${{ matrix.type }} 102 | name: ${{ matrix.name }} 103 | registry: ${{ matrix.registry }} 104 | slug: ${{ matrix.slug }} 105 | username_secret: ${{ matrix.username_secret }} 106 | password_secret: ${{ matrix.password_secret }} 107 | secrets: inherit 108 | -------------------------------------------------------------------------------- /.github/workflows/pr-assign-author.yml: -------------------------------------------------------------------------------- 1 | name: pr-assign-author 2 | 3 | permissions: 4 | contents: read 5 | 6 | on: 7 | pull_request_target: 8 | types: 9 | - opened 10 | - reopened 11 | 12 | jobs: 13 | run: 14 | uses: crazy-max/.github/.github/workflows/pr-assign-author.yml@1b673f36fad86812f538c1df9794904038a23cbf 15 | permissions: 16 | contents: read 17 | pull-requests: write 18 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: publish 2 | 3 | on: 4 | release: 5 | types: 6 | - published 7 | 8 | jobs: 9 | publish: 10 | runs-on: ubuntu-latest 11 | permissions: 12 | contents: read 13 | id-token: write 14 | packages: write 15 | steps: 16 | - 17 | name: Checkout 18 | uses: actions/checkout@v4 19 | - 20 | name: Publish 21 | uses: actions/publish-immutable-action@v0.0.4 22 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | concurrency: 4 | group: ${{ github.workflow }}-${{ github.ref }} 5 | cancel-in-progress: true 6 | 7 | on: 8 | push: 9 | branches: 10 | - 'master' 11 | - 'releases/v*' 12 | pull_request: 13 | 14 | jobs: 15 | test: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - 19 | name: Checkout 20 | uses: actions/checkout@v4 21 | - 22 | name: Test 23 | uses: docker/bake-action@v6 24 | with: 25 | source: . 26 | targets: test 27 | - 28 | name: Upload coverage 29 | uses: codecov/codecov-action@v5 30 | with: 31 | files: ./coverage/clover.xml 32 | token: ${{ secrets.CODECOV_TOKEN }} 33 | -------------------------------------------------------------------------------- /.github/workflows/validate.yml: -------------------------------------------------------------------------------- 1 | name: validate 2 | 3 | concurrency: 4 | group: ${{ github.workflow }}-${{ github.ref }} 5 | cancel-in-progress: true 6 | 7 | on: 8 | push: 9 | branches: 10 | - 'master' 11 | - 'releases/v*' 12 | pull_request: 13 | 14 | jobs: 15 | prepare: 16 | runs-on: ubuntu-latest 17 | outputs: 18 | targets: ${{ steps.generate.outputs.targets }} 19 | steps: 20 | - 21 | name: Checkout 22 | uses: actions/checkout@v4 23 | - 24 | name: List targets 25 | id: generate 26 | uses: docker/bake-action/subaction/list-targets@v6 27 | with: 28 | target: validate 29 | 30 | validate: 31 | runs-on: ubuntu-latest 32 | needs: 33 | - prepare 34 | strategy: 35 | fail-fast: false 36 | matrix: 37 | target: ${{ fromJson(needs.prepare.outputs.targets) }} 38 | steps: 39 | - 40 | name: Validate 41 | uses: docker/bake-action@v6 42 | with: 43 | targets: ${{ matrix.target }} 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore 2 | 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | lerna-debug.log* 10 | .pnpm-debug.log* 11 | 12 | # Diagnostic reports (https://nodejs.org/api/report.html) 13 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 14 | 15 | # Runtime data 16 | pids 17 | *.pid 18 | *.seed 19 | *.pid.lock 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # Dependency directories 26 | node_modules/ 27 | jspm_packages/ 28 | 29 | # TypeScript cache 30 | *.tsbuildinfo 31 | 32 | # Optional npm cache directory 33 | .npm 34 | 35 | # Optional eslint cache 36 | .eslintcache 37 | 38 | # Yarn Integrity file 39 | .yarn-integrity 40 | 41 | # dotenv environment variable files 42 | .env 43 | .env.development.local 44 | .env.test.local 45 | .env.production.local 46 | .env.local 47 | 48 | # yarn v2 49 | .yarn/cache 50 | .yarn/unplugged 51 | .yarn/build-state.yml 52 | .yarn/install-state.gz 53 | .pnp.* 54 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Dependency directories 2 | node_modules/ 3 | jspm_packages/ 4 | 5 | # yarn v2 6 | .yarn/ 7 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 240, 3 | "tabWidth": 2, 4 | "useTabs": false, 5 | "semi": true, 6 | "singleQuote": true, 7 | "trailingComma": "none", 8 | "bracketSpacing": false, 9 | "arrowParens": "avoid", 10 | "parser": "typescript" 11 | } 12 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | logFilters: 2 | - code: YN0013 3 | level: discard 4 | - code: YN0019 5 | level: discard 6 | - code: YN0076 7 | level: discard 8 | 9 | nodeLinker: node-modules 10 | 11 | plugins: 12 | - path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs 13 | spec: "@yarnpkg/plugin-interactive-tools" 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | https://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | Copyright 2013-2018 Docker, Inc. 180 | 181 | Licensed under the Apache License, Version 2.0 (the "License"); 182 | you may not use this file except in compliance with the License. 183 | You may obtain a copy of the License at 184 | 185 | https://www.apache.org/licenses/LICENSE-2.0 186 | 187 | Unless required by applicable law or agreed to in writing, software 188 | distributed under the License is distributed on an "AS IS" BASIS, 189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 190 | See the License for the specific language governing permissions and 191 | limitations under the License. 192 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![GitHub release](https://img.shields.io/github/release/docker/build-push-action.svg?style=flat-square)](https://github.com/docker/build-push-action/releases/latest) 2 | [![GitHub marketplace](https://img.shields.io/badge/marketplace-build--and--push--docker--images-blue?logo=github&style=flat-square)](https://github.com/marketplace/actions/build-and-push-docker-images) 3 | [![CI workflow](https://img.shields.io/github/actions/workflow/status/docker/build-push-action/ci.yml?branch=master&label=ci&logo=github&style=flat-square)](https://github.com/docker/build-push-action/actions?workflow=ci) 4 | [![Test workflow](https://img.shields.io/github/actions/workflow/status/docker/build-push-action/test.yml?branch=master&label=test&logo=github&style=flat-square)](https://github.com/docker/build-push-action/actions?workflow=test) 5 | [![Codecov](https://img.shields.io/codecov/c/github/docker/build-push-action?logo=codecov&style=flat-square)](https://codecov.io/gh/docker/build-push-action) 6 | 7 | ## About 8 | 9 | GitHub Action to build and push Docker images with [Buildx](https://github.com/docker/buildx) 10 | with full support of the features provided by [Moby BuildKit](https://github.com/moby/buildkit) 11 | builder toolkit. This includes multi-platform build, secrets, remote cache, etc. 12 | and different builder deployment/namespacing options. 13 | 14 | ![Screenshot](.github/build-push-action.png) 15 | 16 | ___ 17 | 18 | * [Usage](#usage) 19 | * [Git context](#git-context) 20 | * [Path context](#path-context) 21 | * [Examples](#examples) 22 | * [Summaries](#summaries) 23 | * [Customizing](#customizing) 24 | * [inputs](#inputs) 25 | * [outputs](#outputs) 26 | * [environment variables](#environment-variables) 27 | * [Troubleshooting](#troubleshooting) 28 | * [Contributing](#contributing) 29 | 30 | ## Usage 31 | 32 | In the examples below we are also using 3 other actions: 33 | 34 | * [`setup-buildx`](https://github.com/docker/setup-buildx-action) action will 35 | create and boot a builder using by default the [`docker-container` driver](https://docs.docker.com/build/building/drivers/docker-container/). 36 | This is **not required but recommended** using it to be able to build 37 | multi-platform images, export cache, etc. 38 | * [`setup-qemu`](https://github.com/docker/setup-qemu-action) action can be 39 | useful if you want to add emulation support with QEMU to be able to build 40 | against more platforms. 41 | * [`login`](https://github.com/docker/login-action) action will take care to 42 | log in against a Docker registry. 43 | 44 | ### Git context 45 | 46 | By default, this action uses the [Git context](https://docs.docker.com/engine/reference/commandline/build/#git-repositories), 47 | so you don't need to use the [`actions/checkout`](https://github.com/actions/checkout/) 48 | action to check out the repository as this will be done directly by [BuildKit](https://github.com/moby/buildkit). 49 | 50 | The git reference will be based on the [event that triggered your workflow](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows) 51 | and will result in the following context: `https://github.com//.git#`. 52 | 53 | ```yaml 54 | name: ci 55 | 56 | on: 57 | push: 58 | 59 | jobs: 60 | docker: 61 | runs-on: ubuntu-latest 62 | steps: 63 | - 64 | name: Login to Docker Hub 65 | uses: docker/login-action@v3 66 | with: 67 | username: ${{ vars.DOCKERHUB_USERNAME }} 68 | password: ${{ secrets.DOCKERHUB_TOKEN }} 69 | - 70 | name: Set up QEMU 71 | uses: docker/setup-qemu-action@v3 72 | - 73 | name: Set up Docker Buildx 74 | uses: docker/setup-buildx-action@v3 75 | - 76 | name: Build and push 77 | uses: docker/build-push-action@v6 78 | with: 79 | push: true 80 | tags: user/app:latest 81 | ``` 82 | 83 | Be careful because **any file mutation in the steps that precede the build step 84 | will be ignored, including processing of the `.dockerignore` file** since 85 | the context is based on the Git reference. However, you can use the 86 | [Path context](#path-context) using the [`context` input](#inputs) alongside 87 | the [`actions/checkout`](https://github.com/actions/checkout/) action to remove 88 | this restriction. 89 | 90 | Default Git context can also be provided using the [Handlebars template](https://handlebarsjs.com/guide/) 91 | expression `{{defaultContext}}`. Here we can use it to provide a subdirectory 92 | to the default Git context: 93 | 94 | ```yaml 95 | - 96 | name: Build and push 97 | uses: docker/build-push-action@v6 98 | with: 99 | context: "{{defaultContext}}:mysubdir" 100 | push: true 101 | tags: user/app:latest 102 | ``` 103 | 104 | Building from the current repository automatically uses the [GitHub Token](https://docs.github.com/en/actions/security-guides/automatic-token-authentication), 105 | so it does not need to be passed. If you want to authenticate against another 106 | private repository, you have to use a [secret](https://docs.docker.com/build/ci/github-actions/secrets) 107 | named `GIT_AUTH_TOKEN` to be able to authenticate against it with Buildx: 108 | 109 | ```yaml 110 | - 111 | name: Build and push 112 | uses: docker/build-push-action@v6 113 | with: 114 | push: true 115 | tags: user/app:latest 116 | secrets: | 117 | GIT_AUTH_TOKEN=${{ secrets.MYTOKEN }} 118 | ``` 119 | 120 | ### Path context 121 | 122 | ```yaml 123 | name: ci 124 | 125 | on: 126 | push: 127 | 128 | jobs: 129 | docker: 130 | runs-on: ubuntu-latest 131 | steps: 132 | - 133 | name: Checkout 134 | uses: actions/checkout@v4 135 | - 136 | name: Login to Docker Hub 137 | uses: docker/login-action@v3 138 | with: 139 | username: ${{ vars.DOCKERHUB_USERNAME }} 140 | password: ${{ secrets.DOCKERHUB_TOKEN }} 141 | - 142 | name: Set up QEMU 143 | uses: docker/setup-qemu-action@v3 144 | - 145 | name: Set up Docker Buildx 146 | uses: docker/setup-buildx-action@v3 147 | - 148 | name: Build and push 149 | uses: docker/build-push-action@v6 150 | with: 151 | context: . 152 | push: true 153 | tags: user/app:latest 154 | ``` 155 | 156 | ## Examples 157 | 158 | * [Multi-platform image](https://docs.docker.com/build/ci/github-actions/multi-platform/) 159 | * [Secrets](https://docs.docker.com/build/ci/github-actions/secrets/) 160 | * [Push to multi-registries](https://docs.docker.com/build/ci/github-actions/push-multi-registries/) 161 | * [Manage tags and labels](https://docs.docker.com/build/ci/github-actions/manage-tags-labels/) 162 | * [Cache management](https://docs.docker.com/build/ci/github-actions/cache/) 163 | * [Export to Docker](https://docs.docker.com/build/ci/github-actions/export-docker/) 164 | * [Test before push](https://docs.docker.com/build/ci/github-actions/test-before-push/) 165 | * [Validating build configuration](https://docs.docker.com/build/ci/github-actions/checks/) 166 | * [Local registry](https://docs.docker.com/build/ci/github-actions/local-registry/) 167 | * [Share built image between jobs](https://docs.docker.com/build/ci/github-actions/share-image-jobs/) 168 | * [Named contexts](https://docs.docker.com/build/ci/github-actions/named-contexts/) 169 | * [Copy image between registries](https://docs.docker.com/build/ci/github-actions/copy-image-registries/) 170 | * [Update Docker Hub repo description](https://docs.docker.com/build/ci/github-actions/update-dockerhub-desc/) 171 | * [SBOM and provenance attestations](https://docs.docker.com/build/ci/github-actions/attestations/) 172 | * [Annotations](https://docs.docker.com/build/ci/github-actions/annotations/) 173 | * [Reproducible builds](https://docs.docker.com/build/ci/github-actions/reproducible-builds/) 174 | 175 | ## Summaries 176 | 177 | This action generates a [job summary](https://github.blog/2022-05-09-supercharging-github-actions-with-job-summaries/) 178 | that provides a detailed overview of the build execution. The summary shows an 179 | overview of all the steps executed during the build, including the build inputs 180 | and eventual errors. 181 | 182 | ![build-push-action job summary](./.github/build-push-summary.png) 183 | 184 | The summary also includes a link for downloading the build record with 185 | additional details about the build, including build stats, logs, outputs, and 186 | more. The build record can be imported to Docker Desktop for inspecting the 187 | build in greater detail. 188 | 189 | > [!WARNING] 190 | > 191 | > If you're using the [`actions/download-artifact`](https://github.com/actions/download-artifact) 192 | > action in your workflow, you need to ignore the build record artifacts 193 | > if `name` and `pattern` inputs are not specified ([defaults to download all artifacts](https://github.com/actions/download-artifact?tab=readme-ov-file#download-all-artifacts) of the workflow), 194 | > otherwise the action will fail: 195 | > ```yaml 196 | > - uses: actions/download-artifact@v4 197 | > with: 198 | > pattern: "!*.dockerbuild" 199 | > ``` 200 | > More info: https://github.com/actions/toolkit/pull/1874 201 | 202 | Summaries are enabled by default, but can be disabled with the 203 | `DOCKER_BUILD_SUMMARY` [environment variable](#environment-variables). 204 | 205 | For more information about summaries, refer to the 206 | [documentation](https://docs.docker.com/go/build-summary/). 207 | 208 | ## Customizing 209 | 210 | ### inputs 211 | 212 | The following inputs can be used as `step.with` keys: 213 | 214 | > `List` type is a newline-delimited string 215 | > ```yaml 216 | > cache-from: | 217 | > user/app:cache 218 | > type=local,src=path/to/dir 219 | > ``` 220 | 221 | > `CSV` type is a comma-delimited string 222 | > ```yaml 223 | > tags: name/app:latest,name/app:1.0.0 224 | > ``` 225 | 226 | | Name | Type | Description | 227 | |--------------------|-------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 228 | | `add-hosts` | List/CSV | List of [customs host-to-IP mapping](https://docs.docker.com/engine/reference/commandline/build/#add-entries-to-container-hosts-file---add-host) (e.g., `docker:10.180.0.1`) | 229 | | `allow` | List/CSV | List of [extra privileged entitlement](https://docs.docker.com/engine/reference/commandline/buildx_build/#allow) (e.g., `network.host,security.insecure`) | 230 | | `annotations` | List | List of annotation to set to the image | 231 | | `attests` | List | List of [attestation](https://docs.docker.com/build/attestations/) parameters (e.g., `type=sbom,generator=image`) | 232 | | `builder` | String | Builder instance (see [setup-buildx](https://github.com/docker/setup-buildx-action) action) | 233 | | `build-args` | List | List of [build-time variables](https://docs.docker.com/engine/reference/commandline/buildx_build/#build-arg) | 234 | | `build-contexts` | List | List of additional [build contexts](https://docs.docker.com/engine/reference/commandline/buildx_build/#build-context) (e.g., `name=path`) | 235 | | `cache-from` | List | List of [external cache sources](https://docs.docker.com/engine/reference/commandline/buildx_build/#cache-from) (e.g., `type=local,src=path/to/dir`) | 236 | | `cache-to` | List | List of [cache export destinations](https://docs.docker.com/engine/reference/commandline/buildx_build/#cache-to) (e.g., `type=local,dest=path/to/dir`) | 237 | | `call` | String | Set [method for evaluating build](https://docs.docker.com/reference/cli/docker/buildx/build/#call) (e.g., `check`) | 238 | | `cgroup-parent` | String | Optional [parent cgroup](https://docs.docker.com/engine/reference/commandline/build/#use-a-custom-parent-cgroup---cgroup-parent) for the container used in the build | 239 | | `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)) | 240 | | `file` | String | Path to the Dockerfile. (default `{context}/Dockerfile`) | 241 | | `labels` | List | List of metadata for an image | 242 | | `load` | Bool | [Load](https://docs.docker.com/engine/reference/commandline/buildx_build/#load) is a shorthand for `--output=type=docker` (default `false`) | 243 | | `network` | String | Set the networking mode for the `RUN` instructions during build | 244 | | `no-cache` | Bool | Do not use cache when building the image (default `false`) | 245 | | `no-cache-filters` | List/CSV | Do not cache specified stages | 246 | | `outputs` | List | List of [output destinations](https://docs.docker.com/engine/reference/commandline/buildx_build/#output) (format: `type=local,dest=path`) | 247 | | `platforms` | List/CSV | List of [target platforms](https://docs.docker.com/engine/reference/commandline/buildx_build/#platform) for build | 248 | | `provenance` | Bool/String | Generate [provenance](https://docs.docker.com/build/attestations/slsa-provenance/) attestation for the build (shorthand for `--attest=type=provenance`) | 249 | | `pull` | Bool | Always attempt to pull all referenced images (default `false`) | 250 | | `push` | Bool | [Push](https://docs.docker.com/engine/reference/commandline/buildx_build/#push) is a shorthand for `--output=type=registry` (default `false`) | 251 | | `sbom` | Bool/String | Generate [SBOM](https://docs.docker.com/build/attestations/sbom/) attestation for the build (shorthand for `--attest=type=sbom`) | 252 | | `secrets` | List | List of [secrets](https://docs.docker.com/engine/reference/commandline/buildx_build/#secret) to expose to the build (e.g., `key=string`, `GIT_AUTH_TOKEN=mytoken`) | 253 | | `secret-envs` | List/CSV | List of [secret env vars](https://docs.docker.com/engine/reference/commandline/buildx_build/#secret) to expose to the build (e.g., `key=envname`, `MY_SECRET=MY_ENV_VAR`) | 254 | | `secret-files` | List | List of [secret files](https://docs.docker.com/engine/reference/commandline/buildx_build/#secret) to expose to the build (e.g., `key=filename`, `MY_SECRET=./secret.txt`) | 255 | | `shm-size` | String | Size of [`/dev/shm`](https://docs.docker.com/engine/reference/commandline/buildx_build/#shm-size) (e.g., `2g`) | 256 | | `ssh` | List | List of [SSH agent socket or keys](https://docs.docker.com/engine/reference/commandline/buildx_build/#ssh) to expose to the build | 257 | | `tags` | List/CSV | List of tags | 258 | | `target` | String | Sets the target stage to build | 259 | | `ulimit` | List | [Ulimit](https://docs.docker.com/engine/reference/commandline/buildx_build/#ulimit) options (e.g., `nofile=1024:1024`) | 260 | | `github-token` | String | GitHub Token used to authenticate against a repository for [Git context](#git-context) (default `${{ github.token }}`) | 261 | 262 | ### outputs 263 | 264 | The following outputs are available: 265 | 266 | | Name | Type | Description | 267 | |------------|---------|-----------------------| 268 | | `imageid` | String | Image ID | 269 | | `digest` | String | Image digest | 270 | | `metadata` | JSON | Build result metadata | 271 | 272 | ### environment variables 273 | 274 | | Name | Type | Default | Description | 275 | |--------------------------------------|--------|---------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 276 | | `DOCKER_BUILD_CHECKS_ANNOTATIONS` | Bool | `true` | If `false`, GitHub annotations are not generated for [build checks](https://docs.docker.com/build/checks/) | 277 | | `DOCKER_BUILD_SUMMARY` | Bool | `true` | If `false`, [build summary](https://docs.docker.com/build/ci/github-actions/build-summary/) generation is disabled | 278 | | `DOCKER_BUILD_RECORD_UPLOAD` | Bool | `true` | If `false`, build record upload as [GitHub artifact](https://docs.github.com/en/actions/using-workflows/storing-workflow-data-as-artifacts) is disabled | 279 | | `DOCKER_BUILD_RECORD_RETENTION_DAYS` | Number | | Duration after which build record artifact will expire in days. Defaults to repository/org [retention settings](https://docs.github.com/en/actions/learn-github-actions/usage-limits-billing-and-administration#artifact-and-log-retention-policy) if unset or `0` | 280 | | `DOCKER_BUILD_EXPORT_LEGACY` | Bool | `false` | If `true`, exports build using legacy export-build tool instead of [`buildx history export` command](https://docs.docker.com/reference/cli/docker/buildx/history/export/) | 281 | 282 | ## Troubleshooting 283 | 284 | See [TROUBLESHOOTING.md](TROUBLESHOOTING.md) 285 | 286 | ## Contributing 287 | 288 | Want to contribute? Awesome! You can find information about contributing to 289 | this project in the [CONTRIBUTING.md](/.github/CONTRIBUTING.md) 290 | -------------------------------------------------------------------------------- /TROUBLESHOOTING.md: -------------------------------------------------------------------------------- 1 | # Troubleshooting 2 | 3 | * [Cannot push to a registry](#cannot-push-to-a-registry) 4 | * [BuildKit container logs](#buildkit-container-logs) 5 | * [With containerd](#with-containerd) 6 | * [`repository name must be lowercase`](#repository-name-must-be-lowercase) 7 | 8 | ## Cannot push to a registry 9 | 10 | While pushing to a registry, you may encounter these kinds of issues: 11 | 12 | * `failed commit on ref "layer-sha256:...": invalid content digest in response: invalid checksum digest format` 13 | * `failed commit on ref "layer-sha256:...": no response` 14 | * `failed commit on ref "manifest-sha256:...": unexpected status: 400 Bad Request` 15 | * `failed commit on ref "manifest-sha256:...": unexpected status: 401 Unauthorized` 16 | * `unexpected response: 401 Unauthorized` 17 | 18 | These issues are not directly related to this action but are rather linked to 19 | [Buildx](https://github.com/docker/buildx), [BuildKit](https://github.com/moby/buildkit), 20 | [containerd](https://github.com/containerd/containerd) or the registry on which 21 | you're pushing your image. The quality of error message depends on the registry 22 | and are usually not very informative. 23 | 24 | ### BuildKit container logs 25 | 26 | To help you solve this, you have to [enable debugging in the setup-buildx](https://github.com/docker/setup-buildx-action#buildkit-container-logs) 27 | action step and attach BuildKit container logs to your issue. 28 | 29 | ### With containerd 30 | 31 | Next you can test pushing with [containerd action](https://github.com/crazy-max/ghaction-setup-containerd) 32 | using the following workflow. If it works then open an issue on [BuildKit](https://github.com/moby/buildkit) 33 | repository. 34 | 35 | ```yaml 36 | name: containerd 37 | 38 | on: 39 | push: 40 | 41 | jobs: 42 | containerd: 43 | runs-on: ubuntu-latest 44 | steps: 45 | - 46 | name: Checkout 47 | uses: actions/checkout@v4 48 | - 49 | name: Set up QEMU 50 | uses: docker/setup-qemu-action@v3 51 | - 52 | name: Set up Docker Buildx 53 | uses: docker/setup-buildx-action@v3 54 | with: 55 | buildkitd-flags: --debug 56 | - 57 | name: Set up containerd 58 | uses: crazy-max/ghaction-setup-containerd@v2 59 | - 60 | name: Build Docker image 61 | uses: docker/build-push-action@v6 62 | with: 63 | context: . 64 | platforms: linux/amd64,linux/arm64 65 | tags: docker.io/user/app:latest 66 | outputs: type=oci,dest=/tmp/image.tar 67 | - 68 | name: Import image in containerd 69 | run: | 70 | sudo ctr i import --base-name docker.io/user/app --digests --all-platforms /tmp/image.tar 71 | - 72 | name: Push image with containerd 73 | run: | 74 | sudo ctr --debug i push --user "${{ secrets.DOCKER_USERNAME }}:${{ secrets.DOCKER_PASSWORD }}" docker.io/user/app:latest 75 | ``` 76 | 77 | ## `repository name must be lowercase` 78 | 79 | You may encounter this issue if you're using `github.repository` as a repo slug 80 | in your tag: 81 | 82 | ``` 83 | #6 exporting to image 84 | #6 exporting layers 85 | #6 exporting layers 1.2s done 86 | #6 exporting manifest sha256:b47f7dfb97b89ccd5de553af3c8cd94c4795884cbe5693e93946b1d95a7b1d12 0.0s done 87 | #6 exporting config sha256:995e93fab8196893192f08a38deea6769dc4d98f86cf705eccc24ec96a3e271c 0.0s done 88 | #6 ERROR: invalid reference format: repository name must be lowercase 89 | ------ 90 | > exporting to image: 91 | ------ 92 | error: failed to solve: invalid reference format: repository name must be lowercase 93 | ``` 94 | 95 | or a cache reference: 96 | 97 | ``` 98 | #10 importing cache manifest from ghcr.io/My-Org/repo:main 99 | #10 ERROR: invalid reference format: repository name must be lowercase 100 | ``` 101 | 102 | To fix this issue you can use our [metadata action](https://github.com/docker/metadata-action) 103 | to generate sanitized tags: 104 | 105 | ```yaml 106 | - name: Docker meta 107 | id: meta 108 | uses: docker/metadata-action@v4 109 | with: 110 | images: ghcr.io/${{ github.repository }} 111 | tags: latest 112 | 113 | - name: Build and push 114 | uses: docker/build-push-action@v6 115 | with: 116 | context: . 117 | push: true 118 | tags: ${{ steps.meta.outputs.tags }} 119 | ``` 120 | 121 | Or a dedicated step to sanitize the slug: 122 | 123 | ```yaml 124 | - name: Sanitize repo slug 125 | uses: actions/github-script@v6 126 | id: repo_slug 127 | with: 128 | result-encoding: string 129 | script: return 'ghcr.io/${{ github.repository }}'.toLowerCase() 130 | 131 | - name: Build and push 132 | uses: docker/build-push-action@v6 133 | with: 134 | context: . 135 | push: true 136 | tags: ${{ steps.repo_slug.outputs.result }}:latest 137 | ``` 138 | -------------------------------------------------------------------------------- /__mocks__/@actions/github.ts: -------------------------------------------------------------------------------- 1 | import {jest} from '@jest/globals'; 2 | 3 | export const context = { 4 | repo: { 5 | owner: 'docker', 6 | repo: 'build-push-action' 7 | }, 8 | ref: 'refs/heads/master', 9 | runId: 123456789, 10 | payload: { 11 | after: '860c1904a1ce19322e91ac35af1ab07466440c37', 12 | base_ref: null, 13 | before: '5f3331d7f7044c18ca9f12c77d961c4d7cf3276a', 14 | commits: [ 15 | { 16 | author: { 17 | email: 'crazy-max@users.noreply.github.com', 18 | name: 'CrazyMax', 19 | username: 'crazy-max' 20 | }, 21 | committer: { 22 | email: 'crazy-max@users.noreply.github.com', 23 | name: 'CrazyMax', 24 | username: 'crazy-max' 25 | }, 26 | distinct: true, 27 | id: '860c1904a1ce19322e91ac35af1ab07466440c37', 28 | message: 'hello dev', 29 | timestamp: '2022-04-19T11:27:24+02:00', 30 | tree_id: 'd2c60af597e863787d2d27f569e30495b0b92820', 31 | url: 'https://github.com/docker/test-docker-action/commit/860c1904a1ce19322e91ac35af1ab07466440c37' 32 | } 33 | ], 34 | compare: 'https://github.com/docker/test-docker-action/compare/5f3331d7f704...860c1904a1ce', 35 | created: false, 36 | deleted: false, 37 | forced: false, 38 | head_commit: { 39 | author: { 40 | email: 'crazy-max@users.noreply.github.com', 41 | name: 'CrazyMax', 42 | username: 'crazy-max' 43 | }, 44 | committer: { 45 | email: 'crazy-max@users.noreply.github.com', 46 | name: 'CrazyMax', 47 | username: 'crazy-max' 48 | }, 49 | distinct: true, 50 | id: '860c1904a1ce19322e91ac35af1ab07466440c37', 51 | message: 'hello dev', 52 | timestamp: '2022-04-19T11:27:24+02:00', 53 | tree_id: 'd2c60af597e863787d2d27f569e30495b0b92820', 54 | url: 'https://github.com/docker/test-docker-action/commit/860c1904a1ce19322e91ac35af1ab07466440c37' 55 | }, 56 | organization: { 57 | avatar_url: 'https://avatars.githubusercontent.com/u/5429470?v=4', 58 | description: 'Docker helps developers bring their ideas to life by conquering the complexity of app development.', 59 | events_url: 'https://api.github.com/orgs/docker/events', 60 | hooks_url: 'https://api.github.com/orgs/docker/hooks', 61 | id: 5429470, 62 | issues_url: 'https://api.github.com/orgs/docker/issues', 63 | login: 'docker', 64 | members_url: 'https://api.github.com/orgs/docker/members{/member}', 65 | node_id: 'MDEyOk9yZ2FuaXphdGlvbjU0Mjk0NzA=', 66 | public_members_url: 'https://api.github.com/orgs/docker/public_members{/member}', 67 | repos_url: 'https://api.github.com/orgs/docker/repos', 68 | url: 'https://api.github.com/orgs/docker' 69 | }, 70 | pusher: { 71 | email: 'github@crazymax.dev', 72 | name: 'crazy-max' 73 | }, 74 | ref: 'refs/heads/dev', 75 | repository: { 76 | allow_forking: true, 77 | archive_url: 'https://api.github.com/repos/docker/test-docker-action/{archive_format}{/ref}', 78 | archived: false, 79 | assignees_url: 'https://api.github.com/repos/docker/test-docker-action/assignees{/user}', 80 | blobs_url: 'https://api.github.com/repos/docker/test-docker-action/git/blobs{/sha}', 81 | branches_url: 'https://api.github.com/repos/docker/test-docker-action/branches{/branch}', 82 | clone_url: 'https://github.com/docker/test-docker-action.git', 83 | collaborators_url: 'https://api.github.com/repos/docker/test-docker-action/collaborators{/collaborator}', 84 | comments_url: 'https://api.github.com/repos/docker/test-docker-action/comments{/number}', 85 | commits_url: 'https://api.github.com/repos/docker/test-docker-action/commits{/sha}', 86 | compare_url: 'https://api.github.com/repos/docker/test-docker-action/compare/{base}...{head}', 87 | contents_url: 'https://api.github.com/repos/docker/test-docker-action/contents/{+path}', 88 | contributors_url: 'https://api.github.com/repos/docker/test-docker-action/contributors', 89 | created_at: 1596792180, 90 | default_branch: 'master', 91 | deployments_url: 'https://api.github.com/repos/docker/test-docker-action/deployments', 92 | description: 'Test "Docker" Actions', 93 | disabled: false, 94 | downloads_url: 'https://api.github.com/repos/docker/test-docker-action/downloads', 95 | events_url: 'https://api.github.com/repos/docker/test-docker-action/events', 96 | fork: false, 97 | forks: 1, 98 | forks_count: 1, 99 | forks_url: 'https://api.github.com/repos/docker/test-docker-action/forks', 100 | full_name: 'docker/test-docker-action', 101 | git_commits_url: 'https://api.github.com/repos/docker/test-docker-action/git/commits{/sha}', 102 | git_refs_url: 'https://api.github.com/repos/docker/test-docker-action/git/refs{/sha}', 103 | git_tags_url: 'https://api.github.com/repos/docker/test-docker-action/git/tags{/sha}', 104 | git_url: 'git://github.com/docker/test-docker-action.git', 105 | has_downloads: true, 106 | has_issues: true, 107 | has_pages: false, 108 | has_projects: true, 109 | has_wiki: true, 110 | homepage: '', 111 | hooks_url: 'https://api.github.com/repos/docker/test-docker-action/hooks', 112 | html_url: 'https://github.com/docker/test-docker-action', 113 | id: 285789493, 114 | is_template: false, 115 | issue_comment_url: 'https://api.github.com/repos/docker/test-docker-action/issues/comments{/number}', 116 | issue_events_url: 'https://api.github.com/repos/docker/test-docker-action/issues/events{/number}', 117 | issues_url: 'https://api.github.com/repos/docker/test-docker-action/issues{/number}', 118 | keys_url: 'https://api.github.com/repos/docker/test-docker-action/keys{/key_id}', 119 | labels_url: 'https://api.github.com/repos/docker/test-docker-action/labels{/name}', 120 | language: 'JavaScript', 121 | languages_url: 'https://api.github.com/repos/docker/test-docker-action/languages', 122 | license: { 123 | key: 'mit', 124 | name: 'MIT License', 125 | node_id: 'MDc6TGljZW5zZTEz', 126 | spdx_id: 'MIT', 127 | url: 'https://api.github.com/licenses/mit' 128 | }, 129 | master_branch: 'master', 130 | merges_url: 'https://api.github.com/repos/docker/test-docker-action/merges', 131 | milestones_url: 'https://api.github.com/repos/docker/test-docker-action/milestones{/number}', 132 | mirror_url: null, 133 | name: 'test-docker-action', 134 | node_id: 'MDEwOlJlcG9zaXRvcnkyODU3ODk0OTM=', 135 | notifications_url: 'https://api.github.com/repos/docker/test-docker-action/notifications{?since,all,participating}', 136 | open_issues: 6, 137 | open_issues_count: 6, 138 | organization: 'docker', 139 | owner: { 140 | avatar_url: 'https://avatars.githubusercontent.com/u/5429470?v=4', 141 | email: 'info@docker.com', 142 | events_url: 'https://api.github.com/users/docker/events{/privacy}', 143 | followers_url: 'https://api.github.com/users/docker/followers', 144 | following_url: 'https://api.github.com/users/docker/following{/other_user}', 145 | gists_url: 'https://api.github.com/users/docker/gists{/gist_id}', 146 | gravatar_id: '', 147 | html_url: 'https://github.com/docker', 148 | id: 5429470, 149 | login: 'docker', 150 | name: 'docker', 151 | node_id: 'MDEyOk9yZ2FuaXphdGlvbjU0Mjk0NzA=', 152 | organizations_url: 'https://api.github.com/users/docker/orgs', 153 | received_events_url: 'https://api.github.com/users/docker/received_events', 154 | repos_url: 'https://api.github.com/users/docker/repos', 155 | site_admin: false, 156 | starred_url: 'https://api.github.com/users/docker/starred{/owner}{/repo}', 157 | subscriptions_url: 'https://api.github.com/users/docker/subscriptions', 158 | type: 'Organization', 159 | url: 'https://api.github.com/users/docker' 160 | }, 161 | private: true, 162 | pulls_url: 'https://api.github.com/repos/docker/test-docker-action/pulls{/number}', 163 | pushed_at: 1650360446, 164 | releases_url: 'https://api.github.com/repos/docker/test-docker-action/releases{/id}', 165 | size: 796, 166 | ssh_url: 'git@github.com:docker/test-docker-action.git', 167 | stargazers: 0, 168 | stargazers_count: 0, 169 | stargazers_url: 'https://api.github.com/repos/docker/test-docker-action/stargazers', 170 | statuses_url: 'https://api.github.com/repos/docker/test-docker-action/statuses/{sha}', 171 | subscribers_url: 'https://api.github.com/repos/docker/test-docker-action/subscribers', 172 | subscription_url: 'https://api.github.com/repos/docker/test-docker-action/subscription', 173 | svn_url: 'https://github.com/docker/test-docker-action', 174 | tags_url: 'https://api.github.com/repos/docker/test-docker-action/tags', 175 | teams_url: 'https://api.github.com/repos/docker/test-docker-action/teams', 176 | topics: [], 177 | trees_url: 'https://api.github.com/repos/docker/test-docker-action/git/trees{/sha}', 178 | updated_at: '2022-04-19T09:05:09Z', 179 | url: 'https://github.com/docker/test-docker-action', 180 | visibility: 'private', 181 | watchers: 0, 182 | watchers_count: 0 183 | }, 184 | sender: { 185 | avatar_url: 'https://avatars.githubusercontent.com/u/1951866?v=4', 186 | events_url: 'https://api.github.com/users/crazy-max/events{/privacy}', 187 | followers_url: 'https://api.github.com/users/crazy-max/followers', 188 | following_url: 'https://api.github.com/users/crazy-max/following{/other_user}', 189 | gists_url: 'https://api.github.com/users/crazy-max/gists{/gist_id}', 190 | gravatar_id: '', 191 | html_url: 'https://github.com/crazy-max', 192 | id: 1951866, 193 | login: 'crazy-max', 194 | node_id: 'MDQ6VXNlcjE5NTE4NjY=', 195 | organizations_url: 'https://api.github.com/users/crazy-max/orgs', 196 | received_events_url: 'https://api.github.com/users/crazy-max/received_events', 197 | repos_url: 'https://api.github.com/users/crazy-max/repos', 198 | site_admin: false, 199 | starred_url: 'https://api.github.com/users/crazy-max/starred{/owner}{/repo}', 200 | subscriptions_url: 'https://api.github.com/users/crazy-max/subscriptions', 201 | type: 'User', 202 | url: 'https://api.github.com/users/crazy-max' 203 | } 204 | } 205 | }; 206 | 207 | export const getOctokit = jest.fn(); 208 | -------------------------------------------------------------------------------- /__tests__/context.test.ts: -------------------------------------------------------------------------------- 1 | import {afterEach, beforeEach, describe, expect, jest, test} from '@jest/globals'; 2 | import * as fs from 'fs'; 3 | import * as path from 'path'; 4 | 5 | import {Builder} from '@docker/actions-toolkit/lib/buildx/builder'; 6 | import {Buildx} from '@docker/actions-toolkit/lib/buildx/buildx'; 7 | import {Build} from '@docker/actions-toolkit/lib/buildx/build'; 8 | import {Context} from '@docker/actions-toolkit/lib/context'; 9 | import {Docker} from '@docker/actions-toolkit/lib/docker/docker'; 10 | import {GitHub} from '@docker/actions-toolkit/lib/github'; 11 | import {Toolkit} from '@docker/actions-toolkit/lib/toolkit'; 12 | 13 | import {BuilderInfo} from '@docker/actions-toolkit/lib/types/buildx/builder'; 14 | import {GitHubRepo} from '@docker/actions-toolkit/lib/types/github'; 15 | 16 | import * as context from '../src/context'; 17 | 18 | const tmpDir = path.join('/tmp', '.docker-build-push-jest'); 19 | const tmpName = path.join(tmpDir, '.tmpname-jest'); 20 | 21 | import repoFixture from './fixtures/github-repo.json'; 22 | jest.spyOn(GitHub.prototype, 'repoData').mockImplementation((): Promise => { 23 | return >(repoFixture as unknown); 24 | }); 25 | 26 | jest.spyOn(Context, 'tmpDir').mockImplementation((): string => { 27 | if (!fs.existsSync(tmpDir)) { 28 | fs.mkdirSync(tmpDir, {recursive: true}); 29 | } 30 | return tmpDir; 31 | }); 32 | 33 | jest.spyOn(Context, 'tmpName').mockImplementation((): string => { 34 | return tmpName; 35 | }); 36 | 37 | jest.spyOn(Docker, 'isAvailable').mockImplementation(async (): Promise => { 38 | return true; 39 | }); 40 | 41 | const metadataJson = path.join(tmpDir, 'metadata.json'); 42 | jest.spyOn(Build.prototype, 'getMetadataFilePath').mockImplementation((): string => { 43 | return metadataJson; 44 | }); 45 | 46 | const imageIDFilePath = path.join(tmpDir, 'iidfile.txt'); 47 | jest.spyOn(Build.prototype, 'getImageIDFilePath').mockImplementation((): string => { 48 | return imageIDFilePath; 49 | }); 50 | 51 | jest.spyOn(Builder.prototype, 'inspect').mockImplementation(async (): Promise => { 52 | return { 53 | name: 'builder2', 54 | driver: 'docker-container', 55 | lastActivity: new Date('2023-01-16 09:45:23 +0000 UTC'), 56 | nodes: [ 57 | { 58 | buildkit: 'v0.11.0', 59 | 'buildkitd-flags': '--debug --allow-insecure-entitlement security.insecure --allow-insecure-entitlement network.host', 60 | 'driver-opts': ['BUILDKIT_STEP_LOG_MAX_SIZE=10485760', 'BUILDKIT_STEP_LOG_MAX_SPEED=10485760', 'JAEGER_TRACE=localhost:6831', 'image=moby/buildkit:latest', 'network=host'], 61 | endpoint: 'unix:///var/run/docker.sock', 62 | name: 'builder20', 63 | platforms: 'linux/amd64,linux/amd64/v2,linux/amd64/v3,linux/arm64,linux/riscv64,linux/ppc64le,linux/s390x,linux/386,linux/mips64le,linux/mips64,linux/arm/v7,linux/arm/v6', 64 | status: 'running' 65 | } 66 | ] 67 | }; 68 | }); 69 | 70 | describe('getArgs', () => { 71 | const originalEnv = process.env; 72 | beforeEach(() => { 73 | process.env = Object.keys(process.env).reduce((object, key) => { 74 | if (!key.startsWith('INPUT_')) { 75 | object[key] = process.env[key]; 76 | } 77 | return object; 78 | }, {}); 79 | }); 80 | afterEach(() => { 81 | process.env = originalEnv; 82 | }); 83 | 84 | // prettier-ignore 85 | test.each([ 86 | [ 87 | 0, 88 | '0.4.1', 89 | new Map([ 90 | ['context', '.'], 91 | ['load', 'false'], 92 | ['no-cache', 'false'], 93 | ['push', 'false'], 94 | ['pull', 'false'], 95 | ]), 96 | [ 97 | 'build', 98 | '--iidfile', imageIDFilePath, 99 | '.' 100 | ], 101 | undefined 102 | ], 103 | [ 104 | 1, 105 | '0.4.2', 106 | new Map([ 107 | ['build-args', `MY_ARG=val1,val2,val3 108 | ARG=val 109 | "MULTILINE=aaaa 110 | bbbb 111 | ccc"`], 112 | ['load', 'false'], 113 | ['no-cache', 'false'], 114 | ['push', 'false'], 115 | ['pull', 'false'], 116 | ]), 117 | [ 118 | 'build', 119 | '--build-arg', 'MY_ARG=val1,val2,val3', 120 | '--build-arg', 'ARG=val', 121 | '--build-arg', `MULTILINE=aaaa\nbbbb\nccc`, 122 | '--iidfile', imageIDFilePath, 123 | 'https://github.com/docker/build-push-action.git#refs/heads/master' 124 | ], 125 | undefined 126 | ], 127 | [ 128 | 2, 129 | '0.4.2', 130 | new Map([ 131 | ['tags', 'name/app:7.4, name/app:latest'], 132 | ['load', 'false'], 133 | ['no-cache', 'false'], 134 | ['push', 'false'], 135 | ['pull', 'false'], 136 | ]), 137 | [ 138 | 'build', 139 | '--iidfile', imageIDFilePath, 140 | '--tag', 'name/app:7.4', 141 | '--tag', 'name/app:latest', 142 | 'https://github.com/docker/build-push-action.git#refs/heads/master' 143 | ], 144 | undefined 145 | ], 146 | [ 147 | 3, 148 | '0.4.2', 149 | new Map([ 150 | ['context', '.'], 151 | ['labels', 'org.opencontainers.image.title=buildkit\norg.opencontainers.image.description=concurrent, cache-efficient, and Dockerfile-agnostic builder toolkit'], 152 | ['outputs', 'type=local,dest=./release-out'], 153 | ['load', 'false'], 154 | ['no-cache', 'false'], 155 | ['push', 'false'], 156 | ['pull', 'false'], 157 | ]), 158 | [ 159 | 'build', 160 | '--label', 'org.opencontainers.image.title=buildkit', 161 | '--label', 'org.opencontainers.image.description=concurrent, cache-efficient, and Dockerfile-agnostic builder toolkit', 162 | '--output', 'type=local,dest=./release-out', 163 | '.' 164 | ], 165 | undefined 166 | ], 167 | [ 168 | 4, 169 | '0.4.1', 170 | new Map([ 171 | ['context', '.'], 172 | ['platforms', 'linux/amd64,linux/arm64'], 173 | ['load', 'false'], 174 | ['no-cache', 'false'], 175 | ['push', 'false'], 176 | ['pull', 'false'], 177 | ]), 178 | [ 179 | 'build', 180 | '--platform', 'linux/amd64,linux/arm64', 181 | '.' 182 | ], 183 | undefined 184 | ], 185 | [ 186 | 5, 187 | '0.4.1', 188 | new Map([ 189 | ['context', '.'], 190 | ['load', 'false'], 191 | ['no-cache', 'false'], 192 | ['push', 'false'], 193 | ['pull', 'false'], 194 | ]), 195 | [ 196 | 'build', 197 | '--iidfile', imageIDFilePath, 198 | '.' 199 | ], 200 | undefined 201 | ], 202 | [ 203 | 6, 204 | '0.4.2', 205 | new Map([ 206 | ['context', '.'], 207 | ['secrets', 'GIT_AUTH_TOKEN=abcdefghijklmno=0123456789'], 208 | ['load', 'false'], 209 | ['no-cache', 'false'], 210 | ['push', 'false'], 211 | ['pull', 'false'], 212 | ]), 213 | [ 214 | 'build', 215 | '--iidfile', imageIDFilePath, 216 | '--secret', `id=GIT_AUTH_TOKEN,src=${tmpName}`, 217 | '.' 218 | ], 219 | undefined 220 | ], 221 | [ 222 | 7, 223 | '0.4.2', 224 | new Map([ 225 | ['github-token', 'abcdefghijklmno0123456789'], 226 | ['outputs', '.'], 227 | ['load', 'false'], 228 | ['no-cache', 'false'], 229 | ['push', 'false'], 230 | ['pull', 'false'], 231 | ]), 232 | [ 233 | 'build', 234 | '--output', '.', 235 | '--secret', `id=GIT_AUTH_TOKEN,src=${tmpName}`, 236 | 'https://github.com/docker/build-push-action.git#refs/heads/master' 237 | ], 238 | undefined 239 | ], 240 | [ 241 | 8, 242 | '0.4.2', 243 | new Map([ 244 | ['context', 'https://github.com/docker/build-push-action.git#refs/heads/master'], 245 | ['tag', 'localhost:5000/name/app:latest'], 246 | ['platforms', 'linux/amd64,linux/arm64'], 247 | ['secrets', 'GIT_AUTH_TOKEN=abcdefghijklmno=0123456789'], 248 | ['file', './test/Dockerfile'], 249 | ['builder', 'builder-git-context-2'], 250 | ['load', 'false'], 251 | ['no-cache', 'false'], 252 | ['push', 'true'], 253 | ['pull', 'false'], 254 | ]), 255 | [ 256 | 'build', 257 | '--file', './test/Dockerfile', 258 | '--iidfile', imageIDFilePath, 259 | '--platform', 'linux/amd64,linux/arm64', 260 | '--secret', `id=GIT_AUTH_TOKEN,src=${tmpName}`, 261 | '--builder', 'builder-git-context-2', 262 | '--push', 263 | 'https://github.com/docker/build-push-action.git#refs/heads/master' 264 | ], 265 | undefined 266 | ], 267 | [ 268 | 9, 269 | '0.4.2', 270 | new Map([ 271 | ['context', 'https://github.com/docker/build-push-action.git#refs/heads/master'], 272 | ['tag', 'localhost:5000/name/app:latest'], 273 | ['platforms', 'linux/amd64,linux/arm64'], 274 | ['secrets', `GIT_AUTH_TOKEN=abcdefghi,jklmno=0123456789 275 | "MYSECRET=aaaaaaaa 276 | bbbbbbb 277 | ccccccccc" 278 | FOO=bar 279 | "EMPTYLINE=aaaa 280 | 281 | bbbb 282 | ccc"`], 283 | ['file', './test/Dockerfile'], 284 | ['builder', 'builder-git-context-2'], 285 | ['load', 'false'], 286 | ['no-cache', 'false'], 287 | ['push', 'true'], 288 | ['pull', 'false'], 289 | ]), 290 | [ 291 | 'build', 292 | '--file', './test/Dockerfile', 293 | '--iidfile', imageIDFilePath, 294 | '--platform', 'linux/amd64,linux/arm64', 295 | '--secret', `id=GIT_AUTH_TOKEN,src=${tmpName}`, 296 | '--secret', `id=MYSECRET,src=${tmpName}`, 297 | '--secret', `id=FOO,src=${tmpName}`, 298 | '--secret', `id=EMPTYLINE,src=${tmpName}`, 299 | '--builder', 'builder-git-context-2', 300 | '--push', 301 | 'https://github.com/docker/build-push-action.git#refs/heads/master' 302 | ], 303 | undefined 304 | ], 305 | [ 306 | 10, 307 | '0.4.2', 308 | new Map([ 309 | ['context', 'https://github.com/docker/build-push-action.git#refs/heads/master'], 310 | ['tag', 'localhost:5000/name/app:latest'], 311 | ['platforms', 'linux/amd64,linux/arm64'], 312 | ['secrets', `GIT_AUTH_TOKEN=abcdefghi,jklmno=0123456789 313 | MYSECRET=aaaaaaaa 314 | bbbbbbb 315 | ccccccccc 316 | FOO=bar 317 | EMPTYLINE=aaaa 318 | 319 | bbbb 320 | ccc`], 321 | ['file', './test/Dockerfile'], 322 | ['builder', 'builder-git-context-2'], 323 | ['load', 'false'], 324 | ['no-cache', 'false'], 325 | ['push', 'true'], 326 | ['pull', 'false'], 327 | ]), 328 | [ 329 | 'build', 330 | '--file', './test/Dockerfile', 331 | '--iidfile', imageIDFilePath, 332 | '--platform', 'linux/amd64,linux/arm64', 333 | '--secret', `id=GIT_AUTH_TOKEN,src=${tmpName}`, 334 | '--secret', `id=MYSECRET,src=${tmpName}`, 335 | '--secret', `id=FOO,src=${tmpName}`, 336 | '--secret', `id=EMPTYLINE,src=${tmpName}`, 337 | '--builder', 'builder-git-context-2', 338 | '--push', 339 | 'https://github.com/docker/build-push-action.git#refs/heads/master' 340 | ], 341 | undefined 342 | ], 343 | [ 344 | 11, 345 | '0.5.1', 346 | new Map([ 347 | ['context', 'https://github.com/docker/build-push-action.git#refs/heads/master'], 348 | ['tag', 'localhost:5000/name/app:latest'], 349 | ['secret-files', `MY_SECRET=${path.join(__dirname, 'fixtures', 'secret.txt')}`], 350 | ['file', './test/Dockerfile'], 351 | ['builder', 'builder-git-context-2'], 352 | ['network', 'host'], 353 | ['load', 'false'], 354 | ['no-cache', 'false'], 355 | ['push', 'true'], 356 | ['pull', 'false'], 357 | ]), 358 | [ 359 | 'build', 360 | '--file', './test/Dockerfile', 361 | '--iidfile', imageIDFilePath, 362 | '--secret', `id=MY_SECRET,src=${tmpName}`, 363 | '--builder', 'builder-git-context-2', 364 | '--network', 'host', 365 | '--push', 366 | 'https://github.com/docker/build-push-action.git#refs/heads/master' 367 | ], 368 | undefined 369 | ], 370 | [ 371 | 12, 372 | '0.4.2', 373 | new Map([ 374 | ['context', '.'], 375 | ['labels', 'org.opencontainers.image.title=filter_results_top_n\norg.opencontainers.image.description=Reference implementation of operation "filter results (top-n)"'], 376 | ['outputs', 'type=local,dest=./release-out'], 377 | ['load', 'false'], 378 | ['no-cache', 'false'], 379 | ['push', 'false'], 380 | ['pull', 'false'], 381 | ]), 382 | [ 383 | 'build', 384 | '--label', 'org.opencontainers.image.title=filter_results_top_n', 385 | '--label', 'org.opencontainers.image.description=Reference implementation of operation "filter results (top-n)"', 386 | '--output', 'type=local,dest=./release-out', 387 | '.' 388 | ], 389 | undefined 390 | ], 391 | [ 392 | 13, 393 | '0.6.0', 394 | new Map([ 395 | ['context', '.'], 396 | ['tag', 'localhost:5000/name/app:latest'], 397 | ['file', './test/Dockerfile'], 398 | ['add-hosts', 'docker:10.180.0.1,foo:10.0.0.1'], 399 | ['network', 'host'], 400 | ['load', 'false'], 401 | ['no-cache', 'false'], 402 | ['push', 'true'], 403 | ['pull', 'false'], 404 | ]), 405 | [ 406 | 'build', 407 | '--add-host', 'docker:10.180.0.1', 408 | '--add-host', 'foo:10.0.0.1', 409 | '--file', './test/Dockerfile', 410 | '--iidfile', imageIDFilePath, 411 | '--metadata-file', metadataJson, 412 | '--network', 'host', 413 | '--push', 414 | '.' 415 | ], 416 | undefined 417 | ], 418 | [ 419 | 14, 420 | '0.7.0', 421 | new Map([ 422 | ['context', '.'], 423 | ['file', './test/Dockerfile'], 424 | ['add-hosts', 'docker:10.180.0.1\nfoo:10.0.0.1'], 425 | ['cgroup-parent', 'foo'], 426 | ['shm-size', '2g'], 427 | ['ulimit', `nofile=1024:1024 428 | nproc=3`], 429 | ['load', 'false'], 430 | ['no-cache', 'false'], 431 | ['push', 'false'], 432 | ['pull', 'false'], 433 | ]), 434 | [ 435 | 'build', 436 | '--add-host', 'docker:10.180.0.1', 437 | '--add-host', 'foo:10.0.0.1', 438 | '--cgroup-parent', 'foo', 439 | '--file', './test/Dockerfile', 440 | '--iidfile', imageIDFilePath, 441 | '--shm-size', '2g', 442 | '--ulimit', 'nofile=1024:1024', 443 | '--ulimit', 'nproc=3', 444 | '--metadata-file', metadataJson, 445 | '.' 446 | ], 447 | undefined 448 | ], 449 | [ 450 | 15, 451 | '0.7.0', 452 | new Map([ 453 | ['context', '{{defaultContext}}:docker'], 454 | ['load', 'false'], 455 | ['no-cache', 'false'], 456 | ['push', 'false'], 457 | ['pull', 'false'], 458 | ]), 459 | [ 460 | 'build', 461 | '--iidfile', imageIDFilePath, 462 | '--metadata-file', metadataJson, 463 | 'https://github.com/docker/build-push-action.git#refs/heads/master:docker' 464 | ], 465 | undefined 466 | ], 467 | [ 468 | 16, 469 | '0.8.2', 470 | new Map([ 471 | ['github-token', 'abcdefghijklmno0123456789'], 472 | ['context', '{{defaultContext}}:subdir'], 473 | ['load', 'false'], 474 | ['no-cache', 'false'], 475 | ['push', 'false'], 476 | ['pull', 'false'], 477 | ]), 478 | [ 479 | 'build', 480 | '--iidfile', imageIDFilePath, 481 | '--secret', `id=GIT_AUTH_TOKEN,src=${tmpName}`, 482 | '--metadata-file', metadataJson, 483 | 'https://github.com/docker/build-push-action.git#refs/heads/master:subdir' 484 | ], 485 | undefined 486 | ], 487 | [ 488 | 17, 489 | '0.8.2', 490 | new Map([ 491 | ['context', '.'], 492 | ['load', 'false'], 493 | ['no-cache', 'false'], 494 | ['push', 'false'], 495 | ['pull', 'false'], 496 | ['provenance', 'true'], 497 | ]), 498 | [ 499 | 'build', 500 | '--iidfile', imageIDFilePath, 501 | '--metadata-file', metadataJson, 502 | '.' 503 | ], 504 | undefined 505 | ], 506 | [ 507 | 18, 508 | '0.10.0', 509 | new Map([ 510 | ['context', '.'], 511 | ['load', 'false'], 512 | ['no-cache', 'false'], 513 | ['push', 'false'], 514 | ['pull', 'false'], 515 | ]), 516 | [ 517 | 'build', 518 | '--iidfile', imageIDFilePath, 519 | '--attest', `type=provenance,mode=min,inline-only=true,builder-id=https://github.com/docker/build-push-action/actions/runs/123456789/attempts/1`, 520 | '--metadata-file', metadataJson, 521 | '.' 522 | ], 523 | undefined 524 | ], 525 | [ 526 | 19, 527 | '0.10.0', 528 | new Map([ 529 | ['context', '.'], 530 | ['load', 'false'], 531 | ['no-cache', 'false'], 532 | ['push', 'false'], 533 | ['pull', 'false'], 534 | ['provenance', 'true'], 535 | ]), 536 | [ 537 | 'build', 538 | '--iidfile', imageIDFilePath, 539 | '--attest', `type=provenance,builder-id=https://github.com/docker/build-push-action/actions/runs/123456789/attempts/1`, 540 | '--metadata-file', metadataJson, 541 | '.' 542 | ], 543 | undefined 544 | ], 545 | [ 546 | 20, 547 | '0.10.0', 548 | new Map([ 549 | ['context', '.'], 550 | ['load', 'false'], 551 | ['no-cache', 'false'], 552 | ['push', 'false'], 553 | ['pull', 'false'], 554 | ['provenance', 'mode=max'], 555 | ]), 556 | [ 557 | 'build', 558 | '--iidfile', imageIDFilePath, 559 | '--attest', `type=provenance,mode=max,builder-id=https://github.com/docker/build-push-action/actions/runs/123456789/attempts/1`, 560 | '--metadata-file', metadataJson, 561 | '.' 562 | ], 563 | undefined 564 | ], 565 | [ 566 | 21, 567 | '0.10.0', 568 | new Map([ 569 | ['context', '.'], 570 | ['load', 'false'], 571 | ['no-cache', 'false'], 572 | ['push', 'false'], 573 | ['pull', 'false'], 574 | ['provenance', 'false'], 575 | ]), 576 | [ 577 | 'build', 578 | '--iidfile', imageIDFilePath, 579 | '--attest', 'type=provenance,disabled=true', 580 | '--metadata-file', metadataJson, 581 | '.' 582 | ], 583 | undefined 584 | ], 585 | [ 586 | 22, 587 | '0.10.0', 588 | new Map([ 589 | ['context', '.'], 590 | ['load', 'false'], 591 | ['no-cache', 'false'], 592 | ['push', 'false'], 593 | ['pull', 'false'], 594 | ['provenance', 'builder-id=foo'], 595 | ]), 596 | [ 597 | 'build', 598 | '--iidfile', imageIDFilePath, 599 | '--attest', 'type=provenance,builder-id=foo', 600 | '--metadata-file', metadataJson, 601 | '.' 602 | ], 603 | undefined 604 | ], 605 | [ 606 | 23, 607 | '0.10.0', 608 | new Map([ 609 | ['context', '.'], 610 | ['load', 'false'], 611 | ['no-cache', 'false'], 612 | ['push', 'false'], 613 | ['pull', 'false'], 614 | ['outputs', 'type=docker'], 615 | ]), 616 | [ 617 | 'build', 618 | '--iidfile', imageIDFilePath, 619 | "--output", 'type=docker', 620 | '--metadata-file', metadataJson, 621 | '.' 622 | ], 623 | undefined 624 | ], 625 | [ 626 | 24, 627 | '0.10.0', 628 | new Map([ 629 | ['context', '.'], 630 | ['load', 'true'], 631 | ['no-cache', 'false'], 632 | ['push', 'false'], 633 | ['pull', 'false'], 634 | ]), 635 | [ 636 | 'build', 637 | '--iidfile', imageIDFilePath, 638 | '--load', 639 | '--metadata-file', metadataJson, 640 | '.' 641 | ], 642 | undefined 643 | ], 644 | [ 645 | 25, 646 | '0.10.0', 647 | new Map([ 648 | ['context', '.'], 649 | ['build-args', `FOO=bar#baz`], 650 | ['load', 'true'], 651 | ['no-cache', 'false'], 652 | ['push', 'false'], 653 | ['pull', 'false'], 654 | ]), 655 | [ 656 | 'build', 657 | '--build-arg', 'FOO=bar#baz', 658 | '--iidfile', imageIDFilePath, 659 | '--load', 660 | '--metadata-file', metadataJson, 661 | '.' 662 | ], 663 | undefined 664 | ], 665 | [ 666 | 26, 667 | '0.10.0', 668 | new Map([ 669 | ['context', '.'], 670 | ['no-cache', 'false'], 671 | ['load', 'true'], 672 | ['push', 'false'], 673 | ['pull', 'false'], 674 | ['secret-envs', `MY_SECRET=MY_SECRET_ENV 675 | ANOTHER_SECRET=ANOTHER_SECRET_ENV`] 676 | ]), 677 | [ 678 | 'build', 679 | '--secret', 'id=MY_SECRET,env=MY_SECRET_ENV', 680 | '--secret', 'id=ANOTHER_SECRET,env=ANOTHER_SECRET_ENV', 681 | '--iidfile', imageIDFilePath, 682 | '--load', 683 | '--metadata-file', metadataJson, 684 | '.' 685 | ], 686 | undefined 687 | ], 688 | [ 689 | 27, 690 | '0.10.0', 691 | new Map([ 692 | ['context', '.'], 693 | ['no-cache', 'false'], 694 | ['load', 'true'], 695 | ['push', 'false'], 696 | ['pull', 'false'], 697 | ['secret-envs', 'MY_SECRET=MY_SECRET_ENV,ANOTHER_SECRET=ANOTHER_SECRET_ENV'] 698 | ]), 699 | [ 700 | 'build', 701 | '--secret', 'id=MY_SECRET,env=MY_SECRET_ENV', 702 | '--secret', 'id=ANOTHER_SECRET,env=ANOTHER_SECRET_ENV', 703 | '--iidfile', imageIDFilePath, 704 | '--load', 705 | '--metadata-file', metadataJson, 706 | '.' 707 | ], 708 | undefined 709 | ], 710 | [ 711 | 28, 712 | '0.11.0', 713 | new Map([ 714 | ['context', '.'], 715 | ['annotations', 'example1=www\nindex:example2=xxx\nmanifest:example3=yyy\nmanifest-descriptor[linux/amd64]:example4=zzz'], 716 | ['outputs', 'type=local,dest=./release-out'], 717 | ['load', 'false'], 718 | ['no-cache', 'false'], 719 | ['push', 'false'], 720 | ['pull', 'false'], 721 | ]), 722 | [ 723 | 'build', 724 | '--output', 'type=local,dest=./release-out', 725 | '--attest', `type=provenance,mode=min,inline-only=true,builder-id=https://github.com/docker/build-push-action/actions/runs/123456789/attempts/1`, 726 | '--metadata-file', metadataJson, 727 | '.' 728 | ], 729 | undefined 730 | ], 731 | [ 732 | 29, 733 | '0.12.0', 734 | new Map([ 735 | ['context', '.'], 736 | ['annotations', 'example1=www\nindex:example2=xxx\nmanifest:example3=yyy\nmanifest-descriptor[linux/amd64]:example4=zzz'], 737 | ['outputs', 'type=local,dest=./release-out'], 738 | ['load', 'false'], 739 | ['no-cache', 'false'], 740 | ['push', 'false'], 741 | ['pull', 'false'], 742 | ]), 743 | [ 744 | 'build', 745 | '--annotation', 'example1=www', 746 | '--annotation', 'index:example2=xxx', 747 | '--annotation', 'manifest:example3=yyy', 748 | '--annotation', 'manifest-descriptor[linux/amd64]:example4=zzz', 749 | '--output', 'type=local,dest=./release-out', 750 | '--attest', `type=provenance,mode=min,inline-only=true,builder-id=https://github.com/docker/build-push-action/actions/runs/123456789/attempts/1`, 751 | '--metadata-file', metadataJson, 752 | '.' 753 | ], 754 | undefined 755 | ], 756 | [ 757 | 30, 758 | '0.12.0', 759 | new Map([ 760 | ['context', '.'], 761 | ['outputs', `type=image,"name=localhost:5000/name/app:latest,localhost:5000/name/app:foo",push-by-digest=true,name-canonical=true,push=true`], 762 | ['load', 'false'], 763 | ['no-cache', 'false'], 764 | ['push', 'false'], 765 | ['pull', 'false'], 766 | ]), 767 | [ 768 | 'build', 769 | '--iidfile', imageIDFilePath, 770 | "--output", `type=image,"name=localhost:5000/name/app:latest,localhost:5000/name/app:foo",push-by-digest=true,name-canonical=true,push=true`, 771 | '--attest', `type=provenance,mode=min,inline-only=true,builder-id=https://github.com/docker/build-push-action/actions/runs/123456789/attempts/1`, 772 | '--metadata-file', metadataJson, 773 | '.' 774 | ], 775 | undefined 776 | ], 777 | [ 778 | 31, 779 | '0.13.1', 780 | new Map([ 781 | ['context', '.'], 782 | ['load', 'false'], 783 | ['no-cache', 'false'], 784 | ['push', 'false'], 785 | ['pull', 'false'], 786 | ['provenance', 'mode=max'], 787 | ['sbom', 'true'], 788 | ]), 789 | [ 790 | 'build', 791 | '--iidfile', imageIDFilePath, 792 | '--attest', `type=provenance,mode=max,builder-id=https://github.com/docker/build-push-action/actions/runs/123456789/attempts/1`, 793 | '--attest', `type=sbom,disabled=false`, 794 | '--metadata-file', metadataJson, 795 | '.' 796 | ], 797 | undefined 798 | ], 799 | [ 800 | 32, 801 | '0.13.1', 802 | new Map([ 803 | ['context', '.'], 804 | ['load', 'false'], 805 | ['no-cache', 'false'], 806 | ['push', 'false'], 807 | ['pull', 'false'], 808 | ['attests', 'type=provenance,mode=min'], 809 | ['provenance', 'mode=max'], 810 | ]), 811 | [ 812 | 'build', 813 | '--iidfile', imageIDFilePath, 814 | '--attest', `type=provenance,mode=max,builder-id=https://github.com/docker/build-push-action/actions/runs/123456789/attempts/1`, 815 | '--metadata-file', metadataJson, 816 | '.' 817 | ], 818 | undefined 819 | ], 820 | [ 821 | 33, 822 | '0.13.1', 823 | new Map([ 824 | ['context', '.'], 825 | ['load', 'false'], 826 | ['no-cache', 'false'], 827 | ['push', 'false'], 828 | ['pull', 'false'], 829 | ['attests', 'type=provenance,mode=min'], 830 | ]), 831 | [ 832 | 'build', 833 | '--iidfile', imageIDFilePath, 834 | '--attest', `type=provenance,mode=min,builder-id=https://github.com/docker/build-push-action/actions/runs/123456789/attempts/1`, 835 | '--metadata-file', metadataJson, 836 | '.' 837 | ], 838 | undefined 839 | ], 840 | [ 841 | 34, 842 | '0.13.1', 843 | new Map([ 844 | ['context', '.'], 845 | ['load', 'false'], 846 | ['no-cache', 'false'], 847 | ['push', 'false'], 848 | ['pull', 'false'] 849 | ]), 850 | [ 851 | 'build', 852 | '--iidfile', imageIDFilePath, 853 | '--metadata-file', metadataJson, 854 | '.' 855 | ], 856 | new Map([ 857 | ['BUILDX_NO_DEFAULT_ATTESTATIONS', '1'] 858 | ]) 859 | ], 860 | ])( 861 | '[%d] given %p with %p as inputs, returns %p', 862 | async (num: number, buildxVersion: string, inputs: Map, expected: Array, envs: Map | undefined) => { 863 | if (envs) { 864 | envs.forEach((value: string, name: string) => { 865 | process.env[name] = value; 866 | }); 867 | } 868 | inputs.forEach((value: string, name: string) => { 869 | setInput(name, value); 870 | }); 871 | const toolkit = new Toolkit(); 872 | jest.spyOn(Buildx.prototype, 'version').mockImplementation(async (): Promise => { 873 | return buildxVersion; 874 | }); 875 | const inp = await context.getInputs(); 876 | const res = await context.getArgs(inp, toolkit); 877 | expect(res).toEqual(expected); 878 | } 879 | ); 880 | }); 881 | 882 | // See: https://github.com/actions/toolkit/blob/a1b068ec31a042ff1e10a522d8fdf0b8869d53ca/packages/core/src/core.ts#L89 883 | function getInputName(name: string): string { 884 | return `INPUT_${name.replace(/ /g, '_').toUpperCase()}`; 885 | } 886 | 887 | function setInput(name: string, value: string): void { 888 | process.env[getInputName(name)] = value; 889 | } 890 | -------------------------------------------------------------------------------- /__tests__/fixtures/github-repo.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 1296269, 3 | "node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5", 4 | "name": "Hello-World", 5 | "full_name": "octocat/Hello-World", 6 | "owner": { 7 | "login": "octocat", 8 | "id": 1, 9 | "node_id": "MDQ6VXNlcjE=", 10 | "avatar_url": "https://github.com/images/error/octocat_happy.gif", 11 | "gravatar_id": "", 12 | "url": "https://api.github.com/users/octocat", 13 | "html_url": "https://github.com/octocat", 14 | "followers_url": "https://api.github.com/users/octocat/followers", 15 | "following_url": "https://api.github.com/users/octocat/following{/other_user}", 16 | "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", 17 | "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", 18 | "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", 19 | "organizations_url": "https://api.github.com/users/octocat/orgs", 20 | "repos_url": "https://api.github.com/users/octocat/repos", 21 | "events_url": "https://api.github.com/users/octocat/events{/privacy}", 22 | "received_events_url": "https://api.github.com/users/octocat/received_events", 23 | "type": "User", 24 | "site_admin": false 25 | }, 26 | "private": false, 27 | "html_url": "https://github.com/octocat/Hello-World", 28 | "description": "This your first repo!", 29 | "fork": false, 30 | "url": "https://api.github.com/repos/octocat/Hello-World", 31 | "archive_url": "http://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}", 32 | "assignees_url": "http://api.github.com/repos/octocat/Hello-World/assignees{/user}", 33 | "blobs_url": "http://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}", 34 | "branches_url": "http://api.github.com/repos/octocat/Hello-World/branches{/branch}", 35 | "collaborators_url": "http://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}", 36 | "comments_url": "http://api.github.com/repos/octocat/Hello-World/comments{/number}", 37 | "commits_url": "http://api.github.com/repos/octocat/Hello-World/commits{/sha}", 38 | "compare_url": "http://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}", 39 | "contents_url": "http://api.github.com/repos/octocat/Hello-World/contents/{+path}", 40 | "contributors_url": "http://api.github.com/repos/octocat/Hello-World/contributors", 41 | "deployments_url": "http://api.github.com/repos/octocat/Hello-World/deployments", 42 | "downloads_url": "http://api.github.com/repos/octocat/Hello-World/downloads", 43 | "events_url": "http://api.github.com/repos/octocat/Hello-World/events", 44 | "forks_url": "http://api.github.com/repos/octocat/Hello-World/forks", 45 | "git_commits_url": "http://api.github.com/repos/octocat/Hello-World/git/commits{/sha}", 46 | "git_refs_url": "http://api.github.com/repos/octocat/Hello-World/git/refs{/sha}", 47 | "git_tags_url": "http://api.github.com/repos/octocat/Hello-World/git/tags{/sha}", 48 | "git_url": "git:github.com/octocat/Hello-World.git", 49 | "issue_comment_url": "http://api.github.com/repos/octocat/Hello-World/issues/comments{/number}", 50 | "issue_events_url": "http://api.github.com/repos/octocat/Hello-World/issues/events{/number}", 51 | "issues_url": "http://api.github.com/repos/octocat/Hello-World/issues{/number}", 52 | "keys_url": "http://api.github.com/repos/octocat/Hello-World/keys{/key_id}", 53 | "labels_url": "http://api.github.com/repos/octocat/Hello-World/labels{/name}", 54 | "languages_url": "http://api.github.com/repos/octocat/Hello-World/languages", 55 | "merges_url": "http://api.github.com/repos/octocat/Hello-World/merges", 56 | "milestones_url": "http://api.github.com/repos/octocat/Hello-World/milestones{/number}", 57 | "notifications_url": "http://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}", 58 | "pulls_url": "http://api.github.com/repos/octocat/Hello-World/pulls{/number}", 59 | "releases_url": "http://api.github.com/repos/octocat/Hello-World/releases{/id}", 60 | "ssh_url": "git@github.com:octocat/Hello-World.git", 61 | "stargazers_url": "http://api.github.com/repos/octocat/Hello-World/stargazers", 62 | "statuses_url": "http://api.github.com/repos/octocat/Hello-World/statuses/{sha}", 63 | "subscribers_url": "http://api.github.com/repos/octocat/Hello-World/subscribers", 64 | "subscription_url": "http://api.github.com/repos/octocat/Hello-World/subscription", 65 | "tags_url": "http://api.github.com/repos/octocat/Hello-World/tags", 66 | "teams_url": "http://api.github.com/repos/octocat/Hello-World/teams", 67 | "trees_url": "http://api.github.com/repos/octocat/Hello-World/git/trees{/sha}", 68 | "clone_url": "https://github.com/octocat/Hello-World.git", 69 | "mirror_url": "git:git.example.com/octocat/Hello-World", 70 | "hooks_url": "http://api.github.com/repos/octocat/Hello-World/hooks", 71 | "svn_url": "https://svn.github.com/octocat/Hello-World", 72 | "homepage": "https://github.com", 73 | "language": null, 74 | "forks_count": 9, 75 | "stargazers_count": 80, 76 | "watchers_count": 80, 77 | "size": 108, 78 | "default_branch": "master", 79 | "open_issues_count": 0, 80 | "is_template": true, 81 | "topics": [ 82 | "octocat", 83 | "atom", 84 | "electron", 85 | "api" 86 | ], 87 | "has_issues": true, 88 | "has_projects": true, 89 | "has_wiki": true, 90 | "has_pages": false, 91 | "has_downloads": true, 92 | "archived": false, 93 | "disabled": false, 94 | "visibility": "public", 95 | "pushed_at": "2011-01-26T19:06:43Z", 96 | "created_at": "2011-01-26T19:01:12Z", 97 | "updated_at": "2011-01-26T19:14:43Z", 98 | "permissions": { 99 | "pull": true, 100 | "triage": true, 101 | "push": false, 102 | "maintain": false, 103 | "admin": false 104 | }, 105 | "allow_rebase_merge": true, 106 | "template_repository": null, 107 | "temp_clone_token": "ABTLWHOULUVAXGTRYU7OC2876QJ2O", 108 | "allow_squash_merge": true, 109 | "delete_branch_on_merge": true, 110 | "allow_merge_commit": true, 111 | "subscribers_count": 42, 112 | "network_count": 0, 113 | "license": { 114 | "key": "mit", 115 | "name": "MIT License", 116 | "spdx_id": "MIT", 117 | "url": "https://api.github.com/licenses/mit", 118 | "node_id": "MDc6TGljZW5zZW1pdA==" 119 | }, 120 | "organization": { 121 | "login": "octocat", 122 | "id": 1, 123 | "node_id": "MDQ6VXNlcjE=", 124 | "avatar_url": "https://github.com/images/error/octocat_happy.gif", 125 | "gravatar_id": "", 126 | "url": "https://api.github.com/users/octocat", 127 | "html_url": "https://github.com/octocat", 128 | "followers_url": "https://api.github.com/users/octocat/followers", 129 | "following_url": "https://api.github.com/users/octocat/following{/other_user}", 130 | "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", 131 | "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", 132 | "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", 133 | "organizations_url": "https://api.github.com/users/octocat/orgs", 134 | "repos_url": "https://api.github.com/users/octocat/repos", 135 | "events_url": "https://api.github.com/users/octocat/events{/privacy}", 136 | "received_events_url": "https://api.github.com/users/octocat/received_events", 137 | "type": "Organization", 138 | "site_admin": false 139 | }, 140 | "parent": { 141 | "id": 1296269, 142 | "node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5", 143 | "name": "Hello-World", 144 | "full_name": "octocat/Hello-World", 145 | "owner": { 146 | "login": "octocat", 147 | "id": 1, 148 | "node_id": "MDQ6VXNlcjE=", 149 | "avatar_url": "https://github.com/images/error/octocat_happy.gif", 150 | "gravatar_id": "", 151 | "url": "https://api.github.com/users/octocat", 152 | "html_url": "https://github.com/octocat", 153 | "followers_url": "https://api.github.com/users/octocat/followers", 154 | "following_url": "https://api.github.com/users/octocat/following{/other_user}", 155 | "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", 156 | "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", 157 | "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", 158 | "organizations_url": "https://api.github.com/users/octocat/orgs", 159 | "repos_url": "https://api.github.com/users/octocat/repos", 160 | "events_url": "https://api.github.com/users/octocat/events{/privacy}", 161 | "received_events_url": "https://api.github.com/users/octocat/received_events", 162 | "type": "User", 163 | "site_admin": false 164 | }, 165 | "private": false, 166 | "html_url": "https://github.com/octocat/Hello-World", 167 | "description": "This your first repo!", 168 | "fork": false, 169 | "url": "https://api.github.com/repos/octocat/Hello-World", 170 | "archive_url": "http://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}", 171 | "assignees_url": "http://api.github.com/repos/octocat/Hello-World/assignees{/user}", 172 | "blobs_url": "http://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}", 173 | "branches_url": "http://api.github.com/repos/octocat/Hello-World/branches{/branch}", 174 | "collaborators_url": "http://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}", 175 | "comments_url": "http://api.github.com/repos/octocat/Hello-World/comments{/number}", 176 | "commits_url": "http://api.github.com/repos/octocat/Hello-World/commits{/sha}", 177 | "compare_url": "http://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}", 178 | "contents_url": "http://api.github.com/repos/octocat/Hello-World/contents/{+path}", 179 | "contributors_url": "http://api.github.com/repos/octocat/Hello-World/contributors", 180 | "deployments_url": "http://api.github.com/repos/octocat/Hello-World/deployments", 181 | "downloads_url": "http://api.github.com/repos/octocat/Hello-World/downloads", 182 | "events_url": "http://api.github.com/repos/octocat/Hello-World/events", 183 | "forks_url": "http://api.github.com/repos/octocat/Hello-World/forks", 184 | "git_commits_url": "http://api.github.com/repos/octocat/Hello-World/git/commits{/sha}", 185 | "git_refs_url": "http://api.github.com/repos/octocat/Hello-World/git/refs{/sha}", 186 | "git_tags_url": "http://api.github.com/repos/octocat/Hello-World/git/tags{/sha}", 187 | "git_url": "git:github.com/octocat/Hello-World.git", 188 | "issue_comment_url": "http://api.github.com/repos/octocat/Hello-World/issues/comments{/number}", 189 | "issue_events_url": "http://api.github.com/repos/octocat/Hello-World/issues/events{/number}", 190 | "issues_url": "http://api.github.com/repos/octocat/Hello-World/issues{/number}", 191 | "keys_url": "http://api.github.com/repos/octocat/Hello-World/keys{/key_id}", 192 | "labels_url": "http://api.github.com/repos/octocat/Hello-World/labels{/name}", 193 | "languages_url": "http://api.github.com/repos/octocat/Hello-World/languages", 194 | "merges_url": "http://api.github.com/repos/octocat/Hello-World/merges", 195 | "milestones_url": "http://api.github.com/repos/octocat/Hello-World/milestones{/number}", 196 | "notifications_url": "http://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}", 197 | "pulls_url": "http://api.github.com/repos/octocat/Hello-World/pulls{/number}", 198 | "releases_url": "http://api.github.com/repos/octocat/Hello-World/releases{/id}", 199 | "ssh_url": "git@github.com:octocat/Hello-World.git", 200 | "stargazers_url": "http://api.github.com/repos/octocat/Hello-World/stargazers", 201 | "statuses_url": "http://api.github.com/repos/octocat/Hello-World/statuses/{sha}", 202 | "subscribers_url": "http://api.github.com/repos/octocat/Hello-World/subscribers", 203 | "subscription_url": "http://api.github.com/repos/octocat/Hello-World/subscription", 204 | "tags_url": "http://api.github.com/repos/octocat/Hello-World/tags", 205 | "teams_url": "http://api.github.com/repos/octocat/Hello-World/teams", 206 | "trees_url": "http://api.github.com/repos/octocat/Hello-World/git/trees{/sha}", 207 | "clone_url": "https://github.com/octocat/Hello-World.git", 208 | "mirror_url": "git:git.example.com/octocat/Hello-World", 209 | "hooks_url": "http://api.github.com/repos/octocat/Hello-World/hooks", 210 | "svn_url": "https://svn.github.com/octocat/Hello-World", 211 | "homepage": "https://github.com", 212 | "language": null, 213 | "forks_count": 9, 214 | "stargazers_count": 80, 215 | "watchers_count": 80, 216 | "size": 108, 217 | "default_branch": "master", 218 | "open_issues_count": 0, 219 | "is_template": true, 220 | "topics": [ 221 | "octocat", 222 | "atom", 223 | "electron", 224 | "api" 225 | ], 226 | "has_issues": true, 227 | "has_projects": true, 228 | "has_wiki": true, 229 | "has_pages": false, 230 | "has_downloads": true, 231 | "archived": false, 232 | "disabled": false, 233 | "visibility": "public", 234 | "pushed_at": "2011-01-26T19:06:43Z", 235 | "created_at": "2011-01-26T19:01:12Z", 236 | "updated_at": "2011-01-26T19:14:43Z", 237 | "permissions": { 238 | "admin": false, 239 | "push": false, 240 | "pull": true 241 | }, 242 | "allow_rebase_merge": true, 243 | "template_repository": null, 244 | "temp_clone_token": "ABTLWHOULUVAXGTRYU7OC2876QJ2O", 245 | "allow_squash_merge": true, 246 | "delete_branch_on_merge": true, 247 | "allow_merge_commit": true, 248 | "subscribers_count": 42, 249 | "network_count": 0 250 | }, 251 | "source": { 252 | "id": 1296269, 253 | "node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5", 254 | "name": "Hello-World", 255 | "full_name": "octocat/Hello-World", 256 | "owner": { 257 | "login": "octocat", 258 | "id": 1, 259 | "node_id": "MDQ6VXNlcjE=", 260 | "avatar_url": "https://github.com/images/error/octocat_happy.gif", 261 | "gravatar_id": "", 262 | "url": "https://api.github.com/users/octocat", 263 | "html_url": "https://github.com/octocat", 264 | "followers_url": "https://api.github.com/users/octocat/followers", 265 | "following_url": "https://api.github.com/users/octocat/following{/other_user}", 266 | "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", 267 | "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", 268 | "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", 269 | "organizations_url": "https://api.github.com/users/octocat/orgs", 270 | "repos_url": "https://api.github.com/users/octocat/repos", 271 | "events_url": "https://api.github.com/users/octocat/events{/privacy}", 272 | "received_events_url": "https://api.github.com/users/octocat/received_events", 273 | "type": "User", 274 | "site_admin": false 275 | }, 276 | "private": false, 277 | "html_url": "https://github.com/octocat/Hello-World", 278 | "description": "This your first repo!", 279 | "fork": false, 280 | "url": "https://api.github.com/repos/octocat/Hello-World", 281 | "archive_url": "http://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}", 282 | "assignees_url": "http://api.github.com/repos/octocat/Hello-World/assignees{/user}", 283 | "blobs_url": "http://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}", 284 | "branches_url": "http://api.github.com/repos/octocat/Hello-World/branches{/branch}", 285 | "collaborators_url": "http://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}", 286 | "comments_url": "http://api.github.com/repos/octocat/Hello-World/comments{/number}", 287 | "commits_url": "http://api.github.com/repos/octocat/Hello-World/commits{/sha}", 288 | "compare_url": "http://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}", 289 | "contents_url": "http://api.github.com/repos/octocat/Hello-World/contents/{+path}", 290 | "contributors_url": "http://api.github.com/repos/octocat/Hello-World/contributors", 291 | "deployments_url": "http://api.github.com/repos/octocat/Hello-World/deployments", 292 | "downloads_url": "http://api.github.com/repos/octocat/Hello-World/downloads", 293 | "events_url": "http://api.github.com/repos/octocat/Hello-World/events", 294 | "forks_url": "http://api.github.com/repos/octocat/Hello-World/forks", 295 | "git_commits_url": "http://api.github.com/repos/octocat/Hello-World/git/commits{/sha}", 296 | "git_refs_url": "http://api.github.com/repos/octocat/Hello-World/git/refs{/sha}", 297 | "git_tags_url": "http://api.github.com/repos/octocat/Hello-World/git/tags{/sha}", 298 | "git_url": "git:github.com/octocat/Hello-World.git", 299 | "issue_comment_url": "http://api.github.com/repos/octocat/Hello-World/issues/comments{/number}", 300 | "issue_events_url": "http://api.github.com/repos/octocat/Hello-World/issues/events{/number}", 301 | "issues_url": "http://api.github.com/repos/octocat/Hello-World/issues{/number}", 302 | "keys_url": "http://api.github.com/repos/octocat/Hello-World/keys{/key_id}", 303 | "labels_url": "http://api.github.com/repos/octocat/Hello-World/labels{/name}", 304 | "languages_url": "http://api.github.com/repos/octocat/Hello-World/languages", 305 | "merges_url": "http://api.github.com/repos/octocat/Hello-World/merges", 306 | "milestones_url": "http://api.github.com/repos/octocat/Hello-World/milestones{/number}", 307 | "notifications_url": "http://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}", 308 | "pulls_url": "http://api.github.com/repos/octocat/Hello-World/pulls{/number}", 309 | "releases_url": "http://api.github.com/repos/octocat/Hello-World/releases{/id}", 310 | "ssh_url": "git@github.com:octocat/Hello-World.git", 311 | "stargazers_url": "http://api.github.com/repos/octocat/Hello-World/stargazers", 312 | "statuses_url": "http://api.github.com/repos/octocat/Hello-World/statuses/{sha}", 313 | "subscribers_url": "http://api.github.com/repos/octocat/Hello-World/subscribers", 314 | "subscription_url": "http://api.github.com/repos/octocat/Hello-World/subscription", 315 | "tags_url": "http://api.github.com/repos/octocat/Hello-World/tags", 316 | "teams_url": "http://api.github.com/repos/octocat/Hello-World/teams", 317 | "trees_url": "http://api.github.com/repos/octocat/Hello-World/git/trees{/sha}", 318 | "clone_url": "https://github.com/octocat/Hello-World.git", 319 | "mirror_url": "git:git.example.com/octocat/Hello-World", 320 | "hooks_url": "http://api.github.com/repos/octocat/Hello-World/hooks", 321 | "svn_url": "https://svn.github.com/octocat/Hello-World", 322 | "homepage": "https://github.com", 323 | "language": null, 324 | "forks_count": 9, 325 | "stargazers_count": 80, 326 | "watchers_count": 80, 327 | "size": 108, 328 | "default_branch": "master", 329 | "open_issues_count": 0, 330 | "is_template": true, 331 | "topics": [ 332 | "octocat", 333 | "atom", 334 | "electron", 335 | "api" 336 | ], 337 | "has_issues": true, 338 | "has_projects": true, 339 | "has_wiki": true, 340 | "has_pages": false, 341 | "has_downloads": true, 342 | "archived": false, 343 | "disabled": false, 344 | "visibility": "public", 345 | "pushed_at": "2011-01-26T19:06:43Z", 346 | "created_at": "2011-01-26T19:01:12Z", 347 | "updated_at": "2011-01-26T19:14:43Z", 348 | "permissions": { 349 | "admin": false, 350 | "push": false, 351 | "pull": true 352 | }, 353 | "allow_rebase_merge": true, 354 | "template_repository": null, 355 | "temp_clone_token": "ABTLWHOULUVAXGTRYU7OC2876QJ2O", 356 | "allow_squash_merge": true, 357 | "delete_branch_on_merge": true, 358 | "allow_merge_commit": true, 359 | "subscribers_count": 42, 360 | "network_count": 0 361 | } 362 | } 363 | -------------------------------------------------------------------------------- /__tests__/fixtures/secret.txt: -------------------------------------------------------------------------------- 1 | bar 2 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | # https://help.github.com/en/articles/metadata-syntax-for-github-actions 2 | name: Build and push Docker images 3 | description: Build and push Docker images with Buildx 4 | author: docker 5 | branding: 6 | icon: 'anchor' 7 | color: 'blue' 8 | 9 | inputs: 10 | add-hosts: 11 | description: "List of a customs host-to-IP mapping (e.g., docker:10.180.0.1)" 12 | required: false 13 | allow: 14 | description: "List of extra privileged entitlement (e.g., network.host,security.insecure)" 15 | required: false 16 | annotations: 17 | description: "List of annotation to set to the image" 18 | required: false 19 | attests: 20 | description: "List of attestation parameters (e.g., type=sbom,generator=image)" 21 | required: false 22 | build-args: 23 | description: "List of build-time variables" 24 | required: false 25 | build-contexts: 26 | description: "List of additional build contexts (e.g., name=path)" 27 | required: false 28 | builder: 29 | description: "Builder instance" 30 | required: false 31 | cache-from: 32 | description: "List of external cache sources for buildx (e.g., user/app:cache, type=local,src=path/to/dir)" 33 | required: false 34 | cache-to: 35 | description: "List of cache export destinations for buildx (e.g., user/app:cache, type=local,dest=path/to/dir)" 36 | required: false 37 | call: 38 | description: "Set method for evaluating build (e.g., check)" 39 | required: false 40 | cgroup-parent: 41 | description: "Optional parent cgroup for the container used in the build" 42 | required: false 43 | context: 44 | description: "Build's context is the set of files located in the specified PATH or URL" 45 | required: false 46 | file: 47 | description: "Path to the Dockerfile" 48 | required: false 49 | labels: 50 | description: "List of metadata for an image" 51 | required: false 52 | load: 53 | description: "Load is a shorthand for --output=type=docker" 54 | required: false 55 | default: 'false' 56 | network: 57 | description: "Set the networking mode for the RUN instructions during build" 58 | required: false 59 | no-cache: 60 | description: "Do not use cache when building the image" 61 | required: false 62 | default: 'false' 63 | no-cache-filters: 64 | description: "Do not cache specified stages" 65 | required: false 66 | outputs: 67 | description: "List of output destinations (format: type=local,dest=path)" 68 | required: false 69 | platforms: 70 | description: "List of target platforms for build" 71 | required: false 72 | provenance: 73 | description: "Generate provenance attestation for the build (shorthand for --attest=type=provenance)" 74 | required: false 75 | pull: 76 | description: "Always attempt to pull all referenced images" 77 | required: false 78 | default: 'false' 79 | push: 80 | description: "Push is a shorthand for --output=type=registry" 81 | required: false 82 | default: 'false' 83 | sbom: 84 | description: "Generate SBOM attestation for the build (shorthand for --attest=type=sbom)" 85 | required: false 86 | secrets: 87 | description: "List of secrets to expose to the build (e.g., key=string, GIT_AUTH_TOKEN=mytoken)" 88 | required: false 89 | secret-envs: 90 | description: "List of secret env vars to expose to the build (e.g., key=envname, MY_SECRET=MY_ENV_VAR)" 91 | required: false 92 | secret-files: 93 | description: "List of secret files to expose to the build (e.g., key=filename, MY_SECRET=./secret.txt)" 94 | required: false 95 | shm-size: 96 | description: "Size of /dev/shm (e.g., 2g)" 97 | required: false 98 | ssh: 99 | description: "List of SSH agent socket or keys to expose to the build" 100 | required: false 101 | tags: 102 | description: "List of tags" 103 | required: false 104 | target: 105 | description: "Sets the target stage to build" 106 | required: false 107 | ulimit: 108 | description: "Ulimit options (e.g., nofile=1024:1024)" 109 | required: false 110 | github-token: 111 | description: "GitHub Token used to authenticate against a repository for Git context" 112 | default: ${{ github.token }} 113 | required: false 114 | 115 | outputs: 116 | imageid: 117 | description: 'Image ID' 118 | digest: 119 | description: 'Image digest' 120 | metadata: 121 | description: 'Build result metadata' 122 | 123 | runs: 124 | using: 'node20' 125 | main: 'dist/index.js' 126 | post: 'dist/index.js' 127 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | comment: false 2 | github_checks: 3 | annotations: false 4 | -------------------------------------------------------------------------------- /dev.Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | ARG NODE_VERSION=20 4 | 5 | FROM node:${NODE_VERSION}-alpine AS base 6 | RUN apk add --no-cache cpio findutils git 7 | WORKDIR /src 8 | RUN --mount=type=bind,target=.,rw \ 9 | --mount=type=cache,target=/src/.yarn/cache <&2 'ERROR: Vendor result differs. Please vendor your package with "docker buildx bake vendor-update"' 31 | git status --porcelain -- yarn.lock 32 | exit 1 33 | fi 34 | EOT 35 | 36 | FROM deps AS build 37 | RUN --mount=type=bind,target=.,rw \ 38 | --mount=type=cache,target=/src/.yarn/cache \ 39 | --mount=type=cache,target=/src/node_modules \ 40 | yarn run build && mkdir /out && cp -Rf dist /out/ 41 | 42 | FROM scratch AS build-update 43 | COPY --from=build /out / 44 | 45 | FROM build AS build-validate 46 | RUN --mount=type=bind,target=.,rw <&2 'ERROR: Build result differs. Please build first with "docker buildx bake build"' 52 | git status --porcelain -- dist 53 | exit 1 54 | fi 55 | EOT 56 | 57 | FROM deps AS format 58 | RUN --mount=type=bind,target=.,rw \ 59 | --mount=type=cache,target=/src/.yarn/cache \ 60 | --mount=type=cache,target=/src/node_modules \ 61 | yarn run format \ 62 | && mkdir /out && find . -name '*.ts' -not -path './node_modules/*' -not -path './.yarn/*' | cpio -pdm /out 63 | 64 | FROM scratch AS format-update 65 | COPY --from=format /out / 66 | 67 | FROM deps AS lint 68 | RUN --mount=type=bind,target=.,rw \ 69 | --mount=type=cache,target=/src/.yarn/cache \ 70 | --mount=type=cache,target=/src/node_modules \ 71 | yarn run lint 72 | 73 | FROM deps AS test 74 | RUN --mount=type=bind,target=.,rw \ 75 | --mount=type=cache,target=/src/.yarn/cache \ 76 | --mount=type=cache,target=/src/node_modules \ 77 | yarn run test --coverage --coverageDirectory=/tmp/coverage 78 | 79 | FROM scratch AS test-coverage 80 | COPY --from=test /tmp/coverage / 81 | -------------------------------------------------------------------------------- /dist/sourcemap-register.js: -------------------------------------------------------------------------------- 1 | (()=>{var e={650:e=>{var r=Object.prototype.toString;var n=typeof Buffer.alloc==="function"&&typeof Buffer.allocUnsafe==="function"&&typeof Buffer.from==="function";function isArrayBuffer(e){return r.call(e).slice(8,-1)==="ArrayBuffer"}function fromArrayBuffer(e,r,t){r>>>=0;var o=e.byteLength-r;if(o<0){throw new RangeError("'offset' is out of bounds")}if(t===undefined){t=o}else{t>>>=0;if(t>o){throw new RangeError("'length' is out of bounds")}}return n?Buffer.from(e.slice(r,r+t)):new Buffer(new Uint8Array(e.slice(r,r+t)))}function fromString(e,r){if(typeof r!=="string"||r===""){r="utf8"}if(!Buffer.isEncoding(r)){throw new TypeError('"encoding" must be a valid string encoding')}return n?Buffer.from(e,r):new Buffer(e,r)}function bufferFrom(e,r,t){if(typeof e==="number"){throw new TypeError('"value" argument must not be a number')}if(isArrayBuffer(e)){return fromArrayBuffer(e,r,t)}if(typeof e==="string"){return fromString(e,r)}return n?Buffer.from(e):new Buffer(e)}e.exports=bufferFrom},274:(e,r,n)=>{var t=n(339);var o=Object.prototype.hasOwnProperty;var i=typeof Map!=="undefined";function ArraySet(){this._array=[];this._set=i?new Map:Object.create(null)}ArraySet.fromArray=function ArraySet_fromArray(e,r){var n=new ArraySet;for(var t=0,o=e.length;t=0){return r}}else{var n=t.toSetString(e);if(o.call(this._set,n)){return this._set[n]}}throw new Error('"'+e+'" is not in the set.')};ArraySet.prototype.at=function ArraySet_at(e){if(e>=0&&e{var t=n(190);var o=5;var i=1<>1;return r?-n:n}r.encode=function base64VLQ_encode(e){var r="";var n;var i=toVLQSigned(e);do{n=i&a;i>>>=o;if(i>0){n|=u}r+=t.encode(n)}while(i>0);return r};r.decode=function base64VLQ_decode(e,r,n){var i=e.length;var s=0;var l=0;var c,p;do{if(r>=i){throw new Error("Expected more digits in base 64 VLQ value.")}p=t.decode(e.charCodeAt(r++));if(p===-1){throw new Error("Invalid base64 digit: "+e.charAt(r-1))}c=!!(p&u);p&=a;s=s+(p<{var n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".split("");r.encode=function(e){if(0<=e&&e{r.GREATEST_LOWER_BOUND=1;r.LEAST_UPPER_BOUND=2;function recursiveSearch(e,n,t,o,i,a){var u=Math.floor((n-e)/2)+e;var s=i(t,o[u],true);if(s===0){return u}else if(s>0){if(n-u>1){return recursiveSearch(u,n,t,o,i,a)}if(a==r.LEAST_UPPER_BOUND){return n1){return recursiveSearch(e,u,t,o,i,a)}if(a==r.LEAST_UPPER_BOUND){return u}else{return e<0?-1:e}}}r.search=function search(e,n,t,o){if(n.length===0){return-1}var i=recursiveSearch(-1,n.length,e,n,t,o||r.GREATEST_LOWER_BOUND);if(i<0){return-1}while(i-1>=0){if(t(n[i],n[i-1],true)!==0){break}--i}return i}},680:(e,r,n)=>{var t=n(339);function generatedPositionAfter(e,r){var n=e.generatedLine;var o=r.generatedLine;var i=e.generatedColumn;var a=r.generatedColumn;return o>n||o==n&&a>=i||t.compareByGeneratedPositionsInflated(e,r)<=0}function MappingList(){this._array=[];this._sorted=true;this._last={generatedLine:-1,generatedColumn:0}}MappingList.prototype.unsortedForEach=function MappingList_forEach(e,r){this._array.forEach(e,r)};MappingList.prototype.add=function MappingList_add(e){if(generatedPositionAfter(this._last,e)){this._last=e;this._array.push(e)}else{this._sorted=false;this._array.push(e)}};MappingList.prototype.toArray=function MappingList_toArray(){if(!this._sorted){this._array.sort(t.compareByGeneratedPositionsInflated);this._sorted=true}return this._array};r.H=MappingList},758:(e,r)=>{function swap(e,r,n){var t=e[r];e[r]=e[n];e[n]=t}function randomIntInRange(e,r){return Math.round(e+Math.random()*(r-e))}function doQuickSort(e,r,n,t){if(n{var t;var o=n(339);var i=n(345);var a=n(274).I;var u=n(449);var s=n(758).U;function SourceMapConsumer(e,r){var n=e;if(typeof e==="string"){n=o.parseSourceMapInput(e)}return n.sections!=null?new IndexedSourceMapConsumer(n,r):new BasicSourceMapConsumer(n,r)}SourceMapConsumer.fromSourceMap=function(e,r){return BasicSourceMapConsumer.fromSourceMap(e,r)};SourceMapConsumer.prototype._version=3;SourceMapConsumer.prototype.__generatedMappings=null;Object.defineProperty(SourceMapConsumer.prototype,"_generatedMappings",{configurable:true,enumerable:true,get:function(){if(!this.__generatedMappings){this._parseMappings(this._mappings,this.sourceRoot)}return this.__generatedMappings}});SourceMapConsumer.prototype.__originalMappings=null;Object.defineProperty(SourceMapConsumer.prototype,"_originalMappings",{configurable:true,enumerable:true,get:function(){if(!this.__originalMappings){this._parseMappings(this._mappings,this.sourceRoot)}return this.__originalMappings}});SourceMapConsumer.prototype._charIsMappingSeparator=function SourceMapConsumer_charIsMappingSeparator(e,r){var n=e.charAt(r);return n===";"||n===","};SourceMapConsumer.prototype._parseMappings=function SourceMapConsumer_parseMappings(e,r){throw new Error("Subclasses must implement _parseMappings")};SourceMapConsumer.GENERATED_ORDER=1;SourceMapConsumer.ORIGINAL_ORDER=2;SourceMapConsumer.GREATEST_LOWER_BOUND=1;SourceMapConsumer.LEAST_UPPER_BOUND=2;SourceMapConsumer.prototype.eachMapping=function SourceMapConsumer_eachMapping(e,r,n){var t=r||null;var i=n||SourceMapConsumer.GENERATED_ORDER;var a;switch(i){case SourceMapConsumer.GENERATED_ORDER:a=this._generatedMappings;break;case SourceMapConsumer.ORIGINAL_ORDER:a=this._originalMappings;break;default:throw new Error("Unknown order of iteration.")}var u=this.sourceRoot;a.map((function(e){var r=e.source===null?null:this._sources.at(e.source);r=o.computeSourceURL(u,r,this._sourceMapURL);return{source:r,generatedLine:e.generatedLine,generatedColumn:e.generatedColumn,originalLine:e.originalLine,originalColumn:e.originalColumn,name:e.name===null?null:this._names.at(e.name)}}),this).forEach(e,t)};SourceMapConsumer.prototype.allGeneratedPositionsFor=function SourceMapConsumer_allGeneratedPositionsFor(e){var r=o.getArg(e,"line");var n={source:o.getArg(e,"source"),originalLine:r,originalColumn:o.getArg(e,"column",0)};n.source=this._findSourceIndex(n.source);if(n.source<0){return[]}var t=[];var a=this._findMapping(n,this._originalMappings,"originalLine","originalColumn",o.compareByOriginalPositions,i.LEAST_UPPER_BOUND);if(a>=0){var u=this._originalMappings[a];if(e.column===undefined){var s=u.originalLine;while(u&&u.originalLine===s){t.push({line:o.getArg(u,"generatedLine",null),column:o.getArg(u,"generatedColumn",null),lastColumn:o.getArg(u,"lastGeneratedColumn",null)});u=this._originalMappings[++a]}}else{var l=u.originalColumn;while(u&&u.originalLine===r&&u.originalColumn==l){t.push({line:o.getArg(u,"generatedLine",null),column:o.getArg(u,"generatedColumn",null),lastColumn:o.getArg(u,"lastGeneratedColumn",null)});u=this._originalMappings[++a]}}}return t};r.SourceMapConsumer=SourceMapConsumer;function BasicSourceMapConsumer(e,r){var n=e;if(typeof e==="string"){n=o.parseSourceMapInput(e)}var t=o.getArg(n,"version");var i=o.getArg(n,"sources");var u=o.getArg(n,"names",[]);var s=o.getArg(n,"sourceRoot",null);var l=o.getArg(n,"sourcesContent",null);var c=o.getArg(n,"mappings");var p=o.getArg(n,"file",null);if(t!=this._version){throw new Error("Unsupported version: "+t)}if(s){s=o.normalize(s)}i=i.map(String).map(o.normalize).map((function(e){return s&&o.isAbsolute(s)&&o.isAbsolute(e)?o.relative(s,e):e}));this._names=a.fromArray(u.map(String),true);this._sources=a.fromArray(i,true);this._absoluteSources=this._sources.toArray().map((function(e){return o.computeSourceURL(s,e,r)}));this.sourceRoot=s;this.sourcesContent=l;this._mappings=c;this._sourceMapURL=r;this.file=p}BasicSourceMapConsumer.prototype=Object.create(SourceMapConsumer.prototype);BasicSourceMapConsumer.prototype.consumer=SourceMapConsumer;BasicSourceMapConsumer.prototype._findSourceIndex=function(e){var r=e;if(this.sourceRoot!=null){r=o.relative(this.sourceRoot,r)}if(this._sources.has(r)){return this._sources.indexOf(r)}var n;for(n=0;n1){v.source=l+_[1];l+=_[1];v.originalLine=i+_[2];i=v.originalLine;v.originalLine+=1;v.originalColumn=a+_[3];a=v.originalColumn;if(_.length>4){v.name=c+_[4];c+=_[4]}}m.push(v);if(typeof v.originalLine==="number"){d.push(v)}}}s(m,o.compareByGeneratedPositionsDeflated);this.__generatedMappings=m;s(d,o.compareByOriginalPositions);this.__originalMappings=d};BasicSourceMapConsumer.prototype._findMapping=function SourceMapConsumer_findMapping(e,r,n,t,o,a){if(e[n]<=0){throw new TypeError("Line must be greater than or equal to 1, got "+e[n])}if(e[t]<0){throw new TypeError("Column must be greater than or equal to 0, got "+e[t])}return i.search(e,r,o,a)};BasicSourceMapConsumer.prototype.computeColumnSpans=function SourceMapConsumer_computeColumnSpans(){for(var e=0;e=0){var t=this._generatedMappings[n];if(t.generatedLine===r.generatedLine){var i=o.getArg(t,"source",null);if(i!==null){i=this._sources.at(i);i=o.computeSourceURL(this.sourceRoot,i,this._sourceMapURL)}var a=o.getArg(t,"name",null);if(a!==null){a=this._names.at(a)}return{source:i,line:o.getArg(t,"originalLine",null),column:o.getArg(t,"originalColumn",null),name:a}}}return{source:null,line:null,column:null,name:null}};BasicSourceMapConsumer.prototype.hasContentsOfAllSources=function BasicSourceMapConsumer_hasContentsOfAllSources(){if(!this.sourcesContent){return false}return this.sourcesContent.length>=this._sources.size()&&!this.sourcesContent.some((function(e){return e==null}))};BasicSourceMapConsumer.prototype.sourceContentFor=function SourceMapConsumer_sourceContentFor(e,r){if(!this.sourcesContent){return null}var n=this._findSourceIndex(e);if(n>=0){return this.sourcesContent[n]}var t=e;if(this.sourceRoot!=null){t=o.relative(this.sourceRoot,t)}var i;if(this.sourceRoot!=null&&(i=o.urlParse(this.sourceRoot))){var a=t.replace(/^file:\/\//,"");if(i.scheme=="file"&&this._sources.has(a)){return this.sourcesContent[this._sources.indexOf(a)]}if((!i.path||i.path=="/")&&this._sources.has("/"+t)){return this.sourcesContent[this._sources.indexOf("/"+t)]}}if(r){return null}else{throw new Error('"'+t+'" is not in the SourceMap.')}};BasicSourceMapConsumer.prototype.generatedPositionFor=function SourceMapConsumer_generatedPositionFor(e){var r=o.getArg(e,"source");r=this._findSourceIndex(r);if(r<0){return{line:null,column:null,lastColumn:null}}var n={source:r,originalLine:o.getArg(e,"line"),originalColumn:o.getArg(e,"column")};var t=this._findMapping(n,this._originalMappings,"originalLine","originalColumn",o.compareByOriginalPositions,o.getArg(e,"bias",SourceMapConsumer.GREATEST_LOWER_BOUND));if(t>=0){var i=this._originalMappings[t];if(i.source===n.source){return{line:o.getArg(i,"generatedLine",null),column:o.getArg(i,"generatedColumn",null),lastColumn:o.getArg(i,"lastGeneratedColumn",null)}}}return{line:null,column:null,lastColumn:null}};t=BasicSourceMapConsumer;function IndexedSourceMapConsumer(e,r){var n=e;if(typeof e==="string"){n=o.parseSourceMapInput(e)}var t=o.getArg(n,"version");var i=o.getArg(n,"sections");if(t!=this._version){throw new Error("Unsupported version: "+t)}this._sources=new a;this._names=new a;var u={line:-1,column:0};this._sections=i.map((function(e){if(e.url){throw new Error("Support for url field in sections not implemented.")}var n=o.getArg(e,"offset");var t=o.getArg(n,"line");var i=o.getArg(n,"column");if(t{var t=n(449);var o=n(339);var i=n(274).I;var a=n(680).H;function SourceMapGenerator(e){if(!e){e={}}this._file=o.getArg(e,"file",null);this._sourceRoot=o.getArg(e,"sourceRoot",null);this._skipValidation=o.getArg(e,"skipValidation",false);this._sources=new i;this._names=new i;this._mappings=new a;this._sourcesContents=null}SourceMapGenerator.prototype._version=3;SourceMapGenerator.fromSourceMap=function SourceMapGenerator_fromSourceMap(e){var r=e.sourceRoot;var n=new SourceMapGenerator({file:e.file,sourceRoot:r});e.eachMapping((function(e){var t={generated:{line:e.generatedLine,column:e.generatedColumn}};if(e.source!=null){t.source=e.source;if(r!=null){t.source=o.relative(r,t.source)}t.original={line:e.originalLine,column:e.originalColumn};if(e.name!=null){t.name=e.name}}n.addMapping(t)}));e.sources.forEach((function(t){var i=t;if(r!==null){i=o.relative(r,t)}if(!n._sources.has(i)){n._sources.add(i)}var a=e.sourceContentFor(t);if(a!=null){n.setSourceContent(t,a)}}));return n};SourceMapGenerator.prototype.addMapping=function SourceMapGenerator_addMapping(e){var r=o.getArg(e,"generated");var n=o.getArg(e,"original",null);var t=o.getArg(e,"source",null);var i=o.getArg(e,"name",null);if(!this._skipValidation){this._validateMapping(r,n,t,i)}if(t!=null){t=String(t);if(!this._sources.has(t)){this._sources.add(t)}}if(i!=null){i=String(i);if(!this._names.has(i)){this._names.add(i)}}this._mappings.add({generatedLine:r.line,generatedColumn:r.column,originalLine:n!=null&&n.line,originalColumn:n!=null&&n.column,source:t,name:i})};SourceMapGenerator.prototype.setSourceContent=function SourceMapGenerator_setSourceContent(e,r){var n=e;if(this._sourceRoot!=null){n=o.relative(this._sourceRoot,n)}if(r!=null){if(!this._sourcesContents){this._sourcesContents=Object.create(null)}this._sourcesContents[o.toSetString(n)]=r}else if(this._sourcesContents){delete this._sourcesContents[o.toSetString(n)];if(Object.keys(this._sourcesContents).length===0){this._sourcesContents=null}}};SourceMapGenerator.prototype.applySourceMap=function SourceMapGenerator_applySourceMap(e,r,n){var t=r;if(r==null){if(e.file==null){throw new Error("SourceMapGenerator.prototype.applySourceMap requires either an explicit source file, "+'or the source map\'s "file" property. Both were omitted.')}t=e.file}var a=this._sourceRoot;if(a!=null){t=o.relative(a,t)}var u=new i;var s=new i;this._mappings.unsortedForEach((function(r){if(r.source===t&&r.originalLine!=null){var i=e.originalPositionFor({line:r.originalLine,column:r.originalColumn});if(i.source!=null){r.source=i.source;if(n!=null){r.source=o.join(n,r.source)}if(a!=null){r.source=o.relative(a,r.source)}r.originalLine=i.line;r.originalColumn=i.column;if(i.name!=null){r.name=i.name}}}var l=r.source;if(l!=null&&!u.has(l)){u.add(l)}var c=r.name;if(c!=null&&!s.has(c)){s.add(c)}}),this);this._sources=u;this._names=s;e.sources.forEach((function(r){var t=e.sourceContentFor(r);if(t!=null){if(n!=null){r=o.join(n,r)}if(a!=null){r=o.relative(a,r)}this.setSourceContent(r,t)}}),this)};SourceMapGenerator.prototype._validateMapping=function SourceMapGenerator_validateMapping(e,r,n,t){if(r&&typeof r.line!=="number"&&typeof r.column!=="number"){throw new Error("original.line and original.column are not numbers -- you probably meant to omit "+"the original mapping entirely and only map the generated position. If so, pass "+"null for the original mapping instead of an object with empty or null values.")}if(e&&"line"in e&&"column"in e&&e.line>0&&e.column>=0&&!r&&!n&&!t){return}else if(e&&"line"in e&&"column"in e&&r&&"line"in r&&"column"in r&&e.line>0&&e.column>=0&&r.line>0&&r.column>=0&&n){return}else{throw new Error("Invalid mapping: "+JSON.stringify({generated:e,source:n,original:r,name:t}))}};SourceMapGenerator.prototype._serializeMappings=function SourceMapGenerator_serializeMappings(){var e=0;var r=1;var n=0;var i=0;var a=0;var u=0;var s="";var l;var c;var p;var f;var g=this._mappings.toArray();for(var h=0,d=g.length;h0){if(!o.compareByGeneratedPositionsInflated(c,g[h-1])){continue}l+=","}}l+=t.encode(c.generatedColumn-e);e=c.generatedColumn;if(c.source!=null){f=this._sources.indexOf(c.source);l+=t.encode(f-u);u=f;l+=t.encode(c.originalLine-1-i);i=c.originalLine-1;l+=t.encode(c.originalColumn-n);n=c.originalColumn;if(c.name!=null){p=this._names.indexOf(c.name);l+=t.encode(p-a);a=p}}s+=l}return s};SourceMapGenerator.prototype._generateSourcesContent=function SourceMapGenerator_generateSourcesContent(e,r){return e.map((function(e){if(!this._sourcesContents){return null}if(r!=null){e=o.relative(r,e)}var n=o.toSetString(e);return Object.prototype.hasOwnProperty.call(this._sourcesContents,n)?this._sourcesContents[n]:null}),this)};SourceMapGenerator.prototype.toJSON=function SourceMapGenerator_toJSON(){var e={version:this._version,sources:this._sources.toArray(),names:this._names.toArray(),mappings:this._serializeMappings()};if(this._file!=null){e.file=this._file}if(this._sourceRoot!=null){e.sourceRoot=this._sourceRoot}if(this._sourcesContents){e.sourcesContent=this._generateSourcesContent(e.sources,e.sourceRoot)}return e};SourceMapGenerator.prototype.toString=function SourceMapGenerator_toString(){return JSON.stringify(this.toJSON())};r.h=SourceMapGenerator},351:(e,r,n)=>{var t;var o=n(591).h;var i=n(339);var a=/(\r?\n)/;var u=10;var s="$$$isSourceNode$$$";function SourceNode(e,r,n,t,o){this.children=[];this.sourceContents={};this.line=e==null?null:e;this.column=r==null?null:r;this.source=n==null?null:n;this.name=o==null?null:o;this[s]=true;if(t!=null)this.add(t)}SourceNode.fromStringWithSourceMap=function SourceNode_fromStringWithSourceMap(e,r,n){var t=new SourceNode;var o=e.split(a);var u=0;var shiftNextLine=function(){var e=getNextLine();var r=getNextLine()||"";return e+r;function getNextLine(){return u=0;r--){this.prepend(e[r])}}else if(e[s]||typeof e==="string"){this.children.unshift(e)}else{throw new TypeError("Expected a SourceNode, string, or an array of SourceNodes and strings. Got "+e)}return this};SourceNode.prototype.walk=function SourceNode_walk(e){var r;for(var n=0,t=this.children.length;n0){r=[];for(n=0;n{function getArg(e,r,n){if(r in e){return e[r]}else if(arguments.length===3){return n}else{throw new Error('"'+r+'" is a required argument.')}}r.getArg=getArg;var n=/^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.-]*)(?::(\d+))?(.*)$/;var t=/^data:.+\,.+$/;function urlParse(e){var r=e.match(n);if(!r){return null}return{scheme:r[1],auth:r[2],host:r[3],port:r[4],path:r[5]}}r.urlParse=urlParse;function urlGenerate(e){var r="";if(e.scheme){r+=e.scheme+":"}r+="//";if(e.auth){r+=e.auth+"@"}if(e.host){r+=e.host}if(e.port){r+=":"+e.port}if(e.path){r+=e.path}return r}r.urlGenerate=urlGenerate;function normalize(e){var n=e;var t=urlParse(e);if(t){if(!t.path){return e}n=t.path}var o=r.isAbsolute(n);var i=n.split(/\/+/);for(var a,u=0,s=i.length-1;s>=0;s--){a=i[s];if(a==="."){i.splice(s,1)}else if(a===".."){u++}else if(u>0){if(a===""){i.splice(s+1,u);u=0}else{i.splice(s,2);u--}}}n=i.join("/");if(n===""){n=o?"/":"."}if(t){t.path=n;return urlGenerate(t)}return n}r.normalize=normalize;function join(e,r){if(e===""){e="."}if(r===""){r="."}var n=urlParse(r);var o=urlParse(e);if(o){e=o.path||"/"}if(n&&!n.scheme){if(o){n.scheme=o.scheme}return urlGenerate(n)}if(n||r.match(t)){return r}if(o&&!o.host&&!o.path){o.host=r;return urlGenerate(o)}var i=r.charAt(0)==="/"?r:normalize(e.replace(/\/+$/,"")+"/"+r);if(o){o.path=i;return urlGenerate(o)}return i}r.join=join;r.isAbsolute=function(e){return e.charAt(0)==="/"||n.test(e)};function relative(e,r){if(e===""){e="."}e=e.replace(/\/$/,"");var n=0;while(r.indexOf(e+"/")!==0){var t=e.lastIndexOf("/");if(t<0){return r}e=e.slice(0,t);if(e.match(/^([^\/]+:\/)?\/*$/)){return r}++n}return Array(n+1).join("../")+r.substr(e.length+1)}r.relative=relative;var o=function(){var e=Object.create(null);return!("__proto__"in e)}();function identity(e){return e}function toSetString(e){if(isProtoString(e)){return"$"+e}return e}r.toSetString=o?identity:toSetString;function fromSetString(e){if(isProtoString(e)){return e.slice(1)}return e}r.fromSetString=o?identity:fromSetString;function isProtoString(e){if(!e){return false}var r=e.length;if(r<9){return false}if(e.charCodeAt(r-1)!==95||e.charCodeAt(r-2)!==95||e.charCodeAt(r-3)!==111||e.charCodeAt(r-4)!==116||e.charCodeAt(r-5)!==111||e.charCodeAt(r-6)!==114||e.charCodeAt(r-7)!==112||e.charCodeAt(r-8)!==95||e.charCodeAt(r-9)!==95){return false}for(var n=r-10;n>=0;n--){if(e.charCodeAt(n)!==36){return false}}return true}function compareByOriginalPositions(e,r,n){var t=strcmp(e.source,r.source);if(t!==0){return t}t=e.originalLine-r.originalLine;if(t!==0){return t}t=e.originalColumn-r.originalColumn;if(t!==0||n){return t}t=e.generatedColumn-r.generatedColumn;if(t!==0){return t}t=e.generatedLine-r.generatedLine;if(t!==0){return t}return strcmp(e.name,r.name)}r.compareByOriginalPositions=compareByOriginalPositions;function compareByGeneratedPositionsDeflated(e,r,n){var t=e.generatedLine-r.generatedLine;if(t!==0){return t}t=e.generatedColumn-r.generatedColumn;if(t!==0||n){return t}t=strcmp(e.source,r.source);if(t!==0){return t}t=e.originalLine-r.originalLine;if(t!==0){return t}t=e.originalColumn-r.originalColumn;if(t!==0){return t}return strcmp(e.name,r.name)}r.compareByGeneratedPositionsDeflated=compareByGeneratedPositionsDeflated;function strcmp(e,r){if(e===r){return 0}if(e===null){return 1}if(r===null){return-1}if(e>r){return 1}return-1}function compareByGeneratedPositionsInflated(e,r){var n=e.generatedLine-r.generatedLine;if(n!==0){return n}n=e.generatedColumn-r.generatedColumn;if(n!==0){return n}n=strcmp(e.source,r.source);if(n!==0){return n}n=e.originalLine-r.originalLine;if(n!==0){return n}n=e.originalColumn-r.originalColumn;if(n!==0){return n}return strcmp(e.name,r.name)}r.compareByGeneratedPositionsInflated=compareByGeneratedPositionsInflated;function parseSourceMapInput(e){return JSON.parse(e.replace(/^\)]}'[^\n]*\n/,""))}r.parseSourceMapInput=parseSourceMapInput;function computeSourceURL(e,r,n){r=r||"";if(e){if(e[e.length-1]!=="/"&&r[0]!=="/"){e+="/"}r=e+r}if(n){var t=urlParse(n);if(!t){throw new Error("sourceMapURL could not be parsed")}if(t.path){var o=t.path.lastIndexOf("/");if(o>=0){t.path=t.path.substring(0,o+1)}}r=join(urlGenerate(t),r)}return normalize(r)}r.computeSourceURL=computeSourceURL},997:(e,r,n)=>{n(591).h;r.SourceMapConsumer=n(952).SourceMapConsumer;n(351)},284:(e,r,n)=>{e=n.nmd(e);var t=n(997).SourceMapConsumer;var o=n(17);var i;try{i=n(147);if(!i.existsSync||!i.readFileSync){i=null}}catch(e){}var a=n(650);function dynamicRequire(e,r){return e.require(r)}var u=false;var s=false;var l=false;var c="auto";var p={};var f={};var g=/^data:application\/json[^,]+base64,/;var h=[];var d=[];function isInBrowser(){if(c==="browser")return true;if(c==="node")return false;return typeof window!=="undefined"&&typeof XMLHttpRequest==="function"&&!(window.require&&window.module&&window.process&&window.process.type==="renderer")}function hasGlobalProcessEventEmitter(){return typeof process==="object"&&process!==null&&typeof process.on==="function"}function globalProcessVersion(){if(typeof process==="object"&&process!==null){return process.version}else{return""}}function globalProcessStderr(){if(typeof process==="object"&&process!==null){return process.stderr}}function globalProcessExit(e){if(typeof process==="object"&&process!==null&&typeof process.exit==="function"){return process.exit(e)}}function handlerExec(e){return function(r){for(var n=0;n"}var n=this.getLineNumber();if(n!=null){r+=":"+n;var t=this.getColumnNumber();if(t){r+=":"+t}}}var o="";var i=this.getFunctionName();var a=true;var u=this.isConstructor();var s=!(this.isToplevel()||u);if(s){var l=this.getTypeName();if(l==="[object Object]"){l="null"}var c=this.getMethodName();if(i){if(l&&i.indexOf(l)!=0){o+=l+"."}o+=i;if(c&&i.indexOf("."+c)!=i.length-c.length-1){o+=" [as "+c+"]"}}else{o+=l+"."+(c||"")}}else if(u){o+="new "+(i||"")}else if(i){o+=i}else{o+=r;a=false}if(a){o+=" ("+r+")"}return o}function cloneCallSite(e){var r={};Object.getOwnPropertyNames(Object.getPrototypeOf(e)).forEach((function(n){r[n]=/^(?:is|get)/.test(n)?function(){return e[n].call(e)}:e[n]}));r.toString=CallSiteToString;return r}function wrapCallSite(e,r){if(r===undefined){r={nextPosition:null,curPosition:null}}if(e.isNative()){r.curPosition=null;return e}var n=e.getFileName()||e.getScriptNameOrSourceURL();if(n){var t=e.getLineNumber();var o=e.getColumnNumber()-1;var i=/^v(10\.1[6-9]|10\.[2-9][0-9]|10\.[0-9]{3,}|1[2-9]\d*|[2-9]\d|\d{3,}|11\.11)/;var a=i.test(globalProcessVersion())?0:62;if(t===1&&o>a&&!isInBrowser()&&!e.isEval()){o-=a}var u=mapSourcePosition({source:n,line:t,column:o});r.curPosition=u;e=cloneCallSite(e);var s=e.getFunctionName;e.getFunctionName=function(){if(r.nextPosition==null){return s()}return r.nextPosition.name||s()};e.getFileName=function(){return u.source};e.getLineNumber=function(){return u.line};e.getColumnNumber=function(){return u.column+1};e.getScriptNameOrSourceURL=function(){return u.source};return e}var l=e.isEval()&&e.getEvalOrigin();if(l){l=mapEvalOrigin(l);e=cloneCallSite(e);e.getEvalOrigin=function(){return l};return e}return e}function prepareStackTrace(e,r){if(l){p={};f={}}var n=e.name||"Error";var t=e.message||"";var o=n+": "+t;var i={nextPosition:null,curPosition:null};var a=[];for(var u=r.length-1;u>=0;u--){a.push("\n at "+wrapCallSite(r[u],i));i.nextPosition=i.curPosition}i.curPosition=i.nextPosition=null;return o+a.reverse().join("")}function getErrorSource(e){var r=/\n at [^(]+ \((.*):(\d+):(\d+)\)/.exec(e.stack);if(r){var n=r[1];var t=+r[2];var o=+r[3];var a=p[n];if(!a&&i&&i.existsSync(n)){try{a=i.readFileSync(n,"utf8")}catch(e){a=""}}if(a){var u=a.split(/(?:\r\n|\r|\n)/)[t-1];if(u){return n+":"+t+"\n"+u+"\n"+new Array(o).join(" ")+"^"}}}return null}function printErrorAndExit(e){var r=getErrorSource(e);var n=globalProcessStderr();if(n&&n._handle&&n._handle.setBlocking){n._handle.setBlocking(true)}if(r){console.error();console.error(r)}console.error(e.stack);globalProcessExit(1)}function shimEmitUncaughtException(){var e=process.emit;process.emit=function(r){if(r==="uncaughtException"){var n=arguments[1]&&arguments[1].stack;var t=this.listeners(r).length>0;if(n&&!t){return printErrorAndExit(arguments[1])}}return e.apply(this,arguments)}}var S=h.slice(0);var _=d.slice(0);r.wrapCallSite=wrapCallSite;r.getErrorSource=getErrorSource;r.mapSourcePosition=mapSourcePosition;r.retrieveSourceMap=v;r.install=function(r){r=r||{};if(r.environment){c=r.environment;if(["node","browser","auto"].indexOf(c)===-1){throw new Error("environment "+c+" was unknown. Available options are {auto, browser, node}")}}if(r.retrieveFile){if(r.overrideRetrieveFile){h.length=0}h.unshift(r.retrieveFile)}if(r.retrieveSourceMap){if(r.overrideRetrieveSourceMap){d.length=0}d.unshift(r.retrieveSourceMap)}if(r.hookRequire&&!isInBrowser()){var n=dynamicRequire(e,"module");var t=n.prototype._compile;if(!t.__sourceMapSupport){n.prototype._compile=function(e,r){p[r]=e;f[r]=undefined;return t.call(this,e,r)};n.prototype._compile.__sourceMapSupport=true}}if(!l){l="emptyCacheBetweenOperations"in r?r.emptyCacheBetweenOperations:false}if(!u){u=true;Error.prepareStackTrace=prepareStackTrace}if(!s){var o="handleUncaughtExceptions"in r?r.handleUncaughtExceptions:true;try{var i=dynamicRequire(e,"worker_threads");if(i.isMainThread===false){o=false}}catch(e){}if(o&&hasGlobalProcessEventEmitter()){s=true;shimEmitUncaughtException()}}};r.resetRetrieveHandlers=function(){h.length=0;d.length=0;h=S.slice(0);d=_.slice(0);v=handlerExec(d);m=handlerExec(h)}},147:e=>{"use strict";e.exports=require("fs")},17:e=>{"use strict";e.exports=require("path")}};var r={};function __webpack_require__(n){var t=r[n];if(t!==undefined){return t.exports}var o=r[n]={id:n,loaded:false,exports:{}};var i=true;try{e[n](o,o.exports,__webpack_require__);i=false}finally{if(i)delete r[n]}o.loaded=true;return o.exports}(()=>{__webpack_require__.nmd=e=>{e.paths=[];if(!e.children)e.children=[];return e}})();if(typeof __webpack_require__!=="undefined")__webpack_require__.ab=__dirname+"/";var n={};(()=>{__webpack_require__(284).install()})();module.exports=n})(); -------------------------------------------------------------------------------- /docker-bake.hcl: -------------------------------------------------------------------------------- 1 | target "_common" { 2 | args = { 3 | BUILDKIT_CONTEXT_KEEP_GIT_DIR = 1 4 | } 5 | } 6 | 7 | group "default" { 8 | targets = ["build"] 9 | } 10 | 11 | group "pre-checkin" { 12 | targets = ["vendor", "format", "build"] 13 | } 14 | 15 | group "validate" { 16 | targets = ["lint", "build-validate", "vendor-validate"] 17 | } 18 | 19 | target "build" { 20 | inherits = ["_common"] 21 | dockerfile = "dev.Dockerfile" 22 | target = "build-update" 23 | output = ["."] 24 | } 25 | 26 | target "build-validate" { 27 | inherits = ["_common"] 28 | dockerfile = "dev.Dockerfile" 29 | target = "build-validate" 30 | output = ["type=cacheonly"] 31 | } 32 | 33 | target "format" { 34 | inherits = ["_common"] 35 | dockerfile = "dev.Dockerfile" 36 | target = "format-update" 37 | output = ["."] 38 | } 39 | 40 | target "lint" { 41 | inherits = ["_common"] 42 | dockerfile = "dev.Dockerfile" 43 | target = "lint" 44 | output = ["type=cacheonly"] 45 | } 46 | 47 | target "vendor" { 48 | inherits = ["_common"] 49 | dockerfile = "dev.Dockerfile" 50 | target = "vendor-update" 51 | output = ["."] 52 | } 53 | 54 | target "vendor-validate" { 55 | inherits = ["_common"] 56 | dockerfile = "dev.Dockerfile" 57 | target = "vendor-validate" 58 | output = ["type=cacheonly"] 59 | } 60 | 61 | target "test" { 62 | inherits = ["_common"] 63 | dockerfile = "dev.Dockerfile" 64 | target = "test-coverage" 65 | output = ["./coverage"] 66 | } 67 | -------------------------------------------------------------------------------- /jest.config.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import os from 'os'; 3 | import path from 'path'; 4 | 5 | const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'docker-build-push-action-')); 6 | 7 | process.env = Object.assign({}, process.env, { 8 | TEMP: tmpDir, 9 | GITHUB_REPOSITORY: 'docker/build-push-action', 10 | RUNNER_TEMP: path.join(tmpDir, 'runner-temp'), 11 | RUNNER_TOOL_CACHE: path.join(tmpDir, 'runner-tool-cache') 12 | }) as { 13 | [key: string]: string; 14 | }; 15 | 16 | module.exports = { 17 | clearMocks: false, 18 | testEnvironment: 'node', 19 | moduleFileExtensions: ['js', 'ts'], 20 | testMatch: ['**/*.test.ts'], 21 | transform: { 22 | '^.+\\.ts$': 'ts-jest' 23 | }, 24 | moduleNameMapper: { 25 | '^csv-parse/sync': '/node_modules/csv-parse/dist/cjs/sync.cjs' 26 | }, 27 | collectCoverageFrom: ['src/**/{!(main.ts),}.ts'], 28 | coveragePathIgnorePatterns: ['lib/', 'node_modules/', '__mocks__/', '__tests__/'], 29 | verbose: true 30 | }; 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docker-build-push", 3 | "description": "Build and push Docker images", 4 | "main": "src/main.ts", 5 | "scripts": { 6 | "build": "ncc build --source-map --minify --license licenses.txt", 7 | "lint": "yarn run prettier && yarn run eslint", 8 | "format": "yarn run prettier:fix && yarn run eslint:fix", 9 | "eslint": "eslint --max-warnings=0 .", 10 | "eslint:fix": "eslint --fix .", 11 | "prettier": "prettier --check \"./**/*.ts\"", 12 | "prettier:fix": "prettier --write \"./**/*.ts\"", 13 | "test": "jest" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/docker/build-push-action.git" 18 | }, 19 | "keywords": [ 20 | "actions", 21 | "docker", 22 | "build", 23 | "push" 24 | ], 25 | "author": "Docker Inc.", 26 | "license": "Apache-2.0", 27 | "packageManager": "yarn@3.6.3", 28 | "dependencies": { 29 | "@actions/core": "^1.11.1", 30 | "@docker/actions-toolkit": "0.62.1", 31 | "handlebars": "^4.7.7" 32 | }, 33 | "devDependencies": { 34 | "@types/node": "^20.12.12", 35 | "@typescript-eslint/eslint-plugin": "^7.9.0", 36 | "@typescript-eslint/parser": "^7.9.0", 37 | "@vercel/ncc": "^0.38.1", 38 | "eslint": "^8.57.0", 39 | "eslint-config-prettier": "^9.1.0", 40 | "eslint-plugin-jest": "^28.5.0", 41 | "eslint-plugin-prettier": "^5.1.3", 42 | "jest": "^29.7.0", 43 | "prettier": "^3.2.5", 44 | "ts-jest": "^29.1.2", 45 | "ts-node": "^10.9.2", 46 | "typescript": "^5.4.5" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/context.ts: -------------------------------------------------------------------------------- 1 | import * as core from '@actions/core'; 2 | import * as handlebars from 'handlebars'; 3 | 4 | import {Build} from '@docker/actions-toolkit/lib/buildx/build'; 5 | import {Context} from '@docker/actions-toolkit/lib/context'; 6 | import {GitHub} from '@docker/actions-toolkit/lib/github'; 7 | import {Toolkit} from '@docker/actions-toolkit/lib/toolkit'; 8 | import {Util} from '@docker/actions-toolkit/lib/util'; 9 | 10 | export interface Inputs { 11 | 'add-hosts': string[]; 12 | allow: string[]; 13 | annotations: string[]; 14 | attests: string[]; 15 | 'build-args': string[]; 16 | 'build-contexts': string[]; 17 | builder: string; 18 | 'cache-from': string[]; 19 | 'cache-to': string[]; 20 | call: string; 21 | 'cgroup-parent': string; 22 | context: string; 23 | file: string; 24 | labels: string[]; 25 | load: boolean; 26 | network: string; 27 | 'no-cache': boolean; 28 | 'no-cache-filters': string[]; 29 | outputs: string[]; 30 | platforms: string[]; 31 | provenance: string; 32 | pull: boolean; 33 | push: boolean; 34 | sbom: string; 35 | secrets: string[]; 36 | 'secret-envs': string[]; 37 | 'secret-files': string[]; 38 | 'shm-size': string; 39 | ssh: string[]; 40 | tags: string[]; 41 | target: string; 42 | ulimit: string[]; 43 | 'github-token': string; 44 | } 45 | 46 | export async function getInputs(): Promise { 47 | return { 48 | 'add-hosts': Util.getInputList('add-hosts'), 49 | allow: Util.getInputList('allow'), 50 | annotations: Util.getInputList('annotations', {ignoreComma: true}), 51 | attests: Util.getInputList('attests', {ignoreComma: true}), 52 | 'build-args': Util.getInputList('build-args', {ignoreComma: true}), 53 | 'build-contexts': Util.getInputList('build-contexts', {ignoreComma: true}), 54 | builder: core.getInput('builder'), 55 | 'cache-from': Util.getInputList('cache-from', {ignoreComma: true}), 56 | 'cache-to': Util.getInputList('cache-to', {ignoreComma: true}), 57 | call: core.getInput('call'), 58 | 'cgroup-parent': core.getInput('cgroup-parent'), 59 | context: core.getInput('context') || Context.gitContext(), 60 | file: core.getInput('file'), 61 | labels: Util.getInputList('labels', {ignoreComma: true}), 62 | load: core.getBooleanInput('load'), 63 | network: core.getInput('network'), 64 | 'no-cache': core.getBooleanInput('no-cache'), 65 | 'no-cache-filters': Util.getInputList('no-cache-filters'), 66 | outputs: Util.getInputList('outputs', {ignoreComma: true, quote: false}), 67 | platforms: Util.getInputList('platforms'), 68 | provenance: Build.getProvenanceInput('provenance'), 69 | pull: core.getBooleanInput('pull'), 70 | push: core.getBooleanInput('push'), 71 | sbom: core.getInput('sbom'), 72 | secrets: Util.getInputList('secrets', {ignoreComma: true}), 73 | 'secret-envs': Util.getInputList('secret-envs'), 74 | 'secret-files': Util.getInputList('secret-files', {ignoreComma: true}), 75 | 'shm-size': core.getInput('shm-size'), 76 | ssh: Util.getInputList('ssh'), 77 | tags: Util.getInputList('tags'), 78 | target: core.getInput('target'), 79 | ulimit: Util.getInputList('ulimit', {ignoreComma: true}), 80 | 'github-token': core.getInput('github-token') 81 | }; 82 | } 83 | 84 | export async function getArgs(inputs: Inputs, toolkit: Toolkit): Promise> { 85 | const context = handlebars.compile(inputs.context)({ 86 | defaultContext: Context.gitContext() 87 | }); 88 | // prettier-ignore 89 | return [ 90 | ...await getBuildArgs(inputs, context, toolkit), 91 | ...await getCommonArgs(inputs, toolkit), 92 | context 93 | ]; 94 | } 95 | 96 | async function getBuildArgs(inputs: Inputs, context: string, toolkit: Toolkit): Promise> { 97 | const args: Array = ['build']; 98 | await Util.asyncForEach(inputs['add-hosts'], async addHost => { 99 | args.push('--add-host', addHost); 100 | }); 101 | await Util.asyncForEach(inputs.allow, async allow => { 102 | args.push('--allow', allow); 103 | }); 104 | if (await toolkit.buildx.versionSatisfies('>=0.12.0')) { 105 | await Util.asyncForEach(inputs.annotations, async annotation => { 106 | args.push('--annotation', annotation); 107 | }); 108 | } else if (inputs.annotations.length > 0) { 109 | core.warning("Annotations are only supported by buildx >= 0.12.0; the input 'annotations' is ignored."); 110 | } 111 | await Util.asyncForEach(inputs['build-args'], async buildArg => { 112 | args.push('--build-arg', buildArg); 113 | }); 114 | if (await toolkit.buildx.versionSatisfies('>=0.8.0')) { 115 | await Util.asyncForEach(inputs['build-contexts'], async buildContext => { 116 | args.push( 117 | '--build-context', 118 | handlebars.compile(buildContext)({ 119 | defaultContext: Context.gitContext() 120 | }) 121 | ); 122 | }); 123 | } else if (inputs['build-contexts'].length > 0) { 124 | core.warning("Build contexts are only supported by buildx >= 0.8.0; the input 'build-contexts' is ignored."); 125 | } 126 | await Util.asyncForEach(inputs['cache-from'], async cacheFrom => { 127 | args.push('--cache-from', cacheFrom); 128 | }); 129 | await Util.asyncForEach(inputs['cache-to'], async cacheTo => { 130 | args.push('--cache-to', cacheTo); 131 | }); 132 | if (inputs.call) { 133 | if (!(await toolkit.buildx.versionSatisfies('>=0.15.0'))) { 134 | throw new Error(`Buildx >= 0.15.0 is required to use the call flag.`); 135 | } 136 | args.push('--call', inputs.call); 137 | } 138 | if (inputs['cgroup-parent']) { 139 | args.push('--cgroup-parent', inputs['cgroup-parent']); 140 | } 141 | await Util.asyncForEach(inputs['secret-envs'], async secretEnv => { 142 | try { 143 | args.push('--secret', Build.resolveSecretEnv(secretEnv)); 144 | } catch (err) { 145 | core.warning(err.message); 146 | } 147 | }); 148 | if (inputs.file) { 149 | args.push('--file', inputs.file); 150 | } 151 | if (!Build.hasLocalExporter(inputs.outputs) && !Build.hasTarExporter(inputs.outputs) && (inputs.platforms.length == 0 || (await toolkit.buildx.versionSatisfies('>=0.4.2')))) { 152 | args.push('--iidfile', toolkit.buildxBuild.getImageIDFilePath()); 153 | } 154 | await Util.asyncForEach(inputs.labels, async label => { 155 | args.push('--label', label); 156 | }); 157 | await Util.asyncForEach(inputs['no-cache-filters'], async noCacheFilter => { 158 | args.push('--no-cache-filter', noCacheFilter); 159 | }); 160 | await Util.asyncForEach(inputs.outputs, async output => { 161 | args.push('--output', output); 162 | }); 163 | if (inputs.platforms.length > 0) { 164 | args.push('--platform', inputs.platforms.join(',')); 165 | } 166 | if (await toolkit.buildx.versionSatisfies('>=0.10.0')) { 167 | args.push(...(await getAttestArgs(inputs, toolkit))); 168 | } else { 169 | core.warning("Attestations are only supported by buildx >= 0.10.0; the inputs 'attests', 'provenance' and 'sbom' are ignored."); 170 | } 171 | await Util.asyncForEach(inputs.secrets, async secret => { 172 | try { 173 | args.push('--secret', Build.resolveSecretString(secret)); 174 | } catch (err) { 175 | core.warning(err.message); 176 | } 177 | }); 178 | await Util.asyncForEach(inputs['secret-files'], async secretFile => { 179 | try { 180 | args.push('--secret', Build.resolveSecretFile(secretFile)); 181 | } catch (err) { 182 | core.warning(err.message); 183 | } 184 | }); 185 | if (inputs['github-token'] && !Build.hasGitAuthTokenSecret(inputs.secrets) && context.startsWith(Context.gitContext())) { 186 | args.push('--secret', Build.resolveSecretString(`GIT_AUTH_TOKEN=${inputs['github-token']}`)); 187 | } 188 | if (inputs['shm-size']) { 189 | args.push('--shm-size', inputs['shm-size']); 190 | } 191 | await Util.asyncForEach(inputs.ssh, async ssh => { 192 | args.push('--ssh', ssh); 193 | }); 194 | await Util.asyncForEach(inputs.tags, async tag => { 195 | args.push('--tag', tag); 196 | }); 197 | if (inputs.target) { 198 | args.push('--target', inputs.target); 199 | } 200 | await Util.asyncForEach(inputs.ulimit, async ulimit => { 201 | args.push('--ulimit', ulimit); 202 | }); 203 | return args; 204 | } 205 | 206 | async function getCommonArgs(inputs: Inputs, toolkit: Toolkit): Promise> { 207 | const args: Array = []; 208 | if (inputs.builder) { 209 | args.push('--builder', inputs.builder); 210 | } 211 | if (inputs.load) { 212 | args.push('--load'); 213 | } 214 | if (await toolkit.buildx.versionSatisfies('>=0.6.0')) { 215 | args.push('--metadata-file', toolkit.buildxBuild.getMetadataFilePath()); 216 | } 217 | if (inputs.network) { 218 | args.push('--network', inputs.network); 219 | } 220 | if (inputs['no-cache']) { 221 | args.push('--no-cache'); 222 | } 223 | if (inputs.pull) { 224 | args.push('--pull'); 225 | } 226 | if (inputs.push) { 227 | args.push('--push'); 228 | } 229 | return args; 230 | } 231 | 232 | async function getAttestArgs(inputs: Inputs, toolkit: Toolkit): Promise> { 233 | const args: Array = []; 234 | 235 | // check if provenance attestation is set in attests input 236 | let hasAttestProvenance = false; 237 | await Util.asyncForEach(inputs.attests, async (attest: string) => { 238 | if (Build.hasAttestationType('provenance', attest)) { 239 | hasAttestProvenance = true; 240 | } 241 | }); 242 | 243 | let provenanceSet = false; 244 | let sbomSet = false; 245 | if (inputs.provenance) { 246 | args.push('--attest', Build.resolveAttestationAttrs(`type=provenance,${inputs.provenance}`)); 247 | provenanceSet = true; 248 | } else if (!hasAttestProvenance && !noDefaultAttestations() && (await toolkit.buildkit.versionSatisfies(inputs.builder, '>=0.11.0')) && !Build.hasDockerExporter(inputs.outputs, inputs.load)) { 249 | // if provenance not specified in provenance or attests inputs and BuildKit 250 | // version compatible for attestation, set default provenance. Also needs 251 | // to make sure user doesn't want to explicitly load the image to docker. 252 | if (GitHub.context.payload.repository?.private ?? false) { 253 | // if this is a private repository, we set the default provenance 254 | // attributes being set in buildx: https://github.com/docker/buildx/blob/fb27e3f919dcbf614d7126b10c2bc2d0b1927eb6/build/build.go#L603 255 | args.push('--attest', `type=provenance,${Build.resolveProvenanceAttrs(`mode=min,inline-only=true`)}`); 256 | } else { 257 | // for a public repository, we set max provenance mode. 258 | args.push('--attest', `type=provenance,${Build.resolveProvenanceAttrs(`mode=max`)}`); 259 | } 260 | } 261 | if (inputs.sbom) { 262 | args.push('--attest', Build.resolveAttestationAttrs(`type=sbom,${inputs.sbom}`)); 263 | sbomSet = true; 264 | } 265 | 266 | // set attests but check if provenance or sbom types already set as 267 | // provenance and sbom inputs take precedence over attests input. 268 | await Util.asyncForEach(inputs.attests, async (attest: string) => { 269 | if (!Build.hasAttestationType('provenance', attest) && !Build.hasAttestationType('sbom', attest)) { 270 | args.push('--attest', Build.resolveAttestationAttrs(attest)); 271 | } else if (!provenanceSet && Build.hasAttestationType('provenance', attest)) { 272 | args.push('--attest', Build.resolveProvenanceAttrs(attest)); 273 | } else if (!sbomSet && Build.hasAttestationType('sbom', attest)) { 274 | args.push('--attest', attest); 275 | } 276 | }); 277 | 278 | return args; 279 | } 280 | 281 | function noDefaultAttestations(): boolean { 282 | if (process.env.BUILDX_NO_DEFAULT_ATTESTATIONS) { 283 | return Util.parseBool(process.env.BUILDX_NO_DEFAULT_ATTESTATIONS); 284 | } 285 | return false; 286 | } 287 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as path from 'path'; 3 | import * as stateHelper from './state-helper'; 4 | import * as core from '@actions/core'; 5 | import * as actionsToolkit from '@docker/actions-toolkit'; 6 | 7 | import {Buildx} from '@docker/actions-toolkit/lib/buildx/buildx'; 8 | import {History as BuildxHistory} from '@docker/actions-toolkit/lib/buildx/history'; 9 | import {Context} from '@docker/actions-toolkit/lib/context'; 10 | import {Docker} from '@docker/actions-toolkit/lib/docker/docker'; 11 | import {Exec} from '@docker/actions-toolkit/lib/exec'; 12 | import {GitHub} from '@docker/actions-toolkit/lib/github'; 13 | import {Toolkit} from '@docker/actions-toolkit/lib/toolkit'; 14 | import {Util} from '@docker/actions-toolkit/lib/util'; 15 | 16 | import {BuilderInfo} from '@docker/actions-toolkit/lib/types/buildx/builder'; 17 | import {ConfigFile} from '@docker/actions-toolkit/lib/types/docker/docker'; 18 | import {UploadArtifactResponse} from '@docker/actions-toolkit/lib/types/github'; 19 | 20 | import * as context from './context'; 21 | 22 | actionsToolkit.run( 23 | // main 24 | async () => { 25 | const startedTime = new Date(); 26 | const inputs: context.Inputs = await context.getInputs(); 27 | stateHelper.setSummaryInputs(inputs); 28 | core.debug(`inputs: ${JSON.stringify(inputs)}`); 29 | 30 | const toolkit = new Toolkit(); 31 | 32 | await core.group(`GitHub Actions runtime token ACs`, async () => { 33 | try { 34 | await GitHub.printActionsRuntimeTokenACs(); 35 | } catch (e) { 36 | core.warning(e.message); 37 | } 38 | }); 39 | 40 | await core.group(`Docker info`, async () => { 41 | try { 42 | await Docker.printVersion(); 43 | await Docker.printInfo(); 44 | } catch (e) { 45 | core.info(e.message); 46 | } 47 | }); 48 | 49 | await core.group(`Proxy configuration`, async () => { 50 | let dockerConfig: ConfigFile | undefined; 51 | let dockerConfigMalformed = false; 52 | try { 53 | dockerConfig = await Docker.configFile(); 54 | } catch (e) { 55 | dockerConfigMalformed = true; 56 | core.warning(`Unable to parse config file ${path.join(Docker.configDir, 'config.json')}: ${e}`); 57 | } 58 | if (dockerConfig && dockerConfig.proxies) { 59 | for (const host in dockerConfig.proxies) { 60 | let prefix = ''; 61 | if (Object.keys(dockerConfig.proxies).length > 1) { 62 | prefix = ' '; 63 | core.info(host); 64 | } 65 | for (const key in dockerConfig.proxies[host]) { 66 | core.info(`${prefix}${key}: ${dockerConfig.proxies[host][key]}`); 67 | } 68 | } 69 | } else if (!dockerConfigMalformed) { 70 | core.info('No proxy configuration found'); 71 | } 72 | }); 73 | 74 | if (!(await toolkit.buildx.isAvailable())) { 75 | core.setFailed(`Docker buildx is required. See https://github.com/docker/setup-buildx-action to set up buildx.`); 76 | return; 77 | } 78 | 79 | stateHelper.setTmpDir(Context.tmpDir()); 80 | 81 | await core.group(`Buildx version`, async () => { 82 | await toolkit.buildx.printVersion(); 83 | }); 84 | 85 | let builder: BuilderInfo; 86 | await core.group(`Builder info`, async () => { 87 | builder = await toolkit.builder.inspect(inputs.builder); 88 | stateHelper.setBuilderDriver(builder.driver ?? ''); 89 | stateHelper.setBuilderEndpoint(builder.nodes?.[0]?.endpoint ?? ''); 90 | core.info(JSON.stringify(builder, null, 2)); 91 | }); 92 | 93 | const args: string[] = await context.getArgs(inputs, toolkit); 94 | core.debug(`context.getArgs: ${JSON.stringify(args)}`); 95 | 96 | const buildCmd = await toolkit.buildx.getCommand(args); 97 | core.debug(`buildCmd.command: ${buildCmd.command}`); 98 | core.debug(`buildCmd.args: ${JSON.stringify(buildCmd.args)}`); 99 | 100 | let err: Error | undefined; 101 | await Exec.getExecOutput(buildCmd.command, buildCmd.args, { 102 | ignoreReturnCode: true, 103 | env: Object.assign({}, process.env, { 104 | BUILDX_METADATA_WARNINGS: 'true' 105 | }) as { 106 | [key: string]: string; 107 | } 108 | }).then(res => { 109 | if (res.exitCode != 0) { 110 | if (inputs.call && inputs.call === 'check' && res.stdout.length > 0) { 111 | // checks warnings are printed to stdout: https://github.com/docker/buildx/pull/2647 112 | // take the first line with the message summaryzing the warnings 113 | err = new Error(res.stdout.split('\n')[0]?.trim()); 114 | } else if (res.stderr.length > 0) { 115 | err = new Error(`buildx failed with: ${res.stderr.match(/(.*)\s*$/)?.[0]?.trim() ?? 'unknown error'}`); 116 | } 117 | } 118 | }); 119 | 120 | const imageID = toolkit.buildxBuild.resolveImageID(); 121 | const metadata = toolkit.buildxBuild.resolveMetadata(); 122 | const digest = toolkit.buildxBuild.resolveDigest(metadata); 123 | if (imageID) { 124 | await core.group(`ImageID`, async () => { 125 | core.info(imageID); 126 | core.setOutput('imageid', imageID); 127 | }); 128 | } 129 | if (digest) { 130 | await core.group(`Digest`, async () => { 131 | core.info(digest); 132 | core.setOutput('digest', digest); 133 | }); 134 | } 135 | if (metadata) { 136 | await core.group(`Metadata`, async () => { 137 | const metadatadt = JSON.stringify(metadata, null, 2); 138 | core.info(metadatadt); 139 | core.setOutput('metadata', metadatadt); 140 | }); 141 | } 142 | 143 | let ref: string | undefined; 144 | await core.group(`Reference`, async () => { 145 | ref = await buildRef(toolkit, startedTime, inputs.builder); 146 | if (ref) { 147 | core.info(ref); 148 | stateHelper.setBuildRef(ref); 149 | } else { 150 | core.info('No build reference found'); 151 | } 152 | }); 153 | 154 | if (buildChecksAnnotationsEnabled()) { 155 | const warnings = toolkit.buildxBuild.resolveWarnings(metadata); 156 | if (ref && warnings && warnings.length > 0) { 157 | const annotations = await Buildx.convertWarningsToGitHubAnnotations(warnings, [ref]); 158 | core.debug(`annotations: ${JSON.stringify(annotations, null, 2)}`); 159 | if (annotations && annotations.length > 0) { 160 | await core.group(`Generating GitHub annotations (${annotations.length} build checks found)`, async () => { 161 | for (const annotation of annotations) { 162 | core.warning(annotation.message, annotation); 163 | } 164 | }); 165 | } 166 | } 167 | } 168 | 169 | await core.group(`Check build summary support`, async () => { 170 | if (!buildSummaryEnabled()) { 171 | core.info('Build summary disabled'); 172 | } else if (inputs.call && inputs.call !== 'build') { 173 | core.info(`Build summary skipped for ${inputs.call} subrequest`); 174 | } else if (GitHub.isGHES) { 175 | core.info('Build summary is not yet supported on GHES'); 176 | } else if (!(await toolkit.buildx.versionSatisfies('>=0.13.0'))) { 177 | core.info('Build summary requires Buildx >= 0.13.0'); 178 | } else if (!ref) { 179 | core.info('Build summary requires a build reference'); 180 | } else { 181 | core.info('Build summary supported!'); 182 | stateHelper.setSummarySupported(); 183 | } 184 | }); 185 | 186 | if (err) { 187 | throw err; 188 | } 189 | }, 190 | // post 191 | async () => { 192 | if (stateHelper.isSummarySupported) { 193 | await core.group(`Generating build summary`, async () => { 194 | try { 195 | const recordUploadEnabled = buildRecordUploadEnabled(); 196 | let recordRetentionDays: number | undefined; 197 | if (recordUploadEnabled) { 198 | recordRetentionDays = buildRecordRetentionDays(); 199 | } 200 | 201 | const buildxHistory = new BuildxHistory(); 202 | const exportRes = await buildxHistory.export({ 203 | refs: stateHelper.buildRef ? [stateHelper.buildRef] : [], 204 | useContainer: buildExportLegacy() 205 | }); 206 | core.info(`Build record written to ${exportRes.dockerbuildFilename} (${Util.formatFileSize(exportRes.dockerbuildSize)})`); 207 | 208 | let uploadRes: UploadArtifactResponse | undefined; 209 | if (recordUploadEnabled) { 210 | uploadRes = await GitHub.uploadArtifact({ 211 | filename: exportRes.dockerbuildFilename, 212 | mimeType: 'application/gzip', 213 | retentionDays: recordRetentionDays 214 | }); 215 | } 216 | 217 | await GitHub.writeBuildSummary({ 218 | exportRes: exportRes, 219 | uploadRes: uploadRes, 220 | inputs: stateHelper.summaryInputs, 221 | driver: stateHelper.builderDriver, 222 | endpoint: stateHelper.builderEndpoint 223 | }); 224 | } catch (e) { 225 | core.warning(e.message); 226 | } 227 | }); 228 | } 229 | if (stateHelper.tmpDir.length > 0) { 230 | await core.group(`Removing temp folder ${stateHelper.tmpDir}`, async () => { 231 | try { 232 | fs.rmSync(stateHelper.tmpDir, {recursive: true}); 233 | } catch (e) { 234 | core.warning(`Failed to remove temp folder ${stateHelper.tmpDir}`); 235 | } 236 | }); 237 | } 238 | } 239 | ); 240 | 241 | async function buildRef(toolkit: Toolkit, since: Date, builder?: string): Promise { 242 | // get ref from metadata file 243 | const ref = toolkit.buildxBuild.resolveRef(); 244 | if (ref) { 245 | return ref; 246 | } 247 | // otherwise, look for the very first build ref since the build has started 248 | if (!builder) { 249 | const currentBuilder = await toolkit.builder.inspect(); 250 | builder = currentBuilder.name; 251 | } 252 | const refs = Buildx.refs({ 253 | dir: Buildx.refsDir, 254 | builderName: builder, 255 | since: since 256 | }); 257 | return Object.keys(refs).length > 0 ? Object.keys(refs)[0] : ''; 258 | } 259 | 260 | function buildChecksAnnotationsEnabled(): boolean { 261 | if (process.env.DOCKER_BUILD_CHECKS_ANNOTATIONS) { 262 | return Util.parseBool(process.env.DOCKER_BUILD_CHECKS_ANNOTATIONS); 263 | } 264 | return true; 265 | } 266 | 267 | function buildSummaryEnabled(): boolean { 268 | if (process.env.DOCKER_BUILD_NO_SUMMARY) { 269 | core.warning('DOCKER_BUILD_NO_SUMMARY is deprecated. Set DOCKER_BUILD_SUMMARY to false instead.'); 270 | return !Util.parseBool(process.env.DOCKER_BUILD_NO_SUMMARY); 271 | } else if (process.env.DOCKER_BUILD_SUMMARY) { 272 | return Util.parseBool(process.env.DOCKER_BUILD_SUMMARY); 273 | } 274 | return true; 275 | } 276 | 277 | function buildRecordUploadEnabled(): boolean { 278 | if (process.env.DOCKER_BUILD_RECORD_UPLOAD) { 279 | return Util.parseBool(process.env.DOCKER_BUILD_RECORD_UPLOAD); 280 | } 281 | return true; 282 | } 283 | 284 | function buildRecordRetentionDays(): number | undefined { 285 | let val: string | undefined; 286 | if (process.env.DOCKER_BUILD_EXPORT_RETENTION_DAYS) { 287 | core.warning('DOCKER_BUILD_EXPORT_RETENTION_DAYS is deprecated. Use DOCKER_BUILD_RECORD_RETENTION_DAYS instead.'); 288 | val = process.env.DOCKER_BUILD_EXPORT_RETENTION_DAYS; 289 | } else if (process.env.DOCKER_BUILD_RECORD_RETENTION_DAYS) { 290 | val = process.env.DOCKER_BUILD_RECORD_RETENTION_DAYS; 291 | } 292 | if (val) { 293 | const res = parseInt(val); 294 | if (isNaN(res)) { 295 | throw new Error(`Invalid build record retention days: ${val}`); 296 | } 297 | return res; 298 | } 299 | } 300 | 301 | function buildExportLegacy(): boolean { 302 | if (process.env.DOCKER_BUILD_EXPORT_LEGACY) { 303 | return Util.parseBool(process.env.DOCKER_BUILD_EXPORT_LEGACY); 304 | } 305 | return false; 306 | } 307 | -------------------------------------------------------------------------------- /src/state-helper.ts: -------------------------------------------------------------------------------- 1 | import * as core from '@actions/core'; 2 | 3 | import {Build} from '@docker/actions-toolkit/lib/buildx/build'; 4 | 5 | import {Inputs} from './context'; 6 | 7 | export const tmpDir = process.env['STATE_tmpDir'] || ''; 8 | 9 | export const builderDriver = process.env['STATE_builderDriver'] || ''; 10 | export const builderEndpoint = process.env['STATE_builderEndpoint'] || ''; 11 | export const summaryInputs = process.env['STATE_summaryInputs'] ? JSON.parse(process.env['STATE_summaryInputs']) : undefined; 12 | 13 | export const buildRef = process.env['STATE_buildRef'] || ''; 14 | export const isSummarySupported = !!process.env['STATE_isSummarySupported']; 15 | 16 | export function setTmpDir(tmpDir: string) { 17 | core.saveState('tmpDir', tmpDir); 18 | } 19 | 20 | export function setBuilderDriver(builderDriver: string) { 21 | core.saveState('builderDriver', builderDriver); 22 | } 23 | 24 | export function setBuilderEndpoint(builderEndpoint: string) { 25 | core.saveState('builderEndpoint', builderEndpoint); 26 | } 27 | 28 | export function setBuildRef(buildRef: string) { 29 | core.saveState('buildRef', buildRef); 30 | } 31 | 32 | export function setSummarySupported() { 33 | core.saveState('isSummarySupported', 'true'); 34 | } 35 | 36 | export function setSummaryInputs(inputs: Inputs) { 37 | const res = {}; 38 | for (const key of Object.keys(inputs)) { 39 | if (key === 'github-token') { 40 | continue; 41 | } 42 | const value: string | string[] | boolean = inputs[key]; 43 | if (typeof value === 'boolean' && !value) { 44 | continue; 45 | } else if (Array.isArray(value)) { 46 | if (value.length === 0) { 47 | continue; 48 | } else if (key === 'secrets' && value.length > 0) { 49 | const secretKeys: string[] = []; 50 | for (const secret of value) { 51 | try { 52 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 53 | const [skey, _] = Build.parseSecretKvp(secret, true); 54 | secretKeys.push(skey); 55 | } catch (err) { 56 | // ignore invalid secret 57 | } 58 | } 59 | if (secretKeys.length > 0) { 60 | res[key] = secretKeys; 61 | } 62 | continue; 63 | } 64 | } else if (!value) { 65 | continue; 66 | } 67 | res[key] = value; 68 | } 69 | core.saveState('summaryInputs', JSON.stringify(res)); 70 | } 71 | -------------------------------------------------------------------------------- /test/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | FROM alpine 3 | RUN echo "Hello world!" 4 | -------------------------------------------------------------------------------- /test/addhost.Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | FROM busybox 3 | RUN cat /etc/hosts 4 | -------------------------------------------------------------------------------- /test/cgroup.Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | FROM alpine 3 | RUN cat /proc/self/cgroup 4 | -------------------------------------------------------------------------------- /test/go/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM golang:alpine AS base 4 | ENV CGO_ENABLED=0 5 | RUN apk add --no-cache file git 6 | WORKDIR /src 7 | 8 | FROM base AS build 9 | RUN --mount=type=bind,target=/src \ 10 | --mount=type=cache,target=/root/.cache/go-build \ 11 | go build -ldflags "-s -w" -o /usr/bin/app . 12 | 13 | FROM scratch AS binary 14 | COPY --from=build /usr/bin/app /bin/app 15 | 16 | FROM alpine AS image 17 | COPY --from=build /usr/bin/app /bin/app 18 | EXPOSE 8080 19 | ENTRYPOINT ["/bin/app"] 20 | -------------------------------------------------------------------------------- /test/go/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/docker/build-push-action/test/go 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /test/go/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | ) 8 | 9 | func main() { 10 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 11 | fmt.Fprintf(w, "Hello, Go!") 12 | }) 13 | log.Fatal(http.ListenAndServe(":8080", nil)) 14 | } 15 | -------------------------------------------------------------------------------- /test/lint.Dockerfile: -------------------------------------------------------------------------------- 1 | frOM busybox as base 2 | cOpy lint.Dockerfile . 3 | 4 | from scratch 5 | MAINTAINER moby@example.com 6 | COPy --from=base \ 7 | /lint.Dockerfile \ 8 | / 9 | 10 | CMD [ "echo", "Hello, Norway!" ] 11 | CMD [ "echo", "Hello, Sweden!" ] 12 | ENTRYPOINT my-program start 13 | -------------------------------------------------------------------------------- /test/multi-sudo.Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM --platform=$BUILDPLATFORM alpine AS build 4 | ARG TARGETPLATFORM 5 | ARG BUILDPLATFORM 6 | RUN echo "I am running on $BUILDPLATFORM, building for $TARGETPLATFORM" > /log 7 | RUN apk --update --no-cache add \ 8 | shadow \ 9 | sudo \ 10 | && addgroup -g 1200 buildx \ 11 | && adduser -u 1200 -G buildx -s /sbin/nologin -D buildx \ 12 | && echo 'buildx ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers \ 13 | && rm -rf /tmp/* /var/cache/apk/* 14 | 15 | USER buildx 16 | RUN sudo chown buildx: /log 17 | USER root 18 | 19 | FROM alpine 20 | COPY --from=build /log /log 21 | RUN ls -al /log 22 | -------------------------------------------------------------------------------- /test/multi.Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | FROM --platform=$BUILDPLATFORM golang:alpine AS build 3 | 4 | ARG TARGETPLATFORM 5 | ARG BUILDPLATFORM 6 | RUN echo "I am running on $BUILDPLATFORM, building for $TARGETPLATFORM" > /log 7 | 8 | FROM alpine 9 | COPY --from=build /log /log 10 | -------------------------------------------------------------------------------- /test/named-context-base.Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM debian 4 | RUN echo "Hello debian!" 5 | -------------------------------------------------------------------------------- /test/named-context.Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM alpine 4 | RUN cat /etc/*release 5 | -------------------------------------------------------------------------------- /test/nocachefilter.Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | FROM busybox AS base 3 | RUN echo "Hello world!" > /hello 4 | 5 | FROM alpine AS build 6 | COPY --from=base /hello /hello 7 | RUN uname -a 8 | 9 | FROM build 10 | -------------------------------------------------------------------------------- /test/proxy.Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | FROM alpine 3 | RUN apk add --no-cache curl net-tools 4 | ARG HTTP_PROXY 5 | ARG HTTPS_PROXY 6 | RUN printenv HTTP_PROXY 7 | RUN printenv HTTPS_PROXY 8 | RUN netstat -aptn 9 | RUN curl --retry 5 --retry-all-errors --retry-delay 0 --connect-timeout 5 --proxy $HTTP_PROXY -v --insecure --head https://www.google.com 10 | -------------------------------------------------------------------------------- /test/secret.Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | FROM busybox 3 | RUN --mount=type=secret,id=MYSECRET \ 4 | echo "MYSECRET=$(cat /run/secrets/MYSECRET)" 5 | -------------------------------------------------------------------------------- /test/shmsize.Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | FROM busybox 3 | RUN mount | grep /dev/shm 4 | -------------------------------------------------------------------------------- /test/ulimit.Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | FROM busybox 3 | RUN ulimit -a 4 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "esModuleInterop": true, 4 | "target": "es6", 5 | "module": "commonjs", 6 | "strict": true, 7 | "newLine": "lf", 8 | "outDir": "./lib", 9 | "rootDir": "./src", 10 | "forceConsistentCasingInFileNames": true, 11 | "noImplicitAny": false, 12 | "resolveJsonModule": true, 13 | "useUnknownInCatchVariables": false, 14 | }, 15 | "exclude": [ 16 | "./__mocks__/**/*", 17 | "./__tests__/**/*", 18 | "./lib/**/*", 19 | "node_modules", 20 | "jest.config.ts" 21 | ] 22 | } 23 | --------------------------------------------------------------------------------