Compare commits

..

No commits in common. "master" and "v4.4.0" have entirely different histories.

38 changed files with 5310 additions and 12862 deletions

View File

@ -1,12 +1,2 @@
/coverage
# Dependency directories
node_modules/
jspm_packages/
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
/node_modules

View File

@ -1,3 +0,0 @@
/dist/**
/coverage/**
/node_modules/**

View File

@ -1,19 +1,18 @@
{
"env": {
"node": true,
"es6": true,
"jest": true
"es2021": true,
"jest/globals": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:jest/recommended",
"plugin:prettier/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 2023,
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": [

2
.gitattributes vendored
View File

@ -1,5 +1,3 @@
/.yarn/releases/** binary
/.yarn/plugins/** binary
/__tests__/fixtures/** -linguist-detectable
/dist/** linguist-generated=true
/lib/** linguist-generated=true

1
.github/CODEOWNERS vendored Normal file
View File

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

View File

@ -1,3 +0,0 @@
# Code of conduct
- [Moby community guidelines](https://github.com/moby/moby/blob/master/CONTRIBUTING.md#moby-community-guidelines)

View File

@ -1,101 +0,0 @@
# https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema
name: Bug Report
description: Report a bug
labels:
- status/triage
body:
- type: markdown
attributes:
value: |
Thank you for taking the time to report a bug!
If this is a security issue please report it to the [Docker Security team](mailto:security@docker.com).
- type: checkboxes
attributes:
label: Contributing guidelines
description: >
Make sure you've read the contributing guidelines before proceeding.
options:
- label: I've read the [contributing guidelines](https://github.com/docker/metadata-action/blob/master/.github/CONTRIBUTING.md) and wholeheartedly agree
required: true
- type: checkboxes
attributes:
label: "I've found a bug, and:"
description: |
Make sure that your request fulfills all of the following requirements.
If one requirement cannot be satisfied, explain in detail why.
options:
- label: The documentation does not mention anything about my problem
- label: There are no open or closed issues that are related to my problem
- type: textarea
attributes:
label: Description
description: >
Provide a brief description of the bug in 1-2 sentences.
validations:
required: true
- type: textarea
attributes:
label: Expected behaviour
description: >
Describe precisely what you'd expect to happen.
validations:
required: true
- type: textarea
attributes:
label: Actual behaviour
description: >
Describe precisely what is actually happening.
validations:
required: true
- type: input
attributes:
label: Repository URL
description: >
Enter the URL of the repository where you are experiencing the
issue. If your repository is private, provide a link to a minimal
repository that reproduces the issue.
- type: input
attributes:
label: Workflow run URL
description: >
Enter the URL of the GitHub Action workflow run if public (e.g.
`https://github.com/<user>/<repo>/actions/runs/<id>`)
- type: textarea
attributes:
label: YAML workflow
description: |
Provide the YAML of the workflow that's causing the issue.
Make sure to remove any sensitive information.
render: yaml
validations:
required: true
- type: textarea
attributes:
label: Workflow logs
description: >
[Attach](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/attaching-files)
the [log file of your workflow run](https://docs.github.com/en/actions/managing-workflow-runs/using-workflow-run-logs#downloading-logs)
and make sure to remove any sensitive information.
- type: textarea
attributes:
label: BuildKit logs
description: >
If applicable, provide the [BuildKit container logs](https://docs.docker.com/build/ci/github-actions/configure-builder/#buildkit-container-logs)
render: text
- type: textarea
attributes:
label: Additional info
description: |
Provide any additional information that could be useful.

34
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

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

View File

@ -1,9 +0,0 @@
# 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
blank_issues_enabled: true
contact_links:
- name: Questions and Discussions
url: https://github.com/docker/metadata-action/discussions/new
about: Use Github Discussions to ask questions and/or open discussion topics.
- name: Documentation
url: https://docs.docker.com/build/ci/github-actions/
about: Read the documentation.

View File

@ -1,15 +0,0 @@
# https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema
name: Feature request
description: Missing functionality? Come tell us about it!
labels:
- kind/enhancement
- status/triage
body:
- type: textarea
id: description
attributes:
label: Description
description: What is the feature you want to see?
validations:
required: true

12
.github/SECURITY.md vendored
View File

@ -1,12 +0,0 @@
# Reporting security issues
The project maintainers take security seriously. If you discover a security
issue, please bring it to their attention right away!
**Please _DO NOT_ file a public issue**, instead send your report privately to
[security@docker.com](mailto:security@docker.com).
Security reports are greatly appreciated, and we will publicly thank you for it.
We also like to send gifts&mdash;if you'd like Docker swag, make sure to let
us know. We currently do not offer a paid security bounty program, but are not
ruling it out in the future.

32
.github/SUPPORT.md vendored Normal file
View File

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

View File

@ -11,7 +11,6 @@ updates:
directory: "/"
schedule:
interval: "daily"
versioning-strategy: "increase"
allow:
- dependency-type: "production"
labels:

View File

@ -1,9 +1,5 @@
name: ci
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on:
schedule:
- cron: '0 10 * * *'
@ -17,39 +13,14 @@ on:
env:
DOCKER_IMAGE: localhost:5000/name/app
BUILDX_VERSION: latest
jobs:
context:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
context:
- workflow
- git
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Docker meta
uses: ./
with:
context: ${{ matrix.context }}
multi-images:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
version: ${{ env.BUILDX_VERSION }}
driver: docker
uses: actions/checkout@v3
-
name: Docker meta
uses: ./
@ -80,13 +51,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
version: ${{ env.BUILDX_VERSION }}
driver: docker
uses: actions/checkout@v3
-
name: Docker meta
uses: ./
@ -116,13 +81,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
version: ${{ env.BUILDX_VERSION }}
driver: docker
uses: actions/checkout@v3
-
name: Docker meta
uses: ./
@ -150,13 +109,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
version: ${{ env.BUILDX_VERSION }}
driver: docker
uses: actions/checkout@v3
-
name: Docker meta
uses: ./
@ -181,13 +134,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
version: ${{ env.BUILDX_VERSION }}
driver: docker
uses: actions/checkout@v3
-
name: Docker meta
uses: ./
@ -204,13 +151,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
version: ${{ env.BUILDX_VERSION }}
driver: docker
uses: actions/checkout@v3
-
name: Docker meta
uses: ./
@ -220,18 +161,12 @@ jobs:
name=ghcr.io/name/app,enable=${{ github.event_name == 'pull_request' }}
name=ghcr.io/name/release,enable=${{ startsWith(github.ref, 'refs/tags/') }}
custom-labels-annotations:
labels:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
version: ${{ env.BUILDX_VERSION }}
driver: docker
uses: actions/checkout@v3
-
name: Docker meta
uses: ./
@ -244,24 +179,13 @@ jobs:
org.opencontainers.image.title=MyCustomTitle
org.opencontainers.image.description=this is a "good" example
org.opencontainers.image.vendor=MyCompany
annotations: |
maintainer=Foo
org.opencontainers.image.title=MyFooTitle
org.opencontainers.image.description=this is a "foo" example
org.opencontainers.image.vendor=MyFooCompany
global-exps:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
version: ${{ env.BUILDX_VERSION }}
driver: docker
uses: actions/checkout@v3
-
name: Docker meta
uses: ./
@ -269,8 +193,6 @@ jobs:
images: |
${{ env.DOCKER_IMAGE }}
ghcr.io/name/app
labels: |
org.opencontainers.image.created={{commit_date 'YYYY-MM-DDTHH:mm:ss.SSS[Z]'}}
tags: |
type=sha
type=raw,value=gexp-branch-{{branch}}
@ -284,13 +206,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
version: ${{ env.BUILDX_VERSION }}
driver: docker
uses: actions/checkout@v3
-
name: Docker meta
id: meta
@ -319,13 +235,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
version: ${{ env.BUILDX_VERSION }}
driver-opts: network=host
uses: actions/checkout@v3
-
name: Docker meta
id: docker_meta
@ -342,9 +252,17 @@ jobs:
type=semver,pattern=v{{major}}.{{minor}}
type=semver,pattern=v{{major}}
type=sha
-
name: Set up QEMU
uses: docker/setup-qemu-action@v2
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
with:
driver-opts: network=host
-
name: Build and push to local registry
uses: docker/build-push-action@v6
uses: docker/build-push-action@v4
with:
context: ./test
file: ./test/Dockerfile
@ -361,18 +279,17 @@ jobs:
name: Check manifest
run: |
docker buildx imagetools inspect ${{ env.DOCKER_IMAGE }}:${{ steps.docker_meta.outputs.version }}
-
name: Dump context
if: always()
uses: crazy-max/ghaction-dump-context@v2
bake:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
version: ${{ env.BUILDX_VERSION }}
uses: actions/checkout@v3
-
name: Docker meta
id: docker_meta
@ -390,14 +307,19 @@ jobs:
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha
-
name: Set up QEMU
uses: docker/setup-qemu-action@v2
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
-
name: Build
uses: docker/bake-action@v6
uses: docker/bake-action@v2
with:
files: |
./test/docker-bake.hcl
cwd://${{ steps.docker_meta.outputs.bake-file-tags }}
cwd://${{ steps.docker_meta.outputs.bake-file-labels }}
${{ steps.docker_meta.outputs.bake-file }}
targets: |
release
@ -412,13 +334,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
version: ${{ env.BUILDX_VERSION }}
driver: docker
uses: actions/checkout@v3
-
name: Docker meta
id: meta
@ -430,7 +346,7 @@ jobs:
sep-tags: ${{ matrix.sep }}
-
name: Tags
uses: actions/github-script@v7
uses: actions/github-script@v6
with:
script: |
console.log(`${{ steps.meta.outputs.tags }}`);
@ -440,13 +356,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
version: ${{ env.BUILDX_VERSION }}
driver: docker
uses: actions/checkout@v3
-
name: Docker meta
id: meta
@ -457,11 +367,9 @@ jobs:
ghcr.io/name/app
labels: |
maintainer=CrazyMax
annotations: |
maintainer=Foo
-
name: Build
uses: docker/build-push-action@v6
uses: docker/build-push-action@v4
with:
context: ./test
file: ./test/output.Dockerfile
@ -469,165 +377,4 @@ jobs:
DOCKER_METADATA_OUTPUT_VERSION
DOCKER_METADATA_OUTPUT_TAGS
DOCKER_METADATA_OUTPUT_LABELS
DOCKER_METADATA_OUTPUT_ANNOTATIONS
DOCKER_METADATA_OUTPUT_JSON
no-output-env:
runs-on: ubuntu-latest
env:
DOCKER_METADATA_SET_OUTPUT_ENV: false
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Docker meta
id: meta
uses: ./
with:
images: |
${{ env.DOCKER_IMAGE }}
ghcr.io/name/app
labels: |
maintainer=CrazyMax
annotations: |
maintainer=Foo
-
name: No output environment variables set
shell: bash
run: |
[[ "$(printenv | grep "^DOCKER_METADATA_OUTPUT_" | wc -l)" -eq 0 ]] || exit 1
bake-annotations:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
version: ${{ env.BUILDX_VERSION }}
-
name: Docker meta
id: docker_meta
uses: ./
with:
images: |
${{ env.DOCKER_IMAGE }}
ghcr.io/name/app
tags: |
type=schedule
type=ref,event=branch
type=ref,event=tag
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha
env:
DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,index
-
name: Build
uses: docker/bake-action@v6
with:
files: |
./test/docker-bake.hcl
cwd://${{ steps.docker_meta.outputs.bake-file-tags }}
cwd://${{ steps.docker_meta.outputs.bake-file-annotations }}
targets: |
release
no-images:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
version: ${{ env.BUILDX_VERSION }}
driver: docker
-
name: Docker meta
uses: ./
with:
tags: |
type=schedule
type=ref,event=branch
type=ref,event=tag
type=ref,event=pr
type=sha
-
name: Print envs
run: env|sort
bake-path-context:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
version: latest
-
name: Docker meta
id: docker_meta
uses: ./
-
name: Build
uses: docker/bake-action@v6
with:
source: .
files: |
./test/docker-bake.hcl
${{ steps.docker_meta.outputs.bake-file-tags }}
${{ steps.docker_meta.outputs.bake-file-labels }}
targets: |
release
sha-short:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
short-length:
- ''
- 16
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
version: ${{ env.BUILDX_VERSION }}
driver: docker
-
name: Docker meta
uses: ./
with:
images: |
${{ env.DOCKER_IMAGE }}
ghcr.io/name/app
tags: |
type=sha
env:
DOCKER_METADATA_SHORT_SHA_LENGTH: ${{ matrix.short-length }}
dump:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Dump context
uses: crazy-max/ghaction-dump-context@v2

View File

@ -1,21 +0,0 @@
name: publish
on:
release:
types:
- published
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
packages: write
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Publish
uses: actions/publish-immutable-action@v0.0.4

View File

@ -1,9 +1,5 @@
name: test
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on:
push:
branches:
@ -15,14 +11,16 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v3
-
name: Test
uses: docker/bake-action@v6
uses: docker/bake-action@v2
with:
targets: test
-
name: Upload coverage
uses: codecov/codecov-action@v5
uses: codecov/codecov-action@v3
with:
file: ./coverage/clover.xml
token: ${{ secrets.CODECOV_TOKEN }}

View File

@ -1,9 +1,5 @@
name: validate
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on:
push:
branches:
@ -15,17 +11,16 @@ jobs:
prepare:
runs-on: ubuntu-latest
outputs:
targets: ${{ steps.generate.outputs.targets }}
targets: ${{ steps.targets.outputs.matrix }}
steps:
-
name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
-
name: List targets
id: generate
uses: docker/bake-action/subaction/list-targets@v6
with:
target: validate
name: Targets matrix
id: targets
run: |
echo "matrix=$(docker buildx bake validate --print | jq -cr '.group.validate.targets')" >> $GITHUB_OUTPUT
validate:
runs-on: ubuntu-latest
@ -36,8 +31,11 @@ jobs:
matrix:
target: ${{ fromJson(needs.prepare.outputs.targets) }}
steps:
-
name: Checkout
uses: actions/checkout@v3
-
name: Validate
uses: docker/bake-action@v6
uses: docker/bake-action@v2
with:
targets: ${{ matrix.target }}

71
.gitignore vendored
View File

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

View File

@ -1,6 +0,0 @@
# Dependency directories
node_modules/
jspm_packages/
# yarn v2
.yarn/

File diff suppressed because one or more lines are too long

View File

@ -1,13 +0,0 @@
logFilters:
- code: YN0013
level: discard
- code: YN0019
level: discard
- code: YN0076
level: discard
nodeLinker: node-modules
plugins:
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
spec: "@yarnpkg/plugin-interactive-tools"

209
README.md
View File

@ -45,11 +45,9 @@ ___
* [`{{base_ref}}`](#base_ref)
* [`{{is_default_branch}}`](#is_default_branch)
* [`{{date '<format>' tz='<timezone>'}}`](#date-format-tztimezone)
* [`{{commit_date '<format>' tz='<timezone>'}}`](#commit_date-format-tztimezone)
* [Major version zero](#major-version-zero)
* [JSON output object](#json-output-object)
* [Overwrite labels and annotations](#overwrite-labels-and-annotations)
* [Annotations](#annotations)
* [Overwrite labels](#overwrite-labels)
* [Contributing](#contributing)
## Usage
@ -76,23 +74,23 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
-
name: Docker meta
id: meta
uses: docker/metadata-action@v5
uses: docker/metadata-action@v4
with:
images: name/app
-
name: Login to DockerHub
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Build and push
uses: docker/build-push-action@v6
uses: docker/build-push-action@v4
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
@ -130,11 +128,11 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
-
name: Docker meta
id: meta
uses: docker/metadata-action@v5
uses: docker/metadata-action@v4
with:
images: |
name/app
@ -146,13 +144,13 @@ jobs:
-
name: Login to DockerHub
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Build and push
uses: docker/build-push-action@v6
uses: docker/build-push-action@v4
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
@ -206,10 +204,13 @@ jobs:
docker:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v3
-
name: Docker meta
id: meta
uses: docker/metadata-action@v5
uses: docker/metadata-action@v4
with:
images: |
name/app
@ -221,16 +222,16 @@ jobs:
type=sha
-
name: Build
uses: docker/bake-action@v6
uses: docker/bake-action@v2
with:
files: |
./docker-bake.hcl
cwd://${{ steps.meta.outputs.bake-file }}
${{ steps.meta.outputs.bake-file }}
targets: build
```
Content of `${{ steps.meta.outputs.bake-file }}` file, combining tags and
labels, will look like this with `refs/tags/v1.2.3` ref:
Content of `${{ steps.meta.outputs.bake-file }}` file will look like this with
`refs/tags/v1.2.3` ref:
```json
{
@ -261,27 +262,11 @@ labels, will look like this with `refs/tags/v1.2.3` ref:
}
```
You can also use the `bake-file-tags` and `bake-file-labels` outputs if you
just want to use tags and/or labels respectively. The following example is
similar to the previous one:
```yaml
-
name: Build
uses: docker/bake-action@v6
with:
files: |
./docker-bake.hcl
cwd://${{ steps.meta.outputs.bake-file-tags }}
cwd://${{ steps.meta.outputs.bake-file-labels }}
targets: build
```
## Customizing
### inputs
The following inputs can be used as `step.with` keys:
Following inputs can be used as `step.with` keys
> `List` type is a newline-delimited string
> ```yaml
@ -292,48 +277,40 @@ The following inputs can be used as `step.with` keys:
> ```
| Name | Type | Description |
|-------------------|--------|------------------------------------------------------------------------------|
|---------------------|--------|-------------------------------------------------------------------------------|
| `context` | String | Where to get context data. Allowed options are: `workflow` (default), `git`. |
| `images` | List | List of Docker images to use as base name for tags |
| `tags` | List | List of [tags](#tags-input) as key-value pair attributes |
| `flavor` | List | [Flavor](#flavor-input) to apply |
| `labels` | List | List of custom labels |
| `annotations` | List | List of custom annotations |
| `sep-tags` | String | Separator to use for tags output (default `\n`) |
| `sep-labels` | String | Separator to use for labels output (default `\n`) |
| `sep-annotations` | String | Separator to use for annotations output (default `\n`) |
| `bake-target` | String | Bake target name (default `docker-metadata-action`) |
### outputs
The following outputs are available:
Following outputs are available
| Name | Type | Description |
|-------------------------|--------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------|
|---------------|---------|--------------------------------------------------------------------------------------------|
| `version` | String | Docker image version |
| `tags` | String | Docker tags |
| `labels` | String | Docker labels |
| `annotations` | String | [Annotations](https://github.com/moby/buildkit/blob/master/docs/annotations.md) |
| `json` | String | JSON output of tags and labels |
| `bake-file-tags` | File | [Bake file definition](https://docs.docker.com/build/bake/reference/) path with tags |
| `bake-file-labels` | File | [Bake file definition](https://docs.docker.com/build/bake/reference/) path with labels |
| `bake-file-annotations` | File | [Bake file definition](https://docs.docker.com/build/bake/reference/) path with [annotations](https://github.com/moby/buildkit/blob/master/docs/annotations.md) |
| `bake-file` | File | [Bake file definition](https://docs.docker.com/build/customize/bake/file-definition/) path |
Alternatively, each output is also exported as an environment variable when `DOCKER_METADATA_SET_OUTPUT_ENV` is `true`:
Alternatively, each output is also exported as an environment variable:
* `DOCKER_METADATA_OUTPUT_VERSION`
* `DOCKER_METADATA_OUTPUT_TAGS`
* `DOCKER_METADATA_OUTPUT_LABELS`
* `DOCKER_METADATA_OUTPUT_ANNOTATIONS`
* `DOCKER_METADATA_OUTPUT_JSON`
* `DOCKER_METADATA_OUTPUT_BAKE_FILE_TAGS`
* `DOCKER_METADATA_OUTPUT_BAKE_FILE_LABELS`
* `DOCKER_METADATA_OUTPUT_BAKE_FILE_ANNOTATIONS`
* `DOCKER_METADATA_OUTPUT_BAKE_FILE`
So it can be used with our [Docker Build Push action](https://github.com/docker/build-push-action/):
```yaml
- uses: docker/build-push-action@v6
- uses: docker/build-push-action@v4
with:
build-args: |
DOCKER_METADATA_OUTPUT_JSON
@ -342,11 +319,8 @@ So it can be used with our [Docker Build Push action](https://github.com/docker/
### environment variables
| Name | Type | Description |
|--------------------------------------|--------|----------------------------------------------------------------------------------------------------------------------------------------------|
|-------------------------------|------|------------------------------------------------------------------------------------------------------------|
| `DOCKER_METADATA_PR_HEAD_SHA` | Bool | If `true`, set associated head SHA instead of commit SHA that triggered the workflow on pull request event |
| `DOCKER_METADATA_SHORT_SHA_LENGTH` | Number | Specifies the length of the [short commit SHA](#typesha) to ensure uniqueness. Default is `7`, but can be increased for larger repositories. |
| `DOCKER_METADATA_ANNOTATIONS_LEVELS` | String | Comma separated list of annotations levels to set for annotations output separated (default `manifest`) |
| `DOCKER_METADATA_SET_OUTPUT_ENV` | Bool | If `true`, sets each output as an environment variable (default `true`) |
## `context` input
@ -385,8 +359,6 @@ images: |
* `name=<string>` image base name
* `enable=<true|false>` enable this entry (default `true`)
If `images` is empty, tags will be generated without base name.
## `flavor` input
`flavor` defines a global behavior for [`tags`](#tags-input):
@ -705,26 +677,7 @@ tags: |
type=sha,format=long
```
Output Git short commit (or long if specified) as Docker tag like
`sha-860c190`.
By default, the length of the short commit SHA is `7` characters. You can
increase this length for larger repositories by setting the
[`DOCKER_METADATA_SHORT_SHA_LENGTH` environment variable](#environment-variables):
```yaml
-
name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: |
name/app
tags: |
type=sha
env:
DOCKER_METADATA_SHORT_SHA_LENGTH: 12
```
Output Git short commit (or long if specified) as Docker tag like `sha-ad132f5`.
Extended attributes and default values:
@ -758,7 +711,6 @@ generated by default (`auto` mode) for:
* [`type=ref,event=tag`](#typeref)
* [`type=semver,pattern=...`](#typesemver)
* [`type=pep440,pattern=...`](#typepep440)
* [`type=match,pattern=...`](#typematch)
For conditionally tagging with latest for a specific branch name, e.g. if your
@ -800,8 +752,7 @@ Each tags `type` attribute has a default priority:
### Global expressions
The following [Handlebars' template](https://handlebarsjs.com/guide/) expressions
for `prefix`, `suffix`, `value` and `enable` attributes of `tags` input are
available:
for `prefix`, `suffix`, `value` and `enable` attributes are available:
```yaml
tags: |
@ -811,13 +762,6 @@ tags: |
type=raw,value=mytag-{{branch}}-{{sha}}
```
They can also be applied to `labels` and `annotations` inputs:
```yaml
labels: |
org.opencontainers.image.created={{commit_date 'YYYY-MM-DDTHH:mm:ss.SSS[Z]'}}
```
#### `{{branch}}`
Returns the branch name that triggered the workflow run. Will be empty if not
@ -880,20 +824,6 @@ Default `tz` is UTC.
| `{{date 'dddd, MMMM Do YYYY, h:mm:ss a'}}` | `Friday, January 10th 2020, 3:25:50 pm` |
| `{{date 'YYYYMMDD-HHmmss' tz='Asia/Tokyo'}}` | `20200110-093000` |
#### `{{commit_date '<format>' tz='<timezone>'}}`
Returns the date when the current git commit is committed, rendered by its
[moment format](https://momentjs.com/docs/#/displaying/format/). It falls back
to the current date if the commit date is not available.
Default `tz` is UTC.
| Expression | Output example |
|-----------------------------------------------------|-----------------------------------------|
| `{{commit_date 'YYYYMMDD'}}` | `20200110` |
| `{{commit_date 'dddd, MMMM Do YYYY, h:mm:ss a'}}` | `Friday, January 10th 2020, 3:25:50 pm` |
| `{{commit_date 'YYYYMMDD-HHmmss' tz='Asia/Tokyo'}}` | `20200110-093000` |
### Major version zero
Major version zero (`0.y.z`) is for initial development and **may** change at
@ -921,13 +851,13 @@ that you can reuse them further in your workflow using the [`fromJSON` function]
```yaml
-
name: Docker meta
uses: docker/metadata-action@v5
uses: docker/metadata-action@v4
id: meta
with:
images: name/app
-
name: Build and push
uses: docker/build-push-action@v6
uses: docker/build-push-action@v4
with:
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
@ -937,17 +867,16 @@ that you can reuse them further in your workflow using the [`fromJSON` function]
REVISION=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.revision'] }}
```
### Overwrite labels and annotations
### Overwrite labels
If some [OCI Image Format Specification](https://github.com/opencontainers/image-spec/blob/master/annotations.md)
generated are not suitable as labels/annotations, you can overwrite them like
this:
labels generated are not suitable, you can overwrite them like this:
```yaml
-
name: Docker meta
id: meta
uses: docker/metadata-action@v5
uses: docker/metadata-action@v4
with:
images: name/app
labels: |
@ -957,78 +886,6 @@ this:
org.opencontainers.image.vendor=MyCompany
```
### Annotations
Since Buildx 0.12, it is possible to set annotations to your image through the
`--annotation` flag.
With the [`build-push-action`](https://github.com/docker/build-push-action/),
you can set the `annotations` input with the value of the `annotations` output
of the `metadata-action`:
```yaml
-
name: Docker meta
uses: docker/metadata-action@v5
with:
images: name/app
-
name: Build and push
uses: docker/build-push-action@v6
with:
tags: ${{ steps.meta.outputs.tags }}
annotations: ${{ steps.meta.outputs.annotations }}
```
The same can be done with the [`bake-action`](https://github.com/docker/bake-action/):
```yaml
-
name: Docker meta
uses: docker/metadata-action@v5
with:
images: name/app
-
name: Build
uses: docker/bake-action@v6
with:
files: |
./docker-bake.hcl
cwd://${{ steps.meta.outputs.bake-file-tags }}
cwd://${{ steps.meta.outputs.bake-file-annotations }}
targets: build
```
Note that annotations can be attached at many different levels within a manifest.
By default, the generated annotations will be attached to image manifests,
but different registries may expect annotations at different places;
a common practice is to read annotations at _image indexes_ if present,
which are often used by multi-arch builds to index platform-specific images.
If you want to specify level(s) for your annotations, you can use the
[`DOCKER_METADATA_ANNOTATIONS_LEVELS` environment variable](#environment-variables)
with a comma separated list of all levels the annotations should be attached to (defaults to `manifest`).
The following configuration demonstrates the ability to attach annotations to both image manifests and image indexes,
though your registry may only need annotations at the index level. (That is, `index` alone may be enough.)
Please consult the documentation of your registry.
```yaml
-
name: Docker meta
uses: docker/metadata-action@v5
with:
images: name/app
env:
DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,index
-
name: Build and push
uses: docker/build-push-action@v6
with:
tags: ${{ steps.meta.outputs.tags }}
annotations: ${{ steps.meta.outputs.annotations }}
```
More information about annotations in the [BuildKit documentation](https://github.com/moby/buildkit/blob/master/docs/annotations.md).
## Contributing
Want to contribute? Awesome! You can find information about contributing to

300
UPGRADE.md Normal file
View File

@ -0,0 +1,300 @@
# Upgrade notes
## v2 to v3
* Repository has been moved to docker org. Replace `crazy-max/ghaction-docker-meta@v2`
with `docker/metadata-action@v4`
* The default bake target has been changed: `ghaction-docker-meta` > `docker-metadata-action`
## v1 to v2
* [inputs](#inputs)
* [`tag-sha`](#tag-sha)
* [`tag-edge` / `tag-edge-branch`](#tag-edge--tag-edge-branch)
* [`tag-semver`](#tag-semver)
* [`tag-match` / `tag-match-group`](#tag-match--tag-match-group)
* [`tag-latest`](#tag-latest)
* [`tag-schedule`](#tag-schedule)
* [`tag-custom` / `tag-custom-only`](#tag-custom--tag-custom-only)
* [`label-custom`](#label-custom)
* [Basic workflow](#basic-workflow)
* [Semver workflow](#semver-workflow)
### inputs
| New | Unchanged | Removed |
|------------|-----------------|--------------------|
| `tags` | `images` | `tag-sha` |
| `flavor` | `sep-tags` | `tag-edge` |
| `labels` | `sep-labels` | `tag-edge-branch` |
| | | `tag-semver` |
| | | `tag-match` |
| | | `tag-match-group` |
| | | `tag-latest` |
| | | `tag-schedule` |
| | | `tag-custom` |
| | | `tag-custom-only` |
| | | `label-custom` |
#### `tag-sha`
```yaml
tags: |
type=sha
```
#### `tag-edge` / `tag-edge-branch`
```yaml
tags: |
# default branch
type=edge
# specify branch
type=edge,branch=main
```
#### `tag-semver`
```yaml
tags: |
type=semver,pattern={{version}}
```
#### `tag-match` / `tag-match-group`
```yaml
tags: |
type=match,pattern=v(.*),group=1
```
#### `tag-latest`
`tag-latest` is now handled through the [`flavor` input](README.md#flavor-input):
```yaml
flavor: |
latest=auto
```
See also the notes about ["latest tag" behavior](README.md#latest-tag)
#### `tag-schedule`
```yaml
tags: |
# default tag (nightly)
type=schedule
# specific pattern
type=schedule,pattern={{date 'YYYYMMDD'}}
```
#### `tag-custom` / `tag-custom-only`
```yaml
tags: |
type=raw,value=foo
type=raw,value=bar
# or
type=raw,foo
type=raw,bar
# or
foo
bar
```
#### `label-custom`
Same behavior for `labels`:
```yaml
labels: |
maintainer=CrazyMax
```
### Basic workflow
```yaml
# v1
name: ci
on:
push:
branches:
- 'master'
tags:
- 'v*'
pull_request:
branches:
- 'master'
jobs:
docker:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v3
-
name: Docker meta
id: meta
uses: docker/metadata-action@v1
with:
images: name/app
-
name: Login to DockerHub
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Build and push
uses: docker/build-push-action@v4
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
```
```yaml
# v2
name: ci
on:
push:
branches:
- 'master'
tags:
- 'v*'
pull_request:
branches:
- 'master'
jobs:
docker:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v3
-
name: Docker meta
id: meta
uses: docker/metadata-action@v4
with:
images: name/app
-
name: Login to DockerHub
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Build and push
uses: docker/build-push-action@v4
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
```
### Semver workflow
```yaml
# v1
name: ci
on:
push:
branches:
- 'master'
tags:
- 'v*'
pull_request:
branches:
- 'master'
jobs:
docker:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v3
-
name: Docker meta
id: meta
uses: docker/metadata-action@v1
with:
images: name/app
tag-semver: |
{{version}}
{{major}}.{{minor}}
-
name: Login to DockerHub
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Build and push
uses: docker/build-push-action@v4
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
```
```yaml
# v2
name: ci
on:
push:
branches:
- 'master'
tags:
- 'v*'
pull_request:
branches:
- 'master'
jobs:
docker:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v3
-
name: Docker meta
id: meta
uses: docker/metadata-action@v4
with:
images: name/app
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
-
name: Login to DockerHub
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Build and push
uses: docker/build-push-action@v4
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
```

View File

@ -204,20 +204,4 @@ export const context = {
}
};
export const getOctokit = jest.fn(() => ({
rest: {
repos: {
getCommit: jest.fn(() =>
Promise.resolve({
data: {
commit: {
committer: {
date: '2024-11-13T13:42:28Z'
}
}
}
})
)
}
}
}));
export const getOctokit = jest.fn();

View File

@ -5,12 +5,9 @@ import * as path from 'path';
import {Context} from '@actions/github/lib/context';
import {Git} from '@docker/actions-toolkit/lib/git';
import {GitHub} from '@docker/actions-toolkit/lib/github';
import {Toolkit} from '@docker/actions-toolkit/lib/toolkit';
import {ContextSource, getContext, getInputs, Inputs} from '../src/context';
const toolkit = new Toolkit({githubToken: 'fake-github-token'});
beforeEach(() => {
jest.clearAllMocks();
jest.spyOn(GitHub, 'context', 'get').mockImplementation((): Context => {
@ -42,10 +39,8 @@ describe('getInputs', () => {
githubToken: '',
images: ['moby/buildkit', 'ghcr.io/moby/mbuildkit'],
labels: [],
annotations: [],
sepLabels: '\n',
sepTags: '\n',
sepAnnotations: '\n',
tags: [],
} as Inputs
],
@ -56,7 +51,6 @@ describe('getInputs', () => {
['images', 'moby/buildkit'],
['sep-labels', ','],
['sep-tags', ','],
['sep-annotations', ',']
]),
{
context: ContextSource.workflow,
@ -65,32 +59,11 @@ describe('getInputs', () => {
githubToken: '',
images: ['moby/buildkit'],
labels: [],
annotations: [],
sepLabels: ',',
sepTags: ',',
sepAnnotations: ',',
tags: [],
} as Inputs
],
[
2,
new Map<string, string>([
['images', 'moby/buildkit\n#comment\nghcr.io/moby/mbuildkit'],
]),
{
context: ContextSource.workflow,
bakeTarget: 'docker-metadata-action',
flavor: [],
githubToken: '',
images: ['moby/buildkit', 'ghcr.io/moby/mbuildkit'],
labels: [],
annotations: [],
sepLabels: '\n',
sepTags: '\n',
sepAnnotations: '\n',
tags: [],
} as Inputs
],
]
])(
'[%d] given %p as inputs, returns %p',
async (num: number, inputs: Map<string, string>, expected: Inputs) => {
@ -116,10 +89,9 @@ describe('getContext', () => {
});
it('workflow', async () => {
const context = await getContext(ContextSource.workflow, toolkit);
const context = await getContext(ContextSource.workflow);
expect(context.ref).toEqual('refs/heads/dev');
expect(context.sha).toEqual('5f3331d7f7044c18ca9f12c77d961c4d7cf3276a');
expect(context.commitDate).toEqual(new Date('2024-11-13T13:42:28.000Z'));
});
it('git', async () => {
@ -129,13 +101,9 @@ describe('getContext', () => {
sha: 'git-test-sha'
} as Context);
});
jest.spyOn(Git, 'commitDate').mockImplementation(async (): Promise<Date> => {
return new Date('2023-01-01T13:42:28.000Z');
});
const context = await getContext(ContextSource.git, toolkit);
const context = await getContext(ContextSource.git);
expect(context.ref).toEqual('refs/heads/git-test');
expect(context.sha).toEqual('git-test-sha');
expect(context.commitDate).toEqual(new Date('2023-01-01T13:42:28.000Z'));
});
});

File diff suppressed because it is too large Load Diff

View File

@ -13,7 +13,7 @@ inputs:
required: true
images:
description: 'List of Docker images to use as base name for tags'
required: false
required: true
tags:
description: 'List of tags as key-value pair attributes'
required: false
@ -23,18 +23,12 @@ inputs:
labels:
description: 'List of custom labels'
required: false
annotations:
description: 'List of custom annotations'
required: false
sep-tags:
description: 'Separator to use for tags output (default \n)'
required: false
sep-labels:
description: 'Separator to use for labels output (default \n)'
required: false
sep-annotations:
description: 'Separator to use for annotations output (default \n)'
required: false
bake-target:
description: 'Bake target name (default docker-metadata-action)'
required: false
@ -50,19 +44,11 @@ outputs:
description: 'Generated Docker tags'
labels:
description: 'Generated Docker labels'
annotations:
description: 'Generated annotations'
bake-file:
description: 'Bake definiton file'
json:
description: 'JSON output of tags and labels'
bake-file-tags:
description: 'Bake definition file with tags'
bake-file-labels:
description: 'Bake definition file with labels'
bake-file-annotations:
description: 'Bake definition file with annotations'
bake-file:
description: 'Bake definition file with tags and labels'
runs:
using: 'node20'
using: 'node16'
main: 'dist/index.js'

View File

@ -1,20 +1,13 @@
# syntax=docker/dockerfile:1
ARG NODE_VERSION=20
ARG NODE_VERSION=16
FROM node:${NODE_VERSION}-alpine AS base
RUN apk add --no-cache cpio findutils git
WORKDIR /src
RUN --mount=type=bind,target=.,rw \
--mount=type=cache,target=/src/.yarn/cache <<EOT
corepack enable
yarn --version
yarn config set --home enableTelemetry 0
EOT
FROM base AS deps
RUN --mount=type=bind,target=.,rw \
--mount=type=cache,target=/src/.yarn/cache \
--mount=type=cache,target=/src/node_modules \
yarn install && mkdir /vendor && cp yarn.lock /vendor
@ -27,7 +20,7 @@ RUN --mount=type=bind,target=.,rw <<EOT
git add -A
cp -rf /vendor/* .
if [ -n "$(git status --porcelain -- yarn.lock)" ]; then
echo >&2 'ERROR: Vendor result differs. Please vendor your package with "docker buildx bake vendor"'
echo >&2 'ERROR: Vendor result differs. Please vendor your package with "docker buildx bake vendor-update"'
git status --porcelain -- yarn.lock
exit 1
fi
@ -35,7 +28,6 @@ EOT
FROM deps AS build
RUN --mount=type=bind,target=.,rw \
--mount=type=cache,target=/src/.yarn/cache \
--mount=type=cache,target=/src/node_modules \
yarn run build && mkdir /out && cp -Rf dist /out/
@ -56,25 +48,24 @@ EOT
FROM deps AS format
RUN --mount=type=bind,target=.,rw \
--mount=type=cache,target=/src/.yarn/cache \
--mount=type=cache,target=/src/node_modules \
yarn run format \
&& mkdir /out && find . -name '*.ts' -not -path './node_modules/*' -not -path './.yarn/*' | cpio -pdm /out
&& mkdir /out && find . -name '*.ts' -not -path './node_modules/*' | cpio -pdm /out
FROM scratch AS format-update
COPY --from=format /out /
FROM deps AS lint
RUN --mount=type=bind,target=.,rw \
--mount=type=cache,target=/src/.yarn/cache \
--mount=type=cache,target=/src/node_modules \
yarn run lint
FROM deps AS test
ENV RUNNER_TEMP=/tmp/github_runner
ENV RUNNER_TOOL_CACHE=/tmp/github_tool_cache
RUN --mount=type=bind,target=.,rw \
--mount=type=cache,target=/src/.yarn/cache \
--mount=type=cache,target=/src/node_modules \
yarn run test --coverage --coverageDirectory=/tmp/coverage
yarn run test --coverageDirectory=/tmp/coverage
FROM scratch AS test-coverage
COPY --from=test /tmp/coverage /

119
dist/index.js generated vendored

File diff suppressed because one or more lines are too long

2
dist/index.js.map generated vendored

File diff suppressed because one or more lines are too long

2767
dist/licenses.txt generated vendored

File diff suppressed because it is too large Load Diff

View File

@ -1,15 +1,9 @@
target "_common" {
args = {
BUILDKIT_CONTEXT_KEEP_GIT_DIR = 1
}
}
group "default" {
targets = ["build"]
}
group "pre-checkin" {
targets = ["vendor", "format", "build"]
targets = ["vendor-update", "format", "build"]
}
group "validate" {
@ -17,49 +11,42 @@ group "validate" {
}
target "build" {
inherits = ["_common"]
dockerfile = "dev.Dockerfile"
target = "build-update"
output = ["."]
}
target "build-validate" {
inherits = ["_common"]
dockerfile = "dev.Dockerfile"
target = "build-validate"
output = ["type=cacheonly"]
}
target "format" {
inherits = ["_common"]
dockerfile = "dev.Dockerfile"
target = "format-update"
output = ["."]
}
target "lint" {
inherits = ["_common"]
dockerfile = "dev.Dockerfile"
target = "lint"
output = ["type=cacheonly"]
}
target "vendor" {
inherits = ["_common"]
target "vendor-update" {
dockerfile = "dev.Dockerfile"
target = "vendor-update"
output = ["."]
}
target "vendor-validate" {
inherits = ["_common"]
dockerfile = "dev.Dockerfile"
target = "vendor-validate"
output = ["type=cacheonly"]
}
target "test" {
inherits = ["_common"]
dockerfile = "dev.Dockerfile"
target = "test-coverage"
output = ["./coverage"]

View File

@ -1,16 +1,13 @@
{
"name": "docker-metadata-action",
"description": "GitHub Action to extract metadata (tags, labels) for Docker",
"main": "src/main.ts",
"main": "lib/main.js",
"scripts": {
"build": "ncc build --source-map --minify --license licenses.txt",
"lint": "yarn run prettier && yarn run eslint",
"format": "yarn run prettier:fix && yarn run eslint:fix",
"eslint": "eslint --max-warnings=0 .",
"eslint:fix": "eslint --fix .",
"prettier": "prettier --check \"./**/*.ts\"",
"prettier:fix": "prettier --write \"./**/*.ts\"",
"test": "jest"
"build": "ncc build src/main.ts --source-map --minify --license licenses.txt",
"lint": "eslint src/**/*.ts __tests__/**/*.ts",
"format": "eslint --fix src/**/*.ts __tests__/**/*.ts",
"test": "jest --coverage",
"all": "yarn run build && yarn run format && yarn test"
},
"repository": {
"type": "git",
@ -23,36 +20,41 @@
"tag",
"label"
],
"author": "Docker Inc.",
"author": "Docker",
"contributors": [
{
"name": "CrazyMax",
"url": "https://crazymax.dev"
}
],
"license": "Apache-2.0",
"packageManager": "yarn@3.6.3",
"dependencies": {
"@actions/core": "^1.11.1",
"@actions/github": "^6.0.0",
"@docker/actions-toolkit": "^0.56.0",
"@actions/core": "^1.10.0",
"@actions/github": "^5.1.1",
"@docker/actions-toolkit": "^0.1.0",
"@renovate/pep440": "^1.0.0",
"csv-parse": "^5.6.0",
"handlebars": "^4.7.8",
"moment": "^2.30.1",
"moment-timezone": "^0.5.47",
"semver": "^7.7.1"
"csv-parse": "^5.3.8",
"handlebars": "^4.7.7",
"moment": "^2.29.4",
"moment-timezone": "^0.5.43",
"semver": "^7.5.0"
},
"devDependencies": {
"@types/csv-parse": "^1.2.2",
"@types/node": "^20.12.12",
"@types/semver": "^7.5.8",
"@typescript-eslint/eslint-plugin": "^7.9.0",
"@typescript-eslint/parser": "^7.9.0",
"@vercel/ncc": "^0.38.1",
"dotenv": "^16.4.5",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-jest": "^28.5.0",
"eslint-plugin-prettier": "^5.1.3",
"jest": "^29.7.0",
"prettier": "^3.2.5",
"ts-jest": "^29.1.2",
"ts-node": "^10.9.2",
"typescript": "^5.4.5"
"@types/node": "^16.18.23",
"@types/semver": "^7.3.13",
"@typescript-eslint/eslint-plugin": "^5.56.0",
"@typescript-eslint/parser": "^5.56.0",
"@vercel/ncc": "^0.36.1",
"dotenv": "^16.0.3",
"eslint": "^8.36.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-jest": "^27.2.1",
"eslint-plugin-prettier": "^4.2.1",
"jest": "^29.5.0",
"prettier": "^2.8.7",
"ts-jest": "^29.0.5",
"ts-node": "^10.9.1",
"typescript": "^4.9.5"
}
}

