diff --git a/dist/main/index.js b/dist/main/index.js index 8a85724..972fd8b 100644 --- a/dist/main/index.js +++ b/dist/main/index.js @@ -16357,6 +16357,14 @@ function getPlatform() { return osName && arch && `${osName}_${arch}`; } +function isLatestRequested(versionSpec) { + return versionSpec === undefined || versionSpec === "latest"; +} + +function isStableVersion(version) { + return !version.prerelease && !version.draft; +} + async function selectCompatibleVersion(versionSpec) { // NOTE: authStrategy is set conditionnaly. Docs specifies that GITHUB_TOKEN needs to be set explicitely. // To avoid breaking every pipeline that has no GITHUB_TOKEN set, this strategy is not passed until @@ -16376,8 +16384,13 @@ async function selectCompatibleVersion(versionSpec) { const versions = response.data; for (let i = 0; i < versions.length; i++) { - if (versionSpec === undefined || versionSpec === "latest" - || semver.satisfies(versions[i].tag_name, versionSpec)) { + if (versions[i].draft || versions[i].assets?.length === 0) { + continue; + } + if (isLatestRequested(versionSpec) && isStableVersion(versions[i])) { + return versions[i]; + } + if (semver.satisfies(versions[i].tag_name, versionSpec)) { return versions[i]; } } diff --git a/src/install.js b/src/install.js index 9e1ebb6..1aeff48 100644 --- a/src/install.js +++ b/src/install.js @@ -22,6 +22,14 @@ function getPlatform() { return osName && arch && `${osName}_${arch}`; } +function isLatestRequested(versionSpec) { + return versionSpec === undefined || versionSpec === "latest"; +} + +function isStableVersion(version) { + return !version.prerelease && !version.draft; +} + async function selectCompatibleVersion(versionSpec) { // NOTE: authStrategy is set conditionnaly. Docs specifies that GITHUB_TOKEN needs to be set explicitely. // To avoid breaking every pipeline that has no GITHUB_TOKEN set, this strategy is not passed until @@ -41,8 +49,13 @@ async function selectCompatibleVersion(versionSpec) { const versions = response.data; for (let i = 0; i < versions.length; i++) { - if (versionSpec === undefined || versionSpec === "latest" - || semver.satisfies(versions[i].tag_name, versionSpec)) { + if (versions[i].draft || versions[i].assets?.length === 0) { + continue; + } + if (isLatestRequested(versionSpec) && isStableVersion(versions[i])) { + return versions[i]; + } + if (semver.satisfies(versions[i].tag_name, versionSpec)) { return versions[i]; } } diff --git a/tests/fixtures/github-releases-mock.json b/tests/fixtures/github-releases-mock.json index 235e9a8..12e0f4f 100644 --- a/tests/fixtures/github-releases-mock.json +++ b/tests/fixtures/github-releases-mock.json @@ -1,7 +1,80 @@ [ + + { + "tag_name": "v0.27.0", + "name": "v0.27.0", + "prerelease": false, + "draft": true, + "assets": [ + { + "name": "checksums.txt", + "browser_download_url": "https://github.com/saucelabs/saucectl/releases/download/v0.27.0/checksums.txt" + }, + { + "name": "saucectl_0.27.0_linux_32-bit.tar.gz", + "browser_download_url": "https://github.com/saucelabs/saucectl/releases/download/v0.27.0/saucectl_0.27.0_linux_32-bit.tar.gz" + }, + { + "name": "saucectl_0.27.0_linux_64-bit.tar.gz", + "browser_download_url": "https://github.com/saucelabs/saucectl/releases/download/v0.27.0/saucectl_0.27.0_linux_64-bit.tar.gz" + }, + { + "name": "saucectl_0.27.0_linux_arm64.tar.gz", + "browser_download_url": "https://github.com/saucelabs/saucectl/releases/download/v0.27.0/saucectl_0.27.0_linux_arm64.tar.gz" + }, + { + "name": "saucectl_0.27.0_mac_64-bit.tar.gz", + "browser_download_url": "https://github.com/saucelabs/saucectl/releases/download/v0.27.0/saucectl_0.27.0_mac_64-bit.tar.gz" + }, + { + "name": "saucectl_0.27.0_win_32-bit.zip", + "browser_download_url": "https://github.com/saucelabs/saucectl/releases/download/v0.27.0/saucectl_0.27.0_win_32-bit.zip" + }, + { + "name": "saucectl_0.27.0_win_64-bit.zip", + "browser_download_url": "https://github.com/saucelabs/saucectl/releases/download/v0.27.0/saucectl_0.27.0_win_64-bit.zip" + } + ] + }, + { + "tag_name": "v0.26.0-alpha1", + "name": "v0.26.0-alpha1", + "prerelease": true, + "assets": [ + { + "name": "checksums.txt", + "browser_download_url": "https://github.com/saucelabs/saucectl/releases/download/v0.26.0-alpha1/checksums.txt" + }, + { + "name": "saucectl_0.26.0-alpha1_linux_32-bit.tar.gz", + "browser_download_url": "https://github.com/saucelabs/saucectl/releases/download/v0.26.0-alpha1/saucectl_0.26.0-alpha1_linux_32-bit.tar.gz" + }, + { + "name": "saucectl_0.26.0-alpha1_linux_64-bit.tar.gz", + "browser_download_url": "https://github.com/saucelabs/saucectl/releases/download/v0.26.0-alpha1/saucectl_0.26.0-alpha1_linux_64-bit.tar.gz" + }, + { + "name": "saucectl_0.26.0-alpha1_linux_arm64.tar.gz", + "browser_download_url": "https://github.com/saucelabs/saucectl/releases/download/v0.26.0-alpha1/saucectl_0.26.0-alpha1_linux_arm64.tar.gz" + }, + { + "name": "saucectl_0.26.0-alpha1_mac_64-bit.tar.gz", + "browser_download_url": "https://github.com/saucelabs/saucectl/releases/download/v0.26.0-alpha1/saucectl_0.26.0-alpha1_mac_64-bit.tar.gz" + }, + { + "name": "saucectl_0.26.0-alpha1_win_32-bit.zip", + "browser_download_url": "https://github.com/saucelabs/saucectl/releases/download/v0.26.0-alpha1/saucectl_0.26.0-alpha1_win_32-bit.zip" + }, + { + "name": "saucectl_0.26.0-alpha1_win_64-bit.zip", + "browser_download_url": "https://github.com/saucelabs/saucectl/releases/download/v0.26.0-alpha1/saucectl_0.26.0-alpha1_win_64-bit.zip" + } + ] + }, { "tag_name": "v0.25.1", "name": "v0.25.1", + "prerelease": false, "assets": [ { "name": "checksums.txt", @@ -36,6 +109,7 @@ { "tag_name": "v0.25.0", "name": "v0.25.0", + "prerelease": false, "assets": [ { "name": "checksums.txt", @@ -70,6 +144,7 @@ { "tag_name": "v0.24.1", "name": "v0.24.1", + "prerelease": false, "assets": [ { "name": "checksums.txt", @@ -104,6 +179,7 @@ { "tag_name": "v0.24.0", "name": "v0.24.0", + "prerelease": false, "assets": [ { "name": "checksums.txt", diff --git a/tests/install.spec.js b/tests/install.spec.js index bbf3f8d..611e0ef 100644 --- a/tests/install.spec.js +++ b/tests/install.spec.js @@ -9,28 +9,42 @@ const core = require("@actions/core"); jest.mock("@actions/tool-cache"); const tc = require("@actions/tool-cache"); - -const nock = require('nock'); +jest.mock('@octokit/rest') +const { Octokit } = require("@octokit/rest"); const install = require("../src/install.js"); +const requestMock = jest.fn(); +requestMock.mockImplementation((target) => new Promise((resolve, reject) => { + if (target === "GET /repos/:org/:repo/releases") { + resolve({ + status: 200, + headers: { location: 'mock-url' }, + data: JSON.parse(fs.readFileSync("./tests/fixtures/github-releases-mock.json")), + }); + } + reject('Unexpected Endpoint'); +})); +Octokit.mockImplementation(() => ({ request: requestMock })) + beforeAll(() => { process.env.GITHUB_ACTION = "FAKE_IN_ACTION"; }); +beforeEach(() => { + jest.clearAllMocks(); +}); + it("Version matching", async () => { // rewire wrapping const selectCompatibleVersion = install.selectCompatibleVersion; - const testCases = [["latest", "v0.25.1"], ["0.25.1", "v0.25.1"], ["0.25.0", "v0.25.0"], ["^0.25.0", "v0.25.1"], [">=0.24.0", "v0.25.1"], ["0.24.x", "v0.24.1"]]; + const testCases = [["latest", "v0.25.1"], ["0.25.1", "v0.25.1"], ["0.25.0", "v0.25.0"], ["^0.25.0", "v0.25.1"], [">=0.24.0", "v0.25.1"], ["0.24.x", "v0.24.1"], ["v0.26.0-alpha1", "v0.26.0-alpha1"]]; for (let i = 0; i < testCases.length; i++) { - const scope = nock('https://api.github.com') - .get('/repos/saucelabs/saucectl/releases') - .reply(200, JSON.parse(fs.readFileSync("./tests/fixtures/github-releases-mock.json"))); let [input, expected] = testCases[i]; let version = await selectCompatibleVersion(input); expect(version.tag_name).toBe(expected); - expect(scope.isDone()).toBe(true); + expect(requestMock).toHaveBeenCalled() } }); @@ -52,13 +66,10 @@ describe("Installation", () => { it("Invalid version", async () => { os.platform.mockReturnValue('linux'); os.arch.mockReturnValue('x64'); - const scope = nock('https://api.github.com') - .get('/repos/saucelabs/saucectl/releases') - .reply(200, JSON.parse(fs.readFileSync("./tests/fixtures/github-releases-mock.json"))); const ret = await install.saucectlInstall({ versionSpec: '0.29.5' }); expect(ret).toBe(false); - expect(scope.isDone()).toBe(true); + expect(requestMock).toHaveBeenCalled(); }); it("Install version - linux", async () => { @@ -68,14 +79,10 @@ describe("Installation", () => { const extractFn = tc.extractTar.mockReturnValue('/tmp/install-dir-extracted'); const addPathFn = core.addPath.mockReturnValue('/bin/saucectl'); - const scope = nock('https://api.github.com') - .get('/repos/saucelabs/saucectl/releases') - .reply(200, JSON.parse(fs.readFileSync("./tests/fixtures/github-releases-mock.json"))); - const ret = await install.saucectlInstall({ versionSpec: '0.25.1' }); expect(ret).toBe(true); - expect(scope.isDone()).toBe(true); + expect(requestMock).toHaveBeenCalled(); expect(downloadFn).toHaveBeenCalled(); expect(extractFn).toHaveBeenCalled(); expect(addPathFn).toHaveBeenCalled(); @@ -88,14 +95,10 @@ describe("Installation", () => { const extractFn = tc.extractZip.mockReturnValue('/tmp/install-dir-extracted'); const addPathFn = core.addPath.mockReturnValue('/bin/saucectl'); - const scope = nock('https://api.github.com') - .get('/repos/saucelabs/saucectl/releases') - .reply(200, JSON.parse(fs.readFileSync("./tests/fixtures/github-releases-mock.json"))); - const ret = await install.saucectlInstall({ versionSpec: '0.25.1' }); expect(ret).toBe(true); - expect(scope.isDone()).toBe(true); + expect(requestMock).toHaveBeenCalled(); expect(downloadFn).toHaveBeenCalled(); expect(extractFn).toHaveBeenCalled(); expect(addPathFn).toHaveBeenCalled();