Compare commits
177 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
339a81892b | ||
|
|
df17233557 | ||
|
|
813e942459 | ||
|
|
7df3a0e749 | ||
|
|
caacf56b56 | ||
|
|
c074b5e19f | ||
|
|
9e0366240f | ||
|
|
5b4b1954b0 | ||
|
|
89bab4d0a7 | ||
|
|
00cbfdc960 | ||
|
|
3f973430f2 | ||
|
|
5b3313d377 | ||
|
|
7886be8054 | ||
|
|
411d95d6a3 | ||
|
|
72a3e11e23 | ||
|
|
fc196c3dda | ||
|
|
788c17510f | ||
|
|
93682e08f2 | ||
|
|
5f55df31e1 | ||
|
|
be9a4fac3a | ||
|
|
23d2cc4c27 | ||
|
|
c15e4e52f5 | ||
|
|
b9387a1970 | ||
|
|
327ba10342 | ||
|
|
5e58a022dc | ||
|
|
7ffaa93f91 | ||
|
|
c266243d9d | ||
|
|
29d665a1e2 | ||
|
|
8c0975efae | ||
|
|
015a5adb5c | ||
|
|
73edeebfa1 | ||
|
|
cacc3525a9 | ||
|
|
0dda22e486 | ||
|
|
e00665902f | ||
|
|
a0e7822741 | ||
|
|
206b0f552d | ||
|
|
fb82a3ec11 | ||
|
|
4207a7677e | ||
|
|
b7eabc95ff | ||
|
|
e87de4c2e4 | ||
|
|
98d25d4189 | ||
|
|
d3601261f8 | ||
|
|
571fe81601 | ||
|
|
1c89adf398 | ||
|
|
36bf8dd70f | ||
|
|
b12185d71f | ||
|
|
defcf131e4 | ||
|
|
05013d58ed | ||
|
|
20ce211d17 | ||
|
|
a262ce99ce | ||
|
|
bcfe547070 | ||
|
|
707331a88d | ||
|
|
9128f238ee | ||
|
|
7922049688 | ||
|
|
0683ea3557 | ||
|
|
01c9fac73b | ||
|
|
90dc22b77f | ||
|
|
10585cb926 | ||
|
|
1896b4fc46 | ||
|
|
6996c1bc95 | ||
|
|
c07d979560 | ||
|
|
38da029bb7 | ||
|
|
bf17a40975 | ||
|
|
fa4749311e | ||
|
|
36e78ab629 | ||
|
|
6c1f9baed2 | ||
|
|
db62f3f3ad | ||
|
|
e6992e14b1 | ||
|
|
440c8c1cb0 | ||
|
|
e805702217 | ||
|
|
bd572a1942 | ||
|
|
706b28e44c | ||
|
|
411ac011df | ||
|
|
a8c5a7f252 | ||
|
|
62f16c02e7 | ||
|
|
00e83e3d35 | ||
|
|
906fc77113 | ||
|
|
17b559883e | ||
|
|
33bf18d283 | ||
|
|
009df9782d | ||
|
|
4b84ad5c60 | ||
|
|
34dae7e8c0 | ||
|
|
9fc271e109 | ||
|
|
3cfa40eca4 | ||
|
|
cdcc88a9ac | ||
|
|
033b82b409 | ||
|
|
e774b3eb81 | ||
|
|
80732ccbb8 | ||
|
|
8b72352976 | ||
|
|
e1e10133db | ||
|
|
7fdd38c8a8 | ||
|
|
fb175cca64 | ||
|
|
c4d491c3c9 | ||
|
|
aa3b2fa675 | ||
|
|
215ba89ed6 | ||
|
|
a8bcd956fb | ||
|
|
bb3709bff2 | ||
|
|
e9a92928b6 | ||
|
|
765cc979be | ||
|
|
00fc285722 | ||
|
|
1cbdc80532 | ||
|
|
4b91e3add1 | ||
|
|
b6d6ead779 | ||
|
|
1da2ee60a2 | ||
|
|
4f8867fa5e | ||
|
|
f4b7f82f58 | ||
|
|
4074199da7 | ||
|
|
578e4141d1 | ||
|
|
a36404d62c | ||
|
|
7588f628d1 | ||
|
|
1e3e9c6637 | ||
|
|
c03240caba | ||
|
|
66b1844f0b | ||
|
|
a8aa04e74a | ||
|
|
39c57feb7b | ||
|
|
02a91b5ddd | ||
|
|
0300282a53 | ||
|
|
c4a7702e18 | ||
|
|
4f53bc4bbe | ||
|
|
37813b746a | ||
|
|
b878ec7b82 | ||
|
|
3453d72d33 | ||
|
|
712dbe3d31 | ||
|
|
a454b0181b | ||
|
|
454de296e8 | ||
|
|
823125756f | ||
|
|
53f1c32531 | ||
|
|
7b2b452a12 | ||
|
|
a7c3669135 | ||
|
|
26cdd97dfd | ||
|
|
5a5f227332 | ||
|
|
9324133e56 | ||
|
|
25918b039f | ||
|
|
c18db47aba | ||
|
|
d968c2d7a7 | ||
|
|
e386e41d9f | ||
|
|
476ee61234 | ||
|
|
6db48ced34 | ||
|
|
7ce102b15b | ||
|
|
ddbfce0d45 | ||
|
|
b4b70f8196 | ||
|
|
2600a44651 | ||
|
|
eb7dfdf4be | ||
|
|
e2ce571994 | ||
|
|
69d25a3184 | ||
|
|
b36d9409a9 | ||
|
|
6c75be85e5 | ||
|
|
2d19fb459b | ||
|
|
9cfd0835c6 | ||
|
|
37c87f6b53 | ||
|
|
7288236d4e | ||
|
|
384ae1fad6 | ||
|
|
9347837845 | ||
|
|
893ce36b2e | ||
|
|
c07e9f4dff | ||
|
|
cb77c9ee9c | ||
|
|
8c78ca2662 | ||
|
|
2b385230bf | ||
|
|
3ff6c7eb03 | ||
|
|
3aa9307b7d | ||
|
|
c13ca79f19 | ||
|
|
e9a4ba6596 | ||
|
|
ef679c1c54 | ||
|
|
eb05307dce | ||
|
|
5a8bbe8ea7 | ||
|
|
a48ee3f85e | ||
|
|
bf3b51b3f3 | ||
|
|
67d60d576c | ||
|
|
0371ac0a22 | ||
|
|
231dd3fbcd | ||
|
|
d51f1872a4 | ||
|
|
324f6f4a19 | ||
|
|
ef3e5b02bd | ||
|
|
215101a6e6 | ||
|
|
a5996b3a73 | ||
|
|
8fd54fa522 | ||
|
|
c358143117 |
21
.github/workflows/build.yml
vendored
21
.github/workflows/build.yml
vendored
@@ -11,18 +11,27 @@ jobs:
|
|||||||
check_pr:
|
check_pr:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
version: latest
|
||||||
|
|
||||||
- name: "yarn install"
|
- uses: actions/setup-node@v6
|
||||||
run: yarn install
|
with:
|
||||||
|
node-version: 20
|
||||||
|
cache: 'pnpm'
|
||||||
|
|
||||||
- name: "yarn build"
|
- name: "pnpm install"
|
||||||
run: yarn build
|
run: pnpm install
|
||||||
|
|
||||||
|
- name: "pnpm build"
|
||||||
|
run: pnpm build
|
||||||
|
|
||||||
- name: "check for uncommitted changes"
|
- name: "check for uncommitted changes"
|
||||||
# Ensure no changes, but ignore node_modules dir since dev/fresh ci deps installed.
|
# Ensure no changes, but ignore node_modules dir since dev/fresh ci deps installed.
|
||||||
run: |
|
run: |
|
||||||
git diff --exit-code --stat -- . ':!node_modules' \
|
git diff --exit-code --stat -- . ':!node_modules' \
|
||||||
|| (echo "##[error] found changed files after build. please 'yarn build && npm run format'" \
|
|| (echo "##[error] found changed files after build. please 'pnpm build && pnpm run format'" \
|
||||||
"and check in all changes" \
|
"and check in all changes" \
|
||||||
&& exit 1)
|
&& exit 1)
|
||||||
|
|||||||
22
.github/workflows/release.yml
vendored
Normal file
22
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
name: Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
create_release:
|
||||||
|
if: github.ref != 'refs/tags/v1'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Create Release
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- name: Create Release
|
||||||
|
id: create_release
|
||||||
|
uses: ncipollo/release-action@v1
|
||||||
|
with:
|
||||||
|
allowUpdates: true
|
||||||
|
draft: true
|
||||||
|
generateReleaseNotes: true
|
||||||
19
.github/workflows/test.yml
vendored
19
.github/workflows/test.yml
vendored
@@ -5,10 +5,19 @@ jobs:
|
|||||||
check_pr:
|
check_pr:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
version: latest
|
||||||
|
|
||||||
- name: "yarn install"
|
- uses: actions/setup-node@v6
|
||||||
run: yarn install
|
with:
|
||||||
|
node-version: 20
|
||||||
|
cache: 'pnpm'
|
||||||
|
|
||||||
- name: "yarn test"
|
- name: "pnpm install"
|
||||||
run: yarn test
|
run: pnpm install
|
||||||
|
|
||||||
|
- name: "pnpm test"
|
||||||
|
run: pnpm test
|
||||||
11
.gitignore
vendored
11
.gitignore
vendored
@@ -1,5 +1,6 @@
|
|||||||
node_modules/
|
node_modules/
|
||||||
__tests__/runner/*
|
__tests__/runner/*
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
# Created by https://www.gitignore.io/api/webstorm
|
# Created by https://www.gitignore.io/api/webstorm
|
||||||
# Edit at https://www.gitignore.io/?templates=webstorm
|
# Edit at https://www.gitignore.io/?templates=webstorm
|
||||||
@@ -91,8 +92,14 @@ fabric.properties
|
|||||||
# Markdown Navigator plugin
|
# Markdown Navigator plugin
|
||||||
.idea/**/markdown-navigator.xml
|
.idea/**/markdown-navigator.xml
|
||||||
.idea/**/markdown-navigator/
|
.idea/**/markdown-navigator/
|
||||||
|
.idea/copilot.*
|
||||||
# End of https://www.gitignore.io/api/webstorm
|
# End of https://www.gitignore.io/api/webstorm
|
||||||
|
|
||||||
# Coverage
|
# Coverage
|
||||||
coverage
|
coverage
|
||||||
|
|
||||||
|
# Ignore lib, it contains intermediates
|
||||||
|
/lib
|
||||||
|
|
||||||
|
# Claude
|
||||||
|
.claude/settings.local.json
|
||||||
6
.idea/biome.xml
generated
Normal file
6
.idea/biome.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="BiomeSettings">
|
||||||
|
<option name="enableLspFormat" value="true" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
68
README.md
68
README.md
@@ -13,42 +13,47 @@ This action will create a GitHub release and optionally upload an artifact to it
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
## Action Inputs
|
## Action Inputs
|
||||||
| Input name | Description | Required | Default Value |
|
| Input name | Description | Required | Default Value |
|
||||||
|----------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------|----------------------|
|
|----------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------|----------------------|
|
||||||
| allowUpdates | An optional flag which indicates if we should update a release if it already exists. Defaults to false. | false | "" |
|
| allowUpdates | An optional flag which indicates if we should update a release if it already exists. Defaults to false. | false | "" |
|
||||||
| artifactErrorsFailBuild | An optional flag which indicates if artifact read or upload errors should fail the build. | false | "" |
|
| artifactErrorsFailBuild | An optional flag which indicates if artifact read or upload errors should fail the build. | false | "" |
|
||||||
| artifacts | An optional set of paths representing artifacts to upload to the release. This may be a single path or a comma delimited list of paths (or globs) | false | "" |
|
| artifacts | An optional set of paths representing artifacts to upload to the release. This may be a single path or a comma delimited list of paths (or globs) | false | "" |
|
||||||
| artifactContentType | The content type of the artifact. Defaults to raw | false | "" |
|
| artifactContentType | The content type of the artifact. Defaults to raw | false | "" |
|
||||||
| body | An optional body for the release. | false | "" |
|
| body | An optional body for the release. Note: This input will have white space trimmed before and after it. Use `bodyFile` if you need a non-trivial markdown body. | false | "" |
|
||||||
| bodyFile | An optional body file for the release. This should be the path to the file. | false | "" |
|
| bodyFile | An optional body file for the release. This should be the path to the file. | false | "" |
|
||||||
| commit | An optional commit reference. This will be used to create the tag if it does not exist. | false | "" |
|
| commit | An optional commit reference. This will be used to create the tag if it does not exist. | false | "" |
|
||||||
| discussionCategory | When provided this will generate a discussion of the specified category. The category must exist otherwise this will cause the action to fail. This isn't used with draft releases | false | "" |
|
| discussionCategory | When provided this will generate a discussion of the specified category. The category must exist otherwise this will cause the action to fail. This isn't used with draft releases. The default "Announcements" category is not supported via the API and will cause an error if used here. | false | "" |
|
||||||
| draft | Optionally marks this release as a draft release. Set to true to enable. | false | "" |
|
| draft | Optionally marks this release as a draft release. Set to true to enable. | false | "" |
|
||||||
| generateReleaseNotes | Indicates if release notes should be automatically generated. | false | false |
|
| generateReleaseNotes | Indicates if release notes should be automatically generated. | false | false |
|
||||||
| makeLatest | Indicates if the release should be the "latest" release or not. | false | "legacy" |
|
| generateReleaseNotesPreviousTag | An optional previous tag to use when generating release notes. This will limit the release notes to changes between the two tags. | false | "" |
|
||||||
| name | An optional name for the release. If this is omitted the tag will be used. | false | "" |
|
| immutableCreate | Indicates if immutable release creation should be used. When enabled, the action will first create a draft, upload artifacts, then publish the release. | false | false |
|
||||||
| omitBody | Indicates if the release body should be omitted. | false | false |
|
| makeLatest | Indicates if the release should be the "latest" release or not. legacy specifies that the latest release should be determined based on the release creation date and higher semantic version. | false | "legacy" |
|
||||||
| omitBodyDuringUpdate | Indicates if the release body should be omitted during updates. The body will still be applied for newly created releases. This will preserve the existing body during updates. | false | false |
|
| name | An optional name for the release. If this is omitted the tag will be used. | false | "" |
|
||||||
| omitDraftDuringUpdate | Indicates if the draft flag should be omitted during updates. The draft flag will still be applied for newly created releases. This will preserve the existing draft state during updates. | false | false |
|
| omitBody | Indicates if the release body should be omitted. | false | false |
|
||||||
| omitName | Indicates if the release name should be omitted. | false | false |
|
| omitBodyDuringUpdate | Indicates if the release body should be omitted during updates. The body will still be applied for newly created releases. This will preserve the existing body during updates. | false | false |
|
||||||
| omitNameDuringUpdate | Indicates if the release name should be omitted during updates. The name will still be applied for newly created releases. This will preserve the existing name during updates. | false | false |
|
| omitDraftDuringUpdate | Indicates if the draft flag should be omitted during updates. The draft flag will still be applied for newly created releases. This will preserve the existing draft state during updates. | false | false |
|
||||||
| omitPrereleaseDuringUpdate | Indicates if the prerelease flag should be omitted during updates. The prerelease flag will still be applied for newly created releases. This will preserve the existing prerelease state during updates. | false | false |
|
| omitName | Indicates if the release name should be omitted. | false | false |
|
||||||
| owner | Optionally specify the owner of the repo where the release should be generated. Defaults to current repo's owner. | false | "current repo owner" |
|
| omitNameDuringUpdate | Indicates if the release name should be omitted during updates. The name will still be applied for newly created releases. This will preserve the existing name during updates. | false | false |
|
||||||
| prerelease | Optionally marks this release as prerelease. Set to true to enable. | false | "" |
|
| omitPrereleaseDuringUpdate | Indicates if the prerelease flag should be omitted during updates. The prerelease flag will still be applied for newly created releases. This will preserve the existing prerelease state during updates. | false | false |
|
||||||
| removeArtifacts | Indicates if existing release artifacts should be removed. | false | false |
|
| owner | Optionally specify the owner of the repo where the release should be generated. Defaults to current repo's owner. | false | "current repo owner" |
|
||||||
| replacesArtifacts | Indicates if existing release artifacts should be replaced. | false | true |
|
| prerelease | Optionally marks this release as prerelease. Set to true to enable. | false | "" |
|
||||||
| repo | Optionally specify the repo where the release should be generated. | false | current repo |
|
| removeArtifacts | Indicates if existing release artifacts should be removed. | false | false |
|
||||||
| skipIfReleaseExists | When skipIfReleaseExists is enabled the action will be skipped if a non-draft release already exists for the provided tag. | false | false |
|
| replacesArtifacts | Indicates if existing release artifacts should be replaced. | false | true |
|
||||||
| tag | An optional tag for the release. If this is omitted the git ref will be used (if it is a tag). | false | "" |
|
| repo | Optionally specify the repo where the release should be generated. | false | current repo |
|
||||||
| token | The GitHub token. This will default to the GitHub app token. This is primarily useful if you want to use your personal token (for targeting other repos, etc). If you are using a personal access token it should have access to the `repo` scope. | false | github.token |
|
| skipIfReleaseExists | When skipIfReleaseExists is enabled the action will be skipped if a non-draft release already exists for the provided tag. | false | false |
|
||||||
| updateOnlyUnreleased | When allowUpdates is enabled, this will fail the action if the release it is updating is not a draft or a prerelease. | false | false |
|
| tag | An optional tag for the release. If this is omitted the git ref will be used (if it is a tag). | false | "" |
|
||||||
|
| token | The GitHub token. This will default to the GitHub app token. This is primarily useful if you want to use your personal token (for targeting other repos, etc). If you are using a personal access token it should have access to the `repo` scope. | false | github.token |
|
||||||
|
| updateOnlyUnreleased | When allowUpdates is enabled, this will fail the action if the release it is updating is not a draft or a prerelease. | false | false |
|
||||||
|
|
||||||
## Action Outputs
|
## Action Outputs
|
||||||
| Output name | Description |
|
| Output name | Description |
|
||||||
|-------------|-----------------------------------------------|
|
|-------------|-----------------------------------------------|
|
||||||
| id | The identifier of the created release. |
|
| id | The identifier of the created release. |
|
||||||
| html_url | The HTML URL of the release. |
|
| html_url | The HTML URL of the release. |
|
||||||
| upload_url | The URL for uploading assets to the release. |
|
| upload_url | The URL for [uploading assets](https://docs.github.com/en/rest/releases/assets?apiVersion=2022-11-28#upload-a-release-asset) to the release. |
|
||||||
|
| tarball_url | The URL for downloading the release as a tarball (.tar.gz). |
|
||||||
|
| zipball_url | The URL for downloading the release as a zipball (.zip). |
|
||||||
|
| assets | JSON string containing a map of asset names to download URLs for uploaded assets. |
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
This example will create a release when a tag is pushed:
|
This example will create a release when a tag is pushed:
|
||||||
@@ -68,7 +73,7 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- uses: ncipollo/release-action@v1
|
- uses: ncipollo/release-action@v1
|
||||||
with:
|
with:
|
||||||
artifacts: "release.tar.gz,foo/*.txt"
|
artifacts: "release.tar.gz,foo/*.txt"
|
||||||
@@ -79,3 +84,4 @@ jobs:
|
|||||||
- You must provide a tag either via the action input or the git ref (i.e push / create a tag). If you do not provide a tag the action will fail.
|
- You must provide a tag either via the action input or the git ref (i.e push / create a tag). If you do not provide a tag the action will fail.
|
||||||
- If the tag of the release you are creating does not yet exist, you should set both the `tag` and `commit` action inputs. `commit` can point to a commit hash or a branch name (ex - `main`).
|
- If the tag of the release you are creating does not yet exist, you should set both the `tag` and `commit` action inputs. `commit` can point to a commit hash or a branch name (ex - `main`).
|
||||||
- In the example above only required permissions for the action specified (which is `contents: write`). If you add other actions to the same workflow you should expand `permissions` block accordingly.
|
- In the example above only required permissions for the action specified (which is `contents: write`). If you add other actions to the same workflow you should expand `permissions` block accordingly.
|
||||||
|
- More info about why the default discussion category "Announcements" does not work with this action may be found here: https://github.com/goreleaser/goreleaser/issues/2304.
|
||||||
@@ -20,12 +20,14 @@
|
|||||||
| artifactErrorsFailBuild | 一个可选标志,表示读取或上传产出文件错误时是否应该使构建失败。 | false | "" |
|
| artifactErrorsFailBuild | 一个可选标志,表示读取或上传产出文件错误时是否应该使构建失败。 | false | "" |
|
||||||
| artifacts | 一组可选的路径,表示要上传到版本的产出文件。 这可能是单个路径或以逗号分隔的路径列表(或 globs) | false | "" |
|
| artifacts | 一组可选的路径,表示要上传到版本的产出文件。 这可能是单个路径或以逗号分隔的路径列表(或 globs) | false | "" |
|
||||||
| artifactContentType | 产出文件的内容类型。 默认为 raw | false | "" |
|
| artifactContentType | 产出文件的内容类型。 默认为 raw | false | "" |
|
||||||
| body | 发布的可选主体。 | false | "" |
|
| body | 发布的可选主体。注意:此处输入的首尾空格会被去除。如果您需要特殊的 markdown 内容,请使用 `bodyFile`。 | false | "" |
|
||||||
| bodyFile | 发布的可选正文文件。 这应该是文件的路径。 | false | "" |
|
| bodyFile | 发布的可选正文文件。 这应该是文件的路径。 | false | "" |
|
||||||
| commit | 一个可选的提交 ref。 如果标签不存在,将用于创建标签。 | false | "" |
|
| commit | 一个可选的提交 ref。 如果标签不存在,将用于创建标签。 | false | "" |
|
||||||
| discussionCategory | 当提供该选项时,将生成对指定类别的 discussion。类别必须存在,否则将导致 Action 失败。这在草案发布中没有使用 | false | "" |
|
| discussionCategory | 当提供该选项时,将生成指定类别的 discussion。指定的类别必须存在,否则将导致 Action 失败。这对草案状态的发布不生效。API 不支持默认的 Announcement 分类,使用这个分类会引发错误。 | false | "" |
|
||||||
| draft | 可选择将此版本标记为草稿版本。 设置为 true 以启用。 | false | "" |
|
| draft | 可选择将此版本标记为草稿版本。 设置为 true 以启用。 | false | "" |
|
||||||
| generateReleaseNotes | 指示是否应自动生成发行说明。 | false | false |
|
| generateReleaseNotes | 指示是否应自动生成发行说明。 | false | false |
|
||||||
|
| immutableCreate | 指示是否应使用不可变发布创建。启用时,操作将首先创建草稿,上传产出文件,然后发布版本。 | false | false |
|
||||||
|
| makeLatest | 指示是否将这个发布设置为 latest。使用"lagecy"值则会根据发布时间和语义化版本号自动决定。 | false | "legacy" |
|
||||||
| name | 版本的可选名称。 如果省略,将使用标签。 | false | "" |
|
| name | 版本的可选名称。 如果省略,将使用标签。 | false | "" |
|
||||||
| omitBody | 指示是否应省略发布主体。 | false | false |
|
| omitBody | 指示是否应省略发布主体。 | false | false |
|
||||||
| omitBodyDuringUpdate | 指示在更新期间是否应省略发布主体。 正文仍将应用于新创建的版本。 这将在更新期间保留现有正文。 | false | false |
|
| omitBodyDuringUpdate | 指示在更新期间是否应省略发布主体。 正文仍将应用于新创建的版本。 这将在更新期间保留现有正文。 | false | false |
|
||||||
@@ -38,6 +40,7 @@
|
|||||||
| removeArtifacts | 指示是否应删除现有的发布产出文件。 | false | false |
|
| removeArtifacts | 指示是否应删除现有的发布产出文件。 | false | false |
|
||||||
| replacesArtifacts | 指示是否应替换现有的发布产出文件。 | false | true |
|
| replacesArtifacts | 指示是否应替换现有的发布产出文件。 | false | true |
|
||||||
| repo | (可选)指定应在其中生成版本的存储库。 | false | current repo |
|
| repo | (可选)指定应在其中生成版本的存储库。 | false | current repo |
|
||||||
|
| skipIfReleaseExists | 当该选项启用时,如果提供的标签已存在一个对应的非草案状态的发布,则 action 将会被跳过。 | false | false |
|
||||||
| tag | 发布的可选标签。 如果省略,将使用 git ref (如果它是标签)。 | false | "" |
|
| tag | 发布的可选标签。 如果省略,将使用 git ref (如果它是标签)。 | false | "" |
|
||||||
| token | GitHub 令牌。 这将默认为 GitHub 应用程序令牌。 如果您想使用您的个人令牌(用于定位其他存储库等),这主要是有用的。 如果您使用的是个人访问令牌,它应该可以访问 `repo` 范围。 | false | github.token |
|
| token | GitHub 令牌。 这将默认为 GitHub 应用程序令牌。 如果您想使用您的个人令牌(用于定位其他存储库等),这主要是有用的。 如果您使用的是个人访问令牌,它应该可以访问 `repo` 范围。 | false | github.token |
|
||||||
| updateOnlyUnreleased | 启用 allowUpdates 后,如果它正在更新的版本不是草稿或预发布,则该操作将失败。 | false | false |
|
| updateOnlyUnreleased | 启用 allowUpdates 后,如果它正在更新的版本不是草稿或预发布,则该操作将失败。 | false | false |
|
||||||
@@ -60,20 +63,20 @@ name: Releases
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
- "*"
|
- '*'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- uses: ncipollo/release-action@v1
|
- uses: ncipollo/release-action@v1
|
||||||
with:
|
with:
|
||||||
artifacts: "release.tar.gz,foo/*.txt"
|
artifacts: "release.tar.gz,foo/*.txt"
|
||||||
bodyFile: "body.md"
|
bodyFile: "body.md"
|
||||||
token: ${{ secrets.YOUR_GITHUB_TOKEN }}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 注意
|
## 注意
|
||||||
@@ -81,3 +84,4 @@ jobs:
|
|||||||
- 您必须通过 Action 输入或 git ref 提供一个标签(即推送/创建标签)。如果不提供标签,Action 将会失败。
|
- 您必须通过 Action 输入或 git ref 提供一个标签(即推送/创建标签)。如果不提供标签,Action 将会失败。
|
||||||
- 如果您正在创建的版本的标签不存在,您应该同时设置标签和提交 Action 输入。 commit 可以指向提交 Hash 或分支名称(例如 - main)。
|
- 如果您正在创建的版本的标签不存在,您应该同时设置标签和提交 Action 输入。 commit 可以指向提交 Hash 或分支名称(例如 - main)。
|
||||||
- 在上面的示例中,只需要指定操作的权限(即 contents: write)。 如果您将其他操作添加到同一工作流程,则应相应地扩展权限。
|
- 在上面的示例中,只需要指定操作的权限(即 contents: write)。 如果您将其他操作添加到同一工作流程,则应相应地扩展权限。
|
||||||
|
- 有关于此 action 无法支持 discussion 的 Announcement 分类的原因的更多信息,请参见此处:https://github.com/goreleaser/goreleaser/issues/2304
|
||||||
|
|||||||
9
__mocks__/@actions/core.ts
Normal file
9
__mocks__/@actions/core.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { vi } from "vitest"
|
||||||
|
|
||||||
|
export const debug = vi.fn()
|
||||||
|
export const getBooleanInput = vi.fn()
|
||||||
|
export const getInput = vi.fn()
|
||||||
|
export const notice = vi.fn()
|
||||||
|
export const setFailed = vi.fn()
|
||||||
|
export const setOutput = vi.fn()
|
||||||
|
export const warning = vi.fn()
|
||||||
8
__mocks__/fs.ts
Normal file
8
__mocks__/fs.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { vi } from "vitest"
|
||||||
|
|
||||||
|
export const createReadStream = vi.fn()
|
||||||
|
export const statSync = vi.fn()
|
||||||
|
export const readFileSync = vi.fn()
|
||||||
|
export const writeFileSync = vi.fn()
|
||||||
|
export const existsSync = vi.fn()
|
||||||
|
export const realpathSync = vi.fn()
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,44 +1,46 @@
|
|||||||
import {ActionSkipper, ReleaseActionSkipper} from "../src/ActionSkipper";
|
import { describe, expect, it, vi } from "vitest"
|
||||||
import {Releases} from "../src/Releases";
|
import { ReleaseActionSkipper } from "../src/ActionSkipper.js"
|
||||||
|
import type { Releases } from "../src/Releases.js"
|
||||||
|
|
||||||
describe("shouldSkip", () => {
|
describe("shouldSkip", () => {
|
||||||
const getMock = jest.fn()
|
const getMock = vi.fn()
|
||||||
const tag = "tag"
|
const tag = "tag"
|
||||||
const MockReleases = jest.fn<Releases, any>(() => {
|
const MockReleases = vi.fn<() => Releases>(() => {
|
||||||
return {
|
return {
|
||||||
create: jest.fn(),
|
create: vi.fn(),
|
||||||
deleteArtifact: jest.fn(),
|
deleteArtifact: vi.fn(),
|
||||||
getByTag: getMock,
|
getByTag: getMock,
|
||||||
listArtifactsForRelease: jest.fn(),
|
listArtifactsForRelease: vi.fn(),
|
||||||
listReleases: jest.fn(),
|
listReleases: vi.fn(),
|
||||||
update: jest.fn(),
|
update: vi.fn(),
|
||||||
uploadArtifact: jest.fn()
|
uploadArtifact: vi.fn(),
|
||||||
|
generateReleaseNotes: vi.fn(),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return false when skipIfReleaseExists is false', async () => {
|
it("should return false when skipIfReleaseExists is false", async () => {
|
||||||
const actionSkipper = new ReleaseActionSkipper(false, MockReleases(), tag)
|
const actionSkipper = new ReleaseActionSkipper(false, MockReleases(), tag)
|
||||||
expect(await actionSkipper.shouldSkip()).toBe(false)
|
expect(await actionSkipper.shouldSkip()).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return false when error occurs', async () => {
|
it("should return false when error occurs", async () => {
|
||||||
getMock.mockRejectedValue(new Error())
|
getMock.mockRejectedValue(new Error())
|
||||||
|
|
||||||
const actionSkipper = new ReleaseActionSkipper(true, MockReleases(), tag)
|
const actionSkipper = new ReleaseActionSkipper(true, MockReleases(), tag)
|
||||||
expect(await actionSkipper.shouldSkip()).toBe(false)
|
expect(await actionSkipper.shouldSkip()).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return false when release does not exist', async () => {
|
it("should return false when release does not exist", async () => {
|
||||||
getMock.mockResolvedValue({})
|
getMock.mockResolvedValue({})
|
||||||
|
|
||||||
const actionSkipper = new ReleaseActionSkipper(true, MockReleases(), tag)
|
const actionSkipper = new ReleaseActionSkipper(true, MockReleases(), tag)
|
||||||
expect(await actionSkipper.shouldSkip()).toBe(false)
|
expect(await actionSkipper.shouldSkip()).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return true when release does exist', async () => {
|
it("should return true when release does exist", async () => {
|
||||||
getMock.mockResolvedValue({data: {}})
|
getMock.mockResolvedValue({ data: {} })
|
||||||
|
|
||||||
const actionSkipper = new ReleaseActionSkipper(true, MockReleases(), tag)
|
const actionSkipper = new ReleaseActionSkipper(true, MockReleases(), tag)
|
||||||
expect(await actionSkipper.shouldSkip()).toBe(true)
|
expect(await actionSkipper.shouldSkip()).toBe(true)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,38 +1,43 @@
|
|||||||
import { Artifact } from "../src/Artifact";
|
import * as fs from "node:fs"
|
||||||
|
import { describe, expect, it, vi } from "vitest"
|
||||||
|
import { Artifact } from "../src/Artifact.js"
|
||||||
|
|
||||||
|
vi.mock("fs")
|
||||||
|
|
||||||
const fileContents = Buffer.from('artful facts', 'utf-8')
|
|
||||||
const contentLength = 42
|
const contentLength = 42
|
||||||
|
const fakeReadStream = {}
|
||||||
|
|
||||||
jest.mock('fs', () => {
|
const mockCreateReadStream = vi.mocked(fs.createReadStream)
|
||||||
return {
|
const mockStatSync = vi.mocked(fs.statSync)
|
||||||
readFileSync: () => fileContents,
|
|
||||||
statSync: () => { return { size: contentLength } }
|
// biome-ignore lint/suspicious/noExplicitAny: Mock object for testing
|
||||||
};
|
mockCreateReadStream.mockReturnValue(fakeReadStream as any)
|
||||||
})
|
// biome-ignore lint/suspicious/noExplicitAny: Partial Stats object for testing
|
||||||
|
mockStatSync.mockReturnValue({ size: contentLength } as any)
|
||||||
|
|
||||||
describe("Artifact", () => {
|
describe("Artifact", () => {
|
||||||
it('defaults contentType to raw', () => {
|
it("defaults contentType to raw", () => {
|
||||||
const artifact = new Artifact('')
|
const artifact = new Artifact("")
|
||||||
expect(artifact.contentType).toBe('raw')
|
expect(artifact.contentType).toBe("raw")
|
||||||
})
|
})
|
||||||
|
|
||||||
it('generates name from path', () => {
|
it("generates name from path", () => {
|
||||||
const artifact = new Artifact('some/artifact')
|
const artifact = new Artifact("some/artifact")
|
||||||
expect(artifact.name).toBe('artifact')
|
expect(artifact.name).toBe("artifact")
|
||||||
})
|
})
|
||||||
|
|
||||||
it('provides contentLength', () => {
|
it("provides contentLength", () => {
|
||||||
const artifact = new Artifact('some/artifact')
|
const artifact = new Artifact("some/artifact")
|
||||||
expect(artifact.contentLength).toBe(contentLength)
|
expect(artifact.contentLength).toBe(contentLength)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('provides path', () => {
|
it("provides path", () => {
|
||||||
const artifact = new Artifact('some/artifact')
|
const artifact = new Artifact("some/artifact")
|
||||||
expect(artifact.path).toBe('some/artifact')
|
expect(artifact.path).toBe("some/artifact")
|
||||||
})
|
})
|
||||||
|
|
||||||
it('reads artifact', () => {
|
it("reads artifact", () => {
|
||||||
const artifact = new Artifact('some/artifact')
|
const artifact = new Artifact("some/artifact")
|
||||||
expect(artifact.readFile()).toBe(fileContents)
|
expect(artifact.readFile()).toBe(fakeReadStream)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,45 +1,42 @@
|
|||||||
import {Artifact} from "../src/Artifact"
|
import { beforeEach, describe, expect, it, vi } from "vitest"
|
||||||
import {GithubArtifactUploader} from "../src/ArtifactUploader"
|
import { GithubArtifactDestroyer } from "../src/ArtifactDestroyer.js"
|
||||||
import {Releases} from "../src/Releases";
|
import type { Releases } from "../src/Releases.js"
|
||||||
import {RequestError} from '@octokit/request-error'
|
|
||||||
import {GithubArtifactDestroyer} from "../src/ArtifactDestroyer";
|
|
||||||
|
|
||||||
const releaseId = 100
|
const releaseId = 100
|
||||||
|
|
||||||
const deleteMock = jest.fn()
|
const deleteMock = vi.fn()
|
||||||
const listArtifactsMock = jest.fn()
|
const listArtifactsMock = vi.fn()
|
||||||
|
|
||||||
|
describe("ArtifactDestroyer", () => {
|
||||||
describe('ArtifactDestroyer', () => {
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
deleteMock.mockClear()
|
deleteMock.mockClear()
|
||||||
listArtifactsMock.mockClear()
|
listArtifactsMock.mockClear()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('destroys all artifacts', async () => {
|
it("destroys all artifacts", async () => {
|
||||||
mockListWithAssets()
|
mockListWithAssets()
|
||||||
mockDeleteSuccess()
|
mockDeleteSuccess()
|
||||||
const destroyer = createDestroyer()
|
const destroyer = createDestroyer()
|
||||||
|
|
||||||
await destroyer.destroyArtifacts(releaseId)
|
await destroyer.destroyArtifacts(releaseId)
|
||||||
|
|
||||||
expect(deleteMock).toBeCalledTimes(2)
|
expect(deleteMock).toHaveBeenCalledTimes(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('destroys nothing when no artifacts found', async () => {
|
it("destroys nothing when no artifacts found", async () => {
|
||||||
mockListWithoutAssets()
|
mockListWithoutAssets()
|
||||||
const destroyer = createDestroyer()
|
const destroyer = createDestroyer()
|
||||||
|
|
||||||
await destroyer.destroyArtifacts(releaseId)
|
await destroyer.destroyArtifacts(releaseId)
|
||||||
|
|
||||||
expect(deleteMock).toBeCalledTimes(0)
|
expect(deleteMock).toHaveBeenCalledTimes(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('throws when delete call fails', async () => {
|
it("throws when delete call fails", async () => {
|
||||||
mockListWithAssets()
|
mockListWithAssets()
|
||||||
mockDeleteError()
|
mockDeleteError()
|
||||||
const destroyer = createDestroyer()
|
const destroyer = createDestroyer()
|
||||||
|
|
||||||
expect.hasAssertions()
|
expect.hasAssertions()
|
||||||
try {
|
try {
|
||||||
await destroyer.destroyArtifacts(releaseId)
|
await destroyer.destroyArtifacts(releaseId)
|
||||||
@@ -49,25 +46,26 @@ describe('ArtifactDestroyer', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
function createDestroyer(): GithubArtifactDestroyer {
|
function createDestroyer(): GithubArtifactDestroyer {
|
||||||
const MockReleases = jest.fn<Releases, any>(() => {
|
const MockReleases = vi.fn<() => Releases>(() => {
|
||||||
return {
|
return {
|
||||||
create: jest.fn(),
|
create: vi.fn(),
|
||||||
deleteArtifact: deleteMock,
|
deleteArtifact: deleteMock,
|
||||||
getByTag: jest.fn(),
|
getByTag: vi.fn(),
|
||||||
listArtifactsForRelease: listArtifactsMock,
|
listArtifactsForRelease: listArtifactsMock,
|
||||||
listReleases: jest.fn(),
|
listReleases: vi.fn(),
|
||||||
update: jest.fn(),
|
update: vi.fn(),
|
||||||
uploadArtifact: jest.fn()
|
uploadArtifact: vi.fn(),
|
||||||
|
generateReleaseNotes: vi.fn(),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return new GithubArtifactDestroyer(new MockReleases())
|
return new GithubArtifactDestroyer(MockReleases())
|
||||||
}
|
}
|
||||||
|
|
||||||
function mockDeleteError(): any {
|
function mockDeleteError(): void {
|
||||||
deleteMock.mockRejectedValue("error")
|
deleteMock.mockRejectedValue("error")
|
||||||
}
|
}
|
||||||
|
|
||||||
function mockDeleteSuccess(): any {
|
function mockDeleteSuccess(): void {
|
||||||
deleteMock.mockResolvedValue({})
|
deleteMock.mockResolvedValue({})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,16 +73,16 @@ describe('ArtifactDestroyer', () => {
|
|||||||
listArtifactsMock.mockResolvedValue([
|
listArtifactsMock.mockResolvedValue([
|
||||||
{
|
{
|
||||||
name: "art1",
|
name: "art1",
|
||||||
id: 1
|
id: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "art2",
|
name: "art2",
|
||||||
id: 2
|
id: 2,
|
||||||
}
|
},
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
function mockListWithoutAssets() {
|
function mockListWithoutAssets() {
|
||||||
listArtifactsMock.mockResolvedValue([])
|
listArtifactsMock.mockResolvedValue([])
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|||||||
@@ -1,29 +1,33 @@
|
|||||||
const warnMock = jest.fn()
|
import * as fs from "node:fs"
|
||||||
|
import * as core from "@actions/core"
|
||||||
|
import { beforeEach, describe, expect, it, vi } from "vitest"
|
||||||
|
|
||||||
import {FileArtifactGlobber} from "../src/ArtifactGlobber"
|
vi.mock("@actions/core")
|
||||||
import {Globber} from "../src/Globber";
|
vi.mock("fs")
|
||||||
import {Artifact} from "../src/Artifact";
|
|
||||||
import untildify = require("untildify");
|
import { Artifact } from "../src/Artifact.js"
|
||||||
|
import { FileArtifactGlobber } from "../src/ArtifactGlobber.js"
|
||||||
|
import type { Globber } from "../src/Globber.js"
|
||||||
|
import { expandTilde } from "../src/PathExpander.js"
|
||||||
|
|
||||||
|
const warnMock = vi.mocked(core.warning)
|
||||||
|
const mockStatSync = vi.mocked(fs.statSync)
|
||||||
|
// biome-ignore lint/suspicious/noExplicitAny: fs.realpathSync has overloads that are difficult to type
|
||||||
|
const mockRealpathSync = vi.mocked(fs.realpathSync as any)
|
||||||
|
|
||||||
const contentType = "raw"
|
const contentType = "raw"
|
||||||
const globMock = jest.fn()
|
const globMock = vi.fn()
|
||||||
const globResults = ["file1", "file2"]
|
const globResults = ["file1", "file2"]
|
||||||
|
|
||||||
jest.mock('@actions/core', () => {
|
mockStatSync.mockReturnValue({
|
||||||
return {warning: warnMock};
|
isDirectory(): boolean {
|
||||||
})
|
return false
|
||||||
|
},
|
||||||
|
// biome-ignore lint/suspicious/noExplicitAny: Partial Stats object for testing
|
||||||
|
} as any)
|
||||||
|
|
||||||
jest.mock('fs', () => {
|
// biome-ignore lint/suspicious/noExplicitAny: Mock return value for testing
|
||||||
return {
|
mockRealpathSync.mockReturnValue(false as any)
|
||||||
statSync: () => {
|
|
||||||
return {
|
|
||||||
isDirectory(): boolean {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("ArtifactGlobber", () => {
|
describe("ArtifactGlobber", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -33,79 +37,66 @@ describe("ArtifactGlobber", () => {
|
|||||||
it("expands paths in which start with a ~", () => {
|
it("expands paths in which start with a ~", () => {
|
||||||
const globber = createArtifactGlobber()
|
const globber = createArtifactGlobber()
|
||||||
|
|
||||||
const expectedArtifacts =
|
const expectedArtifacts = globResults.map((path) => new Artifact(path, contentType))
|
||||||
globResults.map((path) => new Artifact(path, contentType))
|
|
||||||
|
|
||||||
expect(globber.globArtifactString('~/path', 'raw', false))
|
expect(globber.globArtifactString("~/path", "raw", false)).toEqual(expectedArtifacts)
|
||||||
.toEqual(expectedArtifacts)
|
expect(globMock).toHaveBeenCalledWith(expandTilde("~/path"))
|
||||||
expect(globMock).toBeCalledWith(untildify('~/path'))
|
expect(warnMock).not.toHaveBeenCalled()
|
||||||
expect(warnMock).not.toBeCalled()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it("globs simple path", () => {
|
it("globs simple path", () => {
|
||||||
const globber = createArtifactGlobber()
|
const globber = createArtifactGlobber()
|
||||||
|
|
||||||
const expectedArtifacts =
|
const expectedArtifacts = globResults.map((path) => new Artifact(path, contentType))
|
||||||
globResults.map((path) => new Artifact(path, contentType))
|
|
||||||
|
|
||||||
expect(globber.globArtifactString('path', 'raw', false))
|
expect(globber.globArtifactString("path", "raw", false)).toEqual(expectedArtifacts)
|
||||||
.toEqual(expectedArtifacts)
|
expect(globMock).toHaveBeenCalledWith("path")
|
||||||
expect(globMock).toBeCalledWith('path')
|
expect(warnMock).not.toHaveBeenCalled()
|
||||||
expect(warnMock).not.toBeCalled()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it("splits multiple paths with comma separator", () => {
|
it("splits multiple paths with comma separator", () => {
|
||||||
const globber = createArtifactGlobber()
|
const globber = createArtifactGlobber()
|
||||||
|
|
||||||
const expectedArtifacts =
|
const expectedArtifacts = globResults.concat(globResults).map((path) => new Artifact(path, contentType))
|
||||||
globResults
|
|
||||||
.concat(globResults)
|
|
||||||
.map((path) => new Artifact(path, contentType))
|
|
||||||
|
|
||||||
expect(globber.globArtifactString('path1,path2', 'raw', false))
|
expect(globber.globArtifactString("path1,path2", "raw", false)).toEqual(expectedArtifacts)
|
||||||
.toEqual(expectedArtifacts)
|
expect(globMock).toHaveBeenCalledWith("path1")
|
||||||
expect(globMock).toBeCalledWith('path1')
|
expect(globMock).toHaveBeenCalledWith("path2")
|
||||||
expect(globMock).toBeCalledWith('path2')
|
expect(warnMock).not.toHaveBeenCalled()
|
||||||
expect(warnMock).not.toBeCalled()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it("splits multiple paths with new line separator and trims start", () => {
|
it("splits multiple paths with new line separator and trims start", () => {
|
||||||
const globber = createArtifactGlobber()
|
const globber = createArtifactGlobber()
|
||||||
|
|
||||||
const expectedArtifacts =
|
const expectedArtifacts = globResults.concat(globResults).map((path) => new Artifact(path, contentType))
|
||||||
globResults
|
|
||||||
.concat(globResults)
|
|
||||||
.map((path) => new Artifact(path, contentType))
|
|
||||||
|
|
||||||
expect(globber.globArtifactString('path1\n path2', 'raw', false))
|
expect(globber.globArtifactString("path1\n path2", "raw", false)).toEqual(expectedArtifacts)
|
||||||
.toEqual(expectedArtifacts)
|
expect(globMock).toHaveBeenCalledWith("path1")
|
||||||
expect(globMock).toBeCalledWith('path1')
|
expect(globMock).toHaveBeenCalledWith("path2")
|
||||||
expect(globMock).toBeCalledWith('path2')
|
expect(warnMock).not.toHaveBeenCalled()
|
||||||
expect(warnMock).not.toBeCalled()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it("warns when no glob results are produced and empty results shouldn't throw", () => {
|
it("warns when no glob results are produced and empty results shouldn't throw", () => {
|
||||||
const globber = createArtifactGlobber([])
|
const globber = createArtifactGlobber([])
|
||||||
|
|
||||||
expect(globber.globArtifactString('path', 'raw', false))
|
expect(globber.globArtifactString("path", "raw", false)).toEqual([])
|
||||||
.toEqual([])
|
expect(warnMock).toHaveBeenCalled()
|
||||||
expect(warnMock).toBeCalled()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it("throws when no glob results are produced and empty results shouild throw", () => {
|
it("throws when no glob results are produced and empty results shouild throw", () => {
|
||||||
const globber = createArtifactGlobber([])
|
const globber = createArtifactGlobber([])
|
||||||
expect(() => {
|
expect(() => {
|
||||||
globber.globArtifactString('path', 'raw', true)
|
globber.globArtifactString("path", "raw", true)
|
||||||
}).toThrow()
|
}).toThrow()
|
||||||
})
|
})
|
||||||
|
|
||||||
function createArtifactGlobber(results: string[] = globResults): FileArtifactGlobber {
|
function createArtifactGlobber(results: string[] = globResults): FileArtifactGlobber {
|
||||||
const MockGlobber = jest.fn<Globber, any>(() => {
|
const MockGlobber = vi.fn<() => Globber>(() => {
|
||||||
return {
|
return {
|
||||||
glob: globMock
|
glob: globMock,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
globMock.mockReturnValue(results)
|
globMock.mockReturnValue(results)
|
||||||
return new FileArtifactGlobber(new MockGlobber())
|
return new FileArtifactGlobber(MockGlobber())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,21 +1,20 @@
|
|||||||
const directoryMock = jest.fn()
|
import * as fs from "node:fs"
|
||||||
const warnMock = jest.fn()
|
import * as core from "@actions/core"
|
||||||
|
import { describe, expect, it, vi } from "vitest"
|
||||||
|
|
||||||
import {ArtifactPathValidator} from "../src/ArtifactPathValidator";
|
vi.mock("@actions/core")
|
||||||
|
vi.mock("fs")
|
||||||
|
|
||||||
const pattern = 'pattern'
|
import { ArtifactPathValidator } from "../src/ArtifactPathValidator.js"
|
||||||
|
|
||||||
jest.mock('@actions/core', () => {
|
const warnMock = vi.mocked(core.warning)
|
||||||
return {warning: warnMock};
|
const mockStatSync = vi.mocked(fs.statSync)
|
||||||
})
|
const directoryMock = vi.fn()
|
||||||
|
|
||||||
jest.mock('fs', () => {
|
// biome-ignore lint/suspicious/noExplicitAny: Partial Stats object for testing
|
||||||
return {
|
mockStatSync.mockReturnValue({ isDirectory: directoryMock } as any)
|
||||||
statSync: () => {
|
|
||||||
return {isDirectory: directoryMock}
|
const pattern = "pattern"
|
||||||
}
|
|
||||||
};
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("ArtifactPathValidator", () => {
|
describe("ArtifactPathValidator", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -24,20 +23,20 @@ describe("ArtifactPathValidator", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("warns and filters out path which points to a directory", () => {
|
it("warns and filters out path which points to a directory", () => {
|
||||||
const paths = ['path1', 'path2']
|
const paths = ["path1", "path2"]
|
||||||
directoryMock.mockReturnValueOnce(true).mockReturnValueOnce(false)
|
directoryMock.mockReturnValueOnce(true).mockReturnValueOnce(false)
|
||||||
|
|
||||||
const validator = new ArtifactPathValidator(false, paths, pattern)
|
const validator = new ArtifactPathValidator(false, paths, pattern)
|
||||||
|
|
||||||
const result = validator.validate()
|
const result = validator.validate()
|
||||||
expect(warnMock).toBeCalled()
|
expect(warnMock).toHaveBeenCalled()
|
||||||
expect(result).toEqual(['path2'])
|
expect(result).toEqual(["path2"])
|
||||||
})
|
})
|
||||||
|
|
||||||
it("warns when no glob results are produced and empty results shouldn't throw", () => {
|
it("warns when no glob results are produced and empty results shouldn't throw", () => {
|
||||||
const validator = new ArtifactPathValidator(false, [], pattern)
|
const validator = new ArtifactPathValidator(false, [], pattern)
|
||||||
const result = validator.validate()
|
const _result = validator.validate()
|
||||||
expect(warnMock).toBeCalled()
|
expect(warnMock).toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("throws when no glob results are produced and empty results shouild throw", () => {
|
it("throws when no glob results are produced and empty results shouild throw", () => {
|
||||||
@@ -48,7 +47,7 @@ describe("ArtifactPathValidator", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("throws when path points to directory", () => {
|
it("throws when path points to directory", () => {
|
||||||
const paths = ['path1', 'path2']
|
const paths = ["path1", "path2"]
|
||||||
directoryMock.mockReturnValueOnce(true).mockReturnValueOnce(false)
|
directoryMock.mockReturnValueOnce(true).mockReturnValueOnce(false)
|
||||||
|
|
||||||
const validator = new ArtifactPathValidator(true, paths, pattern)
|
const validator = new ArtifactPathValidator(true, paths, pattern)
|
||||||
@@ -57,4 +56,4 @@ describe("ArtifactPathValidator", () => {
|
|||||||
validator.validate()
|
validator.validate()
|
||||||
}).toThrow()
|
}).toThrow()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,133 +1,172 @@
|
|||||||
import {Artifact} from "../src/Artifact"
|
import { RequestError } from "@octokit/request-error"
|
||||||
import {GithubArtifactUploader} from "../src/ArtifactUploader"
|
import { beforeEach, describe, expect, it, vi } from "vitest"
|
||||||
import {Releases} from "../src/Releases";
|
import { Artifact } from "../src/Artifact.js"
|
||||||
import {RequestError} from '@octokit/request-error'
|
import { GithubArtifactUploader } from "../src/ArtifactUploader.js"
|
||||||
|
import type { Releases } from "../src/Releases.js"
|
||||||
|
|
||||||
const artifacts = [
|
const artifacts = [new Artifact("a/art1"), new Artifact("b/art2")]
|
||||||
new Artifact('a/art1'),
|
const fakeReadStream = {}
|
||||||
new Artifact('b/art2')
|
|
||||||
]
|
|
||||||
const fileContents = Buffer.from('artful facts', 'utf-8')
|
|
||||||
const contentLength = 42
|
const contentLength = 42
|
||||||
const releaseId = 100
|
const releaseId = 100
|
||||||
const url = 'http://api.example.com'
|
const url = "http://api.example.com"
|
||||||
|
|
||||||
const deleteMock = jest.fn()
|
const deleteMock = vi.fn()
|
||||||
const listArtifactsMock = jest.fn()
|
const listArtifactsMock = vi.fn()
|
||||||
const uploadMock = jest.fn()
|
const uploadMock = vi.fn()
|
||||||
|
|
||||||
jest.mock('fs', () => {
|
// Mock response with browser_download_url
|
||||||
return {
|
const mockUploadResponse = (name: string) => ({
|
||||||
promises: {},
|
data: {
|
||||||
readFileSync: () => fileContents,
|
browser_download_url: `https://github.com/octocat/Hello-World/releases/download/v1.0.0/${name}`,
|
||||||
statSync: () => {
|
name: name,
|
||||||
return {size: contentLength}
|
id: 1,
|
||||||
}
|
},
|
||||||
};
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('ArtifactUploader', () => {
|
vi.mock("fs", async () => {
|
||||||
|
const originalFs = await vi.importActual<typeof import("fs")>("fs")
|
||||||
|
return {
|
||||||
|
...originalFs,
|
||||||
|
promises: {},
|
||||||
|
createReadStream: () => fakeReadStream,
|
||||||
|
statSync: () => {
|
||||||
|
return { size: contentLength }
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("ArtifactUploader", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
deleteMock.mockClear()
|
deleteMock.mockClear()
|
||||||
listArtifactsMock.mockClear()
|
listArtifactsMock.mockClear()
|
||||||
uploadMock.mockClear()
|
uploadMock.mockClear()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('abort when upload failed with non-5xx response', async () => {
|
it("returns asset URLs when upload succeeds", async () => {
|
||||||
|
mockListWithoutAssets()
|
||||||
|
mockUploadSuccess()
|
||||||
|
const uploader = createUploader(true)
|
||||||
|
|
||||||
|
const result = await uploader.uploadArtifacts(artifacts, releaseId, url)
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
art1: "https://github.com/octocat/Hello-World/releases/download/v1.0.0/art1",
|
||||||
|
art2: "https://github.com/octocat/Hello-World/releases/download/v1.0.0/art2",
|
||||||
|
})
|
||||||
|
expect(uploadMock).toHaveBeenCalledTimes(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns empty object when no artifacts are uploaded", async () => {
|
||||||
|
const uploader = createUploader(true)
|
||||||
|
|
||||||
|
const result = await uploader.uploadArtifacts([], releaseId, url)
|
||||||
|
|
||||||
|
expect(result).toEqual({})
|
||||||
|
expect(uploadMock).toHaveBeenCalledTimes(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("excludes failed uploads from returned URLs", async () => {
|
||||||
mockListWithoutAssets()
|
mockListWithoutAssets()
|
||||||
mockUploadArtifact(401, 2)
|
mockUploadArtifact(401, 2)
|
||||||
const uploader = createUploader(true)
|
const uploader = createUploader(true)
|
||||||
|
|
||||||
await uploader.uploadArtifacts(artifacts, releaseId, url)
|
const result = await uploader.uploadArtifacts(artifacts, releaseId, url)
|
||||||
|
|
||||||
expect(uploadMock).toBeCalledTimes(2)
|
expect(result).toEqual({})
|
||||||
expect(uploadMock)
|
expect(uploadMock).toHaveBeenCalledTimes(2)
|
||||||
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art1', releaseId)
|
|
||||||
expect(uploadMock)
|
|
||||||
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art2', releaseId)
|
|
||||||
|
|
||||||
expect(deleteMock).toBeCalledTimes(0)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('abort when upload failed with 5xx response after 3 attempts', async () => {
|
it("abort when upload failed with non-5xx response", async () => {
|
||||||
|
mockListWithoutAssets()
|
||||||
|
mockUploadArtifact(401, 2)
|
||||||
|
const uploader = createUploader(true)
|
||||||
|
|
||||||
|
const result = await uploader.uploadArtifacts(artifacts, releaseId, url)
|
||||||
|
|
||||||
|
expect(result).toEqual({})
|
||||||
|
expect(uploadMock).toHaveBeenCalledTimes(2)
|
||||||
|
expect(uploadMock).toHaveBeenCalledWith(url, contentLength, "raw", fakeReadStream, "art1", releaseId)
|
||||||
|
expect(uploadMock).toHaveBeenCalledWith(url, contentLength, "raw", fakeReadStream, "art2", releaseId)
|
||||||
|
|
||||||
|
expect(deleteMock).toHaveBeenCalledTimes(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("abort when upload failed with 5xx response after 3 attempts", async () => {
|
||||||
mockListWithoutAssets()
|
mockListWithoutAssets()
|
||||||
mockUploadArtifact(500, 4)
|
mockUploadArtifact(500, 4)
|
||||||
const uploader = createUploader(true)
|
const uploader = createUploader(true)
|
||||||
|
|
||||||
await uploader.uploadArtifacts(artifacts, releaseId, url)
|
const result = await uploader.uploadArtifacts(artifacts, releaseId, url)
|
||||||
|
|
||||||
expect(uploadMock).toBeCalledTimes(5)
|
expect(result).toEqual({})
|
||||||
expect(uploadMock)
|
expect(uploadMock).toHaveBeenCalledTimes(5)
|
||||||
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art1', releaseId)
|
expect(uploadMock).toHaveBeenCalledWith(url, contentLength, "raw", fakeReadStream, "art1", releaseId)
|
||||||
expect(uploadMock)
|
expect(uploadMock).toHaveBeenCalledWith(url, contentLength, "raw", fakeReadStream, "art1", releaseId)
|
||||||
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art1', releaseId)
|
expect(uploadMock).toHaveBeenCalledWith(url, contentLength, "raw", fakeReadStream, "art1", releaseId)
|
||||||
expect(uploadMock)
|
expect(uploadMock).toHaveBeenCalledWith(url, contentLength, "raw", fakeReadStream, "art2", releaseId)
|
||||||
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art1', releaseId)
|
expect(uploadMock).toHaveBeenCalledWith(url, contentLength, "raw", fakeReadStream, "art2", releaseId)
|
||||||
expect(uploadMock)
|
|
||||||
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art2', releaseId)
|
|
||||||
expect(uploadMock)
|
|
||||||
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art2', releaseId)
|
|
||||||
|
|
||||||
expect(deleteMock).toBeCalledTimes(0)
|
expect(deleteMock).toHaveBeenCalledTimes(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('replaces all artifacts', async () => {
|
it("replaces all artifacts", async () => {
|
||||||
mockDeleteSuccess()
|
mockDeleteSuccess()
|
||||||
mockListWithAssets()
|
mockListWithAssets()
|
||||||
mockUploadArtifact()
|
mockUploadSuccess()
|
||||||
const uploader = createUploader(true)
|
const uploader = createUploader(true)
|
||||||
|
|
||||||
await uploader.uploadArtifacts(artifacts, releaseId, url)
|
const result = await uploader.uploadArtifacts(artifacts, releaseId, url)
|
||||||
|
|
||||||
expect(uploadMock).toBeCalledTimes(2)
|
expect(result).toEqual({
|
||||||
expect(uploadMock)
|
art1: "https://github.com/octocat/Hello-World/releases/download/v1.0.0/art1",
|
||||||
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art1', releaseId)
|
art2: "https://github.com/octocat/Hello-World/releases/download/v1.0.0/art2",
|
||||||
expect(uploadMock)
|
})
|
||||||
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art2', releaseId)
|
expect(uploadMock).toHaveBeenCalledTimes(2)
|
||||||
|
expect(uploadMock).toHaveBeenCalledWith(url, contentLength, "raw", fakeReadStream, "art1", releaseId)
|
||||||
|
expect(uploadMock).toHaveBeenCalledWith(url, contentLength, "raw", fakeReadStream, "art2", releaseId)
|
||||||
|
|
||||||
expect(deleteMock).toBeCalledTimes(2)
|
expect(deleteMock).toHaveBeenCalledTimes(2)
|
||||||
expect(deleteMock).toBeCalledWith(1)
|
expect(deleteMock).toHaveBeenCalledWith(1)
|
||||||
expect(deleteMock).toBeCalledWith(2)
|
expect(deleteMock).toHaveBeenCalledWith(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('replaces no artifacts when previous asset list empty', async () => {
|
it("replaces no artifacts when previous asset list empty", async () => {
|
||||||
mockDeleteSuccess()
|
mockDeleteSuccess()
|
||||||
mockListWithoutAssets()
|
mockListWithoutAssets()
|
||||||
mockUploadArtifact()
|
mockUploadSuccess()
|
||||||
const uploader = createUploader(true)
|
const uploader = createUploader(true)
|
||||||
|
|
||||||
await uploader.uploadArtifacts(artifacts, releaseId, url)
|
const result = await uploader.uploadArtifacts(artifacts, releaseId, url)
|
||||||
|
|
||||||
expect(uploadMock).toBeCalledTimes(2)
|
expect(result).toEqual({
|
||||||
expect(uploadMock)
|
art1: "https://github.com/octocat/Hello-World/releases/download/v1.0.0/art1",
|
||||||
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art1', releaseId)
|
art2: "https://github.com/octocat/Hello-World/releases/download/v1.0.0/art2",
|
||||||
expect(uploadMock)
|
})
|
||||||
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art2', releaseId)
|
expect(uploadMock).toHaveBeenCalledTimes(2)
|
||||||
|
expect(uploadMock).toHaveBeenCalledWith(url, contentLength, "raw", fakeReadStream, "art1", releaseId)
|
||||||
|
expect(uploadMock).toHaveBeenCalledWith(url, contentLength, "raw", fakeReadStream, "art2", releaseId)
|
||||||
|
|
||||||
expect(deleteMock).toBeCalledTimes(0)
|
expect(deleteMock).toHaveBeenCalledTimes(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('retry when upload failed with 5xx response', async () => {
|
it("retry when upload failed with 5xx response", async () => {
|
||||||
mockListWithoutAssets()
|
mockListWithoutAssets()
|
||||||
mockUploadArtifact(500, 2)
|
mockUploadArtifact(500, 2)
|
||||||
const uploader = createUploader(true)
|
const uploader = createUploader(true)
|
||||||
|
|
||||||
await uploader.uploadArtifacts(artifacts, releaseId, url)
|
const result = await uploader.uploadArtifacts(artifacts, releaseId, url)
|
||||||
|
|
||||||
expect(uploadMock).toBeCalledTimes(4)
|
expect(result).toEqual({})
|
||||||
expect(uploadMock)
|
expect(uploadMock).toHaveBeenCalledTimes(4)
|
||||||
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art1', releaseId)
|
expect(uploadMock).toHaveBeenCalledWith(url, contentLength, "raw", fakeReadStream, "art1", releaseId)
|
||||||
expect(uploadMock)
|
expect(uploadMock).toHaveBeenCalledWith(url, contentLength, "raw", fakeReadStream, "art1", releaseId)
|
||||||
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art1', releaseId)
|
expect(uploadMock).toHaveBeenCalledWith(url, contentLength, "raw", fakeReadStream, "art1", releaseId)
|
||||||
expect(uploadMock)
|
expect(uploadMock).toHaveBeenCalledWith(url, contentLength, "raw", fakeReadStream, "art2", releaseId)
|
||||||
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art1', releaseId)
|
|
||||||
expect(uploadMock)
|
|
||||||
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art2', releaseId)
|
|
||||||
|
|
||||||
expect(deleteMock).toBeCalledTimes(0)
|
expect(deleteMock).toHaveBeenCalledTimes(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('throws upload error when replacesExistingArtifacts is true', async () => {
|
it("throws upload error when replacesExistingArtifacts is true", async () => {
|
||||||
mockListWithoutAssets()
|
mockListWithoutAssets()
|
||||||
mockUploadError()
|
mockUploadError()
|
||||||
const uploader = createUploader(true, true)
|
const uploader = createUploader(true, true)
|
||||||
@@ -140,10 +179,10 @@ describe('ArtifactUploader', () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
it('throws error from replace', async () => {
|
it("throws error from replace", async () => {
|
||||||
mockDeleteError()
|
mockDeleteError()
|
||||||
mockListWithAssets()
|
mockListWithAssets()
|
||||||
mockUploadArtifact()
|
mockUploadSuccess()
|
||||||
const uploader = createUploader(true)
|
const uploader = createUploader(true)
|
||||||
|
|
||||||
expect.hasAssertions()
|
expect.hasAssertions()
|
||||||
@@ -154,43 +193,46 @@ describe('ArtifactUploader', () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
it('updates all artifacts, delete none', async () => {
|
it("updates all artifacts, delete none", async () => {
|
||||||
mockDeleteError()
|
mockDeleteError()
|
||||||
mockListWithAssets()
|
mockListWithAssets()
|
||||||
mockUploadArtifact()
|
mockUploadSuccess()
|
||||||
const uploader = createUploader(false)
|
const uploader = createUploader(false)
|
||||||
|
|
||||||
await uploader.uploadArtifacts(artifacts, releaseId, url)
|
const result = await uploader.uploadArtifacts(artifacts, releaseId, url)
|
||||||
|
|
||||||
expect(uploadMock).toBeCalledTimes(2)
|
expect(result).toEqual({
|
||||||
expect(uploadMock)
|
art1: "https://github.com/octocat/Hello-World/releases/download/v1.0.0/art1",
|
||||||
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art1', releaseId)
|
art2: "https://github.com/octocat/Hello-World/releases/download/v1.0.0/art2",
|
||||||
expect(uploadMock)
|
})
|
||||||
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art2', releaseId)
|
expect(uploadMock).toHaveBeenCalledTimes(2)
|
||||||
|
expect(uploadMock).toHaveBeenCalledWith(url, contentLength, "raw", fakeReadStream, "art1", releaseId)
|
||||||
|
expect(uploadMock).toHaveBeenCalledWith(url, contentLength, "raw", fakeReadStream, "art2", releaseId)
|
||||||
|
|
||||||
expect(deleteMock).toBeCalledTimes(0)
|
expect(deleteMock).toHaveBeenCalledTimes(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
function createUploader(replaces: boolean, throws: boolean = false): GithubArtifactUploader {
|
function createUploader(replaces: boolean, throws = false): GithubArtifactUploader {
|
||||||
const MockReleases = jest.fn<Releases, any>(() => {
|
const MockReleases = vi.fn<() => Releases>(() => {
|
||||||
return {
|
return {
|
||||||
create: jest.fn(),
|
create: vi.fn(),
|
||||||
deleteArtifact: deleteMock,
|
deleteArtifact: deleteMock,
|
||||||
getByTag: jest.fn(),
|
getByTag: vi.fn(),
|
||||||
listArtifactsForRelease: listArtifactsMock,
|
listArtifactsForRelease: listArtifactsMock,
|
||||||
listReleases: jest.fn(),
|
listReleases: vi.fn(),
|
||||||
update: jest.fn(),
|
update: vi.fn(),
|
||||||
uploadArtifact: uploadMock
|
uploadArtifact: uploadMock,
|
||||||
|
generateReleaseNotes: vi.fn(),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return new GithubArtifactUploader(new MockReleases(), replaces, throws)
|
return new GithubArtifactUploader(MockReleases(), replaces, throws)
|
||||||
}
|
}
|
||||||
|
|
||||||
function mockDeleteError(): any {
|
function mockDeleteError(): void {
|
||||||
deleteMock.mockRejectedValue("error")
|
deleteMock.mockRejectedValue("error")
|
||||||
}
|
}
|
||||||
|
|
||||||
function mockDeleteSuccess(): any {
|
function mockDeleteSuccess(): void {
|
||||||
deleteMock.mockResolvedValue({})
|
deleteMock.mockResolvedValue({})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,12 +240,12 @@ describe('ArtifactUploader', () => {
|
|||||||
listArtifactsMock.mockResolvedValue([
|
listArtifactsMock.mockResolvedValue([
|
||||||
{
|
{
|
||||||
name: "art1",
|
name: "art1",
|
||||||
id: 1
|
id: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "art2",
|
name: "art2",
|
||||||
id: 2
|
id: 2,
|
||||||
}
|
},
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,10 +253,14 @@ describe('ArtifactUploader', () => {
|
|||||||
listArtifactsMock.mockResolvedValue([])
|
listArtifactsMock.mockResolvedValue([])
|
||||||
}
|
}
|
||||||
|
|
||||||
function mockUploadArtifact(status: number = 200, failures: number = 0) {
|
function mockUploadSuccess() {
|
||||||
|
uploadMock.mockImplementation((_, __, ___, ____, name) => Promise.resolve(mockUploadResponse(name)))
|
||||||
|
}
|
||||||
|
|
||||||
|
function mockUploadArtifact(status = 200, failures = 0) {
|
||||||
const error = new RequestError(`HTTP ${status}`, status, {
|
const error = new RequestError(`HTTP ${status}`, status, {
|
||||||
headers: {},
|
headers: {},
|
||||||
request: {method: 'GET', url: '', headers: {}}
|
request: { method: "GET", url: "", headers: {} },
|
||||||
})
|
})
|
||||||
for (let index = 0; index < failures; index++) {
|
for (let index = 0; index < failures; index++) {
|
||||||
uploadMock.mockRejectedValueOnce(error)
|
uploadMock.mockRejectedValueOnce(error)
|
||||||
@@ -225,7 +271,7 @@ describe('ArtifactUploader', () => {
|
|||||||
function mockUploadError() {
|
function mockUploadError() {
|
||||||
uploadMock.mockRejectedValue({
|
uploadMock.mockRejectedValue({
|
||||||
message: "error",
|
message: "error",
|
||||||
status: 502
|
status: 502,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|||||||
@@ -1,68 +1,96 @@
|
|||||||
import { GithubError } from "../src/GithubError"
|
import { describe, expect, it } from "vitest"
|
||||||
|
import { GithubError } from "../src/GithubError.js"
|
||||||
|
|
||||||
describe('ErrorMessage', () => {
|
describe("ErrorMessage", () => {
|
||||||
|
describe("has error with code", () => {
|
||||||
describe('has error with code', () => {
|
|
||||||
const error = {
|
const error = {
|
||||||
message: 'something bad happened',
|
message: "something bad happened",
|
||||||
errors: [
|
errors: [
|
||||||
{
|
{
|
||||||
code: 'missing',
|
code: "missing",
|
||||||
resource: 'release'
|
resource: "release",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'already_exists',
|
code: "already_exists",
|
||||||
resource: 'release'
|
resource: "release",
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
status: 422
|
status: 422,
|
||||||
}
|
}
|
||||||
|
|
||||||
it('does not have error', () => {
|
it("does not have error", () => {
|
||||||
const githubError = new GithubError(error)
|
const githubError = new GithubError(error)
|
||||||
expect(githubError.hasErrorWithCode('missing_field')).toBeFalsy()
|
expect(githubError.hasErrorWithCode("missing_field")).toBeFalsy()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has error', () => {
|
it("has error", () => {
|
||||||
const githubError = new GithubError(error)
|
const githubError = new GithubError(error)
|
||||||
expect(githubError.hasErrorWithCode('missing')).toBeTruthy()
|
expect(githubError.hasErrorWithCode("missing")).toBeTruthy()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('generates message with errors', () => {
|
describe("has error with remediation", () => {
|
||||||
|
it("provides remediation with 404 without errors", () => {
|
||||||
|
const error = { status: 404, message: "message" }
|
||||||
|
const githubError = new GithubError(error)
|
||||||
|
expect(githubError.toString()).toBe(
|
||||||
|
"Error 404: message\nMake sure your github token has access to the repo and has permission to author releases"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("provides remediation with 404 with errors", () => {
|
||||||
|
const error = {
|
||||||
|
message: "message",
|
||||||
|
errors: [
|
||||||
|
{
|
||||||
|
code: "missing",
|
||||||
|
resource: "release",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
status: 404,
|
||||||
|
}
|
||||||
|
const githubError = new GithubError(error)
|
||||||
|
expect(githubError.toString()).toBe(
|
||||||
|
"Error 404: message\nErrors:\n- release does not exist.\nMake sure your github token has access to the repo and has permission to author releases"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("generates message with errors", () => {
|
||||||
const error = {
|
const error = {
|
||||||
message: 'something bad happened',
|
message: "something bad happened",
|
||||||
errors: [
|
errors: [
|
||||||
{
|
{
|
||||||
code: 'missing',
|
code: "missing",
|
||||||
resource: 'release'
|
resource: "release",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'already_exists',
|
code: "already_exists",
|
||||||
resource: 'release'
|
resource: "release",
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
status: 422
|
status: 422,
|
||||||
}
|
}
|
||||||
|
|
||||||
const githubError = new GithubError(error)
|
const githubError = new GithubError(error)
|
||||||
|
|
||||||
const expectedString = "Error 422: something bad happened\nErrors:\n- release does not exist.\n- release already exists."
|
const expectedString =
|
||||||
|
"Error 422: something bad happened\nErrors:\n- release does not exist.\n- release already exists."
|
||||||
expect(githubError.toString()).toBe(expectedString)
|
expect(githubError.toString()).toBe(expectedString)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('generates message without errors', () => {
|
it("generates message without errors", () => {
|
||||||
const error = {
|
const error = {
|
||||||
message: 'something bad happened',
|
message: "something bad happened",
|
||||||
status: 422
|
status: 422,
|
||||||
}
|
}
|
||||||
|
|
||||||
const githubError = new GithubError(error)
|
const githubError = new GithubError(error)
|
||||||
|
|
||||||
expect(githubError.toString()).toBe('Error 422: something bad happened')
|
expect(githubError.toString()).toBe("Error 422: something bad happened")
|
||||||
})
|
})
|
||||||
|
|
||||||
it('provides error status', () => {
|
it("provides error status", () => {
|
||||||
const error = { status: 404 }
|
const error = { status: 404 }
|
||||||
const githubError = new GithubError(error)
|
const githubError = new GithubError(error)
|
||||||
expect(githubError.status).toBe(404)
|
expect(githubError.status).toBe(404)
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
import { GithubErrorDetail } from "../src/GithubErrorDetail"
|
import { describe, expect, it } from "vitest"
|
||||||
|
import { GithubErrorDetail } from "../src/GithubErrorDetail.js"
|
||||||
|
|
||||||
describe('GithubErrorDetail', () => {
|
describe("GithubErrorDetail", () => {
|
||||||
|
it("provides error code", () => {
|
||||||
it('provides error code', () => {
|
|
||||||
const error = {
|
const error = {
|
||||||
code: "missing"
|
code: "missing",
|
||||||
}
|
}
|
||||||
|
|
||||||
const detail = new GithubErrorDetail(error)
|
const detail = new GithubErrorDetail(error)
|
||||||
|
|
||||||
expect(detail.code).toBe('missing')
|
expect(detail.code).toBe("missing")
|
||||||
})
|
})
|
||||||
|
|
||||||
it('generates missing resource error message', () => {
|
it("generates missing resource error message", () => {
|
||||||
const resource = "release"
|
const resource = "release"
|
||||||
const error = {
|
const error = {
|
||||||
code: "missing",
|
code: "missing",
|
||||||
resource: resource
|
resource: resource,
|
||||||
}
|
}
|
||||||
|
|
||||||
const detail = new GithubErrorDetail(error)
|
const detail = new GithubErrorDetail(error)
|
||||||
@@ -25,13 +25,13 @@ describe('GithubErrorDetail', () => {
|
|||||||
expect(message).toBe(`${resource} does not exist.`)
|
expect(message).toBe(`${resource} does not exist.`)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('generates missing field error message', () => {
|
it("generates missing field error message", () => {
|
||||||
const resource = "release"
|
const resource = "release"
|
||||||
const field = "body"
|
const field = "body"
|
||||||
const error = {
|
const error = {
|
||||||
code: "missing_field",
|
code: "missing_field",
|
||||||
field: field,
|
field: field,
|
||||||
resource: resource
|
resource: resource,
|
||||||
}
|
}
|
||||||
|
|
||||||
const detail = new GithubErrorDetail(error)
|
const detail = new GithubErrorDetail(error)
|
||||||
@@ -40,13 +40,13 @@ describe('GithubErrorDetail', () => {
|
|||||||
expect(message).toBe(`The ${field} field on ${resource} is missing.`)
|
expect(message).toBe(`The ${field} field on ${resource} is missing.`)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('generates invalid field error message', () => {
|
it("generates invalid field error message", () => {
|
||||||
const resource = "release"
|
const resource = "release"
|
||||||
const field = "body"
|
const field = "body"
|
||||||
const error = {
|
const error = {
|
||||||
code: "invalid",
|
code: "invalid",
|
||||||
field: field,
|
field: field,
|
||||||
resource: resource
|
resource: resource,
|
||||||
}
|
}
|
||||||
|
|
||||||
const detail = new GithubErrorDetail(error)
|
const detail = new GithubErrorDetail(error)
|
||||||
@@ -55,11 +55,11 @@ describe('GithubErrorDetail', () => {
|
|||||||
expect(message).toBe(`The ${field} field on ${resource} is an invalid format.`)
|
expect(message).toBe(`The ${field} field on ${resource} is an invalid format.`)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('generates resource already exists error message', () => {
|
it("generates resource already exists error message", () => {
|
||||||
const resource = "release"
|
const resource = "release"
|
||||||
const error = {
|
const error = {
|
||||||
code: "already_exists",
|
code: "already_exists",
|
||||||
resource: resource
|
resource: resource,
|
||||||
}
|
}
|
||||||
|
|
||||||
const detail = new GithubErrorDetail(error)
|
const detail = new GithubErrorDetail(error)
|
||||||
@@ -68,13 +68,13 @@ describe('GithubErrorDetail', () => {
|
|||||||
expect(message).toBe(`${resource} already exists.`)
|
expect(message).toBe(`${resource} already exists.`)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('generates custom error message', () => {
|
describe("generates custom error message", () => {
|
||||||
it('with documentation url', () => {
|
it("with documentation url", () => {
|
||||||
const url = "https://api.example.com"
|
const url = "https://api.example.com"
|
||||||
const error = {
|
const error = {
|
||||||
code: "custom",
|
code: "custom",
|
||||||
message: "foo",
|
message: "foo",
|
||||||
documentation_url: url
|
documentation_url: url,
|
||||||
}
|
}
|
||||||
|
|
||||||
const detail = new GithubErrorDetail(error)
|
const detail = new GithubErrorDetail(error)
|
||||||
@@ -83,16 +83,16 @@ describe('GithubErrorDetail', () => {
|
|||||||
expect(message).toBe(`foo\nPlease see ${url}.`)
|
expect(message).toBe(`foo\nPlease see ${url}.`)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('without documentation url', () => {
|
it("without documentation url", () => {
|
||||||
const error = {
|
const error = {
|
||||||
code: "custom",
|
code: "custom",
|
||||||
message: "foo"
|
message: "foo",
|
||||||
}
|
}
|
||||||
|
|
||||||
const detail = new GithubErrorDetail(error)
|
const detail = new GithubErrorDetail(error)
|
||||||
const message = detail.toString()
|
const message = detail.toString()
|
||||||
|
|
||||||
expect(message).toBe('foo')
|
expect(message).toBe("foo")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,486 +1,483 @@
|
|||||||
const mockGetInput = jest.fn();
|
import * as fs from "node:fs"
|
||||||
const mockGetBooleanInput = jest.fn();
|
import * as core from "@actions/core"
|
||||||
const mockGlob = jest.fn()
|
import type * as github from "@actions/github"
|
||||||
const mockReadFileSync = jest.fn();
|
import { beforeEach, describe, expect, it, vi } from "vitest"
|
||||||
const mockStatSync = jest.fn();
|
|
||||||
|
|
||||||
import {Artifact} from "../src/Artifact";
|
vi.mock("@actions/core")
|
||||||
import {ArtifactGlobber} from "../src/ArtifactGlobber";
|
vi.mock("fs")
|
||||||
import {Context} from "@actions/github/lib/context";
|
|
||||||
import {Inputs, CoreInputs} from "../src/Inputs";
|
|
||||||
|
|
||||||
const artifacts = [
|
import { Artifact } from "../src/Artifact.js"
|
||||||
new Artifact('a/art1'),
|
import type { ArtifactGlobber } from "../src/ArtifactGlobber.js"
|
||||||
new Artifact('b/art2')
|
import { CoreInputs, type Inputs } from "../src/Inputs.js"
|
||||||
]
|
|
||||||
|
|
||||||
jest.mock('@actions/core', () => {
|
const mockGetInput = vi.mocked(core.getInput)
|
||||||
return {
|
const mockGetBooleanInput = vi.mocked(core.getBooleanInput)
|
||||||
getInput: mockGetInput,
|
const mockReadFileSync = vi.mocked(fs.readFileSync)
|
||||||
getBooleanInput: mockGetBooleanInput
|
const _mockStatSync = vi.mocked(fs.statSync)
|
||||||
};
|
const mockExistsSync = vi.mocked(fs.existsSync)
|
||||||
})
|
const mockGlob = vi.fn()
|
||||||
|
|
||||||
jest.mock('fs', () => {
|
// existsSync is used by Context's constructor
|
||||||
// existsSync is used by Context's constructor
|
mockExistsSync.mockReturnValue(false)
|
||||||
// noinspection JSUnusedGlobalSymbols
|
|
||||||
return {
|
|
||||||
existsSync: () => {
|
|
||||||
return false
|
|
||||||
},
|
|
||||||
readFileSync: mockReadFileSync,
|
|
||||||
statSync: mockStatSync
|
|
||||||
};
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('Inputs', () => {
|
const artifacts = [new Artifact("a/art1"), new Artifact("b/art2")]
|
||||||
let context: Context;
|
|
||||||
let inputs: Inputs;
|
describe("Inputs", () => {
|
||||||
|
let context: typeof github.context
|
||||||
|
let inputs: Inputs
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockGetInput.mockReset()
|
mockGetInput.mockReset()
|
||||||
context = new Context()
|
mockGlob.mockClear()
|
||||||
|
context = {
|
||||||
|
payload: {},
|
||||||
|
eventName: "",
|
||||||
|
sha: "",
|
||||||
|
ref: "",
|
||||||
|
workflow: "",
|
||||||
|
action: "",
|
||||||
|
actor: "",
|
||||||
|
job: "",
|
||||||
|
runNumber: 0,
|
||||||
|
runId: 0,
|
||||||
|
runAttempt: 0,
|
||||||
|
apiUrl: "",
|
||||||
|
serverUrl: "",
|
||||||
|
graphqlUrl: "",
|
||||||
|
get repo() {
|
||||||
|
const repo = process.env.GITHUB_REPOSITORY || "/"
|
||||||
|
const [owner, repoName] = repo.split("/")
|
||||||
|
return { owner: owner || "", repo: repoName || "" }
|
||||||
|
},
|
||||||
|
issue: { owner: "", repo: "", number: 0 },
|
||||||
|
// biome-ignore lint/suspicious/noExplicitAny: Partial Context object for testing
|
||||||
|
} as any
|
||||||
inputs = new CoreInputs(createGlobber(), context)
|
inputs = new CoreInputs(createGlobber(), context)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('commit', () => {
|
describe("commit", () => {
|
||||||
it('returns commit', () => {
|
it("returns commit", () => {
|
||||||
mockGetInput.mockReturnValueOnce('commit')
|
mockGetInput.mockReturnValueOnce("commit")
|
||||||
expect(inputs.commit).toBe('commit')
|
expect(inputs.commit).toBe("commit")
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns undefined when omitted', () => {
|
it("returns undefined when omitted", () => {
|
||||||
mockGetInput.mockReturnValueOnce('')
|
mockGetInput.mockReturnValueOnce("")
|
||||||
expect(inputs.commit).toBeUndefined()
|
expect(inputs.commit).toBeUndefined()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns token', () => {
|
it("returns token", () => {
|
||||||
mockGetInput.mockReturnValue('42')
|
mockGetInput.mockReturnValue("42")
|
||||||
expect(inputs.token).toBe('42')
|
expect(inputs.token).toBe("42")
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('allowsUpdates', () => {
|
describe("allowsUpdates", () => {
|
||||||
it('returns false', () => {
|
it("returns false", () => {
|
||||||
expect(inputs.allowUpdates).toBe(false)
|
expect(inputs.allowUpdates).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns true', () => {
|
it("returns true", () => {
|
||||||
mockGetInput.mockReturnValue('true')
|
mockGetInput.mockReturnValue("true")
|
||||||
expect(inputs.allowUpdates).toBe(true)
|
expect(inputs.allowUpdates).toBe(true)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('artifactErrorsFailBuild', () => {
|
describe("artifactErrorsFailBuild", () => {
|
||||||
it('returns false', () => {
|
it("returns false", () => {
|
||||||
expect(inputs.artifactErrorsFailBuild).toBe(false)
|
expect(inputs.artifactErrorsFailBuild).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns true', () => {
|
it("returns true", () => {
|
||||||
mockGetInput.mockReturnValue('true')
|
mockGetInput.mockReturnValue("true")
|
||||||
expect(inputs.artifactErrorsFailBuild).toBe(true)
|
expect(inputs.artifactErrorsFailBuild).toBe(true)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('artifacts', () => {
|
describe("artifacts", () => {
|
||||||
it('globber told to throw errors', () => {
|
it("globber told to throw errors", () => {
|
||||||
mockGetInput.mockReturnValueOnce('art1')
|
mockGetInput.mockReturnValueOnce("art1").mockReturnValueOnce("contentType").mockReturnValueOnce("true")
|
||||||
.mockReturnValueOnce('contentType')
|
|
||||||
.mockReturnValueOnce('true')
|
|
||||||
|
|
||||||
expect(inputs.artifacts).toEqual(artifacts)
|
expect(inputs.artifacts).toEqual(artifacts)
|
||||||
expect(mockGlob).toBeCalledTimes(1)
|
expect(mockGlob).toHaveBeenCalledTimes(1)
|
||||||
expect(mockGlob).toBeCalledWith('art1', 'contentType', true)
|
expect(mockGlob).toHaveBeenCalledWith("art1", "contentType", true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns empty artifacts', () => {
|
it("returns empty artifacts", () => {
|
||||||
mockGetInput.mockReturnValueOnce('')
|
mockGetInput.mockReturnValueOnce("").mockReturnValueOnce("")
|
||||||
.mockReturnValueOnce('')
|
|
||||||
|
|
||||||
expect(inputs.artifacts).toEqual([])
|
expect(inputs.artifacts).toEqual([])
|
||||||
expect(mockGlob).toBeCalledTimes(0)
|
expect(mockGlob).toHaveBeenCalledTimes(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns input.artifacts', () => {
|
it("returns input.artifacts", () => {
|
||||||
mockGetInput.mockReturnValueOnce('art1')
|
mockGetInput.mockReturnValueOnce("art1").mockReturnValueOnce("contentType").mockReturnValueOnce("false")
|
||||||
.mockReturnValueOnce('contentType')
|
|
||||||
.mockReturnValueOnce('false')
|
|
||||||
|
|
||||||
expect(inputs.artifacts).toEqual(artifacts)
|
expect(inputs.artifacts).toEqual(artifacts)
|
||||||
expect(mockGlob).toBeCalledTimes(1)
|
expect(mockGlob).toHaveBeenCalledTimes(1)
|
||||||
expect(mockGlob).toBeCalledWith('art1', 'contentType', false)
|
expect(mockGlob).toHaveBeenCalledWith("art1", "contentType", false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns input.artifacts with default contentType', () => {
|
it("returns input.artifacts with default contentType", () => {
|
||||||
mockGetInput.mockReturnValueOnce('art1')
|
mockGetInput.mockReturnValueOnce("art1").mockReturnValueOnce("").mockReturnValueOnce("false")
|
||||||
.mockReturnValueOnce('')
|
|
||||||
.mockReturnValueOnce('false')
|
|
||||||
|
|
||||||
expect(inputs.artifacts).toEqual(artifacts)
|
expect(inputs.artifacts).toEqual(artifacts)
|
||||||
expect(mockGlob).toBeCalledTimes(1)
|
expect(mockGlob).toHaveBeenCalledTimes(1)
|
||||||
expect(mockGlob).toBeCalledWith('art1', 'raw', false)
|
expect(mockGlob).toHaveBeenCalledWith("art1", "raw", false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns input.artifact', () => {
|
it("returns input.artifact", () => {
|
||||||
mockGetInput.mockReturnValueOnce('')
|
mockGetInput
|
||||||
.mockReturnValueOnce('art2')
|
.mockReturnValueOnce("")
|
||||||
.mockReturnValueOnce('contentType')
|
.mockReturnValueOnce("art2")
|
||||||
.mockReturnValueOnce('false')
|
.mockReturnValueOnce("contentType")
|
||||||
|
.mockReturnValueOnce("false")
|
||||||
|
|
||||||
expect(inputs.artifacts).toEqual(artifacts)
|
expect(inputs.artifacts).toEqual(artifacts)
|
||||||
expect(mockGlob).toBeCalledTimes(1)
|
expect(mockGlob).toHaveBeenCalledTimes(1)
|
||||||
expect(mockGlob).toBeCalledWith('art2', 'contentType', false)
|
expect(mockGlob).toHaveBeenCalledWith("art2", "contentType", false)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('createdDraft', () => {
|
describe("createdDraft", () => {
|
||||||
it('returns false', () => {
|
it("returns false", () => {
|
||||||
expect(inputs.createdDraft).toBe(false)
|
expect(inputs.createdDraft).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns true', () => {
|
it("returns true", () => {
|
||||||
mockGetInput.mockReturnValue('true')
|
mockGetInput.mockReturnValue("true")
|
||||||
expect(inputs.createdDraft).toBe(true)
|
expect(inputs.createdDraft).toBe(true)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('createdReleaseBody', () => {
|
describe("createdReleaseBody", () => {
|
||||||
it('returns input body', () => {
|
it("returns input body", () => {
|
||||||
mockGetInput
|
mockGetInput.mockReturnValueOnce("false").mockReturnValueOnce("body")
|
||||||
.mockReturnValueOnce('false')
|
expect(inputs.createdReleaseBody).toBe("body")
|
||||||
.mockReturnValueOnce('body')
|
|
||||||
expect(inputs.createdReleaseBody).toBe('body')
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns body file contents', () => {
|
it("returns body file contents", () => {
|
||||||
mockGetInput
|
mockGetInput.mockReturnValueOnce("false").mockReturnValueOnce("").mockReturnValueOnce("a/path")
|
||||||
.mockReturnValueOnce('false')
|
mockReadFileSync.mockReturnValue("file")
|
||||||
.mockReturnValueOnce('')
|
|
||||||
.mockReturnValueOnce('a/path')
|
|
||||||
mockReadFileSync.mockReturnValue('file')
|
|
||||||
|
|
||||||
expect(inputs.createdReleaseBody).toBe('file')
|
expect(inputs.createdReleaseBody).toBe("file")
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns empty', () => {
|
it("returns empty", () => {
|
||||||
mockGetInput
|
mockGetInput.mockReturnValueOnce("false").mockReturnValueOnce("").mockReturnValueOnce("")
|
||||||
.mockReturnValueOnce('false')
|
expect(inputs.createdReleaseBody).toBe("")
|
||||||
.mockReturnValueOnce('')
|
|
||||||
.mockReturnValueOnce('')
|
|
||||||
expect(inputs.createdReleaseBody).toBe('')
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns undefined when omitted', () => {
|
it("returns undefined when omitted", () => {
|
||||||
mockGetInput
|
mockGetInput.mockReturnValueOnce("true").mockReturnValueOnce("body")
|
||||||
.mockReturnValueOnce('true')
|
|
||||||
.mockReturnValueOnce('body')
|
|
||||||
expect(inputs.createdReleaseBody).toBeUndefined()
|
expect(inputs.createdReleaseBody).toBeUndefined()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('createdReleaseName', () => {
|
describe("createdReleaseName", () => {
|
||||||
it('returns input name', () => {
|
it("returns input name", () => {
|
||||||
mockGetInput
|
mockGetInput.mockReturnValueOnce("false").mockReturnValueOnce("name")
|
||||||
.mockReturnValueOnce('false')
|
expect(inputs.createdReleaseName).toBe("name")
|
||||||
.mockReturnValueOnce('name')
|
|
||||||
expect(inputs.createdReleaseName).toBe('name')
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns undefined when omitted', () => {
|
it("returns undefined when omitted", () => {
|
||||||
mockGetInput
|
mockGetInput.mockReturnValueOnce("true").mockReturnValueOnce("name")
|
||||||
.mockReturnValueOnce('true')
|
|
||||||
.mockReturnValueOnce('name')
|
|
||||||
expect(inputs.createdReleaseName).toBeUndefined()
|
expect(inputs.createdReleaseName).toBeUndefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns tag', () => {
|
it("returns tag", () => {
|
||||||
mockGetInput
|
mockGetInput.mockReturnValueOnce("false").mockReturnValueOnce("")
|
||||||
.mockReturnValueOnce('false')
|
context.ref = "refs/tags/sha-tag"
|
||||||
.mockReturnValueOnce('')
|
expect(inputs.createdReleaseName).toBe("sha-tag")
|
||||||
context.ref = 'refs/tags/sha-tag'
|
|
||||||
expect(inputs.createdReleaseName).toBe('sha-tag')
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('discussionCategory', () => {
|
describe("discussionCategory", () => {
|
||||||
it('returns category', () => {
|
it("returns category", () => {
|
||||||
mockGetInput.mockReturnValue('Release')
|
mockGetInput.mockReturnValue("Release")
|
||||||
expect(inputs.discussionCategory).toBe('Release')
|
expect(inputs.discussionCategory).toBe("Release")
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns undefined', () => {
|
it("returns undefined", () => {
|
||||||
mockGetInput.mockReturnValue('')
|
mockGetInput.mockReturnValue("")
|
||||||
expect(inputs.discussionCategory).toBe(undefined)
|
expect(inputs.discussionCategory).toBe(undefined)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('generateReleaseNotes', () => {
|
describe("generateReleaseNotes", () => {
|
||||||
it('returns returns true', function () {
|
it("returns returns true", () => {
|
||||||
mockGetInput.mockReturnValue("true")
|
mockGetInput.mockReturnValue("true")
|
||||||
expect(inputs.generateReleaseNotes).toBe(true)
|
expect(inputs.generateReleaseNotes).toBe(true)
|
||||||
});
|
})
|
||||||
|
|
||||||
it('returns false when omitted', function () {
|
it("returns false when omitted", () => {
|
||||||
mockGetInput.mockReturnValue("")
|
mockGetInput.mockReturnValue("")
|
||||||
expect(inputs.generateReleaseNotes).toBe(false)
|
expect(inputs.generateReleaseNotes).toBe(false)
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('makeLatest', () => {
|
|
||||||
it('returns legacy', () => {
|
|
||||||
mockGetInput.mockReturnValueOnce('legacy')
|
|
||||||
expect(inputs.makeLatest).toBe('legacy')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('returns false', () => {
|
|
||||||
mockGetInput.mockReturnValueOnce('false')
|
|
||||||
expect(inputs.makeLatest).toBe('false')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('returns true', () => {
|
|
||||||
mockGetInput.mockReturnValueOnce('true')
|
|
||||||
expect(inputs.makeLatest).toBe('true')
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('owner', () => {
|
describe("generateReleaseNotesPreviousTag", () => {
|
||||||
it('returns owner from context', function () {
|
it("returns the previous tag when provided", () => {
|
||||||
|
mockGetInput.mockReturnValue("v1.0.0")
|
||||||
|
expect(inputs.generateReleaseNotesPreviousTag).toBe("v1.0.0")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns undefined when omitted", () => {
|
||||||
|
mockGetInput.mockReturnValue("")
|
||||||
|
expect(inputs.generateReleaseNotesPreviousTag).toBeUndefined()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("immutableCreate", () => {
|
||||||
|
it("returns false by default", () => {
|
||||||
|
mockGetInput.mockReturnValue("")
|
||||||
|
expect(inputs.immutableCreate).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns true when explicitly set", () => {
|
||||||
|
mockGetInput.mockReturnValue("true")
|
||||||
|
expect(inputs.immutableCreate).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns false when explicitly disabled", () => {
|
||||||
|
mockGetInput.mockReturnValue("false")
|
||||||
|
expect(inputs.immutableCreate).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("makeLatest", () => {
|
||||||
|
it("returns legacy", () => {
|
||||||
|
mockGetInput.mockReturnValueOnce("legacy")
|
||||||
|
expect(inputs.makeLatest).toBe("legacy")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns false", () => {
|
||||||
|
mockGetInput.mockReturnValueOnce("false")
|
||||||
|
expect(inputs.makeLatest).toBe("false")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns true", () => {
|
||||||
|
mockGetInput.mockReturnValueOnce("true")
|
||||||
|
expect(inputs.makeLatest).toBe("true")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns undefined", () => {
|
||||||
|
mockGetInput.mockReturnValueOnce("something_else")
|
||||||
|
expect(inputs.makeLatest).toBe(undefined)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("owner", () => {
|
||||||
|
it("returns owner from context", () => {
|
||||||
process.env.GITHUB_REPOSITORY = "owner/repo"
|
process.env.GITHUB_REPOSITORY = "owner/repo"
|
||||||
mockGetInput.mockReturnValue("")
|
mockGetInput.mockReturnValue("")
|
||||||
expect(inputs.owner).toBe("owner")
|
expect(inputs.owner).toBe("owner")
|
||||||
});
|
})
|
||||||
it('returns owner from inputs', function () {
|
it("returns owner from inputs", () => {
|
||||||
mockGetInput.mockReturnValue("owner")
|
mockGetInput.mockReturnValue("owner")
|
||||||
expect(inputs.owner).toBe("owner")
|
expect(inputs.owner).toBe("owner")
|
||||||
});
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('createdPrerelase', () => {
|
describe("createdPrerelase", () => {
|
||||||
it('returns false', () => {
|
it("returns false", () => {
|
||||||
expect(inputs.createdPrerelease).toBe(false)
|
expect(inputs.createdPrerelease).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns true', () => {
|
it("returns true", () => {
|
||||||
mockGetInput.mockReturnValue('true')
|
mockGetInput.mockReturnValue("true")
|
||||||
expect(inputs.createdPrerelease).toBe(true)
|
expect(inputs.createdPrerelease).toBe(true)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('replacesArtifacts', () => {
|
describe("replacesArtifacts", () => {
|
||||||
it('returns false', () => {
|
it("returns false", () => {
|
||||||
expect(inputs.replacesArtifacts).toBe(false)
|
expect(inputs.replacesArtifacts).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns true', () => {
|
it("returns true", () => {
|
||||||
mockGetInput.mockReturnValue('true')
|
mockGetInput.mockReturnValue("true")
|
||||||
expect(inputs.replacesArtifacts).toBe(true)
|
expect(inputs.replacesArtifacts).toBe(true)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('removeArtifacts', () => {
|
describe("removeArtifacts", () => {
|
||||||
it('returns false', () => {
|
it("returns false", () => {
|
||||||
expect(inputs.removeArtifacts).toBe(false)
|
expect(inputs.removeArtifacts).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns true', () => {
|
it("returns true", () => {
|
||||||
mockGetInput.mockReturnValue('true')
|
mockGetInput.mockReturnValue("true")
|
||||||
expect(inputs.removeArtifacts).toBe(true)
|
expect(inputs.removeArtifacts).toBe(true)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('repo', () => {
|
describe("repo", () => {
|
||||||
it('returns repo from context', function () {
|
it("returns repo from context", () => {
|
||||||
process.env.GITHUB_REPOSITORY = "owner/repo"
|
process.env.GITHUB_REPOSITORY = "owner/repo"
|
||||||
mockGetInput.mockReturnValue("")
|
mockGetInput.mockReturnValue("")
|
||||||
expect(inputs.repo).toBe("repo")
|
expect(inputs.repo).toBe("repo")
|
||||||
});
|
})
|
||||||
it('returns repo from inputs', function () {
|
it("returns repo from inputs", () => {
|
||||||
mockGetInput.mockReturnValue("repo")
|
mockGetInput.mockReturnValue("repo")
|
||||||
expect(inputs.repo).toBe("repo")
|
expect(inputs.repo).toBe("repo")
|
||||||
});
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('skipIfReleaseExists', () => {
|
describe("skipIfReleaseExists", () => {
|
||||||
it('returns false', () => {
|
it("returns false", () => {
|
||||||
mockGetBooleanInput.mockReturnValue(false)
|
mockGetBooleanInput.mockReturnValue(false)
|
||||||
expect(inputs.skipIfReleaseExists).toBe(false)
|
expect(inputs.skipIfReleaseExists).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns true', () => {
|
it("returns true", () => {
|
||||||
mockGetBooleanInput.mockReturnValue(true)
|
mockGetBooleanInput.mockReturnValue(true)
|
||||||
expect(inputs.skipIfReleaseExists).toBe(true)
|
expect(inputs.skipIfReleaseExists).toBe(true)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('tag', () => {
|
describe("tag", () => {
|
||||||
it('returns input tag', () => {
|
it("returns input tag", () => {
|
||||||
mockGetInput.mockReturnValue('tag')
|
mockGetInput.mockReturnValue("tag")
|
||||||
expect(inputs.tag).toBe('tag')
|
expect(inputs.tag).toBe("tag")
|
||||||
})
|
})
|
||||||
it('returns context sha when input is empty', () => {
|
it("returns context sha when input is empty", () => {
|
||||||
mockGetInput.mockReturnValue('')
|
mockGetInput.mockReturnValue("")
|
||||||
context.ref = 'refs/tags/sha-tag'
|
context.ref = "refs/tags/sha-tag"
|
||||||
expect(inputs.tag).toBe('sha-tag')
|
expect(inputs.tag).toBe("sha-tag")
|
||||||
})
|
})
|
||||||
it('returns context sha when input is null', () => {
|
it("throws if no tag", () => {
|
||||||
mockGetInput.mockReturnValue(null)
|
|
||||||
context.ref = 'refs/tags/sha-tag'
|
|
||||||
expect(inputs.tag).toBe('sha-tag')
|
|
||||||
})
|
|
||||||
it('throws if no tag', () => {
|
|
||||||
context.ref = ""
|
context.ref = ""
|
||||||
expect(() => inputs.tag).toThrow()
|
expect(() => inputs.tag).toThrow()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('updatedDraft', () => {
|
describe("updatedDraft", () => {
|
||||||
it('returns false', () => {
|
it("returns false", () => {
|
||||||
expect(inputs.updatedDraft).toBe(false)
|
expect(inputs.updatedDraft).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns true', () => {
|
it("returns true", () => {
|
||||||
mockGetInput
|
mockGetInput.mockReturnValueOnce("false").mockReturnValue("true")
|
||||||
.mockReturnValueOnce('false')
|
|
||||||
.mockReturnValue('true')
|
|
||||||
expect(inputs.updatedDraft).toBe(true)
|
expect(inputs.updatedDraft).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns true when omitted is blank', () => {
|
it("returns true when omitted is blank", () => {
|
||||||
mockGetInput
|
mockGetInput.mockReturnValueOnce("").mockReturnValue("true")
|
||||||
.mockReturnValueOnce('')
|
|
||||||
.mockReturnValue('true')
|
|
||||||
expect(inputs.updatedDraft).toBe(true)
|
expect(inputs.updatedDraft).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns undefined when omitted for update', () => {
|
it("returns undefined when omitted for update", () => {
|
||||||
mockGetInput
|
mockGetInput.mockReturnValueOnce("true").mockReturnValueOnce("true")
|
||||||
.mockReturnValueOnce('true')
|
|
||||||
.mockReturnValueOnce('true')
|
|
||||||
expect(inputs.updatedDraft).toBeUndefined()
|
expect(inputs.updatedDraft).toBeUndefined()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('updatedReleaseBody', () => {
|
describe("updatedReleaseBody", () => {
|
||||||
it('returns input body', () => {
|
it("returns input body", () => {
|
||||||
mockGetInput
|
mockGetInput.mockReturnValueOnce("false").mockReturnValueOnce("false").mockReturnValueOnce("body")
|
||||||
.mockReturnValueOnce('false')
|
expect(inputs.updatedReleaseBody).toBe("body")
|
||||||
.mockReturnValueOnce('false')
|
|
||||||
.mockReturnValueOnce('body')
|
|
||||||
expect(inputs.updatedReleaseBody).toBe('body')
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns body file contents', () => {
|
it("returns body file contents", () => {
|
||||||
mockGetInput
|
mockGetInput
|
||||||
.mockReturnValueOnce('false')
|
.mockReturnValueOnce("false")
|
||||||
.mockReturnValueOnce('false')
|
.mockReturnValueOnce("false")
|
||||||
.mockReturnValueOnce('')
|
.mockReturnValueOnce("")
|
||||||
.mockReturnValueOnce('a/path')
|
.mockReturnValueOnce("a/path")
|
||||||
mockReadFileSync.mockReturnValue('file')
|
mockReadFileSync.mockReturnValue("file")
|
||||||
|
|
||||||
expect(inputs.updatedReleaseBody).toBe('file')
|
expect(inputs.updatedReleaseBody).toBe("file")
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns empty', () => {
|
it("returns empty", () => {
|
||||||
mockGetInput
|
mockGetInput
|
||||||
.mockReturnValueOnce('false')
|
.mockReturnValueOnce("false")
|
||||||
.mockReturnValueOnce('false')
|
.mockReturnValueOnce("false")
|
||||||
.mockReturnValueOnce('')
|
.mockReturnValueOnce("")
|
||||||
.mockReturnValueOnce('')
|
.mockReturnValueOnce("")
|
||||||
expect(inputs.updatedReleaseBody).toBe('')
|
expect(inputs.updatedReleaseBody).toBe("")
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns undefined when omitted', () => {
|
it("returns undefined when omitted", () => {
|
||||||
mockGetInput
|
mockGetInput.mockReturnValueOnce("true").mockReturnValueOnce("false").mockReturnValueOnce("body")
|
||||||
.mockReturnValueOnce('true')
|
|
||||||
.mockReturnValueOnce('false')
|
|
||||||
.mockReturnValueOnce('body')
|
|
||||||
expect(inputs.updatedReleaseBody).toBeUndefined()
|
expect(inputs.updatedReleaseBody).toBeUndefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns undefined when omitted for update', () => {
|
it("returns undefined when omitted for update", () => {
|
||||||
mockGetInput
|
mockGetInput.mockReturnValueOnce("false").mockReturnValueOnce("true").mockReturnValueOnce("body")
|
||||||
.mockReturnValueOnce('false')
|
|
||||||
.mockReturnValueOnce('true')
|
|
||||||
.mockReturnValueOnce('body')
|
|
||||||
expect(inputs.updatedReleaseBody).toBeUndefined()
|
expect(inputs.updatedReleaseBody).toBeUndefined()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('updatedReleaseName', () => {
|
describe("updatedReleaseName", () => {
|
||||||
it('returns input name', () => {
|
it("returns input name", () => {
|
||||||
mockGetInput
|
mockGetInput.mockReturnValueOnce("false").mockReturnValueOnce("false").mockReturnValueOnce("name")
|
||||||
.mockReturnValueOnce('false')
|
expect(inputs.updatedReleaseName).toBe("name")
|
||||||
.mockReturnValueOnce('false')
|
|
||||||
.mockReturnValueOnce('name')
|
|
||||||
expect(inputs.updatedReleaseName).toBe('name')
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns undefined when omitted', () => {
|
it("returns undefined when omitted", () => {
|
||||||
mockGetInput
|
mockGetInput.mockReturnValueOnce("true").mockReturnValueOnce("false").mockReturnValueOnce("name")
|
||||||
.mockReturnValueOnce('true')
|
|
||||||
.mockReturnValueOnce('false')
|
|
||||||
.mockReturnValueOnce('name')
|
|
||||||
expect(inputs.updatedReleaseName).toBeUndefined()
|
expect(inputs.updatedReleaseName).toBeUndefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns undefined when omitted for update', () => {
|
it("returns undefined when omitted for update", () => {
|
||||||
mockGetInput
|
mockGetInput.mockReturnValueOnce("false").mockReturnValueOnce("true").mockReturnValueOnce("name")
|
||||||
.mockReturnValueOnce('false')
|
|
||||||
.mockReturnValueOnce('true')
|
|
||||||
.mockReturnValueOnce('name')
|
|
||||||
expect(inputs.updatedReleaseName).toBeUndefined()
|
expect(inputs.updatedReleaseName).toBeUndefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns tag', () => {
|
it("returns tag", () => {
|
||||||
mockGetInput
|
mockGetInput.mockReturnValueOnce("false").mockReturnValueOnce("false").mockReturnValueOnce("")
|
||||||
.mockReturnValueOnce('false')
|
context.ref = "refs/tags/sha-tag"
|
||||||
.mockReturnValueOnce('false')
|
expect(inputs.updatedReleaseName).toBe("sha-tag")
|
||||||
.mockReturnValueOnce('')
|
|
||||||
context.ref = 'refs/tags/sha-tag'
|
|
||||||
expect(inputs.updatedReleaseName).toBe('sha-tag')
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('updatedPrerelease', () => {
|
describe("updatedPrerelease", () => {
|
||||||
it('returns false', () => {
|
it("returns false", () => {
|
||||||
mockGetInput
|
mockGetInput.mockReturnValueOnce("false").mockReturnValueOnce("false")
|
||||||
.mockReturnValueOnce('false')
|
|
||||||
.mockReturnValueOnce('false')
|
|
||||||
expect(inputs.updatedPrerelease).toBe(false)
|
expect(inputs.updatedPrerelease).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns true', () => {
|
it("returns true", () => {
|
||||||
mockGetInput
|
mockGetInput.mockReturnValueOnce("false").mockReturnValueOnce("true")
|
||||||
.mockReturnValueOnce('false')
|
|
||||||
.mockReturnValueOnce('true')
|
|
||||||
expect(inputs.updatedPrerelease).toBe(true)
|
expect(inputs.updatedPrerelease).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns undefined when omitted for update', () => {
|
it("returns undefined when omitted for update", () => {
|
||||||
mockGetInput
|
mockGetInput.mockReturnValueOnce("true").mockReturnValueOnce("false")
|
||||||
.mockReturnValueOnce('true')
|
|
||||||
.mockReturnValueOnce('false')
|
|
||||||
expect(inputs.updatedPrerelease).toBeUndefined()
|
expect(inputs.updatedPrerelease).toBeUndefined()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('updateOnlyUnreleased', () => {
|
describe("updateOnlyUnreleased", () => {
|
||||||
it('returns false', () => {
|
it("returns false", () => {
|
||||||
expect(inputs.updateOnlyUnreleased).toBe(false)
|
expect(inputs.updateOnlyUnreleased).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns true', () => {
|
it("returns true", () => {
|
||||||
mockGetInput.mockReturnValueOnce('true')
|
mockGetInput.mockReturnValueOnce("true")
|
||||||
expect(inputs.updateOnlyUnreleased).toBe(true)
|
expect(inputs.updateOnlyUnreleased).toBe(true)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("omitBodyDuringUpdate", () => {
|
||||||
|
it("returns false", () => {
|
||||||
|
expect(inputs.omitBodyDuringUpdate).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns true", () => {
|
||||||
|
mockGetInput.mockReturnValueOnce("true")
|
||||||
|
expect(inputs.omitBodyDuringUpdate).toBe(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
function createGlobber(): ArtifactGlobber {
|
function createGlobber(): ArtifactGlobber {
|
||||||
const MockGlobber = jest.fn<ArtifactGlobber, any>(() => {
|
const MockGlobber = vi.fn<() => ArtifactGlobber>(() => {
|
||||||
return {
|
return {
|
||||||
globArtifactString: mockGlob
|
globArtifactString: mockGlob,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
mockGlob.mockImplementation(() => artifacts)
|
mockGlob.mockImplementation(() => artifacts)
|
||||||
return new MockGlobber()
|
return MockGlobber()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
import {Action} from "../src/Action";
|
import * as path from "node:path"
|
||||||
import * as github from "@actions/github";
|
import * as github from "@actions/github"
|
||||||
import {Inputs} from "../src/Inputs";
|
import { beforeEach, describe, it, vi } from "vitest"
|
||||||
import {GithubReleases, ReleaseData} from "../src/Releases";
|
import { Action } from "../src/Action.js"
|
||||||
import {GithubArtifactUploader} from "../src/ArtifactUploader";
|
import { ReleaseActionSkipper } from "../src/ActionSkipper.js"
|
||||||
import * as path from "path";
|
import { GithubArtifactDestroyer } from "../src/ArtifactDestroyer.js"
|
||||||
import {FileArtifactGlobber} from "../src/ArtifactGlobber";
|
import { FileArtifactGlobber } from "../src/ArtifactGlobber.js"
|
||||||
import {Outputs} from "../src/Outputs";
|
import { GithubArtifactUploader } from "../src/ArtifactUploader.js"
|
||||||
import {GithubArtifactDestroyer} from "../src/ArtifactDestroyer";
|
import type { Inputs } from "../src/Inputs.js"
|
||||||
import {ReleaseActionSkipper} from "../src/ActionSkipper";
|
import type { Outputs } from "../src/Outputs.js"
|
||||||
|
import { GithubReleases, type ReleaseData } from "../src/Releases.js"
|
||||||
|
|
||||||
// This test is currently intended to be manually run during development. To run:
|
// This test is currently intended to be manually run during development. To run:
|
||||||
// - Make sure you have an environment variable named GITHUB_TOKEN assigned to your token
|
// - Make sure you have an environment variable named GITHUB_TOKEN assigned to your token
|
||||||
// - Remove skip from the test below
|
// - Remove skip from the test below
|
||||||
describe.skip('Integration Test', () => {
|
describe.skip("Integration Test", () => {
|
||||||
let action: Action
|
let action: Action
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -22,23 +23,19 @@ describe.skip('Integration Test', () => {
|
|||||||
const inputs = getInputs()
|
const inputs = getInputs()
|
||||||
const outputs = getOutputs()
|
const outputs = getOutputs()
|
||||||
const releases = new GithubReleases(inputs, git)
|
const releases = new GithubReleases(inputs, git)
|
||||||
const uploader = new GithubArtifactUploader(
|
const uploader = new GithubArtifactUploader(releases, inputs.replacesArtifacts, inputs.artifactErrorsFailBuild)
|
||||||
releases,
|
|
||||||
inputs.replacesArtifacts,
|
|
||||||
inputs.artifactErrorsFailBuild,
|
|
||||||
)
|
|
||||||
const artifactDestroyer = new GithubArtifactDestroyer(releases)
|
const artifactDestroyer = new GithubArtifactDestroyer(releases)
|
||||||
const actionSkipper = new ReleaseActionSkipper(inputs.skipIfReleaseExists, releases, inputs.tag)
|
const actionSkipper = new ReleaseActionSkipper(inputs.skipIfReleaseExists, releases, inputs.tag)
|
||||||
|
|
||||||
action = new Action(inputs, outputs, releases, uploader, artifactDestroyer, actionSkipper)
|
action = new Action(inputs, outputs, releases, uploader, artifactDestroyer, actionSkipper)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Performs action', async () => {
|
it("Performs action", async () => {
|
||||||
await action.perform()
|
await action.perform()
|
||||||
})
|
})
|
||||||
|
|
||||||
function getInputs(): Inputs {
|
function getInputs(): Inputs {
|
||||||
const MockInputs = jest.fn<Inputs, any>(() => {
|
const MockInputs = vi.fn<() => Inputs>(() => {
|
||||||
return {
|
return {
|
||||||
allowUpdates: true,
|
allowUpdates: true,
|
||||||
artifactErrorsFailBuild: false,
|
artifactErrorsFailBuild: false,
|
||||||
@@ -47,8 +44,10 @@ describe.skip('Integration Test', () => {
|
|||||||
createdReleaseBody: "This release was generated by release-action's integration test",
|
createdReleaseBody: "This release was generated by release-action's integration test",
|
||||||
createdReleaseName: "Releases Action Integration Test",
|
createdReleaseName: "Releases Action Integration Test",
|
||||||
commit: undefined,
|
commit: undefined,
|
||||||
discussionCategory: 'Release',
|
discussionCategory: "Release",
|
||||||
generateReleaseNotes: true,
|
generateReleaseNotes: true,
|
||||||
|
immutableCreate: true,
|
||||||
|
omitBodyDuringUpdate: false,
|
||||||
owner: "ncipollo",
|
owner: "ncipollo",
|
||||||
createdPrerelease: false,
|
createdPrerelease: false,
|
||||||
replacesArtifacts: true,
|
replacesArtifacts: true,
|
||||||
@@ -61,26 +60,29 @@ describe.skip('Integration Test', () => {
|
|||||||
updatedReleaseBody: "This release was updated by release-action's integration test",
|
updatedReleaseBody: "This release was updated by release-action's integration test",
|
||||||
updatedReleaseName: "Releases Action Integration Test",
|
updatedReleaseName: "Releases Action Integration Test",
|
||||||
updatedPrerelease: false,
|
updatedPrerelease: false,
|
||||||
updateOnlyUnreleased: false
|
updateOnlyUnreleased: false,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return new MockInputs();
|
return MockInputs()
|
||||||
}
|
}
|
||||||
|
|
||||||
function getOutputs(): Outputs {
|
function getOutputs(): Outputs {
|
||||||
const MockOutputs = jest.fn<Outputs, any>(() => {
|
const MockOutputs = vi.fn<() => Outputs>(() => {
|
||||||
return {
|
return {
|
||||||
applyReleaseData(releaseData: ReleaseData) {
|
applyReleaseData(releaseData: ReleaseData) {
|
||||||
console.log(`Release Data: ${releaseData}`)
|
console.log(`Release Data: ${releaseData}`)
|
||||||
}
|
},
|
||||||
|
applyAssetUrls(assetUrls: Record<string, string>) {
|
||||||
|
console.log(`Asset URLs: ${JSON.stringify(assetUrls)}`)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return new MockOutputs()
|
return MockOutputs()
|
||||||
}
|
}
|
||||||
|
|
||||||
function artifacts() {
|
function artifacts() {
|
||||||
const globber = new FileArtifactGlobber()
|
const globber = new FileArtifactGlobber()
|
||||||
const artifactPath = path.join(__dirname, 'Integration.test.ts')
|
const artifactPath = path.join(__dirname, "Integration.test.ts")
|
||||||
const artifactString = `~/Desktop,~/Desktop/test.txt,blarg.tx, ${artifactPath}`
|
const artifactString = `~/Desktop,~/Desktop/test.txt,blarg.tx, ${artifactPath}`
|
||||||
return globber.globArtifactString(artifactString, "raw", false)
|
return globber.globArtifactString(artifactString, "raw", false)
|
||||||
}
|
}
|
||||||
@@ -88,5 +90,4 @@ describe.skip('Integration Test', () => {
|
|||||||
function getToken(): string {
|
function getToken(): string {
|
||||||
return process.env.GITHUB_TOKEN ?? ""
|
return process.env.GITHUB_TOKEN ?? ""
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,29 +1,65 @@
|
|||||||
const mockSetOutput = jest.fn();
|
import * as core from "@actions/core"
|
||||||
|
import { describe, expect, it, vi } from "vitest"
|
||||||
|
import { CoreOutputs, type Outputs } from "../src/Outputs.js"
|
||||||
|
import type { ReleaseData } from "../src/Releases.js"
|
||||||
|
|
||||||
import {CoreOutputs, Outputs} from "../src/Outputs";
|
vi.mock("@actions/core")
|
||||||
import {ReleaseData} from "../src/Releases";
|
const mockSetOutput = vi.mocked(core.setOutput)
|
||||||
|
|
||||||
jest.mock('@actions/core', () => {
|
const TEST_URLS = {
|
||||||
return {setOutput: mockSetOutput};
|
HTML_URL: "https://api.example.com/assets",
|
||||||
})
|
UPLOAD_URL: "https://api.example.com",
|
||||||
|
TARBALL_URL: "https://api.example.com/tarball",
|
||||||
|
ZIPBALL_URL: "https://api.example.com/zipball",
|
||||||
|
} as const
|
||||||
|
|
||||||
describe('Outputs', () => {
|
describe("Outputs", () => {
|
||||||
let outputs: Outputs;
|
let outputs: Outputs
|
||||||
let releaseData: ReleaseData
|
let releaseData: ReleaseData
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
outputs = new CoreOutputs()
|
outputs = new CoreOutputs()
|
||||||
releaseData = {
|
releaseData = {
|
||||||
id: 1,
|
id: 1,
|
||||||
html_url: 'https://api.example.com/assets',
|
html_url: TEST_URLS.HTML_URL,
|
||||||
upload_url: 'https://api.example.com'
|
upload_url: TEST_URLS.UPLOAD_URL,
|
||||||
|
tarball_url: TEST_URLS.TARBALL_URL,
|
||||||
|
zipball_url: TEST_URLS.ZIPBALL_URL,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Applies the release data to the action output', () => {
|
it("Applies the release data to the action output", () => {
|
||||||
outputs.applyReleaseData(releaseData)
|
outputs.applyReleaseData(releaseData)
|
||||||
expect(mockSetOutput).toBeCalledWith('id', releaseData.id)
|
expect(mockSetOutput).toHaveBeenCalledWith("id", releaseData.id)
|
||||||
expect(mockSetOutput).toBeCalledWith('html_url', releaseData.html_url)
|
expect(mockSetOutput).toHaveBeenCalledWith("html_url", releaseData.html_url)
|
||||||
expect(mockSetOutput).toBeCalledWith('upload_url', releaseData.upload_url)
|
expect(mockSetOutput).toHaveBeenCalledWith("upload_url", releaseData.upload_url)
|
||||||
|
expect(mockSetOutput).toHaveBeenCalledWith("tarball_url", releaseData.tarball_url)
|
||||||
|
expect(mockSetOutput).toHaveBeenCalledWith("zipball_url", releaseData.zipball_url)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("Handles null tarball_url and zipball_url", () => {
|
||||||
|
const releaseDataWithNulls = {
|
||||||
|
...releaseData,
|
||||||
|
tarball_url: null,
|
||||||
|
zipball_url: null,
|
||||||
|
}
|
||||||
|
outputs.applyReleaseData(releaseDataWithNulls)
|
||||||
|
expect(mockSetOutput).toHaveBeenCalledWith("tarball_url", "")
|
||||||
|
expect(mockSetOutput).toHaveBeenCalledWith("zipball_url", "")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("Applies asset URLs to the action output", () => {
|
||||||
|
const assetUrls = {
|
||||||
|
"example.zip": "https://github.com/owner/repo/releases/download/v1.0.0/example.zip",
|
||||||
|
"example.tar.gz": "https://github.com/owner/repo/releases/download/v1.0.0/example.tar.gz",
|
||||||
|
}
|
||||||
|
outputs.applyAssetUrls(assetUrls)
|
||||||
|
expect(mockSetOutput).toHaveBeenCalledWith("assets", JSON.stringify(assetUrls))
|
||||||
|
})
|
||||||
|
|
||||||
|
it("Applies empty asset URLs to the action output", () => {
|
||||||
|
const assetUrls = {}
|
||||||
|
outputs.applyAssetUrls(assetUrls)
|
||||||
|
expect(mockSetOutput).toHaveBeenCalledWith("assets", JSON.stringify(assetUrls))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
42
__tests__/PathExpander.test.ts
Normal file
42
__tests__/PathExpander.test.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import os from "node:os"
|
||||||
|
import { describe, expect, it } from "vitest"
|
||||||
|
import { expandTilde } from "../src/PathExpander.js"
|
||||||
|
|
||||||
|
describe("PathExpander", () => {
|
||||||
|
describe("expandTilde", () => {
|
||||||
|
it("expands ~ at the start of a path", () => {
|
||||||
|
const result = expandTilde("~/documents")
|
||||||
|
expect(result).toBe(`${os.homedir()}/documents`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("expands ~ with backslash separator", () => {
|
||||||
|
const result = expandTilde("~\\documents")
|
||||||
|
expect(result).toBe(`${os.homedir()}\\documents`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("expands standalone ~", () => {
|
||||||
|
const result = expandTilde("~")
|
||||||
|
expect(result).toBe(os.homedir())
|
||||||
|
})
|
||||||
|
|
||||||
|
it("does not expand ~ in the middle of a path", () => {
|
||||||
|
const result = expandTilde("/home/~user/documents")
|
||||||
|
expect(result).toBe("/home/~user/documents")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("does not expand ~username patterns", () => {
|
||||||
|
const result = expandTilde("~username/documents")
|
||||||
|
expect(result).toBe("~username/documents")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns path unchanged when no tilde present", () => {
|
||||||
|
const result = expandTilde("/absolute/path")
|
||||||
|
expect(result).toBe("/absolute/path")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns relative path unchanged", () => {
|
||||||
|
const result = expandTilde("relative/path")
|
||||||
|
expect(result).toBe("relative/path")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -1,13 +1,14 @@
|
|||||||
import {ReleaseValidator} from "../src/ReleaseValidator";
|
import { describe, expect, it } from "vitest"
|
||||||
|
import { ReleaseValidator } from "../src/ReleaseValidator.js"
|
||||||
|
|
||||||
describe("validateReleaseUpdate", () => {
|
describe("validateReleaseUpdate", () => {
|
||||||
describe("updateOnlyUnreleased is disabled", () => {
|
describe("updateOnlyUnreleased is disabled", () => {
|
||||||
const validator = new ReleaseValidator(false)
|
const validator = new ReleaseValidator(false)
|
||||||
it('should not throw', () => {
|
it("should not throw", () => {
|
||||||
const releaseResponse = {
|
const releaseResponse = {
|
||||||
draft: false,
|
draft: false,
|
||||||
prerelease: false,
|
prerelease: false,
|
||||||
name: "Name"
|
name: "Name",
|
||||||
}
|
}
|
||||||
expect(() => {
|
expect(() => {
|
||||||
validator.validateReleaseUpdate(releaseResponse)
|
validator.validateReleaseUpdate(releaseResponse)
|
||||||
@@ -16,59 +17,59 @@ describe("validateReleaseUpdate", () => {
|
|||||||
})
|
})
|
||||||
describe("updateOnlyUnreleased is enabled", () => {
|
describe("updateOnlyUnreleased is enabled", () => {
|
||||||
const validator = new ReleaseValidator(true)
|
const validator = new ReleaseValidator(true)
|
||||||
it('should throw if neither draft or prerelease are enabled', () => {
|
it("should throw if neither draft or prerelease are enabled", () => {
|
||||||
const releaseResponse = {
|
const releaseResponse = {
|
||||||
draft: false,
|
draft: false,
|
||||||
prerelease: false,
|
prerelease: false,
|
||||||
name: "Name"
|
name: "Name",
|
||||||
}
|
}
|
||||||
expect(() => {
|
expect(() => {
|
||||||
validator.validateReleaseUpdate(releaseResponse)
|
validator.validateReleaseUpdate(releaseResponse)
|
||||||
}).toThrow()
|
}).toThrow()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should not throw if draft is enabled', () => {
|
it("should not throw if draft is enabled", () => {
|
||||||
const releaseResponse = {
|
const releaseResponse = {
|
||||||
draft: true,
|
draft: true,
|
||||||
prerelease: false,
|
prerelease: false,
|
||||||
name: "Name"
|
name: "Name",
|
||||||
}
|
}
|
||||||
expect(() => {
|
expect(() => {
|
||||||
validator.validateReleaseUpdate(releaseResponse)
|
validator.validateReleaseUpdate(releaseResponse)
|
||||||
}).not.toThrow()
|
}).not.toThrow()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should not throw if prerelease is enabled', () => {
|
it("should not throw if prerelease is enabled", () => {
|
||||||
const releaseResponse = {
|
const releaseResponse = {
|
||||||
draft: false,
|
draft: false,
|
||||||
prerelease: true,
|
prerelease: true,
|
||||||
name: "Name"
|
name: "Name",
|
||||||
}
|
}
|
||||||
expect(() => {
|
expect(() => {
|
||||||
validator.validateReleaseUpdate(releaseResponse)
|
validator.validateReleaseUpdate(releaseResponse)
|
||||||
}).not.toThrow()
|
}).not.toThrow()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should not throw if draft & prerelease is enabled', () => {
|
it("should not throw if draft & prerelease is enabled", () => {
|
||||||
const releaseResponse = {
|
const releaseResponse = {
|
||||||
draft: true,
|
draft: true,
|
||||||
prerelease: true,
|
prerelease: true,
|
||||||
name: "Name"
|
name: "Name",
|
||||||
}
|
}
|
||||||
expect(() => {
|
expect(() => {
|
||||||
validator.validateReleaseUpdate(releaseResponse)
|
validator.validateReleaseUpdate(releaseResponse)
|
||||||
}).not.toThrow()
|
}).not.toThrow()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should default error message release name to release', () => {
|
it("should default error message release name to release", () => {
|
||||||
const releaseResponse = {
|
const releaseResponse = {
|
||||||
draft: false,
|
draft: false,
|
||||||
prerelease: false,
|
prerelease: false,
|
||||||
name: null
|
name: null,
|
||||||
}
|
}
|
||||||
expect(() => {
|
expect(() => {
|
||||||
validator.validateReleaseUpdate(releaseResponse)
|
validator.validateReleaseUpdate(releaseResponse)
|
||||||
}).toThrow(`Tried to update "release" which is neither a draft or prerelease. (updateOnlyUnreleased is on)`)
|
}).toThrow(`Tried to update "release" which is neither a draft or prerelease. (updateOnlyUnreleased is on)`)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
16
action.yml
16
action.yml
@@ -47,6 +47,14 @@ inputs:
|
|||||||
description: 'Indicates if release notes should be automatically generated.'
|
description: 'Indicates if release notes should be automatically generated.'
|
||||||
required: false
|
required: false
|
||||||
default: 'false'
|
default: 'false'
|
||||||
|
generateReleaseNotesPreviousTag:
|
||||||
|
description: 'An optional previous tag to use when generating release notes. This will limit the release notes to changes between the two tags.'
|
||||||
|
required: false
|
||||||
|
default: ''
|
||||||
|
immutableCreate:
|
||||||
|
description: 'Indicates if immutable release creation should be used. When enabled, the action will first create a draft, upload artifacts, then publish the release.'
|
||||||
|
required: false
|
||||||
|
default: 'false'
|
||||||
makeLatest:
|
makeLatest:
|
||||||
description: 'Indicates if the release should be the "latest" release or not.'
|
description: 'Indicates if the release should be the "latest" release or not.'
|
||||||
required: false
|
required: false
|
||||||
@@ -122,8 +130,14 @@ outputs:
|
|||||||
description: 'The HTML URL of the release.'
|
description: 'The HTML URL of the release.'
|
||||||
upload_url:
|
upload_url:
|
||||||
description: 'The URL for uploading assets to the release.'
|
description: 'The URL for uploading assets to the release.'
|
||||||
|
tarball_url:
|
||||||
|
description: 'The URL for downloading the release as a tarball (.tar.gz).'
|
||||||
|
zipball_url:
|
||||||
|
description: 'The URL for downloading the release as a zipball (.zip).'
|
||||||
|
assets:
|
||||||
|
description: 'JSON string containing a map of asset names to download URLs for uploaded assets.'
|
||||||
runs:
|
runs:
|
||||||
using: 'node16'
|
using: 'node24'
|
||||||
main: 'dist/index.js'
|
main: 'dist/index.js'
|
||||||
branding:
|
branding:
|
||||||
icon: 'tag'
|
icon: 'tag'
|
||||||
|
|||||||
35
biome.json
Normal file
35
biome.json
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://biomejs.dev/schemas/2.3.13/schema.json",
|
||||||
|
"vcs": {
|
||||||
|
"enabled": false,
|
||||||
|
"clientKind": "git",
|
||||||
|
"useIgnoreFile": true
|
||||||
|
},
|
||||||
|
"files": {
|
||||||
|
"ignoreUnknown": false,
|
||||||
|
"includes": ["**", "!**/node_modules/**/*", "!**/dist/"]
|
||||||
|
},
|
||||||
|
"formatter": {
|
||||||
|
"enabled": true,
|
||||||
|
"formatWithErrors": false,
|
||||||
|
"indentStyle": "space",
|
||||||
|
"lineEnding": "lf",
|
||||||
|
"indentWidth": 4,
|
||||||
|
"lineWidth": 120
|
||||||
|
},
|
||||||
|
"assist": { "actions": { "source": { "organizeImports": "on" } } },
|
||||||
|
"linter": {
|
||||||
|
"enabled": true,
|
||||||
|
"rules": {
|
||||||
|
"recommended": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"javascript": {
|
||||||
|
"formatter": {
|
||||||
|
"quoteStyle": "double",
|
||||||
|
"trailingCommas": "es5",
|
||||||
|
"semicolons": "asNeeded",
|
||||||
|
"indentWidth": 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
58156
dist/index.js
vendored
58156
dist/index.js
vendored
File diff suppressed because one or more lines are too long
2
dist/index.js.map
vendored
2
dist/index.js.map
vendored
File diff suppressed because one or more lines are too long
387
dist/licenses.txt
vendored
387
dist/licenses.txt
vendored
@@ -10,6 +10,18 @@ The above copyright notice and this permission notice shall be included in all c
|
|||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
|
@actions/exec
|
||||||
|
MIT
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright 2019 GitHub
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
@actions/github
|
@actions/github
|
||||||
MIT
|
MIT
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
@@ -47,6 +59,18 @@ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|||||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
|
@actions/io
|
||||||
|
MIT
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright 2019 GitHub
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
@octokit/auth-token
|
@octokit/auth-token
|
||||||
MIT
|
MIT
|
||||||
The MIT License
|
The MIT License
|
||||||
@@ -219,41 +243,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
@vercel/ncc
|
|
||||||
MIT
|
|
||||||
Copyright 2018 ZEIT, Inc.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
balanced-match
|
|
||||||
MIT
|
|
||||||
(MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2013 Julian Gruber <julian@juliangruber.com>
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
|
|
||||||
before-after-hook
|
before-after-hook
|
||||||
Apache-2.0
|
Apache-2.0
|
||||||
Apache License
|
Apache License
|
||||||
@@ -459,11 +448,14 @@ Apache-2.0
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
|
|
||||||
|
|
||||||
brace-expansion
|
fast-content-type-parse
|
||||||
MIT
|
MIT
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2013 Julian Gruber <julian@juliangruber.com>
|
Copyright (c) 2023 The Fastify Team
|
||||||
|
|
||||||
|
The Fastify team members are listed at https://github.com/fastify/fastify#team
|
||||||
|
and in the README file.
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -483,223 +475,73 @@ 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
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
deprecation
|
|
||||||
ISC
|
|
||||||
The ISC License
|
|
||||||
|
|
||||||
Copyright (c) Gregor Martynus 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.
|
|
||||||
|
|
||||||
|
|
||||||
fs.realpath
|
|
||||||
ISC
|
|
||||||
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.
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
This library bundles a version of the `fs.realpath` and `fs.realpathSync`
|
|
||||||
methods from Node.js v0.10 under the terms of the Node.js MIT license.
|
|
||||||
|
|
||||||
Node's license follows, also included at the header of `old.js` which contains
|
|
||||||
the licensed code:
|
|
||||||
|
|
||||||
Copyright Joyent, Inc. and other Node contributors.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
|
|
||||||
glob
|
glob
|
||||||
ISC
|
BlueOak-1.0.0
|
||||||
The ISC License
|
All packages under `src/` are licensed according to the terms in
|
||||||
|
their respective `LICENSE` or `LICENSE.md` files.
|
||||||
|
|
||||||
Copyright (c) 2009-2022 Isaac Z. Schlueter and Contributors
|
The remainder of this project is licensed under the Blue Oak
|
||||||
|
Model License, as follows:
|
||||||
|
|
||||||
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
|
# Blue Oak Model License
|
||||||
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.
|
|
||||||
|
|
||||||
|
Version 1.0.0
|
||||||
|
|
||||||
inflight
|
## Purpose
|
||||||
ISC
|
|
||||||
The ISC License
|
|
||||||
|
|
||||||
Copyright (c) Isaac Z. Schlueter
|
This license gives everyone as much permission to work with
|
||||||
|
this software as possible, while protecting contributors
|
||||||
|
from liability.
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any
|
## Acceptance
|
||||||
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
|
In order to receive this license, you must agree to its
|
||||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
rules. The rules of this license are both obligations
|
||||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
under that agreement and conditions to your license.
|
||||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
You must not do anything with this software that triggers
|
||||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
a rule that you cannot or will not follow.
|
||||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
|
||||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
|
|
||||||
|
## Copyright
|
||||||
|
|
||||||
inherits
|
Each contributor licenses you to do everything with this
|
||||||
ISC
|
software that would otherwise infringe that contributor's
|
||||||
The ISC License
|
copyright in it.
|
||||||
|
|
||||||
Copyright (c) Isaac Z. Schlueter
|
## Notices
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any
|
You must ensure that everyone who gets a copy of
|
||||||
purpose with or without fee is hereby granted, provided that the above
|
any part of this software from you, with or without
|
||||||
copyright notice and this permission notice appear in all copies.
|
changes, also gets the text of this license or a link to
|
||||||
|
<https://blueoakcouncil.org/license/1.0.0>.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
## Excuse
|
||||||
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.
|
|
||||||
|
|
||||||
|
If anyone notifies you in writing that you have not
|
||||||
|
complied with [Notices](#notices), you can keep your
|
||||||
|
license by taking all practical steps to comply within 30
|
||||||
|
days after the notice. If you do not do so, your license
|
||||||
|
ends immediately.
|
||||||
|
|
||||||
|
## Patent
|
||||||
|
|
||||||
is-plain-object
|
Each contributor licenses you to do everything with this
|
||||||
MIT
|
software that would otherwise infringe any patent claims
|
||||||
The MIT License (MIT)
|
they can license or become able to license.
|
||||||
|
|
||||||
Copyright (c) 2014-2017, Jon Schlinkert.
|
## Reliability
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
No contributor can revoke this license.
|
||||||
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
|
## No Liability
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
***As far as the law allows, this software comes as is,
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
without any warranty or condition, and no contributor
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
will be liable to anyone for any damages related to this
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
software or this license, under any kind of legal claim.***
|
||||||
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.
|
|
||||||
|
|
||||||
|
|
||||||
minimatch
|
|
||||||
ISC
|
|
||||||
The ISC License
|
|
||||||
|
|
||||||
Copyright (c) 2011-2022 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.
|
|
||||||
|
|
||||||
|
|
||||||
node-fetch
|
|
||||||
MIT
|
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2016 David Frank
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
once
|
|
||||||
ISC
|
|
||||||
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.
|
|
||||||
|
|
||||||
|
|
||||||
tr46
|
|
||||||
MIT
|
|
||||||
|
|
||||||
tunnel
|
tunnel
|
||||||
MIT
|
MIT
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
@@ -725,64 +567,11 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
universal-user-agent
|
undici
|
||||||
ISC
|
|
||||||
# [ISC License](https://spdx.org/licenses/ISC)
|
|
||||||
|
|
||||||
Copyright (c) 2018, Gregor Martynus (https://github.com/gr2m)
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
|
|
||||||
untildify
|
|
||||||
MIT
|
MIT
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
|
Copyright (c) Matteo Collina and Undici contributors
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
|
|
||||||
uuid
|
|
||||||
MIT
|
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2010-2020 Robert Kieffer and other contributors
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
|
|
||||||
webidl-conversions
|
|
||||||
BSD-2-Clause
|
|
||||||
# The BSD 2-Clause License
|
|
||||||
|
|
||||||
Copyright (c) 2014, Domenic Denicola
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
2. 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 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.
|
|
||||||
|
|
||||||
|
|
||||||
whatwg-url
|
|
||||||
MIT
|
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2015–2016 Sebastian Mayr
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -791,32 +580,24 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||||||
copies of the Software, and to permit persons to whom the Software is
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
furnished to do so, subject to the following conditions:
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
The above copyright notice and this permission notice shall be included in all
|
||||||
all copies or substantial portions of the Software.
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
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
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
THE SOFTWARE.
|
SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
wrappy
|
universal-user-agent
|
||||||
ISC
|
ISC
|
||||||
The ISC License
|
# [ISC License](https://spdx.org/licenses/ISC)
|
||||||
|
|
||||||
Copyright (c) Isaac Z. Schlueter and Contributors
|
Copyright (c) 2018-2021, Gregor Martynus (https://github.com/gr2m)
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any
|
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.
|
||||||
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
|
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.
|
||||||
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.
|
|
||||||
|
|||||||
3
dist/package.json
vendored
Normal file
3
dist/package.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"type": "module"
|
||||||
|
}
|
||||||
1
dist/sourcemap-register.cjs
vendored
Normal file
1
dist/sourcemap-register.cjs
vendored
Normal file
File diff suppressed because one or more lines are too long
2
dist/sourcemap-register.js
vendored
2
dist/sourcemap-register.js
vendored
File diff suppressed because one or more lines are too long
133
lib/Action.js
133
lib/Action.js
@@ -1,133 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
||||||
if (k2 === undefined) k2 = k;
|
|
||||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
||||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
||||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
||||||
}
|
|
||||||
Object.defineProperty(o, k2, desc);
|
|
||||||
}) : (function(o, m, k, k2) {
|
|
||||||
if (k2 === undefined) k2 = k;
|
|
||||||
o[k2] = m[k];
|
|
||||||
}));
|
|
||||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
||||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
||||||
}) : function(o, v) {
|
|
||||||
o["default"] = v;
|
|
||||||
});
|
|
||||||
var __importStar = (this && this.__importStar) || function (mod) {
|
|
||||||
if (mod && mod.__esModule) return mod;
|
|
||||||
var result = {};
|
|
||||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
||||||
__setModuleDefault(result, mod);
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
||||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
||||||
return new (P || (P = Promise))(function (resolve, reject) {
|
|
||||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
||||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
||||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
||||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
||||||
});
|
|
||||||
};
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
|
||||||
exports.Action = void 0;
|
|
||||||
const core = __importStar(require("@actions/core"));
|
|
||||||
const GithubError_1 = require("./GithubError");
|
|
||||||
const ReleaseValidator_1 = require("./ReleaseValidator");
|
|
||||||
class Action {
|
|
||||||
constructor(inputs, outputs, releases, uploader, artifactDestroyer, skipper) {
|
|
||||||
this.inputs = inputs;
|
|
||||||
this.outputs = outputs;
|
|
||||||
this.releases = releases;
|
|
||||||
this.uploader = uploader;
|
|
||||||
this.artifactDestroyer = artifactDestroyer;
|
|
||||||
this.skipper = skipper;
|
|
||||||
this.releaseValidator = new ReleaseValidator_1.ReleaseValidator(inputs.updateOnlyUnreleased);
|
|
||||||
}
|
|
||||||
perform() {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
if (yield this.skipper.shouldSkip()) {
|
|
||||||
core.notice("Skipping action, release already exists and skipIfReleaseExists is enabled.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const releaseResponse = yield this.createOrUpdateRelease();
|
|
||||||
const releaseData = releaseResponse.data;
|
|
||||||
const releaseId = releaseData.id;
|
|
||||||
const uploadUrl = releaseData.upload_url;
|
|
||||||
if (this.inputs.removeArtifacts) {
|
|
||||||
yield this.artifactDestroyer.destroyArtifacts(releaseId);
|
|
||||||
}
|
|
||||||
const artifacts = this.inputs.artifacts;
|
|
||||||
if (artifacts.length > 0) {
|
|
||||||
yield this.uploader.uploadArtifacts(artifacts, releaseId, uploadUrl);
|
|
||||||
}
|
|
||||||
this.outputs.applyReleaseData(releaseData);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
createOrUpdateRelease() {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
if (this.inputs.allowUpdates) {
|
|
||||||
let getResponse;
|
|
||||||
try {
|
|
||||||
getResponse = yield this.releases.getByTag(this.inputs.tag);
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
return yield this.checkForMissingReleaseError(error);
|
|
||||||
}
|
|
||||||
// Fail if this isn't an unreleased release & updateOnlyUnreleased is enabled.
|
|
||||||
this.releaseValidator.validateReleaseUpdate(getResponse.data);
|
|
||||||
return yield this.updateRelease(getResponse.data.id);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return yield this.createRelease();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
checkForMissingReleaseError(error) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
if (Action.noPublishedRelease(error)) {
|
|
||||||
return yield this.updateDraftOrCreateRelease();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
updateRelease(id) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
return yield this.releases.update(id, this.inputs.tag, this.inputs.updatedReleaseBody, this.inputs.commit, this.inputs.discussionCategory, this.inputs.updatedDraft, this.inputs.makeLatest, this.inputs.updatedReleaseName, this.inputs.updatedPrerelease);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
static noPublishedRelease(error) {
|
|
||||||
const githubError = new GithubError_1.GithubError(error);
|
|
||||||
return githubError.status == 404;
|
|
||||||
}
|
|
||||||
updateDraftOrCreateRelease() {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
const draftReleaseId = yield this.findMatchingDraftReleaseId();
|
|
||||||
if (draftReleaseId) {
|
|
||||||
return yield this.updateRelease(draftReleaseId);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return yield this.createRelease();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
findMatchingDraftReleaseId() {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
const tag = this.inputs.tag;
|
|
||||||
const response = yield this.releases.listReleases();
|
|
||||||
const releases = response.data;
|
|
||||||
const draftRelease = releases.find(release => release.draft && release.tag_name == tag);
|
|
||||||
return draftRelease === null || draftRelease === void 0 ? void 0 : draftRelease.id;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
createRelease() {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
return yield this.releases.create(this.inputs.tag, this.inputs.createdReleaseBody, this.inputs.commit, this.inputs.discussionCategory, this.inputs.createdDraft, this.inputs.generateReleaseNotes, this.inputs.makeLatest, this.inputs.createdReleaseName, this.inputs.createdPrerelease);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exports.Action = Action;
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
||||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
||||||
return new (P || (P = Promise))(function (resolve, reject) {
|
|
||||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
||||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
||||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
||||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
||||||
});
|
|
||||||
};
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
|
||||||
exports.ReleaseActionSkipper = void 0;
|
|
||||||
class ReleaseActionSkipper {
|
|
||||||
constructor(skipIfReleaseExists, releases, tag) {
|
|
||||||
this.skipIfReleaseExists = skipIfReleaseExists;
|
|
||||||
this.releases = releases;
|
|
||||||
this.tag = tag;
|
|
||||||
}
|
|
||||||
shouldSkip() {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
if (!this.skipIfReleaseExists) {
|
|
||||||
// Bail if skip flag isn't set.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const getResponse = yield this.releases.getByTag(this.tag);
|
|
||||||
return getResponse.data != null;
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
// There is either no release or something else went wrong. Either way, run the action.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exports.ReleaseActionSkipper = ReleaseActionSkipper;
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
|
||||||
exports.Artifact = void 0;
|
|
||||||
const path_1 = require("path");
|
|
||||||
const fs_1 = require("fs");
|
|
||||||
class Artifact {
|
|
||||||
constructor(path, contentType = "raw") {
|
|
||||||
this.path = path;
|
|
||||||
this.name = (0, path_1.basename)(path);
|
|
||||||
this.contentType = contentType;
|
|
||||||
}
|
|
||||||
get contentLength() {
|
|
||||||
return (0, fs_1.statSync)(this.path).size;
|
|
||||||
}
|
|
||||||
readFile() {
|
|
||||||
return (0, fs_1.readFileSync)(this.path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exports.Artifact = Artifact;
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
||||||
if (k2 === undefined) k2 = k;
|
|
||||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
||||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
||||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
||||||
}
|
|
||||||
Object.defineProperty(o, k2, desc);
|
|
||||||
}) : (function(o, m, k, k2) {
|
|
||||||
if (k2 === undefined) k2 = k;
|
|
||||||
o[k2] = m[k];
|
|
||||||
}));
|
|
||||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
||||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
||||||
}) : function(o, v) {
|
|
||||||
o["default"] = v;
|
|
||||||
});
|
|
||||||
var __importStar = (this && this.__importStar) || function (mod) {
|
|
||||||
if (mod && mod.__esModule) return mod;
|
|
||||||
var result = {};
|
|
||||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
||||||
__setModuleDefault(result, mod);
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
||||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
||||||
return new (P || (P = Promise))(function (resolve, reject) {
|
|
||||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
||||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
||||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
||||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
||||||
});
|
|
||||||
};
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
|
||||||
exports.GithubArtifactDestroyer = void 0;
|
|
||||||
const core = __importStar(require("@actions/core"));
|
|
||||||
class GithubArtifactDestroyer {
|
|
||||||
constructor(releases) {
|
|
||||||
this.releases = releases;
|
|
||||||
}
|
|
||||||
destroyArtifacts(releaseId) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
const releaseAssets = yield this.releases.listArtifactsForRelease(releaseId);
|
|
||||||
for (const artifact of releaseAssets) {
|
|
||||||
const asset = artifact;
|
|
||||||
core.debug(`Deleting existing artifact ${artifact.name}...`);
|
|
||||||
yield this.releases.deleteArtifact(asset.id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exports.GithubArtifactDestroyer = GithubArtifactDestroyer;
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
||||||
if (k2 === undefined) k2 = k;
|
|
||||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
||||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
||||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
||||||
}
|
|
||||||
Object.defineProperty(o, k2, desc);
|
|
||||||
}) : (function(o, m, k, k2) {
|
|
||||||
if (k2 === undefined) k2 = k;
|
|
||||||
o[k2] = m[k];
|
|
||||||
}));
|
|
||||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
||||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
||||||
}) : function(o, v) {
|
|
||||||
o["default"] = v;
|
|
||||||
});
|
|
||||||
var __importStar = (this && this.__importStar) || function (mod) {
|
|
||||||
if (mod && mod.__esModule) return mod;
|
|
||||||
var result = {};
|
|
||||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
||||||
__setModuleDefault(result, mod);
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
||||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
||||||
};
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
|
||||||
exports.FileArtifactGlobber = void 0;
|
|
||||||
const core = __importStar(require("@actions/core"));
|
|
||||||
const Globber_1 = require("./Globber");
|
|
||||||
const Artifact_1 = require("./Artifact");
|
|
||||||
const untildify_1 = __importDefault(require("untildify"));
|
|
||||||
const ArtifactPathValidator_1 = require("./ArtifactPathValidator");
|
|
||||||
const PathNormalizer_1 = require("./PathNormalizer");
|
|
||||||
class FileArtifactGlobber {
|
|
||||||
constructor(globber = new Globber_1.FileGlobber()) {
|
|
||||||
this.globber = globber;
|
|
||||||
}
|
|
||||||
globArtifactString(artifact, contentType, errorsFailBuild) {
|
|
||||||
const split = /[,\n]/;
|
|
||||||
return artifact.split(split)
|
|
||||||
.map(path => path.trimStart())
|
|
||||||
.map(path => PathNormalizer_1.PathNormalizer.normalizePath(path))
|
|
||||||
.map(path => FileArtifactGlobber.expandPath(path))
|
|
||||||
.map(pattern => this.globPattern(pattern, errorsFailBuild))
|
|
||||||
.map((globResult) => FileArtifactGlobber.validatePattern(errorsFailBuild, globResult[1], globResult[0]))
|
|
||||||
.reduce((accumulated, current) => accumulated.concat(current))
|
|
||||||
.map(path => new Artifact_1.Artifact(path, contentType));
|
|
||||||
}
|
|
||||||
globPattern(pattern, errorsFailBuild) {
|
|
||||||
const paths = this.globber.glob(pattern);
|
|
||||||
if (paths.length == 0) {
|
|
||||||
if (errorsFailBuild) {
|
|
||||||
FileArtifactGlobber.throwGlobError(pattern);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
FileArtifactGlobber.reportGlobWarning(pattern);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [pattern, paths];
|
|
||||||
}
|
|
||||||
static validatePattern(errorsFailBuild, paths, pattern) {
|
|
||||||
const validator = new ArtifactPathValidator_1.ArtifactPathValidator(errorsFailBuild, paths, pattern);
|
|
||||||
return validator.validate();
|
|
||||||
}
|
|
||||||
static reportGlobWarning(pattern) {
|
|
||||||
core.warning(`Artifact pattern :${pattern} did not match any files`);
|
|
||||||
}
|
|
||||||
static throwGlobError(pattern) {
|
|
||||||
throw Error(`Artifact pattern :${pattern} did not match any files`);
|
|
||||||
}
|
|
||||||
static expandPath(path) {
|
|
||||||
return (0, untildify_1.default)(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exports.FileArtifactGlobber = FileArtifactGlobber;
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
||||||
if (k2 === undefined) k2 = k;
|
|
||||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
||||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
||||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
||||||
}
|
|
||||||
Object.defineProperty(o, k2, desc);
|
|
||||||
}) : (function(o, m, k, k2) {
|
|
||||||
if (k2 === undefined) k2 = k;
|
|
||||||
o[k2] = m[k];
|
|
||||||
}));
|
|
||||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
||||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
||||||
}) : function(o, v) {
|
|
||||||
o["default"] = v;
|
|
||||||
});
|
|
||||||
var __importStar = (this && this.__importStar) || function (mod) {
|
|
||||||
if (mod && mod.__esModule) return mod;
|
|
||||||
var result = {};
|
|
||||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
||||||
__setModuleDefault(result, mod);
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
|
||||||
exports.ArtifactPathValidator = void 0;
|
|
||||||
const core = __importStar(require("@actions/core"));
|
|
||||||
const fs_1 = require("fs");
|
|
||||||
class ArtifactPathValidator {
|
|
||||||
constructor(errorsFailBuild, paths, pattern) {
|
|
||||||
this.paths = paths;
|
|
||||||
this.pattern = pattern;
|
|
||||||
this.errorsFailBuild = errorsFailBuild;
|
|
||||||
}
|
|
||||||
validate() {
|
|
||||||
this.verifyPathsNotEmpty();
|
|
||||||
return this.paths.filter((path) => this.verifyNotDirectory(path));
|
|
||||||
}
|
|
||||||
verifyPathsNotEmpty() {
|
|
||||||
if (this.paths.length == 0) {
|
|
||||||
const message = `Artifact pattern:${this.pattern} did not match any files`;
|
|
||||||
this.reportError(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
verifyNotDirectory(path) {
|
|
||||||
const isDir = (0, fs_1.statSync)(path).isDirectory();
|
|
||||||
if (isDir) {
|
|
||||||
const message = `Artifact is a directory:${path}. Directories can not be uploaded to a release.`;
|
|
||||||
this.reportError(message);
|
|
||||||
}
|
|
||||||
return !isDir;
|
|
||||||
}
|
|
||||||
reportError(message) {
|
|
||||||
if (this.errorsFailBuild) {
|
|
||||||
throw Error(message);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
core.warning(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exports.ArtifactPathValidator = ArtifactPathValidator;
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
||||||
if (k2 === undefined) k2 = k;
|
|
||||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
||||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
||||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
||||||
}
|
|
||||||
Object.defineProperty(o, k2, desc);
|
|
||||||
}) : (function(o, m, k, k2) {
|
|
||||||
if (k2 === undefined) k2 = k;
|
|
||||||
o[k2] = m[k];
|
|
||||||
}));
|
|
||||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
||||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
||||||
}) : function(o, v) {
|
|
||||||
o["default"] = v;
|
|
||||||
});
|
|
||||||
var __importStar = (this && this.__importStar) || function (mod) {
|
|
||||||
if (mod && mod.__esModule) return mod;
|
|
||||||
var result = {};
|
|
||||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
||||||
__setModuleDefault(result, mod);
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
||||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
||||||
return new (P || (P = Promise))(function (resolve, reject) {
|
|
||||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
||||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
||||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
||||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
||||||
});
|
|
||||||
};
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
|
||||||
exports.GithubArtifactUploader = void 0;
|
|
||||||
const core = __importStar(require("@actions/core"));
|
|
||||||
class GithubArtifactUploader {
|
|
||||||
constructor(releases, replacesExistingArtifacts = true, throwsUploadErrors = false) {
|
|
||||||
this.releases = releases;
|
|
||||||
this.replacesExistingArtifacts = replacesExistingArtifacts;
|
|
||||||
this.throwsUploadErrors = throwsUploadErrors;
|
|
||||||
}
|
|
||||||
uploadArtifacts(artifacts, releaseId, uploadUrl) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
if (this.replacesExistingArtifacts) {
|
|
||||||
yield this.deleteUpdatedArtifacts(artifacts, releaseId);
|
|
||||||
}
|
|
||||||
for (const artifact of artifacts) {
|
|
||||||
yield this.uploadArtifact(artifact, releaseId, uploadUrl);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
uploadArtifact(artifact, releaseId, uploadUrl, retry = 3) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
try {
|
|
||||||
core.debug(`Uploading artifact ${artifact.name}...`);
|
|
||||||
yield this.releases.uploadArtifact(uploadUrl, artifact.contentLength, artifact.contentType, artifact.readFile(), artifact.name, releaseId);
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
if (error.status >= 500 && retry > 0) {
|
|
||||||
core.warning(`Failed to upload artifact ${artifact.name}. ${error.message}. Retrying...`);
|
|
||||||
yield this.uploadArtifact(artifact, releaseId, uploadUrl, retry - 1);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (this.throwsUploadErrors) {
|
|
||||||
throw Error(`Failed to upload artifact ${artifact.name}. ${error.message}.`);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
core.warning(`Failed to upload artifact ${artifact.name}. ${error.message}.`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
deleteUpdatedArtifacts(artifacts, releaseId) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
const releaseAssets = yield this.releases.listArtifactsForRelease(releaseId);
|
|
||||||
const assetByName = {};
|
|
||||||
releaseAssets.forEach(asset => {
|
|
||||||
assetByName[asset.name] = asset;
|
|
||||||
});
|
|
||||||
for (const artifact of artifacts) {
|
|
||||||
const asset = assetByName[artifact.name];
|
|
||||||
if (asset) {
|
|
||||||
core.debug(`Deleting existing artifact ${artifact.name}...`);
|
|
||||||
yield this.releases.deleteArtifact(asset.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exports.GithubArtifactUploader = GithubArtifactUploader;
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
|
||||||
exports.GithubError = void 0;
|
|
||||||
const GithubErrorDetail_1 = require("./GithubErrorDetail");
|
|
||||||
class GithubError {
|
|
||||||
constructor(error) {
|
|
||||||
this.error = error;
|
|
||||||
this.githubErrors = this.generateGithubErrors();
|
|
||||||
}
|
|
||||||
generateGithubErrors() {
|
|
||||||
const errors = this.error.errors;
|
|
||||||
if (errors instanceof Array) {
|
|
||||||
return errors.map((err) => new GithubErrorDetail_1.GithubErrorDetail(err));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
get status() {
|
|
||||||
return this.error.status;
|
|
||||||
}
|
|
||||||
hasErrorWithCode(code) {
|
|
||||||
return this.githubErrors.some((err) => err.code == code);
|
|
||||||
}
|
|
||||||
toString() {
|
|
||||||
const message = this.error.message;
|
|
||||||
const errors = this.githubErrors;
|
|
||||||
const status = this.status;
|
|
||||||
if (errors.length > 0) {
|
|
||||||
return `Error ${status}: ${message}\nErrors:\n${this.errorBulletedList(errors)}`;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return `Error ${status}: ${message}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
errorBulletedList(errors) {
|
|
||||||
return errors.map((err) => `- ${err}`).join("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exports.GithubError = GithubError;
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
|
||||||
exports.GithubErrorDetail = void 0;
|
|
||||||
class GithubErrorDetail {
|
|
||||||
constructor(error) {
|
|
||||||
this.error = error;
|
|
||||||
}
|
|
||||||
get code() {
|
|
||||||
return this.error.code;
|
|
||||||
}
|
|
||||||
toString() {
|
|
||||||
const code = this.error.code;
|
|
||||||
switch (code) {
|
|
||||||
case 'missing':
|
|
||||||
return this.missingResourceMessage();
|
|
||||||
case 'missing_field':
|
|
||||||
return this.missingFieldMessage();
|
|
||||||
case 'invalid':
|
|
||||||
return this.invalidFieldMessage();
|
|
||||||
case 'already_exists':
|
|
||||||
return this.resourceAlreadyExists();
|
|
||||||
default:
|
|
||||||
return this.customErrorMessage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
customErrorMessage() {
|
|
||||||
const message = this.error.message;
|
|
||||||
const documentation = this.error.documentation_url;
|
|
||||||
let documentationMessage;
|
|
||||||
if (documentation) {
|
|
||||||
documentationMessage = `\nPlease see ${documentation}.`;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
documentationMessage = "";
|
|
||||||
}
|
|
||||||
return `${message}${documentationMessage}`;
|
|
||||||
}
|
|
||||||
invalidFieldMessage() {
|
|
||||||
const resource = this.error.resource;
|
|
||||||
const field = this.error.field;
|
|
||||||
return `The ${field} field on ${resource} is an invalid format.`;
|
|
||||||
}
|
|
||||||
missingResourceMessage() {
|
|
||||||
const resource = this.error.resource;
|
|
||||||
return `${resource} does not exist.`;
|
|
||||||
}
|
|
||||||
missingFieldMessage() {
|
|
||||||
const resource = this.error.resource;
|
|
||||||
const field = this.error.field;
|
|
||||||
return `The ${field} field on ${resource} is missing.`;
|
|
||||||
}
|
|
||||||
resourceAlreadyExists() {
|
|
||||||
const resource = this.error.resource;
|
|
||||||
return `${resource} already exists.`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exports.GithubErrorDetail = GithubErrorDetail;
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
|
||||||
exports.FileGlobber = void 0;
|
|
||||||
const glob_1 = require("glob");
|
|
||||||
class FileGlobber {
|
|
||||||
glob(pattern) {
|
|
||||||
return new glob_1.GlobSync(pattern, { mark: true }).found;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exports.FileGlobber = FileGlobber;
|
|
||||||
199
lib/Inputs.js
199
lib/Inputs.js
@@ -1,199 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
||||||
if (k2 === undefined) k2 = k;
|
|
||||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
||||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
||||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
||||||
}
|
|
||||||
Object.defineProperty(o, k2, desc);
|
|
||||||
}) : (function(o, m, k, k2) {
|
|
||||||
if (k2 === undefined) k2 = k;
|
|
||||||
o[k2] = m[k];
|
|
||||||
}));
|
|
||||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
||||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
||||||
}) : function(o, v) {
|
|
||||||
o["default"] = v;
|
|
||||||
});
|
|
||||||
var __importStar = (this && this.__importStar) || function (mod) {
|
|
||||||
if (mod && mod.__esModule) return mod;
|
|
||||||
var result = {};
|
|
||||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
||||||
__setModuleDefault(result, mod);
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
|
||||||
exports.CoreInputs = void 0;
|
|
||||||
const core = __importStar(require("@actions/core"));
|
|
||||||
const fs_1 = require("fs");
|
|
||||||
class CoreInputs {
|
|
||||||
constructor(artifactGlobber, context) {
|
|
||||||
this.artifactGlobber = artifactGlobber;
|
|
||||||
this.context = context;
|
|
||||||
}
|
|
||||||
get allowUpdates() {
|
|
||||||
const allow = core.getInput('allowUpdates');
|
|
||||||
return allow == 'true';
|
|
||||||
}
|
|
||||||
get artifacts() {
|
|
||||||
let artifacts = core.getInput('artifacts');
|
|
||||||
if (!artifacts) {
|
|
||||||
artifacts = core.getInput('artifact');
|
|
||||||
}
|
|
||||||
if (artifacts) {
|
|
||||||
let contentType = core.getInput('artifactContentType');
|
|
||||||
if (!contentType) {
|
|
||||||
contentType = 'raw';
|
|
||||||
}
|
|
||||||
return this.artifactGlobber
|
|
||||||
.globArtifactString(artifacts, contentType, this.artifactErrorsFailBuild);
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
get artifactErrorsFailBuild() {
|
|
||||||
const allow = core.getInput('artifactErrorsFailBuild');
|
|
||||||
return allow == 'true';
|
|
||||||
}
|
|
||||||
get body() {
|
|
||||||
const body = core.getInput('body');
|
|
||||||
if (body) {
|
|
||||||
return body;
|
|
||||||
}
|
|
||||||
const bodyFile = core.getInput('bodyFile');
|
|
||||||
if (bodyFile) {
|
|
||||||
return this.stringFromFile(bodyFile);
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
get createdDraft() {
|
|
||||||
const draft = core.getInput('draft');
|
|
||||||
return draft == 'true';
|
|
||||||
}
|
|
||||||
get createdPrerelease() {
|
|
||||||
const preRelease = core.getInput('prerelease');
|
|
||||||
return preRelease == 'true';
|
|
||||||
}
|
|
||||||
get createdReleaseBody() {
|
|
||||||
if (CoreInputs.omitBody)
|
|
||||||
return undefined;
|
|
||||||
return this.body;
|
|
||||||
}
|
|
||||||
static get omitBody() {
|
|
||||||
return core.getInput('omitBody') == 'true';
|
|
||||||
}
|
|
||||||
get createdReleaseName() {
|
|
||||||
if (CoreInputs.omitName)
|
|
||||||
return undefined;
|
|
||||||
return this.name;
|
|
||||||
}
|
|
||||||
static get omitName() {
|
|
||||||
return core.getInput('omitName') == 'true';
|
|
||||||
}
|
|
||||||
get commit() {
|
|
||||||
const commit = core.getInput('commit');
|
|
||||||
if (commit) {
|
|
||||||
return commit;
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
get discussionCategory() {
|
|
||||||
const category = core.getInput('discussionCategory');
|
|
||||||
if (category) {
|
|
||||||
return category;
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
get name() {
|
|
||||||
const name = core.getInput('name');
|
|
||||||
if (name) {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
return this.tag;
|
|
||||||
}
|
|
||||||
get generateReleaseNotes() {
|
|
||||||
const generate = core.getInput('generateReleaseNotes');
|
|
||||||
return generate == 'true';
|
|
||||||
}
|
|
||||||
get makeLatest() {
|
|
||||||
return core.getInput('makeLatest');
|
|
||||||
}
|
|
||||||
get owner() {
|
|
||||||
let owner = core.getInput('owner');
|
|
||||||
if (owner) {
|
|
||||||
return owner;
|
|
||||||
}
|
|
||||||
return this.context.repo.owner;
|
|
||||||
}
|
|
||||||
get removeArtifacts() {
|
|
||||||
const removes = core.getInput('removeArtifacts');
|
|
||||||
return removes == 'true';
|
|
||||||
}
|
|
||||||
get replacesArtifacts() {
|
|
||||||
const replaces = core.getInput('replacesArtifacts');
|
|
||||||
return replaces == 'true';
|
|
||||||
}
|
|
||||||
get repo() {
|
|
||||||
let repo = core.getInput('repo');
|
|
||||||
if (repo) {
|
|
||||||
return repo;
|
|
||||||
}
|
|
||||||
return this.context.repo.repo;
|
|
||||||
}
|
|
||||||
get skipIfReleaseExists() {
|
|
||||||
return core.getBooleanInput("skipIfReleaseExists");
|
|
||||||
}
|
|
||||||
get tag() {
|
|
||||||
const tag = core.getInput('tag');
|
|
||||||
if (tag) {
|
|
||||||
return tag;
|
|
||||||
}
|
|
||||||
const ref = this.context.ref;
|
|
||||||
const tagPath = "refs/tags/";
|
|
||||||
if (ref && ref.startsWith(tagPath)) {
|
|
||||||
return ref.substr(tagPath.length, ref.length);
|
|
||||||
}
|
|
||||||
throw Error("No tag found in ref or input!");
|
|
||||||
}
|
|
||||||
get token() {
|
|
||||||
return core.getInput('token', { required: true });
|
|
||||||
}
|
|
||||||
get updatedDraft() {
|
|
||||||
if (CoreInputs.omitDraftDuringUpdate)
|
|
||||||
return undefined;
|
|
||||||
return this.createdDraft;
|
|
||||||
}
|
|
||||||
static get omitDraftDuringUpdate() {
|
|
||||||
return core.getInput('omitDraftDuringUpdate') == 'true';
|
|
||||||
}
|
|
||||||
get updatedPrerelease() {
|
|
||||||
if (CoreInputs.omitPrereleaseDuringUpdate)
|
|
||||||
return undefined;
|
|
||||||
return this.createdPrerelease;
|
|
||||||
}
|
|
||||||
static get omitPrereleaseDuringUpdate() {
|
|
||||||
return core.getInput('omitPrereleaseDuringUpdate') == 'true';
|
|
||||||
}
|
|
||||||
get updatedReleaseBody() {
|
|
||||||
if (CoreInputs.omitBody || CoreInputs.omitBodyDuringUpdate)
|
|
||||||
return undefined;
|
|
||||||
return this.body;
|
|
||||||
}
|
|
||||||
static get omitBodyDuringUpdate() {
|
|
||||||
return core.getInput('omitBodyDuringUpdate') == 'true';
|
|
||||||
}
|
|
||||||
get updatedReleaseName() {
|
|
||||||
if (CoreInputs.omitName || CoreInputs.omitNameDuringUpdate)
|
|
||||||
return undefined;
|
|
||||||
return this.name;
|
|
||||||
}
|
|
||||||
get updateOnlyUnreleased() {
|
|
||||||
return core.getInput('updateOnlyUnreleased') == 'true';
|
|
||||||
}
|
|
||||||
static get omitNameDuringUpdate() {
|
|
||||||
return core.getInput('omitNameDuringUpdate') == 'true';
|
|
||||||
}
|
|
||||||
stringFromFile(path) {
|
|
||||||
return (0, fs_1.readFileSync)(path, 'utf-8');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exports.CoreInputs = CoreInputs;
|
|
||||||
71
lib/Main.js
71
lib/Main.js
@@ -1,71 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
||||||
if (k2 === undefined) k2 = k;
|
|
||||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
||||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
||||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
||||||
}
|
|
||||||
Object.defineProperty(o, k2, desc);
|
|
||||||
}) : (function(o, m, k, k2) {
|
|
||||||
if (k2 === undefined) k2 = k;
|
|
||||||
o[k2] = m[k];
|
|
||||||
}));
|
|
||||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
||||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
||||||
}) : function(o, v) {
|
|
||||||
o["default"] = v;
|
|
||||||
});
|
|
||||||
var __importStar = (this && this.__importStar) || function (mod) {
|
|
||||||
if (mod && mod.__esModule) return mod;
|
|
||||||
var result = {};
|
|
||||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
||||||
__setModuleDefault(result, mod);
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
||||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
||||||
return new (P || (P = Promise))(function (resolve, reject) {
|
|
||||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
||||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
||||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
||||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
||||||
});
|
|
||||||
};
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
|
||||||
const github = __importStar(require("@actions/github"));
|
|
||||||
const core = __importStar(require("@actions/core"));
|
|
||||||
const Inputs_1 = require("./Inputs");
|
|
||||||
const Releases_1 = require("./Releases");
|
|
||||||
const Action_1 = require("./Action");
|
|
||||||
const ArtifactUploader_1 = require("./ArtifactUploader");
|
|
||||||
const ArtifactGlobber_1 = require("./ArtifactGlobber");
|
|
||||||
const GithubError_1 = require("./GithubError");
|
|
||||||
const Outputs_1 = require("./Outputs");
|
|
||||||
const ArtifactDestroyer_1 = require("./ArtifactDestroyer");
|
|
||||||
const ActionSkipper_1 = require("./ActionSkipper");
|
|
||||||
function run() {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
try {
|
|
||||||
const action = createAction();
|
|
||||||
yield action.perform();
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
const githubError = new GithubError_1.GithubError(error);
|
|
||||||
core.setFailed(githubError.toString());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
function createAction() {
|
|
||||||
const token = core.getInput('token');
|
|
||||||
const context = github.context;
|
|
||||||
const git = github.getOctokit(token);
|
|
||||||
const globber = new ArtifactGlobber_1.FileArtifactGlobber();
|
|
||||||
const inputs = new Inputs_1.CoreInputs(globber, context);
|
|
||||||
const outputs = new Outputs_1.CoreOutputs();
|
|
||||||
const releases = new Releases_1.GithubReleases(inputs, git);
|
|
||||||
const skipper = new ActionSkipper_1.ReleaseActionSkipper(inputs.skipIfReleaseExists, releases, inputs.tag);
|
|
||||||
const uploader = new ArtifactUploader_1.GithubArtifactUploader(releases, inputs.replacesArtifacts, inputs.artifactErrorsFailBuild);
|
|
||||||
const artifactDestroyer = new ArtifactDestroyer_1.GithubArtifactDestroyer(releases);
|
|
||||||
return new Action_1.Action(inputs, outputs, releases, uploader, artifactDestroyer, skipper);
|
|
||||||
}
|
|
||||||
run();
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
||||||
if (k2 === undefined) k2 = k;
|
|
||||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
||||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
||||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
||||||
}
|
|
||||||
Object.defineProperty(o, k2, desc);
|
|
||||||
}) : (function(o, m, k, k2) {
|
|
||||||
if (k2 === undefined) k2 = k;
|
|
||||||
o[k2] = m[k];
|
|
||||||
}));
|
|
||||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
||||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
||||||
}) : function(o, v) {
|
|
||||||
o["default"] = v;
|
|
||||||
});
|
|
||||||
var __importStar = (this && this.__importStar) || function (mod) {
|
|
||||||
if (mod && mod.__esModule) return mod;
|
|
||||||
var result = {};
|
|
||||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
||||||
__setModuleDefault(result, mod);
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
|
||||||
exports.CoreOutputs = void 0;
|
|
||||||
const core = __importStar(require("@actions/core"));
|
|
||||||
class CoreOutputs {
|
|
||||||
applyReleaseData(releaseData) {
|
|
||||||
core.setOutput('id', releaseData.id);
|
|
||||||
core.setOutput('html_url', releaseData.html_url);
|
|
||||||
core.setOutput('upload_url', releaseData.upload_url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exports.CoreOutputs = CoreOutputs;
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
||||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
||||||
};
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
|
||||||
exports.PathNormalizer = void 0;
|
|
||||||
const path_1 = __importDefault(require("path"));
|
|
||||||
class PathNormalizer {
|
|
||||||
static normalizePath(pathString) {
|
|
||||||
return pathString.split(path_1.default.sep).join("/");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exports.PathNormalizer = PathNormalizer;
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
|
||||||
exports.ReleaseValidator = void 0;
|
|
||||||
class ReleaseValidator {
|
|
||||||
constructor(updateOnlyUnreleased) {
|
|
||||||
this.updateOnlyUnreleased = updateOnlyUnreleased;
|
|
||||||
}
|
|
||||||
validateReleaseUpdate(releaseResponse) {
|
|
||||||
var _a;
|
|
||||||
if (!this.updateOnlyUnreleased) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!releaseResponse.draft && !releaseResponse.prerelease) {
|
|
||||||
throw new Error(`Tried to update "${(_a = releaseResponse.name) !== null && _a !== void 0 ? _a : "release"}" which is neither a draft or prerelease. (updateOnlyUnreleased is on)`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exports.ReleaseValidator = ReleaseValidator;
|
|
||||||
106
lib/Releases.js
106
lib/Releases.js
@@ -1,106 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
||||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
||||||
return new (P || (P = Promise))(function (resolve, reject) {
|
|
||||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
||||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
||||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
||||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
||||||
});
|
|
||||||
};
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
|
||||||
exports.GithubReleases = void 0;
|
|
||||||
class GithubReleases {
|
|
||||||
constructor(inputs, git) {
|
|
||||||
this.inputs = inputs;
|
|
||||||
this.git = git;
|
|
||||||
}
|
|
||||||
create(tag, body, commitHash, discussionCategory, draft, generateReleaseNotes, makeLatest, name, prerelease) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
// noinspection TypeScriptValidateJSTypes
|
|
||||||
return this.git.rest.repos.createRelease({
|
|
||||||
body: body,
|
|
||||||
name: name,
|
|
||||||
discussion_category_name: discussionCategory,
|
|
||||||
draft: draft,
|
|
||||||
generate_release_notes: generateReleaseNotes,
|
|
||||||
make_latest: makeLatest,
|
|
||||||
owner: this.inputs.owner,
|
|
||||||
prerelease: prerelease,
|
|
||||||
repo: this.inputs.repo,
|
|
||||||
target_commitish: commitHash,
|
|
||||||
tag_name: tag
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
deleteArtifact(assetId) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
return this.git.rest.repos.deleteReleaseAsset({
|
|
||||||
asset_id: assetId,
|
|
||||||
owner: this.inputs.owner,
|
|
||||||
repo: this.inputs.repo
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
getByTag(tag) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
return this.git.rest.repos.getReleaseByTag({
|
|
||||||
owner: this.inputs.owner,
|
|
||||||
repo: this.inputs.repo,
|
|
||||||
tag: tag
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
listArtifactsForRelease(releaseId) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
return this.git.paginate(this.git.rest.repos.listReleaseAssets, {
|
|
||||||
owner: this.inputs.owner,
|
|
||||||
release_id: releaseId,
|
|
||||||
repo: this.inputs.repo
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
listReleases() {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
return this.git.rest.repos.listReleases({
|
|
||||||
owner: this.inputs.owner,
|
|
||||||
repo: this.inputs.repo
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
update(id, tag, body, commitHash, discussionCategory, draft, makeLatest, name, prerelease) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
// noinspection TypeScriptValidateJSTypes
|
|
||||||
return this.git.rest.repos.updateRelease({
|
|
||||||
release_id: id,
|
|
||||||
body: body,
|
|
||||||
name: name,
|
|
||||||
discussion_category_name: discussionCategory,
|
|
||||||
draft: draft,
|
|
||||||
make_latest: makeLatest,
|
|
||||||
owner: this.inputs.owner,
|
|
||||||
prerelease: prerelease,
|
|
||||||
repo: this.inputs.repo,
|
|
||||||
target_commitish: commitHash,
|
|
||||||
tag_name: tag
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
uploadArtifact(assetUrl, contentLength, contentType, file, name, releaseId) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
return this.git.rest.repos.uploadReleaseAsset({
|
|
||||||
url: assetUrl,
|
|
||||||
headers: {
|
|
||||||
"content-length": contentLength,
|
|
||||||
"content-type": contentType
|
|
||||||
},
|
|
||||||
data: file,
|
|
||||||
name: name,
|
|
||||||
owner: this.inputs.owner,
|
|
||||||
release_id: releaseId,
|
|
||||||
repo: this.inputs.repo
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exports.GithubReleases = GithubReleases;
|
|
||||||
116
package.json
116
package.json
@@ -1,71 +1,53 @@
|
|||||||
{
|
{
|
||||||
"name": "release-action",
|
"name": "release-action",
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"private": true,
|
"type": "module",
|
||||||
"description": "An action which manages a github release",
|
"private": true,
|
||||||
"main": "lib/main.js",
|
"description": "An action which manages a github release",
|
||||||
"scripts": {
|
"main": "lib/src/Main.js",
|
||||||
"build": "tsc",
|
"scripts": {
|
||||||
"clean": "rm -rf lib/*",
|
"build": "tsc",
|
||||||
"coverage": "jest --coverage",
|
"clean": "rm -rf lib/*",
|
||||||
"debug": "yarn clean && yarn install && yarn build && yarn package",
|
"coverage": "vitest run --coverage",
|
||||||
"package": "ncc build --source-map --license licenses.txt",
|
"debug": "pnpm clean && pnpm install && pnpm build && pnpm package",
|
||||||
"release": "yarn clean && yarn install --production && yarn build && yarn package",
|
"format": "pnpm biome format --write .",
|
||||||
"test": "jest"
|
"package": "ncc build --source-map --license licenses.txt",
|
||||||
},
|
"release": "pnpm clean && pnpm install && pnpm build && pnpm package",
|
||||||
"repository": {
|
"test": "vitest run"
|
||||||
"type": "git",
|
|
||||||
"url": "git+https://github.com/ncipollo/release-action.git"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"actions",
|
|
||||||
"node",
|
|
||||||
"setup"
|
|
||||||
],
|
|
||||||
"author": "GitHub",
|
|
||||||
"license": "MIT",
|
|
||||||
"jest": {
|
|
||||||
"clearMocks": true,
|
|
||||||
"collectCoverage": true,
|
|
||||||
"coveragePathIgnorePatterns": [
|
|
||||||
"src/Globber.ts",
|
|
||||||
"src/Releases.ts"
|
|
||||||
],
|
|
||||||
"coverageThreshold": {
|
|
||||||
"global": {
|
|
||||||
"branches": 95,
|
|
||||||
"functions": 100,
|
|
||||||
"lines": 100,
|
|
||||||
"statements": 100
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"moduleFileExtensions": [
|
"repository": {
|
||||||
"js",
|
"type": "git",
|
||||||
"ts"
|
"url": "git+https://github.com/ncipollo/release-action.git"
|
||||||
],
|
|
||||||
"testEnvironment": "node",
|
|
||||||
"testMatch": [
|
|
||||||
"**/*.test.ts"
|
|
||||||
],
|
|
||||||
"testRunner": "jest-circus/runner",
|
|
||||||
"transform": {
|
|
||||||
"^.+\\.ts$": "ts-jest"
|
|
||||||
},
|
},
|
||||||
"verbose": true
|
"keywords": [
|
||||||
},
|
"actions",
|
||||||
"dependencies": {
|
"node",
|
||||||
"@actions/core": "^1.10.0",
|
"setup"
|
||||||
"@actions/github": "^5.1.1",
|
],
|
||||||
"@types/glob": "^8.0.0",
|
"author": "GitHub",
|
||||||
"glob": "^8.0.3",
|
"license": "MIT",
|
||||||
"untildify": "^4.0.0"
|
"engines": {
|
||||||
},
|
"node": ">=20"
|
||||||
"devDependencies": {
|
},
|
||||||
"@types/jest": "^28.1.1",
|
"dependencies": {
|
||||||
"@types/node": "^18.11.10",
|
"@actions/core": "^3.0.0",
|
||||||
"jest": "^28.1.1",
|
"@actions/github": "^9.0.0",
|
||||||
"jest-circus": "^29.3.1",
|
"@types/node": "^25.1.0",
|
||||||
"ts-jest": "^28.0.5",
|
"glob": "^13.0.6"
|
||||||
"typescript": "^4.9.3"
|
},
|
||||||
}
|
"pnpm": {
|
||||||
|
"overrides": {
|
||||||
|
"undici": ">=6.24.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@biomejs/biome": "2.4.4",
|
||||||
|
"@octokit/plugin-rest-endpoint-methods": "^10.4.1",
|
||||||
|
"@octokit/request-error": "^7.1.0",
|
||||||
|
"@octokit/types": "^16.0.0",
|
||||||
|
"@vercel/ncc": "^0.38.4",
|
||||||
|
"@vitest/coverage-v8": "^4.1.0",
|
||||||
|
"typescript": "^5.9.3",
|
||||||
|
"vitest": "^4.1.0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1441
pnpm-lock.yaml
generated
Normal file
1441
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
6
sheepit.toml
Normal file
6
sheepit.toml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[repository]
|
||||||
|
enable_commit = true
|
||||||
|
tag_pattern = "v{version}"
|
||||||
|
|
||||||
|
[scripts]
|
||||||
|
before_commit = "pnpm release"
|
||||||
199
src/Action.ts
199
src/Action.ts
@@ -1,18 +1,18 @@
|
|||||||
import * as core from '@actions/core';
|
import * as core from "@actions/core"
|
||||||
import {Inputs} from "./Inputs";
|
import type { ActionSkipper } from "./ActionSkipper.js"
|
||||||
import {
|
import type { ArtifactDestroyer } from "./ArtifactDestroyer.js"
|
||||||
|
import type { ArtifactUploader } from "./ArtifactUploader.js"
|
||||||
|
import { GithubError } from "./GithubError.js"
|
||||||
|
import type { Inputs } from "./Inputs.js"
|
||||||
|
import type { Outputs } from "./Outputs.js"
|
||||||
|
import { ReleaseValidator } from "./ReleaseValidator.js"
|
||||||
|
import type {
|
||||||
CreateOrUpdateReleaseResponse,
|
CreateOrUpdateReleaseResponse,
|
||||||
CreateReleaseResponse,
|
CreateReleaseResponse,
|
||||||
ReleaseByTagResponse,
|
ReleaseByTagResponse,
|
||||||
Releases,
|
Releases,
|
||||||
UpdateReleaseResponse
|
UpdateReleaseResponse,
|
||||||
} from "./Releases";
|
} from "./Releases.js"
|
||||||
import {ArtifactUploader} from "./ArtifactUploader";
|
|
||||||
import {GithubError} from "./GithubError";
|
|
||||||
import {Outputs} from "./Outputs";
|
|
||||||
import {ArtifactDestroyer} from "./ArtifactDestroyer";
|
|
||||||
import {ReleaseValidator} from "./ReleaseValidator";
|
|
||||||
import {ActionSkipper} from "./ActionSkipper";
|
|
||||||
|
|
||||||
export class Action {
|
export class Action {
|
||||||
private inputs: Inputs
|
private inputs: Inputs
|
||||||
@@ -21,15 +21,17 @@ export class Action {
|
|||||||
private uploader: ArtifactUploader
|
private uploader: ArtifactUploader
|
||||||
private artifactDestroyer: ArtifactDestroyer
|
private artifactDestroyer: ArtifactDestroyer
|
||||||
private skipper: ActionSkipper
|
private skipper: ActionSkipper
|
||||||
|
|
||||||
private releaseValidator: ReleaseValidator
|
private releaseValidator: ReleaseValidator
|
||||||
|
|
||||||
constructor(inputs: Inputs,
|
constructor(
|
||||||
outputs: Outputs,
|
inputs: Inputs,
|
||||||
releases: Releases,
|
outputs: Outputs,
|
||||||
uploader: ArtifactUploader,
|
releases: Releases,
|
||||||
artifactDestroyer: ArtifactDestroyer,
|
uploader: ArtifactUploader,
|
||||||
skipper: ActionSkipper) {
|
artifactDestroyer: ArtifactDestroyer,
|
||||||
|
skipper: ActionSkipper
|
||||||
|
) {
|
||||||
this.inputs = inputs
|
this.inputs = inputs
|
||||||
this.outputs = outputs
|
this.outputs = outputs
|
||||||
this.releases = releases
|
this.releases = releases
|
||||||
@@ -44,75 +46,48 @@ export class Action {
|
|||||||
core.notice("Skipping action, release already exists and skipIfReleaseExists is enabled.")
|
core.notice("Skipping action, release already exists and skipIfReleaseExists is enabled.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const releaseResponse = await this.createOrUpdateRelease();
|
|
||||||
const releaseData = releaseResponse.data
|
|
||||||
const releaseId = releaseData.id
|
|
||||||
const uploadUrl = releaseData.upload_url
|
|
||||||
|
|
||||||
if (this.inputs.removeArtifacts) {
|
|
||||||
await this.artifactDestroyer.destroyArtifacts(releaseId)
|
|
||||||
}
|
|
||||||
|
|
||||||
const artifacts = this.inputs.artifacts
|
|
||||||
if (artifacts.length > 0) {
|
|
||||||
await this.uploader.uploadArtifacts(artifacts, releaseId, uploadUrl)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.outputs.applyReleaseData(releaseData)
|
await this.createOrUpdateRelease()
|
||||||
}
|
}
|
||||||
|
|
||||||
private async createOrUpdateRelease(): Promise<CreateOrUpdateReleaseResponse> {
|
private async createOrUpdateRelease() {
|
||||||
if (this.inputs.allowUpdates) {
|
if (this.inputs.allowUpdates) {
|
||||||
let getResponse: ReleaseByTagResponse
|
let getResponse: ReleaseByTagResponse
|
||||||
try {
|
try {
|
||||||
getResponse = await this.releases.getByTag(this.inputs.tag)
|
getResponse = await this.releases.getByTag(this.inputs.tag)
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
return await this.checkForMissingReleaseError(error)
|
await this.checkForMissingReleaseError(error)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fail if this isn't an unreleased release & updateOnlyUnreleased is enabled.
|
// Fail if this isn't an unreleased release & updateOnlyUnreleased is enabled.
|
||||||
this.releaseValidator.validateReleaseUpdate(getResponse.data)
|
this.releaseValidator.validateReleaseUpdate(getResponse.data)
|
||||||
|
|
||||||
return await this.updateRelease(getResponse.data.id)
|
await this.updateRelease(getResponse.data.id)
|
||||||
} else {
|
} else {
|
||||||
return await this.createRelease()
|
await this.createRelease()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async checkForMissingReleaseError(error: Error): Promise<CreateOrUpdateReleaseResponse> {
|
private async checkForMissingReleaseError(error: Error): Promise<void> {
|
||||||
if (Action.noPublishedRelease(error)) {
|
if (Action.noPublishedRelease(error)) {
|
||||||
return await this.updateDraftOrCreateRelease()
|
await this.updateDraftOrCreateRelease()
|
||||||
} else {
|
} else {
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async updateRelease(id: number): Promise<UpdateReleaseResponse> {
|
|
||||||
return await this.releases.update(
|
|
||||||
id,
|
|
||||||
this.inputs.tag,
|
|
||||||
this.inputs.updatedReleaseBody,
|
|
||||||
this.inputs.commit,
|
|
||||||
this.inputs.discussionCategory,
|
|
||||||
this.inputs.updatedDraft,
|
|
||||||
this.inputs.makeLatest,
|
|
||||||
this.inputs.updatedReleaseName,
|
|
||||||
this.inputs.updatedPrerelease
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private static noPublishedRelease(error: any): boolean {
|
private static noPublishedRelease(error: any): boolean {
|
||||||
const githubError = new GithubError(error)
|
const githubError = new GithubError(error)
|
||||||
return githubError.status == 404
|
return githubError.status == 404
|
||||||
}
|
}
|
||||||
|
|
||||||
private async updateDraftOrCreateRelease(): Promise<CreateReleaseResponse | UpdateReleaseResponse> {
|
private async updateDraftOrCreateRelease(): Promise<void> {
|
||||||
const draftReleaseId = await this.findMatchingDraftReleaseId()
|
const draftReleaseId = await this.findMatchingDraftReleaseId()
|
||||||
if (draftReleaseId) {
|
if (draftReleaseId) {
|
||||||
return await this.updateRelease(draftReleaseId)
|
await this.updateRelease(draftReleaseId)
|
||||||
} else {
|
} else {
|
||||||
return await this.createRelease()
|
await this.createRelease()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,22 +95,120 @@ export class Action {
|
|||||||
const tag = this.inputs.tag
|
const tag = this.inputs.tag
|
||||||
const response = await this.releases.listReleases()
|
const response = await this.releases.listReleases()
|
||||||
const releases = response.data
|
const releases = response.data
|
||||||
const draftRelease = releases.find(release => release.draft && release.tag_name == tag)
|
if (!releases) {
|
||||||
|
throw new Error(`No releases found. Response: ${JSON.stringify(response)}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const draftRelease = releases.find((release) => release.draft && release.tag_name == tag)
|
||||||
|
|
||||||
return draftRelease?.id
|
return draftRelease?.id
|
||||||
}
|
}
|
||||||
|
|
||||||
private async createRelease(): Promise<CreateReleaseResponse> {
|
private async updateRelease(id: number) {
|
||||||
return await this.releases.create(
|
const releaseBody = await this.combineBodyWithReleaseNotes(this.inputs.updatedReleaseBody, true)
|
||||||
|
|
||||||
|
const releaseResponse = await this.releases.update(
|
||||||
|
id,
|
||||||
this.inputs.tag,
|
this.inputs.tag,
|
||||||
this.inputs.createdReleaseBody,
|
releaseBody,
|
||||||
this.inputs.commit,
|
this.inputs.commit,
|
||||||
this.inputs.discussionCategory,
|
this.inputs.discussionCategory,
|
||||||
this.inputs.createdDraft,
|
this.inputs.updatedDraft,
|
||||||
this.inputs.generateReleaseNotes,
|
this.inputs.makeLatest,
|
||||||
|
this.inputs.updatedReleaseName,
|
||||||
|
this.inputs.updatedPrerelease
|
||||||
|
)
|
||||||
|
|
||||||
|
await this.processReleaseArtifactsAndOutputs(releaseResponse, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
private async createRelease() {
|
||||||
|
const releaseBody = await this.combineBodyWithReleaseNotes(this.inputs.createdReleaseBody, false)
|
||||||
|
|
||||||
|
// If immutableCreate is enabled we need to start with a draft release
|
||||||
|
const draft = this.inputs.createdDraft || this.inputs.immutableCreate
|
||||||
|
|
||||||
|
const releaseResponse = await this.releases.create(
|
||||||
|
this.inputs.tag,
|
||||||
|
releaseBody,
|
||||||
|
this.inputs.commit,
|
||||||
|
this.inputs.discussionCategory,
|
||||||
|
draft,
|
||||||
|
this.inputs.makeLatest,
|
||||||
|
this.inputs.createdReleaseName,
|
||||||
|
this.inputs.createdPrerelease
|
||||||
|
)
|
||||||
|
|
||||||
|
await this.processReleaseArtifactsAndOutputs(releaseResponse, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private async processReleaseArtifactsAndOutputs(releaseResponse: CreateOrUpdateReleaseResponse, wasCreated: boolean) {
|
||||||
|
const releaseData = releaseResponse.data
|
||||||
|
const releaseId = releaseData.id
|
||||||
|
const uploadUrl = releaseData.upload_url
|
||||||
|
|
||||||
|
if (this.inputs.removeArtifacts) {
|
||||||
|
await this.artifactDestroyer.destroyArtifacts(releaseId)
|
||||||
|
}
|
||||||
|
|
||||||
|
const artifacts = this.inputs.artifacts
|
||||||
|
let assetUrls: Record<string, string> = {}
|
||||||
|
if (artifacts.length > 0) {
|
||||||
|
assetUrls = await this.uploader.uploadArtifacts(artifacts, releaseId, uploadUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wasCreated) {
|
||||||
|
const immutableRelease = await this.publishImmutableRelease(releaseId)
|
||||||
|
if (immutableRelease) {
|
||||||
|
this.setOutputs(immutableRelease.data, assetUrls)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setOutputs(releaseData, assetUrls)
|
||||||
|
}
|
||||||
|
|
||||||
|
private async publishImmutableRelease(releaseId: number): Promise<CreateOrUpdateReleaseResponse | undefined> {
|
||||||
|
// Check if immutableCreate is on and createdDraft is off
|
||||||
|
if (!this.inputs.immutableCreate || this.inputs.createdDraft) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.releases.update(
|
||||||
|
releaseId,
|
||||||
|
this.inputs.tag,
|
||||||
|
undefined, // body is omitted
|
||||||
|
undefined, // commit is omitted
|
||||||
|
this.inputs.discussionCategory,
|
||||||
|
false, // We want to publish the release, set draft to false
|
||||||
this.inputs.makeLatest,
|
this.inputs.makeLatest,
|
||||||
this.inputs.createdReleaseName,
|
this.inputs.createdReleaseName,
|
||||||
this.inputs.createdPrerelease
|
this.inputs.createdPrerelease
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async combineBodyWithReleaseNotes(body: string | undefined, isUpdate: boolean): Promise<string | undefined> {
|
||||||
|
// Determine if we should generate release notes based on operation type
|
||||||
|
const shouldGenerateReleaseNotes = isUpdate
|
||||||
|
? this.inputs.generateReleaseNotes && !this.inputs.omitBodyDuringUpdate
|
||||||
|
: this.inputs.generateReleaseNotes
|
||||||
|
|
||||||
|
if (!shouldGenerateReleaseNotes) {
|
||||||
|
return body
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await this.releases.generateReleaseNotes(this.inputs.tag, this.inputs.generateReleaseNotesPreviousTag, this.inputs.commit)
|
||||||
|
const releaseNotes = response.data.body
|
||||||
|
|
||||||
|
if (!body || body.trim() === "") {
|
||||||
|
return releaseNotes
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${body}\n${releaseNotes}`
|
||||||
|
}
|
||||||
|
|
||||||
|
private setOutputs(releaseData: any, assetUrls: Record<string, string>): void {
|
||||||
|
this.outputs.applyReleaseData(releaseData)
|
||||||
|
this.outputs.applyAssetUrls(assetUrls)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,20 @@
|
|||||||
import {Releases} from "./Releases";
|
import { Releases } from "./Releases.js"
|
||||||
|
|
||||||
export interface ActionSkipper {
|
export interface ActionSkipper {
|
||||||
shouldSkip(): Promise<boolean>
|
shouldSkip(): Promise<boolean>
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ReleaseActionSkipper {
|
export class ReleaseActionSkipper {
|
||||||
constructor(private skipIfReleaseExists: boolean,
|
constructor(
|
||||||
private releases: Releases,
|
private skipIfReleaseExists: boolean,
|
||||||
private tag: string) {
|
private releases: Releases,
|
||||||
}
|
private tag: string
|
||||||
|
) {}
|
||||||
|
|
||||||
async shouldSkip(): Promise<boolean> {
|
async shouldSkip(): Promise<boolean> {
|
||||||
if (!this.skipIfReleaseExists) {
|
if (!this.skipIfReleaseExists) {
|
||||||
// Bail if skip flag isn't set.
|
// Bail if skip flag isn't set.
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -21,7 +22,7 @@ export class ReleaseActionSkipper {
|
|||||||
return getResponse.data != null
|
return getResponse.data != null
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
// There is either no release or something else went wrong. Either way, run the action.
|
// There is either no release or something else went wrong. Either way, run the action.
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { basename } from "path";
|
import { basename } from "path"
|
||||||
import { readFileSync, statSync } from "fs";
|
import { createReadStream, readFileSync, ReadStream, statSync } from "fs"
|
||||||
|
|
||||||
export class Artifact {
|
export class Artifact {
|
||||||
readonly contentType: string
|
readonly contentType: string
|
||||||
@@ -9,14 +9,14 @@ export class Artifact {
|
|||||||
constructor(path: string, contentType: string = "raw") {
|
constructor(path: string, contentType: string = "raw") {
|
||||||
this.path = path
|
this.path = path
|
||||||
this.name = basename(path)
|
this.name = basename(path)
|
||||||
this.contentType = contentType;
|
this.contentType = contentType
|
||||||
}
|
}
|
||||||
|
|
||||||
get contentLength(): number {
|
get contentLength(): number {
|
||||||
return statSync(this.path).size
|
return statSync(this.path).size
|
||||||
}
|
}
|
||||||
|
|
||||||
readFile(): Buffer {
|
readFile(): ReadStream {
|
||||||
return readFileSync(this.path)
|
return createReadStream(this.path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
import {Releases} from "./Releases";
|
import { Releases } from "./Releases.js"
|
||||||
import * as core from "@actions/core";
|
import * as core from "@actions/core"
|
||||||
|
|
||||||
export interface ArtifactDestroyer {
|
export interface ArtifactDestroyer {
|
||||||
destroyArtifacts(releaseId: number): Promise<void>
|
destroyArtifacts(releaseId: number): Promise<void>
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GithubArtifactDestroyer implements ArtifactDestroyer {
|
export class GithubArtifactDestroyer implements ArtifactDestroyer {
|
||||||
constructor(private releases: Releases) {
|
constructor(private releases: Releases) {}
|
||||||
}
|
|
||||||
|
|
||||||
async destroyArtifacts(releaseId: number): Promise<void> {
|
async destroyArtifacts(releaseId: number): Promise<void> {
|
||||||
const releaseAssets = await this.releases.listArtifactsForRelease(releaseId)
|
const releaseAssets = await this.releases.listArtifactsForRelease(releaseId)
|
||||||
@@ -17,4 +16,4 @@ export class GithubArtifactDestroyer implements ArtifactDestroyer {
|
|||||||
await this.releases.deleteArtifact(asset.id)
|
await this.releases.deleteArtifact(asset.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import * as core from '@actions/core';
|
import * as core from "@actions/core"
|
||||||
import {Globber, FileGlobber} from "./Globber";
|
import { Globber, FileGlobber } from "./Globber.js"
|
||||||
import {Artifact} from "./Artifact";
|
import { Artifact } from "./Artifact.js"
|
||||||
import untildify from "untildify";
|
import { expandTilde } from "./PathExpander.js"
|
||||||
import {ArtifactPathValidator} from "./ArtifactPathValidator";
|
import { ArtifactPathValidator } from "./ArtifactPathValidator.js"
|
||||||
import {PathNormalizer} from "./PathNormalizer";
|
import { PathNormalizer } from "./PathNormalizer.js"
|
||||||
|
|
||||||
export interface ArtifactGlobber {
|
export interface ArtifactGlobber {
|
||||||
globArtifactString(artifact: string, contentType: string, errorsFailBuild: boolean): Artifact[]
|
globArtifactString(artifact: string, contentType: string, errorsFailBuild: boolean): Artifact[]
|
||||||
@@ -18,14 +18,15 @@ export class FileArtifactGlobber implements ArtifactGlobber {
|
|||||||
|
|
||||||
globArtifactString(artifact: string, contentType: string, errorsFailBuild: boolean): Artifact[] {
|
globArtifactString(artifact: string, contentType: string, errorsFailBuild: boolean): Artifact[] {
|
||||||
const split = /[,\n]/
|
const split = /[,\n]/
|
||||||
return artifact.split(split)
|
return artifact
|
||||||
.map(path => path.trimStart())
|
.split(split)
|
||||||
.map(path => PathNormalizer.normalizePath(path))
|
.map((path) => path.trimStart())
|
||||||
.map(path => FileArtifactGlobber.expandPath(path))
|
.map((path) => PathNormalizer.normalizePath(path))
|
||||||
.map(pattern => this.globPattern(pattern, errorsFailBuild))
|
.map((path) => FileArtifactGlobber.expandPath(path))
|
||||||
|
.map((pattern) => this.globPattern(pattern, errorsFailBuild))
|
||||||
.map((globResult) => FileArtifactGlobber.validatePattern(errorsFailBuild, globResult[1], globResult[0]))
|
.map((globResult) => FileArtifactGlobber.validatePattern(errorsFailBuild, globResult[1], globResult[0]))
|
||||||
.reduce((accumulated, current) => accumulated.concat(current))
|
.reduce((accumulated, current) => accumulated.concat(current))
|
||||||
.map(path => new Artifact(path, contentType))
|
.map((path) => new Artifact(path, contentType))
|
||||||
}
|
}
|
||||||
|
|
||||||
private globPattern(pattern: string, errorsFailBuild: boolean): [string, string[]] {
|
private globPattern(pattern: string, errorsFailBuild: boolean): [string, string[]] {
|
||||||
@@ -54,6 +55,6 @@ export class FileArtifactGlobber implements ArtifactGlobber {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static expandPath(path: string): string {
|
private static expandPath(path: string): string {
|
||||||
return untildify(path)
|
return expandTilde(path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +1,29 @@
|
|||||||
import * as core from "@actions/core";
|
import * as core from "@actions/core"
|
||||||
import {statSync} from "fs";
|
import { statSync } from "fs"
|
||||||
|
|
||||||
export class ArtifactPathValidator {
|
export class ArtifactPathValidator {
|
||||||
private readonly errorsFailBuild: boolean;
|
private readonly errorsFailBuild: boolean
|
||||||
private paths: string[];
|
private paths: string[]
|
||||||
private readonly pattern: string
|
private readonly pattern: string
|
||||||
|
|
||||||
constructor(errorsFailBuild: boolean, paths: string[], pattern: string) {
|
constructor(errorsFailBuild: boolean, paths: string[], pattern: string) {
|
||||||
this.paths = paths;
|
this.paths = paths
|
||||||
this.pattern = pattern
|
this.pattern = pattern
|
||||||
this.errorsFailBuild = errorsFailBuild;
|
this.errorsFailBuild = errorsFailBuild
|
||||||
}
|
}
|
||||||
|
|
||||||
validate(): string[] {
|
validate(): string[] {
|
||||||
this.verifyPathsNotEmpty()
|
this.verifyPathsNotEmpty()
|
||||||
return this.paths.filter((path) => this.verifyNotDirectory(path))
|
return this.paths.filter((path) => this.verifyNotDirectory(path))
|
||||||
}
|
}
|
||||||
|
|
||||||
private verifyPathsNotEmpty() {
|
private verifyPathsNotEmpty() {
|
||||||
if (this.paths.length == 0) {
|
if (this.paths.length == 0) {
|
||||||
const message = `Artifact pattern:${this.pattern} did not match any files`
|
const message = `Artifact pattern:${this.pattern} did not match any files`
|
||||||
this.reportError(message)
|
this.reportError(message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private verifyNotDirectory(path: string): boolean {
|
private verifyNotDirectory(path: string): boolean {
|
||||||
const isDir = statSync(path).isDirectory()
|
const isDir = statSync(path).isDirectory()
|
||||||
if (isDir) {
|
if (isDir) {
|
||||||
@@ -32,7 +32,7 @@ export class ArtifactPathValidator {
|
|||||||
}
|
}
|
||||||
return !isDir
|
return !isDir
|
||||||
}
|
}
|
||||||
|
|
||||||
private reportError(message: string) {
|
private reportError(message: string) {
|
||||||
if (this.errorsFailBuild) {
|
if (this.errorsFailBuild) {
|
||||||
throw Error(message)
|
throw Error(message)
|
||||||
@@ -40,4 +40,4 @@ export class ArtifactPathValidator {
|
|||||||
core.warning(message)
|
core.warning(message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,51 +1,54 @@
|
|||||||
import * as core from '@actions/core';
|
import * as core from "@actions/core"
|
||||||
import {Artifact} from "./Artifact";
|
import { Artifact } from "./Artifact.js"
|
||||||
import {Releases} from "./Releases";
|
import { Releases } from "./Releases.js"
|
||||||
|
|
||||||
export interface ArtifactUploader {
|
export interface ArtifactUploader {
|
||||||
uploadArtifacts(artifacts: Artifact[], releaseId: number, uploadUrl: string): Promise<void>
|
uploadArtifacts(artifacts: Artifact[], releaseId: number, uploadUrl: string): Promise<Record<string, string>>
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GithubArtifactUploader implements ArtifactUploader {
|
export class GithubArtifactUploader implements ArtifactUploader {
|
||||||
constructor(
|
constructor(
|
||||||
private releases: Releases,
|
private releases: Releases,
|
||||||
private replacesExistingArtifacts: boolean = true,
|
private replacesExistingArtifacts: boolean = true,
|
||||||
private throwsUploadErrors: boolean = false,
|
private throwsUploadErrors: boolean = false
|
||||||
) {
|
) {}
|
||||||
}
|
|
||||||
|
|
||||||
async uploadArtifacts(artifacts: Artifact[],
|
async uploadArtifacts(artifacts: Artifact[], releaseId: number, uploadUrl: string): Promise<Record<string, string>> {
|
||||||
releaseId: number,
|
|
||||||
uploadUrl: string): Promise<void> {
|
|
||||||
if (this.replacesExistingArtifacts) {
|
if (this.replacesExistingArtifacts) {
|
||||||
await this.deleteUpdatedArtifacts(artifacts, releaseId)
|
await this.deleteUpdatedArtifacts(artifacts, releaseId)
|
||||||
}
|
}
|
||||||
|
const assetUrls: Record<string, string> = {}
|
||||||
for (const artifact of artifacts) {
|
for (const artifact of artifacts) {
|
||||||
await this.uploadArtifact(artifact, releaseId, uploadUrl)
|
const assetUrl = await this.uploadArtifact(artifact, releaseId, uploadUrl)
|
||||||
|
if (assetUrl !== null) {
|
||||||
|
assetUrls[artifact.name] = assetUrl
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return assetUrls
|
||||||
}
|
}
|
||||||
|
|
||||||
private async uploadArtifact(artifact: Artifact,
|
private async uploadArtifact(artifact: Artifact, releaseId: number, uploadUrl: string, retry = 3): Promise<string | null> {
|
||||||
releaseId: number,
|
|
||||||
uploadUrl: string,
|
|
||||||
retry = 3) {
|
|
||||||
try {
|
try {
|
||||||
core.debug(`Uploading artifact ${artifact.name}...`)
|
core.debug(`Uploading artifact ${artifact.name}...`)
|
||||||
await this.releases.uploadArtifact(uploadUrl,
|
const assetResponse = await this.releases.uploadArtifact(
|
||||||
|
uploadUrl,
|
||||||
artifact.contentLength,
|
artifact.contentLength,
|
||||||
artifact.contentType,
|
artifact.contentType,
|
||||||
artifact.readFile(),
|
artifact.readFile(),
|
||||||
artifact.name,
|
artifact.name,
|
||||||
releaseId)
|
releaseId
|
||||||
|
)
|
||||||
|
return assetResponse.data.browser_download_url
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
if (error.status >= 500 && retry > 0) {
|
if (error.status >= 500 && retry > 0) {
|
||||||
core.warning(`Failed to upload artifact ${artifact.name}. ${error.message}. Retrying...`)
|
core.warning(`Failed to upload artifact ${artifact.name}. ${error.message}. Retrying...`)
|
||||||
await this.uploadArtifact(artifact, releaseId, uploadUrl, retry - 1)
|
return this.uploadArtifact(artifact, releaseId, uploadUrl, retry - 1)
|
||||||
} else {
|
} else {
|
||||||
if (this.throwsUploadErrors) {
|
if (this.throwsUploadErrors) {
|
||||||
throw Error(`Failed to upload artifact ${artifact.name}. ${error.message}.`)
|
throw Error(`Failed to upload artifact ${artifact.name}. ${error.message}.`)
|
||||||
} else {
|
} else {
|
||||||
core.warning(`Failed to upload artifact ${artifact.name}. ${error.message}.`)
|
core.warning(`Failed to upload artifact ${artifact.name}. ${error.message}.`)
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -54,9 +57,9 @@ export class GithubArtifactUploader implements ArtifactUploader {
|
|||||||
private async deleteUpdatedArtifacts(artifacts: Artifact[], releaseId: number): Promise<void> {
|
private async deleteUpdatedArtifacts(artifacts: Artifact[], releaseId: number): Promise<void> {
|
||||||
const releaseAssets = await this.releases.listArtifactsForRelease(releaseId)
|
const releaseAssets = await this.releases.listArtifactsForRelease(releaseId)
|
||||||
const assetByName: Record<string, { id: number; name: string }> = {}
|
const assetByName: Record<string, { id: number; name: string }> = {}
|
||||||
releaseAssets.forEach(asset => {
|
releaseAssets.forEach((asset) => {
|
||||||
assetByName[asset.name] = asset
|
assetByName[asset.name] = asset
|
||||||
});
|
})
|
||||||
for (const artifact of artifacts) {
|
for (const artifact of artifacts) {
|
||||||
const asset = assetByName[artifact.name]
|
const asset = assetByName[artifact.name]
|
||||||
if (asset) {
|
if (asset) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import {GithubErrorDetail} from "./GithubErrorDetail"
|
import { GithubErrorDetail } from "./GithubErrorDetail.js"
|
||||||
|
|
||||||
export class GithubError {
|
export class GithubError {
|
||||||
private error: any
|
private error: any
|
||||||
@@ -31,14 +31,20 @@ export class GithubError {
|
|||||||
const errors = this.githubErrors
|
const errors = this.githubErrors
|
||||||
const status = this.status
|
const status = this.status
|
||||||
if (errors.length > 0) {
|
if (errors.length > 0) {
|
||||||
return `Error ${status}: ${message}\nErrors:\n${this.errorBulletedList(errors)}`
|
return `Error ${status}: ${message}\nErrors:\n${this.errorBulletedList(errors)}${this.remediation()}`
|
||||||
} else {
|
} else {
|
||||||
return `Error ${status}: ${message}`
|
return `Error ${status}: ${message}${this.remediation()}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private errorBulletedList(errors: GithubErrorDetail[]): string {
|
private errorBulletedList(errors: GithubErrorDetail[]): string {
|
||||||
return errors.map((err) => `- ${err}`).join("\n")
|
return errors.map((err) => `- ${err}`).join("\n")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
private remediation(): String {
|
||||||
|
if (this.status == 404) {
|
||||||
|
return "\nMake sure your github token has access to the repo and has permission to author releases"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export class GithubErrorDetail {
|
export class GithubErrorDetail {
|
||||||
private error: any;
|
private error: any
|
||||||
|
|
||||||
constructor(error: any) {
|
constructor(error: any) {
|
||||||
this.error = error
|
this.error = error
|
||||||
@@ -12,13 +12,13 @@ export class GithubErrorDetail {
|
|||||||
toString(): string {
|
toString(): string {
|
||||||
const code = this.error.code
|
const code = this.error.code
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case 'missing':
|
case "missing":
|
||||||
return this.missingResourceMessage()
|
return this.missingResourceMessage()
|
||||||
case 'missing_field':
|
case "missing_field":
|
||||||
return this.missingFieldMessage()
|
return this.missingFieldMessage()
|
||||||
case 'invalid':
|
case "invalid":
|
||||||
return this.invalidFieldMessage()
|
return this.invalidFieldMessage()
|
||||||
case 'already_exists':
|
case "already_exists":
|
||||||
return this.resourceAlreadyExists()
|
return this.resourceAlreadyExists()
|
||||||
default:
|
default:
|
||||||
return this.customErrorMessage()
|
return this.customErrorMessage()
|
||||||
@@ -26,7 +26,7 @@ export class GithubErrorDetail {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private customErrorMessage(): string {
|
private customErrorMessage(): string {
|
||||||
const message = this.error.message;
|
const message = this.error.message
|
||||||
const documentation = this.error.documentation_url
|
const documentation = this.error.documentation_url
|
||||||
|
|
||||||
let documentationMessage: string
|
let documentationMessage: string
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { GlobSync } from "glob";
|
import { globSync } from "glob"
|
||||||
|
|
||||||
export interface Globber {
|
export interface Globber {
|
||||||
glob(pattern: string): string[]
|
glob(pattern: string): string[]
|
||||||
@@ -6,6 +6,6 @@ export interface Globber {
|
|||||||
|
|
||||||
export class FileGlobber implements Globber {
|
export class FileGlobber implements Globber {
|
||||||
glob(pattern: string): string[] {
|
glob(pattern: string): string[] {
|
||||||
return new GlobSync(pattern, { mark: true }).found
|
return globSync(pattern, { mark: true })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
136
src/Inputs.ts
136
src/Inputs.ts
@@ -1,8 +1,8 @@
|
|||||||
import * as core from '@actions/core';
|
import { readFileSync } from "node:fs"
|
||||||
import {Context} from "@actions/github/lib/context";
|
import * as core from "@actions/core"
|
||||||
import {readFileSync} from 'fs';
|
import type * as github from "@actions/github"
|
||||||
import {ArtifactGlobber} from './ArtifactGlobber';
|
import type { Artifact } from "./Artifact.js"
|
||||||
import {Artifact} from './Artifact';
|
import type { ArtifactGlobber } from "./ArtifactGlobber.js"
|
||||||
|
|
||||||
export interface Inputs {
|
export interface Inputs {
|
||||||
readonly allowUpdates: boolean
|
readonly allowUpdates: boolean
|
||||||
@@ -15,7 +15,10 @@ export interface Inputs {
|
|||||||
readonly createdReleaseName?: string
|
readonly createdReleaseName?: string
|
||||||
readonly discussionCategory?: string
|
readonly discussionCategory?: string
|
||||||
readonly generateReleaseNotes: boolean
|
readonly generateReleaseNotes: boolean
|
||||||
readonly makeLatest?: string
|
readonly generateReleaseNotesPreviousTag?: string
|
||||||
|
readonly immutableCreate: boolean
|
||||||
|
readonly makeLatest?: "legacy" | "true" | "false" | undefined
|
||||||
|
readonly omitBodyDuringUpdate: boolean
|
||||||
readonly owner: string
|
readonly owner: string
|
||||||
readonly removeArtifacts: boolean
|
readonly removeArtifacts: boolean
|
||||||
readonly replacesArtifacts: boolean
|
readonly replacesArtifacts: boolean
|
||||||
@@ -32,61 +35,60 @@ export interface Inputs {
|
|||||||
|
|
||||||
export class CoreInputs implements Inputs {
|
export class CoreInputs implements Inputs {
|
||||||
private artifactGlobber: ArtifactGlobber
|
private artifactGlobber: ArtifactGlobber
|
||||||
private context: Context
|
private context: typeof github.context
|
||||||
|
|
||||||
constructor(artifactGlobber: ArtifactGlobber, context: Context) {
|
constructor(artifactGlobber: ArtifactGlobber, context: typeof github.context) {
|
||||||
this.artifactGlobber = artifactGlobber
|
this.artifactGlobber = artifactGlobber
|
||||||
this.context = context
|
this.context = context
|
||||||
}
|
}
|
||||||
|
|
||||||
get allowUpdates(): boolean {
|
get allowUpdates(): boolean {
|
||||||
const allow = core.getInput('allowUpdates')
|
const allow = core.getInput("allowUpdates")
|
||||||
return allow == 'true'
|
return allow === "true"
|
||||||
}
|
}
|
||||||
|
|
||||||
get artifacts(): Artifact[] {
|
get artifacts(): Artifact[] {
|
||||||
let artifacts = core.getInput('artifacts')
|
let artifacts = core.getInput("artifacts")
|
||||||
if (!artifacts) {
|
if (!artifacts) {
|
||||||
artifacts = core.getInput('artifact')
|
artifacts = core.getInput("artifact")
|
||||||
}
|
}
|
||||||
if (artifacts) {
|
if (artifacts) {
|
||||||
let contentType = core.getInput('artifactContentType')
|
let contentType = core.getInput("artifactContentType")
|
||||||
if (!contentType) {
|
if (!contentType) {
|
||||||
contentType = 'raw'
|
contentType = "raw"
|
||||||
}
|
}
|
||||||
return this.artifactGlobber
|
return this.artifactGlobber.globArtifactString(artifacts, contentType, this.artifactErrorsFailBuild)
|
||||||
.globArtifactString(artifacts, contentType, this.artifactErrorsFailBuild)
|
|
||||||
}
|
}
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
get artifactErrorsFailBuild(): boolean {
|
get artifactErrorsFailBuild(): boolean {
|
||||||
const allow = core.getInput('artifactErrorsFailBuild')
|
const allow = core.getInput("artifactErrorsFailBuild")
|
||||||
return allow == 'true'
|
return allow === "true"
|
||||||
}
|
}
|
||||||
|
|
||||||
private get body(): string | undefined {
|
private get body(): string | undefined {
|
||||||
const body = core.getInput('body')
|
const body = core.getInput("body")
|
||||||
if (body) {
|
if (body) {
|
||||||
return body
|
return body
|
||||||
}
|
}
|
||||||
|
|
||||||
const bodyFile = core.getInput('bodyFile')
|
const bodyFile = core.getInput("bodyFile")
|
||||||
if (bodyFile) {
|
if (bodyFile) {
|
||||||
return this.stringFromFile(bodyFile)
|
return this.stringFromFile(bodyFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ''
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
get createdDraft(): boolean {
|
get createdDraft(): boolean {
|
||||||
const draft = core.getInput('draft')
|
const draft = core.getInput("draft")
|
||||||
return draft == 'true'
|
return draft === "true"
|
||||||
}
|
}
|
||||||
|
|
||||||
get createdPrerelease(): boolean {
|
get createdPrerelease(): boolean {
|
||||||
const preRelease = core.getInput('prerelease')
|
const preRelease = core.getInput("prerelease")
|
||||||
return preRelease == 'true'
|
return preRelease === "true"
|
||||||
}
|
}
|
||||||
|
|
||||||
get createdReleaseBody(): string | undefined {
|
get createdReleaseBody(): string | undefined {
|
||||||
@@ -95,7 +97,7 @@ export class CoreInputs implements Inputs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static get omitBody(): boolean {
|
private static get omitBody(): boolean {
|
||||||
return core.getInput('omitBody') == 'true'
|
return core.getInput("omitBody") === "true"
|
||||||
}
|
}
|
||||||
|
|
||||||
get createdReleaseName(): string | undefined {
|
get createdReleaseName(): string | undefined {
|
||||||
@@ -104,11 +106,11 @@ export class CoreInputs implements Inputs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static get omitName(): boolean {
|
private static get omitName(): boolean {
|
||||||
return core.getInput('omitName') == 'true'
|
return core.getInput("omitName") === "true"
|
||||||
}
|
}
|
||||||
|
|
||||||
get commit(): string | undefined {
|
get commit(): string | undefined {
|
||||||
const commit = core.getInput('commit')
|
const commit = core.getInput("commit")
|
||||||
if (commit) {
|
if (commit) {
|
||||||
return commit
|
return commit
|
||||||
}
|
}
|
||||||
@@ -116,7 +118,7 @@ export class CoreInputs implements Inputs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get discussionCategory(): string | undefined {
|
get discussionCategory(): string | undefined {
|
||||||
const category = core.getInput('discussionCategory')
|
const category = core.getInput("discussionCategory")
|
||||||
if (category) {
|
if (category) {
|
||||||
return category
|
return category
|
||||||
}
|
}
|
||||||
@@ -124,42 +126,58 @@ export class CoreInputs implements Inputs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private get name(): string | undefined {
|
private get name(): string | undefined {
|
||||||
const name = core.getInput('name')
|
const name = core.getInput("name")
|
||||||
if (name) {
|
if (name) {
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.tag
|
return this.tag
|
||||||
}
|
}
|
||||||
|
|
||||||
get generateReleaseNotes(): boolean {
|
get generateReleaseNotes(): boolean {
|
||||||
const generate = core.getInput('generateReleaseNotes')
|
const generate = core.getInput("generateReleaseNotes")
|
||||||
return generate == 'true'
|
return generate === "true"
|
||||||
}
|
}
|
||||||
|
|
||||||
get makeLatest(): string {
|
get generateReleaseNotesPreviousTag(): string | undefined {
|
||||||
return core.getInput('makeLatest')
|
const previousTag = core.getInput("generateReleaseNotesPreviousTag")
|
||||||
|
return previousTag || undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
get immutableCreate(): boolean {
|
||||||
|
const immutable = core.getInput("immutableCreate")
|
||||||
|
return immutable === "true"
|
||||||
|
}
|
||||||
|
|
||||||
|
get makeLatest(): "legacy" | "true" | "false" | undefined {
|
||||||
|
const latest = core.getInput("makeLatest")
|
||||||
|
if (latest === "true" || latest === "false" || latest === "legacy") {
|
||||||
|
return latest
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
get owner(): string {
|
get owner(): string {
|
||||||
let owner = core.getInput('owner')
|
const owner = core.getInput("owner")
|
||||||
if (owner) {
|
if (owner) {
|
||||||
return owner
|
return owner
|
||||||
}
|
}
|
||||||
return this.context.repo.owner
|
return this.context.repo.owner
|
||||||
}
|
}
|
||||||
|
|
||||||
get removeArtifacts(): boolean {
|
get removeArtifacts(): boolean {
|
||||||
const removes = core.getInput('removeArtifacts')
|
const removes = core.getInput("removeArtifacts")
|
||||||
return removes == 'true'
|
return removes === "true"
|
||||||
}
|
}
|
||||||
|
|
||||||
get replacesArtifacts(): boolean {
|
get replacesArtifacts(): boolean {
|
||||||
const replaces = core.getInput('replacesArtifacts')
|
const replaces = core.getInput("replacesArtifacts")
|
||||||
return replaces == 'true'
|
return replaces === "true"
|
||||||
}
|
}
|
||||||
|
|
||||||
get repo(): string {
|
get repo(): string {
|
||||||
let repo = core.getInput('repo')
|
const repo = core.getInput("repo")
|
||||||
if (repo) {
|
if (repo) {
|
||||||
return repo
|
return repo
|
||||||
}
|
}
|
||||||
@@ -169,16 +187,16 @@ export class CoreInputs implements Inputs {
|
|||||||
get skipIfReleaseExists(): boolean {
|
get skipIfReleaseExists(): boolean {
|
||||||
return core.getBooleanInput("skipIfReleaseExists")
|
return core.getBooleanInput("skipIfReleaseExists")
|
||||||
}
|
}
|
||||||
|
|
||||||
get tag(): string {
|
get tag(): string {
|
||||||
const tag = core.getInput('tag')
|
const tag = core.getInput("tag")
|
||||||
if (tag) {
|
if (tag) {
|
||||||
return tag;
|
return tag
|
||||||
}
|
}
|
||||||
|
|
||||||
const ref = this.context.ref
|
const ref = this.context.ref
|
||||||
const tagPath = "refs/tags/"
|
const tagPath = "refs/tags/"
|
||||||
if (ref && ref.startsWith(tagPath)) {
|
if (ref?.startsWith(tagPath)) {
|
||||||
return ref.substr(tagPath.length, ref.length)
|
return ref.substr(tagPath.length, ref.length)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,7 +204,7 @@ export class CoreInputs implements Inputs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get token(): string {
|
get token(): string {
|
||||||
return core.getInput('token', {required: true})
|
return core.getInput("token", { required: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
get updatedDraft(): boolean | undefined {
|
get updatedDraft(): boolean | undefined {
|
||||||
@@ -195,16 +213,16 @@ export class CoreInputs implements Inputs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static get omitDraftDuringUpdate(): boolean {
|
private static get omitDraftDuringUpdate(): boolean {
|
||||||
return core.getInput('omitDraftDuringUpdate') == 'true'
|
return core.getInput("omitDraftDuringUpdate") === "true"
|
||||||
}
|
}
|
||||||
|
|
||||||
get updatedPrerelease(): boolean | undefined {
|
get updatedPrerelease(): boolean | undefined {
|
||||||
if (CoreInputs.omitPrereleaseDuringUpdate) return undefined
|
if (CoreInputs.omitPrereleaseDuringUpdate) return undefined
|
||||||
return this.createdPrerelease
|
return this.createdPrerelease
|
||||||
}
|
}
|
||||||
|
|
||||||
private static get omitPrereleaseDuringUpdate(): boolean {
|
private static get omitPrereleaseDuringUpdate(): boolean {
|
||||||
return core.getInput('omitPrereleaseDuringUpdate') == 'true'
|
return core.getInput("omitPrereleaseDuringUpdate") === "true"
|
||||||
}
|
}
|
||||||
|
|
||||||
get updatedReleaseBody(): string | undefined {
|
get updatedReleaseBody(): string | undefined {
|
||||||
@@ -212,24 +230,28 @@ export class CoreInputs implements Inputs {
|
|||||||
return this.body
|
return this.body
|
||||||
}
|
}
|
||||||
|
|
||||||
private static get omitBodyDuringUpdate(): boolean {
|
get updateOnlyUnreleased(): boolean {
|
||||||
return core.getInput('omitBodyDuringUpdate') == 'true'
|
return core.getInput("updateOnlyUnreleased") === "true"
|
||||||
}
|
}
|
||||||
|
|
||||||
get updatedReleaseName(): string | undefined {
|
get updatedReleaseName(): string | undefined {
|
||||||
if (CoreInputs.omitName || CoreInputs.omitNameDuringUpdate) return undefined
|
if (CoreInputs.omitName || CoreInputs.omitNameDuringUpdate) return undefined
|
||||||
return this.name
|
return this.name
|
||||||
}
|
}
|
||||||
|
|
||||||
get updateOnlyUnreleased(): boolean {
|
private static get omitBodyDuringUpdate(): boolean {
|
||||||
return core.getInput('updateOnlyUnreleased') == 'true'
|
return core.getInput("omitBodyDuringUpdate") === "true"
|
||||||
|
}
|
||||||
|
|
||||||
|
get omitBodyDuringUpdate(): boolean {
|
||||||
|
return CoreInputs.omitBodyDuringUpdate
|
||||||
}
|
}
|
||||||
|
|
||||||
private static get omitNameDuringUpdate(): boolean {
|
private static get omitNameDuringUpdate(): boolean {
|
||||||
return core.getInput('omitNameDuringUpdate') == 'true'
|
return core.getInput("omitNameDuringUpdate") === "true"
|
||||||
}
|
}
|
||||||
|
|
||||||
stringFromFile(path: string): string {
|
stringFromFile(path: string): string {
|
||||||
return readFileSync(path, 'utf-8')
|
return readFileSync(path, "utf-8")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
30
src/Main.ts
30
src/Main.ts
@@ -1,14 +1,14 @@
|
|||||||
import * as github from '@actions/github';
|
import * as github from "@actions/github"
|
||||||
import * as core from '@actions/core';
|
import * as core from "@actions/core"
|
||||||
import {CoreInputs} from './Inputs';
|
import { CoreInputs } from "./Inputs.js"
|
||||||
import {GithubReleases} from './Releases';
|
import { GithubReleases } from "./Releases.js"
|
||||||
import {Action} from './Action';
|
import { Action } from "./Action.js"
|
||||||
import {GithubArtifactUploader} from './ArtifactUploader';
|
import { GithubArtifactUploader } from "./ArtifactUploader.js"
|
||||||
import {FileArtifactGlobber} from './ArtifactGlobber';
|
import { FileArtifactGlobber } from "./ArtifactGlobber.js"
|
||||||
import {GithubError} from './GithubError';
|
import { GithubError } from "./GithubError.js"
|
||||||
import {CoreOutputs} from "./Outputs";
|
import { CoreOutputs } from "./Outputs.js"
|
||||||
import {GithubArtifactDestroyer} from "./ArtifactDestroyer";
|
import { GithubArtifactDestroyer } from "./ArtifactDestroyer.js"
|
||||||
import {ActionSkipper, ReleaseActionSkipper} from "./ActionSkipper";
|
import { ActionSkipper, ReleaseActionSkipper } from "./ActionSkipper.js"
|
||||||
|
|
||||||
async function run() {
|
async function run() {
|
||||||
try {
|
try {
|
||||||
@@ -16,12 +16,12 @@ async function run() {
|
|||||||
await action.perform()
|
await action.perform()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const githubError = new GithubError(error)
|
const githubError = new GithubError(error)
|
||||||
core.setFailed(githubError.toString());
|
core.setFailed(githubError.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createAction(): Action {
|
function createAction(): Action {
|
||||||
const token = core.getInput('token')
|
const token = core.getInput("token")
|
||||||
const context = github.context
|
const context = github.context
|
||||||
const git = github.getOctokit(token)
|
const git = github.getOctokit(token)
|
||||||
const globber = new FileArtifactGlobber()
|
const globber = new FileArtifactGlobber()
|
||||||
@@ -32,8 +32,8 @@ function createAction(): Action {
|
|||||||
const skipper = new ReleaseActionSkipper(inputs.skipIfReleaseExists, releases, inputs.tag)
|
const skipper = new ReleaseActionSkipper(inputs.skipIfReleaseExists, releases, inputs.tag)
|
||||||
const uploader = new GithubArtifactUploader(releases, inputs.replacesArtifacts, inputs.artifactErrorsFailBuild)
|
const uploader = new GithubArtifactUploader(releases, inputs.replacesArtifacts, inputs.artifactErrorsFailBuild)
|
||||||
const artifactDestroyer = new GithubArtifactDestroyer(releases)
|
const artifactDestroyer = new GithubArtifactDestroyer(releases)
|
||||||
|
|
||||||
return new Action(inputs, outputs, releases, uploader, artifactDestroyer, skipper)
|
return new Action(inputs, outputs, releases, uploader, artifactDestroyer, skipper)
|
||||||
}
|
}
|
||||||
|
|
||||||
run();
|
run()
|
||||||
|
|||||||
@@ -1,14 +1,22 @@
|
|||||||
import * as core from '@actions/core';
|
import * as core from "@actions/core"
|
||||||
import {ReleaseData} from "./Releases";
|
import { ReleaseData } from "./Releases.js"
|
||||||
|
|
||||||
export interface Outputs {
|
export interface Outputs {
|
||||||
applyReleaseData(releaseData: ReleaseData): void
|
applyReleaseData(releaseData: ReleaseData): void
|
||||||
|
applyAssetUrls(assetUrls: Record<string, string>): void
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CoreOutputs implements Outputs {
|
export class CoreOutputs implements Outputs {
|
||||||
applyReleaseData(releaseData: ReleaseData) {
|
applyReleaseData(releaseData: ReleaseData) {
|
||||||
core.setOutput('id', releaseData.id)
|
core.setOutput("id", releaseData.id)
|
||||||
core.setOutput('html_url', releaseData.html_url)
|
core.setOutput("html_url", releaseData.html_url)
|
||||||
core.setOutput('upload_url', releaseData.upload_url)
|
core.setOutput("upload_url", releaseData.upload_url)
|
||||||
|
core.setOutput("tarball_url", releaseData.tarball_url || "")
|
||||||
|
core.setOutput("zipball_url", releaseData.zipball_url || "")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
applyAssetUrls(assetUrls: Record<string, string>) {
|
||||||
|
const assetUrlsJson = JSON.stringify(assetUrls)
|
||||||
|
core.setOutput("assets", assetUrlsJson)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
6
src/PathExpander.ts
Normal file
6
src/PathExpander.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import os from "os"
|
||||||
|
|
||||||
|
export function expandTilde(path: string): string {
|
||||||
|
return path.replace(/^~(?=$|\/|\\)/, os.homedir())
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import path from "path";
|
import path from "path"
|
||||||
|
|
||||||
export class PathNormalizer {
|
export class PathNormalizer {
|
||||||
static normalizePath(pathString: string): string {
|
static normalizePath(pathString: string): string {
|
||||||
return pathString.split(path.sep).join("/")
|
return pathString.split(path.sep).join("/")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
export class ReleaseValidator {
|
export class ReleaseValidator {
|
||||||
constructor(private updateOnlyUnreleased: boolean) {
|
constructor(private updateOnlyUnreleased: boolean) {}
|
||||||
}
|
|
||||||
|
|
||||||
validateReleaseUpdate(releaseResponse: ReleaseStageArguments) {
|
validateReleaseUpdate(releaseResponse: ReleaseStageArguments) {
|
||||||
if (!this.updateOnlyUnreleased) {
|
if (!this.updateOnlyUnreleased) {
|
||||||
@@ -8,7 +7,9 @@ export class ReleaseValidator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!releaseResponse.draft && !releaseResponse.prerelease) {
|
if (!releaseResponse.draft && !releaseResponse.prerelease) {
|
||||||
throw new Error(`Tried to update "${releaseResponse.name ?? "release"}" which is neither a draft or prerelease. (updateOnlyUnreleased is on)`)
|
throw new Error(
|
||||||
|
`Tried to update "${releaseResponse.name ?? "release"}" which is neither a draft or prerelease. (updateOnlyUnreleased is on)`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -17,4 +18,4 @@ export type ReleaseStageArguments = {
|
|||||||
draft: boolean
|
draft: boolean
|
||||||
name: string | null
|
name: string | null
|
||||||
prerelease: boolean
|
prerelease: boolean
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import {GitHub} from '@actions/github/lib/utils'
|
import * as github from "@actions/github"
|
||||||
import {OctokitResponse} from "@octokit/types";
|
import type { RestEndpointMethodTypes } from "@octokit/plugin-rest-endpoint-methods"
|
||||||
import {RestEndpointMethodTypes} from "@octokit/plugin-rest-endpoint-methods";
|
import type { OctokitResponse } from "@octokit/types"
|
||||||
import {Inputs} from "./Inputs";
|
import type { Inputs } from "./Inputs.js"
|
||||||
|
|
||||||
export type CreateReleaseResponse = RestEndpointMethodTypes["repos"]["createRelease"]["response"]
|
export type CreateReleaseResponse = RestEndpointMethodTypes["repos"]["createRelease"]["response"]
|
||||||
export type ReleaseByTagResponse = RestEndpointMethodTypes["repos"]["getReleaseByTag"]["response"]
|
export type ReleaseByTagResponse = RestEndpointMethodTypes["repos"]["getReleaseByTag"]["response"]
|
||||||
@@ -10,11 +10,14 @@ export type ListReleaseAssetsResponseData = RestEndpointMethodTypes["repos"]["li
|
|||||||
export type UpdateReleaseResponse = RestEndpointMethodTypes["repos"]["updateRelease"]["response"]
|
export type UpdateReleaseResponse = RestEndpointMethodTypes["repos"]["updateRelease"]["response"]
|
||||||
export type UploadArtifactResponse = RestEndpointMethodTypes["repos"]["uploadReleaseAsset"]["response"]
|
export type UploadArtifactResponse = RestEndpointMethodTypes["repos"]["uploadReleaseAsset"]["response"]
|
||||||
export type CreateOrUpdateReleaseResponse = CreateReleaseResponse | UpdateReleaseResponse
|
export type CreateOrUpdateReleaseResponse = CreateReleaseResponse | UpdateReleaseResponse
|
||||||
|
export type GenerateReleaseNotesResponse = RestEndpointMethodTypes["repos"]["generateReleaseNotes"]["response"]
|
||||||
|
|
||||||
export type ReleaseData = {
|
export type ReleaseData = {
|
||||||
id: number
|
id: number
|
||||||
html_url: string
|
html_url: string
|
||||||
upload_url: string
|
upload_url: string
|
||||||
|
tarball_url: string | null
|
||||||
|
zipball_url: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Releases {
|
export interface Releases {
|
||||||
@@ -24,8 +27,7 @@ export interface Releases {
|
|||||||
commitHash?: string,
|
commitHash?: string,
|
||||||
discussionCategory?: string,
|
discussionCategory?: string,
|
||||||
draft?: boolean,
|
draft?: boolean,
|
||||||
generateReleaseNotes?: boolean,
|
makeLatest?: "legacy" | "true" | "false" | undefined,
|
||||||
makeLatest?: string,
|
|
||||||
name?: string,
|
name?: string,
|
||||||
prerelease?: boolean
|
prerelease?: boolean
|
||||||
): Promise<CreateReleaseResponse>
|
): Promise<CreateReleaseResponse>
|
||||||
@@ -34,6 +36,8 @@ export interface Releases {
|
|||||||
|
|
||||||
getByTag(tag: string): Promise<ReleaseByTagResponse>
|
getByTag(tag: string): Promise<ReleaseByTagResponse>
|
||||||
|
|
||||||
|
generateReleaseNotes(tag: string, previousTag?: string, targetCommitish?: string): Promise<GenerateReleaseNotesResponse>
|
||||||
|
|
||||||
listArtifactsForRelease(releaseId: number): Promise<ListReleaseAssetsResponseData>
|
listArtifactsForRelease(releaseId: number): Promise<ListReleaseAssetsResponseData>
|
||||||
|
|
||||||
listReleases(): Promise<ListReleasesResponse>
|
listReleases(): Promise<ListReleasesResponse>
|
||||||
@@ -45,7 +49,7 @@ export interface Releases {
|
|||||||
commitHash?: string,
|
commitHash?: string,
|
||||||
discussionCategory?: string,
|
discussionCategory?: string,
|
||||||
draft?: boolean,
|
draft?: boolean,
|
||||||
makeLatest?: string,
|
makeLatest?: "legacy" | "true" | "false" | undefined,
|
||||||
name?: string,
|
name?: string,
|
||||||
prerelease?: boolean
|
prerelease?: boolean
|
||||||
): Promise<UpdateReleaseResponse>
|
): Promise<UpdateReleaseResponse>
|
||||||
@@ -56,15 +60,15 @@ export interface Releases {
|
|||||||
contentType: string,
|
contentType: string,
|
||||||
file: string | object,
|
file: string | object,
|
||||||
name: string,
|
name: string,
|
||||||
releaseId: number,
|
releaseId: number
|
||||||
): Promise<UploadArtifactResponse>
|
): Promise<UploadArtifactResponse>
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GithubReleases implements Releases {
|
export class GithubReleases implements Releases {
|
||||||
git: InstanceType<typeof GitHub>
|
git: ReturnType<typeof github.getOctokit>
|
||||||
inputs: Inputs
|
inputs: Inputs
|
||||||
|
|
||||||
constructor(inputs: Inputs, git: InstanceType<typeof GitHub>) {
|
constructor(inputs: Inputs, git: ReturnType<typeof github.getOctokit>) {
|
||||||
this.inputs = inputs
|
this.inputs = inputs
|
||||||
this.git = git
|
this.git = git
|
||||||
}
|
}
|
||||||
@@ -75,8 +79,7 @@ export class GithubReleases implements Releases {
|
|||||||
commitHash?: string,
|
commitHash?: string,
|
||||||
discussionCategory?: string,
|
discussionCategory?: string,
|
||||||
draft?: boolean,
|
draft?: boolean,
|
||||||
generateReleaseNotes?: boolean,
|
makeLatest?: "legacy" | "true" | "false" | undefined,
|
||||||
makeLatest?: string,
|
|
||||||
name?: string,
|
name?: string,
|
||||||
prerelease?: boolean
|
prerelease?: boolean
|
||||||
): Promise<CreateReleaseResponse> {
|
): Promise<CreateReleaseResponse> {
|
||||||
@@ -86,48 +89,61 @@ export class GithubReleases implements Releases {
|
|||||||
name: name,
|
name: name,
|
||||||
discussion_category_name: discussionCategory,
|
discussion_category_name: discussionCategory,
|
||||||
draft: draft,
|
draft: draft,
|
||||||
generate_release_notes: generateReleaseNotes,
|
|
||||||
make_latest: makeLatest,
|
make_latest: makeLatest,
|
||||||
owner: this.inputs.owner,
|
owner: this.inputs.owner,
|
||||||
prerelease: prerelease,
|
prerelease: prerelease,
|
||||||
repo: this.inputs.repo,
|
repo: this.inputs.repo,
|
||||||
target_commitish: commitHash,
|
target_commitish: commitHash,
|
||||||
tag_name: tag
|
tag_name: tag,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteArtifact(
|
async deleteArtifact(assetId: number): Promise<OctokitResponse<any>> {
|
||||||
assetId: number
|
|
||||||
): Promise<OctokitResponse<any>> {
|
|
||||||
return this.git.rest.repos.deleteReleaseAsset({
|
return this.git.rest.repos.deleteReleaseAsset({
|
||||||
asset_id: assetId,
|
asset_id: assetId,
|
||||||
owner: this.inputs.owner,
|
owner: this.inputs.owner,
|
||||||
repo: this.inputs.repo
|
repo: this.inputs.repo,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async generateReleaseNotes(tag: string, previousTag?: string, targetCommitish?: string): Promise<GenerateReleaseNotesResponse> {
|
||||||
|
const params: any = {
|
||||||
|
owner: this.inputs.owner,
|
||||||
|
repo: this.inputs.repo,
|
||||||
|
tag_name: tag,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (previousTag) {
|
||||||
|
params.previous_tag_name = previousTag
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetCommitish) {
|
||||||
|
params.target_commitish = targetCommitish
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.git.rest.repos.generateReleaseNotes(params)
|
||||||
|
}
|
||||||
|
|
||||||
async getByTag(tag: string): Promise<ReleaseByTagResponse> {
|
async getByTag(tag: string): Promise<ReleaseByTagResponse> {
|
||||||
return this.git.rest.repos.getReleaseByTag({
|
return this.git.rest.repos.getReleaseByTag({
|
||||||
owner: this.inputs.owner,
|
owner: this.inputs.owner,
|
||||||
repo: this.inputs.repo,
|
repo: this.inputs.repo,
|
||||||
tag: tag
|
tag: tag,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async listArtifactsForRelease(
|
async listArtifactsForRelease(releaseId: number): Promise<ListReleaseAssetsResponseData> {
|
||||||
releaseId: number
|
|
||||||
): Promise<ListReleaseAssetsResponseData> {
|
|
||||||
return this.git.paginate(this.git.rest.repos.listReleaseAssets, {
|
return this.git.paginate(this.git.rest.repos.listReleaseAssets, {
|
||||||
owner: this.inputs.owner,
|
owner: this.inputs.owner,
|
||||||
release_id: releaseId,
|
release_id: releaseId,
|
||||||
repo: this.inputs.repo
|
repo: this.inputs.repo,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async listReleases(): Promise<ListReleasesResponse> {
|
async listReleases(): Promise<ListReleasesResponse> {
|
||||||
return this.git.rest.repos.listReleases({
|
return this.git.rest.repos.listReleases({
|
||||||
owner: this.inputs.owner,
|
owner: this.inputs.owner,
|
||||||
repo: this.inputs.repo
|
repo: this.inputs.repo,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,7 +154,7 @@ export class GithubReleases implements Releases {
|
|||||||
commitHash?: string,
|
commitHash?: string,
|
||||||
discussionCategory?: string,
|
discussionCategory?: string,
|
||||||
draft?: boolean,
|
draft?: boolean,
|
||||||
makeLatest?: string,
|
makeLatest?: "legacy" | "true" | "false" | undefined,
|
||||||
name?: string,
|
name?: string,
|
||||||
prerelease?: boolean
|
prerelease?: boolean
|
||||||
): Promise<UpdateReleaseResponse> {
|
): Promise<UpdateReleaseResponse> {
|
||||||
@@ -154,7 +170,7 @@ export class GithubReleases implements Releases {
|
|||||||
prerelease: prerelease,
|
prerelease: prerelease,
|
||||||
repo: this.inputs.repo,
|
repo: this.inputs.repo,
|
||||||
target_commitish: commitHash,
|
target_commitish: commitHash,
|
||||||
tag_name: tag
|
tag_name: tag,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,19 +180,19 @@ export class GithubReleases implements Releases {
|
|||||||
contentType: string,
|
contentType: string,
|
||||||
file: string | object,
|
file: string | object,
|
||||||
name: string,
|
name: string,
|
||||||
releaseId: number,
|
releaseId: number
|
||||||
): Promise<UploadArtifactResponse> {
|
): Promise<UploadArtifactResponse> {
|
||||||
return this.git.rest.repos.uploadReleaseAsset({
|
return this.git.rest.repos.uploadReleaseAsset({
|
||||||
url: assetUrl,
|
url: assetUrl,
|
||||||
headers: {
|
headers: {
|
||||||
"content-length": contentLength,
|
"content-length": contentLength,
|
||||||
"content-type": contentType
|
"content-type": contentType,
|
||||||
},
|
},
|
||||||
data: file as any,
|
data: file as any,
|
||||||
name: name,
|
name: name,
|
||||||
owner: this.inputs.owner,
|
owner: this.inputs.owner,
|
||||||
release_id: releaseId,
|
release_id: releaseId,
|
||||||
repo: this.inputs.repo
|
repo: this.inputs.repo,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es2015", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
|
"target": "ES2022",
|
||||||
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
|
"module": "NodeNext",
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "NodeNext",
|
||||||
"outDir": "./lib", /* Redirect output structure to the directory. */
|
"outDir": "./lib",
|
||||||
"rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
"rootDir": ".",
|
||||||
"strict": true, /* Enable all strict type-checking options. */
|
"strict": true,
|
||||||
"noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
"skipLibCheck": true,
|
||||||
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
"noImplicitAny": true,
|
||||||
},
|
"esModuleInterop": true,
|
||||||
"exclude": ["node_modules", "**/*.test.ts"]
|
"types": ["node"]
|
||||||
}
|
},
|
||||||
|
"include": ["src/**/*"],
|
||||||
|
"exclude": ["node_modules", "**/*.test.ts", "__mocks__/**/*", "vitest.config.ts"]
|
||||||
|
}
|
||||||
|
|||||||
7
tsconfig.test.json
Normal file
7
tsconfig.test.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"isolatedModules": true
|
||||||
|
},
|
||||||
|
"include": ["src/**/*", "__tests__/**/*"]
|
||||||
|
}
|
||||||
21
vitest.config.ts
Normal file
21
vitest.config.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { defineConfig } from 'vitest/config'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
test: {
|
||||||
|
globals: true,
|
||||||
|
environment: 'node',
|
||||||
|
include: ['**/*.test.ts'],
|
||||||
|
coverage: {
|
||||||
|
provider: 'v8',
|
||||||
|
reporter: ['text', 'lcov'],
|
||||||
|
include: ['src/**/*.ts'],
|
||||||
|
exclude: ['src/Globber.ts', 'src/Releases.ts'],
|
||||||
|
thresholds: {
|
||||||
|
lines: 100,
|
||||||
|
functions: 100,
|
||||||
|
branches: 95,
|
||||||
|
statements: 100,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user