154 Commits

Author SHA1 Message Date
Nick Cipollo
98d25d4189 preparing release 1.19.2
Some checks failed
Release / Create Release (push) Has been cancelled
Test / check_pr (push) Has been cancelled
2025-09-02 12:53:03 -04:00
Nick Cipollo
d3601261f8 Fixes #548 Add support body + generated release notes (#549) 2025-09-02 12:27:56 -04:00
Benoît Blanchon
571fe81601 Update immutableCreate's default in the documentation (#547)
The default of `immutableCreate` changed from `true` to `false` in version 1.9.1
2025-09-02 08:11:01 -04:00
Nick Cipollo
1c89adf398 preparing release 1.19.1
Some checks failed
Release / Create Release (push) Has been cancelled
Test / check_pr (push) Has been cancelled
2025-09-01 12:57:13 -04:00
Nick Cipollo
36bf8dd70f References #545 Default immutable builds to false 2025-09-01 12:47:09 -04:00
Nick Cipollo
b12185d71f preparing release 1.19.0
Some checks failed
Release / Create Release (push) Has been cancelled
Test / check_pr (push) Has been cancelled
2025-09-01 10:28:36 -04:00
Nick Cipollo
defcf131e4 Fixes #540 Add support for immutable releases (#544) 2025-08-28 13:15:38 -04:00
Nick Cipollo
05013d58ed Standardize on separate call to generate release notes 2025-08-21 20:33:00 -04:00
dependabot[bot]
20ce211d17 Bump glob from 11.0.2 to 11.0.3 (#534)
Bumps [glob](https://github.com/isaacs/node-glob) from 11.0.2 to 11.0.3.
- [Changelog](https://github.com/isaacs/node-glob/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/node-glob/compare/v11.0.2...v11.0.3)

---
updated-dependencies:
- dependency-name: glob
  dependency-version: 11.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-17 16:53:58 -04:00
dependabot[bot]
a262ce99ce Bump actions/checkout from 4 to 5 (#541)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-17 16:53:46 -04:00
Nick Cipollo
bcfe547070 preparing release 1.18.0
Some checks failed
Release / Create Release (push) Has been cancelled
Test / check_pr (push) Has been cancelled
2025-06-29 16:28:21 -04:00
Nick Cipollo
707331a88d Fixes #529 Collect asset URLs into output 2025-06-29 16:12:11 -04:00
Nick Cipollo
9128f238ee preparing release 1.17.0
Some checks failed
Release / Create Release (push) Has been cancelled
Test / check_pr (push) Has been cancelled
2025-06-28 21:32:06 -04:00
Nick Cipollo
7922049688 Fixes #529 Add zip and tarball urls to output 2025-06-28 21:23:15 -04:00
Nick Cipollo
0683ea3557 Cleanup outputs table formatting 2025-06-21 10:39:02 -04:00
Nick Cipollo
01c9fac73b Add a link to uploading assets in output table 2025-06-21 10:21:16 -04:00
dependabot[bot]
90dc22b77f Bump @types/node from 22.15.18 to 22.15.29 (#524)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.15.18 to 22.15.29.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 22.15.29
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-03 09:12:23 -04:00
dependabot[bot]
10585cb926 Bump @actions/github from 6.0.0 to 6.0.1 (#525)
Bumps [@actions/github](https://github.com/actions/toolkit/tree/HEAD/packages/github) from 6.0.0 to 6.0.1.
- [Changelog](https://github.com/actions/toolkit/blob/main/packages/github/RELEASES.md)
- [Commits](https://github.com/actions/toolkit/commits/HEAD/packages/github)

---
updated-dependencies:
- dependency-name: "@actions/github"
  dependency-version: 6.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-03 09:07:35 -04:00
dependabot[bot]
1896b4fc46 Bump ts-jest from 29.3.2 to 29.3.4 (#526)
Bumps [ts-jest](https://github.com/kulshekhar/ts-jest) from 29.3.2 to 29.3.4.
- [Release notes](https://github.com/kulshekhar/ts-jest/releases)
- [Changelog](https://github.com/kulshekhar/ts-jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/kulshekhar/ts-jest/compare/v29.3.2...v29.3.4)

---
updated-dependencies:
- dependency-name: ts-jest
  dependency-version: 29.3.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-03 09:05:36 -04:00
dependabot[bot]
6996c1bc95 Bump @types/node from 22.13.8 to 22.15.3 (#519)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.13.8 to 22.15.3.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 22.15.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-14 09:12:26 -04:00
dependabot[bot]
c07d979560 Bump glob from 11.0.1 to 11.0.2 (#520)
Bumps [glob](https://github.com/isaacs/node-glob) from 11.0.1 to 11.0.2.
- [Changelog](https://github.com/isaacs/node-glob/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/node-glob/compare/v11.0.1...v11.0.2)

---
updated-dependencies:
- dependency-name: glob
  dependency-version: 11.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-14 09:06:24 -04:00
dependabot[bot]
38da029bb7 Bump ts-jest from 29.2.6 to 29.3.2 (#521)
Bumps [ts-jest](https://github.com/kulshekhar/ts-jest) from 29.2.6 to 29.3.2.
- [Release notes](https://github.com/kulshekhar/ts-jest/releases)
- [Changelog](https://github.com/kulshekhar/ts-jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/kulshekhar/ts-jest/compare/v29.2.6...v29.3.2)

---
updated-dependencies:
- dependency-name: ts-jest
  dependency-version: 29.3.2
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-14 09:06:13 -04:00
dependabot[bot]
bf17a40975 Bump typescript from 5.7.3 to 5.8.3 (#518)
Bumps [typescript](https://github.com/microsoft/TypeScript) from 5.7.3 to 5.8.3.
- [Release notes](https://github.com/microsoft/TypeScript/releases)
- [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release-publish.yml)
- [Commits](https://github.com/microsoft/TypeScript/compare/v5.7.3...v5.8.3)

---
updated-dependencies:
- dependency-name: typescript
  dependency-version: 5.8.3
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-14 09:02:53 -04:00
Nick Cipollo
fa4749311e Fixes #513 Don't generate release notes if omitBody is set (#517) 2025-05-11 17:38:39 -04:00
dependabot[bot]
36e78ab629 Bump @octokit/request from 8.1.6 to 8.4.1 (#508)
Bumps [@octokit/request](https://github.com/octokit/request.js) from 8.1.6 to 8.4.1.
- [Release notes](https://github.com/octokit/request.js/releases)
- [Commits](https://github.com/octokit/request.js/compare/v8.1.6...v8.4.1)

---
updated-dependencies:
- dependency-name: "@octokit/request"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-06 23:30:41 -05:00
dependabot[bot]
6c1f9baed2 Bump @octokit/plugin-paginate-rest from 9.1.5 to 9.2.2 (#507)
Bumps [@octokit/plugin-paginate-rest](https://github.com/octokit/plugin-paginate-rest.js) from 9.1.5 to 9.2.2.
- [Release notes](https://github.com/octokit/plugin-paginate-rest.js/releases)
- [Commits](https://github.com/octokit/plugin-paginate-rest.js/compare/v9.1.5...v9.2.2)

---
updated-dependencies:
- dependency-name: "@octokit/plugin-paginate-rest"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-06 23:30:27 -05:00
dependabot[bot]
db62f3f3ad Bump @types/node from 22.13.4 to 22.13.8 (#505)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.13.4 to 22.13.8.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-06 23:28:47 -05:00
dependabot[bot]
e6992e14b1 Bump ts-jest from 29.2.5 to 29.2.6 (#506)
Bumps [ts-jest](https://github.com/kulshekhar/ts-jest) from 29.2.5 to 29.2.6.
- [Release notes](https://github.com/kulshekhar/ts-jest/releases)
- [Changelog](https://github.com/kulshekhar/ts-jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/kulshekhar/ts-jest/compare/v29.2.5...v29.2.6)

---
updated-dependencies:
- dependency-name: ts-jest
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-06 23:28:38 -05:00
Nick Cipollo
440c8c1cb0 preparing release 1.16.0
Some checks failed
Release / Create Release (push) Has been cancelled
Test / check_pr (push) Has been cancelled
2025-02-22 11:35:27 -05:00
Nick Cipollo
e805702217 preparing release 1.17.0 2025-02-22 11:33:08 -05:00
Nick Cipollo
bd572a1942 preparing release 1.17.0 2025-02-22 11:31:24 -05:00
Nick Cipollo
706b28e44c Update tag scheme in sheepit 2025-02-22 11:30:58 -05:00
Nick Cipollo
411ac011df preparing release 1.16.0 2025-02-22 11:26:00 -05:00
Radek Antoniuk
a8c5a7f252 Fixes #474 Regenerate release notes on release updates (#497) 2025-02-20 09:11:25 -05:00
Nick Cipollo
62f16c02e7 Add sheepit config 2025-02-17 17:54:01 -05:00
Nick Cipollo
00e83e3d35 Attempt #1 at fixing workflow file 2025-02-17 16:33:09 -05:00
Nick Cipollo
906fc77113 Add initial releases workflow 2025-02-17 16:29:52 -05:00
Nick Cipollo
17b559883e Add biome and initial format rules 2025-02-17 16:15:54 -05:00
Nick Cipollo
33bf18d283 Install dependency updates 2025-02-16 15:28:37 -05:00
dependabot[bot]
009df9782d Bump @types/node from 22.10.3 to 22.13.4 (#500)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.10.3 to 22.13.4.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-16 15:09:58 -05:00
dependabot[bot]
4b84ad5c60 Bump glob from 11.0.0 to 11.0.1 (#495)
Bumps [glob](https://github.com/isaacs/node-glob) from 11.0.0 to 11.0.1.
- [Changelog](https://github.com/isaacs/node-glob/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/node-glob/compare/v11.0.0...v11.0.1)

---
updated-dependencies:
- dependency-name: glob
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-16 14:56:48 -05:00
dependabot[bot]
34dae7e8c0 Bump undici from 5.28.4 to 5.28.5 (#499)
Bumps [undici](https://github.com/nodejs/undici) from 5.28.4 to 5.28.5.
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v5.28.4...v5.28.5)

---
updated-dependencies:
- dependency-name: undici
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-16 14:56:32 -05:00
Cycloctane
9fc271e109 update checkout action version in example (#496) 2025-02-16 14:56:13 -05:00
dependabot[bot]
3cfa40eca4 Bump @octokit/request-error from 5.0.1 to 5.1.1 (#498) 2025-02-15 07:44:36 -05:00
Cycloctane
cdcc88a9ac update Chinese README (#484)
Some checks failed
Test / check_pr (push) Has been cancelled
2025-01-11 21:57:03 -05:00
Nick Cipollo
033b82b409 Throw explicit error if list releases reponse doesn't have data 2025-01-11 21:52:42 -05:00
Nick Cipollo
e774b3eb81 Skip typescript checks 2025-01-11 21:34:25 -05:00
dependabot[bot]
80732ccbb8 Bump typescript from 5.5.4 to 5.7.3 (#490)
Bumps [typescript](https://github.com/microsoft/TypeScript) from 5.5.4 to 5.7.3.
- [Release notes](https://github.com/microsoft/TypeScript/releases)
- [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release.yml)
- [Commits](https://github.com/microsoft/TypeScript/compare/v5.5.4...v5.7.3)

---
updated-dependencies:
- dependency-name: typescript
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-11 21:23:24 -05:00
Nick Cipollo
8b72352976 Fix ArtifactUploader errors 2025-01-11 21:20:00 -05:00
dependabot[bot]
e1e10133db Bump @actions/core from 1.10.1 to 1.11.1 (#488)
Bumps [@actions/core](https://github.com/actions/toolkit/tree/HEAD/packages/core) from 1.10.1 to 1.11.1.
- [Changelog](https://github.com/actions/toolkit/blob/main/packages/core/RELEASES.md)
- [Commits](https://github.com/actions/toolkit/commits/HEAD/packages/core)

---
updated-dependencies:
- dependency-name: "@actions/core"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-11 21:11:20 -05:00
dependabot[bot]
7fdd38c8a8 Bump ts-jest from 29.2.4 to 29.2.5 (#464)
Bumps [ts-jest](https://github.com/kulshekhar/ts-jest) from 29.2.4 to 29.2.5.
- [Release notes](https://github.com/kulshekhar/ts-jest/releases)
- [Changelog](https://github.com/kulshekhar/ts-jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/kulshekhar/ts-jest/compare/v29.2.4...v29.2.5)

---
updated-dependencies:
- dependency-name: ts-jest
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-11 21:07:20 -05:00
dependabot[bot]
fb175cca64 Bump @types/jest from 29.5.12 to 29.5.14 (#476)
Bumps [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) from 29.5.12 to 29.5.14.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest)

---
updated-dependencies:
- dependency-name: "@types/jest"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-11 21:06:53 -05:00
dependabot[bot]
c4d491c3c9 Bump @types/node from 22.0.2 to 22.10.3 (#487)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.0.2 to 22.10.3.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-11 21:04:25 -05:00
dependabot[bot]
aa3b2fa675 Bump micromatch from 4.0.4 to 4.0.8 (#463)
Bumps [micromatch](https://github.com/micromatch/micromatch) from 4.0.4 to 4.0.8.
- [Release notes](https://github.com/micromatch/micromatch/releases)
- [Changelog](https://github.com/micromatch/micromatch/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/micromatch/compare/4.0.4...4.0.8)

---
updated-dependencies:
- dependency-name: micromatch
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-03 15:52:42 -05:00
dependabot[bot]
215ba89ed6 Bump cross-spawn from 7.0.3 to 7.0.6 (#480)
Bumps [cross-spawn](https://github.com/moxystudio/node-cross-spawn) from 7.0.3 to 7.0.6.
- [Changelog](https://github.com/moxystudio/node-cross-spawn/blob/master/CHANGELOG.md)
- [Commits](https://github.com/moxystudio/node-cross-spawn/compare/v7.0.3...v7.0.6)

---
updated-dependencies:
- dependency-name: cross-spawn
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-19 14:59:01 -05:00
dependabot[bot]
a8bcd956fb Bump @types/node from 20.12.2 to 22.0.2 (#458)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 20.12.2 to 22.0.2.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-01 08:20:52 -04:00
dependabot[bot]
bb3709bff2 Bump ts-jest from 29.1.2 to 29.2.4 (#460)
Bumps [ts-jest](https://github.com/kulshekhar/ts-jest) from 29.1.2 to 29.2.4.
- [Release notes](https://github.com/kulshekhar/ts-jest/releases)
- [Changelog](https://github.com/kulshekhar/ts-jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/kulshekhar/ts-jest/compare/v29.1.2...v29.2.4)

---
updated-dependencies:
- dependency-name: ts-jest
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-01 08:20:35 -04:00
dependabot[bot]
e9a92928b6 Bump typescript from 5.4.3 to 5.5.4 (#459)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 5.4.3 to 5.5.4.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release.yml)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v5.4.3...v5.5.4)

---
updated-dependencies:
- dependency-name: typescript
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-01 07:28:44 -04:00
dependabot[bot]
765cc979be Bump glob from 10.3.12 to 11.0.0 (#457)
Bumps [glob](https://github.com/isaacs/node-glob) from 10.3.12 to 11.0.0.
- [Changelog](https://github.com/isaacs/node-glob/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/node-glob/compare/v10.3.12...v11.0.0)

---
updated-dependencies:
- dependency-name: glob
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-01 07:28:27 -04:00
dependabot[bot]
00fc285722 Bump braces from 3.0.2 to 3.0.3 (#449) 2024-06-16 07:55:48 -04:00
dependabot[bot]
1cbdc80532 Bump glob from 10.3.10 to 10.3.12 (#436)
Bumps [glob](https://github.com/isaacs/node-glob) from 10.3.10 to 10.3.12.
- [Changelog](https://github.com/isaacs/node-glob/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/node-glob/compare/v10.3.10...v10.3.12)

---
updated-dependencies:
- dependency-name: glob
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-18 10:30:20 -04:00
dependabot[bot]
4b91e3add1 Bump @types/node from 20.11.24 to 20.12.2 (#437)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 20.11.24 to 20.12.2.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-16 14:56:57 -04:00
dependabot[bot]
b6d6ead779 Bump typescript from 5.3.3 to 5.4.3 (#438)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 5.3.3 to 5.4.3.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release.yml)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v5.3.3...v5.4.3)

---
updated-dependencies:
- dependency-name: typescript
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-16 14:56:49 -04:00
dependabot[bot]
1da2ee60a2 Bump undici from 5.28.3 to 5.28.4 (#439)
Bumps [undici](https://github.com/nodejs/undici) from 5.28.3 to 5.28.4.
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v5.28.3...v5.28.4)

---
updated-dependencies:
- dependency-name: undici
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-16 14:56:38 -04:00
Nick Cipollo
4f8867fa5e Update body documentation in readme 2024-03-07 11:36:06 -05:00
Nick Cipollo
f4b7f82f58 Create main build with most recent dependencies. 2024-03-07 09:38:03 -05:00
dependabot[bot]
4074199da7 Bump ts-jest from 29.1.1 to 29.1.2 (#409) 2024-03-07 07:29:30 -05:00
dependabot[bot]
578e4141d1 Bump @types/node from 20.10.6 to 20.11.24 (#426)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 20.10.6 to 20.11.24.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-07 07:29:17 -05:00
dependabot[bot]
a36404d62c Bump @types/jest from 29.5.11 to 29.5.12 (#427) 2024-03-07 07:28:59 -05:00
dependabot[bot]
7588f628d1 Bump undici from 5.28.2 to 5.28.3 (#423) 2024-03-01 19:25:28 -05:00
Nick Cipollo
1e3e9c6637 Fix production build error 2024-02-05 18:39:33 -05:00
Nick Cipollo
c03240caba apply debug build 2024-02-05 17:36:37 -05:00
Andrew Ovens
66b1844f0b Upgrade to Node 20 (#411) 2024-02-05 17:30:56 -05:00
dependabot[bot]
a8aa04e74a Bump @types/node from 20.10.1 to 20.10.6 (#402)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 20.10.1 to 20.10.6.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-02 08:48:54 -05:00
dependabot[bot]
39c57feb7b Bump typescript from 5.3.2 to 5.3.3 (#403)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 5.3.2 to 5.3.3.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v5.3.2...v5.3.3)

---
updated-dependencies:
- dependency-name: typescript
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-02 08:48:45 -05:00
Nick Cipollo
02a91b5ddd Fix build errors from github api update 2023-12-11 08:37:08 -05:00
dependabot[bot]
0300282a53 Bump @actions/github from 5.1.1 to 6.0.0 (#385)
Bumps [@actions/github](https://github.com/actions/toolkit/tree/HEAD/packages/github) from 5.1.1 to 6.0.0.
- [Changelog](https://github.com/actions/toolkit/blob/main/packages/github/RELEASES.md)
- [Commits](https://github.com/actions/toolkit/commits/HEAD/packages/github)

---
updated-dependencies:
- dependency-name: "@actions/github"
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-11 08:28:16 -05:00
Nick Cipollo
c4a7702e18 Add more info about announcements category 2023-12-11 08:25:38 -05:00
Nick Cipollo
4f53bc4bbe Add discussionCategory caveat to readme 2023-12-11 08:22:58 -05:00
dependabot[bot]
37813b746a Bump @types/jest from 29.5.7 to 29.5.11 (#399)
Bumps [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) from 29.5.7 to 29.5.11.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest)

---
updated-dependencies:
- dependency-name: "@types/jest"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-11 08:18:00 -05:00
dependabot[bot]
b878ec7b82 Bump @types/node from 20.9.0 to 20.10.1 (#395)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 20.9.0 to 20.10.1.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-11 08:15:39 -05:00
dependabot[bot]
3453d72d33 Bump typescript from 5.2.2 to 5.3.2 (#396)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 5.2.2 to 5.3.2.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v5.2.2...v5.3.2)

---
updated-dependencies:
- dependency-name: typescript
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-11 08:15:30 -05:00
dependabot[bot]
712dbe3d31 Bump @types/node from 20.8.6 to 20.9.0 (#390)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 20.8.6 to 20.9.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-16 13:46:47 -05:00
dependabot[bot]
a454b0181b Bump actions/setup-node from 3 to 4 (#381)
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 3 to 4.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-15 09:04:39 -05:00
dependabot[bot]
454de296e8 Bump @types/jest from 29.5.5 to 29.5.7 (#386)
Bumps [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) from 29.5.5 to 29.5.7.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest)

---
updated-dependencies:
- dependency-name: "@types/jest"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-15 09:04:28 -05:00
Nick Cipollo
823125756f Try and force jest-cli/yargs resolution 2023-10-17 19:15:34 -04:00
Nick Cipollo
53f1c32531 try to bump glob again 2023-10-17 19:09:16 -04:00
Nick Cipollo
7b2b452a12 update build workflow 2023-10-17 18:42:43 -04:00
Nick Cipollo
a7c3669135 Revert "Bump glob"
This reverts commit 5a5f227332.
2023-10-17 18:41:23 -04:00
Nick Cipollo
26cdd97dfd Use node 20 2023-10-17 18:36:46 -04:00
Nick Cipollo
5a5f227332 Bump glob 2023-10-17 18:33:40 -04:00
Nick Cipollo
9324133e56 Update typescript config 2023-10-17 18:21:18 -04:00
Nick Cipollo
25918b039f Build main with 10/2023 dependecy updates 2023-10-17 18:09:44 -04:00
dependabot[bot]
c18db47aba Bump @actions/core from 1.10.0 to 1.10.1 (#375) 2023-10-17 17:13:18 -04:00
dependabot[bot]
d968c2d7a7 Bump @types/node from 20.6.0 to 20.8.6 (#379) 2023-10-17 17:12:54 -04:00
dependabot[bot]
e386e41d9f Bump jest-circus from 29.6.4 to 29.7.0 (#376) 2023-10-17 17:12:35 -04:00
dependabot[bot]
476ee61234 Bump jest and @types/jest (#377) 2023-10-17 16:42:50 -04:00
dependabot[bot]
6db48ced34 Bump @babel/traverse from 7.17.3 to 7.23.2 (#378) 2023-10-17 16:17:57 -04:00
Nick Cipollo
7ce102b15b Build (dev) with dependency updates 2023-09-11 08:10:29 -04:00
dependabot[bot]
ddbfce0d45 Bump jest and @types/jest (#367)
Bumps [jest](https://github.com/jestjs/jest/tree/HEAD/packages/jest) and [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest). These dependencies needed to be updated together.

Updates `jest` from 29.5.0 to 29.6.4
- [Release notes](https://github.com/jestjs/jest/releases)
- [Changelog](https://github.com/jestjs/jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jestjs/jest/commits/v29.6.4/packages/jest)

Updates `@types/jest` from 29.5.2 to 29.5.4
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest)

---
updated-dependencies:
- dependency-name: jest
  dependency-type: direct:development
  update-type: version-update:semver-minor
- dependency-name: "@types/jest"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-11 08:10:12 -04:00
dependabot[bot]
b4b70f8196 Bump @types/node from 20.3.3 to 20.6.0 (#366)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 20.3.3 to 20.6.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-11 08:08:18 -04:00
dependabot[bot]
2600a44651 Bump typescript from 5.1.6 to 5.2.2 (#362)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 5.1.6 to 5.2.2.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v5.1.6...v5.2.2)

---
updated-dependencies:
- dependency-name: typescript
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-11 08:05:57 -04:00
dependabot[bot]
eb7dfdf4be Bump jest-circus from 29.5.0 to 29.6.4 (#359)
Bumps [jest-circus](https://github.com/jestjs/jest/tree/HEAD/packages/jest-circus) from 29.5.0 to 29.6.4.
- [Release notes](https://github.com/jestjs/jest/releases)
- [Changelog](https://github.com/jestjs/jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jestjs/jest/commits/v29.6.4/packages/jest-circus)

---
updated-dependencies:
- dependency-name: jest-circus
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-11 08:04:32 -04:00
dependabot[bot]
e2ce571994 Bump glob from 10.3.1 to 10.3.4 (#361)
Bumps [glob](https://github.com/isaacs/node-glob) from 10.3.1 to 10.3.4.
- [Changelog](https://github.com/isaacs/node-glob/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/node-glob/compare/v10.3.1...v10.3.4)

---
updated-dependencies:
- dependency-name: glob
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-11 08:04:18 -04:00
dependabot[bot]
69d25a3184 Bump actions/checkout from 3 to 4 (#364)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-11 08:03:59 -04:00
dependabot[bot]
b36d9409a9 Bump semver from 6.3.0 to 6.3.1 (#350)
Bumps [semver](https://github.com/npm/node-semver) from 6.3.0 to 6.3.1.
- [Release notes](https://github.com/npm/node-semver/releases)
- [Changelog](https://github.com/npm/node-semver/blob/v6.3.1/CHANGELOG.md)
- [Commits](https://github.com/npm/node-semver/compare/v6.3.0...v6.3.1)

---
updated-dependencies:
- dependency-name: semver
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-26 14:55:34 -04:00
Nick Cipollo
6c75be85e5 Fixes #357 Use a read stream for artifacts.
Some checks failed
Test / check_pr (push) Has been cancelled
This seems to be safe since octokit mentions ReadableStreams in their fetcher wrapper & tests: https://github.com/octokit/request.js/blob/main/src/fetch-wrapper.ts#L44
(and also it seems to work when I test it).
2023-08-24 08:37:01 -04:00
Nick Cipollo
2d19fb459b Remove lib 2023-08-24 07:40:06 -04:00
Nick Cipollo
9cfd0835c6 Remove explicit typescript src setting 2023-08-24 07:39:05 -04:00
Nick Cipollo
37c87f6b53 Fixes #349 Copy description of makeLatest legacy from github docs 2023-07-10 17:35:27 -04:00
Nick Cipollo
7288236d4e build dep updates 2023-07-01 11:32:09 -04:00
dependabot[bot]
384ae1fad6 Bump @types/node from 18.15.11 to 20.3.3 (#341)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 18.15.11 to 20.3.3.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-01 11:31:20 -04:00
dependabot[bot]
9347837845 Bump typescript from 5.0.4 to 5.1.6 (#343)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 5.0.4 to 5.1.6.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/commits)

---
updated-dependencies:
- dependency-name: typescript
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-01 11:31:00 -04:00
dependabot[bot]
893ce36b2e Bump glob from 10.2.6 to 10.3.1 (#342)
Bumps [glob](https://github.com/isaacs/node-glob) from 10.2.6 to 10.3.1.
- [Changelog](https://github.com/isaacs/node-glob/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/node-glob/compare/v10.2.6...v10.3.1)

---
updated-dependencies:
- dependency-name: glob
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-01 10:43:17 -04:00
dependabot[bot]
c07e9f4dff Bump ts-jest from 29.1.0 to 29.1.1 (#345)
Bumps [ts-jest](https://github.com/kulshekhar/ts-jest) from 29.1.0 to 29.1.1.
- [Release notes](https://github.com/kulshekhar/ts-jest/releases)
- [Changelog](https://github.com/kulshekhar/ts-jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/kulshekhar/ts-jest/compare/v29.1.0...v29.1.1)

---
updated-dependencies:
- dependency-name: ts-jest
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-01 10:42:09 -04:00
Nick Cipollo
cb77c9ee9c Revert "Bump @types/node from 18.15.11 to 20.3.2 (#340)"
This reverts commit 8c78ca2662.
2023-06-27 15:40:20 -04:00
dependabot[bot]
8c78ca2662 Bump @types/node from 18.15.11 to 20.3.2 (#340)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 18.15.11 to 20.3.2.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-27 15:25:21 -04:00
dependabot[bot]
2b385230bf Bump glob from 10.2.2 to 10.2.6 (#339)
Bumps [glob](https://github.com/isaacs/node-glob) from 10.2.2 to 10.2.6.
- [Changelog](https://github.com/isaacs/node-glob/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/node-glob/compare/v10.2.2...v10.2.6)

---
updated-dependencies:
- dependency-name: glob
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-27 15:20:29 -04:00
dependabot[bot]
3ff6c7eb03 Bump @types/jest from 29.5.1 to 29.5.2 (#337)
Bumps [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) from 29.5.1 to 29.5.2.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest)

---
updated-dependencies:
- dependency-name: "@types/jest"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-27 15:20:07 -04:00
dependabot[bot]
3aa9307b7d Bump typescript from 5.0.3 to 5.0.4 (#327)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 5.0.3 to 5.0.4.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v5.0.3...v5.0.4)

---
updated-dependencies:
- dependency-name: typescript
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-27 15:19:58 -04:00
dependabot[bot]
c13ca79f19 Bump ts-jest from 29.0.5 to 29.1.0 (#329)
Bumps [ts-jest](https://github.com/kulshekhar/ts-jest) from 29.0.5 to 29.1.0.
- [Release notes](https://github.com/kulshekhar/ts-jest/releases)
- [Changelog](https://github.com/kulshekhar/ts-jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/kulshekhar/ts-jest/compare/v29.0.5...v29.1.0)

---
updated-dependencies:
- dependency-name: ts-jest
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-23 09:59:29 -04:00
dependabot[bot]
e9a4ba6596 Bump glob from 9.3.2 to 10.2.2 (#330)
Bumps [glob](https://github.com/isaacs/node-glob) from 9.3.2 to 10.2.2.
- [Release notes](https://github.com/isaacs/node-glob/releases)
- [Changelog](https://github.com/isaacs/node-glob/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/node-glob/compare/v9.3.2...v10.2.2)

---
updated-dependencies:
- dependency-name: glob
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-23 09:58:48 -04:00
dependabot[bot]
ef679c1c54 Bump @types/jest from 29.5.0 to 29.5.1 (#326)
Bumps [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) from 29.5.0 to 29.5.1.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest)

---
updated-dependencies:
- dependency-name: "@types/jest"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-23 09:58:33 -04:00
Nick Cipollo
eb05307dce Fix stupid mocking error 2023-04-01 18:12:05 -04:00
Nick Cipollo
5a8bbe8ea7 Try and fix weird build error 2023-04-01 17:33:40 -04:00
dependabot[bot]
a48ee3f85e Bump glob from 8.1.0 to 9.3.2 (#321)
Bumps [glob](https://github.com/isaacs/node-glob) from 8.1.0 to 9.3.2.
- [Release notes](https://github.com/isaacs/node-glob/releases)
- [Changelog](https://github.com/isaacs/node-glob/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/node-glob/compare/v8.1.0...v9.3.2)

---
updated-dependencies:
- dependency-name: glob
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-01 17:10:28 -04:00
dependabot[bot]
bf3b51b3f3 Bump typescript from 4.9.5 to 5.0.3 (#320)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.9.5 to 5.0.3.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/commits)

---
updated-dependencies:
- dependency-name: typescript
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-01 17:09:42 -04:00
Spencer C
67d60d576c Readme: update checkout to v3 (#303)
Update checkout to v3 which is the latest version and encourage people to use it when creating a release

Co-authored-by: Spencer Comfort <109806759+GiddyGoatGaming@users.noreply.github.com>
2023-04-01 17:09:32 -04:00
dependabot[bot]
0371ac0a22 Bump @types/node from 18.11.18 to 18.15.11 (#319)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 18.11.18 to 18.15.11.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-01 17:09:09 -04:00
dependabot[bot]
231dd3fbcd Bump jest-circus from 29.4.1 to 29.5.0 (#318)
Bumps [jest-circus](https://github.com/facebook/jest/tree/HEAD/packages/jest-circus) from 29.4.1 to 29.5.0.
- [Release notes](https://github.com/facebook/jest/releases)
- [Changelog](https://github.com/facebook/jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/jest/commits/v29.5.0/packages/jest-circus)

---
updated-dependencies:
- dependency-name: jest-circus
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-01 17:08:56 -04:00
dependabot[bot]
d51f1872a4 Bump jest-circus from 29.3.1 to 29.4.1 (#306)
Bumps [jest-circus](https://github.com/facebook/jest/tree/HEAD/packages/jest-circus) from 29.3.1 to 29.4.1.
- [Release notes](https://github.com/facebook/jest/releases)
- [Changelog](https://github.com/facebook/jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/jest/commits/v29.4.1/packages/jest-circus)

---
updated-dependencies:
- dependency-name: jest-circus
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-23 22:04:06 -05:00
dependabot[bot]
324f6f4a19 Bump typescript from 4.9.4 to 4.9.5 (#305)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.9.4 to 4.9.5.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v4.9.4...v4.9.5)

---
updated-dependencies:
- dependency-name: typescript
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-23 22:03:47 -05:00
dependabot[bot]
ef3e5b02bd Bump glob and @types/glob (#304)
Bumps [glob](https://github.com/isaacs/node-glob) and [@types/glob](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/glob). These dependencies needed to be updated together.

Updates `glob` from 8.0.3 to 8.1.0
- [Release notes](https://github.com/isaacs/node-glob/releases)
- [Changelog](https://github.com/isaacs/node-glob/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/node-glob/compare/v8.0.3...v8.1.0)

Updates `@types/glob` from 8.0.0 to 8.0.1
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/glob)

---
updated-dependencies:
- dependency-name: glob
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: "@types/glob"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-23 22:03:27 -05:00
Nick Cipollo
215101a6e6 Fixes #301 Add remediation suggestion to 404 errors 2023-01-28 13:58:46 -05:00
dependabot[bot]
a5996b3a73 Bump @types/node from 18.11.10 to 18.11.18 (#292)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 18.11.10 to 18.11.18.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-08 20:28:13 -05:00
dependabot[bot]
8fd54fa522 Bump typescript from 4.9.3 to 4.9.4 (#293)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.9.3 to 4.9.4.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v4.9.3...v4.9.4)

---
updated-dependencies:
- dependency-name: typescript
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-08 20:28:02 -05:00
dependabot[bot]
c358143117 Bump json5 from 2.2.1 to 2.2.3 (#296)
Bumps [json5](https://github.com/json5/json5) from 2.2.1 to 2.2.3.
- [Release notes](https://github.com/json5/json5/releases)
- [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md)
- [Commits](https://github.com/json5/json5/compare/v2.2.1...v2.2.3)

---
updated-dependencies:
- dependency-name: json5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-08 20:27:55 -05:00
Nick Cipollo
a2e71bdd4e Update "makeLatest" so it's always defined
Some checks failed
Test / check_pr (push) Has been cancelled
2022-12-11 13:56:37 -05:00
Nick Cipollo
78ea7b5528 Build main 2022-12-11 13:53:05 -05:00
dependabot[bot]
3898f7abfd Bump jest-circus from 29.2.2 to 29.3.1 (#280)
Bumps [jest-circus](https://github.com/facebook/jest/tree/HEAD/packages/jest-circus) from 29.2.2 to 29.3.1.
- [Release notes](https://github.com/facebook/jest/releases)
- [Changelog](https://github.com/facebook/jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/jest/commits/v29.3.1/packages/jest-circus)

---
updated-dependencies:
- dependency-name: jest-circus
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-11 13:49:04 -05:00
dependabot[bot]
2afcaec515 Bump @types/node from 18.11.8 to 18.11.10 (#281)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 18.11.8 to 18.11.10.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-11 13:48:55 -05:00
dependabot[bot]
2954d914c0 Bump typescript from 4.8.4 to 4.9.3 (#282)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.8.4 to 4.9.3.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v4.8.4...v4.9.3)

---
updated-dependencies:
- dependency-name: typescript
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-11 13:48:46 -05:00
Eric Cornelissen
0bf6967166 Add make_latest option (#283) 2022-12-11 13:45:00 -05:00
Nick Cipollo
3dd806f2bf Fix typo in readme 2022-11-25 13:42:27 -05:00
Nick Cipollo
443f2c0c79 Build with security updates 2022-11-11 13:54:21 -05:00
dependabot[bot]
94ba3874ff Bump minimatch from 3.0.4 to 3.1.2 (#278)
Bumps [minimatch](https://github.com/isaacs/minimatch) from 3.0.4 to 3.1.2.
- [Release notes](https://github.com/isaacs/minimatch/releases)
- [Commits](https://github.com/isaacs/minimatch/compare/v3.0.4...v3.1.2)

---
updated-dependencies:
- dependency-name: minimatch
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-11 13:50:46 -05:00
dependabot[bot]
c30ce84c21 Bump jest-circus from 29.1.2 to 29.2.2 (#275)
Bumps [jest-circus](https://github.com/facebook/jest/tree/HEAD/packages/jest-circus) from 29.1.2 to 29.2.2.
- [Release notes](https://github.com/facebook/jest/releases)
- [Changelog](https://github.com/facebook/jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/jest/commits/v29.2.2/packages/jest-circus)

---
updated-dependencies:
- dependency-name: jest-circus
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-11 13:48:23 -05:00
dependabot[bot]
1830b037be Bump @types/node from 18.7.23 to 18.11.8 (#276)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 18.7.23 to 18.11.8.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-11 13:48:06 -05:00
Nick Cipollo
3bacd9e49a Fixes #267 Add ActionSkipper 2022-11-11 13:46:22 -05:00
Misite Bao
3ac4132803 Add Chinese README (#269) 2022-10-24 17:19:37 -04:00
Nick Cipollo
b072aaafe1 update debug build 2022-10-04 08:09:17 -04:00
Nick Cipollo
f3ea29dca7 Fixes #262 normalize artifact paths 2022-10-04 08:08:09 -04:00
Nick Cipollo
889eb279f8 Fixes #261 Correctly set default on updateOnlyUnreleased 2022-10-02 18:19:43 -04:00
Nick Cipollo
5b3ed26ce2 Fixes #219 Add argument to allow only updating drafts and prereleases 2022-10-02 17:35:42 -04:00
60 changed files with 40326 additions and 13162 deletions

View File

@@ -11,11 +11,15 @@ jobs:
check_pr:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v5
- uses: actions/setup-node@v4
with:
node-version: 20
- name: "yarn install"
run: yarn install
- name: "yarn build"
run: yarn build

22
.github/workflows/release.yml vendored Normal file
View 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@v5
- name: Create Release
id: create_release
uses: ncipollo/release-action@v1
with:
allowUpdates: true
draft: true
generateReleaseNotes: true

View File

@@ -5,7 +5,11 @@ jobs:
check_pr:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v5
- uses: actions/setup-node@v4
with:
node-version: 20
- name: "yarn install"
run: yarn install

6
.gitignore vendored
View File

@@ -1,5 +1,6 @@
node_modules/
__tests__/runner/*
.DS_Store
# Created by https://www.gitignore.io/api/webstorm
# Edit at https://www.gitignore.io/?templates=webstorm
@@ -95,4 +96,7 @@ fabric.properties
# End of https://www.gitignore.io/api/webstorm
# Coverage
coverage
coverage
# Ignore lib, it contains intermediates
/lib

6
.idea/biome.xml generated Normal file
View 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>

View File

@@ -2,40 +2,57 @@
This action will create a GitHub release and optionally upload an artifact to it.
<div align="center">
<strong>
<samp>
[English](README.md) · [简体中文](README.zh-Hans.md)
</samp>
</strong>
</div>
## Action Inputs
| 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 | "" |
| 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 | "" |
| artifactContentType | The content type of the artifact. Defaults to raw | false | "" |
| body | An optional body for the release. | 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 | "" |
| 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 | "" |
| 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 |
| name | An optional name for the release. If this is omitted the tag will be used. | false | "" |
| omitBody | Indicates if the release body 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 |
| 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 |
| omitName | Indicates if the release name should be omitted. | 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 |
| 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 |
| owner | Optionally specify the owner of the repo where the release should be generated. Defaults to current repo's owner. | false | "current repo owner" |
| prerelease | Optionally marks this release as prerelease. Set to true to enable. | false | "" |
| removeArtifacts | Indicates if existing release artifacts should be removed. | false | false |
| replacesArtifacts | Indicates if existing release artifacts should be replaced. | false | true |
| repo | Optionally specify the repo where the release should be generated. | false | current repo |
| 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 |
| 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 | "" |
| 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 | "" |
| artifactContentType | The content type of the artifact. Defaults to raw | 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 | "" |
| 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. 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 | "" |
| generateReleaseNotes | Indicates if release notes should be automatically generated. | false | 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 |
| 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" |
| name | An optional name for the release. If this is omitted the tag will be used. | false | "" |
| omitBody | Indicates if the release body 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 |
| 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 |
| omitName | Indicates if the release name should be omitted. | 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 |
| 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 |
| owner | Optionally specify the owner of the repo where the release should be generated. Defaults to current repo's owner. | false | "current repo owner" |
| prerelease | Optionally marks this release as prerelease. Set to true to enable. | false | "" |
| removeArtifacts | Indicates if existing release artifacts should be removed. | false | false |
| replacesArtifacts | Indicates if existing release artifacts should be replaced. | false | true |
| repo | Optionally specify the repo where the release should be generated. | false | current repo |
| skipIfReleaseExists | When skipIfReleaseExists is enabled the action will be skipped if a non-draft release already exists for the provided tag. | 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
| Output name | Description |
|-------------|-----------------------------------------------|
| id | The identifier of the created 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
This example will create a release when a tag is pushed:
@@ -55,16 +72,15 @@ jobs:
permissions:
contents: write
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- uses: ncipollo/release-action@v1
with:
artifacts: "release.tar.gz,foo/*.txt"
bodyFile: "body.md"
token: ${{ secrets.YOUR_GITHUB_TOKEN }}
```
## Notes
- 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`).
- 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.

87
README.zh-Hans.md Normal file
View File

@@ -0,0 +1,87 @@
# Release Action
此操作将创建一个 GitHub Release并可选择将产出文件上传到其中。
<div align="center">
<strong>
<samp>
[English](README.md) · [简体中文](README.zh-Hans.md)
</samp>
</strong>
</div>
## Action 输入
| 输入名称 | 描述 | 必选 | 默认值 |
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | -------------------- |
| allowUpdates | 一个可选标志,表示如果版本已经存在,我们是否应该更新它。默认值为 false。 | false | "" |
| artifactErrorsFailBuild | 一个可选标志,表示读取或上传产出文件错误时是否应该使构建失败。 | false | "" |
| artifacts | 一组可选的路径,表示要上传到版本的产出文件。 这可能是单个路径或以逗号分隔的路径列表(或 globs | false | "" |
| artifactContentType | 产出文件的内容类型。 默认为 raw | false | "" |
| body | 发布的可选主体。注意:此处输入的首尾空格会被去除。如果您需要特殊的 markdown 内容,请使用 `bodyFile`。 | false | "" |
| bodyFile | 发布的可选正文文件。 这应该是文件的路径。 | false | "" |
| commit | 一个可选的提交 ref。 如果标签不存在,将用于创建标签。 | false | "" |
| discussionCategory | 当提供该选项时,将生成指定类别的 discussion。指定的类别必须存在否则将导致 Action 失败。这对草案状态的发布不生效。API 不支持默认的 Announcement 分类,使用这个分类会引发错误。 | false | "" |
| draft | 可选择将此版本标记为草稿版本。 设置为 true 以启用。 | false | "" |
| generateReleaseNotes | 指示是否应自动生成发行说明。 | false | false |
| immutableCreate | 指示是否应使用不可变发布创建。启用时,操作将首先创建草稿,上传产出文件,然后发布版本。 | false | false |
| makeLatest | 指示是否将这个发布设置为 latest。使用"lagecy"值则会根据发布时间和语义化版本号自动决定。 | false | "legacy" |
| name | 版本的可选名称。 如果省略,将使用标签。 | false | "" |
| omitBody | 指示是否应省略发布主体。 | false | false |
| omitBodyDuringUpdate | 指示在更新期间是否应省略发布主体。 正文仍将应用于新创建的版本。 这将在更新期间保留现有正文。 | false | false |
| omitDraftDuringUpdate | 指示是否应在更新期间省略草稿标志。 草稿标志仍将应用于新创建的版本。 这将在更新期间保留现有的草稿状态。 | false | false |
| omitName | 指示是否应省略版本名称。 | false | false |
| omitNameDuringUpdate | 指示在更新期间是否应省略版本名称。 该名称仍将应用于新创建的版本。 这将在更新期间保留现有名称。 | false | false |
| omitPrereleaseDuringUpdate | 指示在更新期间是否应省略预发布标志。 预发布标志仍将应用于新创建的版本。 这将在更新期间保留现有的预发布状态。 | false | false |
| owner | (可选)指定应在其中生成版本的存储库的所有者。 默认为当前存储库的所有者。 | false | "current repo owner" |
| prerelease | 可选择将此版本标记为预发布。 设置为 true 以启用。 | false | "" |
| removeArtifacts | 指示是否应删除现有的发布产出文件。 | false | false |
| replacesArtifacts | 指示是否应替换现有的发布产出文件。 | false | true |
| repo | (可选)指定应在其中生成版本的存储库。 | false | current repo |
| skipIfReleaseExists | 当该选项启用时,如果提供的标签已存在一个对应的非草案状态的发布,则 action 将会被跳过。 | false | false |
| tag | 发布的可选标签。 如果省略,将使用 git ref (如果它是标签)。 | false | "" |
| token | GitHub 令牌。 这将默认为 GitHub 应用程序令牌。 如果您想使用您的个人令牌(用于定位其他存储库等),这主要是有用的。 如果您使用的是个人访问令牌,它应该可以访问 `repo` 范围。 | false | github.token |
| updateOnlyUnreleased | 启用 allowUpdates 后,如果它正在更新的版本不是草稿或预发布,则该操作将失败。 | false | false |
## Action 输出
| 输出名称 | 描述 |
| ---------- | ------------------------ |
| id | 创建的版本的标识符。 |
| html_url | 版本的 HTML URL。 |
| upload_url | 将资产上传到版本的 URL。 |
## 示例
此示例将在推送一个标签时创建一个 Release
```yml
name: Releases
on:
push:
tags:
- '*'
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- uses: ncipollo/release-action@v1
with:
artifacts: "release.tar.gz,foo/*.txt"
bodyFile: "body.md"
```
## 注意
- 您必须通过 Action 输入或 git ref 提供一个标签(即推送/创建标签。如果不提供标签Action 将会失败。
- 如果您正在创建的版本的标签不存在,您应该同时设置标签和提交 Action 输入。 commit 可以指向提交 Hash 或分支名称(例如 - main
- 在上面的示例中,只需要指定操作的权限(即 contents: write。 如果您将其他操作添加到同一工作流程,则应相应地扩展权限。
- 有关于此 action 无法支持 discussion 的 Announcement 分类的原因的更多信息请参见此处https://github.com/goreleaser/goreleaser/issues/2304

View File

@@ -1,156 +1,290 @@
import {Action} from "../src/Action";
import {Artifact} from "../src/Artifact";
import {Inputs} from "../src/Inputs";
import {Releases} from "../src/Releases";
import {ArtifactUploader} from "../src/ArtifactUploader";
import {Outputs} from "../src/Outputs";
import {ArtifactDestroyer} from "../src/ArtifactDestroyer";
import { Action } from "../src/Action"
import type { ActionSkipper } from "../src/ActionSkipper"
import { Artifact } from "../src/Artifact"
import type { ArtifactDestroyer } from "../src/ArtifactDestroyer"
import type { ArtifactUploader } from "../src/ArtifactUploader"
import type { Inputs } from "../src/Inputs"
import type { Outputs } from "../src/Outputs"
import type { Releases } from "../src/Releases"
const TEST_URLS = {
UPLOAD_URL: "http://api.example.com",
HTML_URL: "https://github.com/owner/repo/releases/tag/v1.0.0",
TARBALL_URL: "https://api.github.com/repos/owner/repo/tarball/v1.0.0",
ZIPBALL_URL: "https://api.github.com/repos/owner/repo/zipball/v1.0.0",
} as const
const applyReleaseDataMock = jest.fn()
const applyAssetUrlsMock = jest.fn()
const artifactDestroyMock = jest.fn()
const createMock = jest.fn()
const deleteMock = jest.fn()
const getMock = jest.fn()
const listArtifactsMock = jest.fn()
const listMock = jest.fn()
const shouldSkipMock = jest.fn()
const updateMock = jest.fn()
const uploadMock = jest.fn()
const artifactDestroyMock = jest.fn()
const genReleaseNotesMock = jest.fn()
const artifacts = [
new Artifact('a/art1'),
new Artifact('b/art2')
]
const artifacts = [new Artifact("a/art1"), new Artifact("b/art2")]
const createBody = 'createBody'
const createBody = "createBody"
const createDraft = true
const createName = 'createName'
const commit = 'commit'
const discussionCategory = 'discussionCategory'
const createName = "createName"
const commit = "commit"
const discussionCategory = "discussionCategory"
const generateReleaseNotes = true
const id = 100
const createPrerelease = true
const releaseId = 101
const replacesArtifacts = true
const tag = 'tag'
const token = 'token'
const updateBody = 'updateBody'
const tag = "tag"
const token = "token"
const updateBody = "updateBody"
const updateDraft = false
const updateName = 'updateName'
const updateName = "updateName"
const updatePrerelease = false
const url = 'http://api.example.com'
const updateOnlyUnreleased = false
const url = TEST_URLS.UPLOAD_URL
const makeLatest = "legacy"
const generatedReleaseBody = "test release notes"
describe("Action", () => {
beforeEach(() => {
applyReleaseDataMock.mockClear()
applyAssetUrlsMock.mockClear()
createMock.mockClear()
genReleaseNotesMock.mockClear()
getMock.mockClear()
listMock.mockClear()
shouldSkipMock.mockClear()
updateMock.mockClear()
uploadMock.mockClear()
})
it('creates release but does not upload if no artifact', async () => {
const action = createAction(false, false)
it("creates release with generated release notes when no body provided", async () => {
const action = createAction(false, false, false, true, false, "")
await action.perform()
expect(createMock).toBeCalledWith(tag,
createBody,
expect(genReleaseNotesMock).toHaveBeenCalledWith(tag)
expect(createMock).toHaveBeenCalledWith(
tag,
generatedReleaseBody,
commit,
discussionCategory,
createDraft,
generateReleaseNotes,
makeLatest,
createName,
createPrerelease)
expect(uploadMock).not.toBeCalled()
createPrerelease
)
expect(uploadMock).not.toHaveBeenCalled()
assertOutputApplied()
assertAssetUrlsApplied({})
})
it('creates release if no release exists to update', async () => {
const action = createAction(true, true)
const error = {status: 404}
it("creates release with generated release notes that override existing body", async () => {
const action = createAction(false, false, false, true, false, "")
await action.perform()
expect(genReleaseNotesMock).toHaveBeenCalledWith(tag)
expect(createMock).toHaveBeenCalledWith(
tag,
generatedReleaseBody,
commit,
discussionCategory,
createDraft,
makeLatest,
createName,
createPrerelease
)
expect(uploadMock).not.toHaveBeenCalled()
assertOutputApplied()
assertAssetUrlsApplied({})
})
it("creates release with static body when generateReleaseNotes is false", async () => {
const action = createAction(false, false, false, false, false, "static body")
await action.perform()
expect(genReleaseNotesMock).not.toHaveBeenCalled()
expect(createMock).toHaveBeenCalledWith(
tag,
"static body",
commit,
discussionCategory,
createDraft,
makeLatest,
createName,
createPrerelease
)
expect(uploadMock).not.toHaveBeenCalled()
assertOutputApplied()
assertAssetUrlsApplied({})
})
it("creates release with combined body and generated release notes", async () => {
const action = createAction(false, false, false, true, false, createBody)
await action.perform()
expect(genReleaseNotesMock).toHaveBeenCalledWith(tag)
expect(createMock).toHaveBeenCalledWith(
tag,
`${createBody}\n${generatedReleaseBody}`,
commit,
discussionCategory,
createDraft,
makeLatest,
createName,
createPrerelease
)
expect(uploadMock).not.toHaveBeenCalled()
assertOutputApplied()
assertAssetUrlsApplied({})
})
it("creates release but does not upload if no artifact", async () => {
const action = createAction(false, false, false, true, false, "")
await action.perform()
expect(genReleaseNotesMock).toHaveBeenCalledWith(tag)
expect(createMock).toHaveBeenCalledWith(
tag,
generatedReleaseBody,
commit,
discussionCategory,
createDraft,
makeLatest,
createName,
createPrerelease
)
expect(uploadMock).not.toHaveBeenCalled()
assertOutputApplied()
assertAssetUrlsApplied({})
})
it("creates release if no release exists to update", async () => {
const action = createAction(true, true, false, true, false, "")
const error = { status: 404 }
getMock.mockRejectedValue(error)
await action.perform()
expect(createMock).toBeCalledWith(
expect(genReleaseNotesMock).toHaveBeenCalledWith(tag)
expect(createMock).toHaveBeenCalledWith(
tag,
createBody,
generatedReleaseBody,
commit,
discussionCategory,
createDraft,
generateReleaseNotes,
makeLatest,
createName,
createPrerelease)
expect(uploadMock).toBeCalledWith(artifacts, releaseId, url)
createPrerelease
)
expect(uploadMock).toHaveBeenCalledWith(artifacts, releaseId, url)
assertOutputApplied()
assertAssetUrlsApplied({
"art1": "https://github.com/owner/repo/releases/download/v1.0.0/art1",
"art2": "https://github.com/owner/repo/releases/download/v1.0.0/art2",
})
})
it('creates release if no draft releases', async () => {
const action = createAction(true, true)
const error = {status: 404}
it("creates release if no draft releases", async () => {
const action = createAction(true, true, false, true, false, "")
const error = { status: 404 }
getMock.mockRejectedValue(error)
listMock.mockResolvedValue({
data: [
{id: id, draft: false, tag_name: tag}
]
data: [{ id: id, draft: false, tag_name: tag }],
})
await action.perform()
expect(createMock).toBeCalledWith(
expect(genReleaseNotesMock).toHaveBeenCalledWith(tag)
expect(createMock).toHaveBeenCalledWith(
tag,
createBody,
generatedReleaseBody,
commit,
discussionCategory,
createDraft,
generateReleaseNotes,
makeLatest,
createName,
createPrerelease
)
expect(uploadMock).toBeCalledWith(artifacts, releaseId, url)
expect(uploadMock).toHaveBeenCalledWith(artifacts, releaseId, url)
assertOutputApplied()
assertAssetUrlsApplied({
"art1": "https://github.com/owner/repo/releases/download/v1.0.0/art1",
"art2": "https://github.com/owner/repo/releases/download/v1.0.0/art2",
})
})
it('creates release then uploads artifact', async () => {
const action = createAction(false, true)
it("creates release then uploads artifact", async () => {
const action = createAction(false, true, false, true, false, "")
await action.perform()
expect(createMock).toBeCalledWith(
expect(genReleaseNotesMock).toHaveBeenCalledWith(tag)
expect(createMock).toHaveBeenCalledWith(
tag,
createBody,
generatedReleaseBody,
commit,
discussionCategory,
createDraft,
generateReleaseNotes,
makeLatest,
createName,
createPrerelease
)
expect(uploadMock).toBeCalledWith(artifacts, releaseId, url)
expect(uploadMock).toHaveBeenCalledWith(artifacts, releaseId, url)
assertOutputApplied()
assertAssetUrlsApplied({
"art1": "https://github.com/owner/repo/releases/download/v1.0.0/art1",
"art2": "https://github.com/owner/repo/releases/download/v1.0.0/art2",
})
})
it('removes all artifacts when artifact destroyer is enabled', async () => {
it("removes all artifacts when artifact destroyer is enabled", async () => {
const action = createAction(false, true, true)
await action.perform()
expect(artifactDestroyMock).toBeCalledWith(releaseId)
expect(artifactDestroyMock).toHaveBeenCalledWith(releaseId)
assertOutputApplied()
assertAssetUrlsApplied({
"art1": "https://github.com/owner/repo/releases/download/v1.0.0/art1",
"art2": "https://github.com/owner/repo/releases/download/v1.0.0/art2",
})
})
it('removes no artifacts when artifact destroyer is disabled', async () => {
it("removes no artifacts when artifact destroyer is disabled", async () => {
const action = createAction(false, true)
await action.perform()
expect(artifactDestroyMock).not.toBeCalled()
expect(artifactDestroyMock).not.toHaveBeenCalled()
assertOutputApplied()
assertAssetUrlsApplied({
"art1": "https://github.com/owner/repo/releases/download/v1.0.0/art1",
"art2": "https://github.com/owner/repo/releases/download/v1.0.0/art2",
})
})
it('throws error when create fails', async () => {
const action = createAction(false, true)
it("skips action", async () => {
const action = createAction(false, false, false)
shouldSkipMock.mockResolvedValue(true)
await action.perform()
expect(createMock).not.toHaveBeenCalled()
expect(updateMock).not.toHaveBeenCalled()
})
it("throws error when create fails", async () => {
const action = createAction(false, true, false, true, false, "")
createMock.mockRejectedValue("error")
expect.hasAssertions()
@@ -160,27 +294,28 @@ describe("Action", () => {
expect(error).toEqual("error")
}
expect(createMock).toBeCalledWith(
expect(genReleaseNotesMock).toHaveBeenCalledWith(tag)
expect(createMock).toHaveBeenCalledWith(
tag,
createBody,
generatedReleaseBody,
commit,
discussionCategory,
createDraft,
generateReleaseNotes,
makeLatest,
createName,
createPrerelease
)
expect(uploadMock).not.toBeCalled()
expect(uploadMock).not.toHaveBeenCalled()
})
it('throws error when get fails', async () => {
it("throws error when get fails", async () => {
const action = createAction(true, true)
const error = {
errors: [
{
code: 'already_exists'
}
]
code: "already_exists",
},
],
}
createMock.mockRejectedValue(error)
@@ -192,14 +327,38 @@ describe("Action", () => {
expect(error).toEqual("error")
}
expect(getMock).toBeCalledWith(tag)
expect(updateMock).not.toBeCalled()
expect(uploadMock).not.toBeCalled()
expect(getMock).toHaveBeenCalledWith(tag)
expect(updateMock).not.toHaveBeenCalled()
expect(uploadMock).not.toHaveBeenCalled()
})
it('throws error when update fails', async () => {
it("throws error when list has no data", async () => {
const action = createAction(true, true)
getMock.mockRejectedValue({ status: 404 })
const error = {
errors: [
{
code: "already_exists",
},
],
}
createMock.mockRejectedValue(error)
listMock.mockResolvedValue({})
expect.hasAssertions()
try {
await action.perform()
} catch (error) {
expect(error).toEqual(Error("No releases found. Response: {}"))
}
expect(listMock).toHaveBeenCalled()
expect(createMock).not.toHaveBeenCalled()
expect(updateMock).not.toHaveBeenCalled()
})
it("throws error when update fails", async () => {
const action = createAction(true, true, false, true, false, "", true, createDraft, "")
updateMock.mockRejectedValue("error")
@@ -210,22 +369,23 @@ describe("Action", () => {
expect(error).toEqual("error")
}
expect(updateMock).toBeCalledWith(
expect(updateMock).toHaveBeenCalledWith(
id,
tag,
updateBody,
generatedReleaseBody,
commit,
discussionCategory,
updateDraft,
makeLatest,
updateName,
updatePrerelease
)
expect(uploadMock).not.toBeCalled()
expect(uploadMock).not.toHaveBeenCalled()
})
it('throws error when upload fails', async () => {
const action = createAction(false, true)
const expectedError = {status: 404}
it("throws error when upload fails", async () => {
const action = createAction(false, true, false, true, false, "")
const expectedError = { status: 404 }
uploadMock.mockRejectedValue(expectedError)
expect.hasAssertions()
@@ -235,95 +395,358 @@ describe("Action", () => {
expect(error).toEqual(expectedError)
}
expect(createMock).toBeCalledWith(
expect(genReleaseNotesMock).toHaveBeenCalledWith(tag)
expect(createMock).toHaveBeenCalledWith(
tag,
createBody,
generatedReleaseBody,
commit,
discussionCategory,
createDraft,
generateReleaseNotes,
makeLatest,
createName,
createPrerelease
)
expect(uploadMock).toBeCalledWith(artifacts, releaseId, url)
expect(uploadMock).toHaveBeenCalledWith(artifacts, releaseId, url)
})
it('updates draft release', async () => {
const action = createAction(true, true)
const error = {status: 404}
it("updates draft release", async () => {
const action = createAction(true, true, false, true, false, "", true, createDraft, "")
const error = { status: 404 }
getMock.mockRejectedValue(error)
listMock.mockResolvedValue({
data: [
{id: 123, draft: false, tag_name: tag},
{id: id, draft: true, tag_name: tag}
]
{ id: 123, draft: false, tag_name: tag },
{ id: id, draft: true, tag_name: tag },
],
})
await action.perform()
expect(updateMock).toBeCalledWith(
expect(updateMock).toHaveBeenCalledWith(
id,
tag,
updateBody,
generatedReleaseBody,
commit,
discussionCategory,
updateDraft,
makeLatest,
updateName,
updatePrerelease
)
expect(uploadMock).toBeCalledWith(artifacts, releaseId, url)
expect(uploadMock).toHaveBeenCalledWith(artifacts, releaseId, url)
assertOutputApplied()
assertAssetUrlsApplied({
"art1": "https://github.com/owner/repo/releases/download/v1.0.0/art1",
"art2": "https://github.com/owner/repo/releases/download/v1.0.0/art2",
})
})
it('updates release but does not upload if no artifact', async () => {
const action = createAction(true, false)
it("updates draft release with static body", async () => {
const action = createAction(true, true, false, false)
const error = { status: 404 }
getMock.mockRejectedValue(error)
listMock.mockResolvedValue({
data: [
{ id: 123, draft: false, tag_name: tag },
{ id: id, draft: true, tag_name: tag },
],
})
await action.perform()
expect(updateMock).toBeCalledWith(
expect(updateMock).toHaveBeenCalledWith(
id,
tag,
updateBody,
commit,
discussionCategory,
updateDraft,
makeLatest,
updateName,
updatePrerelease
)
expect(uploadMock).not.toBeCalled()
expect(uploadMock).toHaveBeenCalledWith(artifacts, releaseId, url)
assertOutputApplied()
assertAssetUrlsApplied({
"art1": "https://github.com/owner/repo/releases/download/v1.0.0/art1",
"art2": "https://github.com/owner/repo/releases/download/v1.0.0/art2",
})
})
it('updates release then uploads artifact', async () => {
const action = createAction(true, true)
it("updates release with combined body and generated release notes", async () => {
const action = createAction(true, true, false, true, false, "", true, createDraft, updateBody)
await action.perform()
expect(updateMock).toBeCalledWith(
expect(genReleaseNotesMock).toHaveBeenCalledWith(tag)
expect(updateMock).toHaveBeenCalledWith(
id,
tag,
`${updateBody}\n${generatedReleaseBody}`,
commit,
discussionCategory,
updateDraft,
makeLatest,
updateName,
updatePrerelease
)
expect(uploadMock).toHaveBeenCalledWith(artifacts, releaseId, url)
assertOutputApplied()
assertAssetUrlsApplied({
"art1": "https://github.com/owner/repo/releases/download/v1.0.0/art1",
"art2": "https://github.com/owner/repo/releases/download/v1.0.0/art2",
})
})
it("updates release but does not upload if no artifact", async () => {
const action = createAction(true, false, false, true, false, "", true, createDraft, "")
await action.perform()
expect(updateMock).toHaveBeenCalledWith(
id,
tag,
generatedReleaseBody,
commit,
discussionCategory,
updateDraft,
makeLatest,
updateName,
updatePrerelease
)
expect(uploadMock).not.toHaveBeenCalled()
assertOutputApplied()
assertAssetUrlsApplied({})
})
it("updates release then uploads artifact", async () => {
const action = createAction(true, true, false, true, false, "", true, createDraft, "")
await action.perform()
expect(updateMock).toHaveBeenCalledWith(
id,
tag,
generatedReleaseBody,
commit,
discussionCategory,
updateDraft,
makeLatest,
updateName,
updatePrerelease
)
expect(uploadMock).toHaveBeenCalledWith(artifacts, releaseId, url)
assertOutputApplied()
assertAssetUrlsApplied({
"art1": "https://github.com/owner/repo/releases/download/v1.0.0/art1",
"art2": "https://github.com/owner/repo/releases/download/v1.0.0/art2",
})
})
it("updates release with static body when generateReleaseNotes is true but omitBodyDuringUpdate is true", async () => {
const action = createAction(true, true, false, true, true)
const error = { status: 404 }
getMock.mockRejectedValue(error)
listMock.mockResolvedValue({
data: [
{ id: 123, draft: false, tag_name: tag },
{ id: id, draft: true, tag_name: tag },
],
})
await action.perform()
expect(updateMock).toHaveBeenCalledWith(
id,
tag,
updateBody,
commit,
discussionCategory,
updateDraft,
makeLatest,
updateName,
updatePrerelease
)
expect(uploadMock).toBeCalledWith(artifacts, releaseId, url)
expect(uploadMock).toHaveBeenCalledWith(artifacts, releaseId, url)
assertOutputApplied()
assertAssetUrlsApplied({
"art1": "https://github.com/owner/repo/releases/download/v1.0.0/art1",
"art2": "https://github.com/owner/repo/releases/download/v1.0.0/art2",
})
})
it("does not publish immutable release when immutableCreate is false", async () => {
const action = createAction(false, true, false, true, false, "", false, false)
await action.perform()
expect(createMock).toHaveBeenCalledWith(
tag,
generatedReleaseBody,
commit,
discussionCategory,
false, // draft should be false when createdDraft is false
makeLatest,
createName,
createPrerelease
)
// Should only call update once for regular create, not for publishImmutableRelease
expect(updateMock).not.toHaveBeenCalled()
assertOutputApplied()
})
it("does not publish immutable release when createdDraft is true", async () => {
const action = createAction(false, true, false, true, false, "", true, true)
await action.perform()
expect(createMock).toHaveBeenCalledWith(
tag,
generatedReleaseBody,
commit,
discussionCategory,
true, // draft should be true when createdDraft is true or immutableCreate is true
makeLatest,
createName,
createPrerelease
)
// Should only call update once for regular create, not for publishImmutableRelease
expect(updateMock).not.toHaveBeenCalled()
assertOutputApplied()
})
it("publishes immutable release when immutableCreate is true and createdDraft is false", async () => {
const action = createAction(false, true, false, true, false, "", true, false)
const immutableReleaseResponse = {
data: {
id: 999,
upload_url: "http://immutable.example.com",
html_url: "https://github.com/owner/repo/releases/tag/v1.0.0-immutable",
tarball_url: "https://api.github.com/repos/owner/repo/tarball/v1.0.0-immutable",
zipball_url: "https://api.github.com/repos/owner/repo/zipball/v1.0.0-immutable",
},
}
updateMock.mockResolvedValueOnce(immutableReleaseResponse)
await action.perform()
expect(createMock).toHaveBeenCalledWith(
tag,
generatedReleaseBody,
commit,
discussionCategory,
true, // draft should be true when immutableCreate is true
makeLatest,
createName,
createPrerelease
)
// Should call update for publishImmutableRelease
expect(updateMock).toHaveBeenCalledWith(
releaseId,
tag,
undefined, // body is omitted
undefined, // commit is omitted
discussionCategory,
false, // draft is set to false to publish the release
makeLatest,
createName,
createPrerelease
)
// Should apply the immutable release data instead of the original
expect(applyReleaseDataMock).toHaveBeenCalledWith(immutableReleaseResponse.data)
assertAssetUrlsApplied({
"art1": "https://github.com/owner/repo/releases/download/v1.0.0/art1",
"art2": "https://github.com/owner/repo/releases/download/v1.0.0/art2",
})
})
it("publishes immutable release when allowUpdates is true but release does not exist", async () => {
const action = createAction(true, true, false, true, false, "", true, false)
const error = { status: 404 }
getMock.mockRejectedValue(error)
listMock.mockResolvedValue({ data: [] }) // No draft releases found
const immutableReleaseResponse = {
data: {
id: 888,
upload_url: "http://immutable-update.example.com",
html_url: "https://github.com/owner/repo/releases/tag/v1.0.0-immutable-update",
tarball_url: "https://api.github.com/repos/owner/repo/tarball/v1.0.0-immutable-update",
zipball_url: "https://api.github.com/repos/owner/repo/zipball/v1.0.0-immutable-update",
},
}
updateMock.mockResolvedValueOnce(immutableReleaseResponse)
await action.perform()
// Should try to get the release first (allowUpdates=true)
expect(getMock).toHaveBeenCalledWith(tag)
// Should check for draft releases when get fails with 404
expect(listMock).toHaveBeenCalled()
// Should create a new release when no drafts found
expect(createMock).toHaveBeenCalledWith(
tag,
generatedReleaseBody,
commit,
discussionCategory,
true, // draft should be true when immutableCreate is true
makeLatest,
createName,
createPrerelease
)
// Should call update for publishImmutableRelease
expect(updateMock).toHaveBeenCalledWith(
releaseId,
tag,
undefined, // body is omitted
undefined, // commit is omitted
discussionCategory,
false, // draft is set to false to publish the release
makeLatest,
createName,
createPrerelease
)
// Should apply the immutable release data instead of the original
expect(applyReleaseDataMock).toHaveBeenCalledWith(immutableReleaseResponse.data)
assertAssetUrlsApplied({
"art1": "https://github.com/owner/repo/releases/download/v1.0.0/art1",
"art2": "https://github.com/owner/repo/releases/download/v1.0.0/art2",
})
})
function assertOutputApplied() {
expect(applyReleaseDataMock).toBeCalledWith({id: releaseId, upload_url: url})
expect(applyReleaseDataMock).toHaveBeenCalledWith({
id: releaseId,
upload_url: url,
html_url: TEST_URLS.HTML_URL,
tarball_url: TEST_URLS.TARBALL_URL,
zipball_url: TEST_URLS.ZIPBALL_URL,
})
}
function createAction(allowUpdates: boolean, hasArtifact: boolean, removeArtifacts: boolean = false): Action {
function assertAssetUrlsApplied(expectedUrls: Record<string, string>) {
expect(applyAssetUrlsMock).toHaveBeenCalledWith(expectedUrls)
}
function createAction(
allowUpdates: boolean,
hasArtifact: boolean,
removeArtifacts = false,
generateReleaseNotes = true,
omitBodyDuringUpdate = false,
createdReleaseBody = createBody,
immutableCreate = true,
createdDraft = createDraft,
updatedReleaseBody = updateBody
): Action {
let inputArtifact: Artifact[]
if (hasArtifact) {
inputArtifact = artifacts
} else {
inputArtifact = []
}
const MockReleases = jest.fn<Releases, any>(() => {
return {
create: createMock,
@@ -332,69 +755,98 @@ describe("Action", () => {
listArtifactsForRelease: listArtifactsMock,
listReleases: listMock,
update: updateMock,
uploadArtifact: jest.fn()
uploadArtifact: jest.fn(),
generateReleaseNotes: genReleaseNotesMock,
}
})
createMock.mockResolvedValue({
data: {
id: releaseId,
upload_url: url
}
upload_url: url,
html_url: TEST_URLS.HTML_URL,
tarball_url: TEST_URLS.TARBALL_URL,
zipball_url: TEST_URLS.ZIPBALL_URL,
},
})
genReleaseNotesMock.mockResolvedValue({
data: {
body: generatedReleaseBody,
},
})
getMock.mockResolvedValue({
data: {
id: id
}
id: id,
},
})
listMock.mockResolvedValue({
data: []
data: [],
})
shouldSkipMock.mockResolvedValue(false)
updateMock.mockResolvedValue({
data: {
id: releaseId,
upload_url: url
}
upload_url: url,
html_url: TEST_URLS.HTML_URL,
tarball_url: TEST_URLS.TARBALL_URL,
zipball_url: TEST_URLS.ZIPBALL_URL,
},
})
uploadMock.mockResolvedValue({
"art1": "https://github.com/owner/repo/releases/download/v1.0.0/art1",
"art2": "https://github.com/owner/repo/releases/download/v1.0.0/art2",
})
uploadMock.mockResolvedValue({})
const MockInputs = jest.fn<Inputs, any>(() => {
return {
allowUpdates: allowUpdates,
allowUpdates,
artifactErrorsFailBuild: true,
artifacts: inputArtifact,
createdDraft: createDraft,
createdReleaseBody: createBody,
createdDraft: createdDraft,
createdReleaseBody: createdReleaseBody,
createdReleaseName: createName,
commit: commit,
discussionCategory: discussionCategory,
generateReleaseNotes: true,
commit,
discussionCategory,
generateReleaseNotes,
immutableCreate: immutableCreate,
makeLatest: makeLatest,
owner: "owner",
createdPrerelease: createPrerelease,
replacesArtifacts: replacesArtifacts,
removeArtifacts: removeArtifacts,
replacesArtifacts,
removeArtifacts,
repo: "repo",
tag: tag,
token: token,
skipIfReleaseExists: false,
tag,
token,
updatedDraft: updateDraft,
updatedReleaseBody: updateBody,
updatedReleaseBody: updatedReleaseBody,
updatedReleaseName: updateName,
updatedPrerelease: updatePrerelease
updatedPrerelease: updatePrerelease,
updateOnlyUnreleased: updateOnlyUnreleased,
omitBodyDuringUpdate,
}
})
const MockOutputs = jest.fn<Outputs, any>(() => {
return {
applyReleaseData: applyReleaseDataMock
applyReleaseData: applyReleaseDataMock,
applyAssetUrls: applyAssetUrlsMock,
}
})
const MockUploader = jest.fn<ArtifactUploader, any>(() => {
return {
uploadArtifacts: uploadMock
uploadArtifacts: uploadMock,
}
})
const MockArtifactDestroyer = jest.fn<ArtifactDestroyer, any>(() => {
return {
destroyArtifacts: artifactDestroyMock
destroyArtifacts: artifactDestroyMock,
}
})
const MockActionSkipper = jest.fn<ActionSkipper, any>(() => {
return {
shouldSkip: shouldSkipMock,
}
})
@@ -403,7 +855,8 @@ describe("Action", () => {
const releases = new MockReleases()
const uploader = new MockUploader()
const artifactDestroyer = new MockArtifactDestroyer()
const actionSkipper = new MockActionSkipper()
return new Action(inputs, outputs, releases, uploader, artifactDestroyer)
return new Action(inputs, outputs, releases, uploader, artifactDestroyer, actionSkipper)
}
})

View File

@@ -0,0 +1,45 @@
import { ReleaseActionSkipper } from "../src/ActionSkipper"
import type { Releases } from "../src/Releases"
describe("shouldSkip", () => {
const getMock = jest.fn()
const tag = "tag"
const MockReleases = jest.fn<Releases, any>(() => {
return {
create: jest.fn(),
deleteArtifact: jest.fn(),
getByTag: getMock,
listArtifactsForRelease: jest.fn(),
listReleases: jest.fn(),
update: jest.fn(),
uploadArtifact: jest.fn(),
generateReleaseNotes: jest.fn(),
}
})
it("should return false when skipIfReleaseExists is false", async () => {
const actionSkipper = new ReleaseActionSkipper(false, MockReleases(), tag)
expect(await actionSkipper.shouldSkip()).toBe(false)
})
it("should return false when error occurs", async () => {
getMock.mockRejectedValue(new Error())
const actionSkipper = new ReleaseActionSkipper(true, MockReleases(), tag)
expect(await actionSkipper.shouldSkip()).toBe(false)
})
it("should return false when release does not exist", async () => {
getMock.mockResolvedValue({})
const actionSkipper = new ReleaseActionSkipper(true, MockReleases(), tag)
expect(await actionSkipper.shouldSkip()).toBe(false)
})
it("should return true when release does exist", async () => {
getMock.mockResolvedValue({ data: {} })
const actionSkipper = new ReleaseActionSkipper(true, MockReleases(), tag)
expect(await actionSkipper.shouldSkip()).toBe(true)
})
})

View File

@@ -1,38 +1,40 @@
import { Artifact } from "../src/Artifact";
import { Artifact } from "../src/Artifact"
const fileContents = Buffer.from('artful facts', 'utf-8')
const contentLength = 42
const fakeReadStream = {}
jest.mock('fs', () => {
jest.mock("fs", () => {
return {
readFileSync: () => fileContents,
statSync: () => { return { size: contentLength } }
};
createReadStream: () => fakeReadStream,
statSync: () => {
return { size: contentLength }
},
}
})
describe("Artifact", () => {
it('defaults contentType to raw', () => {
const artifact = new Artifact('')
expect(artifact.contentType).toBe('raw')
it("defaults contentType to raw", () => {
const artifact = new Artifact("")
expect(artifact.contentType).toBe("raw")
})
it('generates name from path', () => {
const artifact = new Artifact('some/artifact')
expect(artifact.name).toBe('artifact')
it("generates name from path", () => {
const artifact = new Artifact("some/artifact")
expect(artifact.name).toBe("artifact")
})
it('provides contentLength', () => {
const artifact = new Artifact('some/artifact')
it("provides contentLength", () => {
const artifact = new Artifact("some/artifact")
expect(artifact.contentLength).toBe(contentLength)
})
it('provides path', () => {
const artifact = new Artifact('some/artifact')
expect(artifact.path).toBe('some/artifact')
it("provides path", () => {
const artifact = new Artifact("some/artifact")
expect(artifact.path).toBe("some/artifact")
})
it('reads artifact', () => {
const artifact = new Artifact('some/artifact')
expect(artifact.readFile()).toBe(fileContents)
it("reads artifact", () => {
const artifact = new Artifact("some/artifact")
expect(artifact.readFile()).toBe(fakeReadStream)
})
})
})

View File

@@ -1,45 +1,41 @@
import {Artifact} from "../src/Artifact"
import {GithubArtifactUploader} from "../src/ArtifactUploader"
import {Releases} from "../src/Releases";
import {RequestError} from '@octokit/request-error'
import {GithubArtifactDestroyer} from "../src/ArtifactDestroyer";
import { GithubArtifactDestroyer } from "../src/ArtifactDestroyer"
import type { Releases } from "../src/Releases"
const releaseId = 100
const deleteMock = jest.fn()
const listArtifactsMock = jest.fn()
describe('ArtifactDestroyer', () => {
describe("ArtifactDestroyer", () => {
beforeEach(() => {
deleteMock.mockClear()
listArtifactsMock.mockClear()
})
it('destroys all artifacts', async () => {
it("destroys all artifacts", async () => {
mockListWithAssets()
mockDeleteSuccess()
const destroyer = createDestroyer()
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()
const destroyer = createDestroyer()
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()
mockDeleteError()
const destroyer = createDestroyer()
expect.hasAssertions()
try {
await destroyer.destroyArtifacts(releaseId)
@@ -57,7 +53,8 @@ describe('ArtifactDestroyer', () => {
listArtifactsForRelease: listArtifactsMock,
listReleases: jest.fn(),
update: jest.fn(),
uploadArtifact: jest.fn()
uploadArtifact: jest.fn(),
generateReleaseNotes: jest.fn(),
}
})
return new GithubArtifactDestroyer(new MockReleases())
@@ -75,16 +72,16 @@ describe('ArtifactDestroyer', () => {
listArtifactsMock.mockResolvedValue([
{
name: "art1",
id: 1
id: 1,
},
{
name: "art2",
id: 2
}
id: 2,
},
])
}
function mockListWithoutAssets() {
listArtifactsMock.mockResolvedValue([])
}
});
})

View File

@@ -1,28 +1,31 @@
const warnMock = jest.fn()
import {FileArtifactGlobber} from "../src/ArtifactGlobber"
import {Globber} from "../src/Globber";
import {Artifact} from "../src/Artifact";
import untildify = require("untildify");
import { FileArtifactGlobber } from "../src/ArtifactGlobber"
import { Globber } from "../src/Globber"
import { Artifact } from "../src/Artifact"
import untildify = require("untildify")
const contentType = "raw"
const globMock = jest.fn()
const globResults = ["file1", "file2"]
jest.mock('@actions/core', () => {
return {warning: warnMock};
jest.mock("@actions/core", () => {
return { warning: warnMock }
})
jest.mock('fs', () => {
jest.mock("fs", () => {
return {
statSync: () => {
return {
isDirectory(): boolean {
return false
}
},
}
}
};
},
realpathSync: () => {
return false
},
}
})
describe("ArtifactGlobber", () => {
@@ -33,79 +36,66 @@ describe("ArtifactGlobber", () => {
it("expands paths in which start with a ~", () => {
const globber = createArtifactGlobber()
const expectedArtifacts =
globResults.map((path) => new Artifact(path, contentType))
const expectedArtifacts = globResults.map((path) => new Artifact(path, contentType))
expect(globber.globArtifactString('~/path', 'raw', false))
.toEqual(expectedArtifacts)
expect(globMock).toBeCalledWith(untildify('~/path'))
expect(warnMock).not.toBeCalled()
expect(globber.globArtifactString("~/path", "raw", false)).toEqual(expectedArtifacts)
expect(globMock).toHaveBeenCalledWith(untildify("~/path"))
expect(warnMock).not.toHaveBeenCalled()
})
it("globs simple path", () => {
const globber = createArtifactGlobber()
const expectedArtifacts =
globResults.map((path) => new Artifact(path, contentType))
const expectedArtifacts = globResults.map((path) => new Artifact(path, contentType))
expect(globber.globArtifactString('path', 'raw', false))
.toEqual(expectedArtifacts)
expect(globMock).toBeCalledWith('path')
expect(warnMock).not.toBeCalled()
expect(globber.globArtifactString("path", "raw", false)).toEqual(expectedArtifacts)
expect(globMock).toHaveBeenCalledWith("path")
expect(warnMock).not.toHaveBeenCalled()
})
it("splits multiple paths with comma separator", () => {
const globber = createArtifactGlobber()
const expectedArtifacts =
globResults
.concat(globResults)
.map((path) => new Artifact(path, contentType))
const expectedArtifacts = globResults.concat(globResults).map((path) => new Artifact(path, contentType))
expect(globber.globArtifactString('path1,path2', 'raw', false))
.toEqual(expectedArtifacts)
expect(globMock).toBeCalledWith('path1')
expect(globMock).toBeCalledWith('path2')
expect(warnMock).not.toBeCalled()
expect(globber.globArtifactString("path1,path2", "raw", false)).toEqual(expectedArtifacts)
expect(globMock).toHaveBeenCalledWith("path1")
expect(globMock).toHaveBeenCalledWith("path2")
expect(warnMock).not.toHaveBeenCalled()
})
it("splits multiple paths with new line separator and trims start", () => {
const globber = createArtifactGlobber()
const expectedArtifacts =
globResults
.concat(globResults)
.map((path) => new Artifact(path, contentType))
const expectedArtifacts = globResults.concat(globResults).map((path) => new Artifact(path, contentType))
expect(globber.globArtifactString('path1\n path2', 'raw', false))
.toEqual(expectedArtifacts)
expect(globMock).toBeCalledWith('path1')
expect(globMock).toBeCalledWith('path2')
expect(warnMock).not.toBeCalled()
expect(globber.globArtifactString("path1\n path2", "raw", false)).toEqual(expectedArtifacts)
expect(globMock).toHaveBeenCalledWith("path1")
expect(globMock).toHaveBeenCalledWith("path2")
expect(warnMock).not.toHaveBeenCalled()
})
it("warns when no glob results are produced and empty results shouldn't throw", () => {
const globber = createArtifactGlobber([])
expect(globber.globArtifactString('path', 'raw', false))
.toEqual([])
expect(warnMock).toBeCalled()
expect(globber.globArtifactString("path", "raw", false)).toEqual([])
expect(warnMock).toHaveBeenCalled()
})
it("throws when no glob results are produced and empty results shouild throw", () => {
const globber = createArtifactGlobber([])
expect(() => {
globber.globArtifactString('path', 'raw', true)
globber.globArtifactString("path", "raw", true)
}).toThrow()
})
function createArtifactGlobber(results: string[] = globResults): FileArtifactGlobber {
const MockGlobber = jest.fn<Globber, any>(() => {
return {
glob: globMock
glob: globMock,
}
})
globMock.mockReturnValue(results)
return new FileArtifactGlobber(new MockGlobber())
}
})
})

View File

@@ -1,20 +1,20 @@
const directoryMock = jest.fn()
const warnMock = jest.fn()
import {ArtifactPathValidator} from "../src/ArtifactPathValidator";
import { ArtifactPathValidator } from "../src/ArtifactPathValidator"
const pattern = 'pattern'
const pattern = "pattern"
jest.mock('@actions/core', () => {
return {warning: warnMock};
jest.mock("@actions/core", () => {
return { warning: warnMock }
})
jest.mock('fs', () => {
jest.mock("fs", () => {
return {
statSync: () => {
return {isDirectory: directoryMock}
}
};
return { isDirectory: directoryMock }
},
}
})
describe("ArtifactPathValidator", () => {
@@ -24,20 +24,20 @@ describe("ArtifactPathValidator", () => {
})
it("warns and filters out path which points to a directory", () => {
const paths = ['path1', 'path2']
const paths = ["path1", "path2"]
directoryMock.mockReturnValueOnce(true).mockReturnValueOnce(false)
const validator = new ArtifactPathValidator(false, paths, pattern)
const result = validator.validate()
expect(warnMock).toBeCalled()
expect(result).toEqual(['path2'])
expect(warnMock).toHaveBeenCalled()
expect(result).toEqual(["path2"])
})
it("warns when no glob results are produced and empty results shouldn't throw", () => {
const validator = new ArtifactPathValidator(false, [], pattern)
const result = validator.validate()
expect(warnMock).toBeCalled()
expect(warnMock).toHaveBeenCalled()
})
it("throws when no glob results are produced and empty results shouild throw", () => {
@@ -48,7 +48,7 @@ describe("ArtifactPathValidator", () => {
})
it("throws when path points to directory", () => {
const paths = ['path1', 'path2']
const paths = ["path1", "path2"]
directoryMock.mockReturnValueOnce(true).mockReturnValueOnce(false)
const validator = new ArtifactPathValidator(true, paths, pattern)
@@ -57,4 +57,4 @@ describe("ArtifactPathValidator", () => {
validator.validate()
}).toThrow()
})
})
})

View File

@@ -1,133 +1,171 @@
import {Artifact} from "../src/Artifact"
import {GithubArtifactUploader} from "../src/ArtifactUploader"
import {Releases} from "../src/Releases";
import {RequestError} from '@octokit/request-error'
import { RequestError } from "@octokit/request-error"
import { Artifact } from "../src/Artifact"
import { GithubArtifactUploader } from "../src/ArtifactUploader"
import type { Releases } from "../src/Releases"
const artifacts = [
new Artifact('a/art1'),
new Artifact('b/art2')
]
const fileContents = Buffer.from('artful facts', 'utf-8')
const artifacts = [new Artifact("a/art1"), new Artifact("b/art2")]
const fakeReadStream = {}
const contentLength = 42
const releaseId = 100
const url = 'http://api.example.com'
const url = "http://api.example.com"
const deleteMock = jest.fn()
const listArtifactsMock = jest.fn()
const uploadMock = jest.fn()
jest.mock('fs', () => {
return {
promises: {},
readFileSync: () => fileContents,
statSync: () => {
return {size: contentLength}
}
};
// Mock response with browser_download_url
const mockUploadResponse = (name: string) => ({
data: {
browser_download_url: `https://github.com/octocat/Hello-World/releases/download/v1.0.0/${name}`,
name: name,
id: 1,
}
})
describe('ArtifactUploader', () => {
jest.mock("fs", () => {
const originalFs = jest.requireActual("fs")
return {
...originalFs,
promises: {},
createReadStream: () => fakeReadStream,
statSync: () => {
return { size: contentLength }
},
}
})
describe("ArtifactUploader", () => {
beforeEach(() => {
deleteMock.mockClear()
listArtifactsMock.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()
mockUploadArtifact(401, 2)
const uploader = createUploader(true)
await uploader.uploadArtifacts(artifacts, releaseId, url)
const result = await uploader.uploadArtifacts(artifacts, releaseId, url)
expect(uploadMock).toBeCalledTimes(2)
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art1', releaseId)
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art2', releaseId)
expect(deleteMock).toBeCalledTimes(0)
expect(result).toEqual({})
expect(uploadMock).toHaveBeenCalledTimes(2)
})
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()
mockUploadArtifact(500, 4)
const uploader = createUploader(true)
await uploader.uploadArtifacts(artifacts, releaseId, url)
const result = await uploader.uploadArtifacts(artifacts, releaseId, url)
expect(uploadMock).toBeCalledTimes(5)
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art1', releaseId)
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art1', releaseId)
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art1', releaseId)
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art2', releaseId)
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art2', releaseId)
expect(result).toEqual({})
expect(uploadMock).toHaveBeenCalledTimes(5)
expect(uploadMock).toHaveBeenCalledWith(url, contentLength, "raw", fakeReadStream, "art1", releaseId)
expect(uploadMock).toHaveBeenCalledWith(url, contentLength, "raw", fakeReadStream, "art1", releaseId)
expect(uploadMock).toHaveBeenCalledWith(url, contentLength, "raw", fakeReadStream, "art1", releaseId)
expect(uploadMock).toHaveBeenCalledWith(url, contentLength, "raw", fakeReadStream, "art2", releaseId)
expect(uploadMock).toHaveBeenCalledWith(url, contentLength, "raw", fakeReadStream, "art2", releaseId)
expect(deleteMock).toBeCalledTimes(0)
expect(deleteMock).toHaveBeenCalledTimes(0)
})
it('replaces all artifacts', async () => {
it("replaces all artifacts", async () => {
mockDeleteSuccess()
mockListWithAssets()
mockUploadArtifact()
mockUploadSuccess()
const uploader = createUploader(true)
await uploader.uploadArtifacts(artifacts, releaseId, url)
const result = await uploader.uploadArtifacts(artifacts, releaseId, url)
expect(uploadMock).toBeCalledTimes(2)
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art1', releaseId)
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art2', releaseId)
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)
expect(uploadMock).toHaveBeenCalledWith(url, contentLength, "raw", fakeReadStream, "art1", releaseId)
expect(uploadMock).toHaveBeenCalledWith(url, contentLength, "raw", fakeReadStream, "art2", releaseId)
expect(deleteMock).toBeCalledTimes(2)
expect(deleteMock).toBeCalledWith(1)
expect(deleteMock).toBeCalledWith(2)
expect(deleteMock).toHaveBeenCalledTimes(2)
expect(deleteMock).toHaveBeenCalledWith(1)
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()
mockListWithoutAssets()
mockUploadArtifact()
mockUploadSuccess()
const uploader = createUploader(true)
await uploader.uploadArtifacts(artifacts, releaseId, url)
const result = await uploader.uploadArtifacts(artifacts, releaseId, url)
expect(uploadMock).toBeCalledTimes(2)
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art1', releaseId)
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art2', releaseId)
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)
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()
mockUploadArtifact(500, 2)
const uploader = createUploader(true)
await uploader.uploadArtifacts(artifacts, releaseId, url)
const result = await uploader.uploadArtifacts(artifacts, releaseId, url)
expect(uploadMock).toBeCalledTimes(4)
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art1', releaseId)
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art1', releaseId)
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art1', releaseId)
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art2', releaseId)
expect(result).toEqual({})
expect(uploadMock).toHaveBeenCalledTimes(4)
expect(uploadMock).toHaveBeenCalledWith(url, contentLength, "raw", fakeReadStream, "art1", releaseId)
expect(uploadMock).toHaveBeenCalledWith(url, contentLength, "raw", fakeReadStream, "art1", releaseId)
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('throws upload error when replacesExistingArtifacts is true', async () => {
it("throws upload error when replacesExistingArtifacts is true", async () => {
mockListWithoutAssets()
mockUploadError()
const uploader = createUploader(true, true)
@@ -140,10 +178,10 @@ describe('ArtifactUploader', () => {
}
})
it('throws error from replace', async () => {
it("throws error from replace", async () => {
mockDeleteError()
mockListWithAssets()
mockUploadArtifact()
mockUploadSuccess()
const uploader = createUploader(true)
expect.hasAssertions()
@@ -154,24 +192,26 @@ describe('ArtifactUploader', () => {
}
})
it('updates all artifacts, delete none', async () => {
it("updates all artifacts, delete none", async () => {
mockDeleteError()
mockListWithAssets()
mockUploadArtifact()
mockUploadSuccess()
const uploader = createUploader(false)
await uploader.uploadArtifacts(artifacts, releaseId, url)
const result = await uploader.uploadArtifacts(artifacts, releaseId, url)
expect(uploadMock).toBeCalledTimes(2)
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art1', releaseId)
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art2', releaseId)
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)
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>(() => {
return {
create: jest.fn(),
@@ -180,7 +220,8 @@ describe('ArtifactUploader', () => {
listArtifactsForRelease: listArtifactsMock,
listReleases: jest.fn(),
update: jest.fn(),
uploadArtifact: uploadMock
uploadArtifact: uploadMock,
generateReleaseNotes: jest.fn(),
}
})
return new GithubArtifactUploader(new MockReleases(), replaces, throws)
@@ -198,12 +239,12 @@ describe('ArtifactUploader', () => {
listArtifactsMock.mockResolvedValue([
{
name: "art1",
id: 1
id: 1,
},
{
name: "art2",
id: 2
}
id: 2,
},
])
}
@@ -211,10 +252,14 @@ describe('ArtifactUploader', () => {
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, {
headers: {},
request: {method: 'GET', url: '', headers: {}}
request: { method: "GET", url: "", headers: {} },
})
for (let index = 0; index < failures; index++) {
uploadMock.mockRejectedValueOnce(error)
@@ -225,7 +270,7 @@ describe('ArtifactUploader', () => {
function mockUploadError() {
uploadMock.mockRejectedValue({
message: "error",
status: 502
status: 502,
})
}
});
})

View File

@@ -1,68 +1,95 @@
import { GithubError } from "../src/GithubError"
describe('ErrorMessage', () => {
describe('has error with code', () => {
describe("ErrorMessage", () => {
describe("has error with code", () => {
const error = {
message: 'something bad happened',
message: "something bad happened",
errors: [
{
code: 'missing',
resource: 'release'
code: "missing",
resource: "release",
},
{
code: 'already_exists',
resource: 'release'
}
code: "already_exists",
resource: "release",
},
],
status: 422
status: 422,
}
it('does not have error', () => {
it("does not have 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)
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 = {
message: 'something bad happened',
message: "something bad happened",
errors: [
{
code: 'missing',
resource: 'release'
code: "missing",
resource: "release",
},
{
code: 'already_exists',
resource: 'release'
}
code: "already_exists",
resource: "release",
},
],
status: 422
status: 422,
}
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)
})
it('generates message without errors', () => {
it("generates message without errors", () => {
const error = {
message: 'something bad happened',
status: 422
message: "something bad happened",
status: 422,
}
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 githubError = new GithubError(error)
expect(githubError.status).toBe(404)

View File

@@ -1,22 +1,21 @@
import { GithubErrorDetail } from "../src/GithubErrorDetail"
describe('GithubErrorDetail', () => {
it('provides error code', () => {
describe("GithubErrorDetail", () => {
it("provides error code", () => {
const error = {
code: "missing"
code: "missing",
}
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 error = {
code: "missing",
resource: resource
resource: resource,
}
const detail = new GithubErrorDetail(error)
@@ -25,13 +24,13 @@ describe('GithubErrorDetail', () => {
expect(message).toBe(`${resource} does not exist.`)
})
it('generates missing field error message', () => {
it("generates missing field error message", () => {
const resource = "release"
const field = "body"
const error = {
code: "missing_field",
field: field,
resource: resource
resource: resource,
}
const detail = new GithubErrorDetail(error)
@@ -40,13 +39,13 @@ describe('GithubErrorDetail', () => {
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 field = "body"
const error = {
code: "invalid",
field: field,
resource: resource
resource: resource,
}
const detail = new GithubErrorDetail(error)
@@ -55,11 +54,11 @@ describe('GithubErrorDetail', () => {
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 error = {
code: "already_exists",
resource: resource
resource: resource,
}
const detail = new GithubErrorDetail(error)
@@ -68,13 +67,13 @@ describe('GithubErrorDetail', () => {
expect(message).toBe(`${resource} already exists.`)
})
describe('generates custom error message', () => {
it('with documentation url', () => {
describe("generates custom error message", () => {
it("with documentation url", () => {
const url = "https://api.example.com"
const error = {
code: "custom",
message: "foo",
documentation_url: url
documentation_url: url,
}
const detail = new GithubErrorDetail(error)
@@ -83,16 +82,16 @@ describe('GithubErrorDetail', () => {
expect(message).toBe(`foo\nPlease see ${url}.`)
})
it('without documentation url', () => {
it("without documentation url", () => {
const error = {
code: "custom",
message: "foo"
message: "foo",
}
const detail = new GithubErrorDetail(error)
const message = detail.toString()
expect(message).toBe('foo')
expect(message).toBe("foo")
})
})
})
})

View File

@@ -1,23 +1,24 @@
const mockGetInput = jest.fn();
const mockGetInput = jest.fn()
const mockGetBooleanInput = jest.fn()
const mockGlob = jest.fn()
const mockReadFileSync = jest.fn();
const mockStatSync = jest.fn();
const mockReadFileSync = jest.fn()
const mockStatSync = jest.fn()
import {Artifact} from "../src/Artifact";
import {ArtifactGlobber} from "../src/ArtifactGlobber";
import {Context} from "@actions/github/lib/context";
import {Inputs, CoreInputs} from "../src/Inputs";
import { Artifact } from "../src/Artifact"
import { ArtifactGlobber } from "../src/ArtifactGlobber"
import { Context } from "@actions/github/lib/context"
import { Inputs, CoreInputs } from "../src/Inputs"
const artifacts = [
new Artifact('a/art1'),
new Artifact('b/art2')
]
const artifacts = [new Artifact("a/art1"), new Artifact("b/art2")]
jest.mock('@actions/core', () => {
return {getInput: mockGetInput};
jest.mock("@actions/core", () => {
return {
getInput: mockGetInput,
getBooleanInput: mockGetBooleanInput,
}
})
jest.mock('fs', () => {
jest.mock("fs", () => {
// existsSync is used by Context's constructor
// noinspection JSUnusedGlobalSymbols
return {
@@ -25,415 +26,433 @@ jest.mock('fs', () => {
return false
},
readFileSync: mockReadFileSync,
statSync: mockStatSync
};
statSync: mockStatSync,
}
})
describe('Inputs', () => {
let context: Context;
let inputs: Inputs;
describe("Inputs", () => {
let context: Context
let inputs: Inputs
beforeEach(() => {
mockGetInput.mockReset()
context = new Context()
inputs = new CoreInputs(createGlobber(), context)
})
describe('commit', () => {
it('returns commit', () => {
mockGetInput.mockReturnValueOnce('commit')
expect(inputs.commit).toBe('commit')
describe("commit", () => {
it("returns commit", () => {
mockGetInput.mockReturnValueOnce("commit")
expect(inputs.commit).toBe("commit")
})
it('returns undefined when omitted', () => {
mockGetInput.mockReturnValueOnce('')
it("returns undefined when omitted", () => {
mockGetInput.mockReturnValueOnce("")
expect(inputs.commit).toBeUndefined()
})
})
it('returns token', () => {
mockGetInput.mockReturnValue('42')
expect(inputs.token).toBe('42')
it("returns token", () => {
mockGetInput.mockReturnValue("42")
expect(inputs.token).toBe("42")
})
describe('allowsUpdates', () => {
it('returns false', () => {
describe("allowsUpdates", () => {
it("returns false", () => {
expect(inputs.allowUpdates).toBe(false)
})
it('returns true', () => {
mockGetInput.mockReturnValue('true')
it("returns true", () => {
mockGetInput.mockReturnValue("true")
expect(inputs.allowUpdates).toBe(true)
})
})
describe('artifactErrorsFailBuild', () => {
it('returns false', () => {
describe("artifactErrorsFailBuild", () => {
it("returns false", () => {
expect(inputs.artifactErrorsFailBuild).toBe(false)
})
it('returns true', () => {
mockGetInput.mockReturnValue('true')
it("returns true", () => {
mockGetInput.mockReturnValue("true")
expect(inputs.artifactErrorsFailBuild).toBe(true)
})
})
describe('artifacts', () => {
it('globber told to throw errors', () => {
mockGetInput.mockReturnValueOnce('art1')
.mockReturnValueOnce('contentType')
.mockReturnValueOnce('true')
describe("artifacts", () => {
it("globber told to throw errors", () => {
mockGetInput.mockReturnValueOnce("art1").mockReturnValueOnce("contentType").mockReturnValueOnce("true")
expect(inputs.artifacts).toEqual(artifacts)
expect(mockGlob).toBeCalledTimes(1)
expect(mockGlob).toBeCalledWith('art1', 'contentType', true)
expect(mockGlob).toHaveBeenCalledTimes(1)
expect(mockGlob).toHaveBeenCalledWith("art1", "contentType", true)
})
it('returns empty artifacts', () => {
mockGetInput.mockReturnValueOnce('')
.mockReturnValueOnce('')
it("returns empty artifacts", () => {
mockGetInput.mockReturnValueOnce("").mockReturnValueOnce("")
expect(inputs.artifacts).toEqual([])
expect(mockGlob).toBeCalledTimes(0)
expect(mockGlob).toHaveBeenCalledTimes(0)
})
it('returns input.artifacts', () => {
mockGetInput.mockReturnValueOnce('art1')
.mockReturnValueOnce('contentType')
.mockReturnValueOnce('false')
it("returns input.artifacts", () => {
mockGetInput.mockReturnValueOnce("art1").mockReturnValueOnce("contentType").mockReturnValueOnce("false")
expect(inputs.artifacts).toEqual(artifacts)
expect(mockGlob).toBeCalledTimes(1)
expect(mockGlob).toBeCalledWith('art1', 'contentType', false)
expect(mockGlob).toHaveBeenCalledTimes(1)
expect(mockGlob).toHaveBeenCalledWith("art1", "contentType", false)
})
it('returns input.artifacts with default contentType', () => {
mockGetInput.mockReturnValueOnce('art1')
.mockReturnValueOnce('')
.mockReturnValueOnce('false')
it("returns input.artifacts with default contentType", () => {
mockGetInput.mockReturnValueOnce("art1").mockReturnValueOnce("").mockReturnValueOnce("false")
expect(inputs.artifacts).toEqual(artifacts)
expect(mockGlob).toBeCalledTimes(1)
expect(mockGlob).toBeCalledWith('art1', 'raw', false)
expect(mockGlob).toHaveBeenCalledTimes(1)
expect(mockGlob).toHaveBeenCalledWith("art1", "raw", false)
})
it('returns input.artifact', () => {
mockGetInput.mockReturnValueOnce('')
.mockReturnValueOnce('art2')
.mockReturnValueOnce('contentType')
.mockReturnValueOnce('false')
it("returns input.artifact", () => {
mockGetInput
.mockReturnValueOnce("")
.mockReturnValueOnce("art2")
.mockReturnValueOnce("contentType")
.mockReturnValueOnce("false")
expect(inputs.artifacts).toEqual(artifacts)
expect(mockGlob).toBeCalledTimes(1)
expect(mockGlob).toBeCalledWith('art2', 'contentType', false)
expect(mockGlob).toHaveBeenCalledTimes(1)
expect(mockGlob).toHaveBeenCalledWith("art2", "contentType", false)
})
})
describe('createdDraft', () => {
it('returns false', () => {
describe("createdDraft", () => {
it("returns false", () => {
expect(inputs.createdDraft).toBe(false)
})
it('returns true', () => {
mockGetInput.mockReturnValue('true')
it("returns true", () => {
mockGetInput.mockReturnValue("true")
expect(inputs.createdDraft).toBe(true)
})
})
describe('createdReleaseBody', () => {
it('returns input body', () => {
mockGetInput
.mockReturnValueOnce('false')
.mockReturnValueOnce('body')
expect(inputs.createdReleaseBody).toBe('body')
describe("createdReleaseBody", () => {
it("returns input body", () => {
mockGetInput.mockReturnValueOnce("false").mockReturnValueOnce("body")
expect(inputs.createdReleaseBody).toBe("body")
})
it('returns body file contents', () => {
mockGetInput
.mockReturnValueOnce('false')
.mockReturnValueOnce('')
.mockReturnValueOnce('a/path')
mockReadFileSync.mockReturnValue('file')
it("returns body file contents", () => {
mockGetInput.mockReturnValueOnce("false").mockReturnValueOnce("").mockReturnValueOnce("a/path")
mockReadFileSync.mockReturnValue("file")
expect(inputs.createdReleaseBody).toBe('file')
expect(inputs.createdReleaseBody).toBe("file")
})
it('returns empty', () => {
mockGetInput
.mockReturnValueOnce('false')
.mockReturnValueOnce('')
.mockReturnValueOnce('')
expect(inputs.createdReleaseBody).toBe('')
it("returns empty", () => {
mockGetInput.mockReturnValueOnce("false").mockReturnValueOnce("").mockReturnValueOnce("")
expect(inputs.createdReleaseBody).toBe("")
})
it('returns undefined when omitted', () => {
mockGetInput
.mockReturnValueOnce('true')
.mockReturnValueOnce('body')
it("returns undefined when omitted", () => {
mockGetInput.mockReturnValueOnce("true").mockReturnValueOnce("body")
expect(inputs.createdReleaseBody).toBeUndefined()
})
})
describe('createdReleaseName', () => {
it('returns input name', () => {
mockGetInput
.mockReturnValueOnce('false')
.mockReturnValueOnce('name')
expect(inputs.createdReleaseName).toBe('name')
describe("createdReleaseName", () => {
it("returns input name", () => {
mockGetInput.mockReturnValueOnce("false").mockReturnValueOnce("name")
expect(inputs.createdReleaseName).toBe("name")
})
it('returns undefined when omitted', () => {
mockGetInput
.mockReturnValueOnce('true')
.mockReturnValueOnce('name')
it("returns undefined when omitted", () => {
mockGetInput.mockReturnValueOnce("true").mockReturnValueOnce("name")
expect(inputs.createdReleaseName).toBeUndefined()
})
it('returns tag', () => {
mockGetInput
.mockReturnValueOnce('false')
.mockReturnValueOnce('')
context.ref = 'refs/tags/sha-tag'
expect(inputs.createdReleaseName).toBe('sha-tag')
it("returns tag", () => {
mockGetInput.mockReturnValueOnce("false").mockReturnValueOnce("")
context.ref = "refs/tags/sha-tag"
expect(inputs.createdReleaseName).toBe("sha-tag")
})
})
describe('discussionCategory', () => {
it('returns category', () => {
mockGetInput.mockReturnValue('Release')
expect(inputs.discussionCategory).toBe('Release')
describe("discussionCategory", () => {
it("returns category", () => {
mockGetInput.mockReturnValue("Release")
expect(inputs.discussionCategory).toBe("Release")
})
it('returns undefined', () => {
mockGetInput.mockReturnValue('')
it("returns undefined", () => {
mockGetInput.mockReturnValue("")
expect(inputs.discussionCategory).toBe(undefined)
})
})
describe('generateReleaseNotes', () => {
it('returns returns true', function () {
describe("generateReleaseNotes", () => {
it("returns returns true", function () {
mockGetInput.mockReturnValue("true")
expect(inputs.generateReleaseNotes).toBe(true)
});
})
it('returns false when omitted', function () {
it("returns false when omitted", function () {
mockGetInput.mockReturnValue("")
expect(inputs.generateReleaseNotes).toBe(false)
});
})
})
describe('owner', () => {
it('returns owner from context', function () {
describe("immutableCreate", () => {
it("returns false by default", function () {
mockGetInput.mockReturnValue("")
expect(inputs.immutableCreate).toBe(false)
})
it("returns true when explicitly set", function () {
mockGetInput.mockReturnValue("true")
expect(inputs.immutableCreate).toBe(true)
})
it("returns false when explicitly disabled", function () {
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", function () {
process.env.GITHUB_REPOSITORY = "owner/repo"
mockGetInput.mockReturnValue("")
expect(inputs.owner).toBe("owner")
});
it('returns owner from inputs', function () {
})
it("returns owner from inputs", function () {
mockGetInput.mockReturnValue("owner")
expect(inputs.owner).toBe("owner")
});
})
})
describe('createdPrerelase', () => {
it('returns false', () => {
describe("createdPrerelase", () => {
it("returns false", () => {
expect(inputs.createdPrerelease).toBe(false)
})
it('returns true', () => {
mockGetInput.mockReturnValue('true')
it("returns true", () => {
mockGetInput.mockReturnValue("true")
expect(inputs.createdPrerelease).toBe(true)
})
})
describe('replacesArtifacts', () => {
it('returns false', () => {
describe("replacesArtifacts", () => {
it("returns false", () => {
expect(inputs.replacesArtifacts).toBe(false)
})
it('returns true', () => {
mockGetInput.mockReturnValue('true')
it("returns true", () => {
mockGetInput.mockReturnValue("true")
expect(inputs.replacesArtifacts).toBe(true)
})
})
describe('removeArtifacts', () => {
it('returns false', () => {
describe("removeArtifacts", () => {
it("returns false", () => {
expect(inputs.removeArtifacts).toBe(false)
})
it('returns true', () => {
mockGetInput.mockReturnValue('true')
it("returns true", () => {
mockGetInput.mockReturnValue("true")
expect(inputs.removeArtifacts).toBe(true)
})
})
describe('repo', () => {
it('returns repo from context', function () {
describe("repo", () => {
it("returns repo from context", function () {
process.env.GITHUB_REPOSITORY = "owner/repo"
mockGetInput.mockReturnValue("")
expect(inputs.repo).toBe("repo")
});
it('returns repo from inputs', function () {
})
it("returns repo from inputs", function () {
mockGetInput.mockReturnValue("repo")
expect(inputs.repo).toBe("repo")
});
})
})
describe('tag', () => {
it('returns input tag', () => {
mockGetInput.mockReturnValue('tag')
expect(inputs.tag).toBe('tag')
describe("skipIfReleaseExists", () => {
it("returns false", () => {
mockGetBooleanInput.mockReturnValue(false)
expect(inputs.skipIfReleaseExists).toBe(false)
})
it('returns context sha when input is empty', () => {
mockGetInput.mockReturnValue('')
context.ref = 'refs/tags/sha-tag'
expect(inputs.tag).toBe('sha-tag')
it("returns true", () => {
mockGetBooleanInput.mockReturnValue(true)
expect(inputs.skipIfReleaseExists).toBe(true)
})
it('returns context sha when input is null', () => {
})
describe("tag", () => {
it("returns input tag", () => {
mockGetInput.mockReturnValue("tag")
expect(inputs.tag).toBe("tag")
})
it("returns context sha when input is empty", () => {
mockGetInput.mockReturnValue("")
context.ref = "refs/tags/sha-tag"
expect(inputs.tag).toBe("sha-tag")
})
it("returns context sha when input is null", () => {
mockGetInput.mockReturnValue(null)
context.ref = 'refs/tags/sha-tag'
expect(inputs.tag).toBe('sha-tag')
context.ref = "refs/tags/sha-tag"
expect(inputs.tag).toBe("sha-tag")
})
it('throws if no tag', () => {
it("throws if no tag", () => {
context.ref = ""
expect(() => inputs.tag).toThrow()
})
})
describe('updatedDraft', () => {
it('returns false', () => {
describe("updatedDraft", () => {
it("returns false", () => {
expect(inputs.updatedDraft).toBe(false)
})
it('returns true', () => {
mockGetInput
.mockReturnValueOnce('false')
.mockReturnValue('true')
it("returns true", () => {
mockGetInput.mockReturnValueOnce("false").mockReturnValue("true")
expect(inputs.updatedDraft).toBe(true)
})
it('returns true when omitted is blank', () => {
mockGetInput
.mockReturnValueOnce('')
.mockReturnValue('true')
it("returns true when omitted is blank", () => {
mockGetInput.mockReturnValueOnce("").mockReturnValue("true")
expect(inputs.updatedDraft).toBe(true)
})
it('returns undefined when omitted for update', () => {
mockGetInput
.mockReturnValueOnce('true')
.mockReturnValueOnce('true')
it("returns undefined when omitted for update", () => {
mockGetInput.mockReturnValueOnce("true").mockReturnValueOnce("true")
expect(inputs.updatedDraft).toBeUndefined()
})
})
describe('updatedReleaseBody', () => {
it('returns input body', () => {
mockGetInput
.mockReturnValueOnce('false')
.mockReturnValueOnce('false')
.mockReturnValueOnce('body')
expect(inputs.updatedReleaseBody).toBe('body')
describe("updatedReleaseBody", () => {
it("returns input body", () => {
mockGetInput.mockReturnValueOnce("false").mockReturnValueOnce("false").mockReturnValueOnce("body")
expect(inputs.updatedReleaseBody).toBe("body")
})
it('returns body file contents', () => {
it("returns body file contents", () => {
mockGetInput
.mockReturnValueOnce('false')
.mockReturnValueOnce('false')
.mockReturnValueOnce('')
.mockReturnValueOnce('a/path')
mockReadFileSync.mockReturnValue('file')
.mockReturnValueOnce("false")
.mockReturnValueOnce("false")
.mockReturnValueOnce("")
.mockReturnValueOnce("a/path")
mockReadFileSync.mockReturnValue("file")
expect(inputs.updatedReleaseBody).toBe('file')
expect(inputs.updatedReleaseBody).toBe("file")
})
it('returns empty', () => {
it("returns empty", () => {
mockGetInput
.mockReturnValueOnce('false')
.mockReturnValueOnce('false')
.mockReturnValueOnce('')
.mockReturnValueOnce('')
expect(inputs.updatedReleaseBody).toBe('')
.mockReturnValueOnce("false")
.mockReturnValueOnce("false")
.mockReturnValueOnce("")
.mockReturnValueOnce("")
expect(inputs.updatedReleaseBody).toBe("")
})
it('returns undefined when omitted', () => {
mockGetInput
.mockReturnValueOnce('true')
.mockReturnValueOnce('false')
.mockReturnValueOnce('body')
it("returns undefined when omitted", () => {
mockGetInput.mockReturnValueOnce("true").mockReturnValueOnce("false").mockReturnValueOnce("body")
expect(inputs.updatedReleaseBody).toBeUndefined()
})
it('returns undefined when omitted for update', () => {
mockGetInput
.mockReturnValueOnce('false')
.mockReturnValueOnce('true')
.mockReturnValueOnce('body')
it("returns undefined when omitted for update", () => {
mockGetInput.mockReturnValueOnce("false").mockReturnValueOnce("true").mockReturnValueOnce("body")
expect(inputs.updatedReleaseBody).toBeUndefined()
})
})
describe('updatedReleaseName', () => {
it('returns input name', () => {
mockGetInput
.mockReturnValueOnce('false')
.mockReturnValueOnce('false')
.mockReturnValueOnce('name')
expect(inputs.updatedReleaseName).toBe('name')
describe("updatedReleaseName", () => {
it("returns input name", () => {
mockGetInput.mockReturnValueOnce("false").mockReturnValueOnce("false").mockReturnValueOnce("name")
expect(inputs.updatedReleaseName).toBe("name")
})
it('returns undefined when omitted', () => {
mockGetInput
.mockReturnValueOnce('true')
.mockReturnValueOnce('false')
.mockReturnValueOnce('name')
it("returns undefined when omitted", () => {
mockGetInput.mockReturnValueOnce("true").mockReturnValueOnce("false").mockReturnValueOnce("name")
expect(inputs.updatedReleaseName).toBeUndefined()
})
it('returns undefined when omitted for update', () => {
mockGetInput
.mockReturnValueOnce('false')
.mockReturnValueOnce('true')
.mockReturnValueOnce('name')
it("returns undefined when omitted for update", () => {
mockGetInput.mockReturnValueOnce("false").mockReturnValueOnce("true").mockReturnValueOnce("name")
expect(inputs.updatedReleaseName).toBeUndefined()
})
it('returns tag', () => {
mockGetInput
.mockReturnValueOnce('false')
.mockReturnValueOnce('false')
.mockReturnValueOnce('')
context.ref = 'refs/tags/sha-tag'
expect(inputs.updatedReleaseName).toBe('sha-tag')
it("returns tag", () => {
mockGetInput.mockReturnValueOnce("false").mockReturnValueOnce("false").mockReturnValueOnce("")
context.ref = "refs/tags/sha-tag"
expect(inputs.updatedReleaseName).toBe("sha-tag")
})
})
describe('updatedPrerelease', () => {
it('returns false', () => {
mockGetInput
.mockReturnValueOnce('false')
.mockReturnValueOnce('false')
describe("updatedPrerelease", () => {
it("returns false", () => {
mockGetInput.mockReturnValueOnce("false").mockReturnValueOnce("false")
expect(inputs.updatedPrerelease).toBe(false)
})
it('returns true', () => {
mockGetInput
.mockReturnValueOnce('false')
.mockReturnValueOnce('true')
it("returns true", () => {
mockGetInput.mockReturnValueOnce("false").mockReturnValueOnce("true")
expect(inputs.updatedPrerelease).toBe(true)
})
it('returns undefined when omitted for update', () => {
mockGetInput
.mockReturnValueOnce('true')
.mockReturnValueOnce('false')
it("returns undefined when omitted for update", () => {
mockGetInput.mockReturnValueOnce("true").mockReturnValueOnce("false")
expect(inputs.updatedPrerelease).toBeUndefined()
})
})
describe("updateOnlyUnreleased", () => {
it("returns false", () => {
expect(inputs.updateOnlyUnreleased).toBe(false)
})
it("returns true", () => {
mockGetInput.mockReturnValueOnce("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 {
const MockGlobber = jest.fn<ArtifactGlobber, any>(() => {
return {
globArtifactString: mockGlob
globArtifactString: mockGlob,
}
})
mockGlob.mockImplementation(() => artifacts)

View File

@@ -1,17 +1,18 @@
import {Action} from "../src/Action";
import * as github from "@actions/github";
import {Inputs} from "../src/Inputs";
import {GithubReleases, ReleaseData} from "../src/Releases";
import {GithubArtifactUploader} from "../src/ArtifactUploader";
import * as path from "path";
import {FileArtifactGlobber} from "../src/ArtifactGlobber";
import {Outputs} from "../src/Outputs";
import {GithubArtifactDestroyer} from "../src/ArtifactDestroyer";
import { Action } from "../src/Action"
import * as github from "@actions/github"
import { Inputs } from "../src/Inputs"
import { GithubReleases, ReleaseData } from "../src/Releases"
import { GithubArtifactUploader } from "../src/ArtifactUploader"
import * as path from "path"
import { FileArtifactGlobber } from "../src/ArtifactGlobber"
import { Outputs } from "../src/Outputs"
import { GithubArtifactDestroyer } from "../src/ArtifactDestroyer"
import { ReleaseActionSkipper } from "../src/ActionSkipper"
// 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
// - Remove skip from the test below
describe.skip('Integration Test', () => {
describe.skip("Integration Test", () => {
let action: Action
beforeEach(() => {
@@ -21,17 +22,14 @@ describe.skip('Integration Test', () => {
const inputs = getInputs()
const outputs = getOutputs()
const releases = new GithubReleases(inputs, git)
const uploader = new GithubArtifactUploader(
releases,
inputs.replacesArtifacts,
inputs.artifactErrorsFailBuild,
)
const uploader = new GithubArtifactUploader(releases, inputs.replacesArtifacts, inputs.artifactErrorsFailBuild)
const artifactDestroyer = new GithubArtifactDestroyer(releases)
action = new Action(inputs, outputs, releases, uploader, artifactDestroyer)
const actionSkipper = new ReleaseActionSkipper(inputs.skipIfReleaseExists, releases, inputs.tag)
action = new Action(inputs, outputs, releases, uploader, artifactDestroyer, actionSkipper)
})
it('Performs action', async () => {
it("Performs action", async () => {
await action.perform()
})
@@ -45,22 +43,26 @@ describe.skip('Integration Test', () => {
createdReleaseBody: "This release was generated by release-action's integration test",
createdReleaseName: "Releases Action Integration Test",
commit: undefined,
discussionCategory: 'Release',
discussionCategory: "Release",
generateReleaseNotes: true,
immutableCreate: true,
omitBodyDuringUpdate: false,
owner: "ncipollo",
createdPrerelease: false,
replacesArtifacts: true,
removeArtifacts: false,
repo: "actions-playground",
skipIfReleaseExists: false,
tag: "release-action-test",
token: getToken(),
updatedDraft: false,
updatedReleaseBody: "This release was generated by release-action's integration test",
updatedReleaseBody: "This release was updated by release-action's integration test",
updatedReleaseName: "Releases Action Integration Test",
updatedPrerelease: false
updatedPrerelease: false,
updateOnlyUnreleased: false,
}
})
return new MockInputs();
return new MockInputs()
}
function getOutputs(): Outputs {
@@ -68,7 +70,10 @@ describe.skip('Integration Test', () => {
return {
applyReleaseData(releaseData: ReleaseData) {
console.log(`Release Data: ${releaseData}`)
}
},
applyAssetUrls(assetUrls: Record<string, string>) {
console.log(`Asset URLs: ${JSON.stringify(assetUrls)}`)
},
}
})
return new MockOutputs()
@@ -76,7 +81,7 @@ describe.skip('Integration Test', () => {
function artifacts() {
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}`
return globber.globArtifactString(artifactString, "raw", false)
}
@@ -84,5 +89,4 @@ describe.skip('Integration Test', () => {
function getToken(): string {
return process.env.GITHUB_TOKEN ?? ""
}
})

View File

@@ -1,29 +1,63 @@
const mockSetOutput = jest.fn();
import { CoreOutputs, type Outputs } from "../src/Outputs"
import type { ReleaseData } from "../src/Releases"
import {CoreOutputs, Outputs} from "../src/Outputs";
import {ReleaseData} from "../src/Releases";
jest.mock("@actions/core")
const { setOutput: mockSetOutput } = jest.mocked(require("@actions/core"))
jest.mock('@actions/core', () => {
return {setOutput: mockSetOutput};
})
const TEST_URLS = {
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', () => {
let outputs: Outputs;
describe("Outputs", () => {
let outputs: Outputs
let releaseData: ReleaseData
beforeEach(() => {
outputs = new CoreOutputs()
releaseData = {
id: 1,
html_url: 'https://api.example.com/assets',
upload_url: 'https://api.example.com'
html_url: TEST_URLS.HTML_URL,
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)
expect(mockSetOutput).toBeCalledWith('id', releaseData.id)
expect(mockSetOutput).toBeCalledWith('html_url', releaseData.html_url)
expect(mockSetOutput).toBeCalledWith('upload_url', releaseData.upload_url)
expect(mockSetOutput).toHaveBeenCalledWith("id", releaseData.id)
expect(mockSetOutput).toHaveBeenCalledWith("html_url", releaseData.html_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))
})
})

View File

@@ -0,0 +1,74 @@
import { ReleaseValidator } from "../src/ReleaseValidator"
describe("validateReleaseUpdate", () => {
describe("updateOnlyUnreleased is disabled", () => {
const validator = new ReleaseValidator(false)
it("should not throw", () => {
const releaseResponse = {
draft: false,
prerelease: false,
name: "Name",
}
expect(() => {
validator.validateReleaseUpdate(releaseResponse)
}).not.toThrow()
})
})
describe("updateOnlyUnreleased is enabled", () => {
const validator = new ReleaseValidator(true)
it("should throw if neither draft or prerelease are enabled", () => {
const releaseResponse = {
draft: false,
prerelease: false,
name: "Name",
}
expect(() => {
validator.validateReleaseUpdate(releaseResponse)
}).toThrow()
})
it("should not throw if draft is enabled", () => {
const releaseResponse = {
draft: true,
prerelease: false,
name: "Name",
}
expect(() => {
validator.validateReleaseUpdate(releaseResponse)
}).not.toThrow()
})
it("should not throw if prerelease is enabled", () => {
const releaseResponse = {
draft: false,
prerelease: true,
name: "Name",
}
expect(() => {
validator.validateReleaseUpdate(releaseResponse)
}).not.toThrow()
})
it("should not throw if draft & prerelease is enabled", () => {
const releaseResponse = {
draft: true,
prerelease: true,
name: "Name",
}
expect(() => {
validator.validateReleaseUpdate(releaseResponse)
}).not.toThrow()
})
it("should default error message release name to release", () => {
const releaseResponse = {
draft: false,
prerelease: false,
name: null,
}
expect(() => {
validator.validateReleaseUpdate(releaseResponse)
}).toThrow(`Tried to update "release" which is neither a draft or prerelease. (updateOnlyUnreleased is on)`)
})
})
})

View File

@@ -47,6 +47,14 @@ inputs:
description: 'Indicates if release notes should be automatically generated.'
required: false
default: 'false'
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:
description: 'Indicates if the release should be the "latest" release or not.'
required: false
default: 'legacy'
name:
description: 'An optional name for the release. If this is omitted the tag will be used.'
required: false
@@ -94,7 +102,11 @@ inputs:
repo:
description: "Optionally specify the repo where the release should be generated. Defaults to current repo"
required: false
default: ''
default: ''
skipIfReleaseExists:
description: "When skipIfReleaseExists is enabled the action will be skipped if a non-draft release already exists for the provided tag."
required: false
default: 'false'
tag:
description: 'An optional tag for the release. If this is omitted the git ref will be used (if it is a tag).'
required: false
@@ -103,6 +115,10 @@ inputs:
description: 'The Github token.'
required: false
default: ${{ github.token }}
updateOnlyUnreleased:
description: "When allowUpdates is enabled, this will fail the action if the release it is updating is not a draft or a prerelease."
required: false
default: 'false'
outputs:
id:
description: 'The identifier of the created release.'
@@ -110,8 +126,14 @@ outputs:
description: 'The HTML URL of the release.'
upload_url:
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:
using: 'node16'
using: 'node20'
main: 'dist/index.js'
branding:
icon: 'tag'

35
biome.json Normal file
View File

@@ -0,0 +1,35 @@
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"vcs": {
"enabled": false,
"clientKind": "git",
"useIgnoreFile": true
},
"files": {
"ignoreUnknown": false,
"ignore": ["node_modules/**/*", "dist/"]
},
"formatter": {
"enabled": true,
"formatWithErrors": false,
"indentStyle": "space",
"lineEnding": "lf",
"indentWidth": 4,
"lineWidth": 120
},
"organizeImports": { "enabled": true },
"linter": {
"enabled": false,
"rules": {
"recommended": true
}
},
"javascript": {
"formatter": {
"quoteStyle": "double",
"trailingCommas": "es5",
"semicolons": "asNeeded",
"indentWidth": 4
}
}
}

46759
dist/index.js vendored

File diff suppressed because one or more lines are too long

2
dist/index.js.map vendored

File diff suppressed because one or more lines are too long

434
dist/licenses.txt vendored
View File

@@ -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.
@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
MIT
The MIT License (MIT)
@@ -47,6 +59,94 @@ 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.
@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.
@fastify/busboy
MIT
Copyright Brian White. All rights reserved.
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.
@isaacs/balanced-match
MIT
(MIT)
Original code Copyright Julian Gruber <julian@juliangruber.com>
Port to TypeScript Copyright Isaac Z. Schlueter <i@izs.me>
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.
@isaacs/brace-expansion
MIT
MIT License
Copyright Julian Gruber <julian@juliangruber.com>
TypeScript port Copyright Isaac Z. Schlueter <i@izs.me>
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
MIT
The MIT License
@@ -219,41 +319,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
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 &lt;julian@juliangruber.com&gt;
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
Apache-2.0
Apache License
@@ -459,31 +524,6 @@ Apache-2.0
limitations under the License.
brace-expansion
MIT
MIT License
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.
deprecation
ISC
The ISC License
@@ -503,58 +543,11 @@ 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
ISC
The ISC License
Copyright (c) 2009-2022 Isaac Z. Schlueter and Contributors
Copyright (c) 2009-2023 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
@@ -569,11 +562,11 @@ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
inflight
lru-cache
ISC
The ISC License
Copyright (c) Isaac Z. Schlueter
Copyright (c) 2010-2023 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
@@ -588,56 +581,11 @@ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
inherits
ISC
The ISC License
Copyright (c) Isaac Z. Schlueter
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.
is-plain-object
MIT
The MIT License (MIT)
Copyright (c) 2014-2017, Jon Schlinkert.
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.
minimatch
ISC
The ISC License
Copyright (c) 2011-2022 Isaac Z. Schlueter and Contributors
Copyright (c) 2011-2023 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
@@ -652,30 +600,23 @@ 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)
minipass
ISC
The ISC License
Copyright (c) 2016 David Frank
Copyright (c) 2017-2023 npm, Inc., Isaac Z. Schlueter, and 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.
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.
once
@@ -697,8 +638,64 @@ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
tr46
MIT
path-scurry
BlueOak-1.0.0
# Blue Oak Model License
Version 1.0.0
## Purpose
This license gives everyone as much permission to work with
this software as possible, while protecting contributors
from liability.
## Acceptance
In order to receive this license, you must agree to its
rules. The rules of this license are both obligations
under that agreement and conditions to your license.
You must not do anything with this software that triggers
a rule that you cannot or will not follow.
## Copyright
Each contributor licenses you to do everything with this
software that would otherwise infringe that contributor's
copyright in it.
## Notices
You must ensure that everyone who gets a copy of
any part of this software from you, with or without
changes, also gets the text of this license or a link to
<https://blueoakcouncil.org/license/1.0.0>.
## Excuse
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
Each contributor licenses you to do everything with this
software that would otherwise infringe any patent claims
they can license or become able to license.
## Reliability
No contributor can revoke this license.
## No Liability
***As far as the law allows, this software comes as is,
without any warranty or condition, and no contributor
will be liable to anyone for any damages related to this
software or this license, under any kind of legal claim.***
tunnel
MIT
@@ -725,6 +722,31 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
undici
MIT
MIT License
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.
universal-user-agent
ISC
# [ISC License](https://spdx.org/licenses/ISC)
@@ -749,60 +771,6 @@ 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.
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) 20152016 Sebastian Mayr
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.
wrappy
ISC
The ISC License

File diff suppressed because one or more lines are too long

View File

@@ -1,100 +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.Action = void 0;
const GithubError_1 = require("./GithubError");
class Action {
constructor(inputs, outputs, releases, uploader, artifactDestroyer) {
this.inputs = inputs;
this.outputs = outputs;
this.releases = releases;
this.uploader = uploader;
this.artifactDestroyer = artifactDestroyer;
}
perform() {
return __awaiter(this, void 0, void 0, function* () {
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);
}
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.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.createdReleaseName, this.inputs.createdPrerelease);
});
}
}
exports.Action = Action;

View File

@@ -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;

View File

@@ -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;

View File

@@ -1,75 +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");
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 => 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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -1,190 +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 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 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;
}
static get omitNameDuringUpdate() {
return core.getInput('omitNameDuringUpdate') == 'true';
}
stringFromFile(path) {
return (0, fs_1.readFileSync)(path, 'utf-8');
}
}
exports.CoreInputs = CoreInputs;

View File

@@ -1,69 +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");
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 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);
}
run();

View File

@@ -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;

View File

@@ -1,104 +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, 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,
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, 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,
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;

View File

@@ -1,71 +1,68 @@
{
"name": "release-action",
"version": "1.1.0",
"private": true,
"description": "An action which manages a github release",
"main": "lib/main.js",
"scripts": {
"build": "tsc",
"clean": "rm -rf lib/*",
"coverage": "jest --coverage",
"debug": "yarn clean && yarn install && yarn build && yarn package",
"package": "ncc build --source-map --license licenses.txt",
"release": "yarn clean && yarn install --production && yarn build && yarn package",
"test": "jest"
},
"repository": {
"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
}
"name": "release-action",
"version": "1.1.0",
"private": true,
"description": "An action which manages a github release",
"main": "lib/main.js",
"scripts": {
"build": "tsc",
"clean": "rm -rf lib/*",
"coverage": "jest --coverage",
"debug": "yarn clean && yarn install && yarn build && yarn package",
"format": "yarn biome format --write .",
"package": "ncc build --source-map --license licenses.txt",
"release": "yarn clean && yarn install --production && yarn build && yarn package",
"test": "jest"
},
"moduleFileExtensions": [
"js",
"ts"
],
"testEnvironment": "node",
"testMatch": [
"**/*.test.ts"
],
"testRunner": "jest-circus/runner",
"transform": {
"^.+\\.ts$": "ts-jest"
"repository": {
"type": "git",
"url": "git+https://github.com/ncipollo/release-action.git"
},
"verbose": true
},
"dependencies": {
"@actions/core": "^1.10.0",
"@actions/github": "^5.1.1",
"@types/glob": "^8.0.0",
"glob": "^8.0.3",
"untildify": "^4.0.0"
},
"devDependencies": {
"@types/jest": "^28.1.1",
"@types/node": "^18.7.23",
"jest": "^28.1.1",
"jest-circus": "^29.1.2",
"ts-jest": "^28.0.5",
"typescript": "^4.8.4"
}
"keywords": ["actions", "node", "setup"],
"author": "GitHub",
"license": "MIT",
"engines": {
"node": ">=20"
},
"jest": {
"clearMocks": true,
"collectCoverage": true,
"coveragePathIgnorePatterns": ["src/Globber.ts", "src/Releases.ts"],
"coverageThreshold": {
"global": {
"branches": 95,
"functions": 100,
"lines": 100,
"statements": 100
}
},
"moduleFileExtensions": ["js", "ts"],
"testEnvironment": "node",
"testMatch": ["**/*.test.ts"],
"testRunner": "jest-circus/runner",
"transform": {
"^.+\\.ts$": ["ts-jest", {
"tsconfig": "tsconfig.test.json"
}]
},
"verbose": true
},
"dependencies": {
"@actions/core": "^1.11.1",
"@actions/github": "^6.0.1",
"@types/node": "^22.15.29",
"glob": "^11.0.3",
"untildify": "^4.0.0"
},
"devDependencies": {
"@biomejs/biome": "1.9.4",
"@types/jest": "^29.5.14",
"jest": "^29.7.0",
"jest-circus": "^29.7.0",
"ts-jest": "^29.3.4",
"typescript": "^5.8.3"
},
"resolutions": {
"jest-cli/yargs": "^17.3.1"
}
}

6
sheepit.toml Normal file
View File

@@ -0,0 +1,6 @@
[repository]
enable_commit = true
tag_pattern = "v{version}"
[scripts]
before_commit = "yarn release"

View File

@@ -1,100 +1,93 @@
import {Inputs} from "./Inputs";
import {
import * as core from "@actions/core"
import type { ActionSkipper } from "./ActionSkipper"
import type { ArtifactDestroyer } from "./ArtifactDestroyer"
import type { ArtifactUploader } from "./ArtifactUploader"
import { GithubError } from "./GithubError"
import type { Inputs } from "./Inputs"
import type { Outputs } from "./Outputs"
import { ReleaseValidator } from "./ReleaseValidator"
import type {
CreateOrUpdateReleaseResponse,
CreateReleaseResponse,
ReleaseByTagResponse,
Releases,
UpdateReleaseResponse
} from "./Releases";
import {ArtifactUploader} from "./ArtifactUploader";
import {GithubError} from "./GithubError";
import {Outputs} from "./Outputs";
import {ArtifactDestroyer} from "./ArtifactDestroyer";
UpdateReleaseResponse,
} from "./Releases"
export class Action {
private inputs: Inputs
private outputs: Outputs
private releases: Releases
private artifactDestroyer: ArtifactDestroyer
private uploader: ArtifactUploader
private artifactDestroyer: ArtifactDestroyer
private skipper: ActionSkipper
constructor(inputs: Inputs,
outputs: Outputs,
releases: Releases,
uploader: ArtifactUploader,
artifactDestroyer: ArtifactDestroyer) {
private releaseValidator: ReleaseValidator
constructor(
inputs: Inputs,
outputs: Outputs,
releases: Releases,
uploader: ArtifactUploader,
artifactDestroyer: ArtifactDestroyer,
skipper: ActionSkipper
) {
this.inputs = inputs
this.outputs = outputs
this.releases = releases
this.uploader = uploader
this.artifactDestroyer = artifactDestroyer
this.skipper = skipper
this.releaseValidator = new ReleaseValidator(inputs.updateOnlyUnreleased)
}
async perform() {
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)
if (await this.skipper.shouldSkip()) {
core.notice("Skipping action, release already exists and skipIfReleaseExists is enabled.")
return
}
this.outputs.applyReleaseData(releaseData)
await this.createOrUpdateRelease()
}
private async createOrUpdateRelease(): Promise<CreateOrUpdateReleaseResponse> {
private async createOrUpdateRelease() {
if (this.inputs.allowUpdates) {
let getResponse: ReleaseByTagResponse
try {
getResponse = await this.releases.getByTag(this.inputs.tag)
} catch (error: any) {
return await this.checkForMissingReleaseError(error)
await this.checkForMissingReleaseError(error)
return
}
return await this.updateRelease(getResponse.data.id)
// Fail if this isn't an unreleased release & updateOnlyUnreleased is enabled.
this.releaseValidator.validateReleaseUpdate(getResponse.data)
await this.updateRelease(getResponse.data.id)
} 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)) {
return await this.updateDraftOrCreateRelease()
await this.updateDraftOrCreateRelease()
} else {
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.updatedReleaseName,
this.inputs.updatedPrerelease
)
}
private static noPublishedRelease(error: any): boolean {
const githubError = new GithubError(error)
return githubError.status == 404
}
private async updateDraftOrCreateRelease(): Promise<CreateReleaseResponse | UpdateReleaseResponse> {
private async updateDraftOrCreateRelease(): Promise<void> {
const draftReleaseId = await this.findMatchingDraftReleaseId()
if (draftReleaseId) {
return await this.updateRelease(draftReleaseId)
await this.updateRelease(draftReleaseId)
} else {
return await this.createRelease()
await this.createRelease()
}
}
@@ -102,21 +95,120 @@ export class Action {
const tag = this.inputs.tag
const response = await this.releases.listReleases()
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
}
private async createRelease(): Promise<CreateReleaseResponse> {
return await this.releases.create(
private async updateRelease(id: number) {
const releaseBody = await this.combineBodyWithReleaseNotes(this.inputs.updatedReleaseBody, true)
const releaseResponse = await this.releases.update(
id,
this.inputs.tag,
this.inputs.createdReleaseBody,
releaseBody,
this.inputs.commit,
this.inputs.discussionCategory,
this.inputs.createdDraft,
this.inputs.generateReleaseNotes,
this.inputs.updatedDraft,
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.createdReleaseName,
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)
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)
}
}

28
src/ActionSkipper.ts Normal file
View File

@@ -0,0 +1,28 @@
import { Releases } from "./Releases"
export interface ActionSkipper {
shouldSkip(): Promise<boolean>
}
export class ReleaseActionSkipper {
constructor(
private skipIfReleaseExists: boolean,
private releases: Releases,
private tag: string
) {}
async shouldSkip(): Promise<boolean> {
if (!this.skipIfReleaseExists) {
// Bail if skip flag isn't set.
return false
}
try {
const getResponse = await this.releases.getByTag(this.tag)
return getResponse.data != null
} catch (error: any) {
// There is either no release or something else went wrong. Either way, run the action.
return false
}
}
}

View File

@@ -1,5 +1,5 @@
import { basename } from "path";
import { readFileSync, statSync } from "fs";
import { basename } from "path"
import { createReadStream, readFileSync, ReadStream, statSync } from "fs"
export class Artifact {
readonly contentType: string
@@ -9,14 +9,14 @@ export class Artifact {
constructor(path: string, contentType: string = "raw") {
this.path = path
this.name = basename(path)
this.contentType = contentType;
this.contentType = contentType
}
get contentLength(): number {
return statSync(this.path).size
}
readFile(): Buffer {
return readFileSync(this.path)
readFile(): ReadStream {
return createReadStream(this.path)
}
}

View File

@@ -1,13 +1,12 @@
import {Releases} from "./Releases";
import * as core from "@actions/core";
import { Releases } from "./Releases"
import * as core from "@actions/core"
export interface ArtifactDestroyer {
destroyArtifacts(releaseId: number): Promise<void>
}
export class GithubArtifactDestroyer implements ArtifactDestroyer {
constructor(private releases: Releases) {
}
constructor(private releases: Releases) {}
async destroyArtifacts(releaseId: number): Promise<void> {
const releaseAssets = await this.releases.listArtifactsForRelease(releaseId)
@@ -17,4 +16,4 @@ export class GithubArtifactDestroyer implements ArtifactDestroyer {
await this.releases.deleteArtifact(asset.id)
}
}
}
}

View File

@@ -1,8 +1,9 @@
import * as core from '@actions/core';
import {Globber, FileGlobber} from "./Globber";
import {Artifact} from "./Artifact";
import untildify from "untildify";
import {ArtifactPathValidator} from "./ArtifactPathValidator";
import * as core from "@actions/core"
import { Globber, FileGlobber } from "./Globber"
import { Artifact } from "./Artifact"
import untildify from "untildify"
import { ArtifactPathValidator } from "./ArtifactPathValidator"
import { PathNormalizer } from "./PathNormalizer"
export interface ArtifactGlobber {
globArtifactString(artifact: string, contentType: string, errorsFailBuild: boolean): Artifact[]
@@ -17,13 +18,15 @@ export class FileArtifactGlobber implements ArtifactGlobber {
globArtifactString(artifact: string, contentType: string, errorsFailBuild: boolean): Artifact[] {
const split = /[,\n]/
return artifact.split(split)
.map(path => path.trimStart())
.map(path => FileArtifactGlobber.expandPath(path))
.map(pattern => this.globPattern(pattern, errorsFailBuild))
return artifact
.split(split)
.map((path) => path.trimStart())
.map((path) => 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(path, contentType))
.map((path) => new Artifact(path, contentType))
}
private globPattern(pattern: string, errorsFailBuild: boolean): [string, string[]] {
@@ -54,4 +57,4 @@ export class FileArtifactGlobber implements ArtifactGlobber {
private static expandPath(path: string): string {
return untildify(path)
}
}
}

View File

@@ -1,29 +1,29 @@
import * as core from "@actions/core";
import {statSync} from "fs";
import * as core from "@actions/core"
import { statSync } from "fs"
export class ArtifactPathValidator {
private readonly errorsFailBuild: boolean;
private paths: string[];
private readonly errorsFailBuild: boolean
private paths: string[]
private readonly pattern: string
constructor(errorsFailBuild: boolean, paths: string[], pattern: string) {
this.paths = paths;
this.paths = paths
this.pattern = pattern
this.errorsFailBuild = errorsFailBuild;
this.errorsFailBuild = errorsFailBuild
}
validate(): string[] {
this.verifyPathsNotEmpty()
return this.paths.filter((path) => this.verifyNotDirectory(path))
}
private verifyPathsNotEmpty() {
if (this.paths.length == 0) {
const message = `Artifact pattern:${this.pattern} did not match any files`
this.reportError(message)
}
}
private verifyNotDirectory(path: string): boolean {
const isDir = statSync(path).isDirectory()
if (isDir) {
@@ -32,7 +32,7 @@ export class ArtifactPathValidator {
}
return !isDir
}
private reportError(message: string) {
if (this.errorsFailBuild) {
throw Error(message)
@@ -40,4 +40,4 @@ export class ArtifactPathValidator {
core.warning(message)
}
}
}
}

View File

@@ -1,51 +1,54 @@
import * as core from '@actions/core';
import {Artifact} from "./Artifact";
import {Releases} from "./Releases";
import * as core from "@actions/core"
import { Artifact } from "./Artifact"
import { Releases } from "./Releases"
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 {
constructor(
private releases: Releases,
private replacesExistingArtifacts: boolean = true,
private throwsUploadErrors: boolean = false,
) {
}
private throwsUploadErrors: boolean = false
) {}
async uploadArtifacts(artifacts: Artifact[],
releaseId: number,
uploadUrl: string): Promise<void> {
async uploadArtifacts(artifacts: Artifact[], releaseId: number, uploadUrl: string): Promise<Record<string, string>> {
if (this.replacesExistingArtifacts) {
await this.deleteUpdatedArtifacts(artifacts, releaseId)
}
const assetUrls: Record<string, string> = {}
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,
releaseId: number,
uploadUrl: string,
retry = 3) {
private async uploadArtifact(artifact: Artifact, releaseId: number, uploadUrl: string, retry = 3): Promise<string | null> {
try {
core.debug(`Uploading artifact ${artifact.name}...`)
await this.releases.uploadArtifact(uploadUrl,
const assetResponse = await this.releases.uploadArtifact(
uploadUrl,
artifact.contentLength,
artifact.contentType,
artifact.readFile(),
artifact.name,
releaseId)
releaseId
)
return assetResponse.data.browser_download_url
} catch (error: any) {
if (error.status >= 500 && retry > 0) {
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 {
if (this.throwsUploadErrors) {
throw Error(`Failed to upload artifact ${artifact.name}. ${error.message}.`)
} else {
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> {
const releaseAssets = await this.releases.listArtifactsForRelease(releaseId)
const assetByName: Record<string, { id: number; name: string }> = {}
releaseAssets.forEach(asset => {
releaseAssets.forEach((asset) => {
assetByName[asset.name] = asset
});
})
for (const artifact of artifacts) {
const asset = assetByName[artifact.name]
if (asset) {

View File

@@ -1,4 +1,4 @@
import {GithubErrorDetail} from "./GithubErrorDetail"
import { GithubErrorDetail } from "./GithubErrorDetail"
export class GithubError {
private error: any
@@ -31,14 +31,20 @@ export class GithubError {
const errors = this.githubErrors
const status = this.status
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 {
return `Error ${status}: ${message}`
return `Error ${status}: ${message}${this.remediation()}`
}
}
private errorBulletedList(errors: GithubErrorDetail[]): string {
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 ""
}
}

View File

@@ -1,5 +1,5 @@
export class GithubErrorDetail {
private error: any;
private error: any
constructor(error: any) {
this.error = error
@@ -12,13 +12,13 @@ export class GithubErrorDetail {
toString(): string {
const code = this.error.code
switch (code) {
case 'missing':
case "missing":
return this.missingResourceMessage()
case 'missing_field':
case "missing_field":
return this.missingFieldMessage()
case 'invalid':
case "invalid":
return this.invalidFieldMessage()
case 'already_exists':
case "already_exists":
return this.resourceAlreadyExists()
default:
return this.customErrorMessage()
@@ -26,7 +26,7 @@ export class GithubErrorDetail {
}
private customErrorMessage(): string {
const message = this.error.message;
const message = this.error.message
const documentation = this.error.documentation_url
let documentationMessage: string

View File

@@ -1,4 +1,4 @@
import { GlobSync } from "glob";
import { globSync } from "glob"
export interface Globber {
glob(pattern: string): string[]
@@ -6,6 +6,6 @@ export interface Globber {
export class FileGlobber implements Globber {
glob(pattern: string): string[] {
return new GlobSync(pattern, { mark: true }).found
return globSync(pattern, { mark: true })
}
}
}

View File

@@ -1,8 +1,8 @@
import * as core from '@actions/core';
import {Context} from "@actions/github/lib/context";
import {readFileSync} from 'fs';
import {ArtifactGlobber} from './ArtifactGlobber';
import {Artifact} from './Artifact';
import * as core from "@actions/core"
import { Context } from "@actions/github/lib/context"
import { readFileSync } from "fs"
import { ArtifactGlobber } from "./ArtifactGlobber"
import { Artifact } from "./Artifact"
export interface Inputs {
readonly allowUpdates: boolean
@@ -15,16 +15,21 @@ export interface Inputs {
readonly createdReleaseName?: string
readonly discussionCategory?: string
readonly generateReleaseNotes: boolean
readonly immutableCreate: boolean
readonly makeLatest?: "legacy" | "true" | "false" | undefined
readonly omitBodyDuringUpdate: boolean
readonly owner: string
readonly removeArtifacts: boolean
readonly replacesArtifacts: boolean
readonly repo: string
readonly skipIfReleaseExists: boolean
readonly tag: string
readonly token: string
readonly updatedDraft?: boolean
readonly updatedReleaseBody?: string
readonly updatedReleaseName?: string
readonly updatedPrerelease?: boolean
readonly updateOnlyUnreleased: boolean
}
export class CoreInputs implements Inputs {
@@ -37,53 +42,52 @@ export class CoreInputs implements Inputs {
}
get allowUpdates(): boolean {
const allow = core.getInput('allowUpdates')
return allow == 'true'
const allow = core.getInput("allowUpdates")
return allow == "true"
}
get artifacts(): Artifact[] {
let artifacts = core.getInput('artifacts')
let artifacts = core.getInput("artifacts")
if (!artifacts) {
artifacts = core.getInput('artifact')
artifacts = core.getInput("artifact")
}
if (artifacts) {
let contentType = core.getInput('artifactContentType')
let contentType = core.getInput("artifactContentType")
if (!contentType) {
contentType = 'raw'
contentType = "raw"
}
return this.artifactGlobber
.globArtifactString(artifacts, contentType, this.artifactErrorsFailBuild)
return this.artifactGlobber.globArtifactString(artifacts, contentType, this.artifactErrorsFailBuild)
}
return []
}
get artifactErrorsFailBuild(): boolean {
const allow = core.getInput('artifactErrorsFailBuild')
return allow == 'true'
const allow = core.getInput("artifactErrorsFailBuild")
return allow == "true"
}
private get body(): string | undefined {
const body = core.getInput('body')
const body = core.getInput("body")
if (body) {
return body
}
const bodyFile = core.getInput('bodyFile')
const bodyFile = core.getInput("bodyFile")
if (bodyFile) {
return this.stringFromFile(bodyFile)
}
return ''
return ""
}
get createdDraft(): boolean {
const draft = core.getInput('draft')
return draft == 'true'
const draft = core.getInput("draft")
return draft == "true"
}
get createdPrerelease(): boolean {
const preRelease = core.getInput('prerelease')
return preRelease == 'true'
const preRelease = core.getInput("prerelease")
return preRelease == "true"
}
get createdReleaseBody(): string | undefined {
@@ -92,7 +96,7 @@ export class CoreInputs implements Inputs {
}
private static get omitBody(): boolean {
return core.getInput('omitBody') == 'true'
return core.getInput("omitBody") == "true"
}
get createdReleaseName(): string | undefined {
@@ -101,11 +105,11 @@ export class CoreInputs implements Inputs {
}
private static get omitName(): boolean {
return core.getInput('omitName') == 'true'
return core.getInput("omitName") == "true"
}
get commit(): string | undefined {
const commit = core.getInput('commit')
const commit = core.getInput("commit")
if (commit) {
return commit
}
@@ -113,7 +117,7 @@ export class CoreInputs implements Inputs {
}
get discussionCategory(): string | undefined {
const category = core.getInput('discussionCategory')
const category = core.getInput("discussionCategory")
if (category) {
return category
}
@@ -121,48 +125,67 @@ export class CoreInputs implements Inputs {
}
private get name(): string | undefined {
const name = core.getInput('name')
const name = core.getInput("name")
if (name) {
return name
}
return this.tag
}
get generateReleaseNotes(): boolean {
const generate = core.getInput('generateReleaseNotes')
return generate == 'true'
const generate = core.getInput("generateReleaseNotes")
return generate == "true"
}
get immutableCreate(): boolean {
const immutable = core.getInput("immutableCreate")
return immutable == "true"
}
get makeLatest(): "legacy" | "true" | "false" | undefined {
let latest = core.getInput("makeLatest")
if (latest == "true" || latest == "false" || latest == "legacy") {
return latest
}
return undefined
}
get owner(): string {
let owner = core.getInput('owner')
let owner = core.getInput("owner")
if (owner) {
return owner
}
return this.context.repo.owner
}
get removeArtifacts(): boolean {
const removes = core.getInput('removeArtifacts')
return removes == 'true'
const removes = core.getInput("removeArtifacts")
return removes == "true"
}
get replacesArtifacts(): boolean {
const replaces = core.getInput('replacesArtifacts')
return replaces == 'true'
const replaces = core.getInput("replacesArtifacts")
return replaces == "true"
}
get repo(): string {
let repo = core.getInput('repo')
let repo = core.getInput("repo")
if (repo) {
return repo
}
return this.context.repo.repo
}
get skipIfReleaseExists(): boolean {
return core.getBooleanInput("skipIfReleaseExists")
}
get tag(): string {
const tag = core.getInput('tag')
const tag = core.getInput("tag")
if (tag) {
return tag;
return tag
}
const ref = this.context.ref
@@ -175,7 +198,7 @@ export class CoreInputs implements Inputs {
}
get token(): string {
return core.getInput('token', {required: true})
return core.getInput("token", { required: true })
}
get updatedDraft(): boolean | undefined {
@@ -184,16 +207,16 @@ export class CoreInputs implements Inputs {
}
private static get omitDraftDuringUpdate(): boolean {
return core.getInput('omitDraftDuringUpdate') == 'true'
return core.getInput("omitDraftDuringUpdate") == "true"
}
get updatedPrerelease(): boolean | undefined {
if (CoreInputs.omitPrereleaseDuringUpdate) return undefined
return this.createdPrerelease
}
private static get omitPrereleaseDuringUpdate(): boolean {
return core.getInput('omitPrereleaseDuringUpdate') == 'true'
return core.getInput("omitPrereleaseDuringUpdate") == "true"
}
get updatedReleaseBody(): string | undefined {
@@ -201,8 +224,8 @@ export class CoreInputs implements Inputs {
return this.body
}
private static get omitBodyDuringUpdate(): boolean {
return core.getInput('omitBodyDuringUpdate') == 'true'
get updateOnlyUnreleased(): boolean {
return core.getInput("updateOnlyUnreleased") == "true"
}
get updatedReleaseName(): string | undefined {
@@ -210,11 +233,19 @@ export class CoreInputs implements Inputs {
return this.name
}
private static get omitBodyDuringUpdate(): boolean {
return core.getInput("omitBodyDuringUpdate") == "true"
}
get omitBodyDuringUpdate(): boolean {
return CoreInputs.omitBodyDuringUpdate
}
private static get omitNameDuringUpdate(): boolean {
return core.getInput('omitNameDuringUpdate') == 'true'
return core.getInput("omitNameDuringUpdate") == "true"
}
stringFromFile(path: string): string {
return readFileSync(path, 'utf-8')
return readFileSync(path, "utf-8")
}
}

View File

@@ -1,13 +1,14 @@
import * as github from '@actions/github';
import * as core from '@actions/core';
import {CoreInputs} from './Inputs';
import {GithubReleases} from './Releases';
import {Action} from './Action';
import {GithubArtifactUploader} from './ArtifactUploader';
import {FileArtifactGlobber} from './ArtifactGlobber';
import {GithubError} from './GithubError';
import {CoreOutputs} from "./Outputs";
import {GithubArtifactDestroyer} from "./ArtifactDestroyer";
import * as github from "@actions/github"
import * as core from "@actions/core"
import { CoreInputs } from "./Inputs"
import { GithubReleases } from "./Releases"
import { Action } from "./Action"
import { GithubArtifactUploader } from "./ArtifactUploader"
import { FileArtifactGlobber } from "./ArtifactGlobber"
import { GithubError } from "./GithubError"
import { CoreOutputs } from "./Outputs"
import { GithubArtifactDestroyer } from "./ArtifactDestroyer"
import { ActionSkipper, ReleaseActionSkipper } from "./ActionSkipper"
async function run() {
try {
@@ -15,12 +16,12 @@ async function run() {
await action.perform()
} catch (error) {
const githubError = new GithubError(error)
core.setFailed(githubError.toString());
core.setFailed(githubError.toString())
}
}
function createAction(): Action {
const token = core.getInput('token')
const token = core.getInput("token")
const context = github.context
const git = github.getOctokit(token)
const globber = new FileArtifactGlobber()
@@ -28,10 +29,11 @@ function createAction(): Action {
const inputs = new CoreInputs(globber, context)
const outputs = new CoreOutputs()
const releases = new GithubReleases(inputs, git)
const skipper = new ReleaseActionSkipper(inputs.skipIfReleaseExists, releases, inputs.tag)
const uploader = new GithubArtifactUploader(releases, inputs.replacesArtifacts, inputs.artifactErrorsFailBuild)
const artifactDestroyer = new GithubArtifactDestroyer(releases)
return new Action(inputs, outputs, releases, uploader, artifactDestroyer)
return new Action(inputs, outputs, releases, uploader, artifactDestroyer, skipper)
}
run();
run()

View File

@@ -1,14 +1,22 @@
import * as core from '@actions/core';
import {ReleaseData} from "./Releases";
import * as core from "@actions/core"
import { ReleaseData } from "./Releases"
export interface Outputs {
applyReleaseData(releaseData: ReleaseData): void
applyAssetUrls(assetUrls: Record<string, string>): void
}
export class CoreOutputs implements Outputs {
applyReleaseData(releaseData: ReleaseData) {
core.setOutput('id', releaseData.id)
core.setOutput('html_url', releaseData.html_url)
core.setOutput('upload_url', releaseData.upload_url)
core.setOutput("id", releaseData.id)
core.setOutput("html_url", releaseData.html_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)
}
}

7
src/PathNormalizer.ts Normal file
View File

@@ -0,0 +1,7 @@
import path from "path"
export class PathNormalizer {
static normalizePath(pathString: string): string {
return pathString.split(path.sep).join("/")
}
}

21
src/ReleaseValidator.ts Normal file
View File

@@ -0,0 +1,21 @@
export class ReleaseValidator {
constructor(private updateOnlyUnreleased: boolean) {}
validateReleaseUpdate(releaseResponse: ReleaseStageArguments) {
if (!this.updateOnlyUnreleased) {
return
}
if (!releaseResponse.draft && !releaseResponse.prerelease) {
throw new Error(
`Tried to update "${releaseResponse.name ?? "release"}" which is neither a draft or prerelease. (updateOnlyUnreleased is on)`
)
}
}
}
export type ReleaseStageArguments = {
draft: boolean
name: string | null
prerelease: boolean
}

View File

@@ -1,7 +1,7 @@
import {GitHub} from '@actions/github/lib/utils'
import {OctokitResponse} from "@octokit/types";
import {RestEndpointMethodTypes} from "@octokit/plugin-rest-endpoint-methods";
import {Inputs} from "./Inputs";
import type { GitHub } from "@actions/github/lib/utils"
import type { RestEndpointMethodTypes } from "@octokit/plugin-rest-endpoint-methods"
import type { OctokitResponse } from "@octokit/types"
import type { Inputs } from "./Inputs"
export type CreateReleaseResponse = RestEndpointMethodTypes["repos"]["createRelease"]["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 UploadArtifactResponse = RestEndpointMethodTypes["repos"]["uploadReleaseAsset"]["response"]
export type CreateOrUpdateReleaseResponse = CreateReleaseResponse | UpdateReleaseResponse
export type GenerateReleaseNotesResponse = RestEndpointMethodTypes["repos"]["generateReleaseNotes"]["response"]
export type ReleaseData = {
id: number
html_url: string
upload_url: string
tarball_url: string | null
zipball_url: string | null
}
export interface Releases {
@@ -24,7 +27,7 @@ export interface Releases {
commitHash?: string,
discussionCategory?: string,
draft?: boolean,
generateReleaseNotes?: boolean,
makeLatest?: "legacy" | "true" | "false" | undefined,
name?: string,
prerelease?: boolean
): Promise<CreateReleaseResponse>
@@ -33,6 +36,8 @@ export interface Releases {
getByTag(tag: string): Promise<ReleaseByTagResponse>
generateReleaseNotes(tag: string): Promise<GenerateReleaseNotesResponse>
listArtifactsForRelease(releaseId: number): Promise<ListReleaseAssetsResponseData>
listReleases(): Promise<ListReleasesResponse>
@@ -44,6 +49,7 @@ export interface Releases {
commitHash?: string,
discussionCategory?: string,
draft?: boolean,
makeLatest?: "legacy" | "true" | "false" | undefined,
name?: string,
prerelease?: boolean
): Promise<UpdateReleaseResponse>
@@ -54,7 +60,7 @@ export interface Releases {
contentType: string,
file: string | object,
name: string,
releaseId: number,
releaseId: number
): Promise<UploadArtifactResponse>
}
@@ -73,7 +79,7 @@ export class GithubReleases implements Releases {
commitHash?: string,
discussionCategory?: string,
draft?: boolean,
generateReleaseNotes?: boolean,
makeLatest?: "legacy" | "true" | "false" | undefined,
name?: string,
prerelease?: boolean
): Promise<CreateReleaseResponse> {
@@ -83,22 +89,28 @@ export class GithubReleases implements Releases {
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
tag_name: tag,
})
}
async deleteArtifact(
assetId: number
): Promise<OctokitResponse<any>> {
async deleteArtifact(assetId: number): Promise<OctokitResponse<any>> {
return this.git.rest.repos.deleteReleaseAsset({
asset_id: assetId,
owner: this.inputs.owner,
repo: this.inputs.repo
repo: this.inputs.repo,
})
}
async generateReleaseNotes(tag: string): Promise<GenerateReleaseNotesResponse> {
return this.git.rest.repos.generateReleaseNotes({
owner: this.inputs.owner,
repo: this.inputs.repo,
tag_name: tag,
})
}
@@ -106,24 +118,22 @@ export class GithubReleases implements Releases {
return this.git.rest.repos.getReleaseByTag({
owner: this.inputs.owner,
repo: this.inputs.repo,
tag: tag
tag: tag,
})
}
async listArtifactsForRelease(
releaseId: number
): Promise<ListReleaseAssetsResponseData> {
async listArtifactsForRelease(releaseId: number): Promise<ListReleaseAssetsResponseData> {
return this.git.paginate(this.git.rest.repos.listReleaseAssets, {
owner: this.inputs.owner,
release_id: releaseId,
repo: this.inputs.repo
repo: this.inputs.repo,
})
}
async listReleases(): Promise<ListReleasesResponse> {
return this.git.rest.repos.listReleases({
owner: this.inputs.owner,
repo: this.inputs.repo
repo: this.inputs.repo,
})
}
@@ -134,6 +144,7 @@ export class GithubReleases implements Releases {
commitHash?: string,
discussionCategory?: string,
draft?: boolean,
makeLatest?: "legacy" | "true" | "false" | undefined,
name?: string,
prerelease?: boolean
): Promise<UpdateReleaseResponse> {
@@ -144,11 +155,12 @@ export class GithubReleases implements Releases {
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
tag_name: tag,
})
}
@@ -158,19 +170,19 @@ export class GithubReleases implements Releases {
contentType: string,
file: string | object,
name: string,
releaseId: number,
releaseId: number
): Promise<UploadArtifactResponse> {
return this.git.rest.repos.uploadReleaseAsset({
url: assetUrl,
headers: {
"content-length": contentLength,
"content-type": contentType
"content-type": contentType,
},
data: file as any,
name: name,
owner: this.inputs.owner,
release_id: releaseId,
repo: this.inputs.repo
repo: this.inputs.repo,
})
}
}

View File

@@ -1,13 +1,13 @@
{
"compilerOptions": {
"target": "es2015", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
"moduleResolution": "node",
"outDir": "./lib", /* Redirect output structure to the directory. */
"rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
"strict": true, /* Enable all strict type-checking options. */
"noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
},
"exclude": ["node_modules", "**/*.test.ts"]
}
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"outDir": "./lib",
"strict": true,
"skipLibCheck": true,
"noImplicitAny": true,
"esModuleInterop": true
},
"exclude": ["node_modules", "**/*.test.ts"]
}

7
tsconfig.test.json Normal file
View File

@@ -0,0 +1,7 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"isolatedModules": true
},
"include": ["src/**/*", "__tests__/**/*"]
}

2329
yarn.lock

File diff suppressed because it is too large Load Diff