diff --git a/__tests__/main.test.ts b/__tests__/main.test.ts index 950caf97..4017fe00 100644 --- a/__tests__/main.test.ts +++ b/__tests__/main.test.ts @@ -27,6 +27,10 @@ beforeEach(() => { .get("/repos/robinraju/probable-potato/releases/latest") .reply(200, readFromFile("1-release-latest.json")) + nock("https://api.github.com") + .get("/repos/robinraju/probable-potato/releases/68092191") + .reply(200, readFromFile("1-release-latest.json")) + nock("https://api.github.com", { reqheaders: {accept: "application/octet-stream"} }) @@ -89,6 +93,7 @@ test("Download all files from public repo", async () => { sourceRepoPath: "robinraju/probable-potato", isLatest: true, tag: "", + id: "", fileName: "*", tarBall: false, zipBall: false, @@ -103,6 +108,7 @@ test("Download single file from public repo", async () => { sourceRepoPath: "robinraju/probable-potato", isLatest: true, tag: "", + id: "", fileName: "test-1.txt", tarBall: false, zipBall: false, @@ -117,6 +123,7 @@ test("Fail loudly if given filename is not found in a release", async () => { sourceRepoPath: "robinraju/probable-potato", isLatest: true, tag: "", + id: "", fileName: "missing-file.txt", tarBall: false, zipBall: false, @@ -128,11 +135,29 @@ test("Fail loudly if given filename is not found in a release", async () => { ) }, 10000) +test("Fail loudly if release is not identified", async () => { + const downloadSettings: IReleaseDownloadSettings = { + sourceRepoPath: "robinraju/probable-potato", + isLatest: false, + tag: "", + id: "", + fileName: "missing-file.txt", + tarBall: false, + zipBall: false, + outFilePath: outputFilePath + } + const result = downloader.download(downloadSettings) + await expect(result).rejects.toThrow( + "Config error: Please input a valid tag or release ID, or specify `latest`" + ) +}, 10000) + test("Download files with wildcard from public repo", async () => { const downloadSettings: IReleaseDownloadSettings = { sourceRepoPath: "robinraju/probable-potato", isLatest: true, tag: "", + id: "", fileName: "test-*.txt", tarBall: false, zipBall: false, @@ -147,6 +172,7 @@ test("Download single file with wildcard from public repo", async () => { sourceRepoPath: "robinraju/probable-potato", isLatest: true, tag: "", + id: "", fileName: "3-*.txt", tarBall: false, zipBall: false, @@ -161,6 +187,7 @@ test("Download multiple pdf files with wildcard filename", async () => { sourceRepoPath: "robinraju/probable-potato", isLatest: true, tag: "", + id: "", fileName: "*.pdf", tarBall: false, zipBall: false, @@ -175,6 +202,7 @@ test("Download a csv file with wildcard filename", async () => { sourceRepoPath: "robinraju/probable-potato", isLatest: true, tag: "", + id: "", fileName: "*.csv", tarBall: false, zipBall: false, @@ -191,6 +219,7 @@ test("Download file from Github Enterprise server", async () => { sourceRepoPath: "my-enterprise/test-repo", isLatest: true, tag: "", + id: "", fileName: "test-1.txt", tarBall: false, zipBall: false, @@ -199,3 +228,18 @@ test("Download file from Github Enterprise server", async () => { const result = await downloader.download(downloadSettings) expect(result.length).toBe(1) }, 10000) + +test("Download file from release identified by ID", async () => { + const downloadSettings: IReleaseDownloadSettings = { + sourceRepoPath: "robinraju/probable-potato", + isLatest: false, + tag: "", + id: "68092191", + fileName: "test-2.txt", + tarBall: false, + zipBall: false, + outFilePath: outputFilePath + } + const result = await downloader.download(downloadSettings) + expect(result.length).toBe(1) +}, 10000) diff --git a/action.yml b/action.yml index d1afc3fa..306632f6 100644 --- a/action.yml +++ b/action.yml @@ -11,7 +11,7 @@ inputs: default: "false" required: false tag: - description: "The github tag to download the release from" + description: "The github tag to download the release from" default: "" required: false fileName: @@ -25,7 +25,7 @@ inputs: zipBall: description: "Download zipball from assets" default: "false" - required: false + required: false out-file-path: description: "Relative path under $GITHUB_WORKSPACE to place the downloaded files" default: "." @@ -38,6 +38,10 @@ inputs: description: "The URL of the Github API, only use this input if you are using Github Enterprise" default: "https://api.github.com" required: false + releaseId: + description: "The release id to download the file from" + default: "" + required: false outputs: tag_name: description: "The github tag used to download the release" diff --git a/dist/index.js b/dist/index.js index 6bfcdf9d..5e49859a 100644 --- a/dist/index.js +++ b/dist/index.js @@ -48,11 +48,13 @@ function getInputs() { downloadSettings.sourceRepoPath = repositoryPath; const latestFlag = core.getInput("latest") === "true"; const ghTag = core.getInput("tag"); - if (latestFlag && ghTag.length > 0) { - throw new Error(`Invalid inputs. latest=${latestFlag} and tag=${ghTag} can't coexist`); + const releaseId = core.getInput("id"); + if (latestFlag && ghTag.length > 0 && releaseId.length > 0 || ghTag.length > 0 && releaseId.length > 0) { + throw new Error(`Invalid inputs. latest=${latestFlag}, tag=${ghTag} and releaseId=${releaseId} can't coexist`); } downloadSettings.isLatest = latestFlag; downloadSettings.tag = ghTag; + downloadSettings.id = releaseId; downloadSettings.fileName = core.getInput("fileName"); downloadSettings.tarBall = core.getInput("tarBall") === "true"; downloadSettings.zipBall = core.getInput("zipBall") === "true"; @@ -188,9 +190,15 @@ class ReleaseDownloader { if (downloadSettings.isLatest) { ghRelease = yield this.getlatestRelease(downloadSettings.sourceRepoPath); } - else { + else if (downloadSettings.tag !== "") { ghRelease = yield this.getReleaseByTag(downloadSettings.sourceRepoPath, downloadSettings.tag); } + else if (downloadSettings.id !== "") { + ghRelease = yield this.getReleaseById(downloadSettings.sourceRepoPath, downloadSettings.id); + } + else { + throw new Error("Config error: Please input a valid tag or release ID, or specify `latest`"); + } // Set the output variables for use by other actions core.setOutput("tag_name", ghRelease.tag_name); const resolvedAssets = this.resolveAssets(ghRelease, downloadSettings); @@ -239,6 +247,29 @@ class ReleaseDownloader { return release; }); } + /** + * Gets release data of the specified release ID + * @param repoPath The source repository + * @param id The github release ID to fetch. + */ + getReleaseById(repoPath, id) { + return __awaiter(this, void 0, void 0, function* () { + core.info(`Fetching release id:${id} from repo ${repoPath}`); + if (id === "") { + throw new Error("Config error: Please input a valid release ID"); + } + const headers = { Accept: "application/vnd.github.v3+json" }; + const response = yield this.httpClient.get(`${this.apiRoot}/repos/${repoPath}/releases/${id}`, headers); + if (response.message.statusCode !== 200) { + const err = new Error(`[getReleaseById] Unexpected response: ${response.message.statusCode}`); + throw err; + } + const responseBody = yield response.readBody(); + const release = JSON.parse(responseBody.toString()); + core.info(`Found release tag: ${release.tag_name}`); + return release; + }); + } resolveAssets(ghRelease, downloadSettings) { const downloads = []; if (ghRelease && ghRelease.assets.length > 0) { diff --git a/src/download-settings.ts b/src/download-settings.ts index b9f0f3ff..1bc4bd1a 100644 --- a/src/download-settings.ts +++ b/src/download-settings.ts @@ -14,6 +14,11 @@ export interface IReleaseDownloadSettings { */ tag: string + /** + * The release id + */ + id: string + /** * Name of the file to download */ diff --git a/src/gh-api.ts b/src/gh-api.ts index 86e5a8df..394fb825 100644 --- a/src/gh-api.ts +++ b/src/gh-api.ts @@ -5,6 +5,7 @@ interface GhAsset { export interface GithubRelease { name: string + id: number tag_name: String assets: GhAsset[] tarball_url: string diff --git a/src/input-helper.ts b/src/input-helper.ts index bd60cbc9..cb83073c 100644 --- a/src/input-helper.ts +++ b/src/input-helper.ts @@ -23,10 +23,14 @@ export function getInputs(): IReleaseDownloadSettings { const latestFlag = core.getInput("latest") === "true" const ghTag = core.getInput("tag") + const releaseId = core.getInput("id") - if (latestFlag && ghTag.length > 0) { + if ( + (latestFlag && ghTag.length > 0 && releaseId.length > 0) || + (ghTag.length > 0 && releaseId.length > 0) + ) { throw new Error( - `Invalid inputs. latest=${latestFlag} and tag=${ghTag} can't coexist` + `Invalid inputs. latest=${latestFlag}, tag=${ghTag} and releaseId=${releaseId} can't coexist` ) } @@ -34,6 +38,8 @@ export function getInputs(): IReleaseDownloadSettings { downloadSettings.tag = ghTag + downloadSettings.id = releaseId + downloadSettings.fileName = core.getInput("fileName") downloadSettings.tarBall = core.getInput("tarBall") === "true" diff --git a/src/release-downloader.ts b/src/release-downloader.ts index bb1e7c1c..abd4fc35 100644 --- a/src/release-downloader.ts +++ b/src/release-downloader.ts @@ -26,11 +26,20 @@ export class ReleaseDownloader { if (downloadSettings.isLatest) { ghRelease = await this.getlatestRelease(downloadSettings.sourceRepoPath) - } else { + } else if (downloadSettings.tag !== "") { ghRelease = await this.getReleaseByTag( downloadSettings.sourceRepoPath, downloadSettings.tag ) + } else if (downloadSettings.id !== "") { + ghRelease = await this.getReleaseById( + downloadSettings.sourceRepoPath, + downloadSettings.id + ) + } else { + throw new Error( + "Config error: Please input a valid tag or release ID, or specify `latest`" + ) } // Set the output variables for use by other actions @@ -111,6 +120,42 @@ export class ReleaseDownloader { return release } + /** + * Gets release data of the specified release ID + * @param repoPath The source repository + * @param id The github release ID to fetch. + */ + private async getReleaseById( + repoPath: string, + id: string + ): Promise { + core.info(`Fetching release id:${id} from repo ${repoPath}`) + + if (id === "") { + throw new Error("Config error: Please input a valid release ID") + } + + const headers: IHeaders = {Accept: "application/vnd.github.v3+json"} + + const response = await this.httpClient.get( + `${this.apiRoot}/repos/${repoPath}/releases/${id}`, + headers + ) + + if (response.message.statusCode !== 200) { + const err: Error = new Error( + `[getReleaseById] Unexpected response: ${response.message.statusCode}` + ) + throw err + } + + const responseBody = await response.readBody() + const release: GithubRelease = JSON.parse(responseBody.toString()) + core.info(`Found release tag: ${release.tag_name}`) + + return release + } + private resolveAssets( ghRelease: GithubRelease, downloadSettings: IReleaseDownloadSettings