Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bcfe547070 | ||
|
|
707331a88d | ||
|
|
9128f238ee | ||
|
|
7922049688 | ||
|
|
0683ea3557 | ||
|
|
01c9fac73b | ||
|
|
90dc22b77f | ||
|
|
10585cb926 | ||
|
|
1896b4fc46 | ||
|
|
6996c1bc95 | ||
|
|
c07d979560 | ||
|
|
38da029bb7 | ||
|
|
bf17a40975 | ||
|
|
fa4749311e | ||
|
|
36e78ab629 | ||
|
|
6c1f9baed2 | ||
|
|
db62f3f3ad | ||
|
|
e6992e14b1 |
@@ -48,7 +48,10 @@ This action will create a GitHub release and optionally upload an artifact to it
|
||||
|-------------|-----------------------------------------------|
|
||||
| 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:
|
||||
|
||||
@@ -7,7 +7,15 @@ 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()
|
||||
@@ -38,12 +46,14 @@ const updateDraft = false
|
||||
const updateName = "updateName"
|
||||
const updatePrerelease = false
|
||||
const updateOnlyUnreleased = false
|
||||
const url = "http://api.example.com"
|
||||
const url = TEST_URLS.UPLOAD_URL
|
||||
const makeLatest = "legacy"
|
||||
const generatedReleaseBody = "test release notes"
|
||||
|
||||
describe("Action", () => {
|
||||
beforeEach(() => {
|
||||
applyReleaseDataMock.mockClear()
|
||||
applyAssetUrlsMock.mockClear()
|
||||
createMock.mockClear()
|
||||
getMock.mockClear()
|
||||
listMock.mockClear()
|
||||
@@ -70,6 +80,7 @@ describe("Action", () => {
|
||||
)
|
||||
expect(uploadMock).not.toHaveBeenCalled()
|
||||
assertOutputApplied()
|
||||
assertAssetUrlsApplied({})
|
||||
})
|
||||
|
||||
it("creates release if no release exists to update", async () => {
|
||||
@@ -92,6 +103,10 @@ describe("Action", () => {
|
||||
)
|
||||
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 () => {
|
||||
@@ -117,6 +132,10 @@ describe("Action", () => {
|
||||
)
|
||||
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 () => {
|
||||
@@ -137,6 +156,10 @@ describe("Action", () => {
|
||||
)
|
||||
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 () => {
|
||||
@@ -146,6 +169,10 @@ describe("Action", () => {
|
||||
|
||||
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 () => {
|
||||
@@ -155,6 +182,10 @@ describe("Action", () => {
|
||||
|
||||
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("skips action", async () => {
|
||||
@@ -319,6 +350,10 @@ describe("Action", () => {
|
||||
)
|
||||
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 draft release with static body", async () => {
|
||||
@@ -347,6 +382,10 @@ describe("Action", () => {
|
||||
)
|
||||
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 () => {
|
||||
@@ -367,6 +406,7 @@ describe("Action", () => {
|
||||
)
|
||||
expect(uploadMock).not.toHaveBeenCalled()
|
||||
assertOutputApplied()
|
||||
assertAssetUrlsApplied({})
|
||||
})
|
||||
|
||||
it("updates release then uploads artifact", async () => {
|
||||
@@ -387,20 +427,64 @@ describe("Action", () => {
|
||||
)
|
||||
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).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",
|
||||
})
|
||||
})
|
||||
|
||||
function assertOutputApplied() {
|
||||
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 assertAssetUrlsApplied(expectedUrls: Record<string, string>) {
|
||||
expect(applyAssetUrlsMock).toHaveBeenCalledWith(expectedUrls)
|
||||
}
|
||||
|
||||
function createAction(
|
||||
allowUpdates: boolean,
|
||||
hasArtifact: boolean,
|
||||
removeArtifacts = false,
|
||||
generateReleaseNotes = true
|
||||
generateReleaseNotes = true,
|
||||
omitBodyDuringUpdate = false
|
||||
): Action {
|
||||
let inputArtifact: Artifact[]
|
||||
|
||||
@@ -427,6 +511,9 @@ describe("Action", () => {
|
||||
data: {
|
||||
id: releaseId,
|
||||
upload_url: url,
|
||||
html_url: TEST_URLS.HTML_URL,
|
||||
tarball_url: TEST_URLS.TARBALL_URL,
|
||||
zipball_url: TEST_URLS.ZIPBALL_URL,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -448,9 +535,15 @@ describe("Action", () => {
|
||||
data: {
|
||||
id: releaseId,
|
||||
upload_url: url,
|
||||
html_url: TEST_URLS.HTML_URL,
|
||||
tarball_url: TEST_URLS.TARBALL_URL,
|
||||
zipball_url: TEST_URLS.ZIPBALL_URL,
|
||||
},
|
||||
})
|
||||
uploadMock.mockResolvedValue({})
|
||||
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",
|
||||
})
|
||||
|
||||
const MockInputs = jest.fn<Inputs, any>(() => {
|
||||
return {
|
||||
@@ -477,11 +570,13 @@ describe("Action", () => {
|
||||
updatedReleaseName: updateName,
|
||||
updatedPrerelease: updatePrerelease,
|
||||
updateOnlyUnreleased: updateOnlyUnreleased,
|
||||
omitBodyDuringUpdate,
|
||||
}
|
||||
})
|
||||
const MockOutputs = jest.fn<Outputs, any>(() => {
|
||||
return {
|
||||
applyReleaseData: applyReleaseDataMock,
|
||||
applyAssetUrls: applyAssetUrlsMock,
|
||||
}
|
||||
})
|
||||
const MockUploader = jest.fn<ArtifactUploader, any>(() => {
|
||||
|
||||
@@ -39,8 +39,8 @@ describe("ArtifactGlobber", () => {
|
||||
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(globMock).toHaveBeenCalledWith(untildify("~/path"))
|
||||
expect(warnMock).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it("globs simple path", () => {
|
||||
@@ -49,8 +49,8 @@ describe("ArtifactGlobber", () => {
|
||||
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(globMock).toHaveBeenCalledWith("path")
|
||||
expect(warnMock).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it("splits multiple paths with comma separator", () => {
|
||||
@@ -59,9 +59,9 @@ describe("ArtifactGlobber", () => {
|
||||
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(globMock).toHaveBeenCalledWith("path1")
|
||||
expect(globMock).toHaveBeenCalledWith("path2")
|
||||
expect(warnMock).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it("splits multiple paths with new line separator and trims start", () => {
|
||||
@@ -70,16 +70,16 @@ describe("ArtifactGlobber", () => {
|
||||
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(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(warnMock).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it("throws when no glob results are produced and empty results shouild throw", () => {
|
||||
|
||||
@@ -30,14 +30,14 @@ describe("ArtifactPathValidator", () => {
|
||||
const validator = new ArtifactPathValidator(false, paths, pattern)
|
||||
|
||||
const result = validator.validate()
|
||||
expect(warnMock).toBeCalled()
|
||||
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", () => {
|
||||
|
||||
@@ -13,6 +13,15 @@ const deleteMock = jest.fn()
|
||||
const listArtifactsMock = jest.fn()
|
||||
const uploadMock = jest.fn()
|
||||
|
||||
// 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,
|
||||
}
|
||||
})
|
||||
|
||||
jest.mock("fs", () => {
|
||||
const originalFs = jest.requireActual("fs")
|
||||
return {
|
||||
@@ -32,13 +41,48 @@ describe("ArtifactUploader", () => {
|
||||
uploadMock.mockClear()
|
||||
})
|
||||
|
||||
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)
|
||||
|
||||
const result = await uploader.uploadArtifacts(artifacts, releaseId, url)
|
||||
|
||||
expect(result).toEqual({})
|
||||
expect(uploadMock).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
it("abort when upload failed with non-5xx response", async () => {
|
||||
mockListWithoutAssets()
|
||||
mockUploadArtifact(401, 2)
|
||||
const uploader = createUploader(true)
|
||||
|
||||
await uploader.uploadArtifacts(artifacts, releaseId, url)
|
||||
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)
|
||||
@@ -51,8 +95,9 @@ describe("ArtifactUploader", () => {
|
||||
mockUploadArtifact(500, 4)
|
||||
const uploader = createUploader(true)
|
||||
|
||||
await uploader.uploadArtifacts(artifacts, releaseId, url)
|
||||
const result = await uploader.uploadArtifacts(artifacts, releaseId, url)
|
||||
|
||||
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)
|
||||
@@ -66,11 +111,15 @@ describe("ArtifactUploader", () => {
|
||||
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(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)
|
||||
@@ -83,11 +132,15 @@ describe("ArtifactUploader", () => {
|
||||
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(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)
|
||||
@@ -100,8 +153,9 @@ describe("ArtifactUploader", () => {
|
||||
mockUploadArtifact(500, 2)
|
||||
const uploader = createUploader(true)
|
||||
|
||||
await uploader.uploadArtifacts(artifacts, releaseId, url)
|
||||
const result = await uploader.uploadArtifacts(artifacts, releaseId, url)
|
||||
|
||||
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)
|
||||
@@ -127,7 +181,7 @@ describe("ArtifactUploader", () => {
|
||||
it("throws error from replace", async () => {
|
||||
mockDeleteError()
|
||||
mockListWithAssets()
|
||||
mockUploadArtifact()
|
||||
mockUploadSuccess()
|
||||
const uploader = createUploader(true)
|
||||
|
||||
expect.hasAssertions()
|
||||
@@ -141,11 +195,15 @@ describe("ArtifactUploader", () => {
|
||||
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(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)
|
||||
@@ -194,6 +252,10 @@ describe("ArtifactUploader", () => {
|
||||
listArtifactsMock.mockResolvedValue([])
|
||||
}
|
||||
|
||||
function mockUploadSuccess() {
|
||||
uploadMock.mockImplementation((_, __, ___, ____, name) => Promise.resolve(mockUploadResponse(name)))
|
||||
}
|
||||
|
||||
function mockUploadArtifact(status = 200, failures = 0) {
|
||||
const error = new RequestError(`HTTP ${status}`, status, {
|
||||
headers: {},
|
||||
|
||||
@@ -83,31 +83,31 @@ describe("Inputs", () => {
|
||||
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("")
|
||||
|
||||
expect(inputs.artifacts).toEqual([])
|
||||
expect(mockGlob).toBeCalledTimes(0)
|
||||
expect(mockGlob).toHaveBeenCalledTimes(0)
|
||||
})
|
||||
|
||||
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")
|
||||
|
||||
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", () => {
|
||||
@@ -118,8 +118,8 @@ describe("Inputs", () => {
|
||||
.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)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -421,6 +421,17 @@ describe("Inputs", () => {
|
||||
})
|
||||
})
|
||||
|
||||
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 {
|
||||
|
||||
@@ -45,6 +45,7 @@ describe.skip("Integration Test", () => {
|
||||
commit: undefined,
|
||||
discussionCategory: "Release",
|
||||
generateReleaseNotes: true,
|
||||
omitBodyDuringUpdate: false,
|
||||
owner: "ncipollo",
|
||||
createdPrerelease: false,
|
||||
replacesArtifacts: true,
|
||||
@@ -69,6 +70,9 @@ describe.skip("Integration Test", () => {
|
||||
applyReleaseData(releaseData: ReleaseData) {
|
||||
console.log(`Release Data: ${releaseData}`)
|
||||
},
|
||||
applyAssetUrls(assetUrls: Record<string, string>) {
|
||||
console.log(`Asset URLs: ${JSON.stringify(assetUrls)}`)
|
||||
},
|
||||
}
|
||||
})
|
||||
return new MockOutputs()
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
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
|
||||
@@ -15,15 +19,45 @@ describe("Outputs", () => {
|
||||
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", () => {
|
||||
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))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -122,6 +122,12 @@ 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: 'node20'
|
||||
main: 'dist/index.js'
|
||||
|
||||
1723
dist/index.js
vendored
1723
dist/index.js
vendored
File diff suppressed because one or more lines are too long
2
dist/index.js.map
vendored
2
dist/index.js.map
vendored
File diff suppressed because one or more lines are too long
2
dist/sourcemap-register.js
vendored
2
dist/sourcemap-register.js
vendored
File diff suppressed because one or more lines are too long
10
package.json
10
package.json
@@ -47,9 +47,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.11.1",
|
||||
"@actions/github": "^6.0.0",
|
||||
"@types/node": "^22.13.4",
|
||||
"glob": "^11.0.1",
|
||||
"@actions/github": "^6.0.1",
|
||||
"@types/node": "^22.15.29",
|
||||
"glob": "^11.0.2",
|
||||
"untildify": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -57,8 +57,8 @@
|
||||
"@types/jest": "^29.5.14",
|
||||
"jest": "^29.7.0",
|
||||
"jest-circus": "^29.7.0",
|
||||
"ts-jest": "^29.2.5",
|
||||
"typescript": "^5.7.3"
|
||||
"ts-jest": "^29.3.4",
|
||||
"typescript": "^5.8.3"
|
||||
},
|
||||
"resolutions": {
|
||||
"jest-cli/yargs": "^17.3.1"
|
||||
|
||||
@@ -57,11 +57,13 @@ export class Action {
|
||||
}
|
||||
|
||||
const artifacts = this.inputs.artifacts
|
||||
let assetUrls: Record<string, string> = {}
|
||||
if (artifacts.length > 0) {
|
||||
await this.uploader.uploadArtifacts(artifacts, releaseId, uploadUrl)
|
||||
assetUrls = await this.uploader.uploadArtifacts(artifacts, releaseId, uploadUrl)
|
||||
}
|
||||
|
||||
this.outputs.applyReleaseData(releaseData)
|
||||
this.outputs.applyAssetUrls(assetUrls)
|
||||
}
|
||||
|
||||
private async createOrUpdateRelease(): Promise<CreateOrUpdateReleaseResponse> {
|
||||
@@ -93,7 +95,7 @@ export class Action {
|
||||
private async updateRelease(id: number): Promise<UpdateReleaseResponse> {
|
||||
let releaseBody = this.inputs.updatedReleaseBody
|
||||
|
||||
if (this.inputs.generateReleaseNotes) {
|
||||
if (this.inputs.generateReleaseNotes && !this.inputs.omitBodyDuringUpdate) {
|
||||
const response = await this.releases.generateReleaseNotes(this.inputs.tag)
|
||||
releaseBody = response.data.body
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ 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 {
|
||||
@@ -13,19 +13,24 @@ export class GithubArtifactUploader implements ArtifactUploader {
|
||||
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(
|
||||
const assetResponse = await this.releases.uploadArtifact(
|
||||
uploadUrl,
|
||||
artifact.contentLength,
|
||||
artifact.contentType,
|
||||
@@ -33,15 +38,17 @@ export class GithubArtifactUploader implements ArtifactUploader {
|
||||
artifact.name,
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ export interface Inputs {
|
||||
readonly discussionCategory?: string
|
||||
readonly generateReleaseNotes: boolean
|
||||
readonly makeLatest?: "legacy" | "true" | "false" | undefined
|
||||
readonly omitBodyDuringUpdate: boolean
|
||||
readonly owner: string
|
||||
readonly removeArtifacts: boolean
|
||||
readonly replacesArtifacts: boolean
|
||||
@@ -217,8 +218,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 {
|
||||
@@ -226,8 +227,12 @@ export class CoreInputs implements Inputs {
|
||||
return this.name
|
||||
}
|
||||
|
||||
get updateOnlyUnreleased(): boolean {
|
||||
return core.getInput("updateOnlyUnreleased") == "true"
|
||||
private static get omitBodyDuringUpdate(): boolean {
|
||||
return core.getInput("omitBodyDuringUpdate") == "true"
|
||||
}
|
||||
|
||||
get omitBodyDuringUpdate(): boolean {
|
||||
return CoreInputs.omitBodyDuringUpdate
|
||||
}
|
||||
|
||||
private static get omitNameDuringUpdate(): boolean {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { ReleaseData } from "./Releases"
|
||||
|
||||
export interface Outputs {
|
||||
applyReleaseData(releaseData: ReleaseData): void
|
||||
applyAssetUrls(assetUrls: Record<string, string>): void
|
||||
}
|
||||
|
||||
export class CoreOutputs implements Outputs {
|
||||
@@ -10,5 +11,12 @@ export class CoreOutputs implements Outputs {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ export type ReleaseData = {
|
||||
id: number
|
||||
html_url: string
|
||||
upload_url: string
|
||||
tarball_url: string | null
|
||||
zipball_url: string | null
|
||||
}
|
||||
|
||||
export interface Releases {
|
||||
|
||||
172
yarn.lock
172
yarn.lock
@@ -17,15 +17,18 @@
|
||||
dependencies:
|
||||
"@actions/io" "^1.0.1"
|
||||
|
||||
"@actions/github@^6.0.0":
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@actions/github/-/github-6.0.0.tgz#65883433f9d81521b782a64cc1fd45eef2191ea7"
|
||||
integrity sha512-alScpSVnYmjNEXboZjarjukQEzgCRmjMv6Xj47fsdnqGS73bjJNDpiiXmp8jr0UZLdUB6d9jW63IcmddUP+l0g==
|
||||
"@actions/github@^6.0.1":
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@actions/github/-/github-6.0.1.tgz#76e5f96df062c90635a7181ef45ff1c4ac21306e"
|
||||
integrity sha512-xbZVcaqD4XnQAe35qSQqskb3SqIAfRyLBrHMd/8TuL7hJSz2QtbDwnNM8zWx4zO5l2fnGtseNE3MbEvD7BxVMw==
|
||||
dependencies:
|
||||
"@actions/http-client" "^2.2.0"
|
||||
"@octokit/core" "^5.0.1"
|
||||
"@octokit/plugin-paginate-rest" "^9.0.0"
|
||||
"@octokit/plugin-rest-endpoint-methods" "^10.0.0"
|
||||
"@octokit/plugin-paginate-rest" "^9.2.2"
|
||||
"@octokit/plugin-rest-endpoint-methods" "^10.4.0"
|
||||
"@octokit/request" "^8.4.1"
|
||||
"@octokit/request-error" "^5.1.1"
|
||||
undici "^5.28.5"
|
||||
|
||||
"@actions/http-client@^2.0.1", "@actions/http-client@^2.2.0":
|
||||
version "2.2.0"
|
||||
@@ -958,12 +961,12 @@
|
||||
before-after-hook "^2.2.0"
|
||||
universal-user-agent "^6.0.0"
|
||||
|
||||
"@octokit/endpoint@^9.0.0":
|
||||
version "9.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-9.0.4.tgz#8afda5ad1ffc3073d08f2b450964c610b821d1ea"
|
||||
integrity sha512-DWPLtr1Kz3tv8L0UvXTDP1fNwM0S+z6EJpRcvH66orY6Eld4XBMCSYsaWp4xIm61jTWxK68BrR7ibO+vSDnZqw==
|
||||
"@octokit/endpoint@^9.0.6":
|
||||
version "9.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-9.0.6.tgz#114d912108fe692d8b139cfe7fc0846dfd11b6c0"
|
||||
integrity sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==
|
||||
dependencies:
|
||||
"@octokit/types" "^12.0.0"
|
||||
"@octokit/types" "^13.1.0"
|
||||
universal-user-agent "^6.0.0"
|
||||
|
||||
"@octokit/graphql@^7.0.0":
|
||||
@@ -980,26 +983,31 @@
|
||||
resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-19.1.0.tgz#75ec7e64743870fc73e1ab4bc6ec252ecdd624dc"
|
||||
integrity sha512-6G+ywGClliGQwRsjvqVYpklIfa7oRPA0vyhPQG/1Feh+B+wU0vGH1JiJ5T25d3g1JZYBHzR2qefLi9x8Gt+cpw==
|
||||
|
||||
"@octokit/openapi-types@^20.0.0":
|
||||
version "20.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-20.0.0.tgz#9ec2daa0090eeb865ee147636e0c00f73790c6e5"
|
||||
integrity sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==
|
||||
|
||||
"@octokit/openapi-types@^23.0.1":
|
||||
version "23.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-23.0.1.tgz#3721646ecd36b596ddb12650e0e89d3ebb2dd50e"
|
||||
integrity sha512-izFjMJ1sir0jn0ldEKhZ7xegCTj/ObmEDlEfpFrx4k/JyZSMRHbO3/rBwgE7f3m2DHt+RrNGIVw4wSmwnm3t/g==
|
||||
|
||||
"@octokit/plugin-paginate-rest@^9.0.0":
|
||||
version "9.1.5"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.1.5.tgz#1705bcef4dcde1f4015ee58a63dc61b68648f480"
|
||||
integrity sha512-WKTQXxK+bu49qzwv4qKbMMRXej1DU2gq017euWyKVudA6MldaSSQuxtz+vGbhxV4CjxpUxjZu6rM2wfc1FiWVg==
|
||||
"@octokit/plugin-paginate-rest@^9.2.2":
|
||||
version "9.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.2.2.tgz#c516bc498736bcdaa9095b9a1d10d9d0501ae831"
|
||||
integrity sha512-u3KYkGF7GcZnSD/3UP0S7K5XUFT2FkOQdcfXZGZQPGv3lm4F2Xbf71lvjldr8c1H3nNbF+33cLEkWYbokGWqiQ==
|
||||
dependencies:
|
||||
"@octokit/types" "^12.4.0"
|
||||
"@octokit/types" "^12.6.0"
|
||||
|
||||
"@octokit/plugin-rest-endpoint-methods@^10.0.0":
|
||||
version "10.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.2.0.tgz#eeaa4de97a2ae26404dea30ce3e17b11928e027c"
|
||||
integrity sha512-ePbgBMYtGoRNXDyKGvr9cyHjQ163PbwD0y1MkDJCpkO2YH4OeXX40c4wYHKikHGZcpGPbcRLuy0unPUuafco8Q==
|
||||
"@octokit/plugin-rest-endpoint-methods@^10.4.0":
|
||||
version "10.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.4.1.tgz#41ba478a558b9f554793075b2e20cd2ef973be17"
|
||||
integrity sha512-xV1b+ceKV9KytQe3zCVqjg+8GTGfDYwaT1ATU5isiUyVtlVAO3HNdzpS4sr4GBx4hxQ46s7ITtZrAsxG22+rVg==
|
||||
dependencies:
|
||||
"@octokit/types" "^12.3.0"
|
||||
"@octokit/types" "^12.6.0"
|
||||
|
||||
"@octokit/request-error@^5.0.0":
|
||||
"@octokit/request-error@^5.0.0", "@octokit/request-error@^5.1.1":
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-5.1.1.tgz#b9218f9c1166e68bb4d0c89b638edc62c9334805"
|
||||
integrity sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==
|
||||
@@ -1008,23 +1016,30 @@
|
||||
deprecation "^2.0.0"
|
||||
once "^1.4.0"
|
||||
|
||||
"@octokit/request@^8.0.1", "@octokit/request@^8.0.2":
|
||||
version "8.1.6"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/request/-/request-8.1.6.tgz#a76a859c30421737a3918b40973c2ff369009571"
|
||||
integrity sha512-YhPaGml3ncZC1NfXpP3WZ7iliL1ap6tLkAp6MvbK2fTTPytzVUyUesBBogcdMm86uRYO5rHaM1xIWxigWZ17MQ==
|
||||
"@octokit/request@^8.0.1", "@octokit/request@^8.0.2", "@octokit/request@^8.4.1":
|
||||
version "8.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/request/-/request-8.4.1.tgz#715a015ccf993087977ea4365c44791fc4572486"
|
||||
integrity sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==
|
||||
dependencies:
|
||||
"@octokit/endpoint" "^9.0.0"
|
||||
"@octokit/request-error" "^5.0.0"
|
||||
"@octokit/types" "^12.0.0"
|
||||
"@octokit/endpoint" "^9.0.6"
|
||||
"@octokit/request-error" "^5.1.1"
|
||||
"@octokit/types" "^13.1.0"
|
||||
universal-user-agent "^6.0.0"
|
||||
|
||||
"@octokit/types@^12.0.0", "@octokit/types@^12.3.0", "@octokit/types@^12.4.0":
|
||||
"@octokit/types@^12.0.0":
|
||||
version "12.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/types/-/types-12.4.0.tgz#8f97b601e91ce6b9776ed8152217e77a71be7aac"
|
||||
integrity sha512-FLWs/AvZllw/AGVs+nJ+ELCDZZJk+kY0zMen118xhL2zD0s1etIUHm1odgjP7epxYU1ln7SZxEUWYop5bhsdgQ==
|
||||
dependencies:
|
||||
"@octokit/openapi-types" "^19.1.0"
|
||||
|
||||
"@octokit/types@^12.6.0":
|
||||
version "12.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/types/-/types-12.6.0.tgz#8100fb9eeedfe083aae66473bd97b15b62aedcb2"
|
||||
integrity sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==
|
||||
dependencies:
|
||||
"@octokit/openapi-types" "^20.0.0"
|
||||
|
||||
"@octokit/types@^13.1.0":
|
||||
version "13.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/types/-/types-13.8.0.tgz#3815885e5abd16ed9ffeea3dced31d37ce3f8a0a"
|
||||
@@ -1123,12 +1138,12 @@
|
||||
expect "^29.0.0"
|
||||
pretty-format "^29.0.0"
|
||||
|
||||
"@types/node@*", "@types/node@^22.13.4":
|
||||
version "22.13.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.13.4.tgz#3fe454d77cd4a2d73c214008b3e331bfaaf5038a"
|
||||
integrity sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg==
|
||||
"@types/node@*", "@types/node@^22.15.29":
|
||||
version "22.15.29"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.15.29.tgz#c75999124a8224a3f79dd8b6ccfb37d74098f678"
|
||||
integrity sha512-LNdjOkUDlU1RZb8e1kOIUpN1qQUlzGkEtbVNo53vbrwDg5om6oduhm4SiUaPW5ASTXhAiP0jInWG8Qx9fVlOeQ==
|
||||
dependencies:
|
||||
undici-types "~6.20.0"
|
||||
undici-types "~6.21.0"
|
||||
|
||||
"@types/stack-utils@^2.0.0":
|
||||
version "2.0.0"
|
||||
@@ -1692,10 +1707,10 @@ get-stream@^6.0.0:
|
||||
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7"
|
||||
integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==
|
||||
|
||||
glob@^11.0.1:
|
||||
version "11.0.1"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-11.0.1.tgz#1c3aef9a59d680e611b53dcd24bb8639cef064d9"
|
||||
integrity sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==
|
||||
glob@^11.0.2:
|
||||
version "11.0.2"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-11.0.2.tgz#3261e3897bbc603030b041fd77ba636022d51ce0"
|
||||
integrity sha512-YT7U7Vye+t5fZ/QMkBFrTJ7ZQxInIUjwyAjVj84CYXqgBdv30MFUPGnBR6sQaVq6Is15wYJUsnzTuWaGRBhBAQ==
|
||||
dependencies:
|
||||
foreground-child "^3.1.0"
|
||||
jackspeak "^4.0.1"
|
||||
@@ -2598,10 +2613,10 @@ semver@^6.0.0, semver@^6.3.0:
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
|
||||
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
|
||||
|
||||
semver@^7.5.3, semver@^7.5.4, semver@^7.6.3:
|
||||
version "7.6.3"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143"
|
||||
integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==
|
||||
semver@^7.5.3, semver@^7.5.4, semver@^7.7.2:
|
||||
version "7.7.2"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58"
|
||||
integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==
|
||||
|
||||
shebang-command@^2.0.0:
|
||||
version "2.0.0"
|
||||
@@ -2678,7 +2693,7 @@ string-length@^4.0.1:
|
||||
char-regex "^1.0.2"
|
||||
strip-ansi "^6.0.0"
|
||||
|
||||
"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.2.3:
|
||||
"string-width-cjs@npm:string-width@^4.2.0":
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||
@@ -2696,6 +2711,15 @@ string-width@^4.1.0, string-width@^4.2.0:
|
||||
is-fullwidth-code-point "^3.0.0"
|
||||
strip-ansi "^6.0.0"
|
||||
|
||||
string-width@^4.2.3:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||
dependencies:
|
||||
emoji-regex "^8.0.0"
|
||||
is-fullwidth-code-point "^3.0.0"
|
||||
strip-ansi "^6.0.1"
|
||||
|
||||
string-width@^5.0.1, string-width@^5.1.2:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794"
|
||||
@@ -2705,7 +2729,7 @@ string-width@^5.0.1, string-width@^5.1.2:
|
||||
emoji-regex "^9.2.2"
|
||||
strip-ansi "^7.0.1"
|
||||
|
||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.1:
|
||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||
@@ -2719,6 +2743,13 @@ strip-ansi@^6.0.0:
|
||||
dependencies:
|
||||
ansi-regex "^5.0.0"
|
||||
|
||||
strip-ansi@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||
dependencies:
|
||||
ansi-regex "^5.0.1"
|
||||
|
||||
strip-ansi@^7.0.1:
|
||||
version "7.1.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
|
||||
@@ -2788,10 +2819,10 @@ to-regex-range@^5.0.1:
|
||||
dependencies:
|
||||
is-number "^7.0.0"
|
||||
|
||||
ts-jest@^29.2.5:
|
||||
version "29.2.5"
|
||||
resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.2.5.tgz#591a3c108e1f5ebd013d3152142cb5472b399d63"
|
||||
integrity sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA==
|
||||
ts-jest@^29.3.4:
|
||||
version "29.3.4"
|
||||
resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.3.4.tgz#9354472aceae1d3867a80e8e02014ea5901aee41"
|
||||
integrity sha512-Iqbrm8IXOmV+ggWHOTEbjwyCf2xZlUMv5npExksXohL+tk8va4Fjhb+X2+Rt9NBmgO7bJ8WpnMLOwih/DnMlFA==
|
||||
dependencies:
|
||||
bs-logger "^0.2.6"
|
||||
ejs "^3.1.10"
|
||||
@@ -2800,7 +2831,8 @@ ts-jest@^29.2.5:
|
||||
json5 "^2.2.3"
|
||||
lodash.memoize "^4.1.2"
|
||||
make-error "^1.3.6"
|
||||
semver "^7.6.3"
|
||||
semver "^7.7.2"
|
||||
type-fest "^4.41.0"
|
||||
yargs-parser "^21.1.1"
|
||||
|
||||
tunnel@^0.0.6:
|
||||
@@ -2818,20 +2850,25 @@ type-fest@^0.11.0:
|
||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1"
|
||||
integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==
|
||||
|
||||
typescript@^5.7.3:
|
||||
version "5.7.3"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.3.tgz#919b44a7dbb8583a9b856d162be24a54bf80073e"
|
||||
integrity sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==
|
||||
type-fest@^4.41.0:
|
||||
version "4.41.0"
|
||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.41.0.tgz#6ae1c8e5731273c2bf1f58ad39cbae2c91a46c58"
|
||||
integrity sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==
|
||||
|
||||
undici-types@~6.20.0:
|
||||
version "6.20.0"
|
||||
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433"
|
||||
integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==
|
||||
typescript@^5.8.3:
|
||||
version "5.8.3"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.3.tgz#92f8a3e5e3cf497356f4178c34cd65a7f5e8440e"
|
||||
integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==
|
||||
|
||||
undici@^5.25.4:
|
||||
version "5.28.5"
|
||||
resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.5.tgz#b2b94b6bf8f1d919bc5a6f31f2c01deb02e54d4b"
|
||||
integrity sha512-zICwjrDrcrUE0pyyJc1I2QzBkLM8FINsgOrt6WjA+BgajVq9Nxu2PbFFXUrAggLfDXlZGZBVZYw7WNV5KiBiBA==
|
||||
undici-types@~6.21.0:
|
||||
version "6.21.0"
|
||||
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb"
|
||||
integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==
|
||||
|
||||
undici@^5.25.4, undici@^5.28.5:
|
||||
version "5.29.0"
|
||||
resolved "https://registry.yarnpkg.com/undici/-/undici-5.29.0.tgz#419595449ae3f2cdcba3580a2e8903399bd1f5a3"
|
||||
integrity sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==
|
||||
dependencies:
|
||||
"@fastify/busboy" "^2.0.0"
|
||||
|
||||
@@ -2868,7 +2905,16 @@ which@^2.0.1:
|
||||
dependencies:
|
||||
isexe "^2.0.0"
|
||||
|
||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
|
||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
||||
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
||||
dependencies:
|
||||
ansi-styles "^4.0.0"
|
||||
string-width "^4.1.0"
|
||||
strip-ansi "^6.0.0"
|
||||
|
||||
wrap-ansi@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
||||
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
||||
|
||||
Reference in New Issue
Block a user