1 Commits

Author SHA1 Message Date
Nick Cipollo
d95f7f9bad Prepare v1.8.1 release
Some checks failed
PR Checks / check_pr (push) Has been cancelled
2021-03-12 12:47:06 -05:00
41 changed files with 537 additions and 1068 deletions

View File

@@ -1,28 +0,0 @@
name: "PR Checks"
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
check_pr:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: "yarn install"
run: yarn install
- name: "yarn build"
run: yarn build
- name: "check for uncommitted changes"
# Ensure no changes, but ignore node_modules dir since dev/fresh ci deps installed.
run: |
git diff --exit-code --stat -- . ':!node_modules' \
|| (echo "##[error] found changed files after build. please 'yarn build && npm run format'" \
"and check in all changes" \
&& exit 1)

25
.github/workflows/checkin.yml vendored Normal file
View File

@@ -0,0 +1,25 @@
name: "PR Checks"
on: [pull_request, push]
jobs:
check_pr:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: "yarn install"
run: yarn install
- name: "yarn build"
run: yarn build
- name: "yarn test"
run: yarn test
- name: "check for uncommitted changes"
# Ensure no changes, but ignore node_modules dir since dev/fresh ci deps installed.
run: |
git diff --exit-code --stat -- . ':!node_modules' \
|| (echo "##[error] found changed files after build. please 'yarn build && npm run format'" \
"and check in all changes" \
&& exit 1)

View File

@@ -1,14 +0,0 @@
name: "Test"
on: [push, pull_request]
jobs:
check_pr:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: "yarn install"
run: yarn install
- name: "yarn test"
run: yarn test

2
.gitignore vendored
View File

