mirror of
https://github.com/jimeh/update-tags-action.git
synced 2026-02-19 09:36:41 +00:00
Compare commits
35 Commits
4e16af70e2
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f709bf4521 | ||
|
|
713ef57d6a | ||
|
|
9faf3c9e39 | ||
|
|
2a6efacda6 | ||
|
|
00f9f5b105 | ||
|
|
b159afa635 | ||
|
|
e9e6aa35a8 | ||
| d92b2d6009 | |||
|
|
9debb9deac | ||
|
|
36243fb9d3 | ||
|
|
98d6d448de | ||
| fee5cc2ff2 | |||
| facf303096 | |||
| 5dc277d712 | |||
| 32e66b04be | |||
| 1d171e9f3c | |||
| 6adab3a060 | |||
| ef41989077 | |||
| bb3e44be2f | |||
|
|
46ebed28cc | ||
|
|
04e502b026 | ||
|
|
fa63dca88c | ||
|
|
6593b10eb9 | ||
| daa038fd7f | |||
| 5580fd8611 | |||
| aa16a0ff75 | |||
| 741ed40bb7 | |||
| a25f37f9e6 | |||
|
|
82dc01ff9e | ||
|
|
1d0890efd6 | ||
|
|
fcb6d5925b | ||
|
|
ecd1432d26 | ||
|
|
73adb32d6a | ||
|
|
0b1a9ea25d | ||
|
|
88c489d592 |
2
.github/.release-please-manifest.json
vendored
2
.github/.release-please-manifest.json
vendored
@@ -1,3 +1,3 @@
|
||||
{
|
||||
".": "2.2.0"
|
||||
".": "2.3.0"
|
||||
}
|
||||
|
||||
4
.github/dependabot.yml
vendored
4
.github/dependabot.yml
vendored
@@ -6,7 +6,7 @@ updates:
|
||||
schedule:
|
||||
interval: weekly
|
||||
groups:
|
||||
actions-minor:
|
||||
actions:
|
||||
update-types:
|
||||
- major
|
||||
- minor
|
||||
@@ -19,10 +19,12 @@ updates:
|
||||
npm-development:
|
||||
dependency-type: development
|
||||
update-types:
|
||||
- major
|
||||
- minor
|
||||
- patch
|
||||
npm-production:
|
||||
dependency-type: production
|
||||
update-types:
|
||||
- major
|
||||
- minor
|
||||
- patch
|
||||
|
||||
27
.github/workflows/ci.yml
vendored
27
.github/workflows/ci.yml
vendored
@@ -16,8 +16,8 @@ jobs:
|
||||
check-dist:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
with:
|
||||
node-version-file: .node-version
|
||||
cache: npm
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
fi
|
||||
- name: Upload Artifact
|
||||
if: ${{ failure() && steps.diff.outcome == 'failure' }}
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: dist
|
||||
path: dist/
|
||||
@@ -51,10 +51,10 @@ jobs:
|
||||
packages: read
|
||||
statuses: write
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
with:
|
||||
node-version-file: .node-version
|
||||
cache: npm
|
||||
@@ -64,10 +64,10 @@ jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
with:
|
||||
node-version-file: .node-version
|
||||
cache: npm
|
||||
@@ -79,14 +79,13 @@ jobs:
|
||||
if: ${{ github.ref == 'refs/heads/main' }}
|
||||
outputs:
|
||||
release_created: ${{ steps.release-please.outputs.release_created }}
|
||||
major: ${{ steps.release-please.outputs.major }}
|
||||
minor: ${{ steps.release-please.outputs.minor }}
|
||||
tag_name: ${{ steps.release-please.outputs.tag_name }}
|
||||
steps:
|
||||
- uses: jimeh/release-please-manifest-action@84f33fd2828210488c36f3e0a7e3209252d2ae7d # v3.0.0
|
||||
id: release-please
|
||||
with:
|
||||
app-id: ${{ secrets.RELEASE_BOT_APP_ID }}
|
||||
private-key: ${{ secrets.RELEASE_BOT_PRIVATE_KEY }}
|
||||
app-id: ${{ secrets.BOT_APP_ID }}
|
||||
private-key: ${{ secrets.BOT_PRIVATE_KEY }}
|
||||
|
||||
release-tags:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -95,8 +94,6 @@ jobs:
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: jimeh/update-tags-action@eecd8caae9a536ed536cff9b2b7f0bd187f67c13 # v2.2.0
|
||||
- uses: jimeh/update-tags-action@98d6d448de7488acb52bd8db115673541d019521 # v2.3.0
|
||||
with:
|
||||
tags: |
|
||||
v${{ needs.release-please.outputs.major }}
|
||||
v${{ needs.release-please.outputs.major }}.${{ needs.release-please.outputs.minor }}
|
||||
derive_from: ${{ needs.release-please.outputs.tag_name }}
|
||||
|
||||
6
.github/workflows/copilot-setup-steps.yml
vendored
6
.github/workflows/copilot-setup-steps.yml
vendored
@@ -15,13 +15,13 @@ jobs:
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
with:
|
||||
node-version-file: .node-version
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- uses: ruby/setup-ruby@d5126b9b3579e429dd52e51e68624dda2e05be25 # v1.267.0
|
||||
- uses: ruby/setup-ruby@708024e6c902387ab41de36e1669e43b5ee7085e # v1.283.0
|
||||
with:
|
||||
ruby-version: ruby
|
||||
bundler-cache: true
|
||||
|
||||
28
.github/workflows/dependabot-rebuild.yml
vendored
28
.github/workflows/dependabot-rebuild.yml
vendored
@@ -10,21 +10,23 @@ permissions:
|
||||
pull-requests: read
|
||||
|
||||
jobs:
|
||||
rebuild-dist:
|
||||
rebuild:
|
||||
runs-on: ubuntu-latest
|
||||
if: |-
|
||||
${{ github.actor == 'dependabot[bot]' && github.event.sender.login == 'dependabot[bot]' }}
|
||||
steps:
|
||||
- name: Generate app token
|
||||
id: app-token
|
||||
uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4
|
||||
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
|
||||
with:
|
||||
app-id: ${{ secrets.BOT_APP_ID }}
|
||||
private-key: ${{ secrets.BOT_PRIVATE_KEY }}
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||
|
||||
# Update and push dist if changed.
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
with:
|
||||
node-version-file: .node-version
|
||||
cache: npm
|
||||
@@ -40,3 +42,21 @@ jobs:
|
||||
dist/
|
||||
commit-message: |-
|
||||
chore: rebuild dist
|
||||
|
||||
# Update and push .licenses if changed.
|
||||
- uses: ruby/setup-ruby@708024e6c902387ab41de36e1669e43b5ee7085e # v1.283.0
|
||||
with:
|
||||
ruby-version: ruby
|
||||
bundler-cache: true
|
||||
- name: Update Licenses
|
||||
id: update-licenses
|
||||
run: bin/licensed cache
|
||||
- name: Commit and push if changed
|
||||
uses: ryancyq/github-signed-commit@e9f3b28c80da7be66d24b8f501a5abe82a6b855f # v1.2.0
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
with:
|
||||
files: |
|
||||
.licenses/
|
||||
commit-message: |-
|
||||
chore(licensed): update license files
|
||||
|
||||
36
.github/workflows/licensed.yml
vendored
36
.github/workflows/licensed.yml
vendored
@@ -20,13 +20,24 @@ jobs:
|
||||
check-licenses:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||
# Checkout code using app token.
|
||||
- name: Generate app token
|
||||
id: app-token
|
||||
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
|
||||
with:
|
||||
app-id: ${{ secrets.BOT_APP_ID }}
|
||||
private-key: ${{ secrets.BOT_PRIVATE_KEY }}
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
|
||||
# Setup Node and Ruby runtimes.
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
with:
|
||||
node-version-file: .node-version
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- uses: ruby/setup-ruby@d5126b9b3579e429dd52e51e68624dda2e05be25 # v1.267.0
|
||||
- uses: ruby/setup-ruby@708024e6c902387ab41de36e1669e43b5ee7085e # v1.283.0
|
||||
with:
|
||||
ruby-version: ruby
|
||||
bundler-cache: true
|
||||
@@ -36,17 +47,16 @@ jobs:
|
||||
name: Update Licenses
|
||||
id: update-licenses
|
||||
run: bin/licensed cache
|
||||
|
||||
# Then, commit the updated licenses to the repository.
|
||||
- if: ${{ github.event_name == 'workflow_dispatch' }}
|
||||
name: Commit Licenses
|
||||
id: commit-licenses
|
||||
run: |
|
||||
git config --local user.email "licensed-ci@users.noreply.github.com"
|
||||
git config --local user.name "licensed-ci"
|
||||
git add .
|
||||
git commit -m "Auto-update license files"
|
||||
git push
|
||||
name: Commit and push if changed
|
||||
uses: ryancyq/github-signed-commit@e9f3b28c80da7be66d24b8f501a5abe82a6b855f # v1.2.0
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
with:
|
||||
files: |
|
||||
.licenses/
|
||||
commit-message: |-
|
||||
chore(licensed): update license files
|
||||
|
||||
# Last, check the status of the cached licenses.
|
||||
- name: Check Licenses
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: "@actions/core"
|
||||
version: 1.11.1
|
||||
version: 2.0.2
|
||||
type: npm
|
||||
summary: Actions core lib
|
||||
homepage: https://github.com/actions/toolkit/tree/main/packages/core
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: "@actions/exec"
|
||||
version: 1.1.1
|
||||
version: 2.0.0
|
||||
type: npm
|
||||
summary: Actions exec lib
|
||||
homepage: https://github.com/actions/toolkit/tree/main/packages/exec
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: "@actions/github"
|
||||
version: 6.0.1
|
||||
version: 7.0.0
|
||||
type: npm
|
||||
summary: Actions github lib
|
||||
homepage: https://github.com/actions/toolkit/tree/main/packages/github
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: "@actions/http-client"
|
||||
version: 2.2.3
|
||||
version: 3.0.1
|
||||
type: npm
|
||||
summary: Actions Http Client
|
||||
homepage: https://github.com/actions/toolkit/tree/main/packages/http-client
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: "@actions/io"
|
||||
version: 1.1.3
|
||||
version: 2.0.0
|
||||
type: npm
|
||||
summary: Actions io lib
|
||||
homepage: https://github.com/actions/toolkit/tree/main/packages/io
|
||||
|
||||
36
.licenses/npm/handlebars.dep.yml
Normal file
36
.licenses/npm/handlebars.dep.yml
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
name: handlebars
|
||||
version: 4.7.8
|
||||
type: npm
|
||||
summary: Handlebars provides the power necessary to let you build semantic templates
|
||||
effectively with no frustration
|
||||
homepage: https://www.handlebarsjs.com/
|
||||
license: mit
|
||||
licenses:
|
||||
- sources: LICENSE
|
||||
text: |
|
||||
Copyright (C) 2011-2019 by Yehuda Katz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
- sources: README.markdown
|
||||
text: |-
|
||||
Handlebars.js is released under the MIT license.
|
||||
|
||||
[pull-request]: https://github.com/handlebars-lang/handlebars.js/pull/new/master
|
||||
notices: []
|
||||
44
.licenses/npm/minimist.dep.yml
Normal file
44
.licenses/npm/minimist.dep.yml
Normal file
@@ -0,0 +1,44 @@
|
||||
---
|
||||
name: minimist
|
||||
version: 1.2.8
|
||||
type: npm
|
||||
summary: parse argument options
|
||||
homepage: https://github.com/minimistjs/minimist
|
||||
license: other
|
||||
licenses:
|
||||
- sources: LICENSE
|
||||
text: |
|
||||
This software is released under the MIT license:
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
- sources: README.md
|
||||
text: |-
|
||||
MIT
|
||||
|
||||
[package-url]: https://npmjs.org/package/minimist
|
||||
[npm-version-svg]: https://versionbadg.es/minimistjs/minimist.svg
|
||||
[npm-badge-png]: https://nodei.co/npm/minimist.png?downloads=true&stars=true
|
||||
[license-image]: https://img.shields.io/npm/l/minimist.svg
|
||||
[license-url]: LICENSE
|
||||
[downloads-image]: https://img.shields.io/npm/dm/minimist.svg
|
||||
[downloads-url]: https://npm-stat.com/charts.html?package=minimist
|
||||
[codecov-image]: https://codecov.io/gh/minimistjs/minimist/branch/main/graphs/badge.svg
|
||||
[codecov-url]: https://app.codecov.io/gh/minimistjs/minimist/
|
||||
[actions-image]: https://img.shields.io/endpoint?url=https://github-actions-badge-u3jn4tfpocch.runkit.sh/minimistjs/minimist
|
||||
[actions-url]: https://github.com/minimistjs/minimist/actions
|
||||
notices: []
|
||||
36
.licenses/npm/neo-async.dep.yml
Normal file
36
.licenses/npm/neo-async.dep.yml
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
name: neo-async
|
||||
version: 2.6.2
|
||||
type: npm
|
||||
summary: 'Neo-Async is a drop-in replacement for Async, it almost fully covers its
|
||||
functionality and runs faster '
|
||||
homepage: https://github.com/suguru03/neo-async
|
||||
license: other
|
||||
licenses:
|
||||
- sources: LICENSE
|
||||
text: |
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2014-2018 Suguru Motegi
|
||||
Based on Async.js, Copyright Caolan McMahon
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
- sources: README.md
|
||||
text: "[](https://app.fossa.io/projects/git%2Bgithub.com%2Fsuguru03%2Fneo-async?ref=badge_large)"
|
||||
notices: []
|
||||
26
.licenses/npm/semver.dep.yml
Normal file
26
.licenses/npm/semver.dep.yml
Normal file
@@ -0,0 +1,26 @@
|
||||
---
|
||||
name: semver
|
||||
version: 7.7.3
|
||||
type: npm
|
||||
summary: The semantic version parser used by npm.
|
||||
homepage:
|
||||
license: isc
|
||||
licenses:
|
||||
- sources: LICENSE
|
||||
text: |
|
||||
The ISC License
|
||||
|
||||
Copyright (c) Isaac Z. Schlueter and Contributors
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
notices: []
|
||||
39
.licenses/npm/source-map.dep.yml
Normal file
39
.licenses/npm/source-map.dep.yml
Normal file
@@ -0,0 +1,39 @@
|
||||
---
|
||||
name: source-map
|
||||
version: 0.6.1
|
||||
type: npm
|
||||
summary: Generates and consumes source maps
|
||||
homepage: https://github.com/mozilla/source-map
|
||||
license: other
|
||||
licenses:
|
||||
- sources: LICENSE
|
||||
text: |2
|
||||
|
||||
Copyright (c) 2009-2011, Mozilla Foundation and contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the names of the Mozilla Foundation nor the names of project
|
||||
contributors may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
notices: []
|
||||
40
.licenses/npm/uglify-js.dep.yml
Normal file
40
.licenses/npm/uglify-js.dep.yml
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
name: uglify-js
|
||||
version: 3.19.3
|
||||
type: npm
|
||||
summary: JavaScript parser, mangler/compressor and beautifier toolkit
|
||||
homepage:
|
||||
license: other
|
||||
licenses:
|
||||
- sources: LICENSE
|
||||
text: |
|
||||
UglifyJS is released under the BSD license:
|
||||
|
||||
Copyright 2012-2024 (c) Mihai Bazon <mihai.bazon@gmail.com>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials
|
||||
provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
notices: []
|
||||
29
.licenses/npm/wordwrap.dep.yml
Normal file
29
.licenses/npm/wordwrap.dep.yml
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
name: wordwrap
|
||||
version: 1.0.0
|
||||
type: npm
|
||||
summary: Wrap those words. Show them at what columns to start and stop.
|
||||
homepage:
|
||||
license: other
|
||||
licenses:
|
||||
- sources: LICENSE
|
||||
text: |
|
||||
This software is released under the MIT license:
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
notices: []
|
||||
2
.mise.toml
Normal file
2
.mise.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
[settings]
|
||||
idiomatic_version_file_enable_tools = ["ruby", "node"]
|
||||
@@ -1 +1 @@
|
||||
24.4.0
|
||||
24.12.0
|
||||
|
||||
1
.ruby-version
Normal file
1
.ruby-version
Normal file
@@ -0,0 +1 @@
|
||||
3.4.8
|
||||
91
AGENTS.md
91
AGENTS.md
@@ -19,9 +19,13 @@ tags.
|
||||
up-to-date. Always run `npm run package` (or `npm run bundle`) after modifying
|
||||
`src/` files.
|
||||
|
||||
**License Compliance**: After modifying NPM dependencies, check license status
|
||||
and re-cache if needed via `npm run licensed:status` and
|
||||
`npm run licensed:cache`.
|
||||
|
||||
## Development Commands
|
||||
|
||||
Package manager: npm (Node 24 via mise.toml)
|
||||
Package manager: npm
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
@@ -35,7 +39,7 @@ npm run package # Build src/index.ts -> dist/index.js via Rollup
|
||||
npm run bundle # Alias: format + package
|
||||
|
||||
# Run a single test file
|
||||
NODE_OPTIONS=--experimental-vm-modules NODE_NO_WARNINGS=1 npx jest tests/main.test.ts
|
||||
NODE_OPTIONS=--experimental-vm-modules NODE_NO_WARNINGS=1 jest tests/main.test.ts
|
||||
|
||||
# CI variants (suppress warnings)
|
||||
npm run ci-test # Run tests in CI mode
|
||||
@@ -48,6 +52,10 @@ npm run update-readme
|
||||
|
||||
# Watch mode
|
||||
npm run package:watch # Auto-rebuild on changes
|
||||
|
||||
# License compliance (after modifying dependencies)
|
||||
npm run licensed:status # Check license status of dependencies
|
||||
npm run licensed:cache # Re-cache licenses if needed
|
||||
```
|
||||
|
||||
## Code Architecture
|
||||
@@ -59,11 +67,17 @@ npm run package:watch # Auto-rebuild on changes
|
||||
function that coordinates input parsing, tag processing, and output setting
|
||||
- **[src/inputs.ts](src/inputs.ts)**: Input parsing and validation. Exports
|
||||
`getInputs()` that reads action inputs and `Inputs` interface
|
||||
- **[src/tags.ts](src/tags.ts)**: Tag parsing and processing logic:
|
||||
- `parseTagsInput()`: Parses CSV/newline input, handles `tag:ref` syntax,
|
||||
pre-resolves all unique refs to SHAs in parallel (optimization)
|
||||
- `processTag()`: Creates/updates individual tags based on `when_exists` mode
|
||||
- `resolveRefToSha()`: Converts git refs to commit SHAs (private helper)
|
||||
- **[src/derive.ts](src/derive.ts)**: Semver parsing and tag derivation:
|
||||
- `parseSemver()`: Parses version strings into components (prefix, major,
|
||||
minor, patch, prerelease, build)
|
||||
- `renderTemplate()`: Renders Handlebars templates with semver context
|
||||
- `deriveTags()`: Derives tags from a version string using a template
|
||||
- **[src/tags.ts](src/tags.ts)**: Tag planning and execution logic:
|
||||
- `planTagOperations()`: Parses tags, pre-resolves refs to SHAs in parallel,
|
||||
plans create/update/skip operations
|
||||
- `executeTagOperation()`: Executes a single planned operation (create,
|
||||
update, or skip with logging)
|
||||
- Private helpers for tag creation, updates, and annotation handling
|
||||
- **[action.yml](action.yml)**: GitHub Action metadata (inputs/outputs)
|
||||
- **[tests/fixtures/](tests/fixtures)**: Mock implementations of @actions/core,
|
||||
@actions/github, and csv-parse for testing
|
||||
@@ -71,19 +85,44 @@ npm run package:watch # Auto-rebuild on changes
|
||||
### Tag Input Parsing
|
||||
|
||||
Uses `csv-parse/sync` to handle both CSV and newline-delimited formats. Supports
|
||||
per-tag ref overrides: `v1:main` tags `v1` to `main` branch.
|
||||
per-tag ref and annotation overrides using the format `tag:ref:annotation`:
|
||||
|
||||
- `tag` — Use default `ref` and `annotation` inputs
|
||||
- `tag:ref` — Override ref for this tag (e.g., `v1:main` tags `v1` to `main`)
|
||||
- `tag:ref:annotation` — Override both ref and annotation
|
||||
- `tag::annotation` — Override annotation only (empty ref uses default)
|
||||
|
||||
Annotations can contain colons; everything after the second colon is treated as
|
||||
the annotation text. Per-tag values override the global `ref` and `annotation`
|
||||
inputs.
|
||||
|
||||
### Tag Derivation
|
||||
|
||||
The `derive_from` input allows automatic generation of tags from a semver
|
||||
version string. Uses Handlebars templates with these placeholders:
|
||||
|
||||
- `{{prefix}}`: "v" or "V" if input had prefix, empty otherwise
|
||||
- `{{major}}`, `{{minor}}`, `{{patch}}`: Version numbers
|
||||
- `{{prerelease}}`, `{{build}}`: Optional semver components
|
||||
- `{{version}}`: Full version without prefix
|
||||
|
||||
Default template `{{prefix}}{{major}},{{prefix}}{{major}}.{{minor}}` generates
|
||||
major and minor tags (e.g., `v1.2.3` → `v1`, `v1.2`). Supports Handlebars
|
||||
conditionals like `{{#if prerelease}}...{{/if}}`.
|
||||
|
||||
### Tag Update Logic
|
||||
|
||||
1. Parse and validate inputs ([inputs.ts](src/inputs.ts))
|
||||
2. Parse tags and extract per-tag refs ([tags.ts](src/tags.ts):parseTagsInput)
|
||||
3. Pre-resolve all unique refs to SHAs in parallel (optimization)
|
||||
4. For each tag ([tags.ts](src/tags.ts):processTag):
|
||||
- If exists + update mode: Update if SHA differs
|
||||
- If exists + skip mode: Skip silently
|
||||
- If exists + fail mode: Fail action
|
||||
- If doesn't exist (404): Create it
|
||||
5. Set outputs with created/updated tag lists ([main.ts](src/main.ts))
|
||||
2. Plan all tag operations ([tags.ts](src/tags.ts):planTagOperations):
|
||||
- Parse `tag:ref:annotation` syntax and extract per-tag refs/annotations
|
||||
- Pre-resolve all unique refs to SHAs in parallel (optimization)
|
||||
- For each tag, check existence and determine operation:
|
||||
- If exists + fail mode: Fail action immediately
|
||||
- If exists + skip mode: Plan skip
|
||||
- If exists + update mode: Plan update if SHA or annotation differs
|
||||
- If doesn't exist (404): Plan create
|
||||
3. Execute each planned operation ([tags.ts](src/tags.ts):executeTagOperation)
|
||||
4. Set outputs with created/updated/skipped tag lists ([main.ts](src/main.ts))
|
||||
|
||||
### Testing Patterns
|
||||
|
||||
@@ -119,9 +158,10 @@ Mock fixtures live in `tests/fixtures/` (e.g., `core.ts` mocks @actions/core).
|
||||
`.github/workflows/ci.yml` runs:
|
||||
|
||||
1. **check-dist**: Verify bundled dist/ matches source
|
||||
2. **lint**: ESLint with GitHub formatter
|
||||
3. **release-please**: Semantic versioning releases
|
||||
4. **release-tags**: Self-referential tag updates after release
|
||||
2. **lint**: ESLint check
|
||||
3. **test**: Run Jest test suite
|
||||
4. **release-please**: Semantic versioning releases
|
||||
5. **release-tags**: Self-referential tag updates after release
|
||||
|
||||
## Release Process
|
||||
|
||||
@@ -184,16 +224,25 @@ chore(deps): bump @actions/core to v1.10.0
|
||||
|
||||
**Inputs:**
|
||||
|
||||
- `tags`: CSV/newline list, supports `tag:ref` syntax
|
||||
- `ref`: SHA/ref to tag (default: current commit)
|
||||
- `tags`: CSV/newline list, supports `tag:ref:annotation` syntax for per-tag
|
||||
overrides
|
||||
- `derive_from`: Semver version string to derive tags from (e.g., "v1.2.3")
|
||||
- `derive_from_template`: Handlebars template for tag derivation (default:
|
||||
`{{prefix}}{{major}},{{prefix}}{{major}}.{{minor}}`)
|
||||
- `ref`: Default SHA/ref to tag (default: current commit)
|
||||
- `when_exists`: update|skip|fail (default: update)
|
||||
- `annotation`: Default annotation message for tags (default: lightweight/none)
|
||||
- `dry_run`: Log planned operations without executing (default: false)
|
||||
- `github_token`: Auth token (default: github.token)
|
||||
|
||||
Either `tags` or `derive_from` (or both) must be provided.
|
||||
|
||||
**Outputs:**
|
||||
|
||||
- `tags`: All created/updated tags
|
||||
- `created`: Newly created tags
|
||||
- `updated`: Updated tags
|
||||
- `skipped`: Skipped tags (already matching or when_exists=skip)
|
||||
|
||||
## Code Style and Guidelines
|
||||
|
||||
|
||||
16
CHANGELOG.md
16
CHANGELOG.md
@@ -1,5 +1,21 @@
|
||||
# Changelog
|
||||
|
||||
## [2.3.0](https://github.com/jimeh/update-tags-action/compare/v2.2.1...v2.3.0) (2025-12-24)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **inputs/derive_from:** derive tags from provided semver string ([#79](https://github.com/jimeh/update-tags-action/issues/79)) ([1d171e9](https://github.com/jimeh/update-tags-action/commit/1d171e9f3c63639b8d0145a881a6df941200742d))
|
||||
* **inputs/dry_run:** add dry run support ([#76](https://github.com/jimeh/update-tags-action/issues/76)) ([ef41989](https://github.com/jimeh/update-tags-action/commit/ef4198907727f6ff9e5813deb5ab5a4307ab4691))
|
||||
* **inputs/tags:** support per-tag annotation overrides ([#81](https://github.com/jimeh/update-tags-action/issues/81)) ([32e66b0](https://github.com/jimeh/update-tags-action/commit/32e66b04be37383e2450d698007faa436c706ffd))
|
||||
|
||||
## [2.2.1](https://github.com/jimeh/update-tags-action/compare/v2.2.0...v2.2.1) (2025-12-22)
|
||||
|
||||
|
||||
### Documentation
|
||||
|
||||
* **AGENTS.md:** update to align with current state of project ([#71](https://github.com/jimeh/update-tags-action/issues/71)) ([a25f37f](https://github.com/jimeh/update-tags-action/commit/a25f37f9e6300ff3fcdf28424cdec9d99944522f))
|
||||
|
||||
## [2.2.0](https://github.com/jimeh/update-tags-action/compare/v2.1.1...v2.2.0) (2025-10-29)
|
||||
|
||||
|
||||
|
||||
195
README.md
195
README.md
@@ -22,23 +22,50 @@ to move its own major and minor tags.
|
||||
|
||||
### Basic
|
||||
|
||||
<!-- x-release-please-start-minor -->
|
||||
|
||||
```yaml
|
||||
- uses: jimeh/update-tags-action@v2
|
||||
with:
|
||||
tags: v2,v2.2
|
||||
tags: v1,v1.2
|
||||
```
|
||||
|
||||
```yaml
|
||||
- uses: jimeh/update-tags-action@v2
|
||||
with:
|
||||
tags: |
|
||||
v2
|
||||
v2.2
|
||||
v1
|
||||
v1.2
|
||||
```
|
||||
|
||||
<!-- x-release-please-end -->
|
||||
### Deriving Tags from Version
|
||||
|
||||
Automatically derive major and minor tags from a semver version string:
|
||||
|
||||
```yaml
|
||||
- uses: jimeh/update-tags-action@v2
|
||||
with:
|
||||
derive_from: v1.2.3
|
||||
# Creates tags: v1, v1.2
|
||||
```
|
||||
|
||||
With a custom template (major tag only):
|
||||
|
||||
```yaml
|
||||
- uses: jimeh/update-tags-action@v2
|
||||
with:
|
||||
derive_from: v1.2.3
|
||||
derive_from_template: '{{prefix}}{{major}}'
|
||||
# Creates tag: v1
|
||||
```
|
||||
|
||||
Combine derived tags with explicit tags:
|
||||
|
||||
```yaml
|
||||
- uses: jimeh/update-tags-action@v2
|
||||
with:
|
||||
derive_from: v1.2.3
|
||||
tags: latest
|
||||
# Creates tags: latest, v1, v1.2
|
||||
```
|
||||
|
||||
### With Release Please
|
||||
|
||||
@@ -65,8 +92,7 @@ jobs:
|
||||
if: ${{ github.ref == 'refs/heads/main' }}
|
||||
outputs:
|
||||
release_created: ${{ steps.release-please.outputs.release_created }}
|
||||
major: ${{ steps.release-please.outputs.major }}
|
||||
minor: ${{ steps.release-please.outputs.minor }}
|
||||
tag_name: ${{ steps.release-please.outputs.tag_name }}
|
||||
permissions:
|
||||
contents: write
|
||||
issues: write
|
||||
@@ -79,8 +105,6 @@ jobs:
|
||||
And finally a job to create MAJOR and MINOR release tags, which only runs when
|
||||
release-please reports having created a release:
|
||||
|
||||
<!-- x-release-please-start-major -->
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
# [...]
|
||||
@@ -93,26 +117,153 @@ jobs:
|
||||
steps:
|
||||
- uses: jimeh/update-tags-action@v2
|
||||
with:
|
||||
tags: |
|
||||
v${{ needs.release-please.outputs.major }}
|
||||
v${{ needs.release-please.outputs.major }}.${{ needs.release-please.outputs.minor }}
|
||||
derive_from: ${{ needs.release-please.outputs.tag_name }}
|
||||
# Creates tags: v1, v1.2 (for tag_name v1.2.3)
|
||||
```
|
||||
|
||||
<!-- x-release-please-end -->
|
||||
|
||||
<!-- action-docs-inputs source="action.yml" -->
|
||||
|
||||
## Inputs
|
||||
|
||||
| name | description | required | default |
|
||||
| -------------- | --------------------------------------------------------------------------------------------------------------------- | -------- | --------------------- |
|
||||
| `tags` | <p>List/CSV of tags to create/update.</p> | `true` | `""` |
|
||||
| `ref` | <p>The SHA or ref to tag. Defaults to SHA of current commit.</p> | `false` | `${{ github.sha }}` |
|
||||
| `when_exists` | <p>What to do if the tag already exists. Must be one of 'update', 'skip', or 'fail'.</p> | `false` | `update` |
|
||||
| `annotation` | <p>Optional annotation message for tags. If provided, creates annotated tags. If empty, creates lightweight tags.</p> | `false` | `""` |
|
||||
| `github_token` | <p>The GitHub token to use for authentication.</p> | `false` | `${{ github.token }}` |
|
||||
| name | description | required | default |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------- | --------------------------------------------------- |
|
||||
| `tags` | <p>List/CSV of tags to create/update. Supports per-tag ref and annotation overrides using the format 'tag:ref:annotation'. Use 'tag::annotation' to specify an annotation with the default ref.</p> | `false` | `""` |
|
||||
| `derive_from` | <p>Semver version string to derive tags from (e.g., 'v1.2.3'). When provided, generates tags using <code>derive_from_template</code> input. Default template will produce major and minor tags. (e.g., 'v1', 'v1.2')</p> | `false` | `""` |
|
||||
| `derive_from_template` | <p>Handlebars template for deriving tags from the <code>derive_from</code> input. Uses the same format as the <code>tags</code> input, and supports the following handlebars placeholders: <code>{{prefix}}</code>, <code>{{major}}</code>, <code>{{minor}}</code>, <code>{{patch}}</code>, <code>{{prerelease}}</code>, <code>{{build}}</code>, <code>{{version}}</code>.</p> | `false` | `{{prefix}}{{major}},{{prefix}}{{major}}.{{minor}}` |
|
||||
| `ref` | <p>The SHA or ref to tag. Defaults to SHA of current commit.</p> | `false` | `${{ github.sha }}` |
|
||||
| `when_exists` | <p>What to do if the tag already exists. Must be one of 'update', 'skip', or 'fail'.</p> | `false` | `update` |
|
||||
| `annotation` | <p>Optional default annotation message for tags. If provided, creates annotated tags. If empty, creates lightweight tags. Can be overridden per-tag using the 'tag:ref:annotation' syntax in the tags input.</p> | `false` | `""` |
|
||||
| `dry_run` | <p>If true, logs planned operations without executing them.</p> | `false` | `false` |
|
||||
| `github_token` | <p>The GitHub token to use for authentication.</p> | `false` | `${{ github.token }}` |
|
||||
|
||||
<!-- action-docs-inputs source="action.yml" -->
|
||||
|
||||
### Tag Input Syntax
|
||||
|
||||
The `tags` input accepts a comma or newline-delimited list of tags. Each tag
|
||||
specification supports optional per-tag ref and annotation overrides using the
|
||||
format:
|
||||
|
||||
```
|
||||
tag[:ref[:annotation]]
|
||||
```
|
||||
|
||||
| Format | Description |
|
||||
| -------------------- | ----------------------------------------------- |
|
||||
| `tag` | Tag using default `ref` and `annotation` inputs |
|
||||
| `tag:ref` | Tag using specified ref, default annotation |
|
||||
| `tag:ref:annotation` | Tag using specified ref and annotation |
|
||||
| `tag::annotation` | Tag using default ref with specified annotation |
|
||||
|
||||
**Per-tag refs** allow different tags to point to different commits:
|
||||
|
||||
```yaml
|
||||
- uses: jimeh/update-tags-action@v2
|
||||
with:
|
||||
tags: |
|
||||
v1:main
|
||||
v2:develop
|
||||
```
|
||||
|
||||
**Per-tag annotations** allow different annotation messages for each tag:
|
||||
|
||||
```yaml
|
||||
- uses: jimeh/update-tags-action@v2
|
||||
with:
|
||||
tags: |
|
||||
v1:main:Latest v1.x release
|
||||
v1.2:main:Latest v1.2.x release
|
||||
```
|
||||
|
||||
Use `tag::annotation` to specify an annotation while using the default ref:
|
||||
|
||||
```yaml
|
||||
- uses: jimeh/update-tags-action@v2
|
||||
with:
|
||||
tags: |
|
||||
v1::This is the v1 tag annotation
|
||||
v1.2::This is the v1.2 tag annotation
|
||||
```
|
||||
|
||||
Per-tag values override the global `ref` and `annotation` inputs:
|
||||
|
||||
```yaml
|
||||
- uses: jimeh/update-tags-action@v2
|
||||
with:
|
||||
tags: |
|
||||
v1:main:Custom annotation for v1
|
||||
v2
|
||||
ref: develop
|
||||
annotation: Default annotation for tags without per-tag override
|
||||
# v1 -> main with "Custom annotation for v1"
|
||||
# v2 -> develop with "Default annotation..."
|
||||
```
|
||||
|
||||
Annotations can contain colons (everything after the second colon is the
|
||||
annotation):
|
||||
|
||||
```yaml
|
||||
- uses: jimeh/update-tags-action@v2
|
||||
with:
|
||||
tags: |
|
||||
v1:main:Release: version 1.0.0
|
||||
# Annotation will be "Release: version 1.0.0"
|
||||
```
|
||||
|
||||
### Derive Template Syntax
|
||||
|
||||
The `derive_from_template` input uses [Handlebars](https://handlebarsjs.com/)
|
||||
for template rendering. Splitting the template into separate tags by comma or
|
||||
newline is done after the template is rendered.
|
||||
|
||||
Available placeholders:
|
||||
|
||||
| Placeholder | Description |
|
||||
| ---------------- | ----------------------------------------------------- |
|
||||
| `{{prefix}}` | `v` or `V` if input had a prefix, empty otherwise |
|
||||
| `{{major}}` | Major version number |
|
||||
| `{{minor}}` | Minor version number |
|
||||
| `{{patch}}` | Patch version number |
|
||||
| `{{prerelease}}` | Prerelease identifier (e.g., `beta.1`), empty if none |
|
||||
| `{{build}}` | Build metadata (e.g., `build.123`), empty if none |
|
||||
| `{{version}}` | Full version string without prefix |
|
||||
|
||||
#### Conditional Sections
|
||||
|
||||
Use Handlebars `{{#if}}` blocks to include content only when a variable has a
|
||||
value. This is useful for optional components like prerelease or build metadata:
|
||||
|
||||
```yaml
|
||||
- uses: jimeh/update-tags-action@v2
|
||||
with:
|
||||
derive_from: v1.2.3-beta.1
|
||||
derive_from_template: |
|
||||
{{prefix}}{{major}}{{#if prerelease}}-{{prerelease}}{{/if}}
|
||||
# Creates tag: v1-beta.1
|
||||
```
|
||||
|
||||
For a stable release without prerelease:
|
||||
|
||||
```yaml
|
||||
- uses: jimeh/update-tags-action@v2
|
||||
with:
|
||||
derive_from: v1.2.3
|
||||
derive_from_template: |
|
||||
{{prefix}}{{major}}{{#if prerelease}}-{{prerelease}}{{/if}}
|
||||
# Creates tag: v1 (prerelease section omitted)
|
||||
```
|
||||
|
||||
You can also use `{{#unless}}` for inverse logic:
|
||||
|
||||
```yaml
|
||||
- uses: jimeh/update-tags-action@v2
|
||||
with:
|
||||
derive_from: v1.2.3
|
||||
derive_from_template: |
|
||||
{{prefix}}{{major}}{{#unless prerelease}}-stable{{/unless}}
|
||||
# Creates tag: v1-stable (only for non-prerelease versions)
|
||||
```
|
||||
|
||||
<!-- action-docs-outputs source="action.yml" -->
|
||||
|
||||
## Outputs
|
||||
|
||||
31
action.yml
31
action.yml
@@ -8,8 +8,25 @@ branding:
|
||||
|
||||
inputs:
|
||||
tags:
|
||||
description: "List/CSV of tags to create/update."
|
||||
required: true
|
||||
description: >-
|
||||
List/CSV of tags to create/update. Supports per-tag ref and annotation
|
||||
overrides using the format 'tag:ref:annotation'. Use 'tag::annotation' to
|
||||
specify an annotation with the default ref.
|
||||
required: false
|
||||
derive_from:
|
||||
description: >-
|
||||
Semver version string to derive tags from (e.g., 'v1.2.3'). When provided,
|
||||
generates tags using `derive_from_template` input. Default template will
|
||||
produce major and minor tags. (e.g., 'v1', 'v1.2')
|
||||
required: false
|
||||
derive_from_template:
|
||||
description: >-
|
||||
Handlebars template for deriving tags from the `derive_from` input. Uses
|
||||
the same format as the `tags` input, and supports the following handlebars
|
||||
placeholders: `{{prefix}}`, `{{major}}`, `{{minor}}`, `{{patch}}`,
|
||||
`{{prerelease}}`, `{{build}}`, `{{version}}`.
|
||||
required: false
|
||||
default: "{{prefix}}{{major}},{{prefix}}{{major}}.{{minor}}"
|
||||
ref:
|
||||
description: "The SHA or ref to tag. Defaults to SHA of current commit."
|
||||
required: false
|
||||
@@ -22,10 +39,16 @@ inputs:
|
||||
default: "update"
|
||||
annotation:
|
||||
description: >-
|
||||
Optional annotation message for tags. If provided, creates annotated tags.
|
||||
If empty, creates lightweight tags.
|
||||
Optional default annotation message for tags. If provided, creates
|
||||
annotated tags. If empty, creates lightweight tags. Can be overridden
|
||||
per-tag using the 'tag:ref:annotation' syntax in the tags input.
|
||||
required: false
|
||||
default: ""
|
||||
dry_run:
|
||||
description: >-
|
||||
If true, logs planned operations without executing them.
|
||||
required: false
|
||||
default: "false"
|
||||
github_token:
|
||||
description: "The GitHub token to use for authentication."
|
||||
required: false
|
||||
|
||||
12548
dist/index.js
generated
vendored
12548
dist/index.js
generated
vendored
File diff suppressed because one or more lines are too long
2
dist/index.js.map
generated
vendored
2
dist/index.js.map
generated
vendored
File diff suppressed because one or more lines are too long
534
package-lock.json
generated
534
package-lock.json
generated
@@ -1,41 +1,44 @@
|
||||
{
|
||||
"name": "update-tags-action",
|
||||
"version": "2.2.0",
|
||||
"version": "2.3.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "update-tags-action",
|
||||
"version": "2.2.0",
|
||||
"version": "2.3.0",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.11.1",
|
||||
"@actions/github": "^6.0.1",
|
||||
"csv-parse": "^6.1.0"
|
||||
"@actions/core": "^2.0.2",
|
||||
"@actions/github": "^7.0.0",
|
||||
"csv-parse": "^6.1.0",
|
||||
"handlebars": "^4.7.8",
|
||||
"semver": "^7.7.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/compat": "^2.0.0",
|
||||
"@eslint/compat": "^2.0.1",
|
||||
"@jest/globals": "^30.2.0",
|
||||
"@rollup/plugin-commonjs": "^29.0.0",
|
||||
"@rollup/plugin-node-resolve": "^16.0.3",
|
||||
"@rollup/plugin-typescript": "^12.3.0",
|
||||
"@types/jest": "^30.0.0",
|
||||
"@types/node": "^24.10.1",
|
||||
"@typescript-eslint/eslint-plugin": "^8.47.0",
|
||||
"@types/node": "^25.0.9",
|
||||
"@types/semver": "^7.7.1",
|
||||
"@typescript-eslint/eslint-plugin": "^8.53.0",
|
||||
"@typescript-eslint/parser": "^8.46.2",
|
||||
"action-docs": "^2.5.1",
|
||||
"eslint": "^9.39.1",
|
||||
"eslint": "^9.39.2",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-import-resolver-typescript": "^4.4.4",
|
||||
"eslint-plugin-import": "^2.32.0",
|
||||
"eslint-plugin-jest": "^29.1.0",
|
||||
"eslint-plugin-prettier": "^5.5.4",
|
||||
"eslint-plugin-jest": "^29.12.1",
|
||||
"eslint-plugin-prettier": "^5.5.5",
|
||||
"jest": "^30.2.0",
|
||||
"make-coverage-badge": "^1.2.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^3.6.2",
|
||||
"prettier": "^3.8.0",
|
||||
"prettier-eslint": "^16.4.2",
|
||||
"rollup": "^4.53.3",
|
||||
"ts-jest": "^29.4.5",
|
||||
"rollup": "^4.55.1",
|
||||
"ts-jest": "^29.4.6",
|
||||
"ts-jest-resolver": "^2.0.1",
|
||||
"typescript": "^5.9.3"
|
||||
},
|
||||
@@ -44,31 +47,31 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/core": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.11.1.tgz",
|
||||
"integrity": "sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A==",
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@actions/core/-/core-2.0.2.tgz",
|
||||
"integrity": "sha512-Ast1V7yHbGAhplAsuVlnb/5J8Mtr/Zl6byPPL+Qjq3lmfIgWF1ak1iYfF/079cRERiuTALTXkSuEUdZeDCfGtA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/exec": "^1.1.1",
|
||||
"@actions/http-client": "^2.0.1"
|
||||
"@actions/exec": "^2.0.0",
|
||||
"@actions/http-client": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/exec": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz",
|
||||
"integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==",
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-2.0.0.tgz",
|
||||
"integrity": "sha512-k8ngrX2voJ/RIN6r9xB82NVqKpnMRtxDoiO+g3olkIUpQNqjArXrCQceduQZCQj3P3xm32pChRLqRrtXTlqhIw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/io": "^1.0.1"
|
||||
"@actions/io": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/github": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@actions/github/-/github-6.0.1.tgz",
|
||||
"integrity": "sha512-xbZVcaqD4XnQAe35qSQqskb3SqIAfRyLBrHMd/8TuL7hJSz2QtbDwnNM8zWx4zO5l2fnGtseNE3MbEvD7BxVMw==",
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@actions/github/-/github-7.0.0.tgz",
|
||||
"integrity": "sha512-PyGODO938aoBTZd/IfN/+e+Pd5hUcVpyf+thm4CPESLeqhdSkq5QwMTGX9v84XHE1ifmHWBQ60KB8kIgm96opw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/http-client": "^2.2.0",
|
||||
"@actions/http-client": "^3.0.1",
|
||||
"@octokit/core": "^5.0.1",
|
||||
"@octokit/plugin-paginate-rest": "^9.2.2",
|
||||
"@octokit/plugin-rest-endpoint-methods": "^10.4.0",
|
||||
@@ -78,19 +81,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/http-client": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.2.3.tgz",
|
||||
"integrity": "sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==",
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-3.0.1.tgz",
|
||||
"integrity": "sha512-SbGS8c/vySbNO3kjFgSW77n83C4MQx/Yoe+b1hAdpuvfHxnkHzDq2pWljUpAA56Si1Gae/7zjeZsV0CYjmLo/w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tunnel": "^0.0.6",
|
||||
"undici": "^5.25.4"
|
||||
"undici": "^5.28.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/io": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz",
|
||||
"integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==",
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@actions/io/-/io-2.0.0.tgz",
|
||||
"integrity": "sha512-Jv33IN09XLO+0HS79aaODsvIRyduiF7NY/F6LYeK5oeUmrsz7aFdRphQjFoESF4jS7lMauDOttKALcpapVDIAg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
@@ -644,9 +647,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint-community/eslint-utils": {
|
||||
"version": "4.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz",
|
||||
"integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==",
|
||||
"version": "4.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
|
||||
"integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -673,13 +676,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/compat": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-2.0.0.tgz",
|
||||
"integrity": "sha512-T9AfE1G1uv4wwq94ozgTGio5EUQBqAVe1X9qsQtSNVEYW6j3hvtZVm8Smr4qL1qDPFg+lOB2cL5RxTRMzq4CTA==",
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-2.0.1.tgz",
|
||||
"integrity": "sha512-yl/JsgplclzuvGFNqwNYV4XNPhP3l62ZOP9w/47atNAdmDtIFCx6X7CSk/SlWUuBGkT4Et/5+UD+WyvX2iiIWA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@eslint/core": "^1.0.0"
|
||||
"@eslint/core": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^20.19.0 || ^22.13.0 || >=24"
|
||||
@@ -694,9 +697,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/compat/node_modules/@eslint/core": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.0.0.tgz",
|
||||
"integrity": "sha512-PRfWP+8FOldvbApr6xL7mNCw4cJcSTq4GA7tYbgq15mRb0kWKO/wEB2jr+uwjFH3sZvEZneZyCUGTxsv4Sahyw==",
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.0.1.tgz",
|
||||
"integrity": "sha512-r18fEAj9uCk+VjzGt2thsbOmychS+4kxI14spVNibUO2vqKX7obOG+ymZljAwuPZl+S3clPGwCwTDtrdqTiY6Q==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
@@ -830,9 +833,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/js": {
|
||||
"version": "9.39.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz",
|
||||
"integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==",
|
||||
"version": "9.39.2",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz",
|
||||
"integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -1838,9 +1841,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz",
|
||||
"integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==",
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.55.1.tgz",
|
||||
"integrity": "sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -1852,9 +1855,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm64": {
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz",
|
||||
"integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==",
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.55.1.tgz",
|
||||
"integrity": "sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1866,9 +1869,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz",
|
||||
"integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==",
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.55.1.tgz",
|
||||
"integrity": "sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1880,9 +1883,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-x64": {
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz",
|
||||
"integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==",
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.55.1.tgz",
|
||||
"integrity": "sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1894,9 +1897,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-arm64": {
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz",
|
||||
"integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==",
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.55.1.tgz",
|
||||
"integrity": "sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1908,9 +1911,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-x64": {
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz",
|
||||
"integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==",
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.55.1.tgz",
|
||||
"integrity": "sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1922,9 +1925,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz",
|
||||
"integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==",
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.55.1.tgz",
|
||||
"integrity": "sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -1936,9 +1939,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz",
|
||||
"integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==",
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.55.1.tgz",
|
||||
"integrity": "sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -1950,9 +1953,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz",
|
||||
"integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==",
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.55.1.tgz",
|
||||
"integrity": "sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1964,9 +1967,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz",
|
||||
"integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==",
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.55.1.tgz",
|
||||
"integrity": "sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1978,9 +1981,23 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-loong64-gnu": {
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz",
|
||||
"integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==",
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.55.1.tgz",
|
||||
"integrity": "sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-loong64-musl": {
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.55.1.tgz",
|
||||
"integrity": "sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
@@ -1992,9 +2009,23 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz",
|
||||
"integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==",
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.55.1.tgz",
|
||||
"integrity": "sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-ppc64-musl": {
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.55.1.tgz",
|
||||
"integrity": "sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@@ -2006,9 +2037,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz",
|
||||
"integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==",
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.55.1.tgz",
|
||||
"integrity": "sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
@@ -2020,9 +2051,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz",
|
||||
"integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==",
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.55.1.tgz",
|
||||
"integrity": "sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
@@ -2034,9 +2065,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz",
|
||||
"integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==",
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.55.1.tgz",
|
||||
"integrity": "sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
@@ -2048,9 +2079,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz",
|
||||
"integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==",
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.55.1.tgz",
|
||||
"integrity": "sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -2062,9 +2093,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz",
|
||||
"integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==",
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.55.1.tgz",
|
||||
"integrity": "sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -2075,10 +2106,24 @@
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-openbsd-x64": {
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.55.1.tgz",
|
||||
"integrity": "sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-openharmony-arm64": {
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz",
|
||||
"integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==",
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.55.1.tgz",
|
||||
"integrity": "sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -2090,9 +2135,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz",
|
||||
"integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==",
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.55.1.tgz",
|
||||
"integrity": "sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -2104,9 +2149,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz",
|
||||
"integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==",
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.55.1.tgz",
|
||||
"integrity": "sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -2118,9 +2163,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-gnu": {
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz",
|
||||
"integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==",
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.55.1.tgz",
|
||||
"integrity": "sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -2132,9 +2177,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz",
|
||||
"integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==",
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.55.1.tgz",
|
||||
"integrity": "sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -2305,9 +2350,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "24.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz",
|
||||
"integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
|
||||
"version": "25.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.9.tgz",
|
||||
"integrity": "sha512-/rpCXHlCWeqClNBwUhDcusJxXYDjZTyE8v5oTO7WbL8eij2nKhUeU89/6xgjU7N4/Vh3He0BtyhJdQbDyhiXAw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -2321,6 +2366,13 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/semver": {
|
||||
"version": "7.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz",
|
||||
"integrity": "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/stack-utils": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz",
|
||||
@@ -2346,21 +2398,20 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.47.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.47.0.tgz",
|
||||
"integrity": "sha512-fe0rz9WJQ5t2iaLfdbDc9T80GJy0AeO453q8C3YCilnGozvOyCG5t+EZtg7j7D88+c3FipfP/x+wzGnh1xp8ZA==",
|
||||
"version": "8.53.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.53.0.tgz",
|
||||
"integrity": "sha512-eEXsVvLPu8Z4PkFibtuFJLJOTAV/nPdgtSjkGoPpddpFk3/ym2oy97jynY6ic2m6+nc5M8SE1e9v/mHKsulcJg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.10.0",
|
||||
"@typescript-eslint/scope-manager": "8.47.0",
|
||||
"@typescript-eslint/type-utils": "8.47.0",
|
||||
"@typescript-eslint/utils": "8.47.0",
|
||||
"@typescript-eslint/visitor-keys": "8.47.0",
|
||||
"graphemer": "^1.4.0",
|
||||
"ignore": "^7.0.0",
|
||||
"@eslint-community/regexpp": "^4.12.2",
|
||||
"@typescript-eslint/scope-manager": "8.53.0",
|
||||
"@typescript-eslint/type-utils": "8.53.0",
|
||||
"@typescript-eslint/utils": "8.53.0",
|
||||
"@typescript-eslint/visitor-keys": "8.53.0",
|
||||
"ignore": "^7.0.5",
|
||||
"natural-compare": "^1.4.0",
|
||||
"ts-api-utils": "^2.1.0"
|
||||
"ts-api-utils": "^2.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -2370,23 +2421,23 @@
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@typescript-eslint/parser": "^8.47.0",
|
||||
"@typescript-eslint/parser": "^8.53.0",
|
||||
"eslint": "^8.57.0 || ^9.0.0",
|
||||
"typescript": ">=4.8.4 <6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "8.47.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.47.0.tgz",
|
||||
"integrity": "sha512-lJi3PfxVmo0AkEY93ecfN+r8SofEqZNGByvHAI3GBLrvt1Cw6H5k1IM02nSzu0RfUafr2EvFSw0wAsZgubNplQ==",
|
||||
"version": "8.53.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.53.0.tgz",
|
||||
"integrity": "sha512-npiaib8XzbjtzS2N4HlqPvlpxpmZ14FjSJrteZpPxGUaYPlvhzlzUZ4mZyABo0EFrOWnvyd0Xxroq//hKhtAWg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.47.0",
|
||||
"@typescript-eslint/types": "8.47.0",
|
||||
"@typescript-eslint/typescript-estree": "8.47.0",
|
||||
"@typescript-eslint/visitor-keys": "8.47.0",
|
||||
"debug": "^4.3.4"
|
||||
"@typescript-eslint/scope-manager": "8.53.0",
|
||||
"@typescript-eslint/types": "8.53.0",
|
||||
"@typescript-eslint/typescript-estree": "8.53.0",
|
||||
"@typescript-eslint/visitor-keys": "8.53.0",
|
||||
"debug": "^4.4.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -2401,15 +2452,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/project-service": {
|
||||
"version": "8.47.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.47.0.tgz",
|
||||
"integrity": "sha512-2X4BX8hUeB5JcA1TQJ7GjcgulXQ+5UkNb0DL8gHsHUHdFoiCTJoYLTpib3LtSDPZsRET5ygN4qqIWrHyYIKERA==",
|
||||
"version": "8.53.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.53.0.tgz",
|
||||
"integrity": "sha512-Bl6Gdr7NqkqIP5yP9z1JU///Nmes4Eose6L1HwpuVHwScgDPPuEWbUVhvlZmb8hy0vX9syLk5EGNL700WcBlbg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/tsconfig-utils": "^8.47.0",
|
||||
"@typescript-eslint/types": "^8.47.0",
|
||||
"debug": "^4.3.4"
|
||||
"@typescript-eslint/tsconfig-utils": "^8.53.0",
|
||||
"@typescript-eslint/types": "^8.53.0",
|
||||
"debug": "^4.4.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -2423,14 +2474,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "8.47.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.47.0.tgz",
|
||||
"integrity": "sha512-a0TTJk4HXMkfpFkL9/WaGTNuv7JWfFTQFJd6zS9dVAjKsojmv9HT55xzbEpnZoY+VUb+YXLMp+ihMLz/UlZfDg==",
|
||||
"version": "8.53.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.53.0.tgz",
|
||||
"integrity": "sha512-kWNj3l01eOGSdVBnfAF2K1BTh06WS0Yet6JUgb9Cmkqaz3Jlu0fdVUjj9UI8gPidBWSMqDIglmEXifSgDT/D0g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.47.0",
|
||||
"@typescript-eslint/visitor-keys": "8.47.0"
|
||||
"@typescript-eslint/types": "8.53.0",
|
||||
"@typescript-eslint/visitor-keys": "8.53.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -2441,9 +2492,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/tsconfig-utils": {
|
||||
"version": "8.47.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.47.0.tgz",
|
||||
"integrity": "sha512-ybUAvjy4ZCL11uryalkKxuT3w3sXJAuWhOoGS3T/Wu+iUu1tGJmk5ytSY8gbdACNARmcYEB0COksD2j6hfGK2g==",
|
||||
"version": "8.53.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.53.0.tgz",
|
||||
"integrity": "sha512-K6Sc0R5GIG6dNoPdOooQ+KtvT5KCKAvTcY8h2rIuul19vxH5OTQk7ArKkd4yTzkw66WnNY0kPPzzcmWA+XRmiA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -2458,17 +2509,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "8.47.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.47.0.tgz",
|
||||
"integrity": "sha512-QC9RiCmZ2HmIdCEvhd1aJELBlD93ErziOXXlHEZyuBo3tBiAZieya0HLIxp+DoDWlsQqDawyKuNEhORyku+P8A==",
|
||||
"version": "8.53.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.53.0.tgz",
|
||||
"integrity": "sha512-BBAUhlx7g4SmcLhn8cnbxoxtmS7hcq39xKCgiutL3oNx1TaIp+cny51s8ewnKMpVUKQUGb41RAUWZ9kxYdovuw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.47.0",
|
||||
"@typescript-eslint/typescript-estree": "8.47.0",
|
||||
"@typescript-eslint/utils": "8.47.0",
|
||||
"debug": "^4.3.4",
|
||||
"ts-api-utils": "^2.1.0"
|
||||
"@typescript-eslint/types": "8.53.0",
|
||||
"@typescript-eslint/typescript-estree": "8.53.0",
|
||||
"@typescript-eslint/utils": "8.53.0",
|
||||
"debug": "^4.4.3",
|
||||
"ts-api-utils": "^2.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -2483,9 +2534,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "8.47.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.47.0.tgz",
|
||||
"integrity": "sha512-nHAE6bMKsizhA2uuYZbEbmp5z2UpffNrPEqiKIeN7VsV6UY/roxanWfoRrf6x/k9+Obf+GQdkm0nPU+vnMXo9A==",
|
||||
"version": "8.53.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.53.0.tgz",
|
||||
"integrity": "sha512-Bmh9KX31Vlxa13+PqPvt4RzKRN1XORYSLlAE+sO1i28NkisGbTtSLFVB3l7PWdHtR3E0mVMuC7JilWJ99m2HxQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -2497,22 +2548,21 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.47.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.47.0.tgz",
|
||||
"integrity": "sha512-k6ti9UepJf5NpzCjH31hQNLHQWupTRPhZ+KFF8WtTuTpy7uHPfeg2NM7cP27aCGajoEplxJDFVCEm9TGPYyiVg==",
|
||||
"version": "8.53.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.53.0.tgz",
|
||||
"integrity": "sha512-pw0c0Gdo7Z4xOG987u3nJ8akL9093yEEKv8QTJ+Bhkghj1xyj8cgPaavlr9rq8h7+s6plUJ4QJYw2gCZodqmGw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/project-service": "8.47.0",
|
||||
"@typescript-eslint/tsconfig-utils": "8.47.0",
|
||||
"@typescript-eslint/types": "8.47.0",
|
||||
"@typescript-eslint/visitor-keys": "8.47.0",
|
||||
"debug": "^4.3.4",
|
||||
"fast-glob": "^3.3.2",
|
||||
"is-glob": "^4.0.3",
|
||||
"minimatch": "^9.0.4",
|
||||
"semver": "^7.6.0",
|
||||
"ts-api-utils": "^2.1.0"
|
||||
"@typescript-eslint/project-service": "8.53.0",
|
||||
"@typescript-eslint/tsconfig-utils": "8.53.0",
|
||||
"@typescript-eslint/types": "8.53.0",
|
||||
"@typescript-eslint/visitor-keys": "8.53.0",
|
||||
"debug": "^4.4.3",
|
||||
"minimatch": "^9.0.5",
|
||||
"semver": "^7.7.3",
|
||||
"tinyglobby": "^0.2.15",
|
||||
"ts-api-utils": "^2.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -2526,16 +2576,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "8.47.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.47.0.tgz",
|
||||
"integrity": "sha512-g7XrNf25iL4TJOiPqatNuaChyqt49a/onq5YsJ9+hXeugK+41LVg7AxikMfM02PC6jbNtZLCJj6AUcQXJS/jGQ==",
|
||||
"version": "8.53.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.53.0.tgz",
|
||||
"integrity": "sha512-XDY4mXTez3Z1iRDI5mbRhH4DFSt46oaIFsLg+Zn97+sYrXACziXSQcSelMybnVZ5pa1P6xYkPr5cMJyunM1ZDA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.7.0",
|
||||
"@typescript-eslint/scope-manager": "8.47.0",
|
||||
"@typescript-eslint/types": "8.47.0",
|
||||
"@typescript-eslint/typescript-estree": "8.47.0"
|
||||
"@eslint-community/eslint-utils": "^4.9.1",
|
||||
"@typescript-eslint/scope-manager": "8.53.0",
|
||||
"@typescript-eslint/types": "8.53.0",
|
||||
"@typescript-eslint/typescript-estree": "8.53.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -2550,13 +2600,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "8.47.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.47.0.tgz",
|
||||
"integrity": "sha512-SIV3/6eftCy1bNzCQoPmbWsRLujS8t5iDIZ4spZOBHqrM+yfX2ogg8Tt3PDTAVKw3sSCiUgg30uOAvK2r9zGjQ==",
|
||||
"version": "8.53.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.53.0.tgz",
|
||||
"integrity": "sha512-LZ2NqIHFhvFwxG0qZeLL9DvdNAHPGCY5dIRwBhyYeU+LfLhcStE1ImjsuTG/WaVh3XysGaeLW8Rqq7cGkPCFvw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.47.0",
|
||||
"@typescript-eslint/types": "8.53.0",
|
||||
"eslint-visitor-keys": "^4.2.1"
|
||||
},
|
||||
"engines": {
|
||||
@@ -4123,9 +4173,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "9.39.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz",
|
||||
"integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==",
|
||||
"version": "9.39.2",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz",
|
||||
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -4135,7 +4185,7 @@
|
||||
"@eslint/config-helpers": "^0.4.2",
|
||||
"@eslint/core": "^0.17.0",
|
||||
"@eslint/eslintrc": "^3.3.1",
|
||||
"@eslint/js": "9.39.1",
|
||||
"@eslint/js": "9.39.2",
|
||||
"@eslint/plugin-kit": "^0.4.1",
|
||||
"@humanfs/node": "^0.16.6",
|
||||
"@humanwhocodes/module-importer": "^1.0.1",
|
||||
@@ -4387,9 +4437,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-jest": {
|
||||
"version": "29.1.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-29.1.0.tgz",
|
||||
"integrity": "sha512-LabxXbASXVjguqL+kBHTPMf3gUeSqwH4fsrEyHTY/MCs42I/p9+ctg09SJpYiD8eGaIsP6GwYr5xW6xWS9XgZg==",
|
||||
"version": "29.12.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-29.12.1.tgz",
|
||||
"integrity": "sha512-Rxo7r4jSANMBkXLICJKS0gjacgyopfNAsoS0e3R9AHnjoKuQOaaPfmsDJPi8UWwygI099OV/K/JhpYRVkxD4AA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -4413,14 +4463,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-prettier": {
|
||||
"version": "5.5.4",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.4.tgz",
|
||||
"integrity": "sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==",
|
||||
"version": "5.5.5",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.5.tgz",
|
||||
"integrity": "sha512-hscXkbqUZ2sPithAuLm5MXL+Wph+U7wHngPBv9OMWwlP8iaflyxpjTYZkmdgB4/vPIhemRlBEoLrH7UC1n7aUw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prettier-linter-helpers": "^1.0.0",
|
||||
"synckit": "^0.11.7"
|
||||
"prettier-linter-helpers": "^1.0.1",
|
||||
"synckit": "^0.11.12"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.18.0 || >=16.0.0"
|
||||
@@ -5198,7 +5248,6 @@
|
||||
"version": "4.7.8",
|
||||
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz",
|
||||
"integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"minimist": "^1.2.5",
|
||||
@@ -7075,7 +7124,6 @@
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
|
||||
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
@@ -7135,7 +7183,6 @@
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
|
||||
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/nice-try": {
|
||||
@@ -7862,9 +7909,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz",
|
||||
"integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
|
||||
"version": "3.8.0",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.0.tgz",
|
||||
"integrity": "sha512-yEPsovQfpxYfgWNhCfECjG5AQaO+K3dp6XERmOepyPDVqcJm+bjyCVO3pmU+nAPe0N5dDvekfGezt/EIiRe1TA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
@@ -8379,9 +8426,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prettier-linter-helpers": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
|
||||
"integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.1.tgz",
|
||||
"integrity": "sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -8751,9 +8798,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "4.53.3",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz",
|
||||
"integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==",
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.55.1.tgz",
|
||||
"integrity": "sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -8767,28 +8814,31 @@
|
||||
"npm": ">=8.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rollup/rollup-android-arm-eabi": "4.53.3",
|
||||
"@rollup/rollup-android-arm64": "4.53.3",
|
||||
"@rollup/rollup-darwin-arm64": "4.53.3",
|
||||
"@rollup/rollup-darwin-x64": "4.53.3",
|
||||
"@rollup/rollup-freebsd-arm64": "4.53.3",
|
||||
"@rollup/rollup-freebsd-x64": "4.53.3",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.53.3",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.53.3",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.53.3",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.53.3",
|
||||
"@rollup/rollup-linux-loong64-gnu": "4.53.3",
|
||||
"@rollup/rollup-linux-ppc64-gnu": "4.53.3",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.53.3",
|
||||
"@rollup/rollup-linux-riscv64-musl": "4.53.3",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.53.3",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.53.3",
|
||||
"@rollup/rollup-linux-x64-musl": "4.53.3",
|
||||
"@rollup/rollup-openharmony-arm64": "4.53.3",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.53.3",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.53.3",
|
||||
"@rollup/rollup-win32-x64-gnu": "4.53.3",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.53.3",
|
||||
"@rollup/rollup-android-arm-eabi": "4.55.1",
|
||||
"@rollup/rollup-android-arm64": "4.55.1",
|
||||
"@rollup/rollup-darwin-arm64": "4.55.1",
|
||||
"@rollup/rollup-darwin-x64": "4.55.1",
|
||||
"@rollup/rollup-freebsd-arm64": "4.55.1",
|
||||
"@rollup/rollup-freebsd-x64": "4.55.1",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.55.1",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.55.1",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.55.1",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.55.1",
|
||||
"@rollup/rollup-linux-loong64-gnu": "4.55.1",
|
||||
"@rollup/rollup-linux-loong64-musl": "4.55.1",
|
||||
"@rollup/rollup-linux-ppc64-gnu": "4.55.1",
|
||||
"@rollup/rollup-linux-ppc64-musl": "4.55.1",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.55.1",
|
||||
"@rollup/rollup-linux-riscv64-musl": "4.55.1",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.55.1",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.55.1",
|
||||
"@rollup/rollup-linux-x64-musl": "4.55.1",
|
||||
"@rollup/rollup-openbsd-x64": "4.55.1",
|
||||
"@rollup/rollup-openharmony-arm64": "4.55.1",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.55.1",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.55.1",
|
||||
"@rollup/rollup-win32-x64-gnu": "4.55.1",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.55.1",
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
@@ -8875,7 +8925,6 @@
|
||||
"version": "7.7.3",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
|
||||
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
@@ -9099,7 +9148,6 @@
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
@@ -9485,9 +9533,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/synckit": {
|
||||
"version": "0.11.11",
|
||||
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz",
|
||||
"integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==",
|
||||
"version": "0.11.12",
|
||||
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.12.tgz",
|
||||
"integrity": "sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -9606,9 +9654,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ts-api-utils": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
|
||||
"integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz",
|
||||
"integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -9619,9 +9667,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ts-jest": {
|
||||
"version": "29.4.5",
|
||||
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.5.tgz",
|
||||
"integrity": "sha512-HO3GyiWn2qvTQA4kTgjDcXiMwYQt68a1Y8+JuLRVpdIzm+UOLSHgl/XqR4c6nzJkq5rOkjc02O2I7P7l/Yof0Q==",
|
||||
"version": "29.4.6",
|
||||
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.6.tgz",
|
||||
"integrity": "sha512-fSpWtOO/1AjSNQguk43hb/JCo16oJDnMJf3CdEGNkqsEX3t0KX96xvyX1D7PfLCpVoKu4MfVrqUkFyblYoY4lA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -10111,7 +10159,6 @@
|
||||
"version": "3.19.3",
|
||||
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz",
|
||||
"integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"optional": true,
|
||||
"bin": {
|
||||
@@ -10456,7 +10503,6 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
|
||||
"integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/wrap-ansi": {
|
||||
|
||||
29
package.json
29
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "update-tags-action",
|
||||
"version": "2.2.0",
|
||||
"version": "2.3.0",
|
||||
"author": "jimeh",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
@@ -41,34 +41,37 @@
|
||||
"update-readme": "npx action-docs --update-readme && npx prettier --write README.md"
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.11.1",
|
||||
"@actions/github": "^6.0.1",
|
||||
"csv-parse": "^6.1.0"
|
||||
"@actions/core": "^2.0.2",
|
||||
"@actions/github": "^7.0.0",
|
||||
"csv-parse": "^6.1.0",
|
||||
"handlebars": "^4.7.8",
|
||||
"semver": "^7.7.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/compat": "^2.0.0",
|
||||
"@eslint/compat": "^2.0.1",
|
||||
"@jest/globals": "^30.2.0",
|
||||
"@rollup/plugin-commonjs": "^29.0.0",
|
||||
"@rollup/plugin-node-resolve": "^16.0.3",
|
||||
"@rollup/plugin-typescript": "^12.3.0",
|
||||
"@types/jest": "^30.0.0",
|
||||
"@types/node": "^24.10.1",
|
||||
"@typescript-eslint/eslint-plugin": "^8.47.0",
|
||||
"@types/node": "^25.0.9",
|
||||
"@types/semver": "^7.7.1",
|
||||
"@typescript-eslint/eslint-plugin": "^8.53.0",
|
||||
"@typescript-eslint/parser": "^8.46.2",
|
||||
"action-docs": "^2.5.1",
|
||||
"eslint": "^9.39.1",
|
||||
"eslint": "^9.39.2",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-import-resolver-typescript": "^4.4.4",
|
||||
"eslint-plugin-import": "^2.32.0",
|
||||
"eslint-plugin-jest": "^29.1.0",
|
||||
"eslint-plugin-prettier": "^5.5.4",
|
||||
"eslint-plugin-jest": "^29.12.1",
|
||||
"eslint-plugin-prettier": "^5.5.5",
|
||||
"jest": "^30.2.0",
|
||||
"make-coverage-badge": "^1.2.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^3.6.2",
|
||||
"prettier": "^3.8.0",
|
||||
"prettier-eslint": "^16.4.2",
|
||||
"rollup": "^4.53.3",
|
||||
"ts-jest": "^29.4.5",
|
||||
"rollup": "^4.55.1",
|
||||
"ts-jest": "^29.4.6",
|
||||
"ts-jest-resolver": "^2.0.1",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
|
||||
105
src/derive.ts
Normal file
105
src/derive.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import { parse } from 'csv-parse/sync'
|
||||
import Handlebars from 'handlebars'
|
||||
import * as semver from 'semver'
|
||||
|
||||
/**
|
||||
* Context containing parsed semver components for template rendering.
|
||||
*/
|
||||
export interface SemverContext {
|
||||
/** "v" or "V" if input had a prefix, empty string otherwise */
|
||||
prefix: string
|
||||
/** Major version number */
|
||||
major: number
|
||||
/** Minor version number */
|
||||
minor: number
|
||||
/** Patch version number */
|
||||
patch: number
|
||||
/** Prerelease identifier (e.g., "beta.1"), empty if none */
|
||||
prerelease: string
|
||||
/** Build metadata (e.g., "build.123"), empty if none */
|
||||
build: string
|
||||
/** Full version string without prefix */
|
||||
version: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a version string into semver components.
|
||||
*
|
||||
* @param input - Version string (e.g., "v1.2.3", "1.2.3-beta.1+build.456")
|
||||
* @returns Parsed semver context
|
||||
* @throws Error if the version string is not valid semver
|
||||
*/
|
||||
export function parseSemver(input: string): SemverContext {
|
||||
const trimmed = input.trim()
|
||||
if (!trimmed) {
|
||||
throw new Error('Invalid semver: empty string')
|
||||
}
|
||||
|
||||
// Check for v/V prefix and preserve original case
|
||||
const firstChar = trimmed[0]
|
||||
const hasPrefix = firstChar === 'v' || firstChar === 'V'
|
||||
const prefix = hasPrefix ? firstChar : ''
|
||||
const versionStr = hasPrefix ? trimmed.slice(1) : trimmed
|
||||
|
||||
// Parse with semver library
|
||||
const parsed = semver.parse(versionStr)
|
||||
if (!parsed) {
|
||||
throw new Error(`Invalid semver: '${input}'`)
|
||||
}
|
||||
|
||||
return {
|
||||
prefix,
|
||||
major: parsed.major,
|
||||
minor: parsed.minor,
|
||||
patch: parsed.patch,
|
||||
prerelease: parsed.prerelease.join('.'),
|
||||
build: parsed.build.join('.'),
|
||||
version: parsed.version
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a single template string with semver context using Handlebars.
|
||||
* Supports {{#if variable}}...{{/if}} conditionals for optional sections.
|
||||
*
|
||||
* @param template - Handlebars template string
|
||||
* @param ctx - Semver context for substitution
|
||||
* @returns Rendered string
|
||||
*/
|
||||
export function renderTemplate(template: string, ctx: SemverContext): string {
|
||||
const compiled = Handlebars.compile(template, { noEscape: true })
|
||||
// Convert numbers to strings so 0 is truthy in conditionals
|
||||
const stringCtx = {
|
||||
...ctx,
|
||||
major: String(ctx.major),
|
||||
minor: String(ctx.minor),
|
||||
patch: String(ctx.patch)
|
||||
}
|
||||
return compiled(stringCtx)
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive tags from a semver version string using a template.
|
||||
*
|
||||
* @param deriveFrom - Semver version string (e.g., "v1.2.3")
|
||||
* @param template - CSV/newline-delimited Handlebars template string
|
||||
* @returns Array of derived tag strings
|
||||
*/
|
||||
export function deriveTags(deriveFrom: string, template: string): string[] {
|
||||
const ctx = parseSemver(deriveFrom)
|
||||
|
||||
// Render template with Handlebars first, enabling conditional tag inclusion
|
||||
const rendered = renderTemplate(template, ctx)
|
||||
|
||||
// Parse rendered result as CSV/newline delimited
|
||||
const tags = (
|
||||
parse(rendered, {
|
||||
delimiter: ',',
|
||||
trim: true,
|
||||
relax_column_count: true
|
||||
}) as string[][]
|
||||
).flat()
|
||||
|
||||
// Exclude empty tags and tags that are just the prefix with no version data
|
||||
return tags.filter((tag) => tag.length > 0 && tag !== ctx.prefix)
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import * as core from '@actions/core'
|
||||
import * as github from '@actions/github'
|
||||
import { parse } from 'csv-parse/sync'
|
||||
import { deriveTags } from './derive.js'
|
||||
|
||||
const WHEN_EXISTS_MODES = ['update', 'skip', 'fail'] as const
|
||||
export type WhenExistsMode = (typeof WHEN_EXISTS_MODES)[number]
|
||||
@@ -10,6 +11,7 @@ export interface Inputs {
|
||||
defaultRef: string
|
||||
whenExists: WhenExistsMode
|
||||
annotation: string
|
||||
dryRun: boolean
|
||||
owner: string
|
||||
repo: string
|
||||
token: string
|
||||
@@ -32,37 +34,62 @@ function validateWhenExists(input: string): WhenExistsMode {
|
||||
return input as WhenExistsMode
|
||||
}
|
||||
|
||||
const DEFAULT_DERIVE_FROM_TEMPLATE =
|
||||
'{{prefix}}{{major}},{{prefix}}{{major}}.{{minor}}'
|
||||
|
||||
/**
|
||||
* Get and validate action inputs.
|
||||
*
|
||||
* @returns Parsed and validated inputs
|
||||
*/
|
||||
export function getInputs(): Inputs {
|
||||
const tagsInput: string = core.getInput('tags', { required: true })
|
||||
const tagsInput: string = core.getInput('tags')
|
||||
const deriveFrom: string = core.getInput('derive_from')
|
||||
const deriveFromTemplate: string =
|
||||
core.getInput('derive_from_template') || DEFAULT_DERIVE_FROM_TEMPLATE
|
||||
const defaultRef: string = core.getInput('ref')
|
||||
const whenExistsInput = core.getInput('when_exists') || 'update'
|
||||
const whenExists = validateWhenExists(whenExistsInput)
|
||||
const annotation: string = core.getInput('annotation')
|
||||
const dryRun: boolean = core.getBooleanInput('dry_run')
|
||||
const token: string = core.getInput('github_token', {
|
||||
required: true
|
||||
})
|
||||
|
||||
const { owner, repo } = github.context.repo
|
||||
|
||||
// Parse tags as CSV/newline delimited strings
|
||||
const tags = (
|
||||
parse(tagsInput, {
|
||||
delimiter: ',',
|
||||
trim: true,
|
||||
relax_column_count: true
|
||||
}) as string[][]
|
||||
).flat()
|
||||
// Parse explicit tags as CSV/newline delimited strings
|
||||
const explicitTags: string[] = tagsInput
|
||||
? (
|
||||
parse(tagsInput, {
|
||||
delimiter: ',',
|
||||
trim: true,
|
||||
relax_column_count: true
|
||||
}) as string[][]
|
||||
).flat()
|
||||
: []
|
||||
|
||||
// Derive tags from semver version string if provided
|
||||
const derivedTags: string[] = deriveFrom
|
||||
? deriveTags(deriveFrom, deriveFromTemplate)
|
||||
: []
|
||||
|
||||
// Combine explicit and derived tags
|
||||
const tags = [...explicitTags, ...derivedTags]
|
||||
|
||||
// Validate that at least one tag source is provided
|
||||
if (tags.length === 0) {
|
||||
throw new Error(
|
||||
"No tags specified. Provide 'tags' input, 'derive_from' input, or both."
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
tags,
|
||||
defaultRef,
|
||||
whenExists,
|
||||
annotation,
|
||||
dryRun,
|
||||
owner,
|
||||
repo,
|
||||
token
|
||||
|
||||
47
src/logger.ts
Normal file
47
src/logger.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import * as core from '@actions/core'
|
||||
import type { AnnotationProperties } from '@actions/core'
|
||||
|
||||
/**
|
||||
* Logger interface that mirrors @actions/core logging functions.
|
||||
*/
|
||||
export interface Logger {
|
||||
debug: (message: string) => void
|
||||
info: (message: string) => void
|
||||
notice: (message: string | Error, properties?: AnnotationProperties) => void
|
||||
warning: (message: string | Error, properties?: AnnotationProperties) => void
|
||||
error: (message: string | Error, properties?: AnnotationProperties) => void
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a logger that optionally prefixes all messages.
|
||||
*
|
||||
* @param prefix - Optional prefix to prepend to all log messages
|
||||
* @returns Logger instance with prefixed messages
|
||||
*/
|
||||
export function createLogger(prefix: string = ''): Logger {
|
||||
const prefixMessage = (msg: string | Error): string | Error => {
|
||||
if (!prefix) {
|
||||
return msg
|
||||
}
|
||||
if (typeof msg === 'string') {
|
||||
return `${prefix}${msg}`
|
||||
}
|
||||
// Wrap Error with prefixed message, preserving the original as cause
|
||||
const wrapped = new Error(`${prefix}${msg.message}`, { cause: msg })
|
||||
if (msg.stack) {
|
||||
wrapped.stack = msg.stack
|
||||
}
|
||||
return wrapped
|
||||
}
|
||||
|
||||
return {
|
||||
debug: (message: string) => core.debug(`${prefix}${message}`),
|
||||
info: (message: string) => core.info(`${prefix}${message}`),
|
||||
notice: (message: string | Error, properties?: AnnotationProperties) =>
|
||||
core.notice(prefixMessage(message), properties),
|
||||
warning: (message: string | Error, properties?: AnnotationProperties) =>
|
||||
core.warning(prefixMessage(message), properties),
|
||||
error: (message: string | Error, properties?: AnnotationProperties) =>
|
||||
core.error(prefixMessage(message), properties)
|
||||
}
|
||||
}
|
||||
21
src/main.ts
21
src/main.ts
@@ -14,20 +14,27 @@ export async function run(): Promise<void> {
|
||||
const octokit = github.getOctokit(inputs.token)
|
||||
const operations = await planTagOperations(inputs, octokit)
|
||||
|
||||
if (inputs.dryRun) {
|
||||
core.info('[dry-run] Dry-run mode enabled, no changes will be made.')
|
||||
}
|
||||
|
||||
const created: string[] = []
|
||||
const updated: string[] = []
|
||||
const skipped: string[] = []
|
||||
|
||||
// Execute all planned operations.
|
||||
for (const operation of operations) {
|
||||
await executeTagOperation(operation, octokit)
|
||||
await executeTagOperation(operation, octokit, { dryRun: inputs.dryRun })
|
||||
|
||||
if (operation.operation === 'create') {
|
||||
created.push(operation.name)
|
||||
} else if (operation.operation === 'update') {
|
||||
updated.push(operation.name)
|
||||
} else if (operation.operation === 'skip') {
|
||||
skipped.push(operation.name)
|
||||
// Only track actual changes when not in dry-run mode
|
||||
if (!inputs.dryRun) {
|
||||
if (operation.operation === 'create') {
|
||||
created.push(operation.name)
|
||||
} else if (operation.operation === 'update') {
|
||||
updated.push(operation.name)
|
||||
} else if (operation.operation === 'skip') {
|
||||
skipped.push(operation.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
116
src/tags.ts
116
src/tags.ts
@@ -1,6 +1,6 @@
|
||||
import * as core from '@actions/core'
|
||||
import * as github from '@actions/github'
|
||||
import type { Inputs } from './inputs.js'
|
||||
import { createLogger, type Logger } from './logger.js'
|
||||
|
||||
export interface ExistingTagInfo {
|
||||
commitSHA: string
|
||||
@@ -37,12 +37,21 @@ export interface SkipOperation extends BaseOperation {
|
||||
|
||||
export type TagOperation = CreateOperation | UpdateOperation | SkipOperation
|
||||
|
||||
interface Context {
|
||||
export interface ExecuteOptions {
|
||||
dryRun?: boolean
|
||||
}
|
||||
|
||||
interface ReadContext {
|
||||
owner: string
|
||||
repo: string
|
||||
octokit: ReturnType<typeof github.getOctokit>
|
||||
}
|
||||
|
||||
interface Context extends ReadContext {
|
||||
dryRun: boolean
|
||||
log: Logger
|
||||
}
|
||||
|
||||
/**
|
||||
* Plan tag operations based on inputs.
|
||||
*
|
||||
@@ -56,19 +65,18 @@ export async function planTagOperations(
|
||||
): Promise<TagOperation[]> {
|
||||
const uniqueRefs = new Set<string>()
|
||||
const tagRefs: Record<string, string> = {}
|
||||
const tagAnnotations: Record<string, string> = {}
|
||||
|
||||
for (const tag of inputs.tags) {
|
||||
const parts = tag.split(':').map((s) => s.trim())
|
||||
if (parts.length > 2) {
|
||||
throw new Error(
|
||||
`Invalid tag specification '${tag}': too many colons. ` +
|
||||
`Format should be 'tag' or 'tag:ref'.`
|
||||
)
|
||||
}
|
||||
const [tagName, tagRef] = parts
|
||||
const parts = tag.split(':')
|
||||
const tagName = (parts[0] || '').trim()
|
||||
const tagRef = (parts[1] || '').trim()
|
||||
// Join remaining parts back with colons to preserve annotation content
|
||||
const tagAnnotation = parts.slice(2).join(':').trim()
|
||||
|
||||
if (!tagName) {
|
||||
// Skip completely empty tags, but fail on invalid ones like ":main"
|
||||
if (tagRef) {
|
||||
if (tagRef || tagAnnotation) {
|
||||
throw new Error(`Invalid tag: '${tag}'`)
|
||||
}
|
||||
continue
|
||||
@@ -88,11 +96,14 @@ export async function planTagOperations(
|
||||
}
|
||||
|
||||
tagRefs[tagName] = ref
|
||||
if (tagAnnotation) {
|
||||
tagAnnotations[tagName] = tagAnnotation
|
||||
}
|
||||
uniqueRefs.add(ref)
|
||||
}
|
||||
|
||||
// Pre-resolve all unique refs in parallel.
|
||||
const ctx: Context = { owner: inputs.owner, repo: inputs.repo, octokit }
|
||||
const ctx: ReadContext = { owner: inputs.owner, repo: inputs.repo, octokit }
|
||||
const refSHAs: Record<string, string> = {}
|
||||
await Promise.all(
|
||||
Array.from(uniqueRefs).map(async (ref) => {
|
||||
@@ -106,6 +117,7 @@ export async function planTagOperations(
|
||||
tagNames.map(async (tagName) => {
|
||||
const tagRef = tagRefs[tagName]
|
||||
const sha = refSHAs[tagRef]
|
||||
const annotation = tagAnnotations[tagName] || inputs.annotation
|
||||
|
||||
// Check if tag already exists
|
||||
let existing: ExistingTagInfo | undefined
|
||||
@@ -146,7 +158,7 @@ export async function planTagOperations(
|
||||
return {
|
||||
...baseOp,
|
||||
operation: 'create',
|
||||
annotation: inputs.annotation
|
||||
annotation
|
||||
} as CreateOperation
|
||||
}
|
||||
|
||||
@@ -163,7 +175,7 @@ export async function planTagOperations(
|
||||
// whenExists === 'update' - check if update is needed
|
||||
const { commitMatches, annotationMatches } = compareTagState(
|
||||
sha,
|
||||
inputs.annotation,
|
||||
annotation,
|
||||
existing
|
||||
)
|
||||
|
||||
@@ -177,11 +189,11 @@ export async function planTagOperations(
|
||||
}
|
||||
|
||||
// Plan update with reasons
|
||||
const reasons = getUpdateReasons(sha, inputs.annotation, existing)
|
||||
const reasons = getUpdateReasons(sha, annotation, existing)
|
||||
return {
|
||||
...baseOp,
|
||||
operation: 'update',
|
||||
annotation: inputs.annotation,
|
||||
annotation,
|
||||
existingSHA: existing.commitSHA,
|
||||
existingIsAnnotated: existing.isAnnotated,
|
||||
reasons
|
||||
@@ -197,26 +209,24 @@ export async function planTagOperations(
|
||||
*
|
||||
* @param operation - The planned tag operation to execute
|
||||
* @param octokit - GitHub API client
|
||||
* @param options - Execution options (e.g., dryRun)
|
||||
*/
|
||||
export async function executeTagOperation(
|
||||
operation: TagOperation,
|
||||
octokit: ReturnType<typeof github.getOctokit>
|
||||
octokit: ReturnType<typeof github.getOctokit>,
|
||||
options: ExecuteOptions = {}
|
||||
): Promise<void> {
|
||||
const dryRun = options.dryRun ?? false
|
||||
const ctx: Context = {
|
||||
owner: operation.owner,
|
||||
repo: operation.repo,
|
||||
octokit
|
||||
octokit,
|
||||
dryRun,
|
||||
log: createLogger(dryRun ? '[dry-run] ' : '')
|
||||
}
|
||||
|
||||
if (operation.operation === 'skip') {
|
||||
if (operation.reason === 'when_exists_skip') {
|
||||
core.info(`Tag '${operation.name}' exists, skipping.`)
|
||||
} else {
|
||||
core.info(
|
||||
`Tag '${operation.name}' already exists with desired commit SHA ${operation.sha}` +
|
||||
(operation.existingIsAnnotated ? ' (annotated).' : '.')
|
||||
)
|
||||
}
|
||||
logSkipOperation(ctx, operation)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -235,15 +245,30 @@ export async function executeTagOperation(
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a skip operation.
|
||||
*/
|
||||
function logSkipOperation(ctx: Context, operation: SkipOperation): void {
|
||||
if (operation.reason === 'when_exists_skip') {
|
||||
ctx.log.info(`Tag '${operation.name}' exists, skipping.`)
|
||||
} else {
|
||||
ctx.log.info(
|
||||
`Tag '${operation.name}' already exists with desired ` +
|
||||
`commit SHA ${operation.sha}` +
|
||||
(operation.existingIsAnnotated ? ' (annotated).' : '.')
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch information about an existing tag, dereferencing if annotated.
|
||||
*
|
||||
* @param ctx - Operation context
|
||||
* @param ctx - Read-only operation context
|
||||
* @param tagName - The name of the tag to fetch
|
||||
* @returns Information about the existing tag
|
||||
*/
|
||||
async function fetchTagInfo(
|
||||
ctx: Context,
|
||||
ctx: ReadContext,
|
||||
tagName: string
|
||||
): Promise<ExistingTagInfo> {
|
||||
const ref = await ctx.octokit.rest.git.getRef({
|
||||
@@ -278,11 +303,11 @@ async function fetchTagInfo(
|
||||
/**
|
||||
* Resolve a ref to a SHA.
|
||||
*
|
||||
* @param ctx - Operation context
|
||||
* @param ctx - Read-only operation context
|
||||
* @param ref - The ref to resolve
|
||||
* @returns The SHA
|
||||
*/
|
||||
async function resolveRefToSha(ctx: Context, ref: string): Promise<string> {
|
||||
async function resolveRefToSha(ctx: ReadContext, ref: string): Promise<string> {
|
||||
try {
|
||||
const {
|
||||
data: { sha }
|
||||
@@ -306,19 +331,24 @@ async function updateExistingTag(
|
||||
operation: UpdateOperation
|
||||
): Promise<void> {
|
||||
const commitMatches = operation.existingSHA === operation.sha
|
||||
const verb = ctx.dryRun ? 'Would update' : 'Updating'
|
||||
|
||||
if (commitMatches) {
|
||||
core.info(
|
||||
`Tag '${operation.name}' exists with same commit but ${operation.reasons.join(', ')}.`
|
||||
ctx.log.info(
|
||||
`${verb} tag '${operation.name}', ${operation.reasons.join(', ')}.`
|
||||
)
|
||||
} else {
|
||||
core.info(
|
||||
`Tag '${operation.name}' exists` +
|
||||
`${operation.existingIsAnnotated ? ' (annotated)' : ''}` +
|
||||
`, updating to ${operation.reasons.join(', ')}.`
|
||||
ctx.log.info(
|
||||
`${verb} tag '${operation.name}'` +
|
||||
`${operation.existingIsAnnotated ? ' (annotated)' : ''} ` +
|
||||
`to ${operation.reasons.join(', ')}.`
|
||||
)
|
||||
}
|
||||
|
||||
if (ctx.dryRun) {
|
||||
return
|
||||
}
|
||||
|
||||
const targetSha = await resolveTargetSHA(
|
||||
ctx,
|
||||
operation.name,
|
||||
@@ -342,10 +372,16 @@ async function createTag(
|
||||
ctx: Context,
|
||||
operation: CreateOperation
|
||||
): Promise<void> {
|
||||
core.info(
|
||||
`Tag '${operation.name}' does not exist, creating with commit SHA ${operation.sha}.`
|
||||
const verb = ctx.dryRun ? 'Would create' : 'Creating'
|
||||
ctx.log.info(
|
||||
`${verb} tag '${operation.name}' at commit SHA ${operation.sha}` +
|
||||
(operation.annotation ? ' (annotated).' : '.')
|
||||
)
|
||||
|
||||
if (ctx.dryRun) {
|
||||
return
|
||||
}
|
||||
|
||||
const targetSha = await resolveTargetSHA(
|
||||
ctx,
|
||||
operation.name,
|
||||
@@ -364,14 +400,14 @@ async function createTag(
|
||||
/**
|
||||
* Resolve the target SHA for a tag (creates annotated tag object if needed).
|
||||
*
|
||||
* @param ctx - Operation context
|
||||
* @param ctx - Read-only operation context
|
||||
* @param tagName - The tag name
|
||||
* @param commitSha - The commit SHA
|
||||
* @param annotation - The annotation message (if any)
|
||||
* @returns The SHA to use (tag object SHA if annotated, commit SHA otherwise)
|
||||
*/
|
||||
async function resolveTargetSHA(
|
||||
ctx: Context,
|
||||
ctx: ReadContext,
|
||||
tagName: string,
|
||||
commitSha: string,
|
||||
annotation: string
|
||||
|
||||
420
tests/derive.test.ts
Normal file
420
tests/derive.test.ts
Normal file
@@ -0,0 +1,420 @@
|
||||
/**
|
||||
* Unit tests for the derive module, src/derive.ts
|
||||
*/
|
||||
import { parseSemver, renderTemplate, deriveTags } from '../src/derive.js'
|
||||
|
||||
describe('parseSemver', () => {
|
||||
it('parses simple version without prefix', () => {
|
||||
const result = parseSemver('1.2.3')
|
||||
expect(result).toEqual({
|
||||
prefix: '',
|
||||
major: 1,
|
||||
minor: 2,
|
||||
patch: 3,
|
||||
prerelease: '',
|
||||
build: '',
|
||||
version: '1.2.3'
|
||||
})
|
||||
})
|
||||
|
||||
it('parses version with v prefix', () => {
|
||||
const result = parseSemver('v1.2.3')
|
||||
expect(result).toEqual({
|
||||
prefix: 'v',
|
||||
major: 1,
|
||||
minor: 2,
|
||||
patch: 3,
|
||||
prerelease: '',
|
||||
build: '',
|
||||
version: '1.2.3'
|
||||
})
|
||||
})
|
||||
|
||||
it('parses version with uppercase V prefix', () => {
|
||||
const result = parseSemver('V1.2.3')
|
||||
expect(result).toEqual({
|
||||
prefix: 'V',
|
||||
major: 1,
|
||||
minor: 2,
|
||||
patch: 3,
|
||||
prerelease: '',
|
||||
build: '',
|
||||
version: '1.2.3'
|
||||
})
|
||||
})
|
||||
|
||||
it('parses version with prerelease', () => {
|
||||
const result = parseSemver('v1.0.0-beta.1')
|
||||
expect(result).toEqual({
|
||||
prefix: 'v',
|
||||
major: 1,
|
||||
minor: 0,
|
||||
patch: 0,
|
||||
prerelease: 'beta.1',
|
||||
build: '',
|
||||
version: '1.0.0-beta.1'
|
||||
})
|
||||
})
|
||||
|
||||
it('parses version with build metadata', () => {
|
||||
const result = parseSemver('v1.0.0+build.456')
|
||||
expect(result).toEqual({
|
||||
prefix: 'v',
|
||||
major: 1,
|
||||
minor: 0,
|
||||
patch: 0,
|
||||
prerelease: '',
|
||||
build: 'build.456',
|
||||
version: '1.0.0'
|
||||
})
|
||||
})
|
||||
|
||||
it('parses version with both prerelease and build metadata', () => {
|
||||
const result = parseSemver('v1.2.3-alpha.1+build.789')
|
||||
expect(result).toEqual({
|
||||
prefix: 'v',
|
||||
major: 1,
|
||||
minor: 2,
|
||||
patch: 3,
|
||||
prerelease: 'alpha.1',
|
||||
build: 'build.789',
|
||||
version: '1.2.3-alpha.1'
|
||||
})
|
||||
})
|
||||
|
||||
it('handles whitespace around version string', () => {
|
||||
const result = parseSemver(' v1.2.3 ')
|
||||
expect(result).toEqual({
|
||||
prefix: 'v',
|
||||
major: 1,
|
||||
minor: 2,
|
||||
patch: 3,
|
||||
prerelease: '',
|
||||
build: '',
|
||||
version: '1.2.3'
|
||||
})
|
||||
})
|
||||
|
||||
it('throws on empty string', () => {
|
||||
expect(() => parseSemver('')).toThrow('Invalid semver: empty string')
|
||||
})
|
||||
|
||||
it('throws on whitespace only', () => {
|
||||
expect(() => parseSemver(' ')).toThrow('Invalid semver: empty string')
|
||||
})
|
||||
|
||||
it('throws on invalid semver', () => {
|
||||
expect(() => parseSemver('not-a-version')).toThrow(
|
||||
"Invalid semver: 'not-a-version'"
|
||||
)
|
||||
})
|
||||
|
||||
it('throws on incomplete version', () => {
|
||||
expect(() => parseSemver('v1.2')).toThrow("Invalid semver: 'v1.2'")
|
||||
})
|
||||
})
|
||||
|
||||
describe('renderTemplate', () => {
|
||||
const ctx = {
|
||||
prefix: 'v',
|
||||
major: 1,
|
||||
minor: 2,
|
||||
patch: 3,
|
||||
prerelease: 'beta.1',
|
||||
build: 'build.456',
|
||||
version: '1.2.3-beta.1'
|
||||
}
|
||||
|
||||
it('renders prefix placeholder', () => {
|
||||
expect(renderTemplate('{{prefix}}', ctx)).toBe('v')
|
||||
})
|
||||
|
||||
it('renders major placeholder', () => {
|
||||
expect(renderTemplate('{{major}}', ctx)).toBe('1')
|
||||
})
|
||||
|
||||
it('renders minor placeholder', () => {
|
||||
expect(renderTemplate('{{minor}}', ctx)).toBe('2')
|
||||
})
|
||||
|
||||
it('renders patch placeholder', () => {
|
||||
expect(renderTemplate('{{patch}}', ctx)).toBe('3')
|
||||
})
|
||||
|
||||
it('renders prerelease placeholder', () => {
|
||||
expect(renderTemplate('{{prerelease}}', ctx)).toBe('beta.1')
|
||||
})
|
||||
|
||||
it('renders build placeholder', () => {
|
||||
expect(renderTemplate('{{build}}', ctx)).toBe('build.456')
|
||||
})
|
||||
|
||||
it('renders version placeholder', () => {
|
||||
expect(renderTemplate('{{version}}', ctx)).toBe('1.2.3-beta.1')
|
||||
})
|
||||
|
||||
it('renders multiple placeholders', () => {
|
||||
expect(renderTemplate('{{prefix}}{{major}}.{{minor}}', ctx)).toBe('v1.2')
|
||||
})
|
||||
|
||||
it('renders same placeholder multiple times', () => {
|
||||
expect(renderTemplate('{{major}}-{{major}}', ctx)).toBe('1-1')
|
||||
})
|
||||
|
||||
it('preserves text without placeholders', () => {
|
||||
expect(renderTemplate('latest', ctx)).toBe('latest')
|
||||
})
|
||||
|
||||
it('handles empty prefix', () => {
|
||||
const noPrefix = { ...ctx, prefix: '' }
|
||||
expect(renderTemplate('{{prefix}}{{major}}', noPrefix)).toBe('1')
|
||||
})
|
||||
|
||||
it('handles empty prerelease', () => {
|
||||
const noPrerelease = { ...ctx, prerelease: '' }
|
||||
expect(renderTemplate('{{major}}-{{prerelease}}', noPrerelease)).toBe('1-')
|
||||
})
|
||||
|
||||
describe('Handlebars conditionals', () => {
|
||||
it('renders {{#if}} section when variable has value', () => {
|
||||
expect(
|
||||
renderTemplate('{{major}}{{#if prerelease}}-{{prerelease}}{{/if}}', ctx)
|
||||
).toBe('1-beta.1')
|
||||
})
|
||||
|
||||
it('omits {{#if}} section when variable is empty', () => {
|
||||
const noPrerelease = { ...ctx, prerelease: '' }
|
||||
expect(
|
||||
renderTemplate(
|
||||
'{{major}}{{#if prerelease}}-{{prerelease}}{{/if}}',
|
||||
noPrerelease
|
||||
)
|
||||
).toBe('1')
|
||||
})
|
||||
|
||||
it('handles {{#if}} with zero-valued numbers (converted to strings)', () => {
|
||||
const zeroMinor = { ...ctx, minor: 0 }
|
||||
expect(
|
||||
renderTemplate('{{major}}{{#if minor}}.{{minor}}{{/if}}', zeroMinor)
|
||||
).toBe('1.0')
|
||||
})
|
||||
|
||||
it('handles nested conditionals', () => {
|
||||
expect(
|
||||
renderTemplate(
|
||||
'{{major}}{{#if prerelease}}-{{prerelease}}{{#if build}}+{{build}}{{/if}}{{/if}}',
|
||||
ctx
|
||||
)
|
||||
).toBe('1-beta.1+build.456')
|
||||
})
|
||||
|
||||
it('handles nested conditionals with inner empty', () => {
|
||||
const noBuild = { ...ctx, build: '' }
|
||||
expect(
|
||||
renderTemplate(
|
||||
'{{major}}{{#if prerelease}}-{{prerelease}}{{#if build}}+{{build}}{{/if}}{{/if}}',
|
||||
noBuild
|
||||
)
|
||||
).toBe('1-beta.1')
|
||||
})
|
||||
|
||||
it('handles {{#unless}} for inverse logic', () => {
|
||||
const noPrerelease = { ...ctx, prerelease: '' }
|
||||
expect(
|
||||
renderTemplate('{{major}}{{#unless prerelease}}-stable{{/unless}}', ctx)
|
||||
).toBe('1')
|
||||
expect(
|
||||
renderTemplate(
|
||||
'{{major}}{{#unless prerelease}}-stable{{/unless}}',
|
||||
noPrerelease
|
||||
)
|
||||
).toBe('1-stable')
|
||||
})
|
||||
|
||||
it('handles multiple {{#if}} sections', () => {
|
||||
const noBuild = { ...ctx, build: '' }
|
||||
expect(
|
||||
renderTemplate(
|
||||
'{{major}}{{#if prerelease}}-{{prerelease}}{{/if}}{{#if build}}+{{build}}{{/if}}',
|
||||
noBuild
|
||||
)
|
||||
).toBe('1-beta.1')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('deriveTags', () => {
|
||||
it('generates tags with default-style template', () => {
|
||||
const result = deriveTags(
|
||||
'v1.2.3',
|
||||
'{{prefix}}{{major}},{{prefix}}{{major}}.{{minor}}'
|
||||
)
|
||||
expect(result).toEqual(['v1', 'v1.2'])
|
||||
})
|
||||
|
||||
it('generates single tag from simple template', () => {
|
||||
const result = deriveTags('v1.2.3', '{{prefix}}{{major}}')
|
||||
expect(result).toEqual(['v1'])
|
||||
})
|
||||
|
||||
it('handles version without prefix', () => {
|
||||
const result = deriveTags(
|
||||
'1.2.3',
|
||||
'{{prefix}}{{major}},{{prefix}}{{major}}.{{minor}}'
|
||||
)
|
||||
expect(result).toEqual(['1', '1.2'])
|
||||
})
|
||||
|
||||
it('handles prerelease in template', () => {
|
||||
const result = deriveTags(
|
||||
'v1.0.0-rc.1',
|
||||
'{{prefix}}{{major}}-{{prerelease}}'
|
||||
)
|
||||
expect(result).toEqual(['v1-rc.1'])
|
||||
})
|
||||
|
||||
it('filters empty tags and prefix-only tags from result', () => {
|
||||
const result = deriveTags('v1.2.3', '{{prefix}}{{major}},,{{prefix}}')
|
||||
expect(result).toEqual(['v1'])
|
||||
})
|
||||
|
||||
it('trims whitespace around template parts', () => {
|
||||
const result = deriveTags('v1.2.3', ' {{prefix}}{{major}} , latest ')
|
||||
expect(result).toEqual(['v1', 'latest'])
|
||||
})
|
||||
|
||||
it('generates full version tag', () => {
|
||||
const result = deriveTags('v1.2.3', '{{prefix}}{{version}}')
|
||||
expect(result).toEqual(['v1.2.3'])
|
||||
})
|
||||
|
||||
it('throws on invalid semver', () => {
|
||||
expect(() => deriveTags('invalid', '{{major}}')).toThrow(
|
||||
"Invalid semver: 'invalid'"
|
||||
)
|
||||
})
|
||||
|
||||
it('handles newline-delimited template', () => {
|
||||
const result = deriveTags(
|
||||
'v1.2.3',
|
||||
'{{prefix}}{{major}}\n{{prefix}}{{major}}.{{minor}}'
|
||||
)
|
||||
expect(result).toEqual(['v1', 'v1.2'])
|
||||
})
|
||||
|
||||
it('handles mixed newlines and commas in template', () => {
|
||||
const result = deriveTags(
|
||||
'v1.2.3',
|
||||
'{{prefix}}{{major}},{{prefix}}{{major}}.{{minor}}\nlatest'
|
||||
)
|
||||
expect(result).toEqual(['v1', 'v1.2', 'latest'])
|
||||
})
|
||||
|
||||
describe('with Handlebars conditionals', () => {
|
||||
it('generates tag with optional prerelease when present', () => {
|
||||
const result = deriveTags(
|
||||
'v1.0.0-beta.1',
|
||||
'{{prefix}}{{major}}{{#if prerelease}}-{{prerelease}}{{/if}}'
|
||||
)
|
||||
expect(result).toEqual(['v1-beta.1'])
|
||||
})
|
||||
|
||||
it('generates tag without prerelease section when absent', () => {
|
||||
const result = deriveTags(
|
||||
'v1.0.0',
|
||||
'{{prefix}}{{major}}{{#if prerelease}}-{{prerelease}}{{/if}}'
|
||||
)
|
||||
expect(result).toEqual(['v1'])
|
||||
})
|
||||
|
||||
it('generates multiple tags with conditionals', () => {
|
||||
const result = deriveTags(
|
||||
'v1.2.3-rc.1',
|
||||
'{{prefix}}{{major}},{{prefix}}{{major}}.{{minor}}{{#if prerelease}}-{{prerelease}}{{/if}}'
|
||||
)
|
||||
expect(result).toEqual(['v1', 'v1.2-rc.1'])
|
||||
})
|
||||
|
||||
it('handles build metadata conditional', () => {
|
||||
const result = deriveTags(
|
||||
'v1.0.0+build.123',
|
||||
'{{prefix}}{{major}}{{#if build}}+{{build}}{{/if}}'
|
||||
)
|
||||
expect(result).toEqual(['v1+build.123'])
|
||||
})
|
||||
|
||||
it('handles both prerelease and build conditionals', () => {
|
||||
const result = deriveTags(
|
||||
'v1.0.0-alpha.1+build.789',
|
||||
'{{prefix}}{{major}}{{#if prerelease}}-{{prerelease}}{{/if}}{{#if build}}+{{build}}{{/if}}'
|
||||
)
|
||||
expect(result).toEqual(['v1-alpha.1+build.789'])
|
||||
})
|
||||
})
|
||||
|
||||
describe('conditional tag inclusion', () => {
|
||||
it('includes latest tag only for stable releases', () => {
|
||||
// Stable release gets latest tag
|
||||
const stable = deriveTags(
|
||||
'v1.2.3',
|
||||
'{{prefix}}{{major}},{{prefix}}{{major}}.{{minor}}{{#unless prerelease}},latest{{/unless}}'
|
||||
)
|
||||
expect(stable).toEqual(['v1', 'v1.2', 'latest'])
|
||||
|
||||
// Prerelease does not get latest tag
|
||||
const prerelease = deriveTags(
|
||||
'v1.2.3-beta.1',
|
||||
'{{prefix}}{{major}},{{prefix}}{{major}}.{{minor}}{{#unless prerelease}},latest{{/unless}}'
|
||||
)
|
||||
expect(prerelease).toEqual(['v1', 'v1.2'])
|
||||
})
|
||||
|
||||
it('conditionally includes entire tag group with {{#if}}', () => {
|
||||
// With prerelease: include prerelease-specific tags
|
||||
const withPre = deriveTags(
|
||||
'v2.0.0-rc.1',
|
||||
'{{prefix}}{{major}}{{#if prerelease}},{{prefix}}{{major}}-{{prerelease}}{{/if}}'
|
||||
)
|
||||
expect(withPre).toEqual(['v2', 'v2-rc.1'])
|
||||
|
||||
// Without prerelease: only major tag
|
||||
const withoutPre = deriveTags(
|
||||
'v2.0.0',
|
||||
'{{prefix}}{{major}}{{#if prerelease}},{{prefix}}{{major}}-{{prerelease}}{{/if}}'
|
||||
)
|
||||
expect(withoutPre).toEqual(['v2'])
|
||||
})
|
||||
|
||||
it('conditionally includes newline-separated tags', () => {
|
||||
// Stable release includes all tags
|
||||
const stable = deriveTags(
|
||||
'v1.0.0',
|
||||
'{{prefix}}{{major}}\n{{prefix}}{{major}}.{{minor}}{{#unless prerelease}}\nstable{{/unless}}'
|
||||
)
|
||||
expect(stable).toEqual(['v1', 'v1.0', 'stable'])
|
||||
|
||||
// Prerelease excludes stable tag
|
||||
const prerelease = deriveTags(
|
||||
'v1.0.0-alpha.1',
|
||||
'{{prefix}}{{major}}\n{{prefix}}{{major}}.{{minor}}{{#unless prerelease}}\nstable{{/unless}}'
|
||||
)
|
||||
expect(prerelease).toEqual(['v1', 'v1.0'])
|
||||
})
|
||||
|
||||
it('excludes multiple tags conditionally', () => {
|
||||
const result = deriveTags(
|
||||
'v1.0.0-beta.1',
|
||||
'{{prefix}}{{major}}{{#unless prerelease}},latest,stable,production{{/unless}}'
|
||||
)
|
||||
expect(result).toEqual(['v1'])
|
||||
})
|
||||
|
||||
it('includes multiple tags conditionally for stable', () => {
|
||||
const result = deriveTags(
|
||||
'v1.0.0',
|
||||
'{{prefix}}{{major}}{{#unless prerelease}},latest,stable,production{{/unless}}'
|
||||
)
|
||||
expect(result).toEqual(['v1', 'latest', 'stable', 'production'])
|
||||
})
|
||||
})
|
||||
})
|
||||
2
tests/fixtures/core.ts
vendored
2
tests/fixtures/core.ts
vendored
@@ -4,7 +4,9 @@ import { jest } from '@jest/globals'
|
||||
export const debug = jest.fn<typeof core.debug>()
|
||||
export const error = jest.fn<typeof core.error>()
|
||||
export const info = jest.fn<typeof core.info>()
|
||||
export const notice = jest.fn<typeof core.notice>()
|
||||
export const getInput = jest.fn<typeof core.getInput>()
|
||||
export const getBooleanInput = jest.fn<typeof core.getBooleanInput>()
|
||||
export const setOutput = jest.fn<typeof core.setOutput>()
|
||||
export const setFailed = jest.fn<typeof core.setFailed>()
|
||||
export const warning = jest.fn<typeof core.warning>()
|
||||
|
||||
193
tests/logger.test.ts
Normal file
193
tests/logger.test.ts
Normal file
@@ -0,0 +1,193 @@
|
||||
/**
|
||||
* Unit tests for the logger module, src/logger.ts
|
||||
*/
|
||||
import { jest } from '@jest/globals'
|
||||
import * as core from './fixtures/core.js'
|
||||
|
||||
jest.unstable_mockModule('@actions/core', () => core)
|
||||
|
||||
const { createLogger } = await import('../src/logger.js')
|
||||
|
||||
describe('createLogger', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks()
|
||||
})
|
||||
|
||||
describe('without prefix', () => {
|
||||
it('calls core.debug with the message', () => {
|
||||
const log = createLogger()
|
||||
log.debug('test debug message')
|
||||
expect(core.debug).toHaveBeenCalledWith('test debug message')
|
||||
})
|
||||
|
||||
it('calls core.info with the message', () => {
|
||||
const log = createLogger()
|
||||
log.info('test info message')
|
||||
expect(core.info).toHaveBeenCalledWith('test info message')
|
||||
})
|
||||
|
||||
it('calls core.notice with string message', () => {
|
||||
const log = createLogger()
|
||||
log.notice('test notice message')
|
||||
expect(core.notice).toHaveBeenCalledWith('test notice message', undefined)
|
||||
})
|
||||
|
||||
it('calls core.notice with Error unchanged', () => {
|
||||
const log = createLogger()
|
||||
const error = new Error('test error')
|
||||
log.notice(error)
|
||||
expect(core.notice).toHaveBeenCalledWith(error, undefined)
|
||||
})
|
||||
|
||||
it('calls core.notice with properties', () => {
|
||||
const log = createLogger()
|
||||
const props = { title: 'Notice Title' }
|
||||
log.notice('test notice', props)
|
||||
expect(core.notice).toHaveBeenCalledWith('test notice', props)
|
||||
})
|
||||
|
||||
it('calls core.warning with string message', () => {
|
||||
const log = createLogger()
|
||||
log.warning('test warning message')
|
||||
expect(core.warning).toHaveBeenCalledWith(
|
||||
'test warning message',
|
||||
undefined
|
||||
)
|
||||
})
|
||||
|
||||
it('calls core.warning with Error unchanged', () => {
|
||||
const log = createLogger()
|
||||
const error = new Error('warning error')
|
||||
log.warning(error)
|
||||
expect(core.warning).toHaveBeenCalledWith(error, undefined)
|
||||
})
|
||||
|
||||
it('calls core.warning with properties', () => {
|
||||
const log = createLogger()
|
||||
const props = { title: 'Warning Title' }
|
||||
log.warning('test warning', props)
|
||||
expect(core.warning).toHaveBeenCalledWith('test warning', props)
|
||||
})
|
||||
|
||||
it('calls core.error with string message', () => {
|
||||
const log = createLogger()
|
||||
log.error('test error message')
|
||||
expect(core.error).toHaveBeenCalledWith('test error message', undefined)
|
||||
})
|
||||
|
||||
it('calls core.error with Error unchanged', () => {
|
||||
const log = createLogger()
|
||||
const error = new Error('error error')
|
||||
log.error(error)
|
||||
expect(core.error).toHaveBeenCalledWith(error, undefined)
|
||||
})
|
||||
|
||||
it('calls core.error with properties', () => {
|
||||
const log = createLogger()
|
||||
const props = { title: 'Error Title' }
|
||||
log.error('test error', props)
|
||||
expect(core.error).toHaveBeenCalledWith('test error', props)
|
||||
})
|
||||
})
|
||||
|
||||
describe('with prefix', () => {
|
||||
it('prefixes debug messages', () => {
|
||||
const log = createLogger('[dry-run] ')
|
||||
log.debug('test debug message')
|
||||
expect(core.debug).toHaveBeenCalledWith('[dry-run] test debug message')
|
||||
})
|
||||
|
||||
it('prefixes info messages', () => {
|
||||
const log = createLogger('[dry-run] ')
|
||||
log.info('test info message')
|
||||
expect(core.info).toHaveBeenCalledWith('[dry-run] test info message')
|
||||
})
|
||||
|
||||
it('prefixes notice string messages', () => {
|
||||
const log = createLogger('[dry-run] ')
|
||||
log.notice('test notice message')
|
||||
expect(core.notice).toHaveBeenCalledWith(
|
||||
'[dry-run] test notice message',
|
||||
undefined
|
||||
)
|
||||
})
|
||||
|
||||
it('wraps notice Error with prefixed message and cause', () => {
|
||||
const log = createLogger('[dry-run] ')
|
||||
const original = new Error('notice error')
|
||||
log.notice(original)
|
||||
|
||||
expect(core.notice).toHaveBeenCalledTimes(1)
|
||||
const [wrapped, props] = core.notice.mock.calls[0]
|
||||
expect(wrapped).toBeInstanceOf(Error)
|
||||
expect((wrapped as Error).message).toBe('[dry-run] notice error')
|
||||
expect((wrapped as Error).cause).toBe(original)
|
||||
expect((wrapped as Error).stack).toBe(original.stack)
|
||||
expect(props).toBeUndefined()
|
||||
})
|
||||
|
||||
it('prefixes warning string messages', () => {
|
||||
const log = createLogger('[dry-run] ')
|
||||
log.warning('test warning message')
|
||||
expect(core.warning).toHaveBeenCalledWith(
|
||||
'[dry-run] test warning message',
|
||||
undefined
|
||||
)
|
||||
})
|
||||
|
||||
it('wraps warning Error with prefixed message and cause', () => {
|
||||
const log = createLogger('[dry-run] ')
|
||||
const original = new Error('warning error')
|
||||
log.warning(original)
|
||||
|
||||
expect(core.warning).toHaveBeenCalledTimes(1)
|
||||
const [wrapped, props] = core.warning.mock.calls[0]
|
||||
expect(wrapped).toBeInstanceOf(Error)
|
||||
expect((wrapped as Error).message).toBe('[dry-run] warning error')
|
||||
expect((wrapped as Error).cause).toBe(original)
|
||||
expect((wrapped as Error).stack).toBe(original.stack)
|
||||
expect(props).toBeUndefined()
|
||||
})
|
||||
|
||||
it('prefixes error string messages', () => {
|
||||
const log = createLogger('[dry-run] ')
|
||||
log.error('test error message')
|
||||
expect(core.error).toHaveBeenCalledWith(
|
||||
'[dry-run] test error message',
|
||||
undefined
|
||||
)
|
||||
})
|
||||
|
||||
it('wraps error Error with prefixed message and cause', () => {
|
||||
const log = createLogger('[dry-run] ')
|
||||
const original = new Error('error error')
|
||||
log.error(original)
|
||||
|
||||
expect(core.error).toHaveBeenCalledTimes(1)
|
||||
const [wrapped, props] = core.error.mock.calls[0]
|
||||
expect(wrapped).toBeInstanceOf(Error)
|
||||
expect((wrapped as Error).message).toBe('[dry-run] error error')
|
||||
expect((wrapped as Error).cause).toBe(original)
|
||||
expect((wrapped as Error).stack).toBe(original.stack)
|
||||
expect(props).toBeUndefined()
|
||||
})
|
||||
|
||||
it('preserves properties when prefixing', () => {
|
||||
const log = createLogger('[test] ')
|
||||
const props = { title: 'Test Title', file: 'test.ts', startLine: 10 }
|
||||
log.warning('message with props', props)
|
||||
expect(core.warning).toHaveBeenCalledWith(
|
||||
'[test] message with props',
|
||||
props
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('with empty prefix', () => {
|
||||
it('behaves the same as no prefix', () => {
|
||||
const log = createLogger('')
|
||||
log.info('test message')
|
||||
expect(core.info).toHaveBeenCalledWith('test message')
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -16,9 +16,14 @@ jest.unstable_mockModule('csv-parse/sync', () => csvParse)
|
||||
const { run } = await import('../src/main.js')
|
||||
|
||||
// Helper functions for cleaner test setup
|
||||
const setupInputs = (inputs: Record<string, string>): void => {
|
||||
const setupInputs = (inputs: Record<string, string | boolean>): void => {
|
||||
core.getInput.mockImplementation((name: string) => {
|
||||
return inputs[name] || ''
|
||||
const value = inputs[name]
|
||||
return typeof value === 'string' ? value : ''
|
||||
})
|
||||
core.getBooleanInput.mockImplementation((name: string) => {
|
||||
const value = inputs[name]
|
||||
return typeof value === 'boolean' ? value : false
|
||||
})
|
||||
}
|
||||
|
||||
@@ -123,7 +128,7 @@ describe('run', () => {
|
||||
})
|
||||
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
"Tag 'v1' does not exist, creating with commit SHA sha-abc123."
|
||||
"Creating tag 'v1' at commit SHA sha-abc123."
|
||||
)
|
||||
expect(getOutputs()).toEqual({
|
||||
created: ['v1', 'v1.0'],
|
||||
@@ -155,7 +160,7 @@ describe('run', () => {
|
||||
})
|
||||
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
"Tag 'v1' exists, updating to commit SHA sha-def456 (was sha-old123)."
|
||||
"Updating tag 'v1' to commit SHA sha-def456 (was sha-old123)."
|
||||
)
|
||||
expect(getOutputs()).toEqual({
|
||||
created: [],
|
||||
@@ -533,22 +538,42 @@ describe('run', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('fails when tag specification has multiple colons', async () => {
|
||||
it('creates annotated tag with per-tag annotation syntax', async () => {
|
||||
setupInputs({
|
||||
tags: 'stable:refs/heads/main:latest',
|
||||
tags: 'stable:refs/heads/main:Release annotation',
|
||||
ref: '',
|
||||
github_token: 'test-token',
|
||||
when_exists: 'update'
|
||||
})
|
||||
setupCommitResolver({ 'refs/heads/main': 'sha-main' })
|
||||
setupTagDoesNotExist()
|
||||
|
||||
github.mockOctokit.rest.git.createTag.mockResolvedValue({
|
||||
data: { sha: 'sha-tag-object-stable' }
|
||||
})
|
||||
|
||||
await run()
|
||||
|
||||
expect(core.setFailed).toHaveBeenCalledWith(
|
||||
expect.stringContaining('Invalid tag specification')
|
||||
)
|
||||
expect(core.setFailed).toHaveBeenCalledWith(
|
||||
expect.stringContaining('too many colons')
|
||||
)
|
||||
expect(github.mockOctokit.rest.git.createTag).toHaveBeenCalledWith({
|
||||
owner: 'test-owner',
|
||||
repo: 'test-repo',
|
||||
tag: 'stable',
|
||||
message: 'Release annotation',
|
||||
object: 'sha-main',
|
||||
type: 'commit'
|
||||
})
|
||||
expect(github.mockOctokit.rest.git.createRef).toHaveBeenCalledWith({
|
||||
owner: 'test-owner',
|
||||
repo: 'test-repo',
|
||||
ref: 'refs/tags/stable',
|
||||
sha: 'sha-tag-object-stable'
|
||||
})
|
||||
expect(getOutputs()).toEqual({
|
||||
created: ['stable'],
|
||||
updated: [],
|
||||
skipped: [],
|
||||
tags: ['stable']
|
||||
})
|
||||
})
|
||||
|
||||
it('handles mixed scenario with multiple tags', async () => {
|
||||
@@ -1219,4 +1244,645 @@ describe('run', () => {
|
||||
tags: ['v1']
|
||||
})
|
||||
})
|
||||
|
||||
describe('dry-run mode', () => {
|
||||
it('logs planned creates without executing them', async () => {
|
||||
setupInputs({
|
||||
tags: 'v1,v1.0',
|
||||
ref: 'abc123',
|
||||
github_token: 'test-token',
|
||||
when_exists: 'update',
|
||||
dry_run: true
|
||||
})
|
||||
setupCommitResolver('sha-abc123')
|
||||
setupTagDoesNotExist()
|
||||
|
||||
await run()
|
||||
|
||||
// Should NOT call createRef in dry-run mode
|
||||
expect(github.mockOctokit.rest.git.createRef).not.toHaveBeenCalled()
|
||||
|
||||
// Should log dry-run messages
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
'[dry-run] Dry-run mode enabled, no changes will be made.'
|
||||
)
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
"[dry-run] Would create tag 'v1' at commit SHA sha-abc123."
|
||||
)
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
"[dry-run] Would create tag 'v1.0' at commit SHA sha-abc123."
|
||||
)
|
||||
|
||||
// Outputs should be empty in dry-run mode
|
||||
expect(getOutputs()).toEqual({
|
||||
created: [],
|
||||
updated: [],
|
||||
skipped: [],
|
||||
tags: []
|
||||
})
|
||||
})
|
||||
|
||||
it('logs planned updates without executing them', async () => {
|
||||
setupInputs({
|
||||
tags: 'v1',
|
||||
ref: 'def456',
|
||||
github_token: 'test-token',
|
||||
when_exists: 'update',
|
||||
dry_run: true
|
||||
})
|
||||
setupCommitResolver('sha-def456')
|
||||
setupTagExistsForAll('sha-old123')
|
||||
|
||||
await run()
|
||||
|
||||
// Should NOT call updateRef in dry-run mode
|
||||
expect(github.mockOctokit.rest.git.updateRef).not.toHaveBeenCalled()
|
||||
|
||||
// Should log dry-run messages
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
'[dry-run] Dry-run mode enabled, no changes will be made.'
|
||||
)
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
"[dry-run] Would update tag 'v1' " +
|
||||
'to commit SHA sha-def456 (was sha-old123).'
|
||||
)
|
||||
|
||||
// Outputs should be empty in dry-run mode
|
||||
expect(getOutputs()).toEqual({
|
||||
created: [],
|
||||
updated: [],
|
||||
skipped: [],
|
||||
tags: []
|
||||
})
|
||||
})
|
||||
|
||||
it('logs skipped tags with dry-run prefix', async () => {
|
||||
setupInputs({
|
||||
tags: 'v1',
|
||||
ref: 'abc123',
|
||||
github_token: 'test-token',
|
||||
when_exists: 'update',
|
||||
dry_run: true
|
||||
})
|
||||
setupCommitResolver('sha-abc123')
|
||||
setupTagExistsForAll('sha-abc123')
|
||||
|
||||
await run()
|
||||
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
'[dry-run] Dry-run mode enabled, no changes will be made.'
|
||||
)
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
"[dry-run] Tag 'v1' already exists with desired commit SHA sha-abc123."
|
||||
)
|
||||
|
||||
expect(getOutputs()).toEqual({
|
||||
created: [],
|
||||
updated: [],
|
||||
skipped: [],
|
||||
tags: []
|
||||
})
|
||||
})
|
||||
|
||||
it('handles mixed operations in dry-run mode', async () => {
|
||||
setupInputs({
|
||||
tags: 'v1,v2,v3',
|
||||
ref: 'abc123',
|
||||
github_token: 'test-token',
|
||||
when_exists: 'update',
|
||||
dry_run: true
|
||||
})
|
||||
setupCommitResolver('sha-abc123')
|
||||
|
||||
// v1 exists with different SHA, v2 matches, v3 doesn't exist
|
||||
github.mockOctokit.rest.git.getRef.mockImplementation(
|
||||
async (args: unknown) => {
|
||||
const { ref } = args as { ref: string }
|
||||
if (ref === 'tags/v1') {
|
||||
return {
|
||||
data: {
|
||||
ref: 'refs/tags/v1',
|
||||
object: { sha: 'sha-old', type: 'commit' }
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ref === 'tags/v2') {
|
||||
return {
|
||||
data: {
|
||||
ref: 'refs/tags/v2',
|
||||
object: { sha: 'sha-abc123', type: 'commit' }
|
||||
}
|
||||
}
|
||||
}
|
||||
throw { status: 404 }
|
||||
}
|
||||
)
|
||||
|
||||
await run()
|
||||
|
||||
// No actual operations should happen
|
||||
expect(github.mockOctokit.rest.git.createRef).not.toHaveBeenCalled()
|
||||
expect(github.mockOctokit.rest.git.updateRef).not.toHaveBeenCalled()
|
||||
|
||||
// Should log all planned operations
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
'[dry-run] Dry-run mode enabled, no changes will be made.'
|
||||
)
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
"[dry-run] Would update tag 'v1' to commit SHA sha-abc123 (was sha-old)."
|
||||
)
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
"[dry-run] Tag 'v2' already exists with desired commit SHA sha-abc123."
|
||||
)
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
"[dry-run] Would create tag 'v3' at commit SHA sha-abc123."
|
||||
)
|
||||
|
||||
expect(getOutputs()).toEqual({
|
||||
created: [],
|
||||
updated: [],
|
||||
skipped: [],
|
||||
tags: []
|
||||
})
|
||||
})
|
||||
|
||||
it('logs annotated tag creation in dry-run mode', async () => {
|
||||
setupInputs({
|
||||
tags: 'v1',
|
||||
ref: 'abc123',
|
||||
github_token: 'test-token',
|
||||
when_exists: 'update',
|
||||
annotation: 'Release v1',
|
||||
dry_run: true
|
||||
})
|
||||
setupCommitResolver('sha-abc123')
|
||||
setupTagDoesNotExist()
|
||||
|
||||
await run()
|
||||
|
||||
expect(github.mockOctokit.rest.git.createTag).not.toHaveBeenCalled()
|
||||
expect(github.mockOctokit.rest.git.createRef).not.toHaveBeenCalled()
|
||||
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
"[dry-run] Would create tag 'v1' at commit SHA sha-abc123 (annotated)."
|
||||
)
|
||||
|
||||
expect(getOutputs()).toEqual({
|
||||
created: [],
|
||||
updated: [],
|
||||
skipped: [],
|
||||
tags: []
|
||||
})
|
||||
})
|
||||
|
||||
it('executes normally when dry_run is false', async () => {
|
||||
setupInputs({
|
||||
tags: 'v1',
|
||||
ref: 'abc123',
|
||||
github_token: 'test-token',
|
||||
when_exists: 'update',
|
||||
dry_run: false
|
||||
})
|
||||
setupCommitResolver('sha-abc123')
|
||||
setupTagDoesNotExist()
|
||||
|
||||
await run()
|
||||
|
||||
// Should actually create the tag
|
||||
expect(github.mockOctokit.rest.git.createRef).toHaveBeenCalledTimes(1)
|
||||
expect(github.mockOctokit.rest.git.createRef).toHaveBeenCalledWith({
|
||||
owner: 'test-owner',
|
||||
repo: 'test-repo',
|
||||
ref: 'refs/tags/v1',
|
||||
sha: 'sha-abc123'
|
||||
})
|
||||
|
||||
expect(getOutputs()).toEqual({
|
||||
created: ['v1'],
|
||||
updated: [],
|
||||
skipped: [],
|
||||
tags: ['v1']
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('derive_from input', () => {
|
||||
it('derives tags from semver using default template', async () => {
|
||||
setupInputs({
|
||||
derive_from: 'v1.2.3',
|
||||
ref: 'abc123',
|
||||
github_token: 'test-token',
|
||||
when_exists: 'update'
|
||||
})
|
||||
setupCommitResolver('sha-abc123')
|
||||
setupTagDoesNotExist()
|
||||
|
||||
await run()
|
||||
|
||||
expect(github.mockOctokit.rest.git.createRef).toHaveBeenCalledTimes(2)
|
||||
expect(github.mockOctokit.rest.git.createRef).toHaveBeenCalledWith({
|
||||
owner: 'test-owner',
|
||||
repo: 'test-repo',
|
||||
ref: 'refs/tags/v1',
|
||||
sha: 'sha-abc123'
|
||||
})
|
||||
expect(github.mockOctokit.rest.git.createRef).toHaveBeenCalledWith({
|
||||
owner: 'test-owner',
|
||||
repo: 'test-repo',
|
||||
ref: 'refs/tags/v1.2',
|
||||
sha: 'sha-abc123'
|
||||
})
|
||||
|
||||
expect(getOutputs()).toEqual({
|
||||
created: ['v1', 'v1.2'],
|
||||
updated: [],
|
||||
skipped: [],
|
||||
tags: ['v1', 'v1.2']
|
||||
})
|
||||
})
|
||||
|
||||
it('derives tags using custom template', async () => {
|
||||
setupInputs({
|
||||
derive_from: 'v2.5.0',
|
||||
derive_from_template: '{{prefix}}{{major}}',
|
||||
ref: 'abc123',
|
||||
github_token: 'test-token',
|
||||
when_exists: 'update'
|
||||
})
|
||||
setupCommitResolver('sha-abc123')
|
||||
setupTagDoesNotExist()
|
||||
|
||||
await run()
|
||||
|
||||
expect(github.mockOctokit.rest.git.createRef).toHaveBeenCalledTimes(1)
|
||||
expect(github.mockOctokit.rest.git.createRef).toHaveBeenCalledWith({
|
||||
owner: 'test-owner',
|
||||
repo: 'test-repo',
|
||||
ref: 'refs/tags/v2',
|
||||
sha: 'sha-abc123'
|
||||
})
|
||||
|
||||
expect(getOutputs()).toEqual({
|
||||
created: ['v2'],
|
||||
updated: [],
|
||||
skipped: [],
|
||||
tags: ['v2']
|
||||
})
|
||||
})
|
||||
|
||||
it('combines derive_from with explicit tags', async () => {
|
||||
setupInputs({
|
||||
tags: 'latest',
|
||||
derive_from: 'v1.0.0',
|
||||
ref: 'abc123',
|
||||
github_token: 'test-token',
|
||||
when_exists: 'update'
|
||||
})
|
||||
setupCommitResolver('sha-abc123')
|
||||
setupTagDoesNotExist()
|
||||
|
||||
await run()
|
||||
|
||||
expect(github.mockOctokit.rest.git.createRef).toHaveBeenCalledTimes(3)
|
||||
expect(github.mockOctokit.rest.git.createRef).toHaveBeenCalledWith({
|
||||
owner: 'test-owner',
|
||||
repo: 'test-repo',
|
||||
ref: 'refs/tags/latest',
|
||||
sha: 'sha-abc123'
|
||||
})
|
||||
expect(github.mockOctokit.rest.git.createRef).toHaveBeenCalledWith({
|
||||
owner: 'test-owner',
|
||||
repo: 'test-repo',
|
||||
ref: 'refs/tags/v1',
|
||||
sha: 'sha-abc123'
|
||||
})
|
||||
expect(github.mockOctokit.rest.git.createRef).toHaveBeenCalledWith({
|
||||
owner: 'test-owner',
|
||||
repo: 'test-repo',
|
||||
ref: 'refs/tags/v1.0',
|
||||
sha: 'sha-abc123'
|
||||
})
|
||||
|
||||
expect(getOutputs()).toEqual({
|
||||
created: ['latest', 'v1', 'v1.0'],
|
||||
updated: [],
|
||||
skipped: [],
|
||||
tags: ['latest', 'v1', 'v1.0']
|
||||
})
|
||||
})
|
||||
|
||||
it('handles version without v prefix', async () => {
|
||||
setupInputs({
|
||||
derive_from: '3.0.0',
|
||||
ref: 'abc123',
|
||||
github_token: 'test-token',
|
||||
when_exists: 'update'
|
||||
})
|
||||
setupCommitResolver('sha-abc123')
|
||||
setupTagDoesNotExist()
|
||||
|
||||
await run()
|
||||
|
||||
expect(github.mockOctokit.rest.git.createRef).toHaveBeenCalledTimes(2)
|
||||
expect(github.mockOctokit.rest.git.createRef).toHaveBeenCalledWith({
|
||||
owner: 'test-owner',
|
||||
repo: 'test-repo',
|
||||
ref: 'refs/tags/3',
|
||||
sha: 'sha-abc123'
|
||||
})
|
||||
expect(github.mockOctokit.rest.git.createRef).toHaveBeenCalledWith({
|
||||
owner: 'test-owner',
|
||||
repo: 'test-repo',
|
||||
ref: 'refs/tags/3.0',
|
||||
sha: 'sha-abc123'
|
||||
})
|
||||
|
||||
expect(getOutputs()).toEqual({
|
||||
created: ['3', '3.0'],
|
||||
updated: [],
|
||||
skipped: [],
|
||||
tags: ['3', '3.0']
|
||||
})
|
||||
})
|
||||
|
||||
it('fails when neither tags nor derive_from is provided', async () => {
|
||||
setupInputs({
|
||||
ref: 'abc123',
|
||||
github_token: 'test-token',
|
||||
when_exists: 'update'
|
||||
})
|
||||
|
||||
await run()
|
||||
|
||||
expect(core.setFailed).toHaveBeenCalledWith(
|
||||
"No tags specified. Provide 'tags' input, 'derive_from' input, or both."
|
||||
)
|
||||
})
|
||||
|
||||
it('fails on invalid semver in derive_from', async () => {
|
||||
setupInputs({
|
||||
derive_from: 'not-a-version',
|
||||
ref: 'abc123',
|
||||
github_token: 'test-token',
|
||||
when_exists: 'update'
|
||||
})
|
||||
|
||||
await run()
|
||||
|
||||
expect(core.setFailed).toHaveBeenCalledWith(
|
||||
"Invalid semver: 'not-a-version'"
|
||||
)
|
||||
})
|
||||
|
||||
it('deduplicates when tags and derive_from produce overlapping tags', async () => {
|
||||
setupInputs({
|
||||
tags: 'v1,v1.2',
|
||||
derive_from: 'v1.2.3', // Derives v1 and v1.2, overlapping with explicit
|
||||
ref: 'abc123',
|
||||
github_token: 'test-token',
|
||||
when_exists: 'update'
|
||||
})
|
||||
setupCommitResolver('sha-abc123')
|
||||
setupTagDoesNotExist()
|
||||
|
||||
await run()
|
||||
|
||||
// Should only create 2 tags (deduplicated), not 4
|
||||
expect(github.mockOctokit.rest.git.createRef).toHaveBeenCalledTimes(2)
|
||||
expect(github.mockOctokit.rest.git.createRef).toHaveBeenCalledWith({
|
||||
owner: 'test-owner',
|
||||
repo: 'test-repo',
|
||||
ref: 'refs/tags/v1',
|
||||
sha: 'sha-abc123'
|
||||
})
|
||||
expect(github.mockOctokit.rest.git.createRef).toHaveBeenCalledWith({
|
||||
owner: 'test-owner',
|
||||
repo: 'test-repo',
|
||||
ref: 'refs/tags/v1.2',
|
||||
sha: 'sha-abc123'
|
||||
})
|
||||
|
||||
expect(getOutputs()).toEqual({
|
||||
created: ['v1', 'v1.2'],
|
||||
updated: [],
|
||||
skipped: [],
|
||||
tags: ['v1', 'v1.2']
|
||||
})
|
||||
})
|
||||
|
||||
it('fails when tags and derive_from produce same tag with different refs', async () => {
|
||||
setupInputs({
|
||||
tags: 'v1:main', // Explicit v1 pointing to main
|
||||
derive_from: 'v1.2.3', // Derives v1 (using default ref)
|
||||
ref: 'develop', // Different default ref
|
||||
github_token: 'test-token',
|
||||
when_exists: 'update'
|
||||
})
|
||||
|
||||
await run()
|
||||
|
||||
expect(core.setFailed).toHaveBeenCalledWith(
|
||||
"Duplicate tag 'v1' with different refs: 'main' and 'develop'"
|
||||
)
|
||||
expect(github.mockOctokit.rest.git.createRef).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('per-tag annotations', () => {
|
||||
it('creates tag with per-tag annotation using empty ref fallback', async () => {
|
||||
setupInputs({
|
||||
tags: 'v1::Per-tag annotation',
|
||||
ref: 'main',
|
||||
github_token: 'test-token',
|
||||
when_exists: 'update'
|
||||
})
|
||||
setupCommitResolver('sha-main')
|
||||
setupTagDoesNotExist()
|
||||
|
||||
github.mockOctokit.rest.git.createTag.mockResolvedValue({
|
||||
data: { sha: 'sha-tag-object-v1' }
|
||||
})
|
||||
|
||||
await run()
|
||||
|
||||
expect(github.mockOctokit.rest.git.createTag).toHaveBeenCalledWith({
|
||||
owner: 'test-owner',
|
||||
repo: 'test-repo',
|
||||
tag: 'v1',
|
||||
message: 'Per-tag annotation',
|
||||
object: 'sha-main',
|
||||
type: 'commit'
|
||||
})
|
||||
expect(github.mockOctokit.rest.git.createRef).toHaveBeenCalledWith({
|
||||
owner: 'test-owner',
|
||||
repo: 'test-repo',
|
||||
ref: 'refs/tags/v1',
|
||||
sha: 'sha-tag-object-v1'
|
||||
})
|
||||
expect(getOutputs()).toEqual({
|
||||
created: ['v1'],
|
||||
updated: [],
|
||||
skipped: [],
|
||||
tags: ['v1']
|
||||
})
|
||||
})
|
||||
|
||||
it('handles per-tag annotation containing colons', async () => {
|
||||
setupInputs({
|
||||
tags: 'v1:main:Release: version 1.0.0: stable',
|
||||
ref: '',
|
||||
github_token: 'test-token',
|
||||
when_exists: 'update'
|
||||
})
|
||||
setupCommitResolver({ main: 'sha-main' })
|
||||
setupTagDoesNotExist()
|
||||
|
||||
github.mockOctokit.rest.git.createTag.mockResolvedValue({
|
||||
data: { sha: 'sha-tag-object-v1' }
|
||||
})
|
||||
|
||||
await run()
|
||||
|
||||
expect(github.mockOctokit.rest.git.createTag).toHaveBeenCalledWith({
|
||||
owner: 'test-owner',
|
||||
repo: 'test-repo',
|
||||
tag: 'v1',
|
||||
message: 'Release: version 1.0.0: stable',
|
||||
object: 'sha-main',
|
||||
type: 'commit'
|
||||
})
|
||||
})
|
||||
|
||||
it('mixes per-tag annotations with global annotation', async () => {
|
||||
setupInputs({
|
||||
tags: 'v1:main:Custom message,v2',
|
||||
ref: 'main',
|
||||
github_token: 'test-token',
|
||||
when_exists: 'update',
|
||||
annotation: 'Global annotation'
|
||||
})
|
||||
setupCommitResolver('sha-main')
|
||||
setupTagDoesNotExist()
|
||||
|
||||
github.mockOctokit.rest.git.createTag.mockImplementation(
|
||||
async (args: unknown) => {
|
||||
const { tag } = args as { tag: string }
|
||||
return { data: { sha: `sha-tag-object-${tag}` } }
|
||||
}
|
||||
)
|
||||
|
||||
await run()
|
||||
|
||||
expect(github.mockOctokit.rest.git.createTag).toHaveBeenCalledTimes(2)
|
||||
expect(github.mockOctokit.rest.git.createTag).toHaveBeenCalledWith({
|
||||
owner: 'test-owner',
|
||||
repo: 'test-repo',
|
||||
tag: 'v1',
|
||||
message: 'Custom message',
|
||||
object: 'sha-main',
|
||||
type: 'commit'
|
||||
})
|
||||
expect(github.mockOctokit.rest.git.createTag).toHaveBeenCalledWith({
|
||||
owner: 'test-owner',
|
||||
repo: 'test-repo',
|
||||
tag: 'v2',
|
||||
message: 'Global annotation',
|
||||
object: 'sha-main',
|
||||
type: 'commit'
|
||||
})
|
||||
})
|
||||
|
||||
it('per-tag annotation overrides global annotation', async () => {
|
||||
setupInputs({
|
||||
tags: 'v1:main:Per-tag wins',
|
||||
ref: 'main',
|
||||
github_token: 'test-token',
|
||||
when_exists: 'update',
|
||||
annotation: 'Global annotation should be ignored'
|
||||
})
|
||||
setupCommitResolver('sha-main')
|
||||
setupTagDoesNotExist()
|
||||
|
||||
github.mockOctokit.rest.git.createTag.mockResolvedValue({
|
||||
data: { sha: 'sha-tag-object-v1' }
|
||||
})
|
||||
|
||||
await run()
|
||||
|
||||
expect(github.mockOctokit.rest.git.createTag).toHaveBeenCalledWith({
|
||||
owner: 'test-owner',
|
||||
repo: 'test-repo',
|
||||
tag: 'v1',
|
||||
message: 'Per-tag wins',
|
||||
object: 'sha-main',
|
||||
type: 'commit'
|
||||
})
|
||||
})
|
||||
|
||||
it('updates tag when per-tag annotation differs from existing', async () => {
|
||||
setupInputs({
|
||||
tags: 'v1:main:New annotation',
|
||||
ref: '',
|
||||
github_token: 'test-token',
|
||||
when_exists: 'update'
|
||||
})
|
||||
setupCommitResolver({ main: 'sha-main' })
|
||||
|
||||
// Tag exists as annotated with different message but same commit
|
||||
github.mockOctokit.rest.git.getRef.mockResolvedValue({
|
||||
data: {
|
||||
ref: 'refs/tags/v1',
|
||||
object: { sha: 'sha-tag-object-old', type: 'tag' }
|
||||
}
|
||||
})
|
||||
github.mockOctokit.rest.git.getTag.mockResolvedValue({
|
||||
data: {
|
||||
sha: 'sha-tag-object-old',
|
||||
message: 'Old annotation',
|
||||
object: { sha: 'sha-main', type: 'commit' }
|
||||
}
|
||||
})
|
||||
github.mockOctokit.rest.git.createTag.mockResolvedValue({
|
||||
data: { sha: 'sha-tag-object-new' }
|
||||
})
|
||||
|
||||
await run()
|
||||
|
||||
expect(github.mockOctokit.rest.git.createTag).toHaveBeenCalledWith({
|
||||
owner: 'test-owner',
|
||||
repo: 'test-repo',
|
||||
tag: 'v1',
|
||||
message: 'New annotation',
|
||||
object: 'sha-main',
|
||||
type: 'commit'
|
||||
})
|
||||
expect(github.mockOctokit.rest.git.updateRef).toHaveBeenCalledWith({
|
||||
owner: 'test-owner',
|
||||
repo: 'test-repo',
|
||||
ref: 'tags/v1',
|
||||
sha: 'sha-tag-object-new',
|
||||
force: true
|
||||
})
|
||||
expect(getOutputs()).toEqual({
|
||||
created: [],
|
||||
updated: ['v1'],
|
||||
skipped: [],
|
||||
tags: ['v1']
|
||||
})
|
||||
})
|
||||
|
||||
it('fails when empty tag name with per-tag annotation', async () => {
|
||||
setupInputs({
|
||||
tags: '::Some annotation',
|
||||
ref: 'main',
|
||||
github_token: 'test-token'
|
||||
})
|
||||
|
||||
await run()
|
||||
|
||||
expect(core.setFailed).toHaveBeenCalledWith(
|
||||
"Invalid tag: '::Some annotation'"
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -4,14 +4,262 @@
|
||||
import { jest } from '@jest/globals'
|
||||
import * as core from './fixtures/core.js'
|
||||
import * as github from './fixtures/github.js'
|
||||
import type { Inputs } from '../src/inputs.js'
|
||||
|
||||
// Mocks should be declared before the module being tested is imported.
|
||||
jest.unstable_mockModule('@actions/core', () => core)
|
||||
jest.unstable_mockModule('@actions/github', () => github)
|
||||
|
||||
// The module being tested should be imported dynamically.
|
||||
const { executeTagOperation } = await import('../src/tags.js')
|
||||
import type { TagOperation } from '../src/tags.js'
|
||||
const { executeTagOperation, planTagOperations } =
|
||||
await import('../src/tags.js')
|
||||
import type {
|
||||
TagOperation,
|
||||
CreateOperation,
|
||||
UpdateOperation
|
||||
} from '../src/tags.js'
|
||||
|
||||
// Helper to create a minimal Inputs object for testing
|
||||
const createInputs = (overrides: Partial<Inputs> = {}): Inputs => ({
|
||||
tags: [],
|
||||
defaultRef: 'main',
|
||||
whenExists: 'update',
|
||||
annotation: '',
|
||||
dryRun: false,
|
||||
owner: 'test-owner',
|
||||
repo: 'test-repo',
|
||||
token: 'test-token',
|
||||
...overrides
|
||||
})
|
||||
|
||||
describe('planTagOperations', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks()
|
||||
github.getOctokit.mockReturnValue(github.mockOctokit)
|
||||
})
|
||||
|
||||
const setupCommitResolver = (
|
||||
refToSha: Record<string, string> | string
|
||||
): void => {
|
||||
if (typeof refToSha === 'string') {
|
||||
github.mockOctokit.rest.repos.getCommit.mockResolvedValue({
|
||||
data: { sha: refToSha }
|
||||
})
|
||||
} else {
|
||||
github.mockOctokit.rest.repos.getCommit.mockImplementation(
|
||||
async (args: unknown) => {
|
||||
const { ref } = args as { ref: string }
|
||||
const sha = refToSha[ref]
|
||||
if (sha) return { data: { sha } }
|
||||
throw new Error(`Unknown ref: ${ref}`)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const setupTagDoesNotExist = (): void => {
|
||||
github.mockOctokit.rest.git.getRef.mockRejectedValue({
|
||||
status: 404
|
||||
})
|
||||
}
|
||||
|
||||
describe('per-tag annotations', () => {
|
||||
it('parses per-tag annotation with explicit ref', async () => {
|
||||
const inputs = createInputs({
|
||||
tags: ['v1:main:Custom annotation'],
|
||||
annotation: 'Global annotation'
|
||||
})
|
||||
setupCommitResolver('sha-main')
|
||||
setupTagDoesNotExist()
|
||||
|
||||
const operations = await planTagOperations(
|
||||
inputs,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
github.mockOctokit as any
|
||||
)
|
||||
|
||||
expect(operations).toHaveLength(1)
|
||||
expect(operations[0].operation).toBe('create')
|
||||
expect((operations[0] as CreateOperation).annotation).toBe(
|
||||
'Custom annotation'
|
||||
)
|
||||
})
|
||||
|
||||
it('parses per-tag annotation with empty ref (fallback to default)', async () => {
|
||||
const inputs = createInputs({
|
||||
tags: ['v1::Custom annotation'],
|
||||
defaultRef: 'develop',
|
||||
annotation: 'Global annotation'
|
||||
})
|
||||
setupCommitResolver('sha-develop')
|
||||
setupTagDoesNotExist()
|
||||
|
||||
const operations = await planTagOperations(
|
||||
inputs,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
github.mockOctokit as any
|
||||
)
|
||||
|
||||
expect(operations).toHaveLength(1)
|
||||
expect(operations[0].operation).toBe('create')
|
||||
expect(operations[0].ref).toBe('develop')
|
||||
expect((operations[0] as CreateOperation).annotation).toBe(
|
||||
'Custom annotation'
|
||||
)
|
||||
})
|
||||
|
||||
it('handles annotation containing colons', async () => {
|
||||
const inputs = createInputs({
|
||||
tags: ['v1:main:Release: v1.0.0'],
|
||||
annotation: ''
|
||||
})
|
||||
setupCommitResolver('sha-main')
|
||||
setupTagDoesNotExist()
|
||||
|
||||
const operations = await planTagOperations(
|
||||
inputs,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
github.mockOctokit as any
|
||||
)
|
||||
|
||||
expect(operations).toHaveLength(1)
|
||||
expect((operations[0] as CreateOperation).annotation).toBe(
|
||||
'Release: v1.0.0'
|
||||
)
|
||||
})
|
||||
|
||||
it('falls back to global annotation when per-tag not specified', async () => {
|
||||
const inputs = createInputs({
|
||||
tags: ['v1', 'v2:main'],
|
||||
annotation: 'Global annotation'
|
||||
})
|
||||
setupCommitResolver('sha-main')
|
||||
setupTagDoesNotExist()
|
||||
|
||||
const operations = await planTagOperations(
|
||||
inputs,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
github.mockOctokit as any
|
||||
)
|
||||
|
||||
expect(operations).toHaveLength(2)
|
||||
expect((operations[0] as CreateOperation).annotation).toBe(
|
||||
'Global annotation'
|
||||
)
|
||||
expect((operations[1] as CreateOperation).annotation).toBe(
|
||||
'Global annotation'
|
||||
)
|
||||
})
|
||||
|
||||
it('mixes per-tag and global annotations', async () => {
|
||||
const inputs = createInputs({
|
||||
tags: ['v1:main:Per-tag message', 'v2'],
|
||||
annotation: 'Global annotation'
|
||||
})
|
||||
setupCommitResolver('sha-main')
|
||||
setupTagDoesNotExist()
|
||||
|
||||
const operations = await planTagOperations(
|
||||
inputs,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
github.mockOctokit as any
|
||||
)
|
||||
|
||||
expect(operations).toHaveLength(2)
|
||||
expect((operations[0] as CreateOperation).annotation).toBe(
|
||||
'Per-tag message'
|
||||
)
|
||||
expect((operations[1] as CreateOperation).annotation).toBe(
|
||||
'Global annotation'
|
||||
)
|
||||
})
|
||||
|
||||
it('uses per-tag annotation for update comparison', async () => {
|
||||
const inputs = createInputs({
|
||||
tags: ['v1:main:New annotation'],
|
||||
annotation: 'Global annotation',
|
||||
whenExists: 'update'
|
||||
})
|
||||
setupCommitResolver('sha-main')
|
||||
|
||||
// Tag exists with same commit but different annotation
|
||||
github.mockOctokit.rest.git.getRef.mockResolvedValue({
|
||||
data: {
|
||||
ref: 'refs/tags/v1',
|
||||
object: { sha: 'sha-tag-object', type: 'tag' }
|
||||
}
|
||||
})
|
||||
github.mockOctokit.rest.git.getTag.mockResolvedValue({
|
||||
data: {
|
||||
sha: 'sha-tag-object',
|
||||
message: 'Old annotation',
|
||||
object: { sha: 'sha-main', type: 'commit' }
|
||||
}
|
||||
})
|
||||
|
||||
const operations = await planTagOperations(
|
||||
inputs,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
github.mockOctokit as any
|
||||
)
|
||||
|
||||
expect(operations).toHaveLength(1)
|
||||
expect(operations[0].operation).toBe('update')
|
||||
expect((operations[0] as UpdateOperation).annotation).toBe(
|
||||
'New annotation'
|
||||
)
|
||||
expect((operations[0] as UpdateOperation).reasons).toContain(
|
||||
'annotation message changed'
|
||||
)
|
||||
})
|
||||
|
||||
it('skips tag when per-tag annotation matches existing', async () => {
|
||||
const inputs = createInputs({
|
||||
tags: ['v1:main:Same annotation'],
|
||||
annotation: 'Global annotation',
|
||||
whenExists: 'update'
|
||||
})
|
||||
setupCommitResolver('sha-main')
|
||||
|
||||
github.mockOctokit.rest.git.getRef.mockResolvedValue({
|
||||
data: {
|
||||
ref: 'refs/tags/v1',
|
||||
object: { sha: 'sha-tag-object', type: 'tag' }
|
||||
}
|
||||
})
|
||||
github.mockOctokit.rest.git.getTag.mockResolvedValue({
|
||||
data: {
|
||||
sha: 'sha-tag-object',
|
||||
message: 'Same annotation',
|
||||
object: { sha: 'sha-main', type: 'commit' }
|
||||
}
|
||||
})
|
||||
|
||||
const operations = await planTagOperations(
|
||||
inputs,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
github.mockOctokit as any
|
||||
)
|
||||
|
||||
expect(operations).toHaveLength(1)
|
||||
expect(operations[0].operation).toBe('skip')
|
||||
})
|
||||
|
||||
it('rejects empty tag name with annotation', async () => {
|
||||
const inputs = createInputs({
|
||||
tags: ['::Some annotation']
|
||||
})
|
||||
|
||||
await expect(
|
||||
planTagOperations(
|
||||
inputs,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
github.mockOctokit as any
|
||||
)
|
||||
).rejects.toThrow("Invalid tag: '::Some annotation'")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('executeTagOperation', () => {
|
||||
beforeEach(() => {
|
||||
|
||||
Reference in New Issue
Block a user