├── .dockerignore ├── .editorconfig ├── .eslintignore ├── .eslintrc.json ├── .gitattributes ├── .github ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── bug.yml │ ├── config.yml │ └── feature.yml ├── SECURITY.md ├── bake-action.png ├── bake-summary.png ├── dependabot.yml ├── subaction-list-targets.png └── workflows │ ├── ci-subaction.yml │ ├── ci.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 ├── __mocks__ └── @actions │ └── github.ts ├── __tests__ ├── context.test.ts └── fixtures │ └── github-repo.json ├── 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 ├── subaction └── list-targets │ ├── README.md │ └── action.yml ├── test ├── Dockerfile ├── config.hcl ├── go │ ├── Dockerfile │ ├── docker-bake.hcl │ ├── go.mod │ └── main.go ├── group-matrix │ └── docker-bake.hcl ├── group │ ├── Dockerfile │ └── docker-bake.hcl ├── lint-other.Dockerfile ├── lint.Dockerfile ├── lint.hcl ├── multi-files │ ├── docker-bake.hcl │ └── docker-bake.json └── proxy.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": "latest", 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/bake-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/bake-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 | - Write tests. 23 | - Make sure the `README.md` and any other relevant **documentation are kept up-to-date**. 24 | - We try to follow [SemVer v2.0.0](https://semver.org/). Randomly breaking public APIs is not an option. 25 | - 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**. 26 | - Write a [good commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html). 27 | 28 | ## Resources 29 | 30 | - [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/) 31 | - [Using Pull Requests](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests) 32 | - [GitHub Help](https://docs.github.com/en) 33 | -------------------------------------------------------------------------------- /.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 | 14 | - type: checkboxes 15 | attributes: 16 | label: Contributing guidelines 17 | description: > 18 | Make sure you've read the contributing guidelines before proceeding. 19 | options: 20 | - label: I've read the [contributing guidelines](https://github.com/docker/bake-action/blob/master/.github/CONTRIBUTING.md) and wholeheartedly agree 21 | required: true 22 | 23 | - type: checkboxes 24 | attributes: 25 | label: "I've found a bug, and:" 26 | description: | 27 | Make sure that your request fulfills all of the following requirements. 28 | If one requirement cannot be satisfied, explain in detail why. 29 | options: 30 | - label: The documentation does not mention anything about my problem 31 | - label: There are no open or closed issues that are related to my problem 32 | 33 | - type: textarea 34 | attributes: 35 | label: Description 36 | description: > 37 | Provide a brief description of the bug in 1-2 sentences. 38 | validations: 39 | required: true 40 | 41 | - type: textarea 42 | attributes: 43 | label: Expected behaviour 44 | description: > 45 | Describe precisely what you'd expect to happen. 46 | validations: 47 | required: true 48 | 49 | - type: textarea 50 | attributes: 51 | label: Actual behaviour 52 | description: > 53 | Describe precisely what is actually happening. 54 | validations: 55 | required: true 56 | 57 | - type: input 58 | attributes: 59 | label: Repository URL 60 | description: > 61 | Enter the URL of the repository where you are experiencing the 62 | issue. If your repository is private, provide a link to a minimal 63 | repository that reproduces the issue. 64 | 65 | - type: input 66 | attributes: 67 | label: Workflow run URL 68 | description: > 69 | Enter the URL of the GitHub Action workflow run if public (e.g. 70 | `https://github.com///actions/runs/`) 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/bake-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/bake-action.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/bake-action/37816e747588cb137173af99ab33873600c46ea8/.github/bake-action.png -------------------------------------------------------------------------------- /.github/bake-summary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/bake-action/37816e747588cb137173af99ab33873600c46ea8/.github/bake-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/subaction-list-targets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/bake-action/37816e747588cb137173af99ab33873600c46ea8/.github/subaction-list-targets.png -------------------------------------------------------------------------------- /.github/workflows/ci-subaction.yml: -------------------------------------------------------------------------------- 1 | name: ci-subaction 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 | - 'releases/v*' 15 | tags: 16 | - 'v*' 17 | paths: 18 | - '.github/workflows/ci-subaction.yml' 19 | - 'subaction/**' 20 | - 'test/**' 21 | pull_request: 22 | paths: 23 | - '.github/workflows/ci-subaction.yml' 24 | - 'subaction/**' 25 | - 'test/**' 26 | 27 | jobs: 28 | list-targets-group: 29 | runs-on: ubuntu-latest 30 | steps: 31 | - 32 | name: Checkout 33 | uses: actions/checkout@v4 34 | - 35 | name: Matrix gen 36 | id: gen 37 | uses: ./subaction/list-targets 38 | with: 39 | workdir: ./test/group 40 | - 41 | name: Check targets 42 | uses: actions/github-script@v7 43 | with: 44 | script: | 45 | const targets = `${{ steps.gen.outputs.targets }}`; 46 | if (!targets) { 47 | core.setFailed('No targets generated'); 48 | } 49 | core.info(`targets=${targets}`); 50 | 51 | list-targets-group-matrix: 52 | runs-on: ubuntu-latest 53 | steps: 54 | - 55 | name: Checkout 56 | uses: actions/checkout@v4 57 | - 58 | name: Matrix gen 59 | id: gen 60 | uses: ./subaction/list-targets 61 | with: 62 | workdir: ./test/group-matrix 63 | target: validate 64 | - 65 | name: Check targets 66 | uses: actions/github-script@v7 67 | with: 68 | script: | 69 | const targets = `${{ steps.gen.outputs.targets }}`; 70 | if (!targets) { 71 | core.setFailed('No targets generated'); 72 | } 73 | core.info(`targets=${targets}`); 74 | 75 | list-targets-multi-files: 76 | runs-on: ubuntu-latest 77 | steps: 78 | - 79 | name: Checkout 80 | uses: actions/checkout@v4 81 | - 82 | name: Matrix gen 83 | id: gen 84 | uses: ./subaction/list-targets 85 | with: 86 | workdir: ./test/multi-files 87 | files: | 88 | docker-bake.json 89 | docker-bake.hcl 90 | - 91 | name: Check targets 92 | uses: actions/github-script@v7 93 | with: 94 | script: | 95 | const targets = `${{ steps.gen.outputs.targets }}`; 96 | if (!targets) { 97 | core.setFailed('No targets generated'); 98 | } 99 | core.info(`targets=${targets}`); 100 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | concurrency: 4 | group: ${{ github.workflow }}-${{ github.ref }} 5 | cancel-in-progress: true 6 | 7 | on: 8 | workflow_dispatch: 9 | inputs: 10 | buildx-version: 11 | description: 'Buildx version or Git context' 12 | default: 'latest' 13 | required: false 14 | buildkit-image: 15 | description: 'BuildKit image' 16 | default: 'moby/buildkit:buildx-stable-1' 17 | required: false 18 | schedule: 19 | - cron: '0 10 * * *' 20 | push: 21 | branches: 22 | - 'master' 23 | - 'releases/v*' 24 | tags: 25 | - 'v*' 26 | paths-ignore: 27 | - '.github/workflows/ci-subaction.yml' 28 | - 'subaction/**' 29 | pull_request: 30 | paths-ignore: 31 | - '.github/workflows/ci-subaction.yml' 32 | - 'subaction/**' 33 | 34 | env: 35 | BUILDX_VERSION: edge 36 | BUILDKIT_IMAGE: moby/buildkit:latest 37 | 38 | jobs: 39 | bake: 40 | runs-on: ubuntu-latest 41 | strategy: 42 | fail-fast: false 43 | matrix: 44 | target: 45 | - default 46 | - release 47 | services: 48 | registry: 49 | image: registry:2 50 | ports: 51 | - 5000:5000 52 | steps: 53 | - 54 | name: Checkout 55 | uses: actions/checkout@v4 56 | - 57 | name: Set up QEMU 58 | uses: docker/setup-qemu-action@v3 59 | - 60 | name: Set up Docker Buildx 61 | id: buildx 62 | uses: docker/setup-buildx-action@v3 63 | with: 64 | version: ${{ inputs.buildx-version || env.BUILDX_VERSION }} 65 | driver-opts: | 66 | image=${{ inputs.buildkit-image || env.BUILDKIT_IMAGE }} 67 | network=host 68 | - 69 | name: Build and push 70 | uses: ./ 71 | with: 72 | source: . 73 | builder: ${{ steps.buildx.outputs.name }} 74 | files: | 75 | ./test/config.hcl 76 | targets: | 77 | ${{ matrix.target }} 78 | push: false # set to true when https://github.com/docker/buildx/issues/179 is fixed 79 | 80 | error-msg: 81 | runs-on: ubuntu-latest 82 | steps: 83 | - 84 | name: Checkout 85 | uses: actions/checkout@v4 86 | - 87 | name: Build 88 | continue-on-error: true 89 | uses: ./ 90 | with: 91 | source: . 92 | files: | 93 | ./test/config.hcl 94 | set: | 95 | *.platform=linux/amd64,linux/ppc64le,linux/s390x 96 | 97 | error-check: 98 | runs-on: ubuntu-latest 99 | steps: 100 | - 101 | name: Checkout 102 | uses: actions/checkout@v4 103 | - 104 | name: Stop docker 105 | run: | 106 | sudo systemctl stop docker docker.socket 107 | - 108 | name: Build 109 | id: bake 110 | continue-on-error: true 111 | uses: ./ 112 | with: 113 | source: . 114 | files: | 115 | ./test/config.hcl 116 | - 117 | name: Check 118 | run: | 119 | echo "${{ toJson(steps.bake) }}" 120 | if [ "${{ steps.bake.outcome }}" != "failure" ] || [ "${{ steps.bake.conclusion }}" != "success" ]; then 121 | echo "::error::Should have failed" 122 | exit 1 123 | fi 124 | 125 | standalone: 126 | runs-on: ubuntu-latest 127 | steps: 128 | - 129 | name: Checkout 130 | uses: actions/checkout@v4 131 | - 132 | name: Uninstall docker cli 133 | run: | 134 | if dpkg -s "docker-ce" >/dev/null 2>&1; then 135 | sudo dpkg -r --force-depends docker-ce-cli docker-buildx-plugin 136 | else 137 | sudo apt-get purge -y moby-cli moby-buildx 138 | fi 139 | - 140 | name: Set up Docker Buildx 141 | uses: docker/setup-buildx-action@v3 142 | with: 143 | version: ${{ inputs.buildx-version || env.BUILDX_VERSION }} 144 | driver-opts: | 145 | image=${{ inputs.buildkit-image || env.BUILDKIT_IMAGE }} 146 | - 147 | name: Build 148 | uses: ./ 149 | with: 150 | source: . 151 | files: | 152 | ./test/config.hcl 153 | 154 | remote: 155 | runs-on: ubuntu-latest 156 | steps: 157 | - 158 | name: Checkout 159 | uses: actions/checkout@v4 160 | - 161 | name: Build 162 | uses: ./ 163 | with: 164 | source: https://github.com/docker/buildx.git#v0.8.2 165 | targets: update-docs 166 | 167 | provenance: 168 | runs-on: ubuntu-latest 169 | strategy: 170 | fail-fast: false 171 | matrix: 172 | attrs: 173 | - '' 174 | - mode=max 175 | - builder-id=foo 176 | - false 177 | - true 178 | steps: 179 | - 180 | name: Checkout 181 | uses: actions/checkout@v4 182 | - 183 | name: Set up Docker Buildx 184 | uses: docker/setup-buildx-action@v3 185 | with: 186 | version: ${{ inputs.buildx-version || env.BUILDX_VERSION }} 187 | driver-opts: | 188 | network=host 189 | image=${{ inputs.buildkit-image || env.BUILDKIT_IMAGE }} 190 | - 191 | name: Build 192 | uses: ./ 193 | with: 194 | workdir: ./test/go 195 | source: . 196 | targets: binary 197 | provenance: ${{ matrix.attrs }} 198 | set: | 199 | *.output=type=oci,dest=/tmp/build.tar 200 | *.cache-from=type=gha,scope=provenance 201 | *.cache-to=type=gha,scope=provenance,mode=max 202 | 203 | sbom: 204 | runs-on: ubuntu-latest 205 | env: 206 | DESTDIR: /tmp/bake-build 207 | strategy: 208 | fail-fast: false 209 | matrix: 210 | include: 211 | - target: image 212 | output: type=image,name=localhost:5000/name/app:latest,push=true 213 | - target: binary 214 | output: /tmp/bake-build 215 | services: 216 | registry: 217 | image: registry:2 218 | ports: 219 | - 5000:5000 220 | steps: 221 | - 222 | name: Checkout 223 | uses: actions/checkout@v4 224 | - 225 | name: Set up Docker Buildx 226 | uses: docker/setup-buildx-action@v3 227 | with: 228 | version: ${{ inputs.buildx-version || env.BUILDX_VERSION }} 229 | driver-opts: | 230 | network=host 231 | image=${{ inputs.buildkit-image || env.BUILDKIT_IMAGE }} 232 | - 233 | name: Build 234 | uses: ./ 235 | with: 236 | workdir: ./test/go 237 | source: . 238 | targets: ${{ matrix.target }} 239 | sbom: true 240 | set: | 241 | *.output=${{ matrix.output }} 242 | *.cache-from=type=gha,scope=attests-${{ matrix.target }} 243 | *.cache-to=type=gha,scope=attests-${{ matrix.target }},mode=max 244 | - 245 | name: Inspect image 246 | if: matrix.target == 'image' 247 | run: | 248 | docker buildx imagetools inspect localhost:5000/name/app:latest --format '{{json .}}' 249 | - 250 | name: Check output folder 251 | if: matrix.target == 'binary' 252 | working-directory: ${{ env.DESTDIR }} 253 | run: | 254 | tree . 255 | - 256 | name: Print provenance 257 | if: matrix.target == 'binary' 258 | working-directory: ${{ env.DESTDIR }} 259 | run: | 260 | cat provenance.json | jq 261 | - 262 | name: Print SBOM 263 | if: matrix.target == 'binary' 264 | working-directory: ${{ env.DESTDIR }} 265 | run: | 266 | cat sbom.spdx.json | jq 267 | 268 | set: 269 | runs-on: ubuntu-latest 270 | services: 271 | registry: 272 | image: registry:2 273 | ports: 274 | - 5000:5000 275 | steps: 276 | - 277 | name: Checkout 278 | uses: actions/checkout@v4 279 | - 280 | name: Build 281 | uses: ./ 282 | with: 283 | workdir: ./test/go 284 | source: . 285 | set: | 286 | *.platform=linux/amd64 287 | *.output=type=image,"name=localhost:5000/name/app:v1.0.0,localhost:5000/name/app:latest",push=true 288 | *.tags= 289 | 290 | group: 291 | runs-on: ubuntu-latest 292 | services: 293 | registry: 294 | image: registry:2 295 | ports: 296 | - 5000:5000 297 | steps: 298 | - 299 | name: Checkout 300 | uses: actions/checkout@v4 301 | - 302 | name: Set up Docker Buildx 303 | uses: docker/setup-buildx-action@v3 304 | with: 305 | version: ${{ inputs.buildx-version || env.BUILDX_VERSION }} 306 | driver-opts: | 307 | image=${{ inputs.buildkit-image || env.BUILDKIT_IMAGE }} 308 | network=host 309 | - 310 | name: Build and push 311 | uses: ./ 312 | with: 313 | workdir: ./test/group 314 | source: . 315 | push: true 316 | set: | 317 | t1.tags=localhost:5000/name/app:t1 318 | t2.tags=localhost:5000/name/app:t2 319 | 320 | docker-config-malformed: 321 | runs-on: ubuntu-latest 322 | steps: 323 | - 324 | name: Checkout 325 | uses: actions/checkout@v4 326 | - 327 | name: Set malformed docker config 328 | run: | 329 | mkdir -p ~/.docker 330 | echo 'foo_bar' >> ~/.docker/config.json 331 | - 332 | name: Build 333 | uses: ./ 334 | with: 335 | source: . 336 | files: | 337 | ./test/config.hcl 338 | 339 | proxy-docker-config: 340 | runs-on: ubuntu-latest 341 | services: 342 | squid-proxy: 343 | image: ubuntu/squid:latest 344 | ports: 345 | - 3128:3128 346 | steps: 347 | - 348 | name: Check proxy 349 | run: | 350 | netstat -aptn 351 | curl --retry 5 --retry-all-errors --retry-delay 0 --connect-timeout 5 --proxy http://127.0.0.1:3128 -v --insecure --head https://www.google.com 352 | - 353 | name: Checkout 354 | uses: actions/checkout@v4 355 | - 356 | name: Set proxy config 357 | run: | 358 | mkdir -p ~/.docker 359 | echo '{"proxies":{"default":{"httpProxy":"http://127.0.0.1:3128","httpsProxy":"http://127.0.0.1:3128"}}}' > ~/.docker/config.json 360 | - 361 | name: Set up Docker Buildx 362 | uses: docker/setup-buildx-action@v3 363 | with: 364 | version: ${{ inputs.buildx-version || env.BUILDX_VERSION }} 365 | driver-opts: | 366 | image=${{ inputs.buildkit-image || env.BUILDKIT_IMAGE }} 367 | network=host 368 | buildkitd-flags: --debug 369 | - 370 | name: Build 371 | uses: ./ 372 | with: 373 | source: . 374 | files: | 375 | ./test/config.hcl 376 | targets: app-proxy 377 | 378 | proxy-buildkitd: 379 | runs-on: ubuntu-latest 380 | services: 381 | squid-proxy: 382 | image: ubuntu/squid:latest 383 | ports: 384 | - 3128:3128 385 | steps: 386 | - 387 | name: Check proxy 388 | run: | 389 | netstat -aptn 390 | curl --retry 5 --retry-all-errors --retry-delay 0 --connect-timeout 5 --proxy http://127.0.0.1:3128 -v --insecure --head https://www.google.com 391 | - 392 | name: Checkout 393 | uses: actions/checkout@v4 394 | - 395 | name: Set up Docker Buildx 396 | uses: docker/setup-buildx-action@v3 397 | with: 398 | version: ${{ inputs.buildx-version || env.BUILDX_VERSION }} 399 | driver-opts: | 400 | image=${{ inputs.buildkit-image || env.BUILDKIT_IMAGE }} 401 | network=host 402 | env.http_proxy=http://127.0.0.1:3128 403 | env.https_proxy=http://127.0.0.1:3128 404 | buildkitd-flags: --debug 405 | - 406 | name: Build 407 | uses: ./ 408 | with: 409 | source: . 410 | files: | 411 | ./test/config.hcl 412 | 413 | git-context: 414 | runs-on: ubuntu-latest 415 | steps: 416 | - 417 | name: Checkout 418 | uses: actions/checkout@v4 419 | - 420 | name: Set up Docker Buildx 421 | uses: docker/setup-buildx-action@v3 422 | with: 423 | version: ${{ inputs.buildx-version || env.BUILDX_VERSION }} 424 | driver-opts: | 425 | image=${{ inputs.buildkit-image || env.BUILDKIT_IMAGE }} 426 | - 427 | name: Build 428 | uses: ./ 429 | 430 | git-context-and-local: 431 | runs-on: ubuntu-latest 432 | steps: 433 | - 434 | name: Checkout 435 | uses: actions/checkout@v4 436 | - 437 | name: Set up Docker Buildx 438 | uses: docker/setup-buildx-action@v3 439 | with: 440 | version: ${{ inputs.buildx-version || env.BUILDX_VERSION }} 441 | driver-opts: | 442 | image=${{ inputs.buildkit-image || env.BUILDKIT_IMAGE }} 443 | - 444 | name: Docker meta 445 | id: meta 446 | uses: docker/metadata-action@v5 447 | - 448 | name: Build 449 | uses: ./ 450 | with: 451 | files: | 452 | cwd://${{ steps.meta.outputs.bake-file }} 453 | 454 | multi-output: 455 | runs-on: ubuntu-latest 456 | services: 457 | registry: 458 | image: registry:2 459 | ports: 460 | - 5000:5000 461 | steps: 462 | - 463 | name: Checkout 464 | uses: actions/checkout@v4 465 | - 466 | name: Set up Docker Buildx 467 | uses: docker/setup-buildx-action@v3 468 | with: 469 | version: ${{ inputs.buildx-version || env.BUILDX_VERSION }} 470 | driver-opts: | 471 | network=host 472 | - 473 | name: Build and push 474 | uses: ./ 475 | with: 476 | workdir: ./test/go 477 | source: . 478 | set: | 479 | *.output=type=image,name=localhost:5000/name/app:latest,push=true 480 | *.output=type=docker,name=app:local 481 | *.output=type=oci,dest=/tmp/oci.tar 482 | - 483 | name: Check registry 484 | run: | 485 | docker buildx imagetools inspect localhost:5000/name/app:latest --format '{{json .}}' 486 | - 487 | name: Check docker 488 | run: | 489 | docker image inspect app:local 490 | - 491 | name: Check oci 492 | run: | 493 | set -ex 494 | mkdir -p /tmp/oci-out 495 | tar xf /tmp/oci.tar -C /tmp/oci-out 496 | tree -nh /tmp/oci-out 497 | 498 | load-and-push: 499 | runs-on: ubuntu-latest 500 | services: 501 | registry: 502 | image: registry:2 503 | ports: 504 | - 5000:5000 505 | steps: 506 | - 507 | name: Checkout 508 | uses: actions/checkout@v4 509 | - 510 | name: Set up Docker Buildx 511 | uses: docker/setup-buildx-action@v3 512 | with: 513 | version: ${{ inputs.buildx-version || env.BUILDX_VERSION }} 514 | driver-opts: | 515 | network=host 516 | - 517 | name: Build and push 518 | uses: ./ 519 | with: 520 | workdir: ./test/go 521 | source: . 522 | targets: image 523 | load: true 524 | push: true 525 | set: | 526 | *.tags=localhost:5000/name/app:latest 527 | - 528 | name: Check registry 529 | run: | 530 | docker buildx imagetools inspect localhost:5000/name/app:latest --format '{{json .}}' 531 | - 532 | name: Check docker 533 | run: | 534 | docker image inspect localhost:5000/name/app:latest 535 | 536 | summary-disable: 537 | runs-on: ubuntu-latest 538 | steps: 539 | - 540 | name: Checkout 541 | uses: actions/checkout@v4 542 | - 543 | name: Set up Docker Buildx 544 | uses: docker/setup-buildx-action@v3 545 | with: 546 | version: ${{ inputs.buildx-version || env.BUILDX_VERSION }} 547 | driver-opts: | 548 | image=${{ inputs.buildkit-image || env.BUILDKIT_IMAGE }} 549 | - 550 | name: Build 551 | uses: ./ 552 | with: 553 | files: | 554 | ./test/config.hcl 555 | targets: app 556 | env: 557 | DOCKER_BUILD_SUMMARY: false 558 | 559 | summary-disable-deprecated: 560 | runs-on: ubuntu-latest 561 | steps: 562 | - 563 | name: Checkout 564 | uses: actions/checkout@v4 565 | - 566 | name: Set up Docker Buildx 567 | uses: docker/setup-buildx-action@v3 568 | with: 569 | version: ${{ inputs.buildx-version || env.BUILDX_VERSION }} 570 | driver-opts: | 571 | image=${{ inputs.buildkit-image || env.BUILDKIT_IMAGE }} 572 | - 573 | name: Build 574 | uses: ./ 575 | with: 576 | source: . 577 | files: | 578 | ./test/config.hcl 579 | targets: app 580 | env: 581 | DOCKER_BUILD_NO_SUMMARY: true 582 | 583 | summary-not-supported: 584 | runs-on: ubuntu-latest 585 | steps: 586 | - 587 | name: Checkout 588 | uses: actions/checkout@v4 589 | - 590 | name: Set up Docker Buildx 591 | uses: docker/setup-buildx-action@v3 592 | with: 593 | version: v0.12.1 594 | driver-opts: | 595 | image=${{ inputs.buildkit-image || env.BUILDKIT_IMAGE }} 596 | - 597 | name: Build 598 | uses: ./ 599 | with: 600 | files: | 601 | ./test/config.hcl 602 | targets: app 603 | 604 | record-upload-disable: 605 | runs-on: ubuntu-latest 606 | steps: 607 | - 608 | name: Checkout 609 | uses: actions/checkout@v4 610 | - 611 | name: Set up Docker Buildx 612 | uses: docker/setup-buildx-action@v3 613 | with: 614 | version: ${{ inputs.buildx-version || env.BUILDX_VERSION }} 615 | driver-opts: | 616 | image=${{ inputs.buildkit-image || env.BUILDKIT_IMAGE }} 617 | - 618 | name: Build 619 | uses: ./ 620 | with: 621 | files: | 622 | ./test/config.hcl 623 | targets: app 624 | env: 625 | DOCKER_BUILD_RECORD_UPLOAD: false 626 | 627 | record-retention-days: 628 | runs-on: ubuntu-latest 629 | strategy: 630 | fail-fast: false 631 | matrix: 632 | days: 633 | - 2 634 | - 0 635 | steps: 636 | - 637 | name: Checkout 638 | uses: actions/checkout@v4 639 | - 640 | name: Set up Docker Buildx 641 | uses: docker/setup-buildx-action@v3 642 | with: 643 | version: ${{ inputs.buildx-version || env.BUILDX_VERSION }} 644 | driver-opts: | 645 | image=${{ inputs.buildkit-image || env.BUILDKIT_IMAGE }} 646 | - 647 | name: Build 648 | uses: ./ 649 | with: 650 | files: | 651 | ./test/config.hcl 652 | targets: app 653 | env: 654 | DOCKER_BUILD_RECORD_RETENTION_DAYS: ${{ matrix.days }} 655 | 656 | export-legacy: 657 | runs-on: ubuntu-latest 658 | strategy: 659 | fail-fast: false 660 | matrix: 661 | legacy: 662 | - false 663 | - true 664 | steps: 665 | - 666 | name: Checkout 667 | uses: actions/checkout@v4 668 | - 669 | name: Set up Docker Buildx 670 | uses: docker/setup-buildx-action@v3 671 | with: 672 | version: ${{ inputs.buildx-version || env.BUILDX_VERSION }} 673 | driver-opts: | 674 | image=${{ inputs.buildkit-image || env.BUILDKIT_IMAGE }} 675 | - 676 | name: Build 677 | uses: ./ 678 | with: 679 | files: | 680 | ./test/config.hcl 681 | targets: app 682 | env: 683 | DOCKER_BUILD_EXPORT_LEGACY: ${{ matrix.legacy }} 684 | 685 | checks: 686 | runs-on: ubuntu-latest 687 | strategy: 688 | fail-fast: false 689 | matrix: 690 | buildx-version: 691 | - edge 692 | - v0.14.1 693 | steps: 694 | - 695 | name: Checkout 696 | uses: actions/checkout@v4 697 | - 698 | name: Set up Docker Buildx 699 | uses: docker/setup-buildx-action@v3 700 | with: 701 | version: ${{ matrix.buildx-version }} 702 | driver-opts: | 703 | image=${{ inputs.buildkit-image || env.BUILDKIT_IMAGE }} 704 | - 705 | name: Build 706 | uses: ./ 707 | with: 708 | workdir: ./test 709 | source: . 710 | files: | 711 | ./lint.hcl 712 | 713 | annotations-disabled: 714 | runs-on: ubuntu-latest 715 | steps: 716 | - 717 | name: Checkout 718 | uses: actions/checkout@v4 719 | - 720 | name: Set up Docker Buildx 721 | uses: docker/setup-buildx-action@v3 722 | with: 723 | version: ${{ inputs.buildx-version || env.BUILDX_VERSION }} 724 | driver-opts: | 725 | image=${{ inputs.buildkit-image || env.BUILDKIT_IMAGE }} 726 | - 727 | name: Build 728 | uses: ./ 729 | with: 730 | workdir: ./test 731 | source: . 732 | files: | 733 | ./lint.hcl 734 | env: 735 | DOCKER_BUILD_CHECKS_ANNOTATIONS: false 736 | 737 | allow: 738 | runs-on: ubuntu-latest 739 | strategy: 740 | fail-fast: false 741 | matrix: 742 | buildx-version: 743 | - edge 744 | - v0.19.0 745 | - v0.18.0 746 | - v0.17.1 747 | steps: 748 | - 749 | name: Checkout 750 | uses: actions/checkout@v4 751 | - 752 | name: Set up Docker Buildx 753 | uses: docker/setup-buildx-action@v3 754 | with: 755 | version: ${{ matrix.buildx-version }} 756 | driver-opts: | 757 | image=${{ inputs.buildkit-image || env.BUILDKIT_IMAGE }} 758 | - 759 | name: Build 760 | uses: ./ 761 | with: 762 | files: | 763 | ./test/config.hcl 764 | allow: network.host 765 | targets: app-entitlements 766 | 767 | no-default-attestations: 768 | runs-on: ubuntu-latest 769 | steps: 770 | - 771 | name: Checkout 772 | uses: actions/checkout@v4 773 | - 774 | name: Build 775 | uses: ./ 776 | with: 777 | source: . 778 | files: | 779 | ./test/config.hcl 780 | env: 781 | BUILDX_NO_DEFAULT_ATTESTATIONS: 1 782 | -------------------------------------------------------------------------------- /.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 | paths-ignore: 13 | - '.github/workflows/ci-subaction.yml' 14 | - 'subaction/**' 15 | pull_request: 16 | paths-ignore: 17 | - '.github/workflows/ci-subaction.yml' 18 | - 'subaction/**' 19 | 20 | jobs: 21 | test: 22 | runs-on: ubuntu-latest 23 | steps: 24 | - 25 | name: Checkout 26 | uses: actions/checkout@v4 27 | - 28 | name: Test 29 | uses: docker/bake-action@v6 30 | with: 31 | source: . 32 | targets: test 33 | - 34 | name: Upload coverage 35 | uses: codecov/codecov-action@v5 36 | with: 37 | files: ./coverage/clover.xml 38 | token: ${{ secrets.CODECOV_TOKEN }} 39 | -------------------------------------------------------------------------------- /.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: ./subaction/list-targets 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/bake-action.svg?style=flat-square)](https://github.com/docker/bake-action/releases/latest) 2 | [![GitHub marketplace](https://img.shields.io/badge/marketplace-docker--buildx--bake-blue?logo=github&style=flat-square)](https://github.com/marketplace/actions/docker-buildx-bake) 3 | [![CI workflow](https://img.shields.io/github/actions/workflow/status/docker/bake-action/ci.yml?branch=master&label=ci&logo=github&style=flat-square)](https://github.com/docker/bake-action/actions?workflow=ci) 4 | [![Test workflow](https://img.shields.io/github/actions/workflow/status/docker/bake-action/test.yml?branch=master&label=test&logo=github&style=flat-square)](https://github.com/docker/bake-action/actions?workflow=test) 5 | [![Codecov](https://img.shields.io/codecov/c/github/docker/bake-action?logo=codecov&style=flat-square)](https://codecov.io/gh/docker/bake-action) 6 | 7 | ## About 8 | 9 | GitHub Action to use Docker [Buildx Bake](https://docs.docker.com/build/customize/bake/) 10 | as a high-level build command. 11 | 12 | ![Screenshot](.github/bake-action.png) 13 | 14 | ___ 15 | 16 | * [Usage](#usage) 17 | * [Git context](#git-context) 18 | * [Path context](#path-context) 19 | * [Summaries](#summaries) 20 | * [Customizing](#customizing) 21 | * [inputs](#inputs) 22 | * [outputs](#outputs) 23 | * [environment variables](#environment-variables) 24 | * [Subactions](#subactions) 25 | * [`list-targets`](subaction/list-targets) 26 | * [Contributing](#contributing) 27 | 28 | ## Usage 29 | 30 | ### Git context 31 | 32 | Since `v6` this action uses the [Git context](https://docs.docker.com/build/bake/remote-definition/) 33 | to build from a remote bake definition by default like the [build-push-action](https://github.com/docker/build-push-action) 34 | does. This means that you don't need to use the [`actions/checkout`](https://github.com/actions/checkout/) 35 | action to check out the repository as [BuildKit](https://docs.docker.com/build/buildkit/) 36 | will do this directly. 37 | 38 | 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) 39 | and will result in the following context: `https://github.com//.git#`. 40 | 41 | ```yaml 42 | name: ci 43 | 44 | on: 45 | push: 46 | 47 | jobs: 48 | bake: 49 | runs-on: ubuntu-latest 50 | steps: 51 | - 52 | name: Login to DockerHub 53 | uses: docker/login-action@v3 54 | with: 55 | username: ${{ vars.DOCKERHUB_USERNAME }} 56 | password: ${{ secrets.DOCKERHUB_TOKEN }} 57 | - 58 | name: Set up Docker Buildx 59 | uses: docker/setup-buildx-action@v3 60 | - 61 | name: Build and push 62 | uses: docker/bake-action@v6 63 | with: 64 | push: true 65 | set: | 66 | *.tags=user/app:latest 67 | ``` 68 | 69 | Be careful because **any file mutation in the steps that precede the build step 70 | will be ignored, including processing of the `.dockerignore` file** since 71 | the context is based on the Git reference. However, you can use the 72 | [Path context](#path-context) using the [`source` input](#inputs) alongside 73 | the [`actions/checkout`](https://github.com/actions/checkout/) action to remove 74 | this restriction. 75 | 76 | Default Git context can also be provided using the [Handlebars template](https://handlebarsjs.com/guide/) 77 | expression `{{defaultContext}}`. Here we can use it to provide a subdirectory 78 | to the default Git context: 79 | 80 | ```yaml 81 | - 82 | name: Build and push 83 | uses: docker/bake-action@v6 84 | with: 85 | source: "{{defaultContext}}:mysubdir" 86 | push: true 87 | set: | 88 | *.tags=user/app:latest 89 | ``` 90 | 91 | Building from the current repository automatically uses the `GITHUB_TOKEN` 92 | secret that GitHub [automatically creates for workflows](https://docs.github.com/en/actions/security-guides/automatic-token-authentication), 93 | so you don't need to pass that manually. If you want to authenticate against 94 | another private repository for remote definitions, you can set the 95 | [`BUILDX_BAKE_GIT_AUTH_TOKEN` environment variable](https://docs.docker.com/build/building/variables/#buildx_bake_git_auth_token). 96 | 97 | > [!NOTE] 98 | > Supported since Buildx 0.14.0 99 | 100 | ```yaml 101 | - 102 | name: Build and push 103 | uses: docker/bake-action@v6 104 | with: 105 | push: true 106 | set: | 107 | *.tags=user/app:latest 108 | env: 109 | BUILDX_BAKE_GIT_AUTH_TOKEN: ${{ secrets.MYTOKEN }} 110 | ``` 111 | 112 | ### Path context 113 | 114 | ```yaml 115 | name: ci 116 | 117 | on: 118 | push: 119 | 120 | jobs: 121 | bake: 122 | runs-on: ubuntu-latest 123 | steps: 124 | - 125 | name: Checkout 126 | uses: actions/checkout@v4 127 | - 128 | name: Login to DockerHub 129 | uses: docker/login-action@v3 130 | with: 131 | username: ${{ vars.DOCKERHUB_USERNAME }} 132 | password: ${{ secrets.DOCKERHUB_TOKEN }} 133 | - 134 | name: Set up Docker Buildx 135 | uses: docker/setup-buildx-action@v3 136 | - 137 | name: Build and push 138 | uses: docker/bake-action@v6 139 | with: 140 | source: . 141 | push: true 142 | set: | 143 | *.tags=user/app:latest 144 | ``` 145 | 146 | ## Summaries 147 | 148 | This action generates a [job summary](https://github.blog/2022-05-09-supercharging-github-actions-with-job-summaries/) 149 | that provides a detailed overview of the build execution. The summary shows an 150 | overview of all the steps executed during the build, including the build 151 | inputs, bake definition, and eventual errors. 152 | 153 | ![build-push-action job summary](./.github/bake-summary.png) 154 | 155 | The summary also includes a link for downloading a build record archive with 156 | additional details about the build execution for all the bake targets, 157 | including build stats, logs, outputs, and more. The build record can be 158 | imported to Docker Desktop for inspecting the build in greater detail. 159 | 160 | > [!WARNING] 161 | > 162 | > If you're using the [`actions/download-artifact`](https://github.com/actions/download-artifact) 163 | > action in your workflow, you need to ignore the build record artifacts 164 | > 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), 165 | > otherwise the action will fail: 166 | > ```yaml 167 | > - uses: actions/download-artifact@v4 168 | > with: 169 | > pattern: "!*.dockerbuild" 170 | > ``` 171 | > More info: https://github.com/actions/toolkit/pull/1874 172 | 173 | Summaries are enabled by default, but can be disabled with the 174 | `DOCKER_BUILD_SUMMARY` [environment variable](#environment-variables). 175 | 176 | For more information about summaries, refer to the 177 | [documentation](https://docs.docker.com/go/build-summary/). 178 | 179 | ## Customizing 180 | 181 | ### inputs 182 | 183 | The following inputs can be used as `step.with` keys 184 | 185 | > `List` type is a newline-delimited string 186 | > ```yaml 187 | > set: target.args.mybuildarg=value 188 | > ``` 189 | > ```yaml 190 | > set: | 191 | > target.args.mybuildarg=value 192 | > foo*.args.mybuildarg=value 193 | > ``` 194 | 195 | > `CSV` type is a comma-delimited string 196 | > ```yaml 197 | > targets: default,release 198 | > ``` 199 | 200 | | Name | Type | Description | 201 | |----------------|-------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------| 202 | | `builder` | String | Builder instance (see [setup-buildx](https://github.com/docker/setup-buildx-action) action) | 203 | | `source` | String | Context to build from. Can be either local (`.`) or a [remote bake definition](https://docs.docker.com/build/customize/bake/file-definition/#remote-definition) | 204 | | `allow` | List/CSV | Allow build to access specified resources (e.g., `network.host`) | 205 | | `files` | List/CSV | List of [bake definition files](https://docs.docker.com/build/customize/bake/file-definition/) | 206 | | `workdir` | String | Working directory of execution | 207 | | `targets` | List/CSV | List of bake targets (`default` target used if empty) | 208 | | `no-cache` | Bool | Do not use cache when building the image (default `false`) | 209 | | `pull` | Bool | Always attempt to pull a newer version of the image (default `false`) | 210 | | `load` | Bool | Load is a shorthand for `--set=*.output=type=docker` (default `false`) | 211 | | `provenance` | Bool/String | [Provenance](https://docs.docker.com/build/attestations/slsa-provenance/) is a shorthand for `--set=*.attest=type=provenance` | 212 | | `push` | Bool | Push is a shorthand for `--set=*.output=type=registry` (default `false`) | 213 | | `sbom` | Bool/String | [SBOM](https://docs.docker.com/build/attestations/sbom/) is a shorthand for `--set=*.attest=type=sbom` | 214 | | `set` | List | List of [targets values to override](https://docs.docker.com/engine/reference/commandline/buildx_bake/#set) (e.g., `targetpattern.key=value`) | 215 | | `github-token` | String | API token used to authenticate to a Git repository for [remote definitions](https://docs.docker.com/build/bake/remote-definition/) (default `${{ github.token }}`) | 216 | 217 | ### outputs 218 | 219 | The following outputs are available 220 | 221 | | Name | Type | Description | 222 | |------------|------|-----------------------| 223 | | `metadata` | JSON | Build result metadata | 224 | 225 | ### environment variables 226 | 227 | | Name | Type | Default | Description | 228 | |--------------------------------------|--------|---------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 229 | | `DOCKER_BUILD_CHECKS_ANNOTATIONS` | Bool | `true` | If `false`, GitHub annotations are not generated for [build checks](https://docs.docker.com/build/checks/) | 230 | | `DOCKER_BUILD_SUMMARY` | Bool | `true` | If `false`, [build summary](https://docs.docker.com/build/ci/github-actions/build-summary/) generation is disabled | 231 | | `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 | 232 | | `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` | 233 | | `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/) | 234 | 235 | ## Subactions 236 | 237 | * [`list-targets`](subaction/list-targets) 238 | 239 | ## Contributing 240 | 241 | Want to contribute? Awesome! You can find information about contributing to 242 | this project in the [CONTRIBUTING.md](/.github/CONTRIBUTING.md) 243 | -------------------------------------------------------------------------------- /__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 {Bake} from '@docker/actions-toolkit/lib/buildx/bake'; 6 | import {Builder} from '@docker/actions-toolkit/lib/buildx/builder'; 7 | import {Buildx} from '@docker/actions-toolkit/lib/buildx/buildx'; 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 {BakeDefinition} from '@docker/actions-toolkit/lib/types/buildx/bake'; 14 | import {BuilderInfo} from '@docker/actions-toolkit/lib/types/buildx/builder'; 15 | import {GitHubRepo} from '@docker/actions-toolkit/lib/types/github'; 16 | 17 | import * as context from '../src/context'; 18 | 19 | const tmpDir = path.join('/tmp', '.docker-bake-action-jest'); 20 | const tmpName = path.join(tmpDir, '.tmpname-jest'); 21 | 22 | import repoFixture from './fixtures/github-repo.json'; 23 | jest.spyOn(GitHub.prototype, 'repoData').mockImplementation((): Promise => { 24 | return >(repoFixture as unknown); 25 | }); 26 | 27 | jest.spyOn(Context, 'tmpDir').mockImplementation((): string => { 28 | if (!fs.existsSync(tmpDir)) { 29 | fs.mkdirSync(tmpDir, {recursive: true}); 30 | } 31 | return tmpDir; 32 | }); 33 | 34 | jest.spyOn(Context, 'tmpName').mockImplementation((): string => { 35 | return tmpName; 36 | }); 37 | 38 | jest.spyOn(Docker, 'isAvailable').mockImplementation(async (): Promise => { 39 | return true; 40 | }); 41 | 42 | const metadataJson = path.join(tmpDir, 'metadata.json'); 43 | jest.spyOn(Bake.prototype, 'getMetadataFilePath').mockImplementation((): string => { 44 | return metadataJson; 45 | }); 46 | 47 | jest.spyOn(Builder.prototype, 'inspect').mockImplementation(async (): Promise => { 48 | return { 49 | name: 'builder2', 50 | driver: 'docker-container', 51 | lastActivity: new Date('2023-01-16 09:45:23 +0000 UTC'), 52 | nodes: [ 53 | { 54 | buildkit: 'v0.11.0', 55 | 'buildkitd-flags': '--debug --allow-insecure-entitlement security.insecure --allow-insecure-entitlement network.host', 56 | 'driver-opts': ['BUILDKIT_STEP_LOG_MAX_SIZE=10485760', 'BUILDKIT_STEP_LOG_MAX_SPEED=10485760', 'JAEGER_TRACE=localhost:6831', 'image=moby/buildkit:latest', 'network=host'], 57 | endpoint: 'unix:///var/run/docker.sock', 58 | name: 'builder20', 59 | 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', 60 | status: 'running' 61 | } 62 | ] 63 | }; 64 | }); 65 | 66 | jest.spyOn(Bake.prototype, 'getDefinition').mockImplementation(async (): Promise => { 67 | return JSON.parse(`{ 68 | "group": { 69 | "default": { 70 | "targets": [ 71 | "validate" 72 | ] 73 | }, 74 | "validate": { 75 | "targets": [ 76 | "lint", 77 | "validate-vendor", 78 | "validate-docs" 79 | ] 80 | } 81 | }, 82 | "target": { 83 | "lint": { 84 | "context": ".", 85 | "dockerfile": "./hack/dockerfiles/lint.Dockerfile", 86 | "args": { 87 | "BUILDKIT_CONTEXT_KEEP_GIT_DIR": "1", 88 | "GO_VERSION": "1.20" 89 | }, 90 | "output": [ 91 | "type=cacheonly" 92 | ] 93 | }, 94 | "validate-docs": { 95 | "context": ".", 96 | "dockerfile": "./hack/dockerfiles/docs.Dockerfile", 97 | "args": { 98 | "BUILDKIT_CONTEXT_KEEP_GIT_DIR": "1", 99 | "BUILDX_EXPERIMENTAL": "1", 100 | "FORMATS": "md", 101 | "GO_VERSION": "1.20" 102 | }, 103 | "target": "validate", 104 | "output": [ 105 | "type=cacheonly" 106 | ] 107 | }, 108 | "validate-vendor": { 109 | "context": ".", 110 | "dockerfile": "./hack/dockerfiles/vendor.Dockerfile", 111 | "args": { 112 | "BUILDKIT_CONTEXT_KEEP_GIT_DIR": "1", 113 | "GO_VERSION": "1.20" 114 | }, 115 | "target": "validate", 116 | "output": [ 117 | "type=cacheonly" 118 | ] 119 | } 120 | } 121 | }`) as BakeDefinition; 122 | }); 123 | 124 | describe('getArgs', () => { 125 | const originalEnv = process.env; 126 | beforeEach(() => { 127 | process.env = Object.keys(process.env).reduce((object, key) => { 128 | if (!key.startsWith('INPUT_')) { 129 | object[key] = process.env[key]; 130 | } 131 | return object; 132 | }, {}); 133 | }); 134 | afterEach(() => { 135 | process.env = originalEnv; 136 | }); 137 | 138 | // prettier-ignore 139 | test.each([ 140 | [ 141 | 0, 142 | '0.4.1', 143 | new Map([ 144 | ['source', '.'], 145 | ['load', 'false'], 146 | ['no-cache', 'false'], 147 | ['push', 'false'], 148 | ['pull', 'false'], 149 | ]), 150 | [ 151 | 'bake', 152 | ], 153 | undefined 154 | ], 155 | [ 156 | 1, 157 | '0.8.2', 158 | new Map([ 159 | ['source', '.'], 160 | ['load', 'false'], 161 | ['no-cache', 'false'], 162 | ['push', 'false'], 163 | ['pull', 'false'] 164 | ]), 165 | [ 166 | 'bake', 167 | '--metadata-file', metadataJson 168 | ], 169 | undefined 170 | ], 171 | [ 172 | 2, 173 | '0.8.2', 174 | new Map([ 175 | ['source', '.'], 176 | ['targets', 'webapp\nvalidate'], 177 | ['load', 'false'], 178 | ['no-cache', 'false'], 179 | ['push', 'false'], 180 | ['pull', 'false'] 181 | ]), 182 | [ 183 | 'bake', 184 | '--metadata-file', metadataJson, 185 | 'webapp', 'validate' 186 | ], 187 | undefined 188 | ], 189 | [ 190 | 3, 191 | '0.8.2', 192 | new Map([ 193 | ['source', '.'], 194 | ['set', '*.cache-from=type=gha\n*.cache-to=type=gha'], 195 | ['load', 'false'], 196 | ['no-cache', 'false'], 197 | ['push', 'false'], 198 | ['pull', 'false'] 199 | ]), 200 | [ 201 | 'bake', 202 | '--set', '*.cache-from=type=gha', 203 | '--set', '*.cache-to=type=gha', 204 | '--metadata-file', metadataJson 205 | ], 206 | undefined 207 | ], 208 | [ 209 | 4, 210 | '0.10.0', 211 | new Map([ 212 | ['source', '.'], 213 | ['load', 'false'], 214 | ['no-cache', 'false'], 215 | ['push', 'false'], 216 | ['pull', 'false'], 217 | ]), 218 | [ 219 | 'bake', 220 | '--metadata-file', metadataJson, 221 | "--provenance", `mode=min,inline-only=true,builder-id=https://github.com/docker/build-push-action/actions/runs/123456789/attempts/1`, 222 | ], 223 | undefined 224 | ], 225 | [ 226 | 5, 227 | '0.10.0', 228 | new Map([ 229 | ['source', '.'], 230 | ['load', 'false'], 231 | ['no-cache', 'false'], 232 | ['push', 'false'], 233 | ['pull', 'false'], 234 | ['provenance', 'true'], 235 | ]), 236 | [ 237 | 'bake', 238 | '--metadata-file', metadataJson, 239 | "--provenance", `builder-id=https://github.com/docker/build-push-action/actions/runs/123456789/attempts/1` 240 | ], 241 | undefined 242 | ], 243 | [ 244 | 6, 245 | '0.10.0', 246 | new Map([ 247 | ['source', '.'], 248 | ['load', 'false'], 249 | ['no-cache', 'false'], 250 | ['push', 'false'], 251 | ['pull', 'false'], 252 | ['provenance', 'mode=max'], 253 | ]), 254 | [ 255 | 'bake', 256 | '--metadata-file', metadataJson, 257 | "--provenance", `mode=max,builder-id=https://github.com/docker/build-push-action/actions/runs/123456789/attempts/1` 258 | ], 259 | undefined 260 | ], 261 | [ 262 | 7, 263 | '0.10.0', 264 | new Map([ 265 | ['source', '.'], 266 | ['load', 'false'], 267 | ['no-cache', 'false'], 268 | ['push', 'false'], 269 | ['pull', 'false'], 270 | ['provenance', 'false'], 271 | ]), 272 | [ 273 | 'bake', 274 | '--metadata-file', metadataJson, 275 | "--provenance", 'false' 276 | ], 277 | undefined 278 | ], 279 | [ 280 | 8, 281 | '0.10.0', 282 | new Map([ 283 | ['source', '.'], 284 | ['load', 'false'], 285 | ['no-cache', 'false'], 286 | ['push', 'false'], 287 | ['pull', 'false'], 288 | ['provenance', 'builder-id=foo'], 289 | ]), 290 | [ 291 | 'bake', 292 | '--metadata-file', metadataJson, 293 | "--provenance", 'builder-id=foo' 294 | ], 295 | undefined 296 | ], 297 | [ 298 | 9, 299 | '0.10.0', 300 | new Map([ 301 | ['source', '.'], 302 | ['load', 'false'], 303 | ['no-cache', 'false'], 304 | ['push', 'false'], 305 | ['pull', 'false'], 306 | ['set', `*.platform=linux/amd64,linux/ppc64le,linux/s390x\n*.output=type=image,"name=moby/buildkit:v0.11.0,moby/buildkit:latest",push=true`], 307 | ['targets', `"image-all"`], 308 | ]), 309 | [ 310 | 'bake', 311 | '--set', '*.platform=linux/amd64,linux/ppc64le,linux/s390x', 312 | '--set', `*.output=type=image,"name=moby/buildkit:v0.11.0,moby/buildkit:latest",push=true`, 313 | '--metadata-file', metadataJson, 314 | '--provenance', `mode=min,inline-only=true,builder-id=https://github.com/docker/build-push-action/actions/runs/123456789/attempts/1`, 315 | 'image-all' 316 | ], 317 | undefined 318 | ], 319 | [ 320 | 10, 321 | '0.10.0', 322 | new Map([ 323 | ['source', '.'], 324 | ['load', 'false'], 325 | ['no-cache', 'false'], 326 | ['push', 'false'], 327 | ['pull', 'false'], 328 | ['set', `*.labels.foo=bar=#baz`], 329 | ['targets', `"image-all"`], 330 | ]), 331 | [ 332 | 'bake', 333 | '--set', `*.labels.foo=bar=#baz`, 334 | '--metadata-file', metadataJson, 335 | '--provenance', `mode=min,inline-only=true,builder-id=https://github.com/docker/build-push-action/actions/runs/123456789/attempts/1`, 336 | 'image-all' 337 | ], 338 | undefined 339 | ], 340 | [ 341 | 11, 342 | '0.10.0', 343 | new Map([ 344 | ['load', 'false'], 345 | ['no-cache', 'false'], 346 | ['push', 'false'], 347 | ['pull', 'false'], 348 | ['files', './foo.hcl'], 349 | ]), 350 | [ 351 | 'bake', 352 | 'https://github.com/docker/build-push-action.git#refs/heads/master', 353 | '--file', './foo.hcl', 354 | '--metadata-file', metadataJson, 355 | '--provenance', `mode=min,inline-only=true,builder-id=https://github.com/docker/build-push-action/actions/runs/123456789/attempts/1`, 356 | ], 357 | undefined 358 | ], 359 | [ 360 | 12, 361 | '0.17.0', 362 | new Map([ 363 | ['source', '.'], 364 | ['allow', 'network.host'], 365 | ['load', 'false'], 366 | ['no-cache', 'false'], 367 | ['push', 'false'], 368 | ['pull', 'false'], 369 | ]), 370 | [ 371 | 'bake', 372 | '--allow', 'network.host', 373 | '--metadata-file', metadataJson, 374 | "--provenance", `mode=min,inline-only=true,builder-id=https://github.com/docker/build-push-action/actions/runs/123456789/attempts/1` 375 | ], 376 | undefined 377 | ], 378 | [ 379 | 13, 380 | '0.15.0', 381 | new Map([ 382 | ['source', '{{defaultContext}}:subdir'], 383 | ['load', 'false'], 384 | ['no-cache', 'false'], 385 | ['push', 'false'], 386 | ['pull', 'false'], 387 | ['files', './foo.hcl'], 388 | ]), 389 | [ 390 | 'bake', 391 | 'https://github.com/docker/build-push-action.git#refs/heads/master:subdir', 392 | '--file', './foo.hcl', 393 | '--metadata-file', metadataJson, 394 | '--provenance', `mode=min,inline-only=true,builder-id=https://github.com/docker/build-push-action/actions/runs/123456789/attempts/1`, 395 | ], 396 | undefined 397 | ], 398 | [ 399 | 14, 400 | '0.15.0', 401 | new Map([ 402 | ['source', '.'], 403 | ['load', 'false'], 404 | ['no-cache', 'false'], 405 | ['push', 'false'], 406 | ['pull', 'false'] 407 | ]), 408 | [ 409 | 'bake', 410 | '--metadata-file', metadataJson 411 | ], 412 | new Map([ 413 | ['BUILDX_NO_DEFAULT_ATTESTATIONS', '1'] 414 | ]) 415 | ], 416 | ])( 417 | '[%d] given %p with %p as inputs, returns %p', 418 | async (num: number, buildxVersion: string, inputs: Map, expected: Array, envs: Map | undefined) => { 419 | if (envs) { 420 | envs.forEach((value: string, name: string) => { 421 | process.env[name] = value; 422 | }); 423 | } 424 | inputs.forEach((value: string, name: string) => { 425 | setInput(name, value); 426 | }); 427 | const toolkit = new Toolkit(); 428 | jest.spyOn(Buildx.prototype, 'version').mockImplementation(async (): Promise => { 429 | return buildxVersion; 430 | }); 431 | const inp = await context.getInputs(); 432 | const definition = await toolkit.buildxBake.getDefinition( 433 | { 434 | files: inp.files, 435 | load: inp.load, 436 | noCache: inp['no-cache'], 437 | overrides: inp.set, 438 | provenance: inp.provenance, 439 | push: inp.push, 440 | sbom: inp.sbom, 441 | source: inp.source, 442 | targets: inp.targets 443 | }, 444 | { 445 | cwd: inp.workdir 446 | } 447 | ); 448 | const res = await context.getArgs(inp, definition, toolkit); 449 | expect(res).toEqual(expected); 450 | } 451 | ); 452 | }); 453 | 454 | // See: https://github.com/actions/toolkit/blob/a1b068ec31a042ff1e10a522d8fdf0b8869d53ca/packages/core/src/core.ts#L89 455 | function getInputName(name: string): string { 456 | return `INPUT_${name.replace(/ /g, '_').toUpperCase()}`; 457 | } 458 | 459 | function setInput(name: string, value: string): void { 460 | process.env[getInputName(name)] = value; 461 | } 462 | -------------------------------------------------------------------------------- /__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 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | # https://help.github.com/en/articles/metadata-syntax-for-github-actions 2 | name: "Docker Buildx Bake" 3 | description: "GitHub Action to use Docker Buildx Bake as a high-level build command" 4 | author: 'docker' 5 | branding: 6 | icon: 'anchor' 7 | color: 'blue' 8 | 9 | inputs: 10 | builder: 11 | description: "Builder instance" 12 | required: false 13 | source: 14 | description: "Context to build from. Can be either local or a remote bake definition" 15 | required: false 16 | allow: 17 | description: "Allow build to access specified resources (e.g., network.host)" 18 | required: false 19 | files: 20 | description: "List of bake definition files" 21 | required: false 22 | workdir: 23 | description: "Working directory of bake execution" 24 | required: false 25 | default: '.' 26 | targets: 27 | description: "List of bake targets" 28 | required: false 29 | no-cache: 30 | description: "Do not use cache when building the image" 31 | required: false 32 | default: 'false' 33 | pull: 34 | description: "Always attempt to pull a newer version of the image" 35 | required: false 36 | default: 'false' 37 | load: 38 | description: "Load is a shorthand for --set=*.output=type=docker" 39 | required: false 40 | default: 'false' 41 | provenance: 42 | description: "Provenance is a shorthand for --set=*.attest=type=provenance" 43 | required: false 44 | push: 45 | description: "Push is a shorthand for --set=*.output=type=registry" 46 | required: false 47 | default: 'false' 48 | sbom: 49 | description: "SBOM is a shorthand for --set=*.attest=type=sbom" 50 | required: false 51 | set: 52 | description: "List of targets values to override (eg. targetpattern.key=value)" 53 | required: false 54 | github-token: 55 | description: "API token used to authenticate to a Git repository for remote definitions" 56 | default: ${{ github.token }} 57 | required: false 58 | 59 | outputs: 60 | metadata: 61 | description: 'Build result metadata' 62 | 63 | runs: 64 | using: 'node20' 65 | main: 'dist/index.js' 66 | post: 'dist/index.js' 67 | -------------------------------------------------------------------------------- /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-bake-action-')); 6 | 7 | process.env = Object.assign({}, process.env, { 8 | TEMP: tmpDir, 9 | GITHUB_REPOSITORY: 'docker/bake-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: true, 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-buildx-bake", 3 | "description": "GitHub Action to use Docker Buildx Bake as a high-level build command", 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/bake-action.git" 18 | }, 19 | "keywords": [ 20 | "actions", 21 | "docker", 22 | "buildx", 23 | "bake" 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.8" 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 {Bake} from '@docker/actions-toolkit/lib/buildx/bake'; 5 | import {Build} from '@docker/actions-toolkit/lib/buildx/build'; 6 | import {Context} from '@docker/actions-toolkit/lib/context'; 7 | import {GitHub} from '@docker/actions-toolkit/lib/github'; 8 | import {Toolkit} from '@docker/actions-toolkit/lib/toolkit'; 9 | import {Util} from '@docker/actions-toolkit/lib/util'; 10 | 11 | import {BakeDefinition} from '@docker/actions-toolkit/lib/types/buildx/bake'; 12 | 13 | export interface Inputs { 14 | allow: string[]; 15 | builder: string; 16 | files: string[]; 17 | workdir: string; 18 | targets: string[]; 19 | 'no-cache': boolean; 20 | pull: boolean; 21 | load: boolean; 22 | provenance: string; 23 | push: boolean; 24 | sbom: string; 25 | set: string[]; 26 | source: string; 27 | 'github-token': string; 28 | } 29 | 30 | export async function getInputs(): Promise { 31 | return { 32 | allow: Util.getInputList('allow'), 33 | builder: core.getInput('builder'), 34 | files: Util.getInputList('files'), 35 | workdir: core.getInput('workdir') || '.', 36 | targets: Util.getInputList('targets'), 37 | 'no-cache': core.getBooleanInput('no-cache'), 38 | pull: core.getBooleanInput('pull'), 39 | load: core.getBooleanInput('load'), 40 | provenance: Build.getProvenanceInput('provenance'), 41 | push: core.getBooleanInput('push'), 42 | sbom: core.getInput('sbom'), 43 | set: Util.getInputList('set', {ignoreComma: true, quote: false}), 44 | source: getSourceInput('source'), 45 | 'github-token': core.getInput('github-token') 46 | }; 47 | } 48 | 49 | export async function getArgs(inputs: Inputs, definition: BakeDefinition, toolkit: Toolkit): Promise> { 50 | // prettier-ignore 51 | return [ 52 | ...await getBakeArgs(inputs, definition, toolkit), 53 | ...await getCommonArgs(inputs), 54 | ...inputs.targets 55 | ]; 56 | } 57 | 58 | async function getBakeArgs(inputs: Inputs, definition: BakeDefinition, toolkit: Toolkit): Promise> { 59 | const args: Array = ['bake']; 60 | if (inputs.source) { 61 | args.push(inputs.source); 62 | } 63 | if (await toolkit.buildx.versionSatisfies('>=0.17.0')) { 64 | if (await toolkit.buildx.versionSatisfies('>=0.18.0')) { 65 | // allow filesystem entitlements by default 66 | inputs.allow.push('fs=*'); 67 | } 68 | await Util.asyncForEach(inputs.allow, async allow => { 69 | args.push('--allow', allow); 70 | }); 71 | } 72 | await Util.asyncForEach(inputs.files, async file => { 73 | args.push('--file', file); 74 | }); 75 | await Util.asyncForEach(inputs.set, async set => { 76 | args.push('--set', set); 77 | }); 78 | if (await toolkit.buildx.versionSatisfies('>=0.6.0')) { 79 | args.push('--metadata-file', toolkit.buildxBake.getMetadataFilePath()); 80 | } 81 | if (await toolkit.buildx.versionSatisfies('>=0.10.0')) { 82 | if (inputs.provenance) { 83 | args.push('--provenance', inputs.provenance); 84 | } else if (!noDefaultAttestations() && (await toolkit.buildkit.versionSatisfies(inputs.builder, '>=0.11.0')) && !Bake.hasDockerExporter(definition, inputs.load)) { 85 | // if provenance not specified and BuildKit version compatible for 86 | // attestation, set default provenance. Also needs to make sure user 87 | // doesn't want to explicitly load the image to docker. 88 | if (GitHub.context.payload.repository?.private ?? false) { 89 | // if this is a private repository, we set the default provenance 90 | // attributes being set in buildx: https://github.com/docker/buildx/blob/fb27e3f919dcbf614d7126b10c2bc2d0b1927eb6/build/build.go#L603 91 | args.push('--provenance', Build.resolveProvenanceAttrs(`mode=min,inline-only=true`)); 92 | } else { 93 | // for a public repository, we set max provenance mode. 94 | args.push('--provenance', Build.resolveProvenanceAttrs(`mode=max`)); 95 | } 96 | } 97 | if (inputs.sbom) { 98 | args.push('--sbom', inputs.sbom); 99 | } 100 | } 101 | return args; 102 | } 103 | 104 | async function getCommonArgs(inputs: Inputs): Promise> { 105 | const args: Array = []; 106 | if (inputs['no-cache']) { 107 | args.push('--no-cache'); 108 | } 109 | if (inputs.builder) { 110 | args.push('--builder', inputs.builder); 111 | } 112 | if (inputs.pull) { 113 | args.push('--pull'); 114 | } 115 | if (inputs.load) { 116 | args.push('--load'); 117 | } 118 | if (inputs.push) { 119 | args.push('--push'); 120 | } 121 | return args; 122 | } 123 | 124 | function getSourceInput(name: string): string { 125 | let source = handlebars.compile(core.getInput(name))({ 126 | defaultContext: Context.gitContext() 127 | }); 128 | if (!source) { 129 | source = Context.gitContext(); 130 | } 131 | if (source === '.') { 132 | source = ''; 133 | } 134 | return source; 135 | } 136 | 137 | function noDefaultAttestations(): boolean { 138 | if (process.env.BUILDX_NO_DEFAULT_ATTESTATIONS) { 139 | return Util.parseBool(process.env.BUILDX_NO_DEFAULT_ATTESTATIONS); 140 | } 141 | return false; 142 | } 143 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as path from 'path'; 3 | import * as core from '@actions/core'; 4 | import * as actionsToolkit from '@docker/actions-toolkit'; 5 | 6 | import {Buildx} from '@docker/actions-toolkit/lib/buildx/buildx'; 7 | import {History as BuildxHistory} from '@docker/actions-toolkit/lib/buildx/history'; 8 | import {Context} from '@docker/actions-toolkit/lib/context'; 9 | import {Docker} from '@docker/actions-toolkit/lib/docker/docker'; 10 | import {Exec} from '@docker/actions-toolkit/lib/exec'; 11 | import {GitHub} from '@docker/actions-toolkit/lib/github'; 12 | import {Toolkit} from '@docker/actions-toolkit/lib/toolkit'; 13 | import {Util} from '@docker/actions-toolkit/lib/util'; 14 | 15 | import {BakeDefinition} from '@docker/actions-toolkit/lib/types/buildx/bake'; 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 | import * as stateHelper from './state-helper'; 22 | 23 | actionsToolkit.run( 24 | // main 25 | async () => { 26 | const startedTime = new Date(); 27 | 28 | const inputs: context.Inputs = await context.getInputs(); 29 | stateHelper.setSummaryInputs(inputs); 30 | core.debug(`inputs: ${JSON.stringify(inputs)}`); 31 | 32 | const toolkit = new Toolkit(); 33 | const gitAuthToken = process.env.BUILDX_BAKE_GIT_AUTH_TOKEN ?? inputs['github-token']; 34 | 35 | await core.group(`GitHub Actions runtime token ACs`, async () => { 36 | try { 37 | await GitHub.printActionsRuntimeTokenACs(); 38 | } catch (e) { 39 | core.warning(e.message); 40 | } 41 | }); 42 | 43 | await core.group(`Docker info`, async () => { 44 | try { 45 | await Docker.printVersion(); 46 | await Docker.printInfo(); 47 | } catch (e) { 48 | core.info(e.message); 49 | } 50 | }); 51 | 52 | await core.group(`Proxy configuration`, async () => { 53 | let dockerConfig: ConfigFile | undefined; 54 | let dockerConfigMalformed = false; 55 | try { 56 | dockerConfig = await Docker.configFile(); 57 | } catch (e) { 58 | dockerConfigMalformed = true; 59 | core.warning(`Unable to parse config file ${path.join(Docker.configDir, 'config.json')}: ${e}`); 60 | } 61 | if (dockerConfig && dockerConfig.proxies) { 62 | for (const host in dockerConfig.proxies) { 63 | let prefix = ''; 64 | if (Object.keys(dockerConfig.proxies).length > 1) { 65 | prefix = ' '; 66 | core.info(host); 67 | } 68 | for (const key in dockerConfig.proxies[host]) { 69 | core.info(`${prefix}${key}: ${dockerConfig.proxies[host][key]}`); 70 | } 71 | } 72 | } else if (!dockerConfigMalformed) { 73 | core.info('No proxy configuration found'); 74 | } 75 | }); 76 | 77 | if (!(await toolkit.buildx.isAvailable())) { 78 | core.setFailed(`Docker buildx is required. See https://github.com/docker/setup-buildx-action to set up buildx.`); 79 | return; 80 | } 81 | 82 | stateHelper.setTmpDir(Context.tmpDir()); 83 | 84 | await core.group(`Buildx version`, async () => { 85 | await toolkit.buildx.printVersion(); 86 | }); 87 | 88 | let builder: BuilderInfo; 89 | await core.group(`Builder info`, async () => { 90 | builder = await toolkit.builder.inspect(inputs.builder); 91 | stateHelper.setBuilderDriver(builder.driver ?? ''); 92 | stateHelper.setBuilderEndpoint(builder.nodes?.[0]?.endpoint ?? ''); 93 | core.info(JSON.stringify(builder, null, 2)); 94 | }); 95 | 96 | let definition: BakeDefinition | undefined; 97 | await core.group(`Parsing raw definition`, async () => { 98 | definition = await toolkit.buildxBake.getDefinition( 99 | { 100 | allow: inputs.allow, 101 | files: inputs.files, 102 | load: inputs.load, 103 | noCache: inputs['no-cache'], 104 | overrides: inputs.set, 105 | provenance: inputs.provenance, 106 | push: inputs.push, 107 | sbom: inputs.sbom, 108 | source: inputs.source, 109 | targets: inputs.targets, 110 | githubToken: gitAuthToken 111 | }, 112 | { 113 | cwd: inputs.workdir 114 | } 115 | ); 116 | }); 117 | if (!definition) { 118 | throw new Error('Bake definition not set'); 119 | } 120 | stateHelper.setBakeDefinition(definition); 121 | 122 | const args: string[] = await context.getArgs(inputs, definition, toolkit); 123 | const buildCmd = await toolkit.buildx.getCommand(args); 124 | const buildEnv = Object.assign({}, process.env, { 125 | BUILDX_BAKE_GIT_AUTH_TOKEN: gitAuthToken, 126 | BUILDX_METADATA_WARNINGS: 'true' 127 | }) as { 128 | [key: string]: string; 129 | }; 130 | 131 | await core.group(`Bake definition`, async () => { 132 | await Exec.getExecOutput(buildCmd.command, [...buildCmd.args, '--print'], { 133 | cwd: inputs.workdir, 134 | env: buildEnv, 135 | ignoreReturnCode: true 136 | }).then(res => { 137 | if (res.stderr.length > 0 && res.exitCode != 0) { 138 | throw Error(res.stderr); 139 | } 140 | }); 141 | }); 142 | 143 | let err: Error | undefined; 144 | await Exec.getExecOutput(buildCmd.command, buildCmd.args, { 145 | cwd: inputs.workdir, 146 | env: buildEnv, 147 | ignoreReturnCode: true 148 | }).then(res => { 149 | if (res.stderr.length > 0 && res.exitCode != 0) { 150 | err = Error(`buildx bake failed with: ${res.stderr.match(/(.*)\s*$/)?.[0]?.trim() ?? 'unknown error'}`); 151 | } 152 | }); 153 | 154 | const metadata = toolkit.buildxBake.resolveMetadata(); 155 | if (metadata) { 156 | await core.group(`Metadata`, async () => { 157 | const metadatadt = JSON.stringify(metadata, null, 2); 158 | core.info(metadatadt); 159 | core.setOutput('metadata', metadatadt); 160 | }); 161 | } 162 | 163 | let refs: Array = []; 164 | await core.group(`Build references`, async () => { 165 | refs = await buildRefs(toolkit, startedTime, inputs.builder); 166 | if (refs.length > 0) { 167 | for (const ref of refs) { 168 | core.info(ref); 169 | } 170 | stateHelper.setBuildRefs(refs); 171 | } else { 172 | core.info('No build references found'); 173 | } 174 | }); 175 | 176 | if (buildChecksAnnotationsEnabled()) { 177 | const warnings = toolkit.buildxBake.resolveWarnings(metadata); 178 | if (refs.length > 0 && warnings && warnings.length > 0) { 179 | const annotations = await Buildx.convertWarningsToGitHubAnnotations(warnings, refs); 180 | core.debug(`annotations: ${JSON.stringify(annotations, null, 2)}`); 181 | if (annotations && annotations.length > 0) { 182 | await core.group(`Generating GitHub annotations (${annotations.length} build checks found)`, async () => { 183 | for (const annotation of annotations) { 184 | core.warning(annotation.message, annotation); 185 | } 186 | }); 187 | } 188 | } 189 | } 190 | 191 | await core.group(`Check build summary support`, async () => { 192 | if (!buildSummaryEnabled()) { 193 | core.info('Build summary disabled'); 194 | } else if (GitHub.isGHES) { 195 | core.info('Build summary is not yet supported on GHES'); 196 | } else if (!(await toolkit.buildx.versionSatisfies('>=0.13.0'))) { 197 | core.info('Build summary requires Buildx >= 0.13.0'); 198 | } else if (refs.length == 0) { 199 | core.info('Build summary requires at least one build reference'); 200 | } else { 201 | core.info('Build summary supported!'); 202 | stateHelper.setSummarySupported(); 203 | } 204 | }); 205 | 206 | if (err) { 207 | throw err; 208 | } 209 | }, 210 | // post 211 | async () => { 212 | if (stateHelper.isSummarySupported) { 213 | await core.group(`Generating build summary`, async () => { 214 | try { 215 | const recordUploadEnabled = buildRecordUploadEnabled(); 216 | let recordRetentionDays: number | undefined; 217 | if (recordUploadEnabled) { 218 | recordRetentionDays = buildRecordRetentionDays(); 219 | } 220 | 221 | const buildxHistory = new BuildxHistory(); 222 | const exportRes = await buildxHistory.export({ 223 | refs: stateHelper.buildRefs, 224 | useContainer: buildExportLegacy() 225 | }); 226 | core.info(`Build records written to ${exportRes.dockerbuildFilename} (${Util.formatFileSize(exportRes.dockerbuildSize)})`); 227 | 228 | let uploadRes: UploadArtifactResponse | undefined; 229 | if (recordUploadEnabled) { 230 | uploadRes = await GitHub.uploadArtifact({ 231 | filename: exportRes.dockerbuildFilename, 232 | mimeType: 'application/gzip', 233 | retentionDays: recordRetentionDays 234 | }); 235 | } 236 | 237 | await GitHub.writeBuildSummary({ 238 | exportRes: exportRes, 239 | uploadRes: uploadRes, 240 | inputs: stateHelper.summaryInputs, 241 | bakeDefinition: stateHelper.bakeDefinition, 242 | driver: stateHelper.builderDriver, 243 | endpoint: stateHelper.builderEndpoint 244 | }); 245 | } catch (e) { 246 | core.warning(e.message); 247 | } 248 | }); 249 | } 250 | if (stateHelper.tmpDir.length > 0) { 251 | await core.group(`Removing temp folder ${stateHelper.tmpDir}`, async () => { 252 | fs.rmSync(stateHelper.tmpDir, {recursive: true}); 253 | }); 254 | } 255 | } 256 | ); 257 | 258 | async function buildRefs(toolkit: Toolkit, since: Date, builder?: string): Promise> { 259 | // get refs from metadata file 260 | const metaRefs = toolkit.buildxBake.resolveRefs(); 261 | if (metaRefs) { 262 | return metaRefs; 263 | } 264 | // otherwise, look for the very first build ref since the build has started 265 | if (!builder) { 266 | const currentBuilder = await toolkit.builder.inspect(); 267 | builder = currentBuilder.name; 268 | } 269 | const res = Buildx.refs({ 270 | dir: Buildx.refsDir, 271 | builderName: builder, 272 | since: since 273 | }); 274 | const refs: Array = []; 275 | for (const ref in res) { 276 | if (Object.prototype.hasOwnProperty.call(res, ref)) { 277 | refs.push(ref); 278 | } 279 | } 280 | return refs; 281 | } 282 | 283 | function buildChecksAnnotationsEnabled(): boolean { 284 | if (process.env.DOCKER_BUILD_CHECKS_ANNOTATIONS) { 285 | return Util.parseBool(process.env.DOCKER_BUILD_CHECKS_ANNOTATIONS); 286 | } 287 | return true; 288 | } 289 | 290 | function buildSummaryEnabled(): boolean { 291 | if (process.env.DOCKER_BUILD_NO_SUMMARY) { 292 | core.warning('DOCKER_BUILD_NO_SUMMARY is deprecated. Set DOCKER_BUILD_SUMMARY to false instead.'); 293 | return !Util.parseBool(process.env.DOCKER_BUILD_NO_SUMMARY); 294 | } else if (process.env.DOCKER_BUILD_SUMMARY) { 295 | return Util.parseBool(process.env.DOCKER_BUILD_SUMMARY); 296 | } 297 | return true; 298 | } 299 | 300 | function buildRecordUploadEnabled(): boolean { 301 | if (process.env.DOCKER_BUILD_RECORD_UPLOAD) { 302 | return Util.parseBool(process.env.DOCKER_BUILD_RECORD_UPLOAD); 303 | } 304 | return true; 305 | } 306 | 307 | function buildRecordRetentionDays(): number | undefined { 308 | let val: string | undefined; 309 | if (process.env.DOCKER_BUILD_EXPORT_RETENTION_DAYS) { 310 | core.warning('DOCKER_BUILD_EXPORT_RETENTION_DAYS is deprecated. Use DOCKER_BUILD_RECORD_RETENTION_DAYS instead.'); 311 | val = process.env.DOCKER_BUILD_EXPORT_RETENTION_DAYS; 312 | } else if (process.env.DOCKER_BUILD_RECORD_RETENTION_DAYS) { 313 | val = process.env.DOCKER_BUILD_RECORD_RETENTION_DAYS; 314 | } 315 | if (val) { 316 | const res = parseInt(val); 317 | if (isNaN(res)) { 318 | throw Error(`Invalid build record retention days: ${val}`); 319 | } 320 | return res; 321 | } 322 | } 323 | 324 | function buildExportLegacy(): boolean { 325 | if (process.env.DOCKER_BUILD_EXPORT_LEGACY) { 326 | return Util.parseBool(process.env.DOCKER_BUILD_EXPORT_LEGACY); 327 | } 328 | return false; 329 | } 330 | -------------------------------------------------------------------------------- /src/state-helper.ts: -------------------------------------------------------------------------------- 1 | import * as core from '@actions/core'; 2 | 3 | import {BakeDefinition} from '@docker/actions-toolkit/lib/types/buildx/bake'; 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 | export const bakeDefinition = process.env['STATE_bakeDefinition'] ? JSON.parse(process.env['STATE_bakeDefinition']) : undefined; 13 | 14 | export const buildRefs = process.env['STATE_buildRefs'] ? process.env['STATE_buildRefs'].split(',') : []; 15 | export const isSummarySupported = !!process.env['STATE_isSummarySupported']; 16 | 17 | export function setTmpDir(tmpDir: string) { 18 | core.saveState('tmpDir', tmpDir); 19 | } 20 | 21 | export function setBuilderDriver(builderDriver: string) { 22 | core.saveState('builderDriver', builderDriver); 23 | } 24 | 25 | export function setBuilderEndpoint(builderEndpoint: string) { 26 | core.saveState('builderEndpoint', builderEndpoint); 27 | } 28 | 29 | export function setBakeDefinition(bakeDefinition: BakeDefinition) { 30 | core.saveState('bakeDefinition', JSON.stringify(bakeDefinition)); 31 | } 32 | 33 | export function setBuildRefs(buildRefs: Array) { 34 | core.saveState('buildRefs', buildRefs.join(',')); 35 | } 36 | 37 | export function setSummarySupported() { 38 | core.saveState('isSummarySupported', 'true'); 39 | } 40 | 41 | export function setSummaryInputs(inputs: Inputs) { 42 | const res = {}; 43 | for (const key of Object.keys(inputs)) { 44 | if (key === 'github-token') { 45 | continue; 46 | } 47 | const value: string | string[] | boolean = inputs[key]; 48 | if (typeof value === 'boolean' && !value) { 49 | continue; 50 | } else if (Array.isArray(value) && value.length === 0) { 51 | continue; 52 | } else if (!value) { 53 | continue; 54 | } 55 | res[key] = value; 56 | } 57 | core.saveState('summaryInputs', JSON.stringify(res)); 58 | } 59 | -------------------------------------------------------------------------------- /subaction/list-targets/README.md: -------------------------------------------------------------------------------- 1 | ## About 2 | 3 | This subaction generates a list of Bake targets that can be used in a [GitHub matrix](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstrategymatrix), 4 | so you can distribute your builds across multiple runners. 5 | 6 | ![Screenshot](../../.github/bake-action.png) 7 | 8 | ___ 9 | 10 | * [Usage](#usage) 11 | * [Customizing](#customizing) 12 | * [inputs](#inputs) 13 | * [outputs](#outputs) 14 | 15 | ## Usage 16 | 17 | ```hcl 18 | # docker-bake.hcl 19 | group "validate" { 20 | targets = ["lint", "doctoc"] 21 | } 22 | 23 | target "lint" { 24 | target = "lint" 25 | } 26 | 27 | target "doctoc" { 28 | target = "doctoc" 29 | } 30 | ``` 31 | 32 | ```yaml 33 | jobs: 34 | prepare: 35 | runs-on: ubuntu-latest 36 | outputs: 37 | targets: ${{ steps.generate.outputs.targets }} 38 | steps: 39 | - 40 | name: Checkout 41 | uses: actions/checkout@v4 42 | - 43 | name: List targets 44 | id: generate 45 | uses: docker/bake-action/subaction/list-targets@v6 46 | with: 47 | target: validate 48 | 49 | validate: 50 | runs-on: ubuntu-latest 51 | needs: 52 | - prepare 53 | strategy: 54 | fail-fast: false 55 | matrix: 56 | target: ${{ fromJson(needs.prepare.outputs.targets) }} 57 | steps: 58 | - 59 | name: Checkout 60 | uses: actions/checkout@v4 61 | - 62 | name: Validate 63 | uses: docker/bake-action@v6 64 | with: 65 | targets: ${{ matrix.target }} 66 | ``` 67 | 68 | ## Customizing 69 | 70 | ### inputs 71 | 72 | | Name | Type | Description | 73 | |--------------|-------------|---------------------------------------------------------------------------------------------------------------------------------------------| 74 | | `workdir` | String | Working directory to use (defaults to `.`) | 75 | | `files` | List/CSV | List of [bake definition files](https://docs.docker.com/build/customize/bake/file-definition/) | 76 | | `target` | String | The target to use within the bake file | 77 | 78 | ### outputs 79 | 80 | The following outputs are available 81 | 82 | | Name | Type | Description | 83 | |------------|----------|----------------------------| 84 | | `targets` | List/CSV | List of extracted targest | 85 | -------------------------------------------------------------------------------- /subaction/list-targets/action.yml: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions 2 | name: 'List Bake targets' 3 | description: 'Generate a list of Bake targets to help distributing builds in your workflow' 4 | 5 | inputs: 6 | workdir: 7 | description: Working directory 8 | default: '.' 9 | required: false 10 | files: 11 | description: Comma separated list of Bake files 12 | required: false 13 | target: 14 | description: Bake target 15 | required: false 16 | 17 | outputs: 18 | targets: 19 | description: List of targets 20 | value: ${{ steps.generate.outputs.targets }} 21 | 22 | runs: 23 | using: composite 24 | steps: 25 | - 26 | name: Generate 27 | id: generate 28 | uses: actions/github-script@v7 29 | with: 30 | script: | 31 | let def; 32 | const files = `${{ inputs.files }}` ? `${{ inputs.files }}`.split(/[\r?\n,]+/).filter(Boolean) : []; 33 | const target = `${{ inputs.target }}`; 34 | 35 | await core.group(`Validating definition`, async () => { 36 | let args = ['buildx', 'bake']; 37 | for (const file of files) { 38 | args.push('--file', file); 39 | } 40 | if (target) { 41 | args.push(target); 42 | } 43 | args.push('--print'); 44 | 45 | const res = await exec.getExecOutput('docker', args, { 46 | ignoreReturnCode: true, 47 | silent: true, 48 | cwd: `${{ inputs.workdir }}` 49 | }); 50 | if (res.stderr.length > 0 && res.exitCode != 0) { 51 | throw new Error(res.stderr); 52 | } 53 | def = JSON.parse(res.stdout.trim()); 54 | core.info(JSON.stringify(def, null, 2)); 55 | }); 56 | 57 | await core.group(`Set output`, async () => { 58 | const targets = Object.keys(def.target); 59 | core.info(`targets: ${JSON.stringify(targets)}`); 60 | core.setOutput('targets', JSON.stringify(targets)); 61 | }); 62 | -------------------------------------------------------------------------------- /test/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine 2 | 3 | ARG name=world 4 | RUN echo "Hello ${name}!" 5 | -------------------------------------------------------------------------------- /test/config.hcl: -------------------------------------------------------------------------------- 1 | group "default" { 2 | targets = ["db", "app"] 3 | } 4 | 5 | group "release" { 6 | targets = ["db", "app-plus"] 7 | } 8 | 9 | target "db" { 10 | context = "./test" 11 | tags = ["docker.io/tonistiigi/db"] 12 | } 13 | 14 | target "app" { 15 | context = "./test" 16 | dockerfile = "Dockerfile" 17 | args = { 18 | name = "foo" 19 | } 20 | tags = [ 21 | "localhost:5000/name/app:latest", 22 | "localhost:5000/name/app:1.0.0" 23 | ] 24 | } 25 | 26 | target "cross" { 27 | platforms = [ 28 | "linux/amd64", 29 | "linux/arm64", 30 | "linux/386" 31 | ] 32 | } 33 | 34 | target "app-plus" { 35 | inherits = ["app", "cross"] 36 | args = { 37 | IAMPLUS = "true" 38 | } 39 | } 40 | 41 | target "app-proxy" { 42 | inherits = ["app"] 43 | dockerfile = "proxy.Dockerfile" 44 | } 45 | 46 | target "app-entitlements" { 47 | inherits = ["app"] 48 | entitlements = ["network.host"] 49 | } 50 | -------------------------------------------------------------------------------- /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/docker-bake.hcl: -------------------------------------------------------------------------------- 1 | variable "DESTDIR" { 2 | default = "/tmp/bake-build" 3 | } 4 | 5 | group "default" { 6 | targets = ["binary"] 7 | } 8 | 9 | target "binary" { 10 | target = "binary" 11 | output = [DESTDIR] 12 | } 13 | 14 | target "image" { 15 | target = "image" 16 | tags = ["localhost:5000/name/app:latest"] 17 | } 18 | -------------------------------------------------------------------------------- /test/go/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/docker/bake-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/group-matrix/docker-bake.hcl: -------------------------------------------------------------------------------- 1 | group "validate" { 2 | targets = ["lint", "validate-vendor", "validate-doctoc"] 3 | } 4 | 5 | target "lint" { 6 | name = "lint-${buildtags.name}" 7 | dockerfile = "./hack/dockerfiles/lint.Dockerfile" 8 | target = buildtags.target 9 | output = ["type=cacheonly"] 10 | matrix = { 11 | buildtags = [ 12 | { name = "default", tags = "", target = "golangci-lint" }, 13 | { name = "labs", tags = "dfrunsecurity dfparents", target = "golangci-lint" }, 14 | { name = "nydus", tags = "nydus", target = "golangci-lint" }, 15 | { name = "yaml", tags = "", target = "yamllint" }, 16 | { name = "proto", tags = "", target = "protolint" }, 17 | ] 18 | } 19 | } 20 | 21 | target "validate-vendor" { 22 | dockerfile = "./hack/dockerfiles/vendor.Dockerfile" 23 | target = "validate" 24 | output = ["type=cacheonly"] 25 | } 26 | 27 | target "validate-doctoc" { 28 | dockerfile = "./hack/dockerfiles/doctoc.Dockerfile" 29 | target = "validate-toc" 30 | output = ["type=cacheonly"] 31 | } 32 | -------------------------------------------------------------------------------- /test/group/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM busybox AS t1 4 | RUN echo "Hello t1" 5 | 6 | FROM busybox AS t2 7 | RUN echo "Hello t2" 8 | -------------------------------------------------------------------------------- /test/group/docker-bake.hcl: -------------------------------------------------------------------------------- 1 | group "default" { 2 | targets = ["t1", "t2"] 3 | } 4 | 5 | target "t1" { 6 | target = "t1" 7 | } 8 | 9 | target "t2" { 10 | target = "t2" 11 | } 12 | -------------------------------------------------------------------------------- /test/lint-other.Dockerfile: -------------------------------------------------------------------------------- 1 | frOM busybox as base 2 | cOpy lint-other.Dockerfile . 3 | 4 | froM busybox aS notused 5 | COPY lint-other.Dockerfile . 6 | 7 | from scratch 8 | COPy --from=base \ 9 | /lint-other.Dockerfile \ 10 | / 11 | -------------------------------------------------------------------------------- /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/lint.hcl: -------------------------------------------------------------------------------- 1 | group "default" { 2 | targets = ["lint", "lint-other", "lint-inline"] 3 | } 4 | target "lint" { 5 | dockerfile = "lint.Dockerfile" 6 | } 7 | target "lint-other" { 8 | dockerfile = "lint-other.Dockerfile" 9 | } 10 | target "lint-inline" { 11 | dockerfile-inline = "FRoM alpine\nENTRYPOINT [\"echo\", \"hello\"]" 12 | } 13 | -------------------------------------------------------------------------------- /test/multi-files/docker-bake.hcl: -------------------------------------------------------------------------------- 1 | group "default" { 2 | targets = ["t3"] 3 | } 4 | 5 | target "t3" { 6 | name = "${item.tag}" 7 | matrix = { 8 | item = t3 9 | } 10 | args = { 11 | VERSION = "${item.version}" 12 | DUMMY_ARG = "${item.arg}" 13 | } 14 | tags = ["${item.tag}"] 15 | } 16 | -------------------------------------------------------------------------------- /test/multi-files/docker-bake.json: -------------------------------------------------------------------------------- 1 | { 2 | "t3": [ 3 | { 4 | "version": "v1", 5 | "arg": "v1-value", 6 | "tag": "v1-tag" 7 | }, 8 | { 9 | "version": "v2", 10 | "arg": "v2-value", 11 | "tag": "v2-tag" 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------