Fixes #8 Add support for draft release updating

This commit is contained in:
Nick Cipollo
2020-01-02 22:03:30 -05:00
parent c8e9e33d90
commit 04d64ce68e
5 changed files with 131 additions and 36 deletions

View File

@@ -6,6 +6,7 @@ import { ArtifactUploader } from "../src/ArtifactUploader";
const createMock = jest.fn() const createMock = jest.fn()
const getMock = jest.fn() const getMock = jest.fn()
const listMock = jest.fn()
const updateMock = jest.fn() const updateMock = jest.fn()
const uploadMock = jest.fn() const uploadMock = jest.fn()
@@ -28,6 +29,7 @@ describe("Action", () => {
beforeEach(() => { beforeEach(() => {
createMock.mockClear() createMock.mockClear()
getMock.mockClear() getMock.mockClear()
listMock.mockClear()
updateMock.mockClear() updateMock.mockClear()
uploadMock.mockClear() uploadMock.mockClear()
}) })
@@ -43,9 +45,7 @@ describe("Action", () => {
it('creates release if no release exists to update', async () => { it('creates release if no release exists to update', async () => {
const action = createAction(true, true) const action = createAction(true, true)
const error = { const error = { status: 404 }
status: 404
}
getMock.mockRejectedValue(error) getMock.mockRejectedValue(error)
await action.perform() await action.perform()
@@ -54,6 +54,23 @@ describe("Action", () => {
expect(uploadMock).toBeCalledWith(artifacts, url) expect(uploadMock).toBeCalledWith(artifacts, url)
}) })
it('creates release if no draft releases', async () => {
const action = createAction(true, true)
const error = { status: 404 }
getMock.mockRejectedValue(error)
listMock.mockResolvedValue({
data: [
{ id: id, draft: false, tag_name: tag }
]
})
await action.perform()
expect(createMock).toBeCalledWith(tag, body, commit, draft, name, prerelease)
expect(uploadMock).toBeCalledWith(artifacts, url)
})
it('creates release then uploads artifact', async () => { it('creates release then uploads artifact', async () => {
const action = createAction(false, true) const action = createAction(false, true)
@@ -134,6 +151,24 @@ describe("Action", () => {
expect(uploadMock).toBeCalledWith(artifacts, url) expect(uploadMock).toBeCalledWith(artifacts, url)
}) })
it('updates draft release', async () => {
const action = createAction(true, true)
const error = { status: 404 }
getMock.mockRejectedValue(error)
listMock.mockResolvedValue({
data: [
{ id: 123, draft: false, tag_name: tag },
{ id: id, draft: true, tag_name: tag }
]
})
await action.perform()
expect(updateMock).toBeCalledWith(id, tag, body, commit, draft, name, prerelease)
expect(uploadMock).toBeCalledWith(artifacts, url)
})
it('updates release but does not upload if no artifact', async () => { it('updates release but does not upload if no artifact', async () => {
const action = createAction(true, false) const action = createAction(true, false)
@@ -165,6 +200,7 @@ describe("Action", () => {
return { return {
create: createMock, create: createMock,
getByTag: getMock, getByTag: getMock,
listReleases: listMock,
update: updateMock, update: updateMock,
uploadArtifact: uploadMock uploadArtifact: uploadMock
} }
@@ -180,6 +216,9 @@ describe("Action", () => {
id: id id: id
} }
}) })
listMock.mockResolvedValue({
data: []
})
updateMock.mockResolvedValue({ updateMock.mockResolvedValue({
data: { data: {
upload_url: url upload_url: url

View File

@@ -33,8 +33,8 @@ class Action {
return yield this.updateRelease(getResponse.data.id); return yield this.updateRelease(getResponse.data.id);
} }
catch (error) { catch (error) {
if (this.noRelease(error)) { if (this.noPublishedRelease(error)) {
return yield this.createRelease(); return yield this.updateDraftOrCreateRelease();
} }
else { else {
throw error; throw error;
@@ -46,21 +46,42 @@ class Action {
} }
}); });
} }
createRelease() {
return __awaiter(this, void 0, void 0, function* () {
const response = yield this.releases.create(this.inputs.tag, this.inputs.body, this.inputs.commit, this.inputs.draft, this.inputs.name, this.inputs.prerelease);
return response.data.upload_url;
});
}
noRelease(error) {
const errorMessage = new ErrorMessage_1.ErrorMessage(error);
return errorMessage.status == 404;
}
updateRelease(id) { updateRelease(id) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const response = yield this.releases.update(id, this.inputs.tag, this.inputs.body, this.inputs.commit, this.inputs.draft, this.inputs.name, this.inputs.prerelease); const response = yield this.releases.update(id, this.inputs.tag, this.inputs.body, this.inputs.commit, this.inputs.draft, this.inputs.name, this.inputs.prerelease);
return response.data.upload_url; return response.data.upload_url;
}); });
} }
noPublishedRelease(error) {
const errorMessage = new ErrorMessage_1.ErrorMessage(error);
return errorMessage.status == 404;
}
updateDraftOrCreateRelease() {
return __awaiter(this, void 0, void 0, function* () {
const draftReleaseId = yield this.findMatchingDraftReleaseId();
if (draftReleaseId) {
return yield this.updateRelease(draftReleaseId);
}
else {
return yield this.createRelease();
}
});
}
findMatchingDraftReleaseId() {
var _a;
return __awaiter(this, void 0, void 0, function* () {
const tag = this.inputs.tag;
const response = yield this.releases.listReleases();
const releases = response.data;
const draftRelease = releases.find(release => release.draft && release.tag_name == tag);
return (_a = draftRelease) === null || _a === void 0 ? void 0 : _a.id;
});
}
createRelease() {
return __awaiter(this, void 0, void 0, function* () {
const response = yield this.releases.create(this.inputs.tag, this.inputs.body, this.inputs.commit, this.inputs.draft, this.inputs.name, this.inputs.prerelease);
return response.data.upload_url;
});
}
} }
exports.Action = Action; exports.Action = Action;

View File

@@ -28,6 +28,14 @@ class GithubReleases {
}); });
}); });
} }
listReleases() {
return __awaiter(this, void 0, void 0, function* () {
return this.git.repos.listReleases({
owner: this.context.repo.owner,
repo: this.context.repo.repo
});
});
}
getByTag(tag) { getByTag(tag) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
return this.git.repos.getReleaseByTag({ return this.git.repos.getReleaseByTag({

View File

@@ -29,8 +29,8 @@ export class Action {
const getResponse = await this.releases.getByTag(this.inputs.tag) const getResponse = await this.releases.getByTag(this.inputs.tag)
return await this.updateRelease(getResponse.data.id) return await this.updateRelease(getResponse.data.id)
} catch (error) { } catch (error) {
if (this.noRelease(error)) { if (this.noPublishedRelease(error)) {
return await this.createRelease() return await this.updateDraftOrCreateRelease()
} else { } else {
throw error throw error
} }
@@ -40,24 +40,6 @@ export class Action {
} }
} }
private async createRelease(): Promise<string> {
const response = await this.releases.create(
this.inputs.tag,
this.inputs.body,
this.inputs.commit,
this.inputs.draft,
this.inputs.name,
this.inputs.prerelease
)
return response.data.upload_url
}
private noRelease(error: any): boolean {
const errorMessage = new ErrorMessage(error)
return errorMessage.status == 404
}
private async updateRelease(id: number): Promise<string> { private async updateRelease(id: number): Promise<string> {
const response = await this.releases.update( const response = await this.releases.update(
id, id,
@@ -71,4 +53,40 @@ export class Action {
return response.data.upload_url return response.data.upload_url
} }
private noPublishedRelease(error: any): boolean {
const errorMessage = new ErrorMessage(error)
return errorMessage.status == 404
}
private async updateDraftOrCreateRelease(): Promise<string> {
const draftReleaseId = await this.findMatchingDraftReleaseId()
if (draftReleaseId) {
return await this.updateRelease(draftReleaseId)
} else {
return await this.createRelease()
}
}
private async findMatchingDraftReleaseId(): Promise<number | undefined> {
const tag = this.inputs.tag
const response = await this.releases.listReleases()
const releases = response.data
const draftRelease = releases.find(release => release.draft && release.tag_name == tag)
return draftRelease?.id
}
private async createRelease(): Promise<string> {
const response = await this.releases.create(
this.inputs.tag,
this.inputs.body,
this.inputs.commit,
this.inputs.draft,
this.inputs.name,
this.inputs.prerelease
)
return response.data.upload_url
}
} }

View File

@@ -1,6 +1,6 @@
import { Context } from "@actions/github/lib/context"; import { Context } from "@actions/github/lib/context";
import { GitHub } from "@actions/github"; import { GitHub } from "@actions/github";
import { AnyResponse, Response, ReposCreateReleaseResponse, ReposGetReleaseByTagResponse } from "@octokit/rest"; import { AnyResponse, Response, ReposCreateReleaseResponse, ReposGetReleaseByTagResponse, ReposListReleasesResponse } from "@octokit/rest";
export interface Releases { export interface Releases {
create( create(
@@ -14,6 +14,8 @@ export interface Releases {
getByTag(tag: string): Promise<Response<ReposGetReleaseByTagResponse>> getByTag(tag: string): Promise<Response<ReposGetReleaseByTagResponse>>
listReleases(): Promise<Response<ReposListReleasesResponse>>
update( update(
id: number, id: number,
tag: string, tag: string,
@@ -62,6 +64,13 @@ export class GithubReleases implements Releases {
}) })
} }
async listReleases(): Promise<Response<ReposListReleasesResponse>> {
return this.git.repos.listReleases({
owner: this.context.repo.owner,
repo: this.context.repo.repo
})
}
async getByTag(tag: string): Promise<Response<ReposGetReleaseByTagResponse>> { async getByTag(tag: string): Promise<Response<ReposGetReleaseByTagResponse>> {
return this.git.repos.getReleaseByTag({ return this.git.repos.getReleaseByTag({
owner: this.context.repo.owner, owner: this.context.repo.owner,