parent
							
								
									9be43f076d
								
							
						
					
					
						commit
						2f83320d17
					
				
							
								
								
									
										103
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										103
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							| @ -5,10 +5,14 @@ on: | ||||
|     - cron: '0 */4 * * *' # every 4 hours | ||||
|   push: | ||||
|     branches: | ||||
|       - '**' | ||||
|       - 'master' | ||||
|       - 'releases/v*' | ||||
|     tags: | ||||
|       - 'v*.*.*' | ||||
|   pull_request: | ||||
|     branches: | ||||
|       - 'master' | ||||
|       - 'releases/v*' | ||||
| 
 | ||||
| env: | ||||
|   DOCKER_IMAGE: localhost:5000/name/app | ||||
| @ -27,7 +31,12 @@ jobs: | ||||
|           images: | | ||||
|             ${{ env.DOCKER_IMAGE }} | ||||
|             ghcr.io/name/app | ||||
|           tag-sha: true | ||||
|           tags: | | ||||
|             type=schedule | ||||
|             type=ref,event=branch | ||||
|             type=ref,event=tag | ||||
|             type=ref,event=pr | ||||
|             type=sha | ||||
| 
 | ||||
|   tag-schedule: | ||||
|     runs-on: ubuntu-latest | ||||
| @ -50,8 +59,12 @@ jobs: | ||||
|           images: | | ||||
|             ${{ env.DOCKER_IMAGE }} | ||||
|             ghcr.io/name/app | ||||
|           tag-sha: true | ||||
|           tag-schedule: ${{ matrix.tag-schedule }} | ||||
|           tags: | | ||||
|             type=schedule,pattern=${{ matrix.tag-schedule }} | ||||
|             type=ref,event=branch | ||||
|             type=ref,event=tag | ||||
|             type=ref,event=pr | ||||
|             type=sha | ||||
| 
 | ||||
|   tag-match: | ||||
|     runs-on: ubuntu-latest | ||||
| @ -76,18 +89,23 @@ jobs: | ||||
|           images: | | ||||
|             ${{ env.DOCKER_IMAGE }} | ||||
|             ghcr.io/name/app | ||||
|           tag-sha: true | ||||
|           tag-match: ${{ matrix.tag-match }} | ||||
|           tag-match-group: ${{ matrix.tag-match-group }} | ||||
|           tags: | | ||||
|             type=schedule | ||||
|             type=ref,event=branch | ||||
|             type=ref,event=tag | ||||
|             type=ref,event=pr | ||||
|             type=match,"pattern=${{ matrix.tag-match }}",group=${{ matrix.tag-match-group }} | ||||
|             type=sha | ||||
| 
 | ||||
|   tag-semver: | ||||
|     runs-on: ubuntu-latest | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       matrix: | ||||
|         tag-latest: | ||||
|           - 'true' | ||||
|           - 'false' | ||||
|         flavor-latest: | ||||
|           - "auto" | ||||
|           - "true" | ||||
|           - "false" | ||||
|     steps: | ||||
|       - | ||||
|         name: Checkout | ||||
| @ -99,13 +117,19 @@ jobs: | ||||
|           images: | | ||||
|             ${{ env.DOCKER_IMAGE }} | ||||
|             ghcr.io/name/app | ||||
|           tag-semver: | | ||||
|             {{raw}} | ||||
|             {{version}} | ||||
|             {{major}}.{{minor}}.{{patch}} | ||||
|           tag-latest: ${{ matrix.tag-latest }} | ||||
|           tags: | | ||||
|             type=schedule | ||||
|             type=ref,event=branch | ||||
|             type=ref,event=tag | ||||
|             type=ref,event=pr | ||||
|             type=semver,pattern={{raw}} | ||||
|             type=semver,pattern={{version}} | ||||
|             type=semver,pattern={{major}}.{{minor}}.{{patch}} | ||||
|             type=sha | ||||
|           flavor: | | ||||
|             latest=${{ matrix.flavor-latest }} | ||||
| 
 | ||||
|   label-custom: | ||||
|   flavor: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - | ||||
| @ -118,7 +142,24 @@ jobs: | ||||
|           images: | | ||||
|             ${{ env.DOCKER_IMAGE }} | ||||
|             ghcr.io/name/app | ||||
|           label-custom: | | ||||
|           flavor: | | ||||
|             prefix=foo- | ||||
|             suffix=-bar | ||||
| 
 | ||||
|   labels: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - | ||||
|         name: Checkout | ||||
|         uses: actions/checkout@v2 | ||||
|       - | ||||
|         name: Docker meta | ||||
|         uses: ./ | ||||
|         with: | ||||
|           images: | | ||||
|             ${{ env.DOCKER_IMAGE }} | ||||
|             ghcr.io/name/app | ||||
|           labels: | | ||||
|             maintainer=CrazyMax | ||||
|             org.opencontainers.image.title=MyCustomTitle | ||||
|             org.opencontainers.image.description=Another description | ||||
| @ -141,11 +182,15 @@ jobs: | ||||
|         uses: ./ | ||||
|         with: | ||||
|           images: ${{ env.DOCKER_IMAGE }} | ||||
|           tag-sha: true | ||||
|           tag-semver: | | ||||
|             v{{version}} | ||||
|             v{{major}}.{{minor}} | ||||
|             v{{major}} | ||||
|           tags: | | ||||
|             type=schedule | ||||
|             type=ref,event=branch | ||||
|             type=ref,event=tag | ||||
|             type=ref,event=pr | ||||
|             type=semver,pattern=v{{version}} | ||||
|             type=semver,pattern=v{{major}}.{{minor}} | ||||
|             type=semver,pattern=v{{major}} | ||||
|             type=sha | ||||
|       - | ||||
|         name: Set up QEMU | ||||
|         uses: docker/setup-qemu-action@v1 | ||||
| @ -192,11 +237,15 @@ jobs: | ||||
|           images: | | ||||
|             ${{ env.DOCKER_IMAGE }} | ||||
|             ghcr.io/name/app | ||||
|           tag-sha: true | ||||
|           tag-semver: | | ||||
|             {{version}} | ||||
|             {{major}}.{{minor}} | ||||
|             {{major}} | ||||
|           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 | ||||
|       - | ||||
|         name: Set up QEMU | ||||
|         uses: docker/setup-qemu-action@v1 | ||||
|  | ||||
							
								
								
									
										11
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
								
							| @ -10,6 +10,7 @@ on: | ||||
|   pull_request: | ||||
|     branches: | ||||
|       - 'master' | ||||
|       - 'releases/v*' | ||||
|     paths-ignore: | ||||
|       - '**.md' | ||||
| 
 | ||||
| @ -20,12 +21,18 @@ jobs: | ||||
|       - | ||||
|         name: Checkout | ||||
|         uses: actions/checkout@v2 | ||||
|       - | ||||
|         name: Validate | ||||
|         uses: docker/bake-action@v1 | ||||
|         with: | ||||
|           targets: validate | ||||
|       - | ||||
|         name: Test | ||||
|         run: docker buildx bake test | ||||
|         uses: docker/bake-action@v1 | ||||
|         with: | ||||
|           targets: test | ||||
|       - | ||||
|         name: Upload coverage | ||||
|         uses: codecov/codecov-action@v1 | ||||
|         with: | ||||
|           token: ${{ secrets.CODECOV_TOKEN }} | ||||
|           file: ./coverage/clover.xml | ||||
|  | ||||
							
								
								
									
										25
									
								
								.github/workflows/validate.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										25
									
								
								.github/workflows/validate.yml
									
									
									
									
										vendored
									
									
								
							| @ -1,25 +0,0 @@ | ||||
| name: validate | ||||
| 
 | ||||
| on: | ||||
|   push: | ||||
|     branches: | ||||
|       - 'master' | ||||
|       - 'releases/v*' | ||||
|     paths-ignore: | ||||
|       - '**.md' | ||||
|   pull_request: | ||||
|     branches: | ||||
|       - 'master' | ||||
|     paths-ignore: | ||||
|       - '**.md' | ||||
| 
 | ||||
| jobs: | ||||
|   validate: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - | ||||
|         name: Checkout | ||||
|         uses: actions/checkout@v2 | ||||
|       - | ||||
|         name: Validate | ||||
|         run: docker buildx bake validate | ||||
							
								
								
									
										474
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										474
									
								
								README.md
									
									
									
									
									
								
							| @ -5,6 +5,10 @@ | ||||
