From e8ce48988f659b4c7b1be73981c33ae83a401067 Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Tue, 17 Nov 2020 23:31:03 +0100 Subject: [PATCH] Handle semver tags (#14) Co-authored-by: CrazyMax --- README.md | 177 +++- __tests__/meta.test.ts | 222 +++- dist/index.js | 2198 +++++++++++++++++++++++++++++++++++++++- package.json | 3 +- src/context.ts | 2 + src/main.ts | 4 +- src/meta.ts | 51 +- 7 files changed, 2573 insertions(+), 84 deletions(-) diff --git a/README.md b/README.md index ebfc324..d95e650 100644 --- a/README.md +++ b/README.md @@ -17,14 +17,17 @@ If you are interested, [check out](https://git.io/Je09Y) my other :octocat: GitH ___ * [Features](#features) -* [Overview](#overview) * [Usage](#usage) + * [Basic](#basic) + * [Semver](#semver) + * [Complete](#complete) * [Customizing](#customizing) * [inputs](#inputs) * [outputs](#outputs) * [Notes](#notes) * [Latest tag](#latest-tag) * [`tag-match` examples](#tag-match-examples) + * [Handle semver tag](#handle-semver-tag) * [Schedule tag](#schedule-tag) * [Overwrite labels](#overwrite-labels) * [Keep up-to-date with GitHub Dependabot](#keep-up-to-date-with-github-dependabot) @@ -37,29 +40,27 @@ ___ * [OCI Image Format Specification](https://github.com/opencontainers/image-spec/blob/master/annotations.md) used to generate Docker labels * [Handlebars template](https://handlebarsjs.com/guide/) to apply to schedule tag -## Overview +## Usage + +### Basic | Event | Ref | Commit SHA | Docker Tags | |-----------------|-------------------------------|------------|-------------------------------------| -| `schedule` | `refs/heads/master` | `45f132a` | `sha-45f132a`, `nightly` | -| `pull_request` | `refs/pull/2/merge` | `a123b57` | `sha-a123b57`, `pr-2` | -| `push` | `refs/heads/master` | `cf20257` | `sha-cf20257`, `master` | -| `push` | `refs/heads/my/branch` | `a5df687` | `sha-a5df687`, `my-branch` | -| `push tag` | `refs/tags/v1.2.3` | `bf4565b` | `sha-bf4565b`, `v1.2.3`, `latest` | - -## Usage +| `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: - schedule: - - cron: '0 10 * * *' # everyday at 10am push: branches: - '**' tags: - - 'v*.*.*' + - 'v*' pull_request: jobs: @@ -100,6 +101,133 @@ jobs: labels: ${{ steps.docker_meta.outputs.labels }} ``` +### 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: + - '**' + tags: + - 'v*' + pull_request: + +jobs: + docker: + runs-on: ubuntu-latest + steps: + - + name: Checkout + uses: actions/checkout@v2 + - + name: Docker meta + id: docker_meta + uses: crazy-max/ghaction-docker-meta@v1 + 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 + - + 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: . + 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 }} +``` + +### Complete + +| Event | Ref | Commit SHA | Docker Tags | +|-----------------|-------------------------------|------------|-----------------------------------------| +| `schedule` | `refs/heads/master` | `45f132a` | `sha-45f132a`, `nightly` | +| `pull_request` | `refs/pull/2/merge` | `a123b57` | `sha-45f132a`, `pr-2` | +| `push` | `refs/heads/master` | `cf20257` | `sha-45f132a`, `master` | +| `push` | `refs/heads/my/branch` | `a5df687` | `sha-45f132a`, `my-branch` | +| `push tag` | `refs/tags/v1.2.3` | `ad132f5` | `sha-45f132a`, `1.2.3`, `1.2`, `latest` | +| `push tag` | `refs/tags/v2.0.8-beta.67` | `fc89efd` | `sha-45f132a`, `2.0.8-beta.67` | + +```yaml +name: ci + +on: + schedule: + - cron: '0 10 * * *' # everyday at 10am + push: + branches: + - '**' + tags: + - 'v*' + pull_request: + +jobs: + docker: + runs-on: ubuntu-latest + steps: + - + name: Checkout + uses: actions/checkout@v2 + - + name: Docker meta + id: docker_meta + uses: crazy-max/ghaction-docker-meta@v1 + 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 + - + 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: . + 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 }} +``` + ## Customizing ### inputs @@ -112,6 +240,7 @@ Following inputs can be used as `step.with` keys | `tag-sha` | Bool | Add git short SHA 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 | 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-match-latest` | Bool | Set `latest` Docker tag if `tag-match` matches or on Git tag event (default `true`) | @@ -135,9 +264,10 @@ Following outputs are available ### Latest tag -Latest Docker tag will be generated by default on `push tag` event. So if for example you push the `v1.2.3` Git 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 the Git tag matches a regular expression with the [`tag-match` input](#tag-match-examples). +generated only if the Git tag matches a regular expression with the [`tag-match` input](#tag-match-examples) or if +`tag-semver` is valid [semver](https://semver.org/). ### `tag-match` examples @@ -149,6 +279,25 @@ generated only if the Git tag matches a regular expression with the [`tag-match` | `release1` | `\d{1,3}.\d{1,3}` | `0` | :x: | `release1` | | `20200110-RC2` | `\d+` | `0` | :white_check_mark: | `20200110`, `latest` | +### 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 | Docker tags | +|--------------------|----------------------------------------------------------|--------------------|--------------------| +| `v1.2.3` | `{{raw}}` | :white_check_mark: | `v1.2.3`, `latest` | +| `v1.2.3` | `{{version}}` | :white_check_mark: | `1.2.3`, `latest` | +| `v1.2.3` | `{{major}}.{{minor}}` | :white_check_mark: | `1.2`, `latest` | +| `v1.2.3` | `v{{major}}` | :white_check_mark: | `v1`, `latest` | +| `v1.2.3` | `{{minor}}` | :white_check_mark: | `2`, `latest` | +| `v1.2.3` | `{{patch}}` | :white_check_mark: | `3`, `latest` | +| `v1.2.3` | `{{major}}.{{minor}}`
`{{major}}.{{minor}}.{{patch}}` | :white_check_mark: | `1.2`, `1.2.3`, `latest` | +| `v2.0.8-beta.67` | `{{raw}}` | :white_check_mark: | `v2.0.8-beta.67` | +| `v2.0.8-beta.67` | `{{version}}` | :white_check_mark: | `2.0.8-beta.67` | +| `v2.0.8-beta.67` | `{{major}}.{{minor}}` | :white_check_mark: | `2.0` | +| `release1` | `{{raw}}` | :x: | `release1` | + ### Schedule tag `tag-schedule` is specially crafted input to support [Handlebars template](https://handlebarsjs.com/guide/) with diff --git a/__tests__/meta.test.ts b/__tests__/meta.test.ts index d5b334a..4c1dab2 100644 --- a/__tests__/meta.test.ts +++ b/__tests__/meta.test.ts @@ -66,7 +66,8 @@ describe('null', () => { images: ['user/app'], } as Inputs, { - version: undefined, + main: undefined, + partial: [], latest: false } as Version, [], @@ -87,7 +88,8 @@ describe('null', () => { images: ['user/app'], } as Inputs, { - version: undefined, + main: undefined, + partial: [], latest: false } as Version, [], @@ -114,7 +116,8 @@ describe('push', () => { images: ['user/app'], } as Inputs, { - version: 'dev', + main: 'dev', + partial: [], latest: false } as Version, [ @@ -138,7 +141,8 @@ describe('push', () => { tagEdge: true, } as Inputs, { - version: 'edge', + main: 'edge', + partial: [], latest: false } as Version, [ @@ -161,7 +165,8 @@ describe('push', () => { images: ['user/app'], } as Inputs, { - version: 'master', + main: 'master', + partial: [], latest: false } as Version, [ @@ -185,7 +190,8 @@ describe('push', () => { tagEdge: true, } as Inputs, { - version: 'edge', + main: 'edge', + partial: [], latest: false } as Version, [ @@ -208,7 +214,8 @@ describe('push', () => { images: ['org/app', 'ghcr.io/user/app'], } as Inputs, { - version: 'dev', + main: 'dev', + partial: [], latest: false } as Version, [ @@ -233,7 +240,8 @@ describe('push', () => { tagEdge: true, } as Inputs, { - version: 'edge', + main: 'edge', + partial: [], latest: false } as Version, [ @@ -258,7 +266,8 @@ describe('push', () => { tagSha: true, } as Inputs, { - version: 'dev', + main: 'dev', + partial: [], latest: false } as Version, [ @@ -286,7 +295,8 @@ describe('push', () => { tagEdge: true, } as Inputs, { - version: 'edge', + main: 'edge', + partial: [], latest: false } as Version, [ @@ -315,7 +325,8 @@ describe('push', () => { tagEdgeBranch: 'dev' } as Inputs, { - version: 'edge', + main: 'edge', + partial: [], latest: false } as Version, [ @@ -344,7 +355,8 @@ describe('push', () => { tagEdgeBranch: 'dev' } as Inputs, { - version: 'master', + main: 'master', + partial: [], latest: false } as Version, [ @@ -376,7 +388,8 @@ describe('push tag', () => { images: ['user/app'], } as Inputs, { - version: 'release1', + main: 'release1', + partial: [], latest: true } as Version, [ @@ -400,7 +413,8 @@ describe('push tag', () => { images: ['user/app'], } as Inputs, { - version: '20200110-RC2', + main: '20200110-RC2', + partial: [], latest: true } as Version, [ @@ -426,7 +440,8 @@ describe('push tag', () => { tagMatchLatest: false, } as Inputs, { - version: '20200110', + main: '20200110', + partial: [], latest: false } as Version, [ @@ -452,7 +467,8 @@ describe('push tag', () => { tagMatchLatest: false, } as Inputs, { - version: '20200110', + main: '20200110', + partial: [], latest: false } as Version, [ @@ -476,7 +492,37 @@ describe('push tag', () => { tagMatch: `\\d{1,3}.\\d{1,3}.\\d{1,3}`, } as Inputs, { - version: '1.1.1', + main: '1.1.1', + partial: [], + latest: true + } as Version, + [ + 'org/app:1.1.1', + 'org/app:latest', + 'ghcr.io/user/app:1.1.1', + 'ghcr.io/user/app:latest' + ], + [ + "org.opencontainers.image.title=Hello-World", + "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.created=2020-01-10T00:30:00.000Z", + "org.opencontainers.image.revision=90dd6032fac8bda1b6c4436a2e65de27961ed071", + "org.opencontainers.image.licenses=MIT" + ] + ], + [ + 'event_tag_v1.1.1.env', + { + images: ['org/app', 'ghcr.io/user/app'], + tagMatch: `^v(\\d{1,3}.\\d{1,3}.\\d{1,3})$`, + tagMatchGroup: 1, + } as Inputs, + { + main: '1.1.1', + partial: [], latest: true } as Version, [ @@ -503,7 +549,8 @@ describe('push tag', () => { tagMatch: `\\d{1,3}.\\d{1,3}.\\d{1,3}-(alpha|beta).\\d{1,3}`, } as Inputs, { - version: '2.0.8-beta.67', + main: '2.0.8-beta.67', + partial: [], latest: true } as Version, [ @@ -530,7 +577,8 @@ describe('push tag', () => { tagMatch: `\\d{1,3}.\\d{1,3}`, } as Inputs, { - version: '2.0', + main: '2.0', + partial: [], latest: true } as Version, [ @@ -550,6 +598,33 @@ describe('push tag', () => { "org.opencontainers.image.licenses=MIT" ] ], + [ + 'event_tag_v2.0.8-beta.67.env', + { + images: ['org/app', 'ghcr.io/user/app'], + tagMatch: `^v(\\d{1,3}.\\d{1,3}.\\d{1,3})$`, + tagMatchGroup: 1, + } as Inputs, + { + main: 'v2.0.8-beta.67', + partial: [], + latest: false + } as Version, + [ + 'org/app:v2.0.8-beta.67', + 'ghcr.io/user/app:v2.0.8-beta.67' + ], + [ + "org.opencontainers.image.title=Hello-World", + "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=v2.0.8-beta.67", + "org.opencontainers.image.created=2020-01-10T00:30:00.000Z", + "org.opencontainers.image.revision=90dd6032fac8bda1b6c4436a2e65de27961ed071", + "org.opencontainers.image.licenses=MIT" + ] + ], [ 'event_tag_sometag.env', { @@ -557,7 +632,8 @@ describe('push tag', () => { tagMatch: `\\d{1,3}.\\d{1,3}`, } as Inputs, { - version: 'sometag', + main: 'sometag', + partial: [], latest: false } as Version, [ @@ -575,6 +651,64 @@ describe('push tag', () => { "org.opencontainers.image.licenses=MIT" ] ], + [ + 'event_tag_v1.1.1.env', + { + images: ['org/app', 'ghcr.io/user/app'], + tagSemver: ['{{version}}', '{{major}}.{{minor}}', '{{major}}'], + } as Inputs, + { + main: '1.1.1', + partial: ['1.1', '1'], + latest: true + } as Version, + [ + 'org/app:1.1.1', + 'org/app:1.1', + 'org/app:1', + 'org/app:latest', + 'ghcr.io/user/app:1.1.1', + 'ghcr.io/user/app:1.1', + 'ghcr.io/user/app:1', + 'ghcr.io/user/app:latest' + ], + [ + "org.opencontainers.image.title=Hello-World", + "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.created=2020-01-10T00:30:00.000Z", + "org.opencontainers.image.revision=90dd6032fac8bda1b6c4436a2e65de27961ed071", + "org.opencontainers.image.licenses=MIT" + ] + ], + [ + 'event_tag_v2.0.8-beta.67.env', + { + images: ['org/app', 'ghcr.io/user/app'], + tagSemver: ['{{version}}', '{{major}}.{{minor}}', '{{major}}'], + } as Inputs, + { + main: '2.0.8-beta.67', + partial: [], + latest: false + } as Version, + [ + 'org/app:2.0.8-beta.67', + 'ghcr.io/user/app:2.0.8-beta.67' + ], + [ + "org.opencontainers.image.title=Hello-World", + "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=2.0.8-beta.67", + "org.opencontainers.image.created=2020-01-10T00:30:00.000Z", + "org.opencontainers.image.revision=90dd6032fac8bda1b6c4436a2e65de27961ed071", + "org.opencontainers.image.licenses=MIT" + ] + ], ])('given %p event ', tagsLabelsTest); }); @@ -588,7 +722,8 @@ describe('latest', () => { tagMatch: `^release\\d{1,2}`, } as Inputs, { - version: 'release1', + main: 'release1', + partial: [], latest: true, } as Version, [ @@ -613,7 +748,8 @@ describe('latest', () => { tagMatch: `^\\d+-RC\\d{1,2}`, } as Inputs, { - version: '20200110-RC2', + main: '20200110-RC2', + partial: [], latest: true } as Version, [ @@ -638,7 +774,8 @@ describe('latest', () => { tagMatch: `\\d{8}`, } as Inputs, { - version: '20200110', + main: '20200110', + partial: [], latest: true } as Version, [ @@ -663,7 +800,8 @@ describe('latest', () => { tagMatch: `\\d{1,3}.\\d{1,3}.\\d{1,3}`, } as Inputs, { - version: '1.1.1', + main: '1.1.1', + partial: [], latest: true } as Version, [ @@ -687,7 +825,8 @@ describe('latest', () => { images: ['org/app', 'ghcr.io/user/app'], } as Inputs, { - version: 'v1.1.1', + main: 'v1.1.1', + partial: [], latest: true } as Version, [ @@ -714,7 +853,8 @@ describe('latest', () => { tagMatch: `\\d{1,3}.\\d{1,3}.\\d{1,3}`, } as Inputs, { - version: '2.0.8', + main: '2.0.8', + partial: [], latest: true } as Version, [ @@ -741,7 +881,8 @@ describe('latest', () => { tagMatchLatest: false, } as Inputs, { - version: 'v1.1.1', + main: 'v1.1.1', + partial: [], latest: false } as Version, [ @@ -771,7 +912,8 @@ describe('pull_request', () => { images: ['user/app'], } as Inputs, { - version: 'pr-2', + main: 'pr-2', + partial: [], latest: false } as Version, [ @@ -794,7 +936,8 @@ describe('pull_request', () => { images: ['org/app', 'ghcr.io/user/app'], } as Inputs, { - version: 'pr-2', + main: 'pr-2', + partial: [], latest: false } as Version, [ @@ -819,7 +962,8 @@ describe('pull_request', () => { tagSha: true, } as Inputs, { - version: 'pr-2', + main: 'pr-2', + partial: [], latest: false } as Version, [ @@ -851,7 +995,8 @@ describe('schedule', () => { images: ['user/app'], } as Inputs, { - version: 'nightly', + main: 'nightly', + partial: [], latest: false } as Version, [ @@ -875,7 +1020,8 @@ describe('schedule', () => { tagSchedule: `{{date 'YYYYMMDD'}}` } as Inputs, { - version: '20200110', + main: '20200110', + partial: [], latest: false } as Version, [ @@ -899,7 +1045,8 @@ describe('schedule', () => { tagSchedule: `{{date 'YYYYMMDD-HHmmss'}}` } as Inputs, { - version: '20200110-003000', + main: '20200110-003000', + partial: [], latest: false } as Version, [ @@ -922,7 +1069,8 @@ describe('schedule', () => { images: ['org/app', 'ghcr.io/user/app'], } as Inputs, { - version: 'nightly', + main: 'nightly', + partial: [], latest: false } as Version, [ @@ -947,7 +1095,8 @@ describe('schedule', () => { tagSha: true, } as Inputs, { - version: 'nightly', + main: 'nightly', + partial: [], latest: false } as Version, [ @@ -979,7 +1128,8 @@ describe('release', () => { images: ['user/app'], } as Inputs, { - version: 'v1.1.1', + main: 'v1.1.1', + partial: [], latest: true } as Version, [ diff --git a/dist/index.js b/dist/index.js index d47f00b..b2d3f3d 100644 --- a/dist/index.js +++ b/dist/index.js @@ -25,6 +25,7 @@ function getInputs() { 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, tagMatchLatest: /true/i.test(core.getInput('tag-match-latest') || 'true'), @@ -132,9 +133,9 @@ function run() { const meta = new meta_1.Meta(inputs, context, repo); const version = meta.version(); core.startGroup(`Docker image version`); - core.info(version.version || ''); + core.info(version.main || ''); core.endGroup(); - core.setOutput('version', version.version || ''); + core.setOutput('version', version.main || ''); const tags = meta.tags(); core.startGroup(`Docker tags`); for (let tag of tags) { @@ -169,6 +170,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.Meta = void 0; const handlebars = __webpack_require__(7492); const moment = __webpack_require__(9623); +const semver = __webpack_require__(1383); class Meta { constructor(inputs, context, repo) { this.inputs = inputs; @@ -182,29 +184,46 @@ class Meta { version() { const currentDate = this.date; const version = { - version: undefined, + main: undefined, + partial: [], latest: false }; if (/schedule/.test(this.context.eventName)) { - version.version = handlebars.compile(this.inputs.tagSchedule)({ + version.main = handlebars.compile(this.inputs.tagSchedule)({ date: function (format) { return moment(currentDate).utc().format(format); } }); } else if (/^refs\/tags\//.test(this.context.ref)) { - version.version = this.context.ref.replace(/^refs\/tags\//g, '').replace(/\//g, '-'); - if (this.inputs.tagMatch) { + version.main = this.context.ref.replace(/^refs\/tags\//g, '').replace(/\//g, '-'); + if (this.inputs.tagSemver.length > 0 && semver.valid(version.main)) { + const sver = semver.parse(version.main, { + includePrerelease: true + }); + version.latest = !semver.prerelease(version.main); + version.main = handlebars.compile(this.inputs.tagSemver[0])(sver); + if (version.latest) { + for (const semverTpl of this.inputs.tagSemver) { + const partial = handlebars.compile(semverTpl)(sver); + if (partial == version.main) { + continue; + } + version.partial.push(partial); + } + } + } + else if (this.inputs.tagMatch) { let tagMatch; const isRegEx = this.inputs.tagMatch.match(/^\/(.+)\/(.*)$/); if (isRegEx) { - tagMatch = version.version.match(new RegExp(isRegEx[1], isRegEx[2])); + tagMatch = version.main.match(new RegExp(isRegEx[1], isRegEx[2])); } else { - tagMatch = version.version.match(this.inputs.tagMatch); + tagMatch = version.main.match(this.inputs.tagMatch); } if (tagMatch) { - version.version = tagMatch[this.inputs.tagMatchGroup]; + version.main = tagMatch[this.inputs.tagMatchGroup]; version.latest = this.inputs.tagMatchLatest; } } @@ -213,24 +232,27 @@ class Meta { } } else if (/^refs\/heads\//.test(this.context.ref)) { - version.version = this.context.ref.replace(/^refs\/heads\//g, '').replace(/\//g, '-'); - if (this.inputs.tagEdge && this.inputs.tagEdgeBranch === version.version) { - version.version = 'edge'; + version.main = this.context.ref.replace(/^refs\/heads\//g, '').replace(/\//g, '-'); + if (this.inputs.tagEdge && this.inputs.tagEdgeBranch === version.main) { + version.main = 'edge'; } } else if (/^refs\/pull\//.test(this.context.ref)) { - version.version = `pr-${this.context.ref.replace(/^refs\/pull\//g, '').replace(/\/merge$/g, '')}`; + version.main = `pr-${this.context.ref.replace(/^refs\/pull\//g, '').replace(/\/merge$/g, '')}`; } return version; } tags() { const version = this.version(); - if (!version.version) { + if (!version.main) { return []; } let tags = []; for (const image of this.inputs.images) { - tags.push(`${image}:${version.version}`); + tags.push(`${image}:${version.main}`); + for (const partial of version.partial) { + tags.push(`${image}:${partial}`); + } if (version.latest) { tags.push(`${image}:latest`); } @@ -247,7 +269,7 @@ class Meta { `org.opencontainers.image.description=${this.repo.description || ''}`, `org.opencontainers.image.url=${this.repo.html_url || ''}`, `org.opencontainers.image.source=${this.repo.html_url || ''}`, - `org.opencontainers.image.version=${this.version().version || ''}`, + `org.opencontainers.image.version=${this.version().main || ''}`, `org.opencontainers.image.created=${this.date.toISOString()}`, `org.opencontainers.image.revision=${this.context.sha || ''}`, `org.opencontainers.image.licenses=${((_a = this.repo.license) === null || _a === void 0 ? void 0 : _a.spdx_id) || ''}` @@ -16556,6 +16578,2150 @@ function onceStrict (fn) { } +/***/ }), + +/***/ 1532: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +const ANY = Symbol('SemVer ANY') +// hoisted class for cyclic dependency +class Comparator { + static get ANY () { + return ANY + } + constructor (comp, options) { + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false + } + } + + if (comp instanceof Comparator) { + if (comp.loose === !!options.loose) { + return comp + } else { + comp = comp.value + } + } + + debug('comparator', comp, options) + this.options = options + this.loose = !!options.loose + this.parse(comp) + + if (this.semver === ANY) { + this.value = '' + } else { + this.value = this.operator + this.semver.version + } + + debug('comp', this) + } + + parse (comp) { + const r = this.options.loose ? re[t.COMPARATORLOOSE] : re[t.COMPARATOR] + const m = comp.match(r) + + if (!m) { + throw new TypeError(`Invalid comparator: ${comp}`) + } + + this.operator = m[1] !== undefined ? m[1] : '' + if (this.operator === '=') { + this.operator = '' + } + + // if it literally is just '>' or '' then allow anything. + if (!m[2]) { + this.semver = ANY + } else { + this.semver = new SemVer(m[2], this.options.loose) + } + } + + toString () { + return this.value + } + + test (version) { + debug('Comparator.test', version, this.options.loose) + + if (this.semver === ANY || version === ANY) { + return true + } + + if (typeof version === 'string') { + try { + version = new SemVer(version, this.options) + } catch (er) { + return false + } + } + + return cmp(version, this.operator, this.semver, this.options) + } + + intersects (comp, options) { + if (!(comp instanceof Comparator)) { + throw new TypeError('a Comparator is required') + } + + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false + } + } + + if (this.operator === '') { + if (this.value === '') { + return true + } + return new Range(comp.value, options).test(this.value) + } else if (comp.operator === '') { + if (comp.value === '') { + return true + } + return new Range(this.value, options).test(comp.semver) + } + + const sameDirectionIncreasing = + (this.operator === '>=' || this.operator === '>') && + (comp.operator === '>=' || comp.operator === '>') + const sameDirectionDecreasing = + (this.operator === '<=' || this.operator === '<') && + (comp.operator === '<=' || comp.operator === '<') + const sameSemVer = this.semver.version === comp.semver.version + const differentDirectionsInclusive = + (this.operator === '>=' || this.operator === '<=') && + (comp.operator === '>=' || comp.operator === '<=') + const oppositeDirectionsLessThan = + cmp(this.semver, '<', comp.semver, options) && + (this.operator === '>=' || this.operator === '>') && + (comp.operator === '<=' || comp.operator === '<') + const oppositeDirectionsGreaterThan = + cmp(this.semver, '>', comp.semver, options) && + (this.operator === '<=' || this.operator === '<') && + (comp.operator === '>=' || comp.operator === '>') + + return ( + sameDirectionIncreasing || + sameDirectionDecreasing || + (sameSemVer && differentDirectionsInclusive) || + oppositeDirectionsLessThan || + oppositeDirectionsGreaterThan + ) + } +} + +module.exports = Comparator + +const {re, t} = __webpack_require__(9523) +const cmp = __webpack_require__(5098) +const debug = __webpack_require__(427) +const SemVer = __webpack_require__(8088) +const Range = __webpack_require__(9828) + + +/***/ }), + +/***/ 9828: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +// hoisted class for cyclic dependency +class Range { + constructor (range, options) { + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false + } + } + + if (range instanceof Range) { + if ( + range.loose === !!options.loose && + range.includePrerelease === !!options.includePrerelease + ) { + return range + } else { + return new Range(range.raw, options) + } + } + + if (range instanceof Comparator) { + // just put it in the set and return + this.raw = range.value + this.set = [[range]] + this.format() + return this + } + + this.options = options + this.loose = !!options.loose + this.includePrerelease = !!options.includePrerelease + + // First, split based on boolean or || + this.raw = range + this.set = range + .split(/\s*\|\|\s*/) + // map the range to a 2d array of comparators + .map(range => this.parseRange(range.trim())) + // throw out any comparator lists that are empty + // this generally means that it was not a valid range, which is allowed + // in loose mode, but will still throw if the WHOLE range is invalid. + .filter(c => c.length) + + if (!this.set.length) { + throw new TypeError(`Invalid SemVer Range: ${range}`) + } + + this.format() + } + + format () { + this.range = this.set + .map((comps) => { + return comps.join(' ').trim() + }) + .join('||') + .trim() + return this.range + } + + toString () { + return this.range + } + + parseRange (range) { + const loose = this.options.loose + range = range.trim() + // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4` + const hr = loose ? re[t.HYPHENRANGELOOSE] : re[t.HYPHENRANGE] + range = range.replace(hr, hyphenReplace(this.options.includePrerelease)) + debug('hyphen replace', range) + // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5` + range = range.replace(re[t.COMPARATORTRIM], comparatorTrimReplace) + debug('comparator trim', range, re[t.COMPARATORTRIM]) + + // `~ 1.2.3` => `~1.2.3` + range = range.replace(re[t.TILDETRIM], tildeTrimReplace) + + // `^ 1.2.3` => `^1.2.3` + range = range.replace(re[t.CARETTRIM], caretTrimReplace) + + // normalize spaces + range = range.split(/\s+/).join(' ') + + // At this point, the range is completely trimmed and + // ready to be split into comparators. + + const compRe = loose ? re[t.COMPARATORLOOSE] : re[t.COMPARATOR] + return range + .split(' ') + .map(comp => parseComparator(comp, this.options)) + .join(' ') + .split(/\s+/) + .map(comp => replaceGTE0(comp, this.options)) + // in loose mode, throw out any that are not valid comparators + .filter(this.options.loose ? comp => !!comp.match(compRe) : () => true) + .map(comp => new Comparator(comp, this.options)) + } + + intersects (range, options) { + if (!(range instanceof Range)) { + throw new TypeError('a Range is required') + } + + return this.set.some((thisComparators) => { + return ( + isSatisfiable(thisComparators, options) && + range.set.some((rangeComparators) => { + return ( + isSatisfiable(rangeComparators, options) && + thisComparators.every((thisComparator) => { + return rangeComparators.every((rangeComparator) => { + return thisComparator.intersects(rangeComparator, options) + }) + }) + ) + }) + ) + }) + } + + // if ANY of the sets match ALL of its comparators, then pass + test (version) { + if (!version) { + return false + } + + if (typeof version === 'string') { + try { + version = new SemVer(version, this.options) + } catch (er) { + return false + } + } + + for (let i = 0; i < this.set.length; i++) { + if (testSet(this.set[i], version, this.options)) { + return true + } + } + return false + } +} +module.exports = Range + +const Comparator = __webpack_require__(1532) +const debug = __webpack_require__(427) +const SemVer = __webpack_require__(8088) +const { + re, + t, + comparatorTrimReplace, + tildeTrimReplace, + caretTrimReplace +} = __webpack_require__(9523) + +// take a set of comparators and determine whether there +// exists a version which can satisfy it +const isSatisfiable = (comparators, options) => { + let result = true + const remainingComparators = comparators.slice() + let testComparator = remainingComparators.pop() + + while (result && remainingComparators.length) { + result = remainingComparators.every((otherComparator) => { + return testComparator.intersects(otherComparator, options) + }) + + testComparator = remainingComparators.pop() + } + + return result +} + +// comprised of xranges, tildes, stars, and gtlt's at this point. +// already replaced the hyphen ranges +// turn into a set of JUST comparators. +const parseComparator = (comp, options) => { + debug('comp', comp, options) + comp = replaceCarets(comp, options) + debug('caret', comp) + comp = replaceTildes(comp, options) + debug('tildes', comp) + comp = replaceXRanges(comp, options) + debug('xrange', comp) + comp = replaceStars(comp, options) + debug('stars', comp) + return comp +} + +const isX = id => !id || id.toLowerCase() === 'x' || id === '*' + +// ~, ~> --> * (any, kinda silly) +// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0-0 +// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0-0 +// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0-0 +// ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0-0 +// ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0-0 +const replaceTildes = (comp, options) => + comp.trim().split(/\s+/).map((comp) => { + return replaceTilde(comp, options) + }).join(' ') + +const replaceTilde = (comp, options) => { + const r = options.loose ? re[t.TILDELOOSE] : re[t.TILDE] + return comp.replace(r, (_, M, m, p, pr) => { + debug('tilde', comp, _, M, m, p, pr) + let ret + + if (isX(M)) { + ret = '' + } else if (isX(m)) { + ret = `>=${M}.0.0 <${+M + 1}.0.0-0` + } else if (isX(p)) { + // ~1.2 == >=1.2.0 <1.3.0-0 + ret = `>=${M}.${m}.0 <${M}.${+m + 1}.0-0` + } else if (pr) { + debug('replaceTilde pr', pr) + ret = `>=${M}.${m}.${p}-${pr + } <${M}.${+m + 1}.0-0` + } else { + // ~1.2.3 == >=1.2.3 <1.3.0-0 + ret = `>=${M}.${m}.${p + } <${M}.${+m + 1}.0-0` + } + + debug('tilde return', ret) + return ret + }) +} + +// ^ --> * (any, kinda silly) +// ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0-0 +// ^2.0, ^2.0.x --> >=2.0.0 <3.0.0-0 +// ^1.2, ^1.2.x --> >=1.2.0 <2.0.0-0 +// ^1.2.3 --> >=1.2.3 <2.0.0-0 +// ^1.2.0 --> >=1.2.0 <2.0.0-0 +const replaceCarets = (comp, options) => + comp.trim().split(/\s+/).map((comp) => { + return replaceCaret(comp, options) + }).join(' ') + +const replaceCaret = (comp, options) => { + debug('caret', comp, options) + const r = options.loose ? re[t.CARETLOOSE] : re[t.CARET] + const z = options.includePrerelease ? '-0' : '' + return comp.replace(r, (_, M, m, p, pr) => { + debug('caret', comp, _, M, m, p, pr) + let ret + + if (isX(M)) { + ret = '' + } else if (isX(m)) { + ret = `>=${M}.0.0${z} <${+M + 1}.0.0-0` + } else if (isX(p)) { + if (M === '0') { + ret = `>=${M}.${m}.0${z} <${M}.${+m + 1}.0-0` + } else { + ret = `>=${M}.${m}.0${z} <${+M + 1}.0.0-0` + } + } else if (pr) { + debug('replaceCaret pr', pr) + if (M === '0') { + if (m === '0') { + ret = `>=${M}.${m}.${p}-${pr + } <${M}.${m}.${+p + 1}-0` + } else { + ret = `>=${M}.${m}.${p}-${pr + } <${M}.${+m + 1}.0-0` + } + } else { + ret = `>=${M}.${m}.${p}-${pr + } <${+M + 1}.0.0-0` + } + } else { + debug('no pr') + if (M === '0') { + if (m === '0') { + ret = `>=${M}.${m}.${p + }${z} <${M}.${m}.${+p + 1}-0` + } else { + ret = `>=${M}.${m}.${p + }${z} <${M}.${+m + 1}.0-0` + } + } else { + ret = `>=${M}.${m}.${p + } <${+M + 1}.0.0-0` + } + } + + debug('caret return', ret) + return ret + }) +} + +const replaceXRanges = (comp, options) => { + debug('replaceXRanges', comp, options) + return comp.split(/\s+/).map((comp) => { + return replaceXRange(comp, options) + }).join(' ') +} + +const replaceXRange = (comp, options) => { + comp = comp.trim() + const r = options.loose ? re[t.XRANGELOOSE] : re[t.XRANGE] + return comp.replace(r, (ret, gtlt, M, m, p, pr) => { + debug('xRange', comp, ret, gtlt, M, m, p, pr) + const xM = isX(M) + const xm = xM || isX(m) + const xp = xm || isX(p) + const anyX = xp + + if (gtlt === '=' && anyX) { + gtlt = '' + } + + // if we're including prereleases in the match, then we need + // to fix this to -0, the lowest possible prerelease value + pr = options.includePrerelease ? '-0' : '' + + if (xM) { + if (gtlt === '>' || gtlt === '<') { + // nothing is allowed + ret = '<0.0.0-0' + } else { + // nothing is forbidden + ret = '*' + } + } else if (gtlt && anyX) { + // we know patch is an x, because we have any x at all. + // replace X with 0 + if (xm) { + m = 0 + } + p = 0 + + if (gtlt === '>') { + // >1 => >=2.0.0 + // >1.2 => >=1.3.0 + gtlt = '>=' + if (xm) { + M = +M + 1 + m = 0 + p = 0 + } else { + m = +m + 1 + p = 0 + } + } else if (gtlt === '<=') { + // <=0.7.x is actually <0.8.0, since any 0.7.x should + // pass. Similarly, <=7.x is actually <8.0.0, etc. + gtlt = '<' + if (xm) { + M = +M + 1 + } else { + m = +m + 1 + } + } + + if (gtlt === '<') + pr = '-0' + + ret = `${gtlt + M}.${m}.${p}${pr}` + } else if (xm) { + ret = `>=${M}.0.0${pr} <${+M + 1}.0.0-0` + } else if (xp) { + ret = `>=${M}.${m}.0${pr + } <${M}.${+m + 1}.0-0` + } + + debug('xRange return', ret) + + return ret + }) +} + +// Because * is AND-ed with everything else in the comparator, +// and '' means "any version", just remove the *s entirely. +const replaceStars = (comp, options) => { + debug('replaceStars', comp, options) + // Looseness is ignored here. star is always as loose as it gets! + return comp.trim().replace(re[t.STAR], '') +} + +const replaceGTE0 = (comp, options) => { + debug('replaceGTE0', comp, options) + return comp.trim() + .replace(re[options.includePrerelease ? t.GTE0PRE : t.GTE0], '') +} + +// This function is passed to string.replace(re[t.HYPHENRANGE]) +// M, m, patch, prerelease, build +// 1.2 - 3.4.5 => >=1.2.0 <=3.4.5 +// 1.2.3 - 3.4 => >=1.2.0 <3.5.0-0 Any 3.4.x will do +// 1.2 - 3.4 => >=1.2.0 <3.5.0-0 +const hyphenReplace = incPr => ($0, + from, fM, fm, fp, fpr, fb, + to, tM, tm, tp, tpr, tb) => { + if (isX(fM)) { + from = '' + } else if (isX(fm)) { + from = `>=${fM}.0.0${incPr ? '-0' : ''}` + } else if (isX(fp)) { + from = `>=${fM}.${fm}.0${incPr ? '-0' : ''}` + } else if (fpr) { + from = `>=${from}` + } else { + from = `>=${from}${incPr ? '-0' : ''}` + } + + if (isX(tM)) { + to = '' + } else if (isX(tm)) { + to = `<${+tM + 1}.0.0-0` + } else if (isX(tp)) { + to = `<${tM}.${+tm + 1}.0-0` + } else if (tpr) { + to = `<=${tM}.${tm}.${tp}-${tpr}` + } else if (incPr) { + to = `<${tM}.${tm}.${+tp + 1}-0` + } else { + to = `<=${to}` + } + + return (`${from} ${to}`).trim() +} + +const testSet = (set, version, options) => { + for (let i = 0; i < set.length; i++) { + if (!set[i].test(version)) { + return false + } + } + + if (version.prerelease.length && !options.includePrerelease) { + // Find the set of versions that are allowed to have prereleases + // For example, ^1.2.3-pr.1 desugars to >=1.2.3-pr.1 <2.0.0 + // That should allow `1.2.3-pr.2` to pass. + // However, `1.2.4-alpha.notready` should NOT be allowed, + // even though it's within the range set by the comparators. + for (let i = 0; i < set.length; i++) { + debug(set[i].semver) + if (set[i].semver === Comparator.ANY) { + continue + } + + if (set[i].semver.prerelease.length > 0) { + const allowed = set[i].semver + if (allowed.major === version.major && + allowed.minor === version.minor && + allowed.patch === version.patch) { + return true + } + } + } + + // Version has a -pre, but it's not one of the ones we like. + return false + } + + return true +} + + +/***/ }), + +/***/ 8088: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +const debug = __webpack_require__(427) +const { MAX_LENGTH, MAX_SAFE_INTEGER } = __webpack_require__(2293) +const { re, t } = __webpack_require__(9523) + +const { compareIdentifiers } = __webpack_require__(2463) +class SemVer { + constructor (version, options) { + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false + } + } + if (version instanceof SemVer) { + if (version.loose === !!options.loose && + version.includePrerelease === !!options.includePrerelease) { + return version + } else { + version = version.version + } + } else if (typeof version !== 'string') { + throw new TypeError(`Invalid Version: ${version}`) + } + + if (version.length > MAX_LENGTH) { + throw new TypeError( + `version is longer than ${MAX_LENGTH} characters` + ) + } + + debug('SemVer', version, options) + this.options = options + this.loose = !!options.loose + // this isn't actually relevant for versions, but keep it so that we + // don't run into trouble passing this.options around. + this.includePrerelease = !!options.includePrerelease + + const m = version.trim().match(options.loose ? re[t.LOOSE] : re[t.FULL]) + + if (!m) { + throw new TypeError(`Invalid Version: ${version}`) + } + + this.raw = version + + // these are actually numbers + this.major = +m[1] + this.minor = +m[2] + this.patch = +m[3] + + if (this.major > MAX_SAFE_INTEGER || this.major < 0) { + throw new TypeError('Invalid major version') + } + + if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) { + throw new TypeError('Invalid minor version') + } + + if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) { + throw new TypeError('Invalid patch version') + } + + // numberify any prerelease numeric ids + if (!m[4]) { + this.prerelease = [] + } else { + this.prerelease = m[4].split('.').map((id) => { + if (/^[0-9]+$/.test(id)) { + const num = +id + if (num >= 0 && num < MAX_SAFE_INTEGER) { + return num + } + } + return id + }) + } + + this.build = m[5] ? m[5].split('.') : [] + this.format() + } + + format () { + this.version = `${this.major}.${this.minor}.${this.patch}` + if (this.prerelease.length) { + this.version += `-${this.prerelease.join('.')}` + } + return this.version + } + + toString () { + return this.version + } + + compare (other) { + debug('SemVer.compare', this.version, this.options, other) + if (!(other instanceof SemVer)) { + if (typeof other === 'string' && other === this.version) { + return 0 + } + other = new SemVer(other, this.options) + } + + if (other.version === this.version) { + return 0 + } + + return this.compareMain(other) || this.comparePre(other) + } + + compareMain (other) { + if (!(other instanceof SemVer)) { + other = new SemVer(other, this.options) + } + + return ( + compareIdentifiers(this.major, other.major) || + compareIdentifiers(this.minor, other.minor) || + compareIdentifiers(this.patch, other.patch) + ) + } + + comparePre (other) { + if (!(other instanceof SemVer)) { + other = new SemVer(other, this.options) + } + + // NOT having a prerelease is > having one + if (this.prerelease.length && !other.prerelease.length) { + return -1 + } else if (!this.prerelease.length && other.prerelease.length) { + return 1 + } else if (!this.prerelease.length && !other.prerelease.length) { + return 0 + } + + let i = 0 + do { + const a = this.prerelease[i] + const b = other.prerelease[i] + debug('prerelease compare', i, a, b) + if (a === undefined && b === undefined) { + return 0 + } else if (b === undefined) { + return 1 + } else if (a === undefined) { + return -1 + } else if (a === b) { + continue + } else { + return compareIdentifiers(a, b) + } + } while (++i) + } + + compareBuild (other) { + if (!(other instanceof SemVer)) { + other = new SemVer(other, this.options) + } + + let i = 0 + do { + const a = this.build[i] + const b = other.build[i] + debug('prerelease compare', i, a, b) + if (a === undefined && b === undefined) { + return 0 + } else if (b === undefined) { + return 1 + } else if (a === undefined) { + return -1 + } else if (a === b) { + continue + } else { + return compareIdentifiers(a, b) + } + } while (++i) + } + + // preminor will bump the version up to the next minor release, and immediately + // down to pre-release. premajor and prepatch work the same way. + inc (release, identifier) { + switch (release) { + case 'premajor': + this.prerelease.length = 0 + this.patch = 0 + this.minor = 0 + this.major++ + this.inc('pre', identifier) + break + case 'preminor': + this.prerelease.length = 0 + this.patch = 0 + this.minor++ + this.inc('pre', identifier) + break + case 'prepatch': + // If this is already a prerelease, it will bump to the next version + // drop any prereleases that might already exist, since they are not + // relevant at this point. + this.prerelease.length = 0 + this.inc('patch', identifier) + this.inc('pre', identifier) + break + // If the input is a non-prerelease version, this acts the same as + // prepatch. + case 'prerelease': + if (this.prerelease.length === 0) { + this.inc('patch', identifier) + } + this.inc('pre', identifier) + break + + case 'major': + // If this is a pre-major version, bump up to the same major version. + // Otherwise increment major. + // 1.0.0-5 bumps to 1.0.0 + // 1.1.0 bumps to 2.0.0 + if ( + this.minor !== 0 || + this.patch !== 0 || + this.prerelease.length === 0 + ) { + this.major++ + } + this.minor = 0 + this.patch = 0 + this.prerelease = [] + break + case 'minor': + // If this is a pre-minor version, bump up to the same minor version. + // Otherwise increment minor. + // 1.2.0-5 bumps to 1.2.0 + // 1.2.1 bumps to 1.3.0 + if (this.patch !== 0 || this.prerelease.length === 0) { + this.minor++ + } + this.patch = 0 + this.prerelease = [] + break + case 'patch': + // If this is not a pre-release version, it will increment the patch. + // If it is a pre-release it will bump up to the same patch version. + // 1.2.0-5 patches to 1.2.0 + // 1.2.0 patches to 1.2.1 + if (this.prerelease.length === 0) { + this.patch++ + } + this.prerelease = [] + break + // This probably shouldn't be used publicly. + // 1.0.0 'pre' would become 1.0.0-0 which is the wrong direction. + case 'pre': + if (this.prerelease.length === 0) { + this.prerelease = [0] + } else { + let i = this.prerelease.length + while (--i >= 0) { + if (typeof this.prerelease[i] === 'number') { + this.prerelease[i]++ + i = -2 + } + } + if (i === -1) { + // didn't increment anything + this.prerelease.push(0) + } + } + if (identifier) { + // 1.2.0-beta.1 bumps to 1.2.0-beta.2, + // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0 + if (this.prerelease[0] === identifier) { + if (isNaN(this.prerelease[1])) { + this.prerelease = [identifier, 0] + } + } else { + this.prerelease = [identifier, 0] + } + } + break + + default: + throw new Error(`invalid increment argument: ${release}`) + } + this.format() + this.raw = this.version + return this + } +} + +module.exports = SemVer + + +/***/ }), + +/***/ 8848: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +const parse = __webpack_require__(5925) +const clean = (version, options) => { + const s = parse(version.trim().replace(/^[=v]+/, ''), options) + return s ? s.version : null +} +module.exports = clean + + +/***/ }), + +/***/ 5098: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +const eq = __webpack_require__(1898) +const neq = __webpack_require__(6017) +const gt = __webpack_require__(4123) +const gte = __webpack_require__(5522) +const lt = __webpack_require__(194) +const lte = __webpack_require__(7520) + +const cmp = (a, op, b, loose) => { + switch (op) { + case '===': + if (typeof a === 'object') + a = a.version + if (typeof b === 'object') + b = b.version + return a === b + + case '!==': + if (typeof a === 'object') + a = a.version + if (typeof b === 'object') + b = b.version + return a !== b + + case '': + case '=': + case '==': + return eq(a, b, loose) + + case '!=': + return neq(a, b, loose) + + case '>': + return gt(a, b, loose) + + case '>=': + return gte(a, b, loose) + + case '<': + return lt(a, b, loose) + + case '<=': + return lte(a, b, loose) + + default: + throw new TypeError(`Invalid operator: ${op}`) + } +} +module.exports = cmp + + +/***/ }), + +/***/ 3466: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +const SemVer = __webpack_require__(8088) +const parse = __webpack_require__(5925) +const {re, t} = __webpack_require__(9523) + +const coerce = (version, options) => { + if (version instanceof SemVer) { + return version + } + + if (typeof version === 'number') { + version = String(version) + } + + if (typeof version !== 'string') { + return null + } + + options = options || {} + + let match = null + if (!options.rtl) { + match = version.match(re[t.COERCE]) + } else { + // Find the right-most coercible string that does not share + // a terminus with a more left-ward coercible string. + // Eg, '1.2.3.4' wants to coerce '2.3.4', not '3.4' or '4' + // + // Walk through the string checking with a /g regexp + // Manually set the index so as to pick up overlapping matches. + // Stop when we get a match that ends at the string end, since no + // coercible string can be more right-ward without the same terminus. + let next + while ((next = re[t.COERCERTL].exec(version)) && + (!match || match.index + match[0].length !== version.length) + ) { + if (!match || + next.index + next[0].length !== match.index + match[0].length) { + match = next + } + re[t.COERCERTL].lastIndex = next.index + next[1].length + next[2].length + } + // leave it in a clean state + re[t.COERCERTL].lastIndex = -1 + } + + if (match === null) + return null + + return parse(`${match[2]}.${match[3] || '0'}.${match[4] || '0'}`, options) +} +module.exports = coerce + + +/***/ }), + +/***/ 2156: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +const SemVer = __webpack_require__(8088) +const compareBuild = (a, b, loose) => { + const versionA = new SemVer(a, loose) + const versionB = new SemVer(b, loose) + return versionA.compare(versionB) || versionA.compareBuild(versionB) +} +module.exports = compareBuild + + +/***/ }), + +/***/ 2804: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +const compare = __webpack_require__(4309) +const compareLoose = (a, b) => compare(a, b, true) +module.exports = compareLoose + + +/***/ }), + +/***/ 4309: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +const SemVer = __webpack_require__(8088) +const compare = (a, b, loose) => + new SemVer(a, loose).compare(new SemVer(b, loose)) + +module.exports = compare + + +/***/ }), + +/***/ 4297: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +const parse = __webpack_require__(5925) +const eq = __webpack_require__(1898) + +const diff = (version1, version2) => { + if (eq(version1, version2)) { + return null + } else { + const v1 = parse(version1) + const v2 = parse(version2) + const hasPre = v1.prerelease.length || v2.prerelease.length + const prefix = hasPre ? 'pre' : '' + const defaultResult = hasPre ? 'prerelease' : '' + for (const key in v1) { + if (key === 'major' || key === 'minor' || key === 'patch') { + if (v1[key] !== v2[key]) { + return prefix + key + } + } + } + return defaultResult // may be undefined + } +} +module.exports = diff + + +/***/ }), + +/***/ 1898: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +const compare = __webpack_require__(4309) +const eq = (a, b, loose) => compare(a, b, loose) === 0 +module.exports = eq + + +/***/ }), + +/***/ 4123: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +const compare = __webpack_require__(4309) +const gt = (a, b, loose) => compare(a, b, loose) > 0 +module.exports = gt + + +/***/ }), + +/***/ 5522: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +const compare = __webpack_require__(4309) +const gte = (a, b, loose) => compare(a, b, loose) >= 0 +module.exports = gte + + +/***/ }), + +/***/ 900: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +const SemVer = __webpack_require__(8088) + +const inc = (version, release, options, identifier) => { + if (typeof (options) === 'string') { + identifier = options + options = undefined + } + + try { + return new SemVer(version, options).inc(release, identifier).version + } catch (er) { + return null + } +} +module.exports = inc + + +/***/ }), + +/***/ 194: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +const compare = __webpack_require__(4309) +const lt = (a, b, loose) => compare(a, b, loose) < 0 +module.exports = lt + + +/***/ }), + +/***/ 7520: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +const compare = __webpack_require__(4309) +const lte = (a, b, loose) => compare(a, b, loose) <= 0 +module.exports = lte + + +/***/ }), + +/***/ 6688: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +const SemVer = __webpack_require__(8088) +const major = (a, loose) => new SemVer(a, loose).major +module.exports = major + + +/***/ }), + +/***/ 8447: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +const SemVer = __webpack_require__(8088) +const minor = (a, loose) => new SemVer(a, loose).minor +module.exports = minor + + +/***/ }), + +/***/ 6017: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +const compare = __webpack_require__(4309) +const neq = (a, b, loose) => compare(a, b, loose) !== 0 +module.exports = neq + + +/***/ }), + +/***/ 5925: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +const {MAX_LENGTH} = __webpack_require__(2293) +const { re, t } = __webpack_require__(9523) +const SemVer = __webpack_require__(8088) + +const parse = (version, options) => { + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false + } + } + + if (version instanceof SemVer) { + return version + } + + if (typeof version !== 'string') { + return null + } + + if (version.length > MAX_LENGTH) { + return null + } + + const r = options.loose ? re[t.LOOSE] : re[t.FULL] + if (!r.test(version)) { + return null + } + + try { + return new SemVer(version, options) + } catch (er) { + return null + } +} + +module.exports = parse + + +/***/ }), + +/***/ 2866: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +const SemVer = __webpack_require__(8088) +const patch = (a, loose) => new SemVer(a, loose).patch +module.exports = patch + + +/***/ }), + +/***/ 6014: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +const parse = __webpack_require__(5925) +const prerelease = (version, options) => { + const parsed = parse(version, options) + return (parsed && parsed.prerelease.length) ? parsed.prerelease : null +} +module.exports = prerelease + + +/***/ }), + +/***/ 6417: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +const compare = __webpack_require__(4309) +const rcompare = (a, b, loose) => compare(b, a, loose) +module.exports = rcompare + + +/***/ }), + +/***/ 8701: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +const compareBuild = __webpack_require__(2156) +const rsort = (list, loose) => list.sort((a, b) => compareBuild(b, a, loose)) +module.exports = rsort + + +/***/ }), + +/***/ 6055: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +const Range = __webpack_require__(9828) +const satisfies = (version, range, options) => { + try { + range = new Range(range, options) + } catch (er) { + return false + } + return range.test(version) +} +module.exports = satisfies + + +/***/ }), + +/***/ 1426: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +const compareBuild = __webpack_require__(2156) +const sort = (list, loose) => list.sort((a, b) => compareBuild(a, b, loose)) +module.exports = sort + + +/***/ }), + +/***/ 9601: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +const parse = __webpack_require__(5925) +const valid = (version, options) => { + const v = parse(version, options) + return v ? v.version : null +} +module.exports = valid + + +/***/ }), + +/***/ 1383: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +// just pre-load all the stuff that index.js lazily exports +const internalRe = __webpack_require__(9523) +module.exports = { + re: internalRe.re, + src: internalRe.src, + tokens: internalRe.t, + SEMVER_SPEC_VERSION: __webpack_require__(2293).SEMVER_SPEC_VERSION, + SemVer: __webpack_require__(8088), + compareIdentifiers: __webpack_require__(2463).compareIdentifiers, + rcompareIdentifiers: __webpack_require__(2463).rcompareIdentifiers, + parse: __webpack_require__(5925), + valid: __webpack_require__(9601), + clean: __webpack_require__(8848), + inc: __webpack_require__(900), + diff: __webpack_require__(4297), + major: __webpack_require__(6688), + minor: __webpack_require__(8447), + patch: __webpack_require__(2866), + prerelease: __webpack_require__(6014), + compare: __webpack_require__(4309), + rcompare: __webpack_require__(6417), + compareLoose: __webpack_require__(2804), + compareBuild: __webpack_require__(2156), + sort: __webpack_require__(1426), + rsort: __webpack_require__(8701), + gt: __webpack_require__(4123), + lt: __webpack_require__(194), + eq: __webpack_require__(1898), + neq: __webpack_require__(6017), + gte: __webpack_require__(5522), + lte: __webpack_require__(7520), + cmp: __webpack_require__(5098), + coerce: __webpack_require__(3466), + Comparator: __webpack_require__(1532), + Range: __webpack_require__(9828), + satisfies: __webpack_require__(6055), + toComparators: __webpack_require__(2706), + maxSatisfying: __webpack_require__(579), + minSatisfying: __webpack_require__(832), + minVersion: __webpack_require__(4179), + validRange: __webpack_require__(2098), + outside: __webpack_require__(420), + gtr: __webpack_require__(9380), + ltr: __webpack_require__(3323), + intersects: __webpack_require__(7008), + simplifyRange: __webpack_require__(5297), + subset: __webpack_require__(7863), +} + + +/***/ }), + +/***/ 2293: +/***/ ((module) => { + +// Note: this is the semver.org version of the spec that it implements +// Not necessarily the package version of this code. +const SEMVER_SPEC_VERSION = '2.0.0' + +const MAX_LENGTH = 256 +const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || + /* istanbul ignore next */ 9007199254740991 + +// Max safe segment length for coercion. +const MAX_SAFE_COMPONENT_LENGTH = 16 + +module.exports = { + SEMVER_SPEC_VERSION, + MAX_LENGTH, + MAX_SAFE_INTEGER, + MAX_SAFE_COMPONENT_LENGTH +} + + +/***/ }), + +/***/ 427: +/***/ ((module) => { + +const debug = ( + typeof process === 'object' && + process.env && + process.env.NODE_DEBUG && + /\bsemver\b/i.test(process.env.NODE_DEBUG) +) ? (...args) => console.error('SEMVER', ...args) + : () => {} + +module.exports = debug + + +/***/ }), + +/***/ 2463: +/***/ ((module) => { + +const numeric = /^[0-9]+$/ +const compareIdentifiers = (a, b) => { + const anum = numeric.test(a) + const bnum = numeric.test(b) + + if (anum && bnum) { + a = +a + b = +b + } + + return a === b ? 0 + : (anum && !bnum) ? -1 + : (bnum && !anum) ? 1 + : a < b ? -1 + : 1 +} + +const rcompareIdentifiers = (a, b) => compareIdentifiers(b, a) + +module.exports = { + compareIdentifiers, + rcompareIdentifiers +} + + +/***/ }), + +/***/ 9523: +/***/ ((module, exports, __webpack_require__) => { + +const { MAX_SAFE_COMPONENT_LENGTH } = __webpack_require__(2293) +const debug = __webpack_require__(427) +exports = module.exports = {} + +// The actual regexps go on exports.re +const re = exports.re = [] +const src = exports.src = [] +const t = exports.t = {} +let R = 0 + +const createToken = (name, value, isGlobal) => { + const index = R++ + debug(index, value) + t[name] = index + src[index] = value + re[index] = new RegExp(value, isGlobal ? 'g' : undefined) +} + +// The following Regular Expressions can be used for tokenizing, +// validating, and parsing SemVer version strings. + +// ## Numeric Identifier +// A single `0`, or a non-zero digit followed by zero or more digits. + +createToken('NUMERICIDENTIFIER', '0|[1-9]\\d*') +createToken('NUMERICIDENTIFIERLOOSE', '[0-9]+') + +// ## Non-numeric Identifier +// Zero or more digits, followed by a letter or hyphen, and then zero or +// more letters, digits, or hyphens. + +createToken('NONNUMERICIDENTIFIER', '\\d*[a-zA-Z-][a-zA-Z0-9-]*') + +// ## Main Version +// Three dot-separated numeric identifiers. + +createToken('MAINVERSION', `(${src[t.NUMERICIDENTIFIER]})\\.` + + `(${src[t.NUMERICIDENTIFIER]})\\.` + + `(${src[t.NUMERICIDENTIFIER]})`) + +createToken('MAINVERSIONLOOSE', `(${src[t.NUMERICIDENTIFIERLOOSE]})\\.` + + `(${src[t.NUMERICIDENTIFIERLOOSE]})\\.` + + `(${src[t.NUMERICIDENTIFIERLOOSE]})`) + +// ## Pre-release Version Identifier +// A numeric identifier, or a non-numeric identifier. + +createToken('PRERELEASEIDENTIFIER', `(?:${src[t.NUMERICIDENTIFIER] +}|${src[t.NONNUMERICIDENTIFIER]})`) + +createToken('PRERELEASEIDENTIFIERLOOSE', `(?:${src[t.NUMERICIDENTIFIERLOOSE] +}|${src[t.NONNUMERICIDENTIFIER]})`) + +// ## Pre-release Version +// Hyphen, followed by one or more dot-separated pre-release version +// identifiers. + +createToken('PRERELEASE', `(?:-(${src[t.PRERELEASEIDENTIFIER] +}(?:\\.${src[t.PRERELEASEIDENTIFIER]})*))`) + +createToken('PRERELEASELOOSE', `(?:-?(${src[t.PRERELEASEIDENTIFIERLOOSE] +}(?:\\.${src[t.PRERELEASEIDENTIFIERLOOSE]})*))`) + +// ## Build Metadata Identifier +// Any combination of digits, letters, or hyphens. + +createToken('BUILDIDENTIFIER', '[0-9A-Za-z-]+') + +// ## Build Metadata +// Plus sign, followed by one or more period-separated build metadata +// identifiers. + +createToken('BUILD', `(?:\\+(${src[t.BUILDIDENTIFIER] +}(?:\\.${src[t.BUILDIDENTIFIER]})*))`) + +// ## Full Version String +// A main version, followed optionally by a pre-release version and +// build metadata. + +// Note that the only major, minor, patch, and pre-release sections of +// the version string are capturing groups. The build metadata is not a +// capturing group, because it should not ever be used in version +// comparison. + +createToken('FULLPLAIN', `v?${src[t.MAINVERSION] +}${src[t.PRERELEASE]}?${ + src[t.BUILD]}?`) + +createToken('FULL', `^${src[t.FULLPLAIN]}$`) + +// like full, but allows v1.2.3 and =1.2.3, which people do sometimes. +// also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty +// common in the npm registry. +createToken('LOOSEPLAIN', `[v=\\s]*${src[t.MAINVERSIONLOOSE] +}${src[t.PRERELEASELOOSE]}?${ + src[t.BUILD]}?`) + +createToken('LOOSE', `^${src[t.LOOSEPLAIN]}$`) + +createToken('GTLT', '((?:<|>)?=?)') + +// Something like "2.*" or "1.2.x". +// Note that "x.x" is a valid xRange identifer, meaning "any version" +// Only the first item is strictly required. +createToken('XRANGEIDENTIFIERLOOSE', `${src[t.NUMERICIDENTIFIERLOOSE]}|x|X|\\*`) +createToken('XRANGEIDENTIFIER', `${src[t.NUMERICIDENTIFIER]}|x|X|\\*`) + +createToken('XRANGEPLAIN', `[v=\\s]*(${src[t.XRANGEIDENTIFIER]})` + + `(?:\\.(${src[t.XRANGEIDENTIFIER]})` + + `(?:\\.(${src[t.XRANGEIDENTIFIER]})` + + `(?:${src[t.PRERELEASE]})?${ + src[t.BUILD]}?` + + `)?)?`) + +createToken('XRANGEPLAINLOOSE', `[v=\\s]*(${src[t.XRANGEIDENTIFIERLOOSE]})` + + `(?:\\.(${src[t.XRANGEIDENTIFIERLOOSE]})` + + `(?:\\.(${src[t.XRANGEIDENTIFIERLOOSE]})` + + `(?:${src[t.PRERELEASELOOSE]})?${ + src[t.BUILD]}?` + + `)?)?`) + +createToken('XRANGE', `^${src[t.GTLT]}\\s*${src[t.XRANGEPLAIN]}$`) +createToken('XRANGELOOSE', `^${src[t.GTLT]}\\s*${src[t.XRANGEPLAINLOOSE]}$`) + +// Coercion. +// Extract anything that could conceivably be a part of a valid semver +createToken('COERCE', `${'(^|[^\\d])' + + '(\\d{1,'}${MAX_SAFE_COMPONENT_LENGTH}})` + + `(?:\\.(\\d{1,${MAX_SAFE_COMPONENT_LENGTH}}))?` + + `(?:\\.(\\d{1,${MAX_SAFE_COMPONENT_LENGTH}}))?` + + `(?:$|[^\\d])`) +createToken('COERCERTL', src[t.COERCE], true) + +// Tilde ranges. +// Meaning is "reasonably at or greater than" +createToken('LONETILDE', '(?:~>?)') + +createToken('TILDETRIM', `(\\s*)${src[t.LONETILDE]}\\s+`, true) +exports.tildeTrimReplace = '$1~' + +createToken('TILDE', `^${src[t.LONETILDE]}${src[t.XRANGEPLAIN]}$`) +createToken('TILDELOOSE', `^${src[t.LONETILDE]}${src[t.XRANGEPLAINLOOSE]}$`) + +// Caret ranges. +// Meaning is "at least and backwards compatible with" +createToken('LONECARET', '(?:\\^)') + +createToken('CARETTRIM', `(\\s*)${src[t.LONECARET]}\\s+`, true) +exports.caretTrimReplace = '$1^' + +createToken('CARET', `^${src[t.LONECARET]}${src[t.XRANGEPLAIN]}$`) +createToken('CARETLOOSE', `^${src[t.LONECARET]}${src[t.XRANGEPLAINLOOSE]}$`) + +// A simple gt/lt/eq thing, or just "" to indicate "any version" +createToken('COMPARATORLOOSE', `^${src[t.GTLT]}\\s*(${src[t.LOOSEPLAIN]})$|^$`) +createToken('COMPARATOR', `^${src[t.GTLT]}\\s*(${src[t.FULLPLAIN]})$|^$`) + +// An expression to strip any whitespace between the gtlt and the thing +// it modifies, so that `> 1.2.3` ==> `>1.2.3` +createToken('COMPARATORTRIM', `(\\s*)${src[t.GTLT] +}\\s*(${src[t.LOOSEPLAIN]}|${src[t.XRANGEPLAIN]})`, true) +exports.comparatorTrimReplace = '$1$2$3' + +// Something like `1.2.3 - 1.2.4` +// Note that these all use the loose form, because they'll be +// checked against either the strict or loose comparator form +// later. +createToken('HYPHENRANGE', `^\\s*(${src[t.XRANGEPLAIN]})` + + `\\s+-\\s+` + + `(${src[t.XRANGEPLAIN]})` + + `\\s*$`) + +createToken('HYPHENRANGELOOSE', `^\\s*(${src[t.XRANGEPLAINLOOSE]})` + + `\\s+-\\s+` + + `(${src[t.XRANGEPLAINLOOSE]})` + + `\\s*$`) + +// Star ranges basically just allow anything at all. +createToken('STAR', '(<|>)?=?\\s*\\*') +// >=0.0.0 is like a star +createToken('GTE0', '^\\s*>=\\s*0\.0\.0\\s*$') +createToken('GTE0PRE', '^\\s*>=\\s*0\.0\.0-0\\s*$') + + +/***/ }), + +/***/ 9380: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +// Determine if version is greater than all the versions possible in the range. +const outside = __webpack_require__(420) +const gtr = (version, range, options) => outside(version, range, '>', options) +module.exports = gtr + + +/***/ }), + +/***/ 7008: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +const Range = __webpack_require__(9828) +const intersects = (r1, r2, options) => { + r1 = new Range(r1, options) + r2 = new Range(r2, options) + return r1.intersects(r2) +} +module.exports = intersects + + +/***/ }), + +/***/ 3323: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +const outside = __webpack_require__(420) +// Determine if version is less than all the versions possible in the range +const ltr = (version, range, options) => outside(version, range, '<', options) +module.exports = ltr + + +/***/ }), + +/***/ 579: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +const SemVer = __webpack_require__(8088) +const Range = __webpack_require__(9828) + +const maxSatisfying = (versions, range, options) => { + let max = null + let maxSV = null + let rangeObj = null + try { + rangeObj = new Range(range, options) + } catch (er) { + return null + } + versions.forEach((v) => { + if (rangeObj.test(v)) { + // satisfies(v, range, options) + if (!max || maxSV.compare(v) === -1) { + // compare(max, v, true) + max = v + maxSV = new SemVer(max, options) + } + } + }) + return max +} +module.exports = maxSatisfying + + +/***/ }), + +/***/ 832: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +const SemVer = __webpack_require__(8088) +const Range = __webpack_require__(9828) +const minSatisfying = (versions, range, options) => { + let min = null + let minSV = null + let rangeObj = null + try { + rangeObj = new Range(range, options) + } catch (er) { + return null + } + versions.forEach((v) => { + if (rangeObj.test(v)) { + // satisfies(v, range, options) + if (!min || minSV.compare(v) === 1) { + // compare(min, v, true) + min = v + minSV = new SemVer(min, options) + } + } + }) + return min +} +module.exports = minSatisfying + + +/***/ }), + +/***/ 4179: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +const SemVer = __webpack_require__(8088) +const Range = __webpack_require__(9828) +const gt = __webpack_require__(4123) + +const minVersion = (range, loose) => { + range = new Range(range, loose) + + let minver = new SemVer('0.0.0') + if (range.test(minver)) { + return minver + } + + minver = new SemVer('0.0.0-0') + if (range.test(minver)) { + return minver + } + + minver = null + for (let i = 0; i < range.set.length; ++i) { + const comparators = range.set[i] + + comparators.forEach((comparator) => { + // Clone to avoid manipulating the comparator's semver object. + const compver = new SemVer(comparator.semver.version) + switch (comparator.operator) { + case '>': + if (compver.prerelease.length === 0) { + compver.patch++ + } else { + compver.prerelease.push(0) + } + compver.raw = compver.format() + /* fallthrough */ + case '': + case '>=': + if (!minver || gt(minver, compver)) { + minver = compver + } + break + case '<': + case '<=': + /* Ignore maximum versions */ + break + /* istanbul ignore next */ + default: + throw new Error(`Unexpected operation: ${comparator.operator}`) + } + }) + } + + if (minver && range.test(minver)) { + return minver + } + + return null +} +module.exports = minVersion + + +/***/ }), + +/***/ 420: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +const SemVer = __webpack_require__(8088) +const Comparator = __webpack_require__(1532) +const {ANY} = Comparator +const Range = __webpack_require__(9828) +const satisfies = __webpack_require__(6055) +const gt = __webpack_require__(4123) +const lt = __webpack_require__(194) +const lte = __webpack_require__(7520) +const gte = __webpack_require__(5522) + +const outside = (version, range, hilo, options) => { + version = new SemVer(version, options) + range = new Range(range, options) + + let gtfn, ltefn, ltfn, comp, ecomp + switch (hilo) { + case '>': + gtfn = gt + ltefn = lte + ltfn = lt + comp = '>' + ecomp = '>=' + break + case '<': + gtfn = lt + ltefn = gte + ltfn = gt + comp = '<' + ecomp = '<=' + break + default: + throw new TypeError('Must provide a hilo val of "<" or ">"') + } + + // If it satisifes the range it is not outside + if (satisfies(version, range, options)) { + return false + } + + // From now on, variable terms are as if we're in "gtr" mode. + // but note that everything is flipped for the "ltr" function. + + for (let i = 0; i < range.set.length; ++i) { + const comparators = range.set[i] + + let high = null + let low = null + + comparators.forEach((comparator) => { + if (comparator.semver === ANY) { + comparator = new Comparator('>=0.0.0') + } + high = high || comparator + low = low || comparator + if (gtfn(comparator.semver, high.semver, options)) { + high = comparator + } else if (ltfn(comparator.semver, low.semver, options)) { + low = comparator + } + }) + + // If the edge version comparator has a operator then our version + // isn't outside it + if (high.operator === comp || high.operator === ecomp) { + return false + } + + // If the lowest version comparator has an operator and our version + // is less than it then it isn't higher than the range + if ((!low.operator || low.operator === comp) && + ltefn(version, low.semver)) { + return false + } else if (low.operator === ecomp && ltfn(version, low.semver)) { + return false + } + } + return true +} + +module.exports = outside + + +/***/ }), + +/***/ 5297: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +// given a set of versions and a range, create a "simplified" range +// that includes the same versions that the original range does +// If the original range is shorter than the simplified one, return that. +const satisfies = __webpack_require__(6055) +const compare = __webpack_require__(4309) +module.exports = (versions, range, options) => { + const set = [] + let min = null + let prev = null + const v = versions.sort((a, b) => compare(a, b, options)) + for (const version of v) { + const included = satisfies(version, range, options) + if (included) { + prev = version + if (!min) + min = version + } else { + if (prev) { + set.push([min, prev]) + } + prev = null + min = null + } + } + if (min) + set.push([min, null]) + + const ranges = [] + for (const [min, max] of set) { + if (min === max) + ranges.push(min) + else if (!max && min === v[0]) + ranges.push('*') + else if (!max) + ranges.push(`>=${min}`) + else if (min === v[0]) + ranges.push(`<=${max}`) + else + ranges.push(`${min} - ${max}`) + } + const simplified = ranges.join(' || ') + const original = typeof range.raw === 'string' ? range.raw : String(range) + return simplified.length < original.length ? simplified : range +} + + +/***/ }), + +/***/ 7863: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +const Range = __webpack_require__(9828) +const { ANY } = __webpack_require__(1532) +const satisfies = __webpack_require__(6055) +const compare = __webpack_require__(4309) + +// Complex range `r1 || r2 || ...` is a subset of `R1 || R2 || ...` iff: +// - Every simple range `r1, r2, ...` is a subset of some `R1, R2, ...` +// +// Simple range `c1 c2 ...` is a subset of simple range `C1 C2 ...` iff: +// - If c is only the ANY comparator +// - If C is only the ANY comparator, return true +// - Else return false +// - Let EQ be the set of = comparators in c +// - If EQ is more than one, return true (null set) +// - Let GT be the highest > or >= comparator in c +// - Let LT be the lowest < or <= comparator in c +// - If GT and LT, and GT.semver > LT.semver, return true (null set) +// - If EQ +// - If GT, and EQ does not satisfy GT, return true (null set) +// - If LT, and EQ does not satisfy LT, return true (null set) +// - If EQ satisfies every C, return true +// - Else return false +// - If GT +// - If GT is lower than any > or >= comp in C, return false +// - If GT is >=, and GT.semver does not satisfy every C, return false +// - If LT +// - If LT.semver is greater than that of any > comp in C, return false +// - If LT is <=, and LT.semver does not satisfy every C, return false +// - If any C is a = range, and GT or LT are set, return false +// - Else return true + +const subset = (sub, dom, options) => { + sub = new Range(sub, options) + dom = new Range(dom, options) + let sawNonNull = false + + OUTER: for (const simpleSub of sub.set) { + for (const simpleDom of dom.set) { + const isSub = simpleSubset(simpleSub, simpleDom, options) + sawNonNull = sawNonNull || isSub !== null + if (isSub) + continue OUTER + } + // the null set is a subset of everything, but null simple ranges in + // a complex range should be ignored. so if we saw a non-null range, + // then we know this isn't a subset, but if EVERY simple range was null, + // then it is a subset. + if (sawNonNull) + return false + } + return true +} + +const simpleSubset = (sub, dom, options) => { + if (sub.length === 1 && sub[0].semver === ANY) + return dom.length === 1 && dom[0].semver === ANY + + const eqSet = new Set() + let gt, lt + for (const c of sub) { + if (c.operator === '>' || c.operator === '>=') + gt = higherGT(gt, c, options) + else if (c.operator === '<' || c.operator === '<=') + lt = lowerLT(lt, c, options) + else + eqSet.add(c.semver) + } + + if (eqSet.size > 1) + return null + + let gtltComp + if (gt && lt) { + gtltComp = compare(gt.semver, lt.semver, options) + if (gtltComp > 0) + return null + else if (gtltComp === 0 && (gt.operator !== '>=' || lt.operator !== '<=')) + return null + } + + // will iterate one or zero times + for (const eq of eqSet) { + if (gt && !satisfies(eq, String(gt), options)) + return null + + if (lt && !satisfies(eq, String(lt), options)) + return null + + for (const c of dom) { + if (!satisfies(eq, String(c), options)) + return false + } + return true + } + + let higher, lower + let hasDomLT, hasDomGT + for (const c of dom) { + hasDomGT = hasDomGT || c.operator === '>' || c.operator === '>=' + hasDomLT = hasDomLT || c.operator === '<' || c.operator === '<=' + if (gt) { + if (c.operator === '>' || c.operator === '>=') { + higher = higherGT(gt, c, options) + if (higher === c) + return false + } else if (gt.operator === '>=' && !satisfies(gt.semver, String(c), options)) + return false + } + if (lt) { + if (c.operator === '<' || c.operator === '<=') { + lower = lowerLT(lt, c, options) + if (lower === c) + return false + } else if (lt.operator === '<=' && !satisfies(lt.semver, String(c), options)) + return false + } + if (!c.operator && (lt || gt) && gtltComp !== 0) + return false + } + + // if there was a < or >, and nothing in the dom, then must be false + // UNLESS it was limited by another range in the other direction. + // Eg, >1.0.0 <1.0.1 is still a subset of <2.0.0 + if (gt && hasDomLT && !lt && gtltComp !== 0) + return false + + if (lt && hasDomGT && !gt && gtltComp !== 0) + return false + + return true +} + +// >=1.2.3 is lower than >1.2.3 +const higherGT = (a, b, options) => { + if (!a) + return b + const comp = compare(a.semver, b.semver, options) + return comp > 0 ? a + : comp < 0 ? b + : b.operator === '>' && a.operator === '>=' ? b + : a +} + +// <=1.2.3 is higher than <1.2.3 +const lowerLT = (a, b, options) => { + if (!a) + return b + const comp = compare(a.semver, b.semver, options) + return comp < 0 ? a + : comp > 0 ? b + : b.operator === '<' && a.operator === '<=' ? b + : a +} + +module.exports = subset + + +/***/ }), + +/***/ 2706: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +const Range = __webpack_require__(9828) + +// Mostly just for testing and legacy API reasons +const toComparators = (range, options) => + new Range(range, options).set + .map(comp => comp.map(c => c.value).join(' ').trim().split(' ')) + +module.exports = toComparators + + +/***/ }), + +/***/ 2098: +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +const Range = __webpack_require__(9828) +const validRange = (range, options) => { + try { + // Return '*' instead of '' so that truthiness works. + // This will throw if it's invalid anyway + return new Range(range, options).range || '*' + } catch (er) { + return null + } +} +module.exports = validRange + + /***/ }), /***/ 6375: diff --git a/package.json b/package.json index b7671cd..fd06cbb 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,8 @@ "@actions/core": "^1.2.6", "@actions/github": "^4.0.0", "handlebars": "^4.7.6", - "moment": "^2.29.1" + "moment": "^2.29.1", + "semver": "^7.3.2" }, "devDependencies": { "@types/jest": "^26.0.0", diff --git a/src/context.ts b/src/context.ts index a5a7eb6..8227241 100644 --- a/src/context.ts +++ b/src/context.ts @@ -5,6 +5,7 @@ export interface Inputs { tagSha: boolean; tagEdge: boolean; tagEdgeBranch: string; + tagSemver: string[]; tagMatch: string; tagMatchGroup: number; tagMatchLatest: boolean; @@ -20,6 +21,7 @@ export function getInputs(): Inputs { 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, tagMatchLatest: /true/i.test(core.getInput('tag-match-latest') || 'true'), diff --git a/src/main.ts b/src/main.ts index 692ef21..d61b4fc 100644 --- a/src/main.ts +++ b/src/main.ts @@ -29,9 +29,9 @@ async function run() { const version: Version = meta.version(); core.startGroup(`Docker image version`); - core.info(version.version || ''); + core.info(version.main || ''); core.endGroup(); - core.setOutput('version', version.version || ''); + core.setOutput('version', version.main || ''); const tags: Array = meta.tags(); core.startGroup(`Docker tags`); diff --git a/src/meta.ts b/src/meta.ts index 31624a6..9921ed1 100644 --- a/src/meta.ts +++ b/src/meta.ts @@ -1,11 +1,13 @@ import * as handlebars from 'handlebars'; import * as moment from 'moment'; +import * as semver from 'semver'; import {Inputs} from './context'; import {Context} from '@actions/github/lib/context'; import {ReposGetResponseData} from '@octokit/types'; export interface Version { - version: string | undefined; + main: string | undefined; + partial: string[]; latest: boolean; } @@ -28,40 +30,56 @@ export class Meta { public version(): Version { const currentDate = this.date; const version: Version = { - version: undefined, + main: undefined, + partial: [], latest: false }; if (/schedule/.test(this.context.eventName)) { - version.version = handlebars.compile(this.inputs.tagSchedule)({ + version.main = handlebars.compile(this.inputs.tagSchedule)({ date: function (format) { return moment(currentDate).utc().format(format); } }); } else if (/^refs\/tags\//.test(this.context.ref)) { - version.version = this.context.ref.replace(/^refs\/tags\//g, '').replace(/\//g, '-'); - if (this.inputs.tagMatch) { + version.main = this.context.ref.replace(/^refs\/tags\//g, '').replace(/\//g, '-'); + if (this.inputs.tagSemver.length > 0 && semver.valid(version.main)) { + const sver = semver.parse(version.main, { + includePrerelease: true + }); + version.latest = !semver.prerelease(version.main); + version.main = handlebars.compile(this.inputs.tagSemver[0])(sver); + if (version.latest) { + for (const semverTpl of this.inputs.tagSemver) { + const partial = handlebars.compile(semverTpl)(sver); + if (partial == version.main) { + continue; + } + version.partial.push(partial); + } + } + } else if (this.inputs.tagMatch) { let tagMatch; const isRegEx = this.inputs.tagMatch.match(/^\/(.+)\/(.*)$/); if (isRegEx) { - tagMatch = version.version.match(new RegExp(isRegEx[1], isRegEx[2])); + tagMatch = version.main.match(new RegExp(isRegEx[1], isRegEx[2])); } else { - tagMatch = version.version.match(this.inputs.tagMatch); + tagMatch = version.main.match(this.inputs.tagMatch); } if (tagMatch) { - version.version = tagMatch[this.inputs.tagMatchGroup]; + version.main = tagMatch[this.inputs.tagMatchGroup]; version.latest = this.inputs.tagMatchLatest; } } else { version.latest = this.inputs.tagMatchLatest; } } else if (/^refs\/heads\//.test(this.context.ref)) { - version.version = this.context.ref.replace(/^refs\/heads\//g, '').replace(/\//g, '-'); - if (this.inputs.tagEdge && this.inputs.tagEdgeBranch === version.version) { - version.version = 'edge'; + version.main = this.context.ref.replace(/^refs\/heads\//g, '').replace(/\//g, '-'); + if (this.inputs.tagEdge && this.inputs.tagEdgeBranch === version.main) { + version.main = 'edge'; } } else if (/^refs\/pull\//.test(this.context.ref)) { - version.version = `pr-${this.context.ref.replace(/^refs\/pull\//g, '').replace(/\/merge$/g, '')}`; + version.main = `pr-${this.context.ref.replace(/^refs\/pull\//g, '').replace(/\/merge$/g, '')}`; } return version; @@ -69,13 +87,16 @@ export class Meta { public tags(): Array { const version: Version = this.version(); - if (!version.version) { + if (!version.main) { return []; } let tags: Array = []; for (const image of this.inputs.images) { - tags.push(`${image}:${version.version}`); + tags.push(`${image}:${version.main}`); + for (const partial of version.partial) { + tags.push(`${image}:${partial}`); + } if (version.latest) { tags.push(`${image}:latest`); } @@ -92,7 +113,7 @@ export class Meta { `org.opencontainers.image.description=${this.repo.description || ''}`, `org.opencontainers.image.url=${this.repo.html_url || ''}`, `org.opencontainers.image.source=${this.repo.html_url || ''}`, - `org.opencontainers.image.version=${this.version().version || ''}`, + `org.opencontainers.image.version=${this.version().main || ''}`, `org.opencontainers.image.created=${this.date.toISOString()}`, `org.opencontainers.image.revision=${this.context.sha || ''}`, `org.opencontainers.image.licenses=${this.repo.license?.spdx_id || ''}`