diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index cbd3ddd..e94d53d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -33,6 +33,13 @@ jobs: version: "1.71.0" - run: sam --version | grep -F 1.71.0 + - name: Test official installer (pinned version; should use cache) + uses: ./ + with: + use-installer: true + version: "1.71.0" + - run: sam --version | grep -F 1.71.0 + - name: Test official installer (latest version) uses: ./ with: diff --git a/dist/index.js b/dist/index.js index 1bb769f..d59b1bf 100644 --- a/dist/index.js +++ b/dist/index.js @@ -131,6 +131,13 @@ function getInput(name, pattern, defaultValue) { return value; } +/** + * Returns whether a string is in the format x.y.z. + */ +function isSemver(s) { + return /^\d+\.\d+\.\d+$/.test(s); +} + /** * Installs SAM CLI using the native installers. * @@ -140,13 +147,27 @@ function getInput(name, pattern, defaultValue) { * @returns {Promise} The directory SAM CLI is installed in. */ // TODO: Support more platforms -// TODO: Support caching async function installUsingNativeInstaller(version) { if (os.platform() !== "linux" || os.arch() !== "x64") { core.setFailed("Only Linux x86-64 is supported with use-installer: true"); return ""; } + // Must be full semantic version; downloads version directly from GitHub + if (version && !isSemver(version)) { + core.setFailed("Version must be in the format x.y.z"); + return ""; + } + + // TODO: If not set, check latest version and return from cache if exists + if (version) { + const cachedDir = tc.find("sam", version); + if (cachedDir) { + core.info(`Using cached AWS SAM CLI ${version} from ${cachedDir}`); + return path.join(cachedDir, "dist"); + } + } + const url = version ? `https://github.com/aws/aws-sam-cli/releases/download/v${version}/aws-sam-cli-linux-x86_64.zip` : "https://github.com/aws/aws-sam-cli/releases/latest/download/aws-sam-cli-linux-x86_64.zip"; @@ -155,6 +176,13 @@ async function installUsingNativeInstaller(version) { const extractedDir = await tc.extractZip(toolPath); const binDir = path.join(extractedDir, "dist"); + // TODO: If not set, cache with latest version + if (version) { + const cachedDir = await tc.cacheDir(extractedDir, "sam", version); + core.info(`Cached AWS SAM CLI ${version} to ${cachedDir}`); + return path.join(cachedDir, "dist"); + } + return binDir; } diff --git a/lib/setup.js b/lib/setup.js index 018ecd9..f099fdf 100644 --- a/lib/setup.js +++ b/lib/setup.js @@ -106,6 +106,13 @@ function getInput(name, pattern, defaultValue) { return value; } +/** + * Returns whether a string is in the format x.y.z. + */ +function isSemver(s) { + return /^\d+\.\d+\.\d+$/.test(s); +} + /** * Installs SAM CLI using the native installers. * @@ -115,13 +122,27 @@ function getInput(name, pattern, defaultValue) { * @returns {Promise} The directory SAM CLI is installed in. */ // TODO: Support more platforms -// TODO: Support caching async function installUsingNativeInstaller(version) { if (os.platform() !== "linux" || os.arch() !== "x64") { core.setFailed("Only Linux x86-64 is supported with use-installer: true"); return ""; } + // Must be full semantic version; downloads version directly from GitHub + if (version && !isSemver(version)) { + core.setFailed("Version must be in the format x.y.z"); + return ""; + } + + // TODO: If not set, check latest version and return from cache if exists + if (version) { + const cachedDir = tc.find("sam", version); + if (cachedDir) { + core.info(`Using cached AWS SAM CLI ${version} from ${cachedDir}`); + return path.join(cachedDir, "dist"); + } + } + const url = version ? `https://github.com/aws/aws-sam-cli/releases/download/v${version}/aws-sam-cli-linux-x86_64.zip` : "https://github.com/aws/aws-sam-cli/releases/latest/download/aws-sam-cli-linux-x86_64.zip"; @@ -130,6 +151,13 @@ async function installUsingNativeInstaller(version) { const extractedDir = await tc.extractZip(toolPath); const binDir = path.join(extractedDir, "dist"); + // TODO: If not set, cache with latest version + if (version) { + const cachedDir = await tc.cacheDir(extractedDir, "sam", version); + core.info(`Cached AWS SAM CLI ${version} to ${cachedDir}`); + return path.join(cachedDir, "dist"); + } + return binDir; } diff --git a/test/setup.test.js b/test/setup.test.js index 0073ee8..7ecb212 100644 --- a/test/setup.test.js +++ b/test/setup.test.js @@ -1,12 +1,14 @@ jest.mock("@actions/core"); jest.mock("@actions/exec"); jest.mock("@actions/io"); +jest.mock("@actions/tool-cache"); const os = require("os"); const core = require("@actions/core"); const exec = require("@actions/exec"); const io = require("@actions/io"); +const tc = require("@actions/tool-cache"); const setup = require("../lib/setup"); @@ -82,3 +84,79 @@ test.each([ .mockReturnValueOnce(input.python); await expect(setup).rejects.toThrow(Error); }); + +test("when use-installer enabled and version specified and cached version exists, uses cached version", async () => { + jest.spyOn(os, "platform").mockReturnValue("linux"); + jest.spyOn(os, "arch").mockReturnValue("x64"); + + core.getBooleanInput = jest.fn().mockReturnValue(true); + core.getInput = jest.fn().mockReturnValueOnce("1.23.456"); + + tc.find = jest.fn().mockReturnValueOnce("/path/to/cached/sam"); + + await setup(); + + expect(tc.find).toHaveBeenCalledTimes(1); + expect(tc.cacheDir).toHaveBeenCalledTimes(0); + + // Must be cached path + expect(core.addPath).toHaveBeenCalledWith("/path/to/cached/sam/dist"); +}); + +test("when use-installer enabled and version specified and cached version does not exist, downloads and caches version", async () => { + jest.spyOn(os, "platform").mockReturnValue("linux"); + jest.spyOn(os, "arch").mockReturnValue("x64"); + + core.getBooleanInput = jest.fn().mockReturnValue(true); + core.getInput = jest.fn().mockReturnValueOnce("1.23.456"); + + tc.find = jest.fn().mockReturnValueOnce(""); + tc.extractZip = jest.fn().mockReturnValueOnce("/path/to/extracted/sam"); + tc.cacheDir = jest.fn().mockReturnValueOnce("/path/to/cached/sam"); + + await setup(); + + expect(tc.find).toHaveBeenCalledTimes(1); + expect(tc.cacheDir).toHaveBeenCalledTimes(1); + + // Must return cached path + expect(core.addPath).toHaveBeenCalledWith("/path/to/cached/sam/dist"); +}); + +test("when use-installer enabled and version not specified, downloads latest version (Linux x64)", async () => { + jest.spyOn(os, "platform").mockReturnValue("linux"); + jest.spyOn(os, "arch").mockReturnValue("x64"); + + core.getBooleanInput = jest.fn().mockReturnValue(true); + core.getInput = jest.fn().mockReturnValueOnce(""); + + tc.find = jest.fn().mockReturnValueOnce(""); + tc.extractZip = jest.fn().mockReturnValueOnce("/path/to/extracted/sam"); + tc.cacheDir = jest.fn().mockReturnValueOnce("/path/to/cached/sam"); + tc.downloadTool = jest.fn().mockReturnValueOnce("/path/to/downloaded/sam"); + + await setup(); + + expect(tc.downloadTool).toHaveBeenCalledWith( + "https://github.com/aws/aws-sam-cli/releases/latest/download/aws-sam-cli-linux-x86_64.zip" + ); + + // Currently no caching on latest + expect(tc.find).toHaveBeenCalledTimes(0); + expect(tc.cacheDir).toHaveBeenCalledTimes(0); + expect(core.addPath).toHaveBeenCalledWith("/path/to/extracted/sam/dist"); +}); + +test("when use-installer enabled but version is not in format x.y.z, not downloaded or checked in cache", async () => { + jest.spyOn(os, "platform").mockReturnValue("linux"); + jest.spyOn(os, "arch").mockReturnValue("x64"); + + core.getBooleanInput = jest.fn().mockReturnValue(true); + + for (const version of ["1.2", "1.*", "3"]) { + core.getInput = jest.fn().mockReturnValueOnce(version); + await setup(); + expect(tc.downloadTool).toHaveBeenCalledTimes(0); + expect(tc.find).toHaveBeenCalledTimes(0); + } +});