| [](https://github.com/sponsors/crazy-max) | ||||
| [](https://www.paypal.me/crazyws) | ||||
| 
 | ||||
| ## Upgrade from v1 | ||||
| 
 | ||||
| `v2` of this action includes significant changes. Please read the [upgrade notes](UPGRADE.md) for a smooth migration. | ||||
| 
 | ||||
| ## About | ||||
| 
 | ||||
| GitHub Action to extract metadata (tags, labels) for Docker. This action is particularly useful if used with | ||||
| @ -23,11 +27,17 @@ ___ | ||||
| * [Customizing](#customizing) | ||||
|   * [inputs](#inputs) | ||||
|   * [outputs](#outputs) | ||||
| * [`flavor` input](#flavor-input) | ||||
| * [`tags` input](#tags-input) | ||||
|   * [`type=schedule`](#typeschedule) | ||||
|   * [`type=semver`](#typesemver) | ||||
|   * [`type=match`](#typematch) | ||||
|   * [`type=edge`](#typeedge) | ||||
|   * [`type=ref`](#typeref) | ||||
|   * [`type=raw`](#typeraw) | ||||
|   * [`type=sha`](#typesha) | ||||
| * [Notes](#notes) | ||||
|   * [Latest tag](#latest-tag) | ||||
|   * [Handle semver tag](#handle-semver-tag) | ||||
|   * [`tag-match` examples](#tag-match-examples) | ||||
|   * [Schedule tag](#schedule-tag) | ||||
|   * [Overwrite labels](#overwrite-labels) | ||||
| * [Keep up-to-date with GitHub Dependabot](#keep-up-to-date-with-github-dependabot) | ||||
| * [Contributing](#contributing) | ||||
| @ -37,24 +47,18 @@ ___ | ||||
| 
 | ||||
| ### Basic | ||||
| 
 | ||||
| | Event           | Ref                           | Commit SHA | Docker Tags                         | | ||||
| |-----------------|-------------------------------|------------|-------------------------------------| | ||||
| | `pull_request`  | `refs/pull/2/merge`           | `a123b57`  | `pr-2`                              | | ||||
| | `push`          | `refs/heads/master`           | `cf20257`  | `master`                            | | ||||
| | `push`          | `refs/heads/my/branch`        | `a5df687`  | `my-branch`                         | | ||||
| | `push tag`      | `refs/tags/v1.2.3`            | `ad132f5`  | `v1.2.3`, `latest`                  | | ||||
| | `push tag`      | `refs/tags/v2.0.8-beta.67`    | `fc89efd`  | `v2.0.8-beta.67`, `latest`          | | ||||
| 
 | ||||
| ```yaml | ||||
| name: ci | ||||
| 
 | ||||
| on: | ||||
|   push: | ||||
|     branches: | ||||
|       - '**' | ||||
|       - 'master' | ||||
|     tags: | ||||
|       - 'v*' | ||||
|   pull_request: | ||||
|     branches: | ||||
|       - 'master' | ||||
| 
 | ||||
| jobs: | ||||
|   docker: | ||||
| @ -65,16 +69,10 @@ jobs: | ||||
|         uses: actions/checkout@v2 | ||||
|       - | ||||
|         name: Docker meta | ||||
|         id: docker_meta | ||||
|         uses: crazy-max/ghaction-docker-meta@v1 | ||||
|         id: meta | ||||
|         uses: crazy-max/ghaction-docker-meta@v2 | ||||
|         with: | ||||
|           images: name/app | ||||
|       - | ||||
|         name: Set up QEMU | ||||
|         uses: docker/setup-qemu-action@v1 | ||||
|       - | ||||
|         name: Set up Docker Buildx | ||||
|         uses: docker/setup-buildx-action@v1 | ||||
|       - | ||||
|         name: Login to DockerHub | ||||
|         if: github.event_name != 'pull_request' | ||||
| @ -87,33 +85,33 @@ jobs: | ||||
|         uses: docker/build-push-action@v2 | ||||
|         with: | ||||
|           context: . | ||||
|           file: ./Dockerfile | ||||
|           platforms: linux/amd64,linux/arm64,linux/386 | ||||
|           push: ${{ github.event_name != 'pull_request' }} | ||||
|           tags: ${{ steps.docker_meta.outputs.tags }} | ||||
|           labels: ${{ steps.docker_meta.outputs.labels }} | ||||
|           tags: ${{ steps.meta.outputs.tags }} | ||||
|           labels: ${{ steps.meta.outputs.labels }} | ||||
| ``` | ||||
| 
 | ||||
| | Event           | Ref                           | Docker Tags                         | | ||||
| |-----------------|-------------------------------|-------------------------------------| | ||||
| | `pull_request`  | `refs/pull/2/merge`           | `pr-2`                              | | ||||
| | `push`          | `refs/heads/master`           | `master`                            | | ||||
| | `push`          | `refs/heads/releases/v1`      | `releases-v1`                       | | ||||
| | `push tag`      | `refs/tags/v1.2.3`            | `v1.2.3`, `latest`                  | | ||||
| | `push tag`      | `refs/tags/v2.0.8-beta.67`    | `v2.0.8-beta.67`, `latest`          | | ||||
| 
 | ||||
| ### Semver | ||||
| 
 | ||||
| | Event           | Ref                           | Commit SHA | Docker Tags                         | | ||||
| |-----------------|-------------------------------|------------|-------------------------------------| | ||||
| | `pull_request`  | `refs/pull/2/merge`           | `a123b57`  | `pr-2`                              | | ||||
| | `push`          | `refs/heads/master`           | `cf20257`  | `master`                            | | ||||
| | `push`          | `refs/heads/my/branch`        | `a5df687`  | `my-branch`                         | | ||||
| | `push tag`      | `refs/tags/v1.2.3`            | `ad132f5`  | `1.2.3`, `1.2`, `latest`            | | ||||
| | `push tag`      | `refs/tags/v2.0.8-beta.67`    | `fc89efd`  | `2.0.8-beta.67`                     | | ||||
| 
 | ||||
| ```yaml | ||||
| name: ci | ||||
| 
 | ||||
| on: | ||||
|   push: | ||||
|     branches: | ||||
|       - '**' | ||||
|       - 'master' | ||||
|     tags: | ||||
|       - 'v*' | ||||
|   pull_request: | ||||
|     branches: | ||||
|       - 'master' | ||||
| 
 | ||||
| jobs: | ||||
|   docker: | ||||
| @ -124,19 +122,16 @@ jobs: | ||||
|         uses: actions/checkout@v2 | ||||
|       - | ||||
|         name: Docker meta | ||||
|         id: docker_meta | ||||
|         uses: crazy-max/ghaction-docker-meta@v1 | ||||
|         id: meta | ||||
|         uses: crazy-max/ghaction-docker-meta@v2 | ||||
|         with: | ||||
|           images: name/app | ||||
|           tag-semver: | | ||||
|             {{version}} | ||||
|             {{major}}.{{minor}} | ||||
|       - | ||||
|         name: Set up QEMU | ||||
|         uses: docker/setup-qemu-action@v1 | ||||
|       - | ||||
|         name: Set up Docker Buildx | ||||
|         uses: docker/setup-buildx-action@v1 | ||||
|           tags: | | ||||
|             type=ref,event=branch | ||||
|             type=ref,event=tag | ||||
|             type=ref,event=pr | ||||
|             type=semver,pattern={{version}} | ||||
|             type=semver,pattern={{major}}.{{minor}} | ||||
|       - | ||||
|         name: Login to DockerHub | ||||
|         if: github.event_name != 'pull_request' | ||||
| @ -149,13 +144,19 @@ jobs: | ||||
|         uses: docker/build-push-action@v2 | ||||
|         with: | ||||
|           context: . | ||||
|           file: ./Dockerfile | ||||
|           platforms: linux/amd64,linux/arm64,linux/386 | ||||
|           push: ${{ github.event_name != 'pull_request' }} | ||||
|           tags: ${{ steps.docker_meta.outputs.tags }} | ||||
|           labels: ${{ steps.docker_meta.outputs.labels }} | ||||
|           tags: ${{ steps.meta.outputs.tags }} | ||||
|           labels: ${{ steps.meta.outputs.labels }} | ||||
| ``` | ||||
| 
 | ||||
| | Event           | Ref                           | Docker Tags                         | | ||||
| |-----------------|-------------------------------|-------------------------------------| | ||||
| | `pull_request`  | `refs/pull/2/merge`           | `pr-2`                              | | ||||
| | `push`          | `refs/heads/master`           | `master`                            | | ||||
| | `push`          | `refs/heads/releases/v1`      | `releases-v1`                       | | ||||
| | `push tag`      | `refs/tags/v1.2.3`            | `1.2.3`, `1.2`, `latest`            | | ||||
| | `push tag`      | `refs/tags/v2.0.8-beta.67`    | `2.0.8-beta.67`                     | | ||||
| 
 | ||||
| ### Bake definition | ||||
| 
 | ||||
| This action also handles a bake definition file that can be used with the | ||||
| @ -164,7 +165,6 @@ This action also handles a bake definition file that can be used with the | ||||
| 
 | ||||
| ```hcl | ||||
| // docker-bake.hcl | ||||
| 
 | ||||
| target "ghaction-docker-meta" {} | ||||
| 
 | ||||
| target "build" { | ||||
| @ -181,10 +181,9 @@ name: ci | ||||
| on: | ||||
|   push: | ||||
|     branches: | ||||
|       - '**' | ||||
|       - 'master' | ||||
|     tags: | ||||
|       - 'v*' | ||||
|   pull_request: | ||||
| 
 | ||||
| jobs: | ||||
|   docker: | ||||
| @ -195,40 +194,37 @@ jobs: | ||||
|         uses: actions/checkout@v2 | ||||
|       - | ||||
|         name: Docker meta | ||||
|         id: docker_meta | ||||
|         uses: crazy-max/ghaction-docker-meta@v1 | ||||
|         id: meta | ||||
|         uses: crazy-max/ghaction-docker-meta@v2 | ||||
|         with: | ||||
|           images: name/app | ||||
|           tag-sha: true | ||||
|           tag-semver: | | ||||
|             {{version}} | ||||
|             {{major}}.{{minor}} | ||||
|       - | ||||
|         name: Set up QEMU | ||||
|         uses: docker/setup-qemu-action@v1 | ||||
|       - | ||||
|         name: Set up Docker Buildx | ||||
|         uses: docker/setup-buildx-action@v1 | ||||
|           tags: | | ||||
|             type=ref,event=branch | ||||
|             type=ref,event=tag | ||||
|             type=ref,event=pr | ||||
|             type=semver,pattern={{version}} | ||||
|             type=semver,pattern={{major}}.{{minor}} | ||||
|             type=sha | ||||
|       - | ||||
|         name: Build | ||||
|         uses: docker/bake-action@v1 | ||||
|         with: | ||||
|           files: | | ||||
|             ./docker-bake.hcl | ||||
|             ${{ steps.docker_meta.outputs.bake-file }} | ||||
|           targets: | | ||||
|             build | ||||
|             ${{ steps.meta.outputs.bake-file }} | ||||
|           targets: build | ||||
| ``` | ||||
| 
 | ||||
| Content of `${{ steps.docker_meta.outputs.bake-file }}` file will look like this: | ||||
| Content of `${{ steps.meta.outputs.bake-file }}` file will look like this with `refs/tags/v1.2.3` ref: | ||||
| 
 | ||||
| ```json | ||||
| { | ||||
|   "target": { | ||||
|     "ghaction-docker-meta": { | ||||
|       "tags": [ | ||||
|         "name/app:1.1.1", | ||||
|         "name/app:1.1", | ||||
|         "name/app:1.2.3", | ||||
|         "name/app:1.2", | ||||
|         "name/app:sha-90dd603", | ||||
|         "name/app:latest" | ||||
|       ], | ||||
|       "labels": { | ||||
| @ -236,14 +232,14 @@ Content of `${{ steps.docker_meta.outputs.bake-file }}` file will look like this | ||||
|         "org.opencontainers.image.description": "This your first repo!", | ||||
|         "org.opencontainers.image.url": "https://github.com/octocat/Hello-World", | ||||
|         "org.opencontainers.image.source": "https://github.com/octocat/Hello-World", | ||||
|         "org.opencontainers.image.version": "1.1.1", | ||||
|         "org.opencontainers.image.version": "1.2.3", | ||||
|         "org.opencontainers.image.created": "2020-01-10T00:30:00.000Z", | ||||
|         "org.opencontainers.image.revision": "90dd6032fac8bda1b6c4436a2e65de27961ed071", | ||||
|         "org.opencontainers.image.licenses": "MIT" | ||||
|       }, | ||||
|       "args": { | ||||
|         "DOCKER_META_IMAGES": "name/app", | ||||
|         "DOCKER_META_VERSION": "1.1.1" | ||||
|         "DOCKER_META_VERSION": "1.2.3" | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| @ -272,22 +268,12 @@ Following inputs can be used as `step.with` keys | ||||
| | Name                | Type     | Description                        | | ||||
| |---------------------|----------|------------------------------------| | ||||
| | `images`            | List/CSV | List of Docker images to use as base name for tags | | ||||
| | `tag-sha`           | Bool     | Add git short commit as Docker tag (default `false`) | | ||||
| | `tag-edge`          | Bool     | Enable edge branch tagging (default `false`) | | ||||
| | `tag-edge-branch`   | String   | Branch that will be tagged as edge (default `repo.default_branch`) | | ||||
| | `tag-semver`        | List/CSV | Handle Git tag as semver [template](#handle-semver-tag) if possible | | ||||
| | `tag-match`         | String   | RegExp to match against a Git tag and use first match as Docker tag | | ||||
| | `tag-match-group`   | Number   | Group to get if `tag-match` matches (default `0`) | | ||||
| | `tag-latest`        | Bool     | Set `latest` Docker tag if `tag-semver`, `tag-match` or Git tag event occurs (default `true`) | | ||||
| | `tag-schedule`      | String   | [Template](#schedule-tag) to apply to schedule tag (default `nightly`) | | ||||
| | `tag-custom`        | List/CSV | List of custom tags | | ||||
| | `tag-custom-only`   | Bool     | Only use `tag-custom` as Docker tags | | ||||
| | `label-custom`      | List     | List of custom labels | | ||||
| | `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 | | ||||
| | `sep-tags`          | String   | Separator to use for tags output (default `\n`) | | ||||
| | `sep-labels`        | String   | Separator to use for labels output (default `\n`) | | ||||
| 
 | ||||
| > `tag-semver` and `tag-match` are mutually exclusive | ||||
| 
 | ||||
| ### outputs | ||||
| 
 | ||||
| Following outputs are available | ||||
| @ -299,59 +285,277 @@ Following outputs are available | ||||
| | `labels`      | String  | Docker labels | | ||||
| | `bake-file`   | File    | [Bake definition file](https://github.com/docker/buildx#file-definition) path | | ||||
| 
 | ||||
| ## `flavor` input | ||||
| 
 | ||||
| `flavor` defines a global behavior for [`tags`](#tags-input): | ||||
| 
 | ||||
| ```yaml | ||||
| flavor: | | ||||
|   latest=auto | ||||
|   prefix= | ||||
|   suffix= | ||||
| ``` | ||||
| 
 | ||||
| * `latest=<auto|true|false>`: Handle [latest tag](#latest-tag) (default `auto`) | ||||
| * `prefix=<string>`: A global prefix for each generated tag | ||||
| * `suffix=<string>`: A global suffix for each generated tag | ||||
| 
 | ||||
| ## `tags` input | ||||
| 
 | ||||
| `tags` is the core input of this action as everything related to it will reflect the output metadata. This one is in | ||||
| the form of a key-value pair list in CSV format to remove limitations intrinsically linked to GitHub Actions | ||||
| (only string format is handled in the input fields). Here is an example: | ||||
| 
 | ||||
| ```yaml | ||||
| 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 | ||||
| ``` | ||||
| 
 | ||||
| Each entry is defined by a `type`, which are: | ||||
| 
 | ||||
| * [`type=schedule`](#typeschedule) | ||||
| * [`type=semver`](#typesemver) | ||||
| * [`type=match`](#typematch) | ||||
| * [`type=edge`](#typeedge) | ||||
| * [`type=ref`](#typeref) | ||||
| * [`type=raw`](#typeraw) | ||||
| * [`type=sha`](#typesha) | ||||
| 
 | ||||
| And global attributes: | ||||
| 
 | ||||
| * `enable=<true|false>` enable this entry (default `true`) | ||||
| * `priority=<number>` priority to manage the order of tags | ||||
| * `prefix=<string>` add prefix | ||||
| * `suffix=<string>` add suffix | ||||
| 
 | ||||
| Default entries if `tags` input is empty: | ||||
| 
 | ||||
| ```yaml | ||||
| tags: | | ||||
|   type=schedule | ||||
|   type=ref,event=branch | ||||
|   type=ref,event=tag | ||||
|   type=ref,event=pr | ||||
| ``` | ||||
| 
 | ||||
| ### `type=schedule` | ||||
| 
 | ||||
| ```yaml | ||||
| tags: | | ||||
|   # minimal | ||||
|   type=schedule | ||||
|   # default | ||||
|   type=schedule,pattern=nightly | ||||
|   # handlebars | ||||
|   type=schedule,pattern={{date 'YYYYMMDD'}} | ||||
| ``` | ||||
| 
 | ||||
| Will be used on [schedule event](https://docs.github.com/en/actions/reference/events-that-trigger-workflows#schedule). | ||||
| 
 | ||||
| `pattern` is a specially crafted attribute to support [Handlebars template](https://handlebarsjs.com/guide/) with | ||||
| the following expressions: | ||||
| * `date 'format'` ; render date by its [moment format](https://momentjs.com/docs/#/displaying/format/) | ||||
| 
 | ||||
| | Pattern                  | Output               | | ||||
| |--------------------------|----------------------| | ||||
| | `nightly`                | `nightly`            | | ||||
| | `{{date 'YYYYMMDD'}}`    | `20210326`           | | ||||
| 
 | ||||
| Extended attributes and default values: | ||||
| 
 | ||||
| ```yaml | ||||
| tags: | | ||||
|   type=schedule,enable=true,priority=1000,prefix=,suffix=,pattern=nightly | ||||
| ``` | ||||
| 
 | ||||
| ### `type=semver` | ||||
| 
 | ||||
| ```yaml | ||||
| tags: | | ||||
|   # minimal | ||||
|   type=semver,pattern={{version}} | ||||
|   # use custom value instead of git tag | ||||
|   type=semver,pattern={{version}},value=v1.0.0 | ||||
| ``` | ||||
| 
 | ||||
| Will be used on a [push tag event](https://docs.github.com/en/actions/reference/events-that-trigger-workflows#push) | ||||
| and requires a valid Git tag [semver](https://semver.org/) but you can also use a custom value through `value` | ||||
| attribute. | ||||
| 
 | ||||
| `pattern` attribute supports [Handlebars template](https://handlebarsjs.com/guide/) with the following expressions: | ||||
| * `raw` ; the actual semver | ||||
| * `version` ; shorthand for `{{major}}.{{minor}}.{{patch}}` (can include pre-release) | ||||
| * `major` ; major version identifier | ||||
| * `minor` ; minor version identifier | ||||
| * `patch` ; patch version identifier | ||||
| 
 | ||||
| | Git tag            | Pattern                                                  | Output               | | ||||
| |--------------------|----------------------------------------------------------|----------------------| | ||||
| | `v1.2.3`           | `{{raw}}`                                                | `v1.2.3`             | | ||||
| | `v1.2.3`           | `{{version}}`                                            | `1.2.3`              | | ||||
| | `v1.2.3`           | `{{major}}.{{minor}}`                                    | `1.2`                | | ||||
| | `v1.2.3`           | `v{{major}}`                                             | `v1`                 | | ||||
| | `v1.2.3`           | `{{minor}}`                                              | `2`                  | | ||||
| | `v1.2.3`           | `{{patch}}`                                              | `3`                  | | ||||
| | `v2.0.8-beta.67`   | `{{raw}}`                                                | `2.0.8-beta.67`*     | | ||||
| | `v2.0.8-beta.67`   | `{{version}}`                                            | `2.0.8-beta.67`      | | ||||
| | `v2.0.8-beta.67`   | `{{major}}.{{minor}}`                                    | `2.0.8-beta.67`*     | | ||||
| 
 | ||||
| > *Pre-release (rc, beta, alpha) will only extend `{{version}}` as tag because they are updated frequently, | ||||
| > and contain many breaking changes that are (by the author's design) not yet fit for public consumption. | ||||
| 
 | ||||
| Extended attributes and default values: | ||||
| 
 | ||||
| ```yaml | ||||
| tags: | | ||||
|   type=semver,enable=true,priority=900,prefix=,suffix=,pattern=,value= | ||||
| ``` | ||||
| 
 | ||||
| ### `type=match` | ||||
| 
 | ||||
| ```yaml | ||||
| tags: | | ||||
|   # minimal | ||||
|   type=match,pattern=\d{8} | ||||
|   # double quotes if comma in pattern | ||||
|   type=match,"pattern=\d{1,3}.\d{1,3}.\d{1,3}" | ||||
|   # define match group | ||||
|   type=match,pattern=v(.*),group=1 | ||||
|   # use custom value instead of git tag | ||||
|   type=match,pattern=v(.*),group=1,value=v1.0.0 | ||||
| ``` | ||||
| 
 | ||||
| Can create a regular expression for matching Git tag with a pattern and capturing group. Will be used on a | ||||
| [push tag event](https://docs.github.com/en/actions/reference/events-that-trigger-workflows#push) but you can also use | ||||
| a custom value through `value` attribute. | ||||
| 
 | ||||
| | Git tag                 | Pattern                       | Group   | Output                 | | ||||
| |-------------------------|-------------------------------|---------|------------------------| | ||||
| | `v1.2.3`                | `\d{1,3}.\d{1,3}.\d{1,3}`     | `0`     | `1.2.3`                | | ||||
| | `v2.0.8-beta.67`        | `v(.*)`                       | `1`     | `2.0.8-beta.67`        | | ||||
| | `v2.0.8-beta.67`        | `v(\d.\d)`                    | `1`     | `2.0`                  | | ||||
| | `20200110-RC2`          | `\d+`                         | `0`     | `20200110`             | | ||||
| 
 | ||||
| Extended attributes and default values: | ||||
| 
 | ||||
| ```yaml | ||||
| tags: | | ||||
|   type=group,enable=true,priority=800,prefix=,suffix=,pattern=,group=0,value= | ||||
| ``` | ||||
| 
 | ||||
| ### `type=edge` | ||||
| 
 | ||||
| ```yaml | ||||
| tags: | | ||||
|   # minimal | ||||
|   type=edge | ||||
|   # define default branch | ||||
|   type=edge,branch=main | ||||
| ``` | ||||
| 
 | ||||
| An `edge` tag reflects the last commit of the active branch on your Git repository. I usually prefer to use `edge` | ||||
| as a Docker tag for a better distinction or common pattern. This is also used by official images | ||||
| like [Alpine](https://hub.docker.com/_/alpine). | ||||
| 
 | ||||
| Extended attributes and default values: | ||||
| 
 | ||||
| ```yaml | ||||
| tags: | | ||||
|   type=edge,enable=true,priority=700,prefix=,suffix=,branch=$repo.default_branch | ||||
| ``` | ||||
| 
 | ||||
| ### `type=ref` | ||||
| 
 | ||||
| ```yaml | ||||
| tags: | | ||||
|   # minimal branch event | ||||
|   type=ref,event=branch | ||||
|   # minimal tag event | ||||
|   type=ref,event=tag | ||||
|   # minimal pull request event | ||||
|   type=ref,event=pr | ||||
| ``` | ||||
| 
 | ||||
| This type handles Git ref (or reference) for the following events: | ||||
| * `branch` ; eg. `refs/heads/master` | ||||
| * `tag` ; eg. `refs/tags/v1.0.0` | ||||
| * `pr` ; eg. `refs/pull/318/merge` | ||||
| 
 | ||||
| | Event           | Ref                           | Output                        | | ||||
| |-----------------|-------------------------------|-------------------------------| | ||||
| | `pull_request`  | `refs/pull/2/merge`           | `pr-2`                        | | ||||
| | `push`          | `refs/heads/master`           | `master`                      | | ||||
| | `push`          | `refs/heads/my/branch`        | `my-branch`                   | | ||||
| | `push tag`      | `refs/tags/v1.2.3`            | `v1.2.3`                      | | ||||
| | `push tag`      | `refs/tags/v2.0.8-beta.67`    | `v2.0.8-beta.67`              | | ||||
| 
 | ||||
| Extended attributes and default values: | ||||
| 
 | ||||
| ```yaml | ||||
| tags: | | ||||
|   # event branch | ||||
|   type=ref,enable=true,priority=600,prefix=,suffix=,event= | ||||
|   # event tag | ||||
|   type=ref,enable=true,priority=600,prefix=,suffix=,event= | ||||
|   # event pr | ||||
|   type=ref,enable=true,priority=600,prefix=pr-,suffix=,event= | ||||
| ``` | ||||
| 
 | ||||
| ### `type=raw` | ||||
| 
 | ||||
| ```yaml | ||||
| tags: | | ||||
|   type=raw,value=foo | ||||
|   type=raw,value=bar | ||||
|   # or | ||||
|   type=raw,foo | ||||
|   type=raw,bar | ||||
|   # or | ||||
|   foo | ||||
|   bar | ||||
| ``` | ||||
| 
 | ||||
| Output custom tags according to your needs. | ||||
| 
 | ||||
| Extended attributes and default values: | ||||
| 
 | ||||
| ```yaml | ||||
| tags: | | ||||
|   type=raw,enable=true,priority=200,prefix=,suffix=,value= | ||||
| ``` | ||||
| 
 | ||||
| ### `type=sha` | ||||
| 
 | ||||
| ```yaml | ||||
| tags: | | ||||
|   # minimal | ||||
|   type=sha | ||||
| ``` | ||||
| 
 | ||||
| Output Git short commit as Docker tag like `sha-ad132f5`. | ||||
| 
 | ||||
| Extended attributes and default values: | ||||
| 
 | ||||
| ```yaml | ||||
| tags: | | ||||
|   type=sha,enable=true,priority=100,prefix=sha-,suffix= | ||||
| ``` | ||||
| 
 | ||||
| ## Notes | ||||
| 
 | ||||
| ### Latest tag | ||||
| 
 | ||||
| Latest Docker tag will be generated by default on `push tag` event. If for example you push the `v1.2.3` Git tag, | ||||
| you will have at the output of this action the Docker tags `v1.2.3` and `latest`. But you can allow the latest tag to be | ||||
| generated only if `tag-semver` is a valid [semver](https://semver.org/) or if Git tag matches a regular expression | ||||
| with the [`tag-match` input](#tag-match-examples). Can be disabled if `tag-latest` is `false`. | ||||
| 
 | ||||
| ### Handle semver tag | ||||
| 
 | ||||
| If Git tag is a valid [semver](https://semver.org/) you can handle it to output multi Docker tags at once. | ||||
| `tag-semver` supports multi-line [Handlebars template](https://handlebarsjs.com/guide/) with the following inputs: | ||||
| 
 | ||||
| | Git tag            | `tag-semver`                                             | Valid              | Output tags                | Output version               | | ||||
| |--------------------|----------------------------------------------------------|--------------------|----------------------------|------------------------------| | ||||
| | `v1.2.3`           | `{{raw}}`                                                | :white_check_mark: | `v1.2.3`, `latest`         | `v1.2.3`                     | | ||||
| | `v1.2.3`           | `{{version}}`                                            | :white_check_mark: | `1.2.3`, `latest`          | `1.2.3`                      | | ||||
| | `v1.2.3`           | `{{major}}.{{minor}}`                                    | :white_check_mark: | `1.2`, `latest`            | `1.2`                        | | ||||
| | `v1.2.3`           | `v{{major}}`                                             | :white_check_mark: | `v1`, `latest`             | `v1`                         | | ||||
| | `v1.2.3`           | `{{minor}}`                                              | :white_check_mark: | `2`, `latest`              | `2`                          | | ||||
| | `v1.2.3`           | `{{patch}}`                                              | :white_check_mark: | `3`, `latest`              | `3`                          | | ||||
| | `v1.2.3`           | `{{major}}.{{minor}}`<br>`{{major}}.{{minor}}.{{patch}}` | :white_check_mark: | `1.2`, `1.2.3`, `latest`   | `1.2`*                       | | ||||
| | `v2.0.8-beta.67`   | `{{raw}}`                                                | :white_check_mark: | `2.0.8-beta.67`**          | `2.0.8-beta.67`              | | ||||
| | `v2.0.8-beta.67`   | `{{version}}`                                            | :white_check_mark: | `2.0.8-beta.67`            | `2.0.8-beta.67`              | | ||||
| | `v2.0.8-beta.67`   | `{{major}}.{{minor}}`                                    | :white_check_mark: | `2.0.8-beta.67`**          | `2.0.8-beta.67`              | | ||||
| | `release1`         | `{{raw}}`                                                | :x:                | `release1`                 | `release1`                   | | ||||
| 
 | ||||
| > *First occurrence of `tag-semver` will be taken as `output.version` | ||||
| 
 | ||||
| > **Pre-release (rc, beta, alpha) will only extend `{{version}}` as tag because they are updated frequently, | ||||
| > and contain many breaking changes that are (by the author's design) not yet fit for public consumption. | ||||
| 
 | ||||
| ### `tag-match` examples | ||||
| 
 | ||||
| | Git tag                 | `tag-match`                        | `tag-match-group` | Match                | Output tags               | Output version               | | ||||
| |-------------------------|------------------------------------|-------------------|----------------------|---------------------------|------------------------------| | ||||
| | `v1.2.3`                | `\d{1,3}.\d{1,3}.\d{1,3}`          | `0`               | :white_check_mark:   | `1.2.3`, `latest`         | `1.2.3`                      | | ||||
| | `v2.0.8-beta.67`        | `v(.*)`                            | `1`               | :white_check_mark:   | `2.0.8-beta.67`, `latest` | `2.0.8-beta.67`              | | ||||
| | `v2.0.8-beta.67`        | `v(\d.\d)`                         | `1`               | :white_check_mark:   | `2.0`, `latest`           | `2.0`                        | | ||||
| | `release1`              | `\d{1,3}.\d{1,3}`                  | `0`               | :x:                  | `release1`                | `release1`                   | | ||||
| | `20200110-RC2`          | `\d+`                              | `0`               | :white_check_mark:   | `20200110`, `latest`      | `20200110`                   | | ||||
| 
 | ||||
| ### Schedule tag | ||||
| 
 | ||||
| `tag-schedule` is specially crafted input to support [Handlebars template](https://handlebarsjs.com/guide/) with | ||||
| the following expressions: | ||||
| 
 | ||||
| | Expression              | Example                                   | Description                              | | ||||
| |-------------------------|-------------------------------------------|------------------------------------------| | ||||
| | `{{date 'format'}}`     | `{{date 'YYYYMMDD'}}` > `20200110`        | Render date by its [moment format](https://momentjs.com/docs/#/displaying/format/) | ||||
| 
 | ||||
| You can find more examples in the [CI workflow](.github/workflows/ci.yml). | ||||
| `latest` tag is handled through the [`flavor` input](#flavor-input). It will be generated by default (`auto` mode) for: | ||||
| * [`type=ref,event=tag`](#typeref) | ||||
| * [`type=semver,pattern=...`](#typesemver) | ||||
| * [`type=match,pattern=...`](#typematch) | ||||
| 
 | ||||
| ### Overwrite labels | ||||
| 
 | ||||
| @ -362,10 +566,10 @@ labels generated are not suitable, you can overwrite them like this: | ||||
|       - | ||||
|         name: Docker meta | ||||
|         id: docker_meta | ||||
|         uses: crazy-max/ghaction-docker-meta@v1 | ||||
|         uses: crazy-max/ghaction-docker-meta@v2 | ||||
|         with: | ||||
|           images: name/app | ||||
|           label-custom: | | ||||
|           labels: | | ||||
|             maintainer=CrazyMax | ||||
|             org.opencontainers.image.title=MyCustomTitle | ||||
|             org.opencontainers.image.description=Another description | ||||
|  | ||||
							
								
								
									
										295
									
								
								UPGRADE.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										295
									
								
								UPGRADE.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,295 @@ | ||||
| # Upgrade notes | ||||
| 
 | ||||
| ## 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@v2 | ||||
|       - | ||||
|         name: Docker meta | ||||
|         id: meta | ||||
|         uses: crazy-max/ghaction-docker-meta@v1 | ||||
|         with: | ||||
|           images: name/app | ||||
|       - | ||||
|         name: Login to DockerHub | ||||
|         if: github.event_name != 'pull_request' | ||||
|         uses: docker/login-action@v1 | ||||
|         with: | ||||
|           username: ${{ secrets.DOCKERHUB_USERNAME }} | ||||
|           password: ${{ secrets.DOCKERHUB_TOKEN }} | ||||
|       - | ||||
|         name: Build and push | ||||
|         uses: docker/build-push-action@v2 | ||||
|         with: | ||||
|           context: . | ||||
|           push: ${{ github.event_name != 'pull_request' }} | ||||
|           tags: ${{ steps.docker_meta.outputs.tags }} | ||||
|           labels: ${{ steps.docker_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@v2 | ||||
|       - | ||||
|         name: Docker meta | ||||
|         id: meta | ||||
|         uses: crazy-max/ghaction-docker-meta@v2 | ||||
|         with: | ||||
|           images: name/app | ||||
|       - | ||||
|         name: Login to DockerHub | ||||
|         if: github.event_name != 'pull_request' | ||||
|         uses: docker/login-action@v1 | ||||
|         with: | ||||
|           username: ${{ secrets.DOCKERHUB_USERNAME }} | ||||
|           password: ${{ secrets.DOCKERHUB_TOKEN }} | ||||
|       - | ||||
|         name: Build and push | ||||
|         uses: docker/build-push-action@v2 | ||||
|         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@v2 | ||||
|       - | ||||
|         name: Docker meta | ||||
|         id: meta | ||||
|         uses: crazy-max/ghaction-docker-meta@v1 | ||||
|         with: | ||||
|           images: name/app | ||||
|           tag-semver: | | ||||
|             {{version}} | ||||
|             {{major}}.{{minor}} | ||||
|       - | ||||
|         name: Login to DockerHub | ||||
|         if: github.event_name != 'pull_request' | ||||
|         uses: docker/login-action@v1 | ||||
|         with: | ||||
|           username: ${{ secrets.DOCKERHUB_USERNAME }} | ||||
|           password: ${{ secrets.DOCKERHUB_TOKEN }} | ||||
|       - | ||||
|         name: Build and push | ||||
|         uses: docker/build-push-action@v2 | ||||
|         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@v2 | ||||
|       - | ||||
|         name: Docker meta | ||||
|         id: meta | ||||
|         uses: crazy-max/ghaction-docker-meta@v2 | ||||
|         with: | ||||
|           images: name/app | ||||
|           tags: | | ||||
|             type=ref,event=branch | ||||
|             type=ref,event=tag | ||||
|             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@v1 | ||||
|         with: | ||||
|           username: ${{ secrets.DOCKERHUB_USERNAME }} | ||||
|           password: ${{ secrets.DOCKERHUB_TOKEN }} | ||||
|       - | ||||
|         name: Build and push | ||||
|         uses: docker/build-push-action@v2 | ||||
|         with: | ||||
|           context: . | ||||
|           push: ${{ github.event_name != 'pull_request' }} | ||||
|           tags: ${{ steps.meta.outputs.tags }} | ||||
|           labels: ${{ steps.meta.outputs.labels }} | ||||
| ``` | ||||
							
								
								
									
										115
									
								
								__tests__/flavor.test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								__tests__/flavor.test.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,115 @@ | ||||
| import {Flavor, Transform} from '../src/flavor'; | ||||
| 
 | ||||
| describe('transform', () => { | ||||
|   // prettier-ignore
 | ||||
|   test.each([ | ||||
|     [ | ||||
|       [ | ||||
|         `randomstr`, | ||||
|         `latest=auto` | ||||
|       ], | ||||
|       {} as Flavor, | ||||
|       true | ||||
|     ], | ||||
|     [ | ||||
|       [ | ||||
|         `unknwown=foo` | ||||
|       ], | ||||
|       {} as Flavor, | ||||
|       true | ||||
|     ], | ||||
|     [ | ||||
|       [ | ||||
|         `latest`, | ||||
|       ], | ||||
|       {} as Flavor, | ||||
|       true | ||||
|     ], | ||||
|     [ | ||||
|       [ | ||||
|         `latest=true` | ||||
|       ], | ||||
|       { | ||||
|         latest: "true", | ||||
|         prefix: "", | ||||
|         suffix: "" | ||||
|       } as Flavor, | ||||
|       false | ||||
|     ], | ||||
|     [ | ||||
|       [ | ||||
|         `latest=false` | ||||
|       ], | ||||
|       { | ||||
|         latest: "false", | ||||
|         prefix: "", | ||||
|         suffix: "" | ||||
|       } as Flavor, | ||||
|       false | ||||
|     ], | ||||
|     [ | ||||
|       [ | ||||
|         `latest=auto` | ||||
|       ], | ||||
|       { | ||||
|         latest: "auto", | ||||
|         prefix: "", | ||||
|         suffix: "" | ||||
|       } as Flavor, | ||||
|       false | ||||
|     ], | ||||
|     [ | ||||
|       [ | ||||
|         `latest=foo` | ||||
|       ], | ||||
|       {} as Flavor, | ||||
|       true | ||||
|     ], | ||||
|     [ | ||||
|       [ | ||||
|         `prefix=sha-` | ||||
|       ], | ||||
|       { | ||||
|         latest: "auto", | ||||
|         prefix: "sha-", | ||||
|         suffix: "" | ||||
|       } as Flavor, | ||||
|       false | ||||
|     ], | ||||
|     [ | ||||
|       [ | ||||
|         `suffix=-alpine` | ||||
|       ], | ||||
|       { | ||||
|         latest: "auto", | ||||
|         prefix: "", | ||||
|         suffix: "-alpine" | ||||
|       } as Flavor, | ||||
|       false | ||||
|     ], | ||||
|     [ | ||||
|       [ | ||||
|         `latest=false`, | ||||
|         `prefix=dev-`, | ||||
|         `suffix=-alpine` | ||||
|       ], | ||||
|       { | ||||
|         latest: "false", | ||||
|         prefix: "dev-", | ||||
|         suffix: "-alpine" | ||||
|       } as Flavor, | ||||
|       false | ||||
|     ], | ||||
|   ])('given %p attributes ', async (inputs: string[], expected: Flavor, invalid: boolean) => { | ||||
|     try { | ||||
|       const flavor = Transform(inputs); | ||||
|       console.log(flavor); | ||||
|       expect(flavor).toEqual(expected); | ||||
|     } catch (err) { | ||||
|       if (!invalid) { | ||||
|         console.error(err); | ||||
|       } | ||||
|       expect(true).toBe(invalid); | ||||
|     } | ||||
|   }); | ||||
| }); | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										471
									
								
								__tests__/tag.test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										471
									
								
								__tests__/tag.test.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,471 @@ | ||||
| import {Transform, Parse, Tag, Type, RefEvent, DefaultPriorities} from '../src/tag'; | ||||
| 
 | ||||
| describe('transform', () => { | ||||
|   // prettier-ignore
 | ||||
|   test.each([ | ||||
|     [ | ||||
|       [ | ||||
|         `type=ref,event=branch`, | ||||
|         `type=ref,event=tag`, | ||||
|         `type=ref,event=pr`, | ||||
|         `type=schedule`, | ||||
|         `type=sha`, | ||||
|         `type=raw,foo`, | ||||
|         `type=edge`, | ||||
|         `type=semver,pattern={{version}}`, | ||||
|         `type=match,"pattern=\\d{1,3}.\\d{1,3}.\\d{1,3}"` | ||||
|       ], | ||||
|       [ | ||||
|         { | ||||
|           type: Type.Schedule, | ||||
|           attrs: { | ||||
|             "priority": DefaultPriorities[Type.Schedule], | ||||
|             "enable": "true", | ||||
|             "prefix": "", | ||||
|             "suffix": "", | ||||
|             "pattern": "nightly" | ||||
|           } | ||||
|         }, | ||||
|         { | ||||
|           type: Type.Semver, | ||||
|           attrs: { | ||||
|             "priority": DefaultPriorities[Type.Semver], | ||||
|             "enable": "true", | ||||
|             "prefix": "", | ||||
|             "suffix": "", | ||||
|             "pattern": "{{version}}", | ||||
|             "value": "" | ||||
|           } | ||||
|         }, | ||||
|         { | ||||
|           type: Type.Match, | ||||
|           attrs: { | ||||
|             "priority": DefaultPriorities[Type.Match], | ||||
|             "enable": "true", | ||||
|             "prefix": "", | ||||
|             "suffix": "", | ||||
|             "pattern": "\\d{1,3}.\\d{1,3}.\\d{1,3}", | ||||
|             "group": "0", | ||||
|             "value": "" | ||||
|           } | ||||
|         }, | ||||
|         { | ||||
|           type: Type.Edge, | ||||
|           attrs: { | ||||
|             "priority": DefaultPriorities[Type.Edge], | ||||
|             "enable": "true", | ||||
|             "prefix": "", | ||||
|             "suffix": "", | ||||
|             "branch": "" | ||||
|           } | ||||
|         }, | ||||
|         { | ||||
|           type: Type.Ref, | ||||
|           attrs: { | ||||
|             "priority": DefaultPriorities[Type.Ref], | ||||
|             "enable": "true", | ||||
|             "prefix": "", | ||||
|             "suffix": "", | ||||
|             "event": RefEvent.Branch | ||||
|           } | ||||
|         }, | ||||
|         { | ||||
|           type: Type.Ref, | ||||
|           attrs: { | ||||
|             "priority": DefaultPriorities[Type.Ref], | ||||
|             "enable": "true", | ||||
|             "prefix": "", | ||||
|             "suffix": "", | ||||
|             "event": RefEvent.Tag | ||||
|           } | ||||
|         }, | ||||
|         { | ||||
|           type: Type.Ref, | ||||
|           attrs: { | ||||
|             "priority": DefaultPriorities[Type.Ref], | ||||
|             "enable": "true", | ||||
|             "prefix": "pr-", | ||||
|             "suffix": "", | ||||
|             "event": RefEvent.PR | ||||
|           } | ||||
|         }, | ||||
|         { | ||||
|           type: Type.Raw, | ||||
|           attrs: { | ||||
|             "priority": DefaultPriorities[Type.Raw], | ||||
|             "enable": "true", | ||||
|             "prefix": "", | ||||
|             "suffix": "", | ||||
|             "value": "foo" | ||||
|           } | ||||
|         }, | ||||
|         { | ||||
|           type: Type.Sha, | ||||
|           attrs: { | ||||
|             "priority": DefaultPriorities[Type.Sha], | ||||
|             "enable": "true", | ||||
|             "prefix": "sha-", | ||||
|             "suffix": "" | ||||
|           } | ||||
|         } | ||||
|       ] as Tag[], | ||||
|       false | ||||
|     ] | ||||
|   ])('given %p', async (l: string[], expected: Tag[], invalid: boolean) => { | ||||
|     try { | ||||
|       const tags = Transform(l); | ||||
|       console.log(tags); | ||||
|       expect(tags).toEqual(expected); | ||||
|     } catch (err) { | ||||
|       if (!invalid) { | ||||
|         console.error(err); | ||||
|       } | ||||
|       expect(true).toBe(invalid); | ||||
|     } | ||||
|   }); | ||||
| }); | ||||
| 
 | ||||
| describe('parse', () => { | ||||
|   // prettier-ignore
 | ||||
|   test.each([ | ||||
|     [ | ||||
|       `type=schedule,enable=true,pattern={{date 'YYYYMMDD'}}`, | ||||
|       { | ||||
|         type: Type.Schedule, | ||||
|         attrs: { | ||||
|           "priority": DefaultPriorities[Type.Schedule], | ||||
|           "enable": "true", | ||||
|           "prefix": "", | ||||
|           "suffix": "", | ||||
|           "pattern": "{{date 'YYYYMMDD'}}" | ||||
|         } | ||||
|       } as Tag, | ||||
|       false | ||||
|     ], | ||||
|     [ | ||||
|       `type=semver,enable=true,pattern={{version}}`, | ||||
|       { | ||||
|         type: Type.Semver, | ||||
|         attrs: { | ||||
|           "priority": DefaultPriorities[Type.Semver], | ||||
|           "enable": "true", | ||||
|           "prefix": "", | ||||
|           "suffix": "", | ||||
|           "pattern": "{{version}}", | ||||
|           "value": "" | ||||
|         } | ||||
|       } as Tag, | ||||
|       false | ||||
|     ], | ||||
|     [ | ||||
|       `type=semver,priority=1,enable=true,pattern={{version}}`, | ||||
|       { | ||||
|         type: Type.Semver, | ||||
|         attrs: { | ||||
|           "priority": "1", | ||||
|           "enable": "true", | ||||
|           "prefix": "", | ||||
|           "suffix": "", | ||||
|           "pattern": "{{version}}", | ||||
|           "value": "" | ||||
|         } | ||||
|       } as Tag, | ||||
|       false | ||||
|     ], | ||||
|     [ | ||||
|       `type=semver,priority=1,enable=true,pattern={{version}},value=v1.0.0`, | ||||
|       { | ||||
|         type: Type.Semver, | ||||
|         attrs: { | ||||
|           "priority": "1", | ||||
|           "enable": "true", | ||||
|           "prefix": "", | ||||
|           "suffix": "", | ||||
|           "pattern": "{{version}}", | ||||
|           "value": "v1.0.0" | ||||
|         } | ||||
|       } as Tag, | ||||
|       false | ||||
|     ], | ||||
|     [ | ||||
|       `type=match,enable=true,pattern=v(.*),group=1`, | ||||
|       { | ||||
|         type: Type.Match, | ||||
|         attrs: { | ||||
|           "priority": DefaultPriorities[Type.Match], | ||||
|           "enable": "true", | ||||
|           "prefix": "", | ||||
|           "suffix": "", | ||||
|           "pattern": "v(.*)", | ||||
|           "group": "1", | ||||
|           "value": "" | ||||
|         } | ||||
|       } as Tag, | ||||
|       false | ||||
|     ], | ||||
|     [ | ||||
|       `type=match,enable=true,"pattern=^v(\\d{1,3}.\\d{1,3}.\\d{1,3})$",group=1`, | ||||
|       { | ||||
|         type: Type.Match, | ||||
|         attrs: { | ||||
|           "priority": DefaultPriorities[Type.Match], | ||||
|           "enable": "true", | ||||
|           "prefix": "", | ||||
|           "suffix": "", | ||||
|           "pattern": "^v(\\d{1,3}.\\d{1,3}.\\d{1,3})$", | ||||
|           "group": "1", | ||||
|           "value": "" | ||||
|         } | ||||
|       } as Tag, | ||||
|       false | ||||
|     ], | ||||
|     [ | ||||
|       `type=match,priority=700,enable=true,pattern=v(.*),group=1`, | ||||
|       { | ||||
|         type: Type.Match, | ||||
|         attrs: { | ||||
|           "priority": "700", | ||||
|           "enable": "true", | ||||
|           "prefix": "", | ||||
|           "suffix": "", | ||||
|           "pattern": "v(.*)", | ||||
|           "group": "1", | ||||
|           "value": "" | ||||
|         } | ||||
|       } as Tag, | ||||
|       false | ||||
|     ], | ||||
|     [ | ||||
|       `type=match,enable=true,pattern=v(.*),group=1,value=v1.2.3`, | ||||
|       { | ||||
|         type: Type.Match, | ||||
|         attrs: { | ||||
|           "priority": DefaultPriorities[Type.Match], | ||||
|           "enable": "true", | ||||
|           "prefix": "", | ||||
|           "suffix": "", | ||||
|           "pattern": "v(.*)", | ||||
|           "group": "1", | ||||
|           "value": "v1.2.3" | ||||
|         } | ||||
|       } as Tag, | ||||
|       false | ||||
|     ], | ||||
|     [ | ||||
|       `type=match,enable=true,pattern=v(.*),group=foo`, | ||||
|       {} as Tag, | ||||
|       true | ||||
|     ], | ||||
|     [ | ||||
|       `type=edge`, | ||||
|       { | ||||
|         type: Type.Edge, | ||||
|         attrs: { | ||||
|           "priority": DefaultPriorities[Type.Edge], | ||||
|           "enable": "true", | ||||
|           "prefix": "", | ||||
|           "suffix": "", | ||||
|           "branch": "" | ||||
|         } | ||||
|       } as Tag, | ||||
|       false | ||||
|     ], | ||||
|     [ | ||||
|       `type=edge,enable=true,branch=master`, | ||||
|       { | ||||
|         type: Type.Edge, | ||||
|         attrs: { | ||||
|           "priority": DefaultPriorities[Type.Edge], | ||||
|           "enable": "true", | ||||
|           "prefix": "", | ||||
|           "suffix": "", | ||||
|           "branch": "master" | ||||
|         } | ||||
|       } as Tag, | ||||
|       false | ||||
|     ], | ||||
|     [ | ||||
|       `type=ref,event=tag`, | ||||
|       { | ||||
|         type: Type.Ref, | ||||
|         attrs: { | ||||
|           "priority": DefaultPriorities[Type.Ref], | ||||
|           "enable": "true", | ||||
|           "prefix": "", | ||||
|           "suffix": "", | ||||
|           "event": RefEvent.Tag | ||||
|         } | ||||
|       } as Tag, | ||||
|       false | ||||
|     ], | ||||
|     [ | ||||
|       `type=ref,event=branch`, | ||||
|       { | ||||
|         type: Type.Ref, | ||||
|         attrs: { | ||||
|           "priority": DefaultPriorities[Type.Ref], | ||||
|           "enable": "true", | ||||
|           "prefix": "", | ||||
|           "suffix": "", | ||||
|           "event": RefEvent.Branch | ||||
|         } | ||||
|       } as Tag, | ||||
|       false | ||||
|     ], | ||||
|     [ | ||||
|       `type=ref,event=pr`, | ||||
|       { | ||||
|         type: Type.Ref, | ||||
|         attrs: { | ||||
|           "priority": DefaultPriorities[Type.Ref], | ||||
|           "enable": "true", | ||||
|           "prefix": "pr-", | ||||
|           "suffix": "", | ||||
|           "event": RefEvent.PR | ||||
|         } | ||||
|       } as Tag, | ||||
|       false | ||||
|     ], | ||||
|     [ | ||||
|       `type=ref,event=foo`, | ||||
|       {} as Tag, | ||||
|       true | ||||
|     ], | ||||
|     [ | ||||
|       `type=ref`, | ||||
|       {} as Tag, | ||||
|       true | ||||
|     ], | ||||
|     [ | ||||
|       `acustomtag`, | ||||
|       { | ||||
|         type: Type.Raw, | ||||
|         attrs: { | ||||
|           "priority": DefaultPriorities[Type.Raw], | ||||
|           "enable": "true", | ||||
|           "prefix": "", | ||||
|           "suffix": "", | ||||
|           "value": "acustomtag" | ||||
|         } | ||||
|       } as Tag, | ||||
|       false | ||||
|     ], | ||||
|     [ | ||||
|       `type=raw`, | ||||
|       {} as Tag, | ||||
|       true | ||||
|     ], | ||||
|     [ | ||||
|       `type=raw,value=acustomtag2`, | ||||
|       { | ||||
|         type: Type.Raw, | ||||
|         attrs: { | ||||
|           "priority": DefaultPriorities[Type.Raw], | ||||
|           "enable": "true", | ||||
|           "prefix": "", | ||||
|           "suffix": "", | ||||
|           "value": "acustomtag2" | ||||
|         } | ||||
|       } as Tag, | ||||
|       false | ||||
|     ], | ||||
|     [ | ||||
|       `type=raw,enable=true,value=acustomtag4`, | ||||
|       { | ||||
|         type: Type.Raw, | ||||
|         attrs: { | ||||
|           "priority": DefaultPriorities[Type.Raw], | ||||
|           "enable": "true", | ||||
|           "prefix": "", | ||||
|           "suffix": "", | ||||
|           "value": "acustomtag4" | ||||
|         } | ||||
|       } as Tag, | ||||
|       false | ||||
|     ], | ||||
|     [ | ||||
|       `type=raw,enable=false,value=acustomtag5`, | ||||
|       { | ||||
|         type: Type.Raw, | ||||
|         attrs: { | ||||
|           "priority": DefaultPriorities[Type.Raw], | ||||
|           "enable": "false", | ||||
|           "prefix": "", | ||||
|           "suffix": "", | ||||
|           "value": "acustomtag5" | ||||
|         } | ||||
|       } as Tag, | ||||
|       false | ||||
|     ], | ||||
|     [ | ||||
|       `type=sha`, | ||||
|       { | ||||
|         type: Type.Sha, | ||||
|         attrs: { | ||||
|           "priority": DefaultPriorities[Type.Sha], | ||||
|           "enable": "true", | ||||
|           "prefix": "sha-", | ||||
|           "suffix": "" | ||||
|         } | ||||
|       } as Tag, | ||||
|       false | ||||
|     ], | ||||
|     [ | ||||
|       `type=sha,prefix=`, | ||||
|       { | ||||
|         type: Type.Sha, | ||||
|         attrs: { | ||||
|           "priority": DefaultPriorities[Type.Sha], | ||||
|           "enable": "true", | ||||
|           "prefix": "", | ||||
|           "suffix": "" | ||||
|         } | ||||
|       } as Tag, | ||||
|       false | ||||
|     ], | ||||
|     [ | ||||
|       `type=sha,enable=false`, | ||||
|       { | ||||
|         type: Type.Sha, | ||||
|         attrs: { | ||||
|           "priority": DefaultPriorities[Type.Sha], | ||||
|           "enable": "false", | ||||
|           "prefix": "sha-", | ||||
|           "suffix": "" | ||||
|         } | ||||
|       } as Tag, | ||||
|       false | ||||
|     ], | ||||
|     [ | ||||
|       `type=semver`, | ||||
|       {} as Tag, | ||||
|       true | ||||
|     ], | ||||
|     [ | ||||
|       `type=match`, | ||||
|       {} as Tag, | ||||
|       true | ||||
|     ], | ||||
|     [ | ||||
|       `type=foo`, | ||||
|       {} as Tag, | ||||
|       true | ||||
|     ], | ||||
|     [ | ||||
|       `type=sha,enable=foo`, | ||||
|       {} as Tag, | ||||
|       true | ||||
|     ] | ||||
|   ])('given %p event ', async (s: string, expected: Tag, invalid: boolean) => { | ||||
|     try { | ||||
|       const tag = Parse(s); | ||||
|       console.log(tag); | ||||
|       expect(tag).toEqual(expected); | ||||
|     } catch (err) { | ||||
|       if (!invalid) { | ||||
|         console.error(err); | ||||
|       } | ||||
|       expect(true).toBe(invalid); | ||||
|     } | ||||
|   }); | ||||
| }); | ||||
							
								
								
									
										44
									
								
								action.yml
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								action.yml
									
									
									
									
									
								
							| @ -10,47 +10,13 @@ inputs: | ||||
|   images: | ||||
|     description: 'List of Docker images to use as base name for tags' | ||||
|     required: true | ||||
|   tag-sha: | ||||
|     description: 'Add git short SHA as Docker tag' | ||||
|     default: 'false' | ||||
|   tags: | ||||
|     description: 'List of tags as key-value pair attributes' | ||||
|     required: false | ||||
|   tag-edge: | ||||
|     description: 'Enable edge branch tagging' | ||||
|     default: 'false' | ||||
|   flavor: | ||||
|     description: 'Flavors to apply' | ||||
|     required: false | ||||
|   tag-edge-branch: | ||||
|     description: 'Branch that will be tagged as edge (default repo.default_branch)' | ||||
|     required: false | ||||
|   tag-semver: | ||||
|     description: 'Handle Git tag as semver template if possible' | ||||
|     required: false | ||||
|   tag-match: | ||||
|     description: 'RegExp to match against a Git tag and use match group as Docker tag' | ||||
|     required: false | ||||
|   tag-match-group: | ||||
|     description: 'Group to get if tag-match matches (default 0)' | ||||
|     default: '0' | ||||
|     required: false | ||||
|   tag-latest: | ||||
|     description: 'Set latest Docker tag if tag-semver, tag-match or Git tag event occurs' | ||||
|     default: 'true' | ||||
|     required: false | ||||
|   tag-match-latest: | ||||
|     deprecationMessage: 'tag-match-latest is deprecated. Use tag-latest instead' | ||||
|     description: '(DEPRECATED) Set latest Docker tag if tag-match matches or on Git tag event' | ||||
|     default: 'true' | ||||
|     required: false | ||||
|   tag-schedule: | ||||
|     description: 'Template to apply to schedule tag' | ||||
|     default: 'nightly' | ||||
|     required: false | ||||
|   tag-custom: | ||||
|     description: 'List of custom tags' | ||||
|     required: false | ||||
|   tag-custom-only: | ||||
|     description: 'Only use tag-custom as Docker tags' | ||||
|     required: false | ||||
|   label-custom: | ||||
|   labels: | ||||
|     description: 'List of custom labels' | ||||
|     required: false | ||||
|   sep-tags: | ||||
|  | ||||
							
								
								
									
										605
									
								
								dist/index.js
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										605
									
								
								dist/index.js
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -56,17 +56,9 @@ exports.tmpDir = tmpDir; | ||||
| function getInputs() { | ||||
|     return { | ||||
|         images: getInputList('images'), | ||||
|         tagSha: /true/i.test(core.getInput('tag-sha') || 'false'), | ||||
|         tagEdge: /true/i.test(core.getInput('tag-edge') || 'false'), | ||||
|         tagEdgeBranch: core.getInput('tag-edge-branch'), | ||||
|         tagSemver: getInputList('tag-semver'), | ||||
|         tagMatch: core.getInput('tag-match'), | ||||
|         tagMatchGroup: Number(core.getInput('tag-match-group')) || 0, | ||||
|         tagLatest: /true/i.test(core.getInput('tag-latest') || core.getInput('tag-match-latest') || 'true'), | ||||
|         tagSchedule: core.getInput('tag-schedule') || 'nightly', | ||||
|         tagCustom: getInputList('tag-custom'), | ||||
|         tagCustomOnly: /true/i.test(core.getInput('tag-custom-only') || 'false'), | ||||
|         labelCustom: getInputList('label-custom', true), | ||||
|         tags: getInputList('tags', true), | ||||
|         flavor: getInputList('flavor', true), | ||||
|         labels: getInputList('labels', true), | ||||
|         sepTags: core.getInput('sep-tags') || `\n`, | ||||
|         sepLabels: core.getInput('sep-labels') || `\n`, | ||||
|         githubToken: core.getInput('github-token') | ||||
| @ -106,6 +98,52 @@ exports.asyncForEach = (array, callback) => __awaiter(void 0, void 0, void 0, fu | ||||
| 
 | ||||
| /***/ }), | ||||
| 
 | ||||
| /***/ 3716: | ||||
| /***/ ((__unused_webpack_module, exports) => { | ||||
| 
 | ||||
| "use strict"; | ||||
| 
 | ||||
| Object.defineProperty(exports, "__esModule", ({ value: true })); | ||||
| exports.Transform = void 0; | ||||
| function Transform(inputs) { | ||||
|     const flavor = { | ||||
|         latest: 'auto', | ||||
|         prefix: '', | ||||
|         suffix: '' | ||||
|     }; | ||||
|     for (const input of inputs) { | ||||
|         const parts = input.split('=', 2); | ||||
|         if (parts.length == 1) { | ||||
|             throw new Error(`Invalid entry: ${input}`); | ||||
|         } | ||||
|         switch (parts[0]) { | ||||
|             case 'latest': { | ||||
|                 flavor.latest = parts[1]; | ||||
|                 if (!['auto', 'true', 'false'].includes(flavor.latest)) { | ||||
|                     throw new Error(`Invalid latest flavor entry: ${input}`); | ||||
|                 } | ||||
|                 break; | ||||
|             } | ||||
|             case 'prefix': { | ||||
|                 flavor.prefix = parts[1]; | ||||
|                 break; | ||||
|             } | ||||
|             case 'suffix': { | ||||
|                 flavor.suffix = parts[1]; | ||||
|                 break; | ||||
|             } | ||||
|             default: { | ||||
|                 throw new Error(`Unknown entry: ${input}`); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return flavor; | ||||
| } | ||||
| exports.Transform = Transform; | ||||
| //# sourceMappingURL=flavor.js.map
 | ||||
| 
 | ||||
| /***/ }), | ||||
| 
 | ||||
| /***/ 5928: | ||||
| /***/ (function(__unused_webpack_module, exports, __webpack_require__) { | ||||
| 
 | ||||
| @ -221,20 +259,30 @@ function run() { | ||||
|             core.endGroup(); | ||||
|             const meta = new meta_1.Meta(inputs, context, repo); | ||||
|             const version = meta.version; | ||||
|             core.startGroup(`Docker image version`); | ||||
|             core.info(version.main || ''); | ||||
|             core.endGroup(); | ||||
|             if (meta.version.main == undefined || meta.version.main.length == 0) { | ||||
|                 core.warning(`No Docker image version has been generated. Check tags input.`); | ||||
|             } | ||||
|             else { | ||||
|                 core.startGroup(`Docker image version`); | ||||
|                 core.info(version.main || ''); | ||||
|                 core.endGroup(); | ||||
|             } | ||||
|             core.setOutput('version', version.main || ''); | ||||
|             // Docker tags
 | ||||
|             const tags = meta.tags(); | ||||
|             core.startGroup(`Docker tags`); | ||||
|             for (let tag of tags) { | ||||
|                 core.info(tag); | ||||
|             const tags = meta.getTags(); | ||||
|             if (tags.length == 0) { | ||||
|                 core.warning('No Docker tag has been generated. Check tags input.'); | ||||
|             } | ||||
|             else { | ||||
|                 core.startGroup(`Docker tags`); | ||||
|                 for (let tag of tags) { | ||||
|                     core.info(tag); | ||||
|                 } | ||||
|                 core.endGroup(); | ||||
|             } | ||||
|             core.endGroup(); | ||||
|             core.setOutput('tags', tags.join(inputs.sepTags)); | ||||
|             // Docker labels
 | ||||
|             const labels = meta.labels(); | ||||
|             const labels = meta.getLabels(); | ||||
|             core.startGroup(`Docker labels`); | ||||
|             for (let label of labels) { | ||||
|                 core.info(label); | ||||
| @ -242,7 +290,7 @@ function run() { | ||||
|             core.endGroup(); | ||||
|             core.setOutput('labels', labels.join(inputs.sepLabels)); | ||||
|             // Bake definition file
 | ||||
|             const bakeFile = meta.bakeFile(); | ||||
|             const bakeFile = meta.getBakeFile(); | ||||
|             core.startGroup(`Bake definition file`); | ||||
|             core.info(fs.readFileSync(bakeFile, 'utf8')); | ||||
|             core.endGroup(); | ||||
| @ -293,99 +341,286 @@ const path = __importStar(__webpack_require__(5622)); | ||||
| const moment_1 = __importDefault(__webpack_require__(9623)); | ||||
| const semver = __importStar(__webpack_require__(1383)); | ||||
| const context_1 = __webpack_require__(3842); | ||||
| const tcl = __importStar(__webpack_require__(2829)); | ||||
| const fcl = __importStar(__webpack_require__(3716)); | ||||
| const core = __importStar(__webpack_require__(2186)); | ||||
| class Meta { | ||||
|     constructor(inputs, context, repo) { | ||||
|         this.inputs = inputs; | ||||
|         if (!this.inputs.tagEdgeBranch) { | ||||
|             this.inputs.tagEdgeBranch = repo.default_branch; | ||||
|         } | ||||
|         this.context = context; | ||||
|         this.repo = repo; | ||||
|         this.tags = tcl.Transform(inputs.tags); | ||||
|         this.flavor = fcl.Transform(inputs.flavor); | ||||
|         this.date = new Date(); | ||||
|         this.version = this.getVersion(); | ||||
|     } | ||||
|     getVersion() { | ||||
|         const currentDate = this.date; | ||||
|         let version = { | ||||
|             main: undefined, | ||||
|             partial: [], | ||||
|             latest: false | ||||
|             latest: undefined | ||||
|         }; | ||||
|         if (/schedule/.test(this.context.eventName)) { | ||||
|             version.main = handlebars.compile(this.inputs.tagSchedule)({ | ||||
|                 date: function (format) { | ||||
|                     return moment_1.default(currentDate).utc().format(format); | ||||
|         for (const tag of this.tags) { | ||||
|             switch (tag.type) { | ||||
|                 case tcl.Type.Schedule: { | ||||
|                     version = this.procSchedule(version, tag); | ||||
|                     break; | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
|         else if (/^refs\/tags\//.test(this.context.ref)) { | ||||
|             version.main = this.context.ref.replace(/^refs\/tags\//g, '').replace(/\//g, '-'); | ||||
|             if (this.inputs.tagSemver.length > 0 && !semver.valid(version.main)) { | ||||
|                 core.warning(`${version.main} is not a valid semver. More info: https://semver.org/`); | ||||
|             } | ||||
|             if (this.inputs.tagSemver.length > 0 && semver.valid(version.main)) { | ||||
|                 const sver = semver.parse(version.main, { | ||||
|                     includePrerelease: true | ||||
|                 }); | ||||
|                 if (semver.prerelease(version.main)) { | ||||
|                     version.main = handlebars.compile('{{version}}')(sver); | ||||
|                 case tcl.Type.Semver: { | ||||
|                     version = this.procSemver(version, tag); | ||||
|                     break; | ||||
|                 } | ||||
|                 else { | ||||
|                     version.latest = this.inputs.tagLatest; | ||||
|                     version.main = handlebars.compile(this.inputs.tagSemver[0])(sver); | ||||
|                     for (const semverTpl of this.inputs.tagSemver) { | ||||
|                         const partial = handlebars.compile(semverTpl)(sver); | ||||
|                         if (partial == version.main) { | ||||
|                             continue; | ||||
|                         } | ||||
|                         version.partial.push(partial); | ||||
|                 case tcl.Type.Match: { | ||||
|                     version = this.procMatch(version, tag); | ||||
|                     break; | ||||
|                 } | ||||
|                 case tcl.Type.Ref: { | ||||
|                     if (tag.attrs['event'] == tcl.RefEvent.Branch) { | ||||
|                         version = this.procRefBranch(version, tag); | ||||
|                     } | ||||
|                     else if (tag.attrs['event'] == tcl.RefEvent.Tag) { | ||||
|                         version = this.procRefTag(version, tag); | ||||
|                     } | ||||
|                     else if (tag.attrs['event'] == tcl.RefEvent.PR) { | ||||
|                         version = this.procRefPr(version, tag); | ||||
|                     } | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|             else if (this.inputs.tagMatch) { | ||||
|                 let tagMatch; | ||||
|                 const isRegEx = this.inputs.tagMatch.match(/^\/(.+)\/(.*)$/); | ||||
|                 if (isRegEx) { | ||||
|                     tagMatch = version.main.match(new RegExp(isRegEx[1], isRegEx[2])); | ||||
|                 case tcl.Type.Edge: { | ||||
|                     version = this.procEdge(version, tag); | ||||
|                     break; | ||||
|                 } | ||||
|                 else { | ||||
|                     tagMatch = version.main.match(this.inputs.tagMatch); | ||||
|                 case tcl.Type.Raw: { | ||||
|                     version = this.procRaw(version, tag); | ||||
|                     break; | ||||
|                 } | ||||
|                 if (tagMatch) { | ||||
|                     version.main = tagMatch[this.inputs.tagMatchGroup]; | ||||
|                     version.latest = this.inputs.tagLatest; | ||||
|                 case tcl.Type.Sha: { | ||||
|                     version = this.procSha(version, tag); | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|             else { | ||||
|                 version.latest = this.inputs.tagLatest; | ||||
|             } | ||||
|         } | ||||
|         else if (/^refs\/heads\//.test(this.context.ref)) { | ||||
|             version.main = this.context.ref.replace(/^refs\/heads\//g, '').replace(/[^a-zA-Z0-9._-]+/g, '-'); | ||||
|             if (this.inputs.tagEdge && this.inputs.tagEdgeBranch === version.main) { | ||||
|                 version.main = 'edge'; | ||||
|             } | ||||
|         } | ||||
|         else if (/^refs\/pull\//.test(this.context.ref)) { | ||||
|             version.main = `pr-${this.context.ref.replace(/^refs\/pull\//g, '').replace(/\/merge$/g, '')}`; | ||||
|         } | ||||
|         if (this.inputs.tagCustom.length > 0) { | ||||
|             if (this.inputs.tagCustomOnly) { | ||||
|                 version = { | ||||
|                     main: this.inputs.tagCustom.shift(), | ||||
|                     partial: this.inputs.tagCustom, | ||||
|                     latest: false | ||||
|                 }; | ||||
|             } | ||||
|             else { | ||||
|                 version.partial.push(...this.inputs.tagCustom); | ||||
|             } | ||||
|         } | ||||
|         version.partial = version.partial.filter((item, index) => version.partial.indexOf(item) === index); | ||||
|         if (version.latest == undefined) { | ||||
|             version.latest = false; | ||||
|         } | ||||
|         return version; | ||||
|     } | ||||
|     tags() { | ||||
|     procSchedule(version, tag) { | ||||
|         if (!/schedule/.test(this.context.eventName)) { | ||||
|             return version; | ||||
|         } | ||||
|         const currentDate = this.date; | ||||
|         const vraw = handlebars.compile(tag.attrs['pattern'])({ | ||||
|             date: function (format) { | ||||
|                 return moment_1.default(currentDate).utc().format(format); | ||||
|             } | ||||
|         }); | ||||
|         if (version.main == undefined) { | ||||
|             version.main = vraw; | ||||
|         } | ||||
|         else if (vraw !== version.main) { | ||||
|             version.partial.push(vraw); | ||||
|         } | ||||
|         if (version.latest == undefined) { | ||||
|             version.latest = this.flavor.latest == 'auto' ? false : this.flavor.latest == 'true'; | ||||
|         } | ||||
|         return version; | ||||
|     } | ||||
|     procSemver(version, tag) { | ||||
|         if (!/^refs\/tags\//.test(this.context.ref) && tag.attrs['value'].length == 0) { | ||||
|             return version; | ||||
|         } | ||||
|         let vraw; | ||||
|         if (tag.attrs['value'].length > 0) { | ||||
|             vraw = tag.attrs['value']; | ||||
|         } | ||||
|         else { | ||||
|             vraw = this.context.ref.replace(/^refs\/tags\//g, '').replace(/\//g, '-'); | ||||
|         } | ||||
|         if (!semver.valid(vraw)) { | ||||
|             core.warning(`${vraw} is not a valid semver. More info: https://semver.org/`); | ||||
|             return version; | ||||
|         } | ||||
|         let latest = false; | ||||
|         const sver = semver.parse(vraw, { | ||||
|             includePrerelease: true | ||||
|         }); | ||||
|         if (semver.prerelease(vraw)) { | ||||
|             vraw = handlebars.compile('{{version}}')(sver); | ||||
|             if (version.main == undefined) { | ||||
|                 version.main = vraw; | ||||
|             } | ||||
|             else if (vraw !== version.main) { | ||||
|                 version.partial.push(vraw); | ||||
|             } | ||||
|         } | ||||
|         else { | ||||
|             vraw = handlebars.compile(tag.attrs['pattern'])(sver); | ||||
|             if (version.main == undefined) { | ||||
|                 version.main = vraw; | ||||
|             } | ||||
|             else if (vraw !== version.main) { | ||||
|                 version.partial.push(vraw); | ||||
|             } | ||||
|             latest = true; | ||||
|         } | ||||
|         if (version.latest == undefined) { | ||||
|             version.latest = this.flavor.latest == 'auto' ? latest : this.flavor.latest == 'true'; | ||||
|         } | ||||
|         return version; | ||||
|     } | ||||
|     procMatch(version, tag) { | ||||
|         if (!/^refs\/tags\//.test(this.context.ref) && tag.attrs['value'].length == 0) { | ||||
|             return version; | ||||
|         } | ||||
|         let vraw; | ||||
|         if (tag.attrs['value'].length > 0) { | ||||
|             vraw = tag.attrs['value']; | ||||
|         } | ||||
|         else { | ||||
|             vraw = this.context.ref.replace(/^refs\/tags\//g, '').replace(/\//g, '-'); | ||||
|         } | ||||
|         let latest = false; | ||||
|         let tmatch; | ||||
|         const isRegEx = tag.attrs['pattern'].match(/^\/(.+)\/(.*)$/); | ||||
|         if (isRegEx) { | ||||
|             tmatch = vraw.match(new RegExp(isRegEx[1], isRegEx[2])); | ||||
|         } | ||||
|         else { | ||||
|             tmatch = vraw.match(tag.attrs['pattern']); | ||||
|         } | ||||
|         if (tmatch) { | ||||
|             vraw = tmatch[tag.attrs['group']]; | ||||
|             latest = true; | ||||
|         } | ||||
|         if (version.main == undefined) { | ||||
|             version.main = vraw; | ||||
|         } | ||||
|         else if (vraw !== version.main) { | ||||
|             version.partial.push(vraw); | ||||
|         } | ||||
|         if (version.latest == undefined) { | ||||
|             version.latest = this.flavor.latest == 'auto' ? latest : this.flavor.latest == 'true'; | ||||
|         } | ||||
|         return version; | ||||
|     } | ||||
|     procRefBranch(version, tag) { | ||||
|         if (!/^refs\/heads\//.test(this.context.ref)) { | ||||
|             return version; | ||||
|         } | ||||
|         const vraw = this.setFlavor(this.context.ref.replace(/^refs\/heads\//g, '').replace(/[^a-zA-Z0-9._-]+/g, '-'), tag); | ||||
|         if (version.main == undefined) { | ||||
|             version.main = vraw; | ||||
|         } | ||||
|         else if (vraw !== version.main) { | ||||
|             version.partial.push(vraw); | ||||
|         } | ||||
|         if (version.latest == undefined) { | ||||
|             version.latest = this.flavor.latest == 'auto' ? false : this.flavor.latest == 'true'; | ||||
|         } | ||||
|         return version; | ||||
|     } | ||||
|     procRefTag(version, tag) { | ||||
|         if (!/^refs\/tags\//.test(this.context.ref)) { | ||||
|             return version; | ||||
|         } | ||||
|         const vraw = this.setFlavor(this.context.ref.replace(/^refs\/tags\//g, '').replace(/\//g, '-'), tag); | ||||
|         if (version.main == undefined) { | ||||
|             version.main = vraw; | ||||
|         } | ||||
|         else if (vraw !== version.main) { | ||||
|             version.partial.push(vraw); | ||||
|         } | ||||
|         if (version.latest == undefined) { | ||||
|             version.latest = this.flavor.latest == 'auto' ? true : this.flavor.latest == 'true'; | ||||
|         } | ||||
|         return version; | ||||
|     } | ||||
|     procRefPr(version, tag) { | ||||
|         if (!/^refs\/pull\//.test(this.context.ref)) { | ||||
|             return version; | ||||
|         } | ||||
|         const vraw = this.setFlavor(this.context.ref.replace(/^refs\/pull\//g, '').replace(/\/merge$/g, ''), tag); | ||||
|         if (version.main == undefined) { | ||||
|             version.main = vraw; | ||||
|         } | ||||
|         else if (vraw !== version.main) { | ||||
|             version.partial.push(vraw); | ||||
|         } | ||||
|         if (version.latest == undefined) { | ||||
|             version.latest = this.flavor.latest == 'auto' ? false : this.flavor.latest == 'true'; | ||||
|         } | ||||
|         return version; | ||||
|     } | ||||
|     procEdge(version, tag) { | ||||
|         if (!/^refs\/heads\//.test(this.context.ref)) { | ||||
|             return version; | ||||
|         } | ||||
|         let val = this.context.ref.replace(/^refs\/heads\//g, '').replace(/[^a-zA-Z0-9._-]+/g, '-'); | ||||
|         if (tag.attrs['branch'].length == 0) { | ||||
|             tag.attrs['branch'] = this.repo.default_branch; | ||||
|         } | ||||
|         if (tag.attrs['branch'] === val) { | ||||
|             val = 'edge'; | ||||
|         } | ||||
|         const vraw = this.setFlavor(val, tag); | ||||
|         if (version.main == undefined) { | ||||
|             version.main = vraw; | ||||
|         } | ||||
|         else if (vraw !== version.main) { | ||||
|             version.partial.push(vraw); | ||||
|         } | ||||
|         if (version.latest == undefined) { | ||||
|             version.latest = this.flavor.latest == 'auto' ? false : this.flavor.latest == 'true'; | ||||
|         } | ||||
|         return version; | ||||
|     } | ||||
|     procRaw(version, tag) { | ||||
|         const vraw = this.setFlavor(tag.attrs['value'], tag); | ||||
|         if (version.main == undefined) { | ||||
|             version.main = vraw; | ||||
|         } | ||||
|         else if (vraw !== version.main) { | ||||
|             version.partial.push(vraw); | ||||
|         } | ||||
|         if (version.latest == undefined) { | ||||
|             version.latest = this.flavor.latest == 'auto' ? false : this.flavor.latest == 'true'; | ||||
|         } | ||||
|         return version; | ||||
|     } | ||||
|     procSha(version, tag) { | ||||
|         if (!this.context.sha) { | ||||
|             return version; | ||||
|         } | ||||
|         const vraw = this.setFlavor(this.context.sha.substr(0, 7), tag); | ||||
|         if (version.main == undefined) { | ||||
|             version.main = vraw; | ||||
|         } | ||||
|         else if (vraw !== version.main) { | ||||
|             version.partial.push(vraw); | ||||
|         } | ||||
|         if (version.latest == undefined) { | ||||
|             version.latest = this.flavor.latest == 'auto' ? false : this.flavor.latest == 'true'; | ||||
|         } | ||||
|         return version; | ||||
|     } | ||||
|     setFlavor(val, tag) { | ||||
|         if (tag.attrs['prefix'].length > 0) { | ||||
|             val = `${tag.attrs['prefix']}${val}`; | ||||
|         } | ||||
|         else if (this.flavor.prefix.length > 0) { | ||||
|             val = `${this.flavor.prefix}${val}`; | ||||
|         } | ||||
|         if (tag.attrs['suffix'].length > 0) { | ||||
|             val = `${val}${tag.attrs['suffix']}`; | ||||
|         } | ||||
|         else if (this.flavor.suffix.length > 0) { | ||||
|             val = `${val}${this.flavor.suffix}`; | ||||
|         } | ||||
|         return val; | ||||
|     } | ||||
|     getTags() { | ||||
|         if (!this.version.main) { | ||||
|             return []; | ||||
|         } | ||||
| @ -399,13 +634,10 @@ class Meta { | ||||
|             if (this.version.latest) { | ||||
|                 tags.push(`${imageLc}:latest`); | ||||
|             } | ||||
|             if (this.context.sha && this.inputs.tagSha) { | ||||
|                 tags.push(`${imageLc}:sha-${this.context.sha.substr(0, 7)}`); | ||||
|             } | ||||
|         } | ||||
|         return tags; | ||||
|     } | ||||
|     labels() { | ||||
|     getLabels() { | ||||
|         var _a; | ||||
|         let labels = [ | ||||
|             `org.opencontainers.image.title=${this.repo.name || ''}`, | ||||
| @ -417,12 +649,12 @@ class Meta { | ||||
|             `org.opencontainers.image.revision=${this.context.sha || ''}`, | ||||
|             `org.opencontainers.image.licenses=${((_a = this.repo.license) === null || _a === void 0 ? void 0 : _a.spdx_id) || ''}` | ||||
|         ]; | ||||
|         labels.push(...this.inputs.labelCustom); | ||||
|         labels.push(...this.inputs.labels); | ||||
|         return labels; | ||||
|     } | ||||
|     bakeFile() { | ||||
|     getBakeFile() { | ||||
|         let jsonLabels = {}; | ||||
|         for (let label of this.labels()) { | ||||
|         for (let label of this.getLabels()) { | ||||
|             const matches = label.match(/([^=]*)=(.*)/); | ||||
|             if (!matches) { | ||||
|                 continue; | ||||
| @ -433,7 +665,7 @@ class Meta { | ||||
|         fs.writeFileSync(bakeFile, JSON.stringify({ | ||||
|             target: { | ||||
|                 'ghaction-docker-meta': { | ||||
|                     tags: this.tags(), | ||||
|                     tags: this.getTags(), | ||||
|                     labels: jsonLabels, | ||||
|                     args: { | ||||
|                         DOCKER_META_IMAGES: this.inputs.images.join(','), | ||||
| @ -450,6 +682,187 @@ exports.Meta = Meta; | ||||
| 
 | ||||
| /***/ }), | ||||
| 
 | ||||
| /***/ 2829: | ||||
| /***/ (function(__unused_webpack_module, exports, __webpack_require__) { | ||||
| 
 | ||||
| "use strict"; | ||||
| 
 | ||||
| var __importDefault = (this && this.__importDefault) || function (mod) { | ||||
|     return (mod && mod.__esModule) ? mod : { "default": mod }; | ||||
| }; | ||||
| Object.defineProperty(exports, "__esModule", ({ value: true })); | ||||
| exports.Parse = exports.Transform = exports.DefaultPriorities = exports.RefEvent = exports.Type = void 0; | ||||
| const sync_1 = __importDefault(__webpack_require__(8750)); | ||||
| var Type; | ||||
| (function (Type) { | ||||
|     Type["Schedule"] = "schedule"; | ||||
|     Type["Semver"] = "semver"; | ||||
|     Type["Match"] = "match"; | ||||
|     Type["Edge"] = "edge"; | ||||
|     Type["Ref"] = "ref"; | ||||
|     Type["Raw"] = "raw"; | ||||
|     Type["Sha"] = "sha"; | ||||
| })(Type = exports.Type || (exports.Type = {})); | ||||
| var RefEvent; | ||||
| (function (RefEvent) { | ||||
|     RefEvent["Branch"] = "branch"; | ||||
|     RefEvent["Tag"] = "tag"; | ||||
|     RefEvent["PR"] = "pr"; | ||||
| })(RefEvent = exports.RefEvent || (exports.RefEvent = {})); | ||||
| exports.DefaultPriorities = { | ||||
|     [Type.Schedule]: '1000', | ||||
|     [Type.Semver]: '900', | ||||
|     [Type.Match]: '800', | ||||
|     [Type.Edge]: '700', | ||||
|     [Type.Ref]: '600', | ||||
|     [Type.Raw]: '200', | ||||
|     [Type.Sha]: '100' | ||||
| }; | ||||
| function Transform(inputs) { | ||||
|     const tags = []; | ||||
|     if (inputs.length == 0) { | ||||
|         // prettier-ignore
 | ||||
|         inputs = [ | ||||
|             `type=schedule`, | ||||
|             `type=ref,event=${RefEvent.Branch}`, | ||||
|             `type=ref,event=${RefEvent.Tag}`, | ||||
|             `type=ref,event=${RefEvent.PR}` | ||||
|         ]; | ||||
|     } | ||||
|     for (const input of inputs) { | ||||
|         tags.push(Parse(input)); | ||||
|     } | ||||
|     return tags.sort((tag1, tag2) => { | ||||
|         if (Number(tag1.attrs['priority']) < Number(tag2.attrs['priority'])) { | ||||
|             return 1; | ||||
|         } | ||||
|         if (Number(tag1.attrs['priority']) > Number(tag2.attrs['priority'])) { | ||||
|             return -1; | ||||
|         } | ||||
|         return 0; | ||||
|     }); | ||||
| } | ||||
| exports.Transform = Transform; | ||||
| function Parse(s) { | ||||
|     const fields = sync_1.default(s, { | ||||
|         relaxColumnCount: true, | ||||
|         skipLinesWithEmptyValues: true | ||||
|     })[0]; | ||||
|     const tag = { | ||||
|         attrs: {} | ||||
|     }; | ||||
|     for (const field of fields) { | ||||
|         const parts = field.toString().split('=', 2); | ||||
|         if (parts.length == 1) { | ||||
|             tag.attrs['value'] = parts[0].trim(); | ||||
|         } | ||||
|         else { | ||||
|             const key = parts[0].trim().toLowerCase(); | ||||
|             const value = parts[1].trim(); | ||||
|             switch (key) { | ||||
|                 case 'type': { | ||||
|                     if (!Object.values(Type).includes(value)) { | ||||
|                         throw new Error(`Unknown type attribute: ${value}`); | ||||
|                     } | ||||
|                     tag.type = value; | ||||
|                     break; | ||||
|                 } | ||||
|                 default: { | ||||
|                     tag.attrs[key] = value; | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     if (tag.type == undefined) { | ||||
|         tag.type = Type.Raw; | ||||
|     } | ||||
|     switch (tag.type) { | ||||
|         case Type.Schedule: { | ||||
|             if (!tag.attrs.hasOwnProperty('pattern')) { | ||||
|                 tag.attrs['pattern'] = 'nightly'; | ||||
|             } | ||||
|             break; | ||||
|         } | ||||
|         case Type.Semver: { | ||||
|             if (!tag.attrs.hasOwnProperty('pattern')) { | ||||
|                 throw new Error(`Missing pattern attribute for ${s}`); | ||||
|             } | ||||
|             if (!tag.attrs.hasOwnProperty('value')) { | ||||
|                 tag.attrs['value'] = ''; | ||||
|             } | ||||
|             break; | ||||
|         } | ||||
|         case Type.Match: { | ||||
|             if (!tag.attrs.hasOwnProperty('pattern')) { | ||||
|                 throw new Error(`Missing pattern attribute for ${s}`); | ||||
|             } | ||||
|             if (!tag.attrs.hasOwnProperty('group')) { | ||||
|                 tag.attrs['group'] = '0'; | ||||
|             } | ||||
|             if (isNaN(+tag.attrs['group'])) { | ||||
|                 throw new Error(`Invalid match group for ${s}`); | ||||
|             } | ||||
|             if (!tag.attrs.hasOwnProperty('value')) { | ||||
|                 tag.attrs['value'] = ''; | ||||
|             } | ||||
|             break; | ||||
|         } | ||||
|         case Type.Edge: { | ||||
|             if (!tag.attrs.hasOwnProperty('branch')) { | ||||
|                 tag.attrs['branch'] = ''; | ||||
|             } | ||||
|             break; | ||||
|         } | ||||
|         case Type.Ref: { | ||||
|             if (!tag.attrs.hasOwnProperty('event')) { | ||||
|                 throw new Error(`Missing event attribute for ${s}`); | ||||
|             } | ||||
|             if (!Object.keys(RefEvent) | ||||
|                 .map(k => RefEvent[k]) | ||||
|                 .includes(tag.attrs['event'])) { | ||||
|                 throw new Error(`Invalid event for ${s}`); | ||||
|             } | ||||
|             if (tag.attrs['event'] == RefEvent.PR && !tag.attrs.hasOwnProperty('prefix')) { | ||||
|                 tag.attrs['prefix'] = 'pr-'; | ||||
|             } | ||||
|             break; | ||||
|         } | ||||
|         case Type.Raw: { | ||||
|             if (!tag.attrs.hasOwnProperty('value')) { | ||||
|                 throw new Error(`Missing value attribute for ${s}`); | ||||
|             } | ||||
|             break; | ||||
|         } | ||||
|         case Type.Sha: { | ||||
|             if (!tag.attrs.hasOwnProperty('prefix')) { | ||||
|                 tag.attrs['prefix'] = 'sha-'; | ||||
|             } | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     if (!tag.attrs.hasOwnProperty('enable')) { | ||||
|         tag.attrs['enable'] = 'true'; | ||||
|     } | ||||
|     if (!tag.attrs.hasOwnProperty('priority')) { | ||||
|         tag.attrs['priority'] = exports.DefaultPriorities[tag.type]; | ||||
|     } | ||||
|     if (!tag.attrs.hasOwnProperty('prefix')) { | ||||
|         tag.attrs['prefix'] = ''; | ||||
|     } | ||||
|     if (!tag.attrs.hasOwnProperty('suffix')) { | ||||
|         tag.attrs['suffix'] = ''; | ||||
|     } | ||||
|     if (!['true', 'false'].includes(tag.attrs['enable'])) { | ||||
|         throw new Error(`Invalid value for enable attribute: ${tag.attrs['enable']}`); | ||||
|     } | ||||
|     return tag; | ||||
| } | ||||
| exports.Parse = Parse; | ||||
| //# sourceMappingURL=tag.js.map
 | ||||
| 
 | ||||
| /***/ }), | ||||
| 
 | ||||
| /***/ 7351: | ||||
| /***/ (function(__unused_webpack_module, exports, __webpack_require__) { | ||||
| 
 | ||||
|  | ||||
| @ -8,17 +8,9 @@ let _tmpDir: string; | ||||
| 
 | ||||
| export interface Inputs { | ||||
|   images: string[]; | ||||
|   tagSha: boolean; | ||||
|   tagEdge: boolean; | ||||
|   tagEdgeBranch: string; | ||||
|   tagSemver: string[]; | ||||
|   tagMatch: string; | ||||
|   tagMatchGroup: number; | ||||
|   tagLatest: boolean; | ||||
|   tagSchedule: string; | ||||
|   tagCustom: string[]; | ||||
|   tagCustomOnly: boolean; | ||||
|   labelCustom: string[]; | ||||
|   tags: string[]; | ||||
|   flavor: string[]; | ||||
|   labels: string[]; | ||||
|   sepTags: string; | ||||
|   sepLabels: string; | ||||
|   githubToken: string; | ||||
| @ -34,17 +26,9 @@ export function tmpDir(): string { | ||||
| export function getInputs(): Inputs { | ||||
|   return { | ||||
|     images: getInputList('images'), | ||||
|     tagSha: /true/i.test(core.getInput('tag-sha') || 'false'), | ||||
|     tagEdge: /true/i.test(core.getInput('tag-edge') || 'false'), | ||||
|     tagEdgeBranch: core.getInput('tag-edge-branch'), | ||||
|     tagSemver: getInputList('tag-semver'), | ||||
|     tagMatch: core.getInput('tag-match'), | ||||
|     tagMatchGroup: Number(core.getInput('tag-match-group')) || 0, | ||||
|     tagLatest: /true/i.test(core.getInput('tag-latest') || core.getInput('tag-match-latest') || 'true'), | ||||
|     tagSchedule: core.getInput('tag-schedule') || 'nightly', | ||||
|     tagCustom: getInputList('tag-custom'), | ||||
|     tagCustomOnly: /true/i.test(core.getInput('tag-custom-only') || 'false'), | ||||
|     labelCustom: getInputList('label-custom', true), | ||||
|     tags: getInputList('tags', true), | ||||
|     flavor: getInputList('flavor', true), | ||||
|     labels: getInputList('labels', true), | ||||
|     sepTags: core.getInput('sep-tags') || `\n`, | ||||
|     sepLabels: core.getInput('sep-labels') || `\n`, | ||||
|     githubToken: core.getInput('github-token') | ||||
|  | ||||
							
								
								
									
										42
									
								
								src/flavor.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/flavor.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,42 @@ | ||||
| export interface Flavor { | ||||
|   latest: string; | ||||
|   prefix: string; | ||||
|   suffix: string; | ||||
| } | ||||
| 
 | ||||
| export function Transform(inputs: string[]): Flavor { | ||||
|   const flavor: Flavor = { | ||||
|     latest: 'auto', | ||||
|     prefix: '', | ||||
|     suffix: '' | ||||
|   }; | ||||
| 
 | ||||
|   for (const input of inputs) { | ||||
|     const parts = input.split('=', 2); | ||||
|     if (parts.length == 1) { | ||||
|       throw new Error(`Invalid entry: ${input}`); | ||||
|     } | ||||
|     switch (parts[0]) { | ||||
|       case 'latest': { | ||||
|         flavor.latest = parts[1]; | ||||
|         if (!['auto', 'true', 'false'].includes(flavor.latest)) { | ||||
|           throw new Error(`Invalid latest flavor entry: ${input}`); | ||||
|         } | ||||
|         break; | ||||
|       } | ||||
|       case 'prefix': { | ||||
|         flavor.prefix = parts[1]; | ||||
|         break; | ||||
|       } | ||||
|       case 'suffix': { | ||||
|         flavor.suffix = parts[1]; | ||||
|         break; | ||||
|       } | ||||
|       default: { | ||||
|         throw new Error(`Unknown entry: ${input}`); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   return flavor; | ||||
| } | ||||
							
								
								
									
										28
									
								
								src/main.ts
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								src/main.ts
									
									
									
									
									
								
							| @ -29,22 +29,30 @@ async function run() { | ||||
|     const meta: Meta = new Meta(inputs, context, repo); | ||||
| 
 | ||||
|     const version: Version = meta.version; | ||||
|     core.startGroup(`Docker image version`); | ||||
|     core.info(version.main || ''); | ||||
|     core.endGroup(); | ||||
|     if (meta.version.main == undefined || meta.version.main.length == 0) { | ||||
|       core.warning(`No Docker image version has been generated. Check tags input.`); | ||||
|     } else { | ||||
|       core.startGroup(`Docker image version`); | ||||
|       core.info(version.main || ''); | ||||
|       core.endGroup(); | ||||
|     } | ||||
|     core.setOutput('version', version.main || ''); | ||||
| 
 | ||||
|     // Docker tags
 | ||||
|     const tags: Array<string> = meta.tags(); | ||||
|     core.startGroup(`Docker tags`); | ||||
|     for (let tag of tags) { | ||||
|       core.info(tag); | ||||
|     const tags: Array<string> = meta.getTags(); | ||||
|     if (tags.length == 0) { | ||||
|       core.warning('No Docker tag has been generated. Check tags input.'); | ||||
|     } else { | ||||
|       core.startGroup(`Docker tags`); | ||||
|       for (let tag of tags) { | ||||
|         core.info(tag); | ||||
|       } | ||||
|       core.endGroup(); | ||||
|     } | ||||
|     core.endGroup(); | ||||
|     core.setOutput('tags', tags.join(inputs.sepTags)); | ||||
| 
 | ||||
|     // Docker labels
 | ||||
|     const labels: Array<string> = meta.labels(); | ||||
|     const labels: Array<string> = meta.getLabels(); | ||||
|     core.startGroup(`Docker labels`); | ||||
|     for (let label of labels) { | ||||
|       core.info(label); | ||||
| @ -53,7 +61,7 @@ async function run() { | ||||
|     core.setOutput('labels', labels.join(inputs.sepLabels)); | ||||
| 
 | ||||
|     // Bake definition file
 | ||||
|     const bakeFile: string = meta.bakeFile(); | ||||
|     const bakeFile: string = meta.getBakeFile(); | ||||
|     core.startGroup(`Bake definition file`); | ||||
|     core.info(fs.readFileSync(bakeFile, 'utf8')); | ||||
|     core.endGroup(); | ||||
|  | ||||
							
								
								
									
										350
									
								
								src/meta.ts
									
									
									
									
									
								
							
							
						
						
									
										350
									
								
								src/meta.ts
									
									
									
									
									
								
							| @ -4,6 +4,8 @@ import * as path from 'path'; | ||||
| import moment from 'moment'; | ||||
| import * as semver from 'semver'; | ||||
| import {Inputs, tmpDir} from './context'; | ||||
| import * as tcl from './tag'; | ||||
| import * as fcl from './flavor'; | ||||
| import * as core from '@actions/core'; | ||||
| import {Context} from '@actions/github/lib/context'; | ||||
| import {ReposGetResponseData} from '@octokit/types'; | ||||
| @ -11,7 +13,7 @@ import {ReposGetResponseData} from '@octokit/types'; | ||||
| export interface Version { | ||||
|   main: string | undefined; | ||||
|   partial: string[]; | ||||
|   latest: boolean; | ||||
|   latest: boolean | undefined; | ||||
| } | ||||
| 
 | ||||
| export class Meta { | ||||
| @ -20,96 +22,305 @@ export class Meta { | ||||
|   private readonly inputs: Inputs; | ||||
|   private readonly context: Context; | ||||
|   private readonly repo: ReposGetResponseData; | ||||
|   private readonly tags: tcl.Tag[]; | ||||
|   private readonly flavor: fcl.Flavor; | ||||
|   private readonly date: Date; | ||||
| 
 | ||||
|   constructor(inputs: Inputs, context: Context, repo: ReposGetResponseData) { | ||||
|     this.inputs = inputs; | ||||
|     if (!this.inputs.tagEdgeBranch) { | ||||
|       this.inputs.tagEdgeBranch = repo.default_branch; | ||||
|     } | ||||
|     this.context = context; | ||||
|     this.repo = repo; | ||||
|     this.tags = tcl.Transform(inputs.tags); | ||||
|     this.flavor = fcl.Transform(inputs.flavor); | ||||
|     this.date = new Date(); | ||||
|     this.version = this.getVersion(); | ||||
|   } | ||||
| 
 | ||||
|   private getVersion(): Version { | ||||
|     const currentDate = this.date; | ||||
|     let version: Version = { | ||||
|       main: undefined, | ||||
|       partial: [], | ||||
|       latest: false | ||||
|       latest: undefined | ||||
|     }; | ||||
| 
 | ||||
|     if (/schedule/.test(this.context.eventName)) { | ||||
|       version.main = handlebars.compile(this.inputs.tagSchedule)({ | ||||
|         date: function (format) { | ||||
|           return moment(currentDate).utc().format(format); | ||||
|     for (const tag of this.tags) { | ||||
|       switch (tag.type) { | ||||
|         case tcl.Type.Schedule: { | ||||
|           version = this.procSchedule(version, tag); | ||||
|           break; | ||||
|         } | ||||
|       }); | ||||
|     } else if (/^refs\/tags\//.test(this.context.ref)) { | ||||
|       version.main = this.context.ref.replace(/^refs\/tags\//g, '').replace(/\//g, '-'); | ||||
|       if (this.inputs.tagSemver.length > 0 && !semver.valid(version.main)) { | ||||
|         core.warning(`${version.main} is not a valid semver. More info: https://semver.org/`); | ||||
|       } | ||||
|       if (this.inputs.tagSemver.length > 0 && semver.valid(version.main)) { | ||||
|         const sver = semver.parse(version.main, { | ||||
|           includePrerelease: true | ||||
|         }); | ||||
|         if (semver.prerelease(version.main)) { | ||||
|           version.main = handlebars.compile('{{version}}')(sver); | ||||
|         } else { | ||||
|           version.latest = this.inputs.tagLatest; | ||||
|           version.main = handlebars.compile(this.inputs.tagSemver[0])(sver); | ||||
|           for (const semverTpl of this.inputs.tagSemver) { | ||||
|             const partial = handlebars.compile(semverTpl)(sver); | ||||
|             if (partial == version.main) { | ||||
|               continue; | ||||
|             } | ||||
|             version.partial.push(partial); | ||||
|         case tcl.Type.Semver: { | ||||
|           version = this.procSemver(version, tag); | ||||
|           break; | ||||
|         } | ||||
|         case tcl.Type.Match: { | ||||
|           version = this.procMatch(version, tag); | ||||
|           break; | ||||
|         } | ||||
|         case tcl.Type.Ref: { | ||||
|           if (tag.attrs['event'] == tcl.RefEvent.Branch) { | ||||
|             version = this.procRefBranch(version, tag); | ||||
|           } else if (tag.attrs['event'] == tcl.RefEvent.Tag) { | ||||
|             version = this.procRefTag(version, tag); | ||||
|           } else if (tag.attrs['event'] == tcl.RefEvent.PR) { | ||||
|             version = this.procRefPr(version, tag); | ||||
|           } | ||||
|           break; | ||||
|         } | ||||
|       } else if (this.inputs.tagMatch) { | ||||
|         let tagMatch; | ||||
|         const isRegEx = this.inputs.tagMatch.match(/^\/(.+)\/(.*)$/); | ||||
|         if (isRegEx) { | ||||
|           tagMatch = version.main.match(new RegExp(isRegEx[1], isRegEx[2])); | ||||
|         } else { | ||||
|           tagMatch = version.main.match(this.inputs.tagMatch); | ||||
|         case tcl.Type.Edge: { | ||||
|           version = this.procEdge(version, tag); | ||||
|           break; | ||||
|         } | ||||
|         if (tagMatch) { | ||||
|           version.main = tagMatch[this.inputs.tagMatchGroup]; | ||||
|           version.latest = this.inputs.tagLatest; | ||||
|         case tcl.Type.Raw: { | ||||
|           version = this.procRaw(version, tag); | ||||
|           break; | ||||
|         } | ||||
|         case tcl.Type.Sha: { | ||||
|           version = this.procSha(version, tag); | ||||
|           break; | ||||
|         } | ||||
|       } else { | ||||
|         version.latest = this.inputs.tagLatest; | ||||
|       } | ||||
|     } else if (/^refs\/heads\//.test(this.context.ref)) { | ||||
|       version.main = this.context.ref.replace(/^refs\/heads\//g, '').replace(/[^a-zA-Z0-9._-]+/g, '-'); | ||||
|       if (this.inputs.tagEdge && this.inputs.tagEdgeBranch === version.main) { | ||||
|         version.main = 'edge'; | ||||
|       } | ||||
|     } else if (/^refs\/pull\//.test(this.context.ref)) { | ||||
|       version.main = `pr-${this.context.ref.replace(/^refs\/pull\//g, '').replace(/\/merge$/g, '')}`; | ||||
|     } | ||||
| 
 | ||||
|     if (this.inputs.tagCustom.length > 0) { | ||||
|       if (this.inputs.tagCustomOnly) { | ||||
|         version = { | ||||
|           main: this.inputs.tagCustom.shift(), | ||||
|           partial: this.inputs.tagCustom, | ||||
|           latest: false | ||||
|         }; | ||||
|       } else { | ||||
|         version.partial.push(...this.inputs.tagCustom); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     version.partial = version.partial.filter((item, index) => version.partial.indexOf(item) === index); | ||||
|     if (version.latest == undefined) { | ||||
|       version.latest = false; | ||||
|     } | ||||
| 
 | ||||
|     return version; | ||||
|   } | ||||
| 
 | ||||
|   public tags(): Array<string> { | ||||
|   private procSchedule(version: Version, tag: tcl.Tag): Version { | ||||
|     if (!/schedule/.test(this.context.eventName)) { | ||||
|       return version; | ||||
|     } | ||||
| 
 | ||||
|     const currentDate = this.date; | ||||
|     const vraw = handlebars.compile(tag.attrs['pattern'])({ | ||||
|       date: function (format) { | ||||
|         return moment(currentDate).utc().format(format); | ||||
|       } | ||||
|     }); | ||||
| 
 | ||||
|     if (version.main == undefined) { | ||||
|       version.main = vraw; | ||||
|     } else if (vraw !== version.main) { | ||||
|       version.partial.push(vraw); | ||||
|     } | ||||
|     if (version.latest == undefined) { | ||||
|       version.latest = this.flavor.latest == 'auto' ? false : this.flavor.latest == 'true'; | ||||
|     } | ||||
| 
 | ||||
|     return version; | ||||
|   } | ||||
| 
 | ||||
|   private procSemver(version: Version, tag: tcl.Tag): Version { | ||||
|     if (!/^refs\/tags\//.test(this.context.ref) && tag.attrs['value'].length == 0) { | ||||
|       return version; | ||||
|     } | ||||
| 
 | ||||
|     let vraw: string; | ||||
|     if (tag.attrs['value'].length > 0) { | ||||
|       vraw = tag.attrs['value']; | ||||
|     } else { | ||||
|       vraw = this.context.ref.replace(/^refs\/tags\//g, '').replace(/\//g, '-'); | ||||
|     } | ||||
|     if (!semver.valid(vraw)) { | ||||
|       core.warning(`${vraw} is not a valid semver. More info: https://semver.org/`); | ||||
|       return version; | ||||
|     } | ||||
| 
 | ||||
|     let latest: boolean = false; | ||||
|     const sver = semver.parse(vraw, { | ||||
|       includePrerelease: true | ||||
|     }); | ||||
|     if (semver.prerelease(vraw)) { | ||||
|       vraw = handlebars.compile('{{version}}')(sver); | ||||
|       if (version.main == undefined) { | ||||
|         version.main = vraw; | ||||
|       } else if (vraw !== version.main) { | ||||
|         version.partial.push(vraw); | ||||
|       } | ||||
|     } else { | ||||
|       vraw = handlebars.compile(tag.attrs['pattern'])(sver); | ||||
|       if (version.main == undefined) { | ||||
|         version.main = vraw; | ||||
|       } else if (vraw !== version.main) { | ||||
|         version.partial.push(vraw); | ||||
|       } | ||||
|       latest = true; | ||||
|     } | ||||
|     if (version.latest == undefined) { | ||||
|       version.latest = this.flavor.latest == 'auto' ? latest : this.flavor.latest == 'true'; | ||||
|     } | ||||
| 
 | ||||
|     return version; | ||||
|   } | ||||
| 
 | ||||
|   private procMatch(version: Version, tag: tcl.Tag): Version { | ||||
|     if (!/^refs\/tags\//.test(this.context.ref) && tag.attrs['value'].length == 0) { | ||||
|       return version; | ||||
|     } | ||||
| 
 | ||||
|     let vraw: string; | ||||
|     if (tag.attrs['value'].length > 0) { | ||||
|       vraw = tag.attrs['value']; | ||||
|     } else { | ||||
|       vraw = this.context.ref.replace(/^refs\/tags\//g, '').replace(/\//g, '-'); | ||||
|     } | ||||
| 
 | ||||
|     let latest: boolean = false; | ||||
|     let tmatch; | ||||
|     const isRegEx = tag.attrs['pattern'].match(/^\/(.+)\/(.*)$/); | ||||
|     if (isRegEx) { | ||||
|       tmatch = vraw.match(new RegExp(isRegEx[1], isRegEx[2])); | ||||
|     } else { | ||||
|       tmatch = vraw.match(tag.attrs['pattern']); | ||||
|     } | ||||
|     if (tmatch) { | ||||
|       vraw = tmatch[tag.attrs['group']]; | ||||
|       latest = true; | ||||
|     } | ||||
| 
 | ||||
|     if (version.main == undefined) { | ||||
|       version.main = vraw; | ||||
|     } else if (vraw !== version.main) { | ||||
|       version.partial.push(vraw); | ||||
|     } | ||||
|     if (version.latest == undefined) { | ||||
|       version.latest = this.flavor.latest == 'auto' ? latest : this.flavor.latest == 'true'; | ||||
|     } | ||||
| 
 | ||||
|     return version; | ||||
|   } | ||||
| 
 | ||||
|   private procRefBranch(version: Version, tag: tcl.Tag): Version { | ||||
|     if (!/^refs\/heads\//.test(this.context.ref)) { | ||||
|       return version; | ||||
|     } | ||||
| 
 | ||||
|     const vraw = this.setFlavor(this.context.ref.replace(/^refs\/heads\//g, '').replace(/[^a-zA-Z0-9._-]+/g, '-'), tag); | ||||
|     if (version.main == undefined) { | ||||
|       version.main = vraw; | ||||
|     } else if (vraw !== version.main) { | ||||
|       version.partial.push(vraw); | ||||
|     } | ||||
|     if (version.latest == undefined) { | ||||
|       version.latest = this.flavor.latest == 'auto' ? false : this.flavor.latest == 'true'; | ||||
|     } | ||||
| 
 | ||||
|     return version; | ||||
|   } | ||||
| 
 | ||||
|   private procRefTag(version: Version, tag: tcl.Tag): Version { | ||||
|     if (!/^refs\/tags\//.test(this.context.ref)) { | ||||
|       return version; | ||||
|     } | ||||
| 
 | ||||
|     const vraw = this.setFlavor(this.context.ref.replace(/^refs\/tags\//g, '').replace(/\//g, '-'), tag); | ||||
|     if (version.main == undefined) { | ||||
|       version.main = vraw; | ||||
|     } else if (vraw !== version.main) { | ||||
|       version.partial.push(vraw); | ||||
|     } | ||||
|     if (version.latest == undefined) { | ||||
|       version.latest = this.flavor.latest == 'auto' ? true : this.flavor.latest == 'true'; | ||||
|     } | ||||
| 
 | ||||
|     return version; | ||||
|   } | ||||
| 
 | ||||
|   private procRefPr(version: Version, tag: tcl.Tag): Version { | ||||
|     if (!/^refs\/pull\//.test(this.context.ref)) { | ||||
|       return version; | ||||
|     } | ||||
| 
 | ||||
|     const vraw = this.setFlavor(this.context.ref.replace(/^refs\/pull\//g, '').replace(/\/merge$/g, ''), tag); | ||||
|     if (version.main == undefined) { | ||||
|       version.main = vraw; | ||||
|     } else if (vraw !== version.main) { | ||||
|       version.partial.push(vraw); | ||||
|     } | ||||
|     if (version.latest == undefined) { | ||||
|       version.latest = this.flavor.latest == 'auto' ? false : this.flavor.latest == 'true'; | ||||
|     } | ||||
| 
 | ||||
|     return version; | ||||
|   } | ||||
| 
 | ||||
|   private procEdge(version: Version, tag: tcl.Tag): Version { | ||||
|     if (!/^refs\/heads\//.test(this.context.ref)) { | ||||
|       return version; | ||||
|     } | ||||
| 
 | ||||
|     let val = this.context.ref.replace(/^refs\/heads\//g, '').replace(/[^a-zA-Z0-9._-]+/g, '-'); | ||||
|     if (tag.attrs['branch'].length == 0) { | ||||
|       tag.attrs['branch'] = this.repo.default_branch; | ||||
|     } | ||||
|     if (tag.attrs['branch'] === val) { | ||||
|       val = 'edge'; | ||||
|     } | ||||
| 
 | ||||
|     const vraw = this.setFlavor(val, tag); | ||||
|     if (version.main == undefined) { | ||||
|       version.main = vraw; | ||||
|     } else if (vraw !== version.main) { | ||||
|       version.partial.push(vraw); | ||||
|     } | ||||
|     if (version.latest == undefined) { | ||||
|       version.latest = this.flavor.latest == 'auto' ? false : this.flavor.latest == 'true'; | ||||
|     } | ||||
| 
 | ||||
|     return version; | ||||
|   } | ||||
| 
 | ||||
|   private procRaw(version: Version, tag: tcl.Tag): Version { | ||||
|     const vraw = this.setFlavor(tag.attrs['value'], tag); | ||||
|     if (version.main == undefined) { | ||||
|       version.main = vraw; | ||||
|     } else if (vraw !== version.main) { | ||||
|       version.partial.push(vraw); | ||||
|     } | ||||
|     if (version.latest == undefined) { | ||||
|       version.latest = this.flavor.latest == 'auto' ? false : this.flavor.latest == 'true'; | ||||
|     } | ||||
| 
 | ||||
|     return version; | ||||
|   } | ||||
| 
 | ||||
|   private procSha(version: Version, tag: tcl.Tag): Version { | ||||
|     if (!this.context.sha) { | ||||
|       return version; | ||||
|     } | ||||
| 
 | ||||
|     const vraw = this.setFlavor(this.context.sha.substr(0, 7), tag); | ||||
|     if (version.main == undefined) { | ||||
|       version.main = vraw; | ||||
|     } else if (vraw !== version.main) { | ||||
|       version.partial.push(vraw); | ||||
|     } | ||||
|     if (version.latest == undefined) { | ||||
|       version.latest = this.flavor.latest == 'auto' ? false : this.flavor.latest == 'true'; | ||||
|     } | ||||
| 
 | ||||
|     return version; | ||||
|   } | ||||
| 
 | ||||
|   private setFlavor(val: string, tag: tcl.Tag): string { | ||||
|     if (tag.attrs['prefix'].length > 0) { | ||||
|       val = `${tag.attrs['prefix']}${val}`; | ||||
|     } else if (this.flavor.prefix.length > 0) { | ||||
|       val = `${this.flavor.prefix}${val}`; | ||||
|     } | ||||
|     if (tag.attrs['suffix'].length > 0) { | ||||
|       val = `${val}${tag.attrs['suffix']}`; | ||||
|     } else if (this.flavor.suffix.length > 0) { | ||||
|       val = `${val}${this.flavor.suffix}`; | ||||
|     } | ||||
|     return val; | ||||
|   } | ||||
| 
 | ||||
|   public getTags(): Array<string> { | ||||
|     if (!this.version.main) { | ||||
|       return []; | ||||
|     } | ||||
| @ -124,14 +335,11 @@ export class Meta { | ||||
|       if (this.version.latest) { | ||||
|         tags.push(`${imageLc}:latest`); | ||||
|       } | ||||
|       if (this.context.sha && this.inputs.tagSha) { | ||||
|         tags.push(`${imageLc}:sha-${this.context.sha.substr(0, 7)}`); | ||||
|       } | ||||
|     } | ||||
|     return tags; | ||||
|   } | ||||
| 
 | ||||
|   public labels(): Array<string> { | ||||
|   public getLabels(): Array<string> { | ||||
|     let labels: Array<string> = [ | ||||
|       `org.opencontainers.image.title=${this.repo.name || ''}`, | ||||
|       `org.opencontainers.image.description=${this.repo.description || ''}`, | ||||
| @ -142,13 +350,13 @@ export class Meta { | ||||
|       `org.opencontainers.image.revision=${this.context.sha || ''}`, | ||||
|       `org.opencontainers.image.licenses=${this.repo.license?.spdx_id || ''}` | ||||
|     ]; | ||||
|     labels.push(...this.inputs.labelCustom); | ||||
|     labels.push(...this.inputs.labels); | ||||
|     return labels; | ||||
|   } | ||||
| 
 | ||||
|   public bakeFile(): string { | ||||
|   public getBakeFile(): string { | ||||
|     let jsonLabels = {}; | ||||
|     for (let label of this.labels()) { | ||||
|     for (let label of this.getLabels()) { | ||||
|       const matches = label.match(/([^=]*)=(.*)/); | ||||
|       if (!matches) { | ||||
|         continue; | ||||
| @ -163,7 +371,7 @@ export class Meta { | ||||
|         { | ||||
|           target: { | ||||
|             'ghaction-docker-meta': { | ||||
|               tags: this.tags(), | ||||
|               tags: this.getTags(), | ||||
|               labels: jsonLabels, | ||||
|               args: { | ||||
|                 DOCKER_META_IMAGES: this.inputs.images.join(','), | ||||
|  | ||||
							
								
								
									
										180
									
								
								src/tag.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								src/tag.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,180 @@ | ||||
| import csvparse from 'csv-parse/lib/sync'; | ||||
| 
 | ||||
| export enum Type { | ||||
|   Schedule = 'schedule', | ||||
|   Semver = 'semver', | ||||
|   Match = 'match', | ||||
|   Edge = 'edge', | ||||
|   Ref = 'ref', | ||||
|   Raw = 'raw', | ||||
|   Sha = 'sha' | ||||
| } | ||||
| 
 | ||||
| export enum RefEvent { | ||||
|   Branch = 'branch', | ||||
|   Tag = 'tag', | ||||
|   PR = 'pr' | ||||
| } | ||||
| 
 | ||||
| export interface Tag { | ||||
|   type: Type; | ||||
|   attrs: Record<string, string>; | ||||
| } | ||||
| 
 | ||||
| export const DefaultPriorities: Record<Type, string> = { | ||||
|   [Type.Schedule]: '1000', | ||||
|   [Type.Semver]: '900', | ||||
|   [Type.Match]: '800', | ||||
|   [Type.Edge]: '700', | ||||
|   [Type.Ref]: '600', | ||||
|   [Type.Raw]: '200', | ||||
|   [Type.Sha]: '100' | ||||
| }; | ||||
| 
 | ||||
| export function Transform(inputs: string[]): Tag[] { | ||||
|   const tags: Tag[] = []; | ||||
|   if (inputs.length == 0) { | ||||
|     // prettier-ignore
 | ||||
|     inputs = [ | ||||
|       `type=schedule`, | ||||
|       `type=ref,event=${RefEvent.Branch}`, | ||||
|       `type=ref,event=${RefEvent.Tag}`, | ||||
|       `type=ref,event=${RefEvent.PR}` | ||||
|     ]; | ||||
|   } | ||||
|   for (const input of inputs) { | ||||
|     tags.push(Parse(input)); | ||||
|   } | ||||
|   return tags.sort((tag1, tag2) => { | ||||
|     if (Number(tag1.attrs['priority']) < Number(tag2.attrs['priority'])) { | ||||
|       return 1; | ||||
|     } | ||||
|     if (Number(tag1.attrs['priority']) > Number(tag2.attrs['priority'])) { | ||||
|       return -1; | ||||
|     } | ||||
|     return 0; | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| export function Parse(s: string): Tag { | ||||
|   const fields = csvparse(s, { | ||||
|     relaxColumnCount: true, | ||||
|     skipLinesWithEmptyValues: true | ||||
|   })[0]; | ||||
| 
 | ||||
|   const tag = { | ||||
|     attrs: {} | ||||
|   } as Tag; | ||||
| 
 | ||||
|   for (const field of fields) { | ||||
|     const parts = field.toString().split('=', 2); | ||||
|     if (parts.length == 1) { | ||||
|       tag.attrs['value'] = parts[0].trim(); | ||||
|     } else { | ||||
|       const key = parts[0].trim().toLowerCase(); | ||||
|       const value = parts[1].trim(); | ||||
|       switch (key) { | ||||
|         case 'type': { | ||||
|           if (!Object.values(Type).includes(value)) { | ||||
|             throw new Error(`Unknown type attribute: ${value}`); | ||||
|           } | ||||
|           tag.type = value; | ||||
|           break; | ||||
|         } | ||||
|         default: { | ||||
|           tag.attrs[key] = value; | ||||
|           break; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   if (tag.type == undefined) { | ||||
|     tag.type = Type.Raw; | ||||
|   } | ||||
| 
 | ||||
|   switch (tag.type) { | ||||
|     case Type.Schedule: { | ||||
|       if (!tag.attrs.hasOwnProperty('pattern')) { | ||||
|         tag.attrs['pattern'] = 'nightly'; | ||||
|       } | ||||
|       break; | ||||
|     } | ||||
|     case Type.Semver: { | ||||
|       if (!tag.attrs.hasOwnProperty('pattern')) { | ||||
|         throw new Error(`Missing pattern attribute for ${s}`); | ||||
|       } | ||||
|       if (!tag.attrs.hasOwnProperty('value')) { | ||||
|         tag.attrs['value'] = ''; | ||||
|       } | ||||
|       break; | ||||
|     } | ||||
|     case Type.Match: { | ||||
|       if (!tag.attrs.hasOwnProperty('pattern')) { | ||||
|         throw new Error(`Missing pattern attribute for ${s}`); | ||||
|       } | ||||
|       if (!tag.attrs.hasOwnProperty('group')) { | ||||
|         tag.attrs['group'] = '0'; | ||||
|       } | ||||
|       if (isNaN(+tag.attrs['group'])) { | ||||
|         throw new Error(`Invalid match group for ${s}`); | ||||
|       } | ||||
|       if (!tag.attrs.hasOwnProperty('value')) { | ||||
|         tag.attrs['value'] = ''; | ||||
|       } | ||||
|       break; | ||||
|     } | ||||
|     case Type.Edge: { | ||||
|       if (!tag.attrs.hasOwnProperty('branch')) { | ||||
|         tag.attrs['branch'] = ''; | ||||
|       } | ||||
|       break; | ||||
|     } | ||||
|     case Type.Ref: { | ||||
|       if (!tag.attrs.hasOwnProperty('event')) { | ||||
|         throw new Error(`Missing event attribute for ${s}`); | ||||
|       } | ||||
|       if ( | ||||
|         !Object.keys(RefEvent) | ||||
|           .map(k => RefEvent[k]) | ||||
|           .includes(tag.attrs['event']) | ||||
|       ) { | ||||
|         throw new Error(`Invalid event for ${s}`); | ||||
|       } | ||||
|       if (tag.attrs['event'] == RefEvent.PR && !tag.attrs.hasOwnProperty('prefix')) { | ||||
|         tag.attrs['prefix'] = 'pr-'; | ||||
|       } | ||||
|       break; | ||||
|     } | ||||
|     case Type.Raw: { | ||||
|       if (!tag.attrs.hasOwnProperty('value')) { | ||||
|         throw new Error(`Missing value attribute for ${s}`); | ||||
|       } | ||||
|       break; | ||||
|     } | ||||
|     case Type.Sha: { | ||||
|       if (!tag.attrs.hasOwnProperty('prefix')) { | ||||
|         tag.attrs['prefix'] = 'sha-'; | ||||
|       } | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   if (!tag.attrs.hasOwnProperty('enable')) { | ||||
|     tag.attrs['enable'] = 'true'; | ||||
|   } | ||||
|   if (!tag.attrs.hasOwnProperty('priority')) { | ||||
|     tag.attrs['priority'] = DefaultPriorities[tag.type]; | ||||
|   } | ||||
|   if (!tag.attrs.hasOwnProperty('prefix')) { | ||||
|     tag.attrs['prefix'] = ''; | ||||
|   } | ||||
|   if (!tag.attrs.hasOwnProperty('suffix')) { | ||||
|     tag.attrs['suffix'] = ''; | ||||
|   } | ||||
|   if (!['true', 'false'].includes(tag.attrs['enable'])) { | ||||
|     throw new Error(`Invalid value for enable attribute: ${tag.attrs['enable']}`); | ||||
|   } | ||||
| 
 | ||||
|   return tag; | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 CrazyMax
						CrazyMax