@@ -1,4 +1,4 @@
# node_modules/ #node_modules/
__tests__/runner/* __tests__/runner/*
# Created by https://www.gitignore.io/api/webstorm # Created by https://www.gitignore.io/api/webstorm

View File

@@ -4,14 +4,12 @@ This action will create a github release and optionally upload an artifact to it
## Action Inputs ## Action Inputs
- **allowUpdates**: An optional flag which indicates if we should update a release if it already exists. Defaults to false. - **allowUpdates**: An optional flag which indicates if we should update a release if it already exists. Defaults to false.
- **artifactErrorsFailBuild**: An optional flag which indicates if artifact read or upload errors should fail the build.
- **artifact**: 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). - **artifact**: 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).
- **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). - **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).
- **artifactContentType**: The content type of the artifact. Defaults to raw. - **artifactContentType**: The content type of the artifact. Defaults to raw.
- **body**: An optional body for the release. - **body**: An optional body for the release.
- **bodyFile**: An optional body file for the release. This should be the path to the file. - **bodyFile**: An optional body file for the release. This should be the path to the file.
- **commit**: An optional commit reference. This will be used to create the tag if it does not exist. - **commit**: An optional commit reference. This will be used to create the tag if it does not exist.
- **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.
- **draft**: Optionally marks this release as a draft release. Set to `true` to enable. - **draft**: Optionally marks this release as a draft release. Set to `true` to enable.
- **name**: An optional name for the release. If this is omitted the tag will be used. - **name**: An optional name for the release. If this is omitted the tag will be used.
- **omitBody**: Indicates if the release body should be omitted. - **omitBody**: Indicates if the release body should be omitted.
@@ -25,11 +23,6 @@ This action will create a github release and optionally upload an artifact to it
- **tag**: An optional tag for the release. If this is omitted the git ref will be used (if it is a tag). - **tag**: An optional tag for the release. If this is omitted the git ref will be used (if it is a tag).
- **token**: (**Required**) The Github token. Typically this will be `${{ secrets.GITHUB_TOKEN }}`. - **token**: (**Required**) The Github token. Typically this will be `${{ secrets.GITHUB_TOKEN }}`.
## Action Outputs
- **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.
## Example ## Example
This example will create a release when tag is pushed: This example will create a release when tag is pushed:

View File

@@ -3,9 +3,7 @@ import {Artifact} from "../src/Artifact";
import {Inputs} from "../src/Inputs"; import {Inputs} from "../src/Inputs";
import {Releases} from "../src/Releases"; import {Releases} from "../src/Releases";
import {ArtifactUploader} from "../src/ArtifactUploader"; import {ArtifactUploader} from "../src/ArtifactUploader";
import {Outputs} from "../src/Outputs";
const applyReleaseDataMock = jest.fn()
const createMock = jest.fn() const createMock = jest.fn()
const deleteMock = jest.fn() const deleteMock = jest.fn()
const getMock = jest.fn() const getMock = jest.fn()
@@ -22,7 +20,6 @@ const artifacts = [
const createBody = 'createBody' const createBody = 'createBody'
const createName = 'createName' const createName = 'createName'
const commit = 'commit' const commit = 'commit'
const discussionCategory = 'discussionCategory'
const draft = true const draft = true
const id = 100 const id = 100
const prerelease = true const prerelease = true
@@ -48,9 +45,8 @@ describe("Action", () => {
await action.perform() await action.perform()
expect(createMock).toBeCalledWith(tag, createBody, commit, discussionCategory, draft, createName, prerelease) expect(createMock).toBeCalledWith(tag, createBody, commit, draft, createName, prerelease)
expect(uploadMock).not.toBeCalled() expect(uploadMock).not.toBeCalled()
assertOutputApplied()
}) })
it('creates release if no release exists to update', async () => { it('creates release if no release exists to update', async () => {
@@ -60,9 +56,8 @@ describe("Action", () => {
await action.perform() await action.perform()
expect(createMock).toBeCalledWith(tag, createBody, commit, discussionCategory, draft, createName, prerelease) expect(createMock).toBeCalledWith(tag, createBody, commit, draft, createName, prerelease)
expect(uploadMock).toBeCalledWith(artifacts, releaseId, url) expect(uploadMock).toBeCalledWith(artifacts, releaseId, url)
assertOutputApplied()
}) })
it('creates release if no draft releases', async () => { it('creates release if no draft releases', async () => {
@@ -77,9 +72,8 @@ describe("Action", () => {
await action.perform() await action.perform()
expect(createMock).toBeCalledWith(tag, createBody, commit, discussionCategory, draft, createName, prerelease) expect(createMock).toBeCalledWith(tag, createBody, commit, draft, createName, prerelease)
expect(uploadMock).toBeCalledWith(artifacts, releaseId, url) expect(uploadMock).toBeCalledWith(artifacts, releaseId, url)
assertOutputApplied()
}) })
@@ -88,9 +82,8 @@ describe("Action", () => {
await action.perform() await action.perform()
expect(createMock).toBeCalledWith(tag, createBody, commit, discussionCategory, draft, createName, prerelease) expect(createMock).toBeCalledWith(tag, createBody, commit, draft, createName, prerelease)
expect(uploadMock).toBeCalledWith(artifacts, releaseId, url) expect(uploadMock).toBeCalledWith(artifacts, releaseId, url)
assertOutputApplied()
}) })
it('throws error when create fails', async () => { it('throws error when create fails', async () => {
@@ -104,7 +97,7 @@ describe("Action", () => {
expect(error).toEqual("error") expect(error).toEqual("error")
} }
expect(createMock).toBeCalledWith(tag, createBody, commit, discussionCategory, draft, createName, prerelease) expect(createMock).toBeCalledWith(tag, createBody, commit, draft, createName, prerelease)
expect(uploadMock).not.toBeCalled() expect(uploadMock).not.toBeCalled()
}) })
@@ -145,32 +138,22 @@ describe("Action", () => {
expect(error).toEqual("error") expect(error).toEqual("error")
} }
expect(updateMock).toBeCalledWith( expect(updateMock).toBeCalledWith(id, tag, updateBody, commit, draft, updateName, prerelease)
id,
tag,
updateBody,
commit,
discussionCategory,
draft,
updateName,
prerelease
)
expect(uploadMock).not.toBeCalled() expect(uploadMock).not.toBeCalled()
}) })
it('throws error when upload fails', async () => { it('throws error when upload fails', async () => {
const action = createAction(false, true) const action = createAction(false, true)
const expectedError = {status: 404} uploadMock.mockRejectedValue("error")
uploadMock.mockRejectedValue(expectedError)
expect.hasAssertions() expect.hasAssertions()
try { try {
await action.perform() await action.perform()
} catch (error) { } catch (error) {
expect(error).toEqual(expectedError) expect(error).toEqual("error")
} }
expect(createMock).toBeCalledWith(tag, createBody, commit, discussionCategory, draft, createName, prerelease) expect(createMock).toBeCalledWith(tag, createBody, commit, draft, createName, prerelease)
expect(uploadMock).toBeCalledWith(artifacts, releaseId, url) expect(uploadMock).toBeCalledWith(artifacts, releaseId, url)
}) })
@@ -187,18 +170,9 @@ describe("Action", () => {
await action.perform() await action.perform()
expect(updateMock).toBeCalledWith( expect(updateMock).toBeCalledWith(id, tag, updateBody, commit, draft, updateName, prerelease)
id,
tag,
updateBody,
commit,
discussionCategory,
draft,
updateName,
prerelease
)
expect(uploadMock).toBeCalledWith(artifacts, releaseId, url) expect(uploadMock).toBeCalledWith(artifacts, releaseId, url)
assertOutputApplied()
}) })
it('updates release but does not upload if no artifact', async () => { it('updates release but does not upload if no artifact', async () => {
@@ -206,18 +180,9 @@ describe("Action", () => {
await action.perform() await action.perform()
expect(updateMock).toBeCalledWith( expect(updateMock).toBeCalledWith(id, tag, updateBody, commit, draft, updateName, prerelease)
id,
tag,
updateBody,
commit,
discussionCategory,
draft,
updateName,
prerelease
)
expect(uploadMock).not.toBeCalled() expect(uploadMock).not.toBeCalled()
assertOutputApplied()
}) })
it('updates release then uploads artifact', async () => { it('updates release then uploads artifact', async () => {
@@ -225,23 +190,10 @@ describe("Action", () => {
await action.perform() await action.perform()
expect(updateMock).toBeCalledWith( expect(updateMock).toBeCalledWith(id, tag, updateBody, commit, draft, updateName, prerelease)
id,
tag,
updateBody,
commit,
discussionCategory,
draft,
updateName,
prerelease
)
expect(uploadMock).toBeCalledWith(artifacts, releaseId, url) expect(uploadMock).toBeCalledWith(artifacts, releaseId, url)
assertOutputApplied()
})
function assertOutputApplied() { })
expect(applyReleaseDataMock).toBeCalledWith({id: releaseId, upload_url: url})
}
function createAction(allowUpdates: boolean, hasArtifact: boolean): Action { function createAction(allowUpdates: boolean, hasArtifact: boolean): Action {
let inputArtifact: Artifact[] let inputArtifact: Artifact[]
@@ -287,12 +239,10 @@ describe("Action", () => {
const MockInputs = jest.fn<Inputs, any>(() => { const MockInputs = jest.fn<Inputs, any>(() => {
return { return {
allowUpdates: allowUpdates, allowUpdates: allowUpdates,
artifactErrorsFailBuild: true,
artifacts: inputArtifact, artifacts: inputArtifact,
createdReleaseBody: createBody, createdReleaseBody: createBody,
createdReleaseName: createName, createdReleaseName: createName,
commit: commit, commit: commit,
discussionCategory: discussionCategory,
draft: draft, draft: draft,
owner: "owner", owner: "owner",
prerelease: prerelease, prerelease: prerelease,
@@ -304,11 +254,6 @@ describe("Action", () => {
updatedReleaseName: updateName updatedReleaseName: updateName
} }
}) })
const MockOutputs = jest.fn<Outputs, any>(() => {
return {
applyReleaseData: applyReleaseDataMock
}
})
const MockUploader = jest.fn<ArtifactUploader, any>(() => { const MockUploader = jest.fn<ArtifactUploader, any>(() => {
return { return {
uploadArtifacts: uploadMock uploadArtifacts: uploadMock
@@ -316,10 +261,9 @@ describe("Action", () => {
}) })
const inputs = new MockInputs() const inputs = new MockInputs()
const outputs = new MockOutputs()
const releases = new MockReleases() const releases = new MockReleases()
const uploader = new MockUploader() const uploader = new MockUploader()
return new Action(inputs, outputs, releases, uploader) return new Action(inputs, releases, uploader)
} }
}) })

View File

@@ -1,8 +1,8 @@
const warnMock = jest.fn() const warnMock = jest.fn()
import {FileArtifactGlobber} from "../src/ArtifactGlobber" import { FileArtifactGlobber } from "../src/ArtifactGlobber"
import {Globber} from "../src/Globber"; import { Globber } from "../src/Globber";
import {Artifact} from "../src/Artifact"; import { Artifact } from "../src/Artifact";
import untildify = require("untildify"); import untildify = require("untildify");
const contentType = "raw" const contentType = "raw"
@@ -13,18 +13,6 @@ jest.mock('@actions/core', () => {
return {warning: warnMock}; return {warning: warnMock};
}) })
jest.mock('fs', () => {
return {
statSync: () => {
return {
isDirectory(): boolean {
return false
}
}
}
};
})
describe("ArtifactGlobber", () => { describe("ArtifactGlobber", () => {
beforeEach(() => { beforeEach(() => {
globMock.mockClear() globMock.mockClear()
@@ -36,19 +24,19 @@ describe("ArtifactGlobber", () => {
const expectedArtifacts = const expectedArtifacts =
globResults.map((path) => new Artifact(path, contentType)) globResults.map((path) => new Artifact(path, contentType))
expect(globber.globArtifactString('~/path', 'raw', false)) expect(globber.globArtifactString('~/path', 'raw'))
.toEqual(expectedArtifacts) .toEqual(expectedArtifacts)
expect(globMock).toBeCalledWith(untildify('~/path')) expect(globMock).toBeCalledWith(untildify('~/path'))
expect(warnMock).not.toBeCalled() expect(warnMock).not.toBeCalled()
}) })
it("globs simple path", () => { it("globs simple path", () => {
const globber = createArtifactGlobber() const globber = createArtifactGlobber()
const expectedArtifacts = const expectedArtifacts =
globResults.map((path) => new Artifact(path, contentType)) globResults.map((path) => new Artifact(path, contentType))
expect(globber.globArtifactString('path', 'raw', false)) expect(globber.globArtifactString('path', 'raw'))
.toEqual(expectedArtifacts) .toEqual(expectedArtifacts)
expect(globMock).toBeCalledWith('path') expect(globMock).toBeCalledWith('path')
expect(warnMock).not.toBeCalled() expect(warnMock).not.toBeCalled()
@@ -62,28 +50,24 @@ describe("ArtifactGlobber", () => {
.concat(globResults) .concat(globResults)
.map((path) => new Artifact(path, contentType)) .map((path) => new Artifact(path, contentType))
expect(globber.globArtifactString('path1,path2', 'raw', false)) expect(globber.globArtifactString('path1,path2', 'raw'))
.toEqual(expectedArtifacts) .toEqual(expectedArtifacts)
expect(globMock).toBeCalledWith('path1') expect(globMock).toBeCalledWith('path1')
expect(globMock).toBeCalledWith('path2') expect(globMock).toBeCalledWith('path2')
expect(warnMock).not.toBeCalled() expect(warnMock).not.toBeCalled()
}) })
it("warns when no glob results are produced and empty results shouldn't throw", () => { it("warns when no glob results are produced", () => {
const globber = createArtifactGlobber([]) const globber = createArtifactGlobber([])
expect(globber.globArtifactString('path', 'raw', false)) const expectedArtifacts =
globResults.map((path) => new Artifact(path, contentType))
expect(globber.globArtifactString('path', 'raw'))
.toEqual([]) .toEqual([])
expect(warnMock).toBeCalled() expect(warnMock).toBeCalled()
}) })
it("throws when no glob results are produced and empty results shouild throw", () => {
const globber = createArtifactGlobber([])
expect(() => {
globber.globArtifactString('path', 'raw', true)
}).toThrow()
})
function createArtifactGlobber(results: string[] = globResults): FileArtifactGlobber { function createArtifactGlobber(results: string[] = globResults): FileArtifactGlobber {
const MockGlobber = jest.fn<Globber, any>(() => { const MockGlobber = jest.fn<Globber, any>(() => {
return { return {

View File

@@ -1,60 +0,0 @@
const directoryMock = jest.fn()
const warnMock = jest.fn()
import {ArtifactPathValidator} from "../src/ArtifactPathValidator";
const pattern = 'pattern'
jest.mock('@actions/core', () => {
return {warning: warnMock};
})
jest.mock('fs', () => {
return {
statSync: () => {
return {isDirectory: directoryMock}
}
};
})
describe("ArtifactPathValidator", () => {
beforeEach(() => {
warnMock.mockClear()
directoryMock.mockClear()
})
it("warns and filters out path which points to a directory", () => {
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'])
})
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()
})
it("throws when no glob results are produced and empty results shouild throw", () => {
const validator = new ArtifactPathValidator(true, [], pattern)
expect(() => {
validator.validate()
}).toThrow()
})
it("throws when path points to directory", () => {
const paths = ['path1', 'path2']
directoryMock.mockReturnValueOnce(true).mockReturnValueOnce(false)
const validator = new ArtifactPathValidator(true, paths, pattern)
expect(() => {
validator.validate()
}).toThrow()
})
})

View File

@@ -1,7 +1,7 @@
import {Artifact} from "../src/Artifact" import { Artifact } from "../src/Artifact"
import {GithubArtifactUploader} from "../src/ArtifactUploader" import { GithubArtifactUploader } from "../src/ArtifactUploader"
import {Releases} from "../src/Releases"; import { Releases } from "../src/Releases";
import {RequestError} from '@octokit/request-error' import { RequestError } from '@octokit/request-error'
const artifacts = [ const artifacts = [
new Artifact('a/art1'), new Artifact('a/art1'),
@@ -19,9 +19,7 @@ const uploadMock = jest.fn()
jest.mock('fs', () => { jest.mock('fs', () => {
return { return {
readFileSync: () => fileContents, readFileSync: () => fileContents,
statSync: () => { statSync: () => { return { size: contentLength } }
return {size: contentLength}
}
}; };
}) })
@@ -126,19 +124,6 @@ describe('ArtifactUploader', () => {
expect(deleteMock).toBeCalledTimes(0) expect(deleteMock).toBeCalledTimes(0)
}) })
it('throws upload error when replacesExistingArtifacts is true', async () => {
mockListWithoutAssets()
mockUploadError()
const uploader = createUploader(true, true)
expect.hasAssertions()
try {
await uploader.uploadArtifacts(artifacts, releaseId, url)
} catch (error) {
expect(error).toEqual(Error("Failed to upload artifact art1. error."))
}
})
it('throws error from replace', async () => { it('throws error from replace', async () => {
mockDeleteError() mockDeleteError()
mockListWithAssets() mockListWithAssets()
@@ -170,7 +155,7 @@ describe('ArtifactUploader', () => {
expect(deleteMock).toBeCalledTimes(0) expect(deleteMock).toBeCalledTimes(0)
}) })
function createUploader(replaces: boolean, throws: boolean = false): GithubArtifactUploader { function createUploader(replaces: boolean): GithubArtifactUploader {
const MockReleases = jest.fn<Releases, any>(() => { const MockReleases = jest.fn<Releases, any>(() => {
return { return {
create: jest.fn(), create: jest.fn(),
@@ -182,7 +167,7 @@ describe('ArtifactUploader', () => {
uploadArtifact: uploadMock uploadArtifact: uploadMock
} }
}) })
return new GithubArtifactUploader(new MockReleases(), replaces, throws) return new GithubArtifactUploader(new MockReleases(), replaces)
} }
function mockDeleteError(): any { function mockDeleteError(): any {
@@ -194,37 +179,29 @@ describe('ArtifactUploader', () => {
} }
function mockListWithAssets() { function mockListWithAssets() {
listArtifactsMock.mockResolvedValue([ listArtifactsMock.mockResolvedValue({
{ data: [
name: "art1", {
id: 1 name: "art1",
}, id: 1
{ },
name: "art2", {
id: 2 name: "art2",
} id: 2
]) }
]
})
} }
function mockListWithoutAssets() { function mockListWithoutAssets() {
listArtifactsMock.mockResolvedValue([]) listArtifactsMock.mockResolvedValue({ data: [] })
} }
function mockUploadArtifact(status: number = 200, failures: number = 0) { function mockUploadArtifact(status: number = 200, failures: number = 0) {
const error = new RequestError(`HTTP ${status}`, status, { const error = new RequestError(`HTTP ${status}`, status, { headers: {}, request: { method: 'GET', url: '', headers: {} } })
headers: {},
request: {method: 'GET', url: '', headers: {}}
})
for (let index = 0; index < failures; index++) { for (let index = 0; index < failures; index++) {
uploadMock.mockRejectedValueOnce(error) uploadMock.mockRejectedValueOnce(error)
} }
uploadMock.mockResolvedValue({}) uploadMock.mockResolvedValue({})
} }
function mockUploadError() {
uploadMock.mockRejectedValue({
message: "error",
status: 502
})
}
}); });

View File

@@ -0,0 +1,70 @@
import { ErrorMessage } from "../src/ErrorMessage"
describe('ErrorMessage', () => {
describe('has error with code', () => {
const error = {
message: 'something bad happened',
errors: [
{
code: 'missing',
resource: 'release'
},
{
code: 'already_exists',
resource: 'release'
}
],
status: 422
}
it('does not have error', () => {
const errorMessage = new ErrorMessage(error)
expect(errorMessage.hasErrorWithCode('missing_field')).toBeFalsy()
})
it('has error', () => {
const errorMessage = new ErrorMessage(error)
expect(errorMessage.hasErrorWithCode('missing')).toBeTruthy()
})
})
it('generates message with errors', () => {
const error = {
message: 'something bad happened',
errors: [
{
code: 'missing',
resource: 'release'
},
{
code: 'already_exists',
resource: 'release'
}
],
status: 422
}
const errorMessage = new ErrorMessage(error)
const expectedString = "Error 422: something bad happened\nErrors:\n- release does not exist.\n- release already exists."
expect(errorMessage.toString()).toBe(expectedString)
})
it('generates message without errors', () => {
const error = {
message: 'something bad happened',
status: 422
}
const errorMessage = new ErrorMessage(error)
expect(errorMessage.toString()).toBe('Error 422: something bad happened')
})
it('provides error status', () => {
const error = { status: 404 }
const errorMessage = new ErrorMessage(error)
expect(errorMessage.status).toBe(404)
})
})

View File

@@ -1,70 +1,99 @@
import { GithubError } from "../src/GithubError" import { GithubError } from "../src/GithubError"
describe('ErrorMessage', () => { describe('GithubError', () => {
describe('has error with code', () => { it('provides error code', () => {
const error = { const error = {
message: 'something bad happened', code: "missing"
errors: [
{
code: 'missing',
resource: 'release'
},
{
code: 'already_exists',
resource: 'release'
}
],
status: 422
} }
it('does not have error', () => { const githubError = new GithubError(error)
expect(githubError.code).toBe('missing')
})
it('generates missing resource error message', () => {
const resource = "release"
const error = {
code: "missing",
resource: resource
}
const githubError = new GithubError(error)
const message = githubError.toString()
expect(message).toBe(`${resource} does not exist.`)
})
it('generates missing field error message', () => {
const resource = "release"
const field = "body"
const error = {
code: "missing_field",
field: field,
resource: resource
}
const githubError = new GithubError(error)
const message = githubError.toString()
expect(message).toBe(`The ${field} field on ${resource} is missing.`)
})
it('generates invalid field error message', () => {
const resource = "release"
const field = "body"
const error = {
code: "invalid",
field: field,
resource: resource
}
const githubError = new GithubError(error)
const message = githubError.toString()
expect(message).toBe(`The ${field} field on ${resource} is an invalid format.`)
})
it('generates resource already exists error message', () => {
const resource = "release"
const field = "body"
const error = {
code: "already_exists",
resource: resource
}
const githubError = new GithubError(error)
const message = githubError.toString()
expect(message).toBe(`${resource} already exists.`)
})
describe('generates custom error message', () => {
it('with documentation url', () => {
const url = "https://api.example.com"
const error = {
code: "custom",
message: "foo",
documentation_url: url
}
const githubError = new GithubError(error) const githubError = new GithubError(error)
expect(githubError.hasErrorWithCode('missing_field')).toBeFalsy() const message = githubError.toString()
expect(message).toBe(`foo\nPlease see ${url}.`)
}) })
it('has error', () => { it('without documentation url', () => {
const error = {
code: "custom",
message: "foo"
}
const githubError = new GithubError(error) const githubError = new GithubError(error)
expect(githubError.hasErrorWithCode('missing')).toBeTruthy() const message = githubError.toString()
expect(message).toBe('foo')
}) })
}) })
})
it('generates message with errors', () => {
const error = {
message: 'something bad happened',
errors: [
{
code: 'missing',
resource: 'release'
},
{
code: 'already_exists',
resource: 'release'
}
],
status: 422
}
const githubError = new GithubError(error)
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', () => {
const error = {
message: 'something bad happened',
status: 422
}
const githubError = new GithubError(error)
expect(githubError.toString()).toBe('Error 422: something bad happened')
})
it('provides error status', () => {
const error = { status: 404 }
const githubError = new GithubError(error)
expect(githubError.status).toBe(404)
})
})

View File

@@ -1,98 +0,0 @@
import { GithubErrorDetail } from "../src/GithubErrorDetail"
describe('GithubErrorDetail', () => {
it('provides error code', () => {
const error = {
code: "missing"
}
const detail = new GithubErrorDetail(error)
expect(detail.code).toBe('missing')
})
it('generates missing resource error message', () => {
const resource = "release"
const error = {
code: "missing",
resource: resource
}
const detail = new GithubErrorDetail(error)
const message = detail.toString()
expect(message).toBe(`${resource} does not exist.`)
})
it('generates missing field error message', () => {
const resource = "release"
const field = "body"
const error = {
code: "missing_field",
field: field,
resource: resource
}
const detail = new GithubErrorDetail(error)
const message = detail.toString()
expect(message).toBe(`The ${field} field on ${resource} is missing.`)
})
it('generates invalid field error message', () => {
const resource = "release"
const field = "body"
const error = {
code: "invalid",
field: field,
resource: resource
}
const detail = new GithubErrorDetail(error)
const message = detail.toString()
expect(message).toBe(`The ${field} field on ${resource} is an invalid format.`)
})
it('generates resource already exists error message', () => {
const resource = "release"
const error = {
code: "already_exists",
resource: resource
}
const detail = new GithubErrorDetail(error)
const message = detail.toString()
expect(message).toBe(`${resource} already exists.`)
})
describe('generates custom error message', () => {
it('with documentation url', () => {
const url = "https://api.example.com"
const error = {
code: "custom",
message: "foo",
documentation_url: url
}
const detail = new GithubErrorDetail(error)
const message = detail.toString()
expect(message).toBe(`foo\nPlease see ${url}.`)
})
it('without documentation url', () => {
const error = {
code: "custom",
message: "foo"
}
const detail = new GithubErrorDetail(error)
const message = detail.toString()
expect(message).toBe('foo')
})
})
})

View File

@@ -59,28 +59,7 @@ describe('Inputs', () => {
}) })
}) })
describe('artifactErrorsFailBuild', () => {
it('returns false', () => {
expect(inputs.artifactErrorsFailBuild).toBe(false)
})
it('returns true', () => {
mockGetInput.mockReturnValue('true')
expect(inputs.artifactErrorsFailBuild).toBe(true)
})
})
describe('artifacts', () => { 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)
})
it('returns empty artifacts', () => { it('returns empty artifacts', () => {
mockGetInput.mockReturnValueOnce('') mockGetInput.mockReturnValueOnce('')
.mockReturnValueOnce('') .mockReturnValueOnce('')
@@ -92,32 +71,28 @@ describe('Inputs', () => {
it('returns input.artifacts', () => { it('returns input.artifacts', () => {
mockGetInput.mockReturnValueOnce('art1') mockGetInput.mockReturnValueOnce('art1')
.mockReturnValueOnce('contentType') .mockReturnValueOnce('contentType')
.mockReturnValueOnce('false')
expect(inputs.artifacts).toEqual(artifacts) expect(inputs.artifacts).toEqual(artifacts)
expect(mockGlob).toBeCalledTimes(1) expect(mockGlob).toBeCalledTimes(1)
expect(mockGlob).toBeCalledWith('art1', 'contentType', false) expect(mockGlob).toBeCalledWith('art1', 'contentType')
}) })
it('returns input.artifacts with default contentType', () => { it('returns input.artifacts with default contentType', () => {
mockGetInput.mockReturnValueOnce('art1') mockGetInput.mockReturnValueOnce('art1')
.mockReturnValueOnce('raw')
.mockReturnValueOnce('false')
expect(inputs.artifacts).toEqual(artifacts) expect(inputs.artifacts).toEqual(artifacts)
expect(mockGlob).toBeCalledTimes(1) expect(mockGlob).toBeCalledTimes(1)
expect(mockGlob).toBeCalledWith('art1', 'raw', false) expect(mockGlob).toBeCalledWith('art1', 'raw')
}) })
it('returns input.artifact', () => { it('returns input.artifact', () => {
mockGetInput.mockReturnValueOnce('') mockGetInput.mockReturnValueOnce('')
.mockReturnValueOnce('art2') .mockReturnValueOnce('art2')
.mockReturnValueOnce('contentType') .mockReturnValueOnce('contentType')
.mockReturnValueOnce('false')
expect(inputs.artifacts).toEqual(artifacts) expect(inputs.artifacts).toEqual(artifacts)
expect(mockGlob).toBeCalledTimes(1) expect(mockGlob).toBeCalledTimes(1)
expect(mockGlob).toBeCalledWith('art2', 'contentType', false) expect(mockGlob).toBeCalledWith('art2', 'contentType')
}) })
}) })
@@ -179,18 +154,6 @@ describe('Inputs', () => {
}) })
}) })
describe('discussionCategory', () => {
it('returns category', () => {
mockGetInput.mockReturnValue('Release')
expect(inputs.discussionCategory).toBe('Release')
})
it('returns undefined', () => {
mockGetInput.mockReturnValue('')
expect(inputs.discussionCategory).toBe(undefined)
})
})
describe('draft', () => { describe('draft', () => {
it('returns false', () => { it('returns false', () => {
expect(inputs.draft).toBe(false) expect(inputs.draft).toBe(false)
@@ -201,7 +164,7 @@ describe('Inputs', () => {
expect(inputs.draft).toBe(true) expect(inputs.draft).toBe(true)
}) })
}) })
describe('owner', () => { describe('owner', () => {
it('returns owner from context', function () { it('returns owner from context', function () {
process.env.GITHUB_REPOSITORY = "owner/repo" process.env.GITHUB_REPOSITORY = "owner/repo"
@@ -212,7 +175,7 @@ describe('Inputs', () => {
mockGetInput.mockReturnValue("owner") mockGetInput.mockReturnValue("owner")
expect(inputs.owner).toBe("owner") expect(inputs.owner).toBe("owner")
}); });
}) })
describe('prerelase', () => { describe('prerelase', () => {
it('returns false', () => { it('returns false', () => {

View File

@@ -1,11 +1,10 @@
import {Action} from "../src/Action"; import {Action} from "../src/Action";
import * as github from "@actions/github"; import * as github from "@actions/github";
import {Inputs} from "../src/Inputs"; import {Inputs} from "../src/Inputs";
import {GithubReleases, ReleaseData} from "../src/Releases"; import {GithubReleases} from "../src/Releases";
import {GithubArtifactUploader} from "../src/ArtifactUploader"; import {GithubArtifactUploader} from "../src/ArtifactUploader";
import * as path from "path"; import * as path from "path";
import {FileArtifactGlobber} from "../src/ArtifactGlobber"; import {FileArtifactGlobber} from "../src/ArtifactGlobber";
import {Outputs} from "../src/Outputs";
// This test is currently intended to be manually run during development. To run: // This test is currently intended to be manually run during development. To run:
// - Make sure you have an environment variable named GITHUB_TOKEN assigned to your token // - Make sure you have an environment variable named GITHUB_TOKEN assigned to your token
@@ -18,14 +17,9 @@ describe.skip('Integration Test', () => {
const git = github.getOctokit(token) const git = github.getOctokit(token)
const inputs = getInputs() const inputs = getInputs()
const outputs = getOutputs()
const releases = new GithubReleases(inputs, git) const releases = new GithubReleases(inputs, git)
const uploader = new GithubArtifactUploader( const uploader = new GithubArtifactUploader(releases, inputs.replacesArtifacts)
releases, action = new Action(inputs, releases, uploader)
inputs.replacesArtifacts,
inputs.artifactErrorsFailBuild,
)
action = new Action(inputs, outputs, releases, uploader)
}) })
it('Performs action', async () => { it('Performs action', async () => {
@@ -36,12 +30,10 @@ describe.skip('Integration Test', () => {
const MockInputs = jest.fn<Inputs, any>(() => { const MockInputs = jest.fn<Inputs, any>(() => {
return { return {
allowUpdates: true, allowUpdates: true,
artifactErrorsFailBuild: false,
artifacts: artifacts(), artifacts: artifacts(),
createdReleaseBody: "This release was generated by release-action's integration test", createdReleaseBody: "This release was generated by release-action's integration test",
createdReleaseName: "Releases Action Integration Test 2", createdReleaseName: "Releases Action Integration Test",
commit: "", commit: "",
discussionCategory: 'Release',
draft: false, draft: false,
owner: "ncipollo", owner: "ncipollo",
prerelease: false, prerelease: false,
@@ -50,28 +42,17 @@ describe.skip('Integration Test', () => {
tag: "release-action-test", tag: "release-action-test",
token: getToken(), token: getToken(),
updatedReleaseBody: "This release was generated by release-action's integration test", updatedReleaseBody: "This release was generated by release-action's integration test",
updatedReleaseName: "Releases Action Integration Test 2" updatedReleaseName: "Releases Action Integration Test"
} }
}) })
return new MockInputs(); return new MockInputs();
} }
function getOutputs(): Outputs {
const MockOutputs = jest.fn<Outputs, any>(() => {
return {
applyReleaseData(releaseData: ReleaseData) {
console.log(`Release Data: ${releaseData}`)
}
}
})
return new MockOutputs()
}
function artifacts() { function artifacts() {
const globber = new FileArtifactGlobber() const globber = new FileArtifactGlobber()
const artifactPath = path.join(__dirname, 'Integration.test.ts') const artifactPath = path.join(__dirname, 'Integration.test.ts')
const artifactString = `~/Desktop,~/Desktop/test.txt,blarg.tx, ${artifactPath}` const artifactString = `~/Desktop/test.txt,blarg.tx, ${artifactPath}`
return globber.globArtifactString(artifactString, "raw", false) return globber.globArtifactString(artifactString, "raw")
} }
function getToken(): string { function getToken(): string {

View File

@@ -1,29 +0,0 @@
const mockSetOutput = jest.fn();
import {CoreOutputs, Outputs} from "../src/Outputs";
import {ReleaseData} from "../src/Releases";
jest.mock('@actions/core', () => {
return {setOutput: mockSetOutput};
})
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'
}
})
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)
})
})

View File

@@ -6,10 +6,6 @@ inputs:
description: 'An optional flag which indicates if we should update a release if it already exists. Defaults to false.' description: 'An optional flag which indicates if we should update a release if it already exists. Defaults to false.'
required: false required: false
default: '' default: ''
artifactErrorsFailBuild:
description: 'An optional flag which indicates if artifact read or upload errors should fail the build.'
required: false
default: ''
artifact: artifact:
deprecationMessage: Use 'artifacts' instead. deprecationMessage: Use 'artifacts' instead.
description: '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)' description: '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)'
@@ -34,10 +30,6 @@ inputs:
commit: commit:
description: "An optional commit reference. This will be used to create the tag if it does not exist." description: "An optional commit reference. This will be used to create the tag if it does not exist."
required: false required: false
default: ''
discussionCategory:
description: "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"
required: false
default: '' default: ''
draft: draft:
description: "Optionally marks this release as a draft release. Set to true to enable." description: "Optionally marks this release as a draft release. Set to true to enable."
@@ -87,13 +79,6 @@ inputs:
description: 'The Github token.' description: 'The Github token.'
required: true required: true
default: '' default: ''
outputs:
id:
description: 'The identifier of the created release.'
html_url:
description: 'The HTML URL of the release.'
upload_url:
description: 'The URL for uploading assets to the release.'
runs: runs:
using: 'node12' using: 'node12'
main: 'lib/Main.js' main: 'lib/Main.js'

View File

@@ -10,62 +10,53 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
}; };
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.Action = void 0; exports.Action = void 0;
const GithubError_1 = require("./GithubError"); const ErrorMessage_1 = require("./ErrorMessage");
class Action { class Action {
constructor(inputs, outputs, releases, uploader) { constructor(inputs, releases, uploader) {
this.inputs = inputs; this.inputs = inputs;
this.outputs = outputs;
this.releases = releases; this.releases = releases;
this.uploader = uploader; this.uploader = uploader;
} }
perform() { perform() {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const releaseResponse = yield this.createOrUpdateRelease(); const releaseResponse = yield this.createOrUpdateRelease();
const releaseData = releaseResponse.data; const releaseId = releaseResponse.data.id;
const releaseId = releaseData.id; const uploadUrl = releaseResponse.data.upload_url;
const uploadUrl = releaseData.upload_url;
const artifacts = this.inputs.artifacts; const artifacts = this.inputs.artifacts;
if (artifacts.length > 0) { if (artifacts.length > 0) {
yield this.uploader.uploadArtifacts(artifacts, releaseId, uploadUrl); yield this.uploader.uploadArtifacts(artifacts, releaseId, uploadUrl);
} }
this.outputs.applyReleaseData(releaseData);
}); });
} }
createOrUpdateRelease() { createOrUpdateRelease() {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
if (this.inputs.allowUpdates) { if (this.inputs.allowUpdates) {
let getResponse;
try { try {
getResponse = yield this.releases.getByTag(this.inputs.tag); const getResponse = yield this.releases.getByTag(this.inputs.tag);
return yield this.updateRelease(getResponse.data.id);
} }
catch (error) { catch (error) {
return yield this.checkForMissingReleaseError(error); if (Action.noPublishedRelease(error)) {
return yield this.updateDraftOrCreateRelease();
}
else {
throw error;
}
} }
return yield this.updateRelease(getResponse.data.id);
} }
else { else {
return yield this.createRelease(); 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) { updateRelease(id) {
return __awaiter(this, void 0, void 0, function* () { 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.draft, this.inputs.updatedReleaseName, this.inputs.prerelease); return yield this.releases.update(id, this.inputs.tag, this.inputs.updatedReleaseBody, this.inputs.commit, this.inputs.draft, this.inputs.updatedReleaseName, this.inputs.prerelease);
}); });
} }
static noPublishedRelease(error) { static noPublishedRelease(error) {
const githubError = new GithubError_1.GithubError(error); const errorMessage = new ErrorMessage_1.ErrorMessage(error);
return githubError.status == 404; return errorMessage.status == 404;
} }
updateDraftOrCreateRelease() { updateDraftOrCreateRelease() {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
@@ -89,7 +80,8 @@ class Action {
} }
createRelease() { createRelease() {
return __awaiter(this, void 0, void 0, function* () { 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.draft, this.inputs.createdReleaseName, this.inputs.prerelease); const response = yield this.releases.create(this.inputs.tag, this.inputs.createdReleaseBody, this.inputs.commit, this.inputs.draft, this.inputs.createdReleaseName, this.inputs.prerelease);
return response;
}); });
} }
} }

View File

@@ -27,41 +27,27 @@ const core = __importStar(require("@actions/core"));
const Globber_1 = require("./Globber"); const Globber_1 = require("./Globber");
const Artifact_1 = require("./Artifact"); const Artifact_1 = require("./Artifact");
const untildify_1 = __importDefault(require("untildify")); const untildify_1 = __importDefault(require("untildify"));
const ArtifactPathValidator_1 = require("./ArtifactPathValidator");
class FileArtifactGlobber { class FileArtifactGlobber {
constructor(globber = new Globber_1.FileGlobber()) { constructor(globber = new Globber_1.FileGlobber()) {
this.globber = globber; this.globber = globber;
} }
globArtifactString(artifact, contentType, errorsFailBuild) { globArtifactString(artifact, contentType) {
return artifact.split(',') return artifact.split(',')
.map(path => FileArtifactGlobber.expandPath(path)) .map(path => FileArtifactGlobber.expandPath(path))
.map(pattern => this.globPattern(pattern, errorsFailBuild)) .map(pattern => this.globPattern(pattern))
.map((globResult) => FileArtifactGlobber.validatePattern(errorsFailBuild, globResult[1], globResult[0]))
.reduce((accumulated, current) => accumulated.concat(current)) .reduce((accumulated, current) => accumulated.concat(current))
.map(path => new Artifact_1.Artifact(path, contentType)); .map(path => new Artifact_1.Artifact(path, contentType));
} }
globPattern(pattern, errorsFailBuild) { globPattern(pattern) {
const paths = this.globber.glob(pattern); const paths = this.globber.glob(pattern);
if (paths.length == 0) { if (paths.length == 0) {
if (errorsFailBuild) { FileArtifactGlobber.reportGlobWarning(pattern);
FileArtifactGlobber.throwGlobError(pattern);
}
else {
FileArtifactGlobber.reportGlobWarning(pattern);
}
} }
return [pattern, paths]; return paths;
}
static validatePattern(errorsFailBuild, paths, pattern) {
const validator = new ArtifactPathValidator_1.ArtifactPathValidator(errorsFailBuild, paths, pattern);
return validator.validate();
} }
static reportGlobWarning(pattern) { static reportGlobWarning(pattern) {
core.warning(`Artifact pattern :${pattern} did not match any files`); 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) { static expandPath(path) {
return untildify_1.default(path); return untildify_1.default(path);
} }

View File

@@ -1,58 +0,0 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (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 = 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

@@ -31,10 +31,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
exports.GithubArtifactUploader = void 0; exports.GithubArtifactUploader = void 0;
const core = __importStar(require("@actions/core")); const core = __importStar(require("@actions/core"));
class GithubArtifactUploader { class GithubArtifactUploader {
constructor(releases, replacesExistingArtifacts = true, throwsUploadErrors = false) { constructor(releases, replacesExistingArtifacts = true) {
this.releases = releases; this.releases = releases;
this.replacesExistingArtifacts = replacesExistingArtifacts; this.replacesExistingArtifacts = replacesExistingArtifacts;
this.throwsUploadErrors = throwsUploadErrors;
} }
uploadArtifacts(artifacts, releaseId, uploadUrl) { uploadArtifacts(artifacts, releaseId, uploadUrl) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
@@ -58,19 +57,15 @@ class GithubArtifactUploader {
yield this.uploadArtifact(artifact, releaseId, uploadUrl, retry - 1); yield this.uploadArtifact(artifact, releaseId, uploadUrl, retry - 1);
} }
else { else {
if (this.throwsUploadErrors) { core.warning(`Failed to upload artifact ${artifact.name}. ${error.message}.`);
throw Error(`Failed to upload artifact ${artifact.name}. ${error.message}.`);
}
else {
core.warning(`Failed to upload artifact ${artifact.name}. ${error.message}.`);
}
} }
} }
}); });
} }
deleteUpdatedArtifacts(artifacts, releaseId) { deleteUpdatedArtifacts(artifacts, releaseId) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const releaseAssets = yield this.releases.listArtifactsForRelease(releaseId); const response = yield this.releases.listArtifactsForRelease(releaseId);
const releaseAssets = response.data;
const assetByName = {}; const assetByName = {};
releaseAssets.forEach(asset => { releaseAssets.forEach(asset => {
assetByName[asset.name] = asset; assetByName[asset.name] = asset;

40
lib/ErrorMessage.js Normal file
View File

@@ -0,0 +1,40 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ErrorMessage = void 0;
const GithubError_1 = require("./GithubError");
class ErrorMessage {
constructor(error) {
this.error = error;
this.githubErrors = this.generateGithubErrors();
}
generateGithubErrors() {
const errors = this.error.errors;
if (errors instanceof Array) {
return errors.map((err) => new GithubError_1.GithubError(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.ErrorMessage = ErrorMessage;

View File

@@ -1,40 +1,57 @@
"use strict"; "use strict";
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.GithubError = void 0; exports.GithubError = void 0;
const GithubErrorDetail_1 = require("./GithubErrorDetail");
class GithubError { class GithubError {
constructor(error) { constructor(error) {
this.error = error; this.error = error;
this.githubErrors = this.generateGithubErrors();
} }
generateGithubErrors() { get code() {
const errors = this.error.errors; return this.error.code;
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() { toString() {
const message = this.error.message; const code = this.error.code;
const errors = this.githubErrors; switch (code) {
const status = this.status; case 'missing':
if (errors.length > 0) { return this.missingResourceMessage();
return `Error ${status}: ${message}\nErrors:\n${this.errorBulletedList(errors)}`; case 'missing_field':
} return this.missingFieldMessage();
else { case 'invalid':
return `Error ${status}: ${message}`; return this.invalidFieldMessage();
case 'already_exists':
return this.resourceAlreadyExists();
default:
return this.customErrorMessage();
} }
} }
errorBulletedList(errors) { customErrorMessage() {
return errors.map((err) => `- ${err}`).join("\n"); 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.GithubError = GithubError; 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

@@ -42,14 +42,10 @@ class CoreInputs {
contentType = 'raw'; contentType = 'raw';
} }
return this.artifactGlobber return this.artifactGlobber
.globArtifactString(artifacts, contentType, this.artifactErrorsFailBuild); .globArtifactString(artifacts, contentType);
} }
return []; return [];
} }
get artifactErrorsFailBuild() {
const allow = core.getInput('artifactErrorsFailBuild');
return allow == 'true';
}
get createdReleaseBody() { get createdReleaseBody() {
if (CoreInputs.omitBody) if (CoreInputs.omitBody)
return undefined; return undefined;
@@ -77,13 +73,6 @@ class CoreInputs {
return undefined; return undefined;
return this.name; return this.name;
} }
get discussionCategory() {
const category = core.getInput('discussionCategory');
if (category) {
return category;
}
return undefined;
}
static get omitName() { static get omitName() {
return core.getInput('omitName') == 'true'; return core.getInput('omitName') == 'true';
} }

View File

@@ -35,8 +35,7 @@ const Releases_1 = require("./Releases");
const Action_1 = require("./Action"); const Action_1 = require("./Action");
const ArtifactUploader_1 = require("./ArtifactUploader"); const ArtifactUploader_1 = require("./ArtifactUploader");
const ArtifactGlobber_1 = require("./ArtifactGlobber"); const ArtifactGlobber_1 = require("./ArtifactGlobber");
const GithubError_1 = require("./GithubError"); const ErrorMessage_1 = require("./ErrorMessage");
const Outputs_1 = require("./Outputs");
function run() { function run() {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
try { try {
@@ -44,8 +43,8 @@ function run() {
yield action.perform(); yield action.perform();
} }
catch (error) { catch (error) {
const githubError = new GithubError_1.GithubError(error); const errorMessage = new ErrorMessage_1.ErrorMessage(error);
core.setFailed(githubError.toString()); core.setFailed(errorMessage.toString());
} }
}); });
} }
@@ -55,9 +54,8 @@ function createAction() {
const git = github.getOctokit(token); const git = github.getOctokit(token);
const globber = new ArtifactGlobber_1.FileArtifactGlobber(); const globber = new ArtifactGlobber_1.FileArtifactGlobber();
const inputs = new Inputs_1.CoreInputs(globber, context); const inputs = new Inputs_1.CoreInputs(globber, context);
const outputs = new Outputs_1.CoreOutputs();
const releases = new Releases_1.GithubReleases(inputs, git); const releases = new Releases_1.GithubReleases(inputs, git);
const uploader = new ArtifactUploader_1.GithubArtifactUploader(releases, inputs.replacesArtifacts, inputs.artifactErrorsFailBuild); const uploader = new ArtifactUploader_1.GithubArtifactUploader(releases, inputs.replacesArtifacts);
return new Action_1.Action(inputs, outputs, releases, uploader); return new Action_1.Action(inputs, releases, uploader);
} }
run(); run();

View File

@@ -1,31 +0,0 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (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

@@ -15,13 +15,12 @@ class GithubReleases {
this.inputs = inputs; this.inputs = inputs;
this.git = git; this.git = git;
} }
create(tag, body, commitHash, discussionCategory, draft, name, prerelease) { create(tag, body, commitHash, draft, name, prerelease) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
// noinspection TypeScriptValidateJSTypes // noinspection TypeScriptValidateJSTypes
return this.git.repos.createRelease({ return this.git.repos.createRelease({
body: body, body: body,
name: name, name: name,
discussion_category_name: discussionCategory,
draft: draft, draft: draft,
owner: this.inputs.owner, owner: this.inputs.owner,
prerelease: prerelease, prerelease: prerelease,
@@ -51,7 +50,7 @@ class GithubReleases {
} }
listArtifactsForRelease(releaseId) { listArtifactsForRelease(releaseId) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
return this.git.paginate(this.git.repos.listReleaseAssets, { return this.git.repos.listReleaseAssets({
owner: this.inputs.owner, owner: this.inputs.owner,
release_id: releaseId, release_id: releaseId,
repo: this.inputs.repo repo: this.inputs.repo
@@ -66,14 +65,13 @@ class GithubReleases {
}); });
}); });
} }
update(id, tag, body, commitHash, discussionCategory, draft, name, prerelease) { update(id, tag, body, commitHash, draft, name, prerelease) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
// noinspection TypeScriptValidateJSTypes // noinspection TypeScriptValidateJSTypes
return this.git.repos.updateRelease({ return this.git.repos.updateRelease({
release_id: id, release_id: id,
body: body, body: body,
name: name, name: name,
discussion_category_name: discussionCategory,
draft: draft, draft: draft,
owner: this.inputs.owner, owner: this.inputs.owner,
prerelease: prerelease, prerelease: prerelease,

15
node_modules/.yarn-integrity generated vendored
View File

@@ -15,8 +15,8 @@
"@types/node@^14.14.25", "@types/node@^14.14.25",
"glob@^7.1.4", "glob@^7.1.4",
"jest-circus@^26.6.3", "jest-circus@^26.6.3",
"jest@^26.6.3", "jest@^26.1.0",
"ts-jest@^26.5.6", "ts-jest@^26.5.1",
"typescript@^4.1.4", "typescript@^4.1.4",
"untildify@^4.0.0" "untildify@^4.0.0"
], ],
@@ -112,6 +112,7 @@
"@types/istanbul-lib-coverage@^2.0.1": "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762", "@types/istanbul-lib-coverage@^2.0.1": "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762",
"@types/istanbul-lib-report@*": "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686", "@types/istanbul-lib-report@*": "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686",
"@types/istanbul-reports@^3.0.0": "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz#508b13aa344fa4976234e75dddcc34925737d821", "@types/istanbul-reports@^3.0.0": "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz#508b13aa344fa4976234e75dddcc34925737d821",
"@types/jest@26.x": "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.20.tgz#cd2f2702ecf69e86b586e1f5223a60e454056307",
"@types/jest@^26.0.20": "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.20.tgz#cd2f2702ecf69e86b586e1f5223a60e454056307", "@types/jest@^26.0.20": "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.20.tgz#cd2f2702ecf69e86b586e1f5223a60e454056307",
"@types/minimatch@*": "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d", "@types/minimatch@*": "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d",
"@types/node@*": "https://registry.yarnpkg.com/@types/node/-/node-14.14.25.tgz#15967a7b577ff81383f9b888aa6705d43fbbae93", "@types/node@*": "https://registry.yarnpkg.com/@types/node/-/node-14.14.25.tgz#15967a7b577ff81383f9b888aa6705d43fbbae93",
@@ -285,7 +286,7 @@
"has-values@^0.1.4": "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771", "has-values@^0.1.4": "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771",
"has-values@^1.0.0": "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f", "has-values@^1.0.0": "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f",
"has@^1.0.3": "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796", "has@^1.0.3": "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796",
"hosted-git-info@^2.1.4": "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9", "hosted-git-info@^2.1.4": "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488",
"html-encoding-sniffer@^2.0.1": "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3", "html-encoding-sniffer@^2.0.1": "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3",
"html-escaper@^2.0.0": "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453", "html-escaper@^2.0.0": "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453",
"http-signature@~1.2.0": "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1", "http-signature@~1.2.0": "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1",
@@ -367,7 +368,7 @@
"jest-validate@^26.6.2": "https://registry.yarnpkg.com/jest-validate/-/jest-validate-26.6.2.tgz#23d380971587150467342911c3d7b4ac57ab20ec", "jest-validate@^26.6.2": "https://registry.yarnpkg.com/jest-validate/-/jest-validate-26.6.2.tgz#23d380971587150467342911c3d7b4ac57ab20ec",
"jest-watcher@^26.6.2": "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-26.6.2.tgz#a5b683b8f9d68dbcb1d7dae32172d2cca0592975", "jest-watcher@^26.6.2": "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-26.6.2.tgz#a5b683b8f9d68dbcb1d7dae32172d2cca0592975",
"jest-worker@^26.6.2": "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed", "jest-worker@^26.6.2": "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed",
"jest@^26.6.3": "https://registry.yarnpkg.com/jest/-/jest-26.6.3.tgz#40e8fdbe48f00dfa1f0ce8121ca74b88ac9148ef", "jest@^26.1.0": "https://registry.yarnpkg.com/jest/-/jest-26.6.3.tgz#40e8fdbe48f00dfa1f0ce8121ca74b88ac9148ef",
"js-tokens@^4.0.0": "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499", "js-tokens@^4.0.0": "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499",
"js-yaml@^3.13.1": "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537", "js-yaml@^3.13.1": "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537",
"jsbn@~0.1.0": "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513", "jsbn@~0.1.0": "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513",
@@ -393,8 +394,8 @@
"lines-and-columns@^1.1.6": "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00", "lines-and-columns@^1.1.6": "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00",
"locate-path@^5.0.0": "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0", "locate-path@^5.0.0": "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0",
"lodash.sortby@^4.7.0": "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438", "lodash.sortby@^4.7.0": "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438",
"lodash@4.x": "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c", "lodash@4.x": "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52",
"lodash@^4.17.19": "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c", "lodash@^4.17.19": "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52",
"lru-cache@^6.0.0": "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94", "lru-cache@^6.0.0": "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94",
"make-dir@^3.0.0": "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f", "make-dir@^3.0.0": "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f",
"make-error@1.x": "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2", "make-error@1.x": "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2",
@@ -567,7 +568,7 @@
"tough-cookie@^3.0.1": "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-3.0.1.tgz#9df4f57e739c26930a018184887f4adb7dca73b2", "tough-cookie@^3.0.1": "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-3.0.1.tgz#9df4f57e739c26930a018184887f4adb7dca73b2",
"tough-cookie@~2.5.0": "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2", "tough-cookie@~2.5.0": "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2",
"tr46@^2.0.2": "https://registry.yarnpkg.com/tr46/-/tr46-2.0.2.tgz#03273586def1595ae08fedb38d7733cee91d2479", "tr46@^2.0.2": "https://registry.yarnpkg.com/tr46/-/tr46-2.0.2.tgz#03273586def1595ae08fedb38d7733cee91d2479",
"ts-jest@^26.5.6": "https://registry.yarnpkg.com/ts-jest/-/ts-jest-26.5.6.tgz#c32e0746425274e1dfe333f43cd3c800e014ec35", "ts-jest@^26.5.1": "https://registry.yarnpkg.com/ts-jest/-/ts-jest-26.5.1.tgz#4d53ee4481552f57c1624f0bd3425c8b17996150",
"tunnel-agent@^0.6.0": "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd", "tunnel-agent@^0.6.0": "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd",
"tunnel@0.0.6": "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c", "tunnel@0.0.6": "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c",
"tweetnacl@^0.14.3": "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64", "tweetnacl@^0.14.3": "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64",

View File

@@ -32,9 +32,9 @@
"devDependencies": { "devDependencies": {
"@types/jest": "^26.0.20", "@types/jest": "^26.0.20",
"@types/node": "^14.14.25", "@types/node": "^14.14.25",
"jest": "^26.6.3", "jest": "^26.1.0",
"jest-circus": "^26.6.3", "jest-circus": "^26.6.3",
"ts-jest": "^26.5.6", "ts-jest": "^26.5.1",
"typescript": "^4.1.4" "typescript": "^4.1.4"
} }
} }

View File

@@ -1,72 +1,53 @@
import {Inputs} from "./Inputs"; import {Inputs} from "./Inputs";
import { import {CreateReleaseResponse, Releases, UpdateReleaseResponse} from "./Releases";
CreateOrUpdateReleaseResponse,
CreateReleaseResponse,
ReleaseByTagResponse,
Releases,
UpdateReleaseResponse
} from "./Releases";
import {ArtifactUploader} from "./ArtifactUploader"; import {ArtifactUploader} from "./ArtifactUploader";
import {GithubError} from "./GithubError"; import {ErrorMessage} from "./ErrorMessage";
import {Outputs} from "./Outputs";
export class Action { export class Action {
private inputs: Inputs private inputs: Inputs
private outputs: Outputs
private releases: Releases private releases: Releases
private uploader: ArtifactUploader private uploader: ArtifactUploader
constructor(inputs: Inputs, outputs: Outputs, releases: Releases, uploader: ArtifactUploader) { constructor(inputs: Inputs, releases: Releases, uploader: ArtifactUploader) {
this.inputs = inputs this.inputs = inputs
this.outputs = outputs
this.releases = releases this.releases = releases
this.uploader = uploader this.uploader = uploader
} }
async perform() { async perform() {
const releaseResponse = await this.createOrUpdateRelease(); const releaseResponse = await this.createOrUpdateRelease();
const releaseData = releaseResponse.data const releaseId = releaseResponse.data.id
const releaseId = releaseData.id const uploadUrl = releaseResponse.data.upload_url
const uploadUrl = releaseData.upload_url
const artifacts = this.inputs.artifacts const artifacts = this.inputs.artifacts
if (artifacts.length > 0) { if (artifacts.length > 0) {
await this.uploader.uploadArtifacts(artifacts, releaseId, uploadUrl) await this.uploader.uploadArtifacts(artifacts, releaseId, uploadUrl)
} }
this.outputs.applyReleaseData(releaseData)
} }
private async createOrUpdateRelease(): Promise<CreateOrUpdateReleaseResponse> { private async createOrUpdateRelease(): Promise<CreateReleaseResponse | UpdateReleaseResponse> {
if (this.inputs.allowUpdates) { if (this.inputs.allowUpdates) {
let getResponse: ReleaseByTagResponse
try { try {
getResponse = await this.releases.getByTag(this.inputs.tag) const getResponse = await this.releases.getByTag(this.inputs.tag)
return await this.updateRelease(getResponse.data.id)
} catch (error) { } catch (error) {
return await this.checkForMissingReleaseError(error) if (Action.noPublishedRelease(error)) {
return await this.updateDraftOrCreateRelease()
} else {
throw error
}
} }
return await this.updateRelease(getResponse.data.id)
} else { } else {
return await this.createRelease() return await this.createRelease()
} }
} }
private async checkForMissingReleaseError(error: Error): Promise<CreateOrUpdateReleaseResponse> {
if (Action.noPublishedRelease(error)) {
return await this.updateDraftOrCreateRelease()
} else {
throw error
}
}
private async updateRelease(id: number): Promise<UpdateReleaseResponse> { private async updateRelease(id: number): Promise<UpdateReleaseResponse> {
return await this.releases.update( return await this.releases.update(
id, id,
this.inputs.tag, this.inputs.tag,
this.inputs.updatedReleaseBody, this.inputs.updatedReleaseBody,
this.inputs.commit, this.inputs.commit,
this.inputs.discussionCategory,
this.inputs.draft, this.inputs.draft,
this.inputs.updatedReleaseName, this.inputs.updatedReleaseName,
this.inputs.prerelease this.inputs.prerelease
@@ -74,8 +55,8 @@ export class Action {
} }
private static noPublishedRelease(error: any): boolean { private static noPublishedRelease(error: any): boolean {
const githubError = new GithubError(error) const errorMessage = new ErrorMessage(error)
return githubError.status == 404 return errorMessage.status == 404
} }
private async updateDraftOrCreateRelease(): Promise<CreateReleaseResponse | UpdateReleaseResponse> { private async updateDraftOrCreateRelease(): Promise<CreateReleaseResponse | UpdateReleaseResponse> {
@@ -97,14 +78,15 @@ export class Action {
} }
private async createRelease(): Promise<CreateReleaseResponse> { private async createRelease(): Promise<CreateReleaseResponse> {
return await this.releases.create( const response = await this.releases.create(
this.inputs.tag, this.inputs.tag,
this.inputs.createdReleaseBody, this.inputs.createdReleaseBody,
this.inputs.commit, this.inputs.commit,
this.inputs.discussionCategory,
this.inputs.draft, this.inputs.draft,
this.inputs.createdReleaseName, this.inputs.createdReleaseName,
this.inputs.prerelease this.inputs.prerelease
) )
return response
} }
} }

View File

@@ -2,10 +2,9 @@ import * as core from '@actions/core';
import {Globber, FileGlobber} from "./Globber"; import {Globber, FileGlobber} from "./Globber";
import {Artifact} from "./Artifact"; import {Artifact} from "./Artifact";
import untildify from "untildify"; import untildify from "untildify";
import {ArtifactPathValidator} from "./ArtifactPathValidator";
export interface ArtifactGlobber { export interface ArtifactGlobber {
globArtifactString(artifact: string, contentType: string, errorsFailBuild: boolean): Artifact[] globArtifactString(artifact: string, contentType: string): Artifact[]
} }
export class FileArtifactGlobber implements ArtifactGlobber { export class FileArtifactGlobber implements ArtifactGlobber {
@@ -15,40 +14,26 @@ export class FileArtifactGlobber implements ArtifactGlobber {
this.globber = globber this.globber = globber
} }
globArtifactString(artifact: string, contentType: string, errorsFailBuild: boolean): Artifact[] { globArtifactString(artifact: string, contentType: string): Artifact[] {
return artifact.split(',') return artifact.split(',')
.map(path => FileArtifactGlobber.expandPath(path)) .map(path => FileArtifactGlobber.expandPath(path))
.map(pattern => this.globPattern(pattern, errorsFailBuild)) .map(pattern => this.globPattern(pattern))
.map((globResult) => FileArtifactGlobber.validatePattern(errorsFailBuild, globResult[1], globResult[0]))
.reduce((accumulated, current) => accumulated.concat(current)) .reduce((accumulated, current) => accumulated.concat(current))
.map(path => new Artifact(path, contentType)) .map(path => new Artifact(path, contentType))
} }
private globPattern(pattern: string, errorsFailBuild: boolean): [string, string[]] { private globPattern(pattern: string): string[] {
const paths = this.globber.glob(pattern) const paths = this.globber.glob(pattern)
if (paths.length == 0) { if (paths.length == 0) {
if (errorsFailBuild) { FileArtifactGlobber.reportGlobWarning(pattern)
FileArtifactGlobber.throwGlobError(pattern)
} else {
FileArtifactGlobber.reportGlobWarning(pattern)
}
} }
return [pattern, paths] return paths
}
private static validatePattern(errorsFailBuild: boolean, paths: string[], pattern: string): string[] {
const validator = new ArtifactPathValidator(errorsFailBuild, paths, pattern)
return validator.validate()
} }
private static reportGlobWarning(pattern: string) { private static reportGlobWarning(pattern: string) {
core.warning(`Artifact pattern :${pattern} did not match any files`) core.warning(`Artifact pattern :${pattern} did not match any files`)
} }
private static throwGlobError(pattern: string) {
throw Error(`Artifact pattern :${pattern} did not match any files`)
}
private static expandPath(path: string): string { private static expandPath(path: string): string {
return untildify(path) return untildify(path)
} }

View File

@@ -1,43 +0,0 @@
import * as core from "@actions/core";
import {statSync} from "fs";
export class ArtifactPathValidator {
private readonly errorsFailBuild: boolean;
private paths: string[];
private readonly pattern: string
constructor(errorsFailBuild: boolean, paths: string[], pattern: string) {
this.paths = paths;
this.pattern = pattern
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) {
const message = `Artifact is a directory:${path}. Directories can not be uploaded to a release.`
this.reportError(message)
}
return !isDir
}
private reportError(message: string) {
if (this.errorsFailBuild) {
throw Error(message)
} else {
core.warning(message)
}
}
}

View File

@@ -10,7 +10,6 @@ export class GithubArtifactUploader implements ArtifactUploader {
constructor( constructor(
private releases: Releases, private releases: Releases,
private replacesExistingArtifacts: boolean = true, private replacesExistingArtifacts: boolean = true,
private throwsUploadErrors: boolean = false,
) { ) {
} }
@@ -42,17 +41,14 @@ export class GithubArtifactUploader implements ArtifactUploader {
core.warning(`Failed to upload artifact ${artifact.name}. ${error.message}. Retrying...`) core.warning(`Failed to upload artifact ${artifact.name}. ${error.message}. Retrying...`)
await this.uploadArtifact(artifact, releaseId, uploadUrl, retry - 1) await this.uploadArtifact(artifact, releaseId, uploadUrl, retry - 1)
} else { } else {
if (this.throwsUploadErrors) { core.warning(`Failed to upload artifact ${artifact.name}. ${error.message}.`)
throw Error(`Failed to upload artifact ${artifact.name}. ${error.message}.`)
} else {
core.warning(`Failed to upload artifact ${artifact.name}. ${error.message}.`)
}
} }
} }
} }
private async deleteUpdatedArtifacts(artifacts: Artifact[], releaseId: number): Promise<void> { private async deleteUpdatedArtifacts(artifacts: Artifact[], releaseId: number): Promise<void> {
const releaseAssets = await this.releases.listArtifactsForRelease(releaseId) const response = await this.releases.listArtifactsForRelease(releaseId)
const releaseAssets = response.data
const assetByName: Record<string, { id: number; name: string }> = {} const assetByName: Record<string, { id: number; name: string }> = {}
releaseAssets.forEach(asset => { releaseAssets.forEach(asset => {
assetByName[asset.name] = asset assetByName[asset.name] = asset

44
src/ErrorMessage.ts Normal file
View File

@@ -0,0 +1,44 @@
import {GithubError} from "./GithubError"
export class ErrorMessage {
private error: any
private githubErrors: GithubError[]
constructor(error: any) {
this.error = error
this.githubErrors = this.generateGithubErrors()
}
private generateGithubErrors(): GithubError[] {
const errors = this.error.errors
if (errors instanceof Array) {
return errors.map((err) => new GithubError(err))
} else {
return []
}
}
get status(): number {
return this.error.status
}
hasErrorWithCode(code: String): boolean {
return this.githubErrors.some((err) => err.code == code)
}
toString(): string {
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}`
}
}
private errorBulletedList(errors: GithubError[]): string {
return errors.map((err) => `- ${err}`).join("\n")
}
}

View File

@@ -1,44 +1,65 @@
import {GithubErrorDetail} from "./GithubErrorDetail"
export class GithubError { export class GithubError {
private error: any private error: any;
private readonly githubErrors: GithubErrorDetail[]
constructor(error: any) { constructor(error: any) {
this.error = error this.error = error
this.githubErrors = this.generateGithubErrors()
} }
private generateGithubErrors(): GithubErrorDetail[] { get code(): string {
const errors = this.error.errors return this.error.code
if (errors instanceof Array) {
return errors.map((err) => new GithubErrorDetail(err))
} else {
return []
}
}
get status(): number {
return this.error.status
}
hasErrorWithCode(code: String): boolean {
return this.githubErrors.some((err) => err.code == code)
} }
toString(): string { toString(): string {
const message = this.error.message const code = this.error.code
const errors = this.githubErrors switch (code) {
const status = this.status case 'missing':
if (errors.length > 0) { return this.missingResourceMessage()
return `Error ${status}: ${message}\nErrors:\n${this.errorBulletedList(errors)}` case 'missing_field':
} else { return this.missingFieldMessage()
return `Error ${status}: ${message}` case 'invalid':
return this.invalidFieldMessage()
case 'already_exists':
return this.resourceAlreadyExists()
default:
return this.customErrorMessage()
} }
} }
private errorBulletedList(errors: GithubErrorDetail[]): string { private customErrorMessage(): string {
return errors.map((err) => `- ${err}`).join("\n") const message = this.error.message;
const documentation = this.error.documentation_url
let documentationMessage: string
if (documentation) {
documentationMessage = `\nPlease see ${documentation}.`
} else {
documentationMessage = ""
}
return `${message}${documentationMessage}`
}
private invalidFieldMessage(): string {
const resource = this.error.resource
const field = this.error.field
return `The ${field} field on ${resource} is an invalid format.`
}
private missingResourceMessage(): string {
const resource = this.error.resource
return `${resource} does not exist.`
}
private missingFieldMessage(): string {
const resource = this.error.resource
const field = this.error.field
return `The ${field} field on ${resource} is missing.`
}
private resourceAlreadyExists(): string {
const resource = this.error.resource
return `${resource} already exists.`
} }
} }

View File

@@ -1,65 +0,0 @@
export class GithubErrorDetail {
private error: any;
constructor(error: any) {
this.error = error
}
get code(): string {
return this.error.code
}
toString(): string {
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()
}
}
private customErrorMessage(): string {
const message = this.error.message;
const documentation = this.error.documentation_url
let documentationMessage: string
if (documentation) {
documentationMessage = `\nPlease see ${documentation}.`
} else {
documentationMessage = ""
}
return `${message}${documentationMessage}`
}
private invalidFieldMessage(): string {
const resource = this.error.resource
const field = this.error.field
return `The ${field} field on ${resource} is an invalid format.`
}
private missingResourceMessage(): string {
const resource = this.error.resource
return `${resource} does not exist.`
}
private missingFieldMessage(): string {
const resource = this.error.resource
const field = this.error.field
return `The ${field} field on ${resource} is missing.`
}
private resourceAlreadyExists(): string {
const resource = this.error.resource
return `${resource} already exists.`
}
}

View File

@@ -6,12 +6,10 @@ import {Artifact} from './Artifact';
export interface Inputs { export interface Inputs {
readonly allowUpdates: boolean readonly allowUpdates: boolean
readonly artifactErrorsFailBuild: boolean
readonly artifacts: Artifact[] readonly artifacts: Artifact[]
readonly commit: string readonly commit: string
readonly createdReleaseBody?: string readonly createdReleaseBody?: string
readonly createdReleaseName?: string readonly createdReleaseName?: string
readonly discussionCategory?: string
readonly draft: boolean readonly draft: boolean
readonly owner: string readonly owner: string
readonly prerelease: boolean readonly prerelease: boolean
@@ -48,16 +46,11 @@ export class CoreInputs implements Inputs {
contentType = 'raw' contentType = 'raw'
} }
return this.artifactGlobber return this.artifactGlobber
.globArtifactString(artifacts, contentType, this.artifactErrorsFailBuild) .globArtifactString(artifacts, contentType)
} }
return [] return []
} }
get artifactErrorsFailBuild(): boolean {
const allow = core.getInput('artifactErrorsFailBuild')
return allow == 'true'
}
get createdReleaseBody(): string | undefined { get createdReleaseBody(): string | undefined {
if (CoreInputs.omitBody) return undefined if (CoreInputs.omitBody) return undefined
return this.body return this.body
@@ -67,7 +60,7 @@ export class CoreInputs implements Inputs {
return core.getInput('omitBody') == 'true' return core.getInput('omitBody') == 'true'
} }
private get body(): string | undefined { private get body() : string | undefined {
const body = core.getInput('body') const body = core.getInput('body')
if (body) { if (body) {
return body return body
@@ -90,14 +83,6 @@ export class CoreInputs implements Inputs {
return this.name return this.name
} }
get discussionCategory(): string | undefined {
const category = core.getInput('discussionCategory')
if (category) {
return category
}
return undefined
}
private static get omitName(): boolean { private static get omitName(): boolean {
return core.getInput('omitName') == 'true' return core.getInput('omitName') == 'true'
} }
@@ -171,7 +156,7 @@ export class CoreInputs implements Inputs {
} }
get updatedReleaseName(): string | undefined { get updatedReleaseName(): string | undefined {
if (CoreInputs.omitName || CoreInputs.omitNameDuringUpdate) return undefined if (CoreInputs.omitName || CoreInputs.omitNameDuringUpdate) return undefined
return this.name return this.name
} }

View File

@@ -1,34 +1,32 @@
import * as github from '@actions/github'; import * as github from '@actions/github';
import * as core from '@actions/core'; import * as core from '@actions/core';
import {CoreInputs} from './Inputs'; import { CoreInputs } from './Inputs';
import {GithubReleases} from './Releases'; import { GithubReleases } from './Releases';
import {Action} from './Action'; import { Action } from './Action';
import {GithubArtifactUploader} from './ArtifactUploader'; import { GithubArtifactUploader } from './ArtifactUploader';
import {FileArtifactGlobber} from './ArtifactGlobber'; import { FileArtifactGlobber } from './ArtifactGlobber';
import {GithubError} from './GithubError'; import { ErrorMessage } from './ErrorMessage';
import {CoreOutputs} from "./Outputs";
async function run() { async function run() {
try { try {
const action = createAction() const action = createAction()
await action.perform() await action.perform()
} catch (error) { } catch (error) {
const githubError = new GithubError(error) const errorMessage = new ErrorMessage(error)
core.setFailed(githubError.toString()); core.setFailed(errorMessage.toString());
} }
} }
function createAction(): Action { function createAction(): Action {
const token = core.getInput('token') const token = core.getInput('token')
const context = github.context const context = github.context
const git = github.getOctokit(token) const git = github.getOctokit(token)
const globber = new FileArtifactGlobber() const globber = new FileArtifactGlobber()
const inputs = new CoreInputs(globber, context) const inputs = new CoreInputs(globber, context)
const outputs = new CoreOutputs() const releases = new GithubReleases(inputs, git)
const releases = new GithubReleases(inputs, git) const uploader = new GithubArtifactUploader(releases, inputs.replacesArtifacts)
const uploader = new GithubArtifactUploader(releases, inputs.replacesArtifacts, inputs.artifactErrorsFailBuild) return new Action(inputs, releases, uploader)
return new Action(inputs, outputs, releases, uploader)
} }
run(); run();

View File

@@ -1,14 +0,0 @@
import * as core from '@actions/core';
import {ReleaseData} from "./Releases";
export interface Outputs {
applyReleaseData(releaseData: ReleaseData): 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)
}
}

View File

@@ -6,23 +6,15 @@ import {Inputs} from "./Inputs";
export type CreateReleaseResponse = RestEndpointMethodTypes["repos"]["createRelease"]["response"] export type CreateReleaseResponse = RestEndpointMethodTypes["repos"]["createRelease"]["response"]
export type ReleaseByTagResponse = RestEndpointMethodTypes["repos"]["getReleaseByTag"]["response"] export type ReleaseByTagResponse = RestEndpointMethodTypes["repos"]["getReleaseByTag"]["response"]
export type ListReleasesResponse = RestEndpointMethodTypes["repos"]["listReleases"]["response"] export type ListReleasesResponse = RestEndpointMethodTypes["repos"]["listReleases"]["response"]
export type ListReleaseAssetsResponseData = RestEndpointMethodTypes["repos"]["listReleaseAssets"]["response"]["data"] export type ListReleaseAssetsResponse = RestEndpointMethodTypes["repos"]["listReleaseAssets"]["response"]
export type UpdateReleaseResponse = RestEndpointMethodTypes["repos"]["updateRelease"]["response"] export type UpdateReleaseResponse = RestEndpointMethodTypes["repos"]["updateRelease"]["response"]
export type UploadArtifactResponse = RestEndpointMethodTypes["repos"]["uploadReleaseAsset"]["response"] export type UploadArtifactResponse = RestEndpointMethodTypes["repos"]["uploadReleaseAsset"]["response"]
export type CreateOrUpdateReleaseResponse = CreateReleaseResponse | UpdateReleaseResponse
export type ReleaseData = {
id: number
html_url: string
upload_url: string
}
export interface Releases { export interface Releases {
create( create(
tag: string, tag: string,
body?: string, body?: string,
commitHash?: string, commitHash?: string,
discussionCategory?: string,
draft?: boolean, draft?: boolean,
name?: string, name?: string,
prerelease?: boolean prerelease?: boolean
@@ -32,7 +24,7 @@ export interface Releases {
getByTag(tag: string): Promise<ReleaseByTagResponse> getByTag(tag: string): Promise<ReleaseByTagResponse>
listArtifactsForRelease(releaseId: number): Promise<ListReleaseAssetsResponseData> listArtifactsForRelease(releaseId: number): Promise<ListReleaseAssetsResponse>
listReleases(): Promise<ListReleasesResponse> listReleases(): Promise<ListReleasesResponse>
@@ -41,7 +33,6 @@ export interface Releases {
tag: string, tag: string,
body?: string, body?: string,
commitHash?: string, commitHash?: string,
discussionCategory?: string,
draft?: boolean, draft?: boolean,
name?: string, name?: string,
prerelease?: boolean prerelease?: boolean
@@ -70,7 +61,6 @@ export class GithubReleases implements Releases {
tag: string, tag: string,
body?: string, body?: string,
commitHash?: string, commitHash?: string,
discussionCategory?: string,
draft?: boolean, draft?: boolean,
name?: string, name?: string,
prerelease?: boolean prerelease?: boolean
@@ -79,7 +69,6 @@ export class GithubReleases implements Releases {
return this.git.repos.createRelease({ return this.git.repos.createRelease({
body: body, body: body,
name: name, name: name,
discussion_category_name: discussionCategory,
draft: draft, draft: draft,
owner: this.inputs.owner, owner: this.inputs.owner,
prerelease: prerelease, prerelease: prerelease,
@@ -109,8 +98,8 @@ export class GithubReleases implements Releases {
async listArtifactsForRelease( async listArtifactsForRelease(
releaseId: number releaseId: number
): Promise<ListReleaseAssetsResponseData> { ): Promise<ListReleaseAssetsResponse> {
return this.git.paginate(this.git.repos.listReleaseAssets, { return this.git.repos.listReleaseAssets({
owner: this.inputs.owner, owner: this.inputs.owner,
release_id: releaseId, release_id: releaseId,
repo: this.inputs.repo repo: this.inputs.repo
@@ -129,7 +118,6 @@ export class GithubReleases implements Releases {
tag: string, tag: string,
body?: string, body?: string,
commitHash?: string, commitHash?: string,
discussionCategory?: string,
draft?: boolean, draft?: boolean,
name?: string, name?: string,
prerelease?: boolean prerelease?: boolean
@@ -139,7 +127,6 @@ export class GithubReleases implements Releases {
release_id: id, release_id: id,
body: body, body: body,
name: name, name: name,
discussion_category_name: discussionCategory,
draft: draft, draft: draft,
owner: this.inputs.owner, owner: this.inputs.owner,
prerelease: prerelease, prerelease: prerelease,

View File

@@ -656,7 +656,7 @@
dependencies: dependencies:
"@types/istanbul-lib-report" "*" "@types/istanbul-lib-report" "*"
"@types/jest@^26.0.20": "@types/jest@26.x", "@types/jest@^26.0.20":
version "26.0.20" version "26.0.20"
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.20.tgz#cd2f2702ecf69e86b586e1f5223a60e454056307" resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.20.tgz#cd2f2702ecf69e86b586e1f5223a60e454056307"
integrity sha512-9zi2Y+5USJRxd0FsahERhBwlcvFh6D2GLQnY2FH2BzK8J9s9omvNHIbvABwIluXa0fD8XVKMLTO0aOEuUfACAA== integrity sha512-9zi2Y+5USJRxd0FsahERhBwlcvFh6D2GLQnY2FH2BzK8J9s9omvNHIbvABwIluXa0fD8XVKMLTO0aOEuUfACAA==
@@ -1695,9 +1695,9 @@ has@^1.0.3:
function-bind "^1.1.1" function-bind "^1.1.1"
hosted-git-info@^2.1.4: hosted-git-info@^2.1.4:
version "2.8.9" version "2.8.8"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488"
integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==
html-encoding-sniffer@^2.0.1: html-encoding-sniffer@^2.0.1:
version "2.0.1" version "2.0.1"
@@ -2375,7 +2375,7 @@ jest-worker@^26.6.2:
merge-stream "^2.0.0" merge-stream "^2.0.0"
supports-color "^7.0.0" supports-color "^7.0.0"
jest@^26.6.3: jest@^26.1.0:
version "26.6.3" version "26.6.3"
resolved "https://registry.yarnpkg.com/jest/-/jest-26.6.3.tgz#40e8fdbe48f00dfa1f0ce8121ca74b88ac9148ef" resolved "https://registry.yarnpkg.com/jest/-/jest-26.6.3.tgz#40e8fdbe48f00dfa1f0ce8121ca74b88ac9148ef"
integrity sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q== integrity sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q==
@@ -2536,9 +2536,9 @@ lodash.sortby@^4.7.0:
integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=
lodash@4.x, lodash@^4.17.19: lodash@4.x, lodash@^4.17.19:
version "4.17.21" version "4.17.20"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
lru-cache@^6.0.0: lru-cache@^6.0.0:
version "6.0.0" version "6.0.0"
@@ -3517,11 +3517,12 @@ tr46@^2.0.2:
dependencies: dependencies:
punycode "^2.1.1" punycode "^2.1.1"
ts-jest@^26.5.6: ts-jest@^26.5.1:
version "26.5.6" version "26.5.1"
resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-26.5.6.tgz#c32e0746425274e1dfe333f43cd3c800e014ec35" resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-26.5.1.tgz#4d53ee4481552f57c1624f0bd3425c8b17996150"
integrity sha512-rua+rCP8DxpA8b4DQD/6X2HQS8Zy/xzViVYfEs2OQu68tkCuKLV0Md8pmX55+W24uRIyAsf/BajRfxOs+R2MKA== integrity sha512-G7Rmo3OJMvlqE79amJX8VJKDiRcd7/r61wh9fnvvG8cAjhA9edklGw/dCxRSQmfZ/z8NDums5srSVgwZos1qfg==
dependencies: dependencies:
"@types/jest" "26.x"
bs-logger "0.x" bs-logger "0.x"
buffer-from "1.x" buffer-from "1.x"
fast-json-stable-stringify "2.x" fast-json-stable-stringify "2.x"