Fixes #19 Bump dependencies & update octokit usage

This commit is contained in:
Nick Cipollo
2020-07-20 14:07:43 -04:00
parent 94366fd224
commit 067437b167
12 changed files with 1411 additions and 1248 deletions

View File

@@ -30,6 +30,44 @@ describe('ArtifactUploader', () => {
uploadMock.mockClear()
})
it('abort when upload failed with non-5xx response', async () => {
mockListWithoutAssets()
mockUploadArtifact(401, 2)
const uploader = createUploader(true)
await uploader.uploadArtifacts(artifacts, releaseId, url)
expect(uploadMock).toBeCalledTimes(2)
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art1', releaseId)
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art2', releaseId)
expect(deleteMock).toBeCalledTimes(0)
})
it('abort when upload failed with 5xx response after 3 attempts', async () => {
mockListWithoutAssets()
mockUploadArtifact(500, 4)
const uploader = createUploader(true)
await uploader.uploadArtifacts(artifacts, releaseId, url)
expect(uploadMock).toBeCalledTimes(5)
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art1', releaseId)
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art1', releaseId)
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art1', releaseId)
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art2', releaseId)
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art2', releaseId)
expect(deleteMock).toBeCalledTimes(0)
})
it('replaces all artifacts', async () => {
mockDeleteSuccess()
mockListWithAssets()
@@ -40,9 +78,9 @@ describe('ArtifactUploader', () => {
expect(uploadMock).toBeCalledTimes(2)
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art1')
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art1', releaseId)
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art2')
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art2', releaseId)
expect(deleteMock).toBeCalledTimes(2)
expect(deleteMock).toBeCalledWith(1)
@@ -59,9 +97,9 @@ describe('ArtifactUploader', () => {
expect(uploadMock).toBeCalledTimes(2)
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art1')
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art1', releaseId)
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art2')
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art2', releaseId)
expect(deleteMock).toBeCalledTimes(0)
})
@@ -75,51 +113,13 @@ describe('ArtifactUploader', () => {
expect(uploadMock).toBeCalledTimes(4)
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art1')
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art1', releaseId)
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art1')
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art1', releaseId)
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art1')
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art1', releaseId)
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art2')
expect(deleteMock).toBeCalledTimes(0)
})
it('abort when upload failed with 5xx response after 3 attemps', async () => {
mockListWithoutAssets()
mockUploadArtifact(500, 4)
const uploader = createUploader(true)
await uploader.uploadArtifacts(artifacts, releaseId, url)
expect(uploadMock).toBeCalledTimes(5)
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art1')
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art1')
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art1')
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art2')
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art2')
expect(deleteMock).toBeCalledTimes(0)
})
it('abort when upload failed with non-5xx response', async () => {
mockListWithoutAssets()
mockUploadArtifact(401, 2)
const uploader = createUploader(true)
await uploader.uploadArtifacts(artifacts, releaseId, url)
expect(uploadMock).toBeCalledTimes(2)
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art1')
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art2')
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art2', releaseId)
expect(deleteMock).toBeCalledTimes(0)
})
@@ -148,9 +148,9 @@ describe('ArtifactUploader', () => {
expect(uploadMock).toBeCalledTimes(2)
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art1')
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art1', releaseId)
expect(uploadMock)
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art2')
.toBeCalledWith(url, contentLength, 'raw', fileContents, 'art2', releaseId)
expect(deleteMock).toBeCalledTimes(0)
})
@@ -204,4 +204,4 @@ describe('ArtifactUploader', () => {
}
uploadMock.mockResolvedValue({})
}
});
});

View File