View File

@ -1,13 +1,8 @@
import * as core from '@actions/core';
import {Context as GithubContext} from '@actions/github/lib/context';
import {Context} from '@actions/github/lib/context';
import {Util} from '@docker/actions-toolkit/lib/util';
import {Git} from '@docker/actions-toolkit/lib/git';
import {GitHub} from '@docker/actions-toolkit/lib/github';
import {Toolkit} from '@docker/actions-toolkit/lib/toolkit';
export interface Context extends GithubContext {
commitDate: Date;
}
export interface Inputs {
context: ContextSource;
@ -15,10 +10,8 @@ export interface Inputs {
tags: string[];
flavor: string[];
labels: string[];
annotations: string[];
sepTags: string;
sepLabels: string;
sepAnnotations: string;
bakeTarget: string;
githubToken: string;
}
@ -26,14 +19,12 @@ export interface Inputs {
export function getInputs(): Inputs {
return {
context: (core.getInput('context') || ContextSource.workflow) as ContextSource,
images: Util.getInputList('images', {ignoreComma: true, comment: '#'}),
tags: Util.getInputList('tags', {ignoreComma: true, comment: '#'}),
flavor: Util.getInputList('flavor', {ignoreComma: true, comment: '#'}),
labels: Util.getInputList('labels', {ignoreComma: true, comment: '#'}),
annotations: Util.getInputList('annotations', {ignoreComma: true, comment: '#'}),
images: Util.getInputList('images', {ignoreComma: true}),
tags: Util.getInputList('tags', {ignoreComma: true}),
flavor: Util.getInputList('flavor', {ignoreComma: true}),
labels: Util.getInputList('labels', {ignoreComma: true}),
sepTags: core.getInput('sep-tags', {trimWhitespace: false}) || `\n`,
sepLabels: core.getInput('sep-labels', {trimWhitespace: false}) || `\n`,
sepAnnotations: core.getInput('sep-annotations', {trimWhitespace: false}) || `\n`,
bakeTarget: core.getInput('bake-target') || `docker-metadata-action`,
githubToken: core.getInput('github-token')
};
@ -44,10 +35,10 @@ export enum ContextSource {
git = 'git'
}
export async function getContext(source: ContextSource, toolkit: Toolkit): Promise<Context> {
export async function getContext(source: ContextSource): Promise<Context> {
switch (source) {
case ContextSource.workflow:
return await getContextFromWorkflow(toolkit);
return getContextFromWorkflow();
case ContextSource.git:
return await getContextFromGit();
default:
@ -55,7 +46,7 @@ export async function getContext(source: ContextSource, toolkit: Toolkit): Promi
}
}
async function getContextFromWorkflow(toolkit: Toolkit): Promise<Context> {
function getContextFromWorkflow(): Context {
const context = GitHub.context;
// Needs to override Git reference with pr ref instead of upstream branch ref
@ -74,63 +65,9 @@ async function getContextFromWorkflow(toolkit: Toolkit): Promise<Context> {
}
}
return {
commitDate: await getCommitDateFromWorkflow(context.sha, toolkit),
...context
} as Context;
return context;
}
async function getContextFromGit(): Promise<Context> {
const ctx = await Git.context();
return {
commitDate: await Git.commitDate(ctx.sha),
...ctx
} as Context;
}
async function getCommitDateFromWorkflow(sha: string, toolkit: Toolkit): Promise<Date> {
const event = GitHub.context.payload as unknown as {
// branch push
commits?: Array<{
timestamp: string;
// commit sha
id: string;
}>;
// tags
head_commit?: {
timestamp: string;
// commit sha
id: string;
};
};
if (event.commits) {
const commitDate = event.commits.find(x => x.id === sha)?.timestamp;
if (commitDate) {
return new Date(commitDate);
}
}
if (event.head_commit) {
if (event.head_commit.id === sha) {
return new Date(event.head_commit.timestamp);
}
}
// fallback to github api for commit date
try {
const commit = await toolkit.github.octokit.rest.repos.getCommit({
owner: GitHub.context.repo.owner,
repo: GitHub.context.repo.repo,
ref: sha
});
if (commit.data.commit.committer?.date) {
return new Date(commit.data.commit.committer.date);
}
throw new Error('Committer date not found');
} catch (error) {
core.debug(`Failed to get commit date from GitHub API: ${error.message}`);
return new Date();
}
return await Git.context();
}

View File

@ -2,19 +2,26 @@ import * as fs from 'fs';
import * as core from '@actions/core';
import * as actionsToolkit from '@docker/actions-toolkit';
import {Toolkit} from '@docker/actions-toolkit/lib/toolkit';
import {Util} from '@docker/actions-toolkit/lib/util';
import {getContext, getInputs, Inputs} from './context';
import {Meta, Version} from './meta';
function setOutput(name: string, value: string) {
core.setOutput(name, value);
core.exportVariable(`DOCKER_METADATA_OUTPUT_${name.replace(/\W/g, '_').toUpperCase()}`, value);
}
actionsToolkit.run(
// main
async () => {
const inputs: Inputs = getInputs();
if (inputs.images.length == 0) {
throw new Error(`images input required`);
}
const toolkit = new Toolkit({githubToken: inputs.githubToken});
const context = await getContext(inputs.context, toolkit);
const context = await getContext(inputs.context);
const repo = await toolkit.github.repoData();
const setOutput = outputEnvEnabled() ? setOutputAndEnv : core.setOutput;
await core.group(`Context info`, async () => {
core.info(`eventName: ${context.eventName}`);
@ -25,7 +32,6 @@ actionsToolkit.run(
core.info(`actor: ${context.actor}`);
core.info(`runNumber: ${context.runNumber}`);
core.info(`runId: ${context.runId}`);
core.info(`commitDate: ${context.commitDate}`);
});
if (core.isDebug()) {
@ -68,53 +74,18 @@ actionsToolkit.run(
setOutput('labels', labels.join(inputs.sepLabels));
});
// Annotations
const annotationsRaw: Array<string> = meta.getAnnotations();
const annotationsLevels = process.env.DOCKER_METADATA_ANNOTATIONS_LEVELS || 'manifest';
await core.group(`Annotations`, async () => {
const annotations: Array<string> = [];
for (const level of annotationsLevels.split(',')) {
annotations.push(
...annotationsRaw.map(label => {
const v = `${level}:${label}`;
core.info(v);
return v;
})
);
}
setOutput(`annotations`, annotations.join(inputs.sepAnnotations));
});
// JSON
const jsonOutput = meta.getJSON(annotationsLevels.split(','));
const jsonOutput = meta.getJSON();
await core.group(`JSON output`, async () => {
core.info(JSON.stringify(jsonOutput, null, 2));
setOutput('json', JSON.stringify(jsonOutput));
});
// Bake files
for (const kind of ['tags', 'labels', 'annotations:' + annotationsLevels]) {
const outputName = kind.split(':')[0];
const bakeFile: string = meta.getBakeFile(kind);
await core.group(`Bake file definition (${outputName})`, async () => {
// Bake file definition
const bakeFile: string = meta.getBakeFile();
await core.group(`Bake file definition`, async () => {
core.info(fs.readFileSync(bakeFile, 'utf8'));
setOutput(`bake-file-${outputName}`, bakeFile);
setOutput('bake-file', bakeFile);
});
}
// Bake file with tags and labels
setOutput(`bake-file`, `${meta.getBakeFileTagsLabels()}`);
}
);
function setOutputAndEnv(name: string, value: string) {
core.setOutput(name, value);
core.exportVariable(`DOCKER_METADATA_OUTPUT_${name.replace(/\W/g, '_').toUpperCase()}`, value);
}
function outputEnvEnabled(): boolean {
if (process.env.DOCKER_METADATA_SET_OUTPUT_ENV) {
return Util.parseBool(process.env.DOCKER_METADATA_SET_OUTPUT_ENV);
}
return true;
}

View File

@ -5,16 +5,15 @@ import moment from 'moment-timezone';
import * as pep440 from '@renovate/pep440';
import * as semver from 'semver';
import * as core from '@actions/core';
import {Context} from '@actions/github/lib/context';
import {Context as ToolkitContext} from '@docker/actions-toolkit/lib/context';
import {GitHubRepo} from '@docker/actions-toolkit/lib/types/github';
import {Inputs, Context} from './context';
import {Inputs} from './context';
import * as icl from './image';
import * as tcl from './tag';
import * as fcl from './flavor';
const defaultShortShaLength = 7;
export interface Version {
main: string | undefined;
partial: string[];
@ -114,7 +113,6 @@ export class Meta {
}
const currentDate = this.date;
const commitDate = this.context.commitDate;
const vraw = this.setValue(
handlebars.compile(tag.attrs['pattern'])({
date: function (format, options) {
@ -130,20 +128,6 @@ export class Meta {
}
});
return m.tz(tz).format(format);
},
commit_date: function (format, options) {
const m = moment(commitDate);
let tz = 'UTC';
Object.keys(options.hash).forEach(key => {
switch (key) {
case 'tz':
tz = options.hash[key];
break;
default:
throw new Error(`Unknown ${key} attribute`);
}
});
return m.tz(tz).format(format);
}
}),
tag
@ -323,7 +307,7 @@ export class Meta {
let val = this.context.sha;
if (tag.attrs['format'] === tcl.ShaFormat.Short) {
val = Meta.shortSha(this.context.sha);
val = this.context.sha.substring(0, 7);
}
const vraw = this.setValue(val, tag);
@ -372,10 +356,9 @@ export class Meta {
return val;
}
private setGlobalExp(val: string): string {
private setGlobalExp(val): string {
const context = this.context;
const currentDate = this.date;
const commitDate = this.context.commitDate;
return handlebars.compile(val)({
branch: function () {
if (!/^refs\/heads\//.test(context.ref)) {
@ -390,7 +373,7 @@ export class Meta {
return context.ref.replace(/^refs\/tags\//g, '');
},
sha: function () {
return Meta.shortSha(context.sha);
return context.sha.substring(0, 7);
},
base_ref: function () {
if (/^refs\/tags\//.test(context.ref) && context.payload?.base_ref != undefined) {
@ -403,20 +386,6 @@ export class Meta {
}
return '';
},
commit_date: function (format, options) {
const m = moment(commitDate);
let tz = 'UTC';
Object.keys(options.hash).forEach(key => {
switch (key) {
case 'tz':
tz = options.hash[key];
break;
default:
throw new Error(`Unknown ${key} attribute`);
}
});
return m.tz(tz).format(format);
},
is_default_branch: function () {
const branch = context.ref.replace(/^refs\/heads\//g, '');
// TODO: "base_ref" is available in the push payload but doesn't always seem to
@ -471,43 +440,22 @@ export class Meta {
if (!this.version.main) {
return [];
}
const generateTags = (imageName: string, version: string): Array<string> => {
const tags: Array<string> = [];
const prefix = imageName !== '' ? `${imageName}:` : '';
tags.push(`${prefix}${version}`);
for (const imageName of this.getImageNames()) {
tags.push(`${imageName}:${this.version.main}`);
for (const partial of this.version.partial) {
tags.push(`${prefix}${partial}`);
tags.push(`${imageName}:${partial}`);
}
if (this.version.latest) {
const latestTag = `${this.flavor.prefixLatest ? this.flavor.prefix : ''}latest${this.flavor.suffixLatest ? this.flavor.suffix : ''}`;
tags.push(`${prefix}${Meta.sanitizeTag(latestTag)}`);
tags.push(`${imageName}:${Meta.sanitizeTag(latestTag)}`);
}
return tags;
};
const tags: Array<string> = [];
const images = this.getImageNames();
if (images.length > 0) {
for (const imageName of images) {
tags.push(...generateTags(imageName, this.version.main));
}
} else {
tags.push(...generateTags('', this.version.main));
}
return tags;
}
public getLabels(): Array<string> {
return this.getOCIAnnotationsWithCustoms(this.inputs.labels);
}
public getAnnotations(): Array<string> {
return this.getOCIAnnotationsWithCustoms(this.inputs.annotations);
}
private getOCIAnnotationsWithCustoms(extra: string[]): Array<string> {
const res: Array<string> = [
const labels: Array<string> = [
`org.opencontainers.image.title=${this.repo.name || ''}`,
`org.opencontainers.image.description=${this.repo.description || ''}`,
`org.opencontainers.image.url=${this.repo.html_url || ''}`,
@ -517,28 +465,11 @@ export class Meta {
`org.opencontainers.image.revision=${this.context.sha || ''}`,
`org.opencontainers.image.licenses=${this.repo.license?.spdx_id || ''}`
];
extra.forEach(label => {
res.push(this.setGlobalExp(label));
});
return Array.from(
new Map<string, string>(
res
.map(label => label.split('='))
// eslint-disable-next-line @typescript-eslint/no-unused-vars
.filter(([_key, ...values]) => values.length > 0)
.map(([key, ...values]) => [key, values.join('=')] as [string, string])
)
)
.sort((a, b) => a[0].localeCompare(b[0]))
.map(([key, value]) => `${key}=${value}`);
labels.push(...this.inputs.labels);
return labels;
}
public getJSON(alevels: string[]): unknown {
const annotations: Array<string> = [];
for (const level of alevels) {
annotations.push(...this.getAnnotations().map(label => `${level}:${label}`));
}
public getJSON(): unknown {
return {
tags: this.getTags(),
labels: this.getLabels().reduce((res, label) => {
@ -548,55 +479,18 @@ export class Meta {
}
res[matches[1]] = matches[2];
return res;
}, {}),
annotations: annotations
}, {})
};
}
public getBakeFile(kind: string): string {
if (kind == 'tags') {
return this.generateBakeFile(
public getBakeFile(): string {
const bakeFile = path.join(ToolkitContext.tmpDir(), 'docker-metadata-action-bake.json');
fs.writeFileSync(
bakeFile,
JSON.stringify(
{
tags: this.getTags(),
args: {
DOCKER_META_IMAGES: this.getImageNames().join(','),
DOCKER_META_VERSION: this.version.main
}
},
kind
);
} else if (kind == 'labels') {
return this.generateBakeFile(
{
labels: this.getLabels().reduce((res, label) => {
const matches = label.match(/([^=]*)=(.*)/);
if (!matches) {
return res;
}
res[matches[1]] = matches[2];
return res;
}, {})
},
kind
);
} else if (kind.startsWith('annotations:')) {
const name = kind.split(':')[0];
const annotations: Array<string> = [];
for (const level of kind.split(':')[1].split(',')) {
annotations.push(...this.getAnnotations().map(label => `${level}:${label}`));
}
return this.generateBakeFile(
{
annotations: annotations
},
name
);
}
throw new Error(`Unknown bake file type: ${kind}`);
}
public getBakeFileTagsLabels(): string {
return this.generateBakeFile({
target: {
[this.inputs.bakeTarget]: {
tags: this.getTags(),
labels: this.getLabels().reduce((res, label) => {
const matches = label.match(/([^=]*)=(.*)/);
@ -610,12 +504,14 @@ export class Meta {
DOCKER_META_IMAGES: this.getImageNames().join(','),
DOCKER_META_VERSION: this.version.main
}
});
}
}
},
null,
2
)
);
private generateBakeFile(dt, suffix?: string): string {
const bakeFile = path.join(ToolkitContext.tmpDir(), `docker-metadata-action-bake${suffix ? `-${suffix}` : ''}.json`);
fs.writeFileSync(bakeFile, JSON.stringify({target: {[this.inputs.bakeTarget]: dt}}, null, 2));
return bakeFile;
}
@ -626,18 +522,4 @@ export class Meta {
private static sanitizeTag(tag: string): string {
return tag.replace(/[^a-zA-Z0-9._-]+/g, '-');
}
private static shortSha(sha: string): string {
let shortShaLength = defaultShortShaLength;
if (process.env.DOCKER_METADATA_SHORT_SHA_LENGTH) {
if (isNaN(Number(process.env.DOCKER_METADATA_SHORT_SHA_LENGTH))) {
throw new Error(`DOCKER_METADATA_SHORT_SHA_LENGTH is not a valid number: ${process.env.DOCKER_METADATA_SHORT_SHA_LENGTH}`);
}
shortShaLength = Number(process.env.DOCKER_METADATA_SHORT_SHA_LENGTH);
}
if (shortShaLength >= sha.length) {
return sha;
}
return sha.substring(0, shortShaLength);
}
}

View File

@ -4,11 +4,9 @@ RUN apk add --no-cache coreutils jq
ARG DOCKER_METADATA_OUTPUT_VERSION
ARG DOCKER_METADATA_OUTPUT_TAGS
ARG DOCKER_METADATA_OUTPUT_LABELS
ARG DOCKER_METADATA_OUTPUT_ANNOTATIONS
ARG DOCKER_METADATA_OUTPUT_JSON
RUN printenv DOCKER_METADATA_OUTPUT_VERSION
RUN printenv DOCKER_METADATA_OUTPUT_TAGS
RUN printenv DOCKER_METADATA_OUTPUT_LABELS
RUN printenv DOCKER_METADATA_OUTPUT_ANNOTATIONS
RUN printenv DOCKER_METADATA_OUTPUT_JSON
RUN echo $DOCKER_METADATA_OUTPUT_JSON | jq

10514
yarn.lock

File diff suppressed because it is too large Load Diff