@@ -23,7 +23,7 @@ class Action {
return await this.updateRelease(getResponse.data.id);
}
catch (error) {
if (this.noPublishedRelease(error)) {
if (Action.noPublishedRelease(error)) {
return await this.updateDraftOrCreateRelease();
}
else {
@@ -39,7 +39,7 @@ class Action {
const response = await this.releases.update(id, this.inputs.tag, this.inputs.updatedReleaseBody, this.inputs.commit, this.inputs.draft, this.inputs.updatedReleaseName, this.inputs.prerelease);
return response.data;
}
noPublishedRelease(error) {
static noPublishedRelease(error) {
const errorMessage = new ErrorMessage_1.ErrorMessage(error);
return errorMessage.status == 404;
}

View File

@@ -18,18 +18,18 @@ class GithubArtifactUploader {
await this.deleteUpdatedArtifacts(artifacts, releaseId);
}
for (const artifact of artifacts) {
await this.uploadArtifact(artifact, uploadUrl);
await this.uploadArtifact(artifact, releaseId, uploadUrl);
}
}
async uploadArtifact(artifact, uploadUrl, retry = 3) {
async uploadArtifact(artifact, releaseId, uploadUrl, retry = 3) {
try {
core.debug(`Uploading artifact ${artifact.name}...`);
await this.releases.uploadArtifact(uploadUrl, artifact.contentLength, artifact.contentType, artifact.readFile(), artifact.name);
await this.releases.uploadArtifact(uploadUrl, artifact.contentLength, artifact.contentType, artifact.readFile(), artifact.name, releaseId);
}
catch (error) {
if (error.status >= 500 && retry > 0) {
core.warning(`Failed to upload artifact ${artifact.name}. ${error.message}. Retrying...`);
await this.uploadArtifact(artifact, uploadUrl, retry - 1);
await this.uploadArtifact(artifact, releaseId, uploadUrl, retry - 1);
}
else {
core.warning(`Failed to upload artifact ${artifact.name}. ${error.message}.`);

View File

@@ -28,7 +28,7 @@ async function run() {
function createAction() {
const token = core.getInput('token');
const context = github.context;
const git = new github.GitHub(token);
const git = github.getOctokit(token);
const globber = new ArtifactGlobber_1.FileArtifactGlobber();
const inputs = new Inputs_1.CoreInputs(globber, context);
const releases = new Releases_1.GithubReleases(context, git);

View File

@@ -6,6 +6,7 @@ class GithubReleases {
this.git = git;
}
async create(tag, body, commitHash, draft, name, prerelease) {
// noinspection TypeScriptValidateJSTypes
return this.git.repos.createRelease({
body: body,
name: name,
@@ -25,7 +26,7 @@ class GithubReleases {
});
}
async listArtifactsForRelease(releaseId) {
return this.git.repos.listAssetsForRelease({
return this.git.repos.listReleaseAssets({
owner: this.context.repo.owner,
release_id: releaseId,
repo: this.context.repo.repo
@@ -45,6 +46,7 @@ class GithubReleases {
});
}
async update(id, tag, body, commitHash, draft, name, prerelease) {
// noinspection TypeScriptValidateJSTypes
return this.git.repos.updateRelease({
release_id: id,
body: body,
@@ -57,15 +59,18 @@ class GithubReleases {
tag_name: tag
});
}
async uploadArtifact(assetUrl, contentLength, contentType, file, name) {
async uploadArtifact(assetUrl, contentLength, contentType, file, name, releaseId) {
return this.git.repos.uploadReleaseAsset({
url: assetUrl,
headers: {
"content-length": contentLength,
"content-type": contentType
},
file: file,
name: name
data: file,
name: name,
owner: this.context.repo.owner,
release_id: releaseId,
repo: this.context.repo.repo
});
}
}

View File

@@ -23,19 +23,19 @@
"author": "GitHub",
"license": "MIT",
"dependencies": {
"@actions/core": "^1.0.0",
"@actions/github": "^1.0.0",
"@types/glob": "^7.1.1",
"@types/node": "^12.0.4",
"@actions/core": "^1.2.4",
"@actions/github": "^4.0.0",
"add": "^2.0.6",
"glob": "^7.1.4",
"global": "^4.4.0"
},
"devDependencies": {
"@types/jest": "^25.1.4",
"jest": "^25.1.0",
"jest-circus": "^25.1.0",
"ts-jest": "^25.2.1",
"typescript": "^3.7.3"
"@types/glob": "^7.1.1",
"@types/node": "^12.0.4",
"@types/jest": "^26.0.5",
"jest": "^26.1.0",
"jest-circus": "^26.1.0",
"ts-jest": "^26.1.3",
"typescript": "^3.9.7"
}
}

View File

@@ -1,6 +1,6 @@
import { Inputs } from "./Inputs";
import { Releases } from "./Releases";
import { ReposCreateReleaseResponse } from "@octokit/rest";
import { ReposCreateReleaseResponseData } from "@octokit/types";
import { ArtifactUploader } from "./ArtifactUploader";
import { ErrorMessage } from "./ErrorMessage";
@@ -26,13 +26,13 @@ export class Action {
}
}
private async createOrUpdateRelease(): Promise<ReposCreateReleaseResponse> {
private async createOrUpdateRelease(): Promise<ReposCreateReleaseResponseData> {
if (this.inputs.allowUpdates) {
try {
const getResponse = await this.releases.getByTag(this.inputs.tag)
return await this.updateRelease(getResponse.data.id)
} catch (error) {
if (this.noPublishedRelease(error)) {
if (Action.noPublishedRelease(error)) {
return await this.updateDraftOrCreateRelease()
} else {
throw error
@@ -43,7 +43,7 @@ export class Action {
}
}
private async updateRelease(id: number): Promise<ReposCreateReleaseResponse> {
private async updateRelease(id: number): Promise<ReposCreateReleaseResponseData> {
const response = await this.releases.update(
id,
this.inputs.tag,
@@ -57,12 +57,12 @@ export class Action {
return response.data
}
private noPublishedRelease(error: any): boolean {
private static noPublishedRelease(error: any): boolean {
const errorMessage = new ErrorMessage(error)
return errorMessage.status == 404
}
private async updateDraftOrCreateRelease(): Promise<ReposCreateReleaseResponse> {
private async updateDraftOrCreateRelease(): Promise<ReposCreateReleaseResponseData> {
const draftReleaseId = await this.findMatchingDraftReleaseId()
if (draftReleaseId) {
return await this.updateRelease(draftReleaseId)
@@ -80,7 +80,7 @@ export class Action {
return draftRelease?.id
}
private async createRelease(): Promise<ReposCreateReleaseResponse> {
private async createRelease(): Promise<ReposCreateReleaseResponseData> {
const response = await this.releases.create(
this.inputs.tag,
this.inputs.createdReleaseBody,

View File

@@ -19,4 +19,4 @@ export class Artifact {
readFile(): Buffer {
return readFileSync(this.path)
}
}
}

View File

@@ -1,7 +1,6 @@
import * as core from '@actions/core';
import { Artifact } from "./Artifact";
import { Releases } from "./Releases";
import { ReposListAssetsForReleaseResponseItem } from "@octokit/rest";
import {Artifact} from "./Artifact";
import {Releases} from "./Releases";
export interface ArtifactUploader {
uploadArtifacts(artifacts: Artifact[], releaseId: number, uploadUrl: string): Promise<void>
@@ -15,28 +14,32 @@ export class GithubArtifactUploader implements ArtifactUploader {
}
async uploadArtifacts(artifacts: Artifact[],
releaseId: number,
uploadUrl: string): Promise<void> {
releaseId: number,
uploadUrl: string): Promise<void> {
if (this.replacesExistingArtifacts) {
await this.deleteUpdatedArtifacts(artifacts, releaseId)
}
for (const artifact of artifacts) {
await this.uploadArtifact(artifact, uploadUrl)
await this.uploadArtifact(artifact, releaseId, uploadUrl)
}
}
private async uploadArtifact(artifact: Artifact, uploadUrl: string, retry = 3) {
private async uploadArtifact(artifact: Artifact,
releaseId: number,
uploadUrl: string,
retry = 3) {
try {
core.debug(`Uploading artifact ${artifact.name}...`)
await this.releases.uploadArtifact(uploadUrl,
artifact.contentLength,
artifact.contentType,
artifact.readFile(),
artifact.name)
artifact.name,
releaseId)
} catch (error) {
if (error.status >= 500 && retry > 0) {
core.warning(`Failed to upload artifact ${artifact.name}. ${error.message}. Retrying...`)
await this.uploadArtifact(artifact, uploadUrl, retry - 1)
await this.uploadArtifact(artifact, releaseId, uploadUrl, retry - 1)
} else {
core.warning(`Failed to upload artifact ${artifact.name}. ${error.message}.`)
}
@@ -44,9 +47,9 @@ export class GithubArtifactUploader implements ArtifactUploader {
}
private async deleteUpdatedArtifacts(artifacts: Artifact[], releaseId: number): Promise<void> {
const response = await this.releases.listArtifactsForRelease(releaseId)
const response = await this.releases.listArtifactsForRelease(releaseId)
const releaseAssets = response.data
const assetByName: Record<string, ReposListAssetsForReleaseResponseItem> = {}
const assetByName: Record<string, { id: number; name: string }> = {}
releaseAssets.forEach(asset => {
assetByName[asset.name] = asset
});
@@ -58,4 +61,4 @@ export class GithubArtifactUploader implements ArtifactUploader {
}
}
}
}
}

View File

@@ -20,7 +20,7 @@ async function run() {
function createAction(): Action {
const token = core.getInput('token')
const context = github.context
const git = new github.GitHub(token)
const git = github.getOctokit(token)
const globber = new FileArtifactGlobber()
const inputs = new CoreInputs(globber, context)

View File

@@ -1,6 +1,13 @@
import { Context } from "@actions/github/lib/context";
import { GitHub } from "@actions/github";
import { AnyResponse, Response, ReposDeleteReleaseAssetResponse, ReposListAssetsForReleaseResponse, ReposCreateReleaseResponse, ReposGetReleaseByTagResponse, ReposListReleasesResponse } from "@octokit/rest";
import {Context} from "@actions/github/lib/context";
import { GitHub } from '@actions/github/lib/utils'
import {
OctokitResponse,
ReposListReleaseAssetsResponseData,
ReposCreateReleaseResponseData,
ReposGetReleaseByTagResponseData,
ReposListReleasesResponseData,
ReposUploadReleaseAssetResponseData
} from "@octokit/types";
export interface Releases {
create(
@@ -10,15 +17,15 @@ export interface Releases {
draft?: boolean,
name?: string,
prerelease?: boolean
): Promise<Response<ReposCreateReleaseResponse>>
): Promise<OctokitResponse<ReposCreateReleaseResponseData>>
deleteArtifact(assetId: number): Promise<Response<ReposDeleteReleaseAssetResponse>>
deleteArtifact(assetId: number): Promise<OctokitResponse<any>>
getByTag(tag: string): Promise<Response<ReposGetReleaseByTagResponse>>
getByTag(tag: string): Promise<OctokitResponse<ReposGetReleaseByTagResponseData>>
listArtifactsForRelease(releaseId: number): Promise<Response<ReposListAssetsForReleaseResponse>>
listArtifactsForRelease(releaseId: number): Promise<OctokitResponse<ReposListReleaseAssetsResponseData>>
listReleases(): Promise<Response<ReposListReleasesResponse>>
listReleases(): Promise<OctokitResponse<ReposListReleasesResponseData>>
update(
id: number,
@@ -28,22 +35,23 @@ export interface Releases {
draft?: boolean,
name?: string,
prerelease?: boolean
): Promise<Response<ReposCreateReleaseResponse>>
): Promise<OctokitResponse<ReposCreateReleaseResponseData>>
uploadArtifact(
assetUrl: string,
contentLength: number,
contentType: string,
file: string | object,
name: string
): Promise<Response<AnyResponse>>
name: string,
releaseId: number,
): Promise<OctokitResponse<ReposUploadReleaseAssetResponseData>>
}
export class GithubReleases implements Releases {
context: Context
git: GitHub
git: InstanceType<typeof GitHub>
constructor(context: Context, git: GitHub) {
constructor(context: Context, git: InstanceType<typeof GitHub>) {
this.context = context
this.git = git
}
@@ -55,7 +63,8 @@ export class GithubReleases implements Releases {
draft?: boolean,
name?: string,
prerelease?: boolean
): Promise<Response<ReposCreateReleaseResponse>> {
): Promise<OctokitResponse<ReposCreateReleaseResponseData>> {
// noinspection TypeScriptValidateJSTypes
return this.git.repos.createRelease({
body: body,
name: name,
@@ -70,7 +79,7 @@ export class GithubReleases implements Releases {
async deleteArtifact(
assetId: number
): Promise<Response<ReposDeleteReleaseAssetResponse>> {
): Promise<OctokitResponse<any>> {
return this.git.repos.deleteReleaseAsset({
asset_id: assetId,
owner: this.context.repo.owner,
@@ -80,22 +89,22 @@ export class GithubReleases implements Releases {
async listArtifactsForRelease(
releaseId: number
): Promise<Response<ReposListAssetsForReleaseResponse>> {
return this.git.repos.listAssetsForRelease({
): Promise<OctokitResponse<ReposListReleaseAssetsResponseData>> {
return this.git.repos.listReleaseAssets({
owner: this.context.repo.owner,
release_id: releaseId,
repo: this.context.repo.repo
})
}
async listReleases(): Promise<Response<ReposListReleasesResponse>> {
async listReleases(): Promise<OctokitResponse<ReposListReleasesResponseData>> {
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<OctokitResponse<ReposGetReleaseByTagResponseData>> {
return this.git.repos.getReleaseByTag({
owner: this.context.repo.owner,
repo: this.context.repo.repo,
@@ -111,7 +120,8 @@ export class GithubReleases implements Releases {
draft?: boolean,
name?: string,
prerelease?: boolean
): Promise<Response<ReposCreateReleaseResponse>> {
): Promise<OctokitResponse<ReposCreateReleaseResponseData>> {
// noinspection TypeScriptValidateJSTypes
return this.git.repos.updateRelease({
release_id: id,
body: body,
@@ -130,16 +140,20 @@ export class GithubReleases implements Releases {
contentLength: number,
contentType: string,
file: string | object,
name: string
): Promise<Response<AnyResponse>> {
name: string,
releaseId: number,
): Promise<OctokitResponse<ReposUploadReleaseAssetResponseData>> {
return this.git.repos.uploadReleaseAsset({
url: assetUrl,
headers: {
"content-length": contentLength,
"content-type": contentType
},
file: file,
name: name
data: file as any,
name: name,
owner: this.context.repo.owner,
release_id: releaseId,
repo: this.context.repo.repo
})
}
}

2409
yarn.lock

File diff suppressed because it is too large Load Diff