Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf: cache when use-installer and version specified #73

Merged
merged 15 commits into from
Mar 2, 2023
Merged
7 changes: 7 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
30 changes: 29 additions & 1 deletion dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand All @@ -140,13 +147,27 @@ function getInput(name, pattern, defaultValue) {
* @returns {Promise<string>} 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)) {
hoffa marked this conversation as resolved.
Show resolved Hide resolved
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";
Expand All @@ -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;
}

Expand Down
30 changes: 29 additions & 1 deletion lib/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand All @@ -115,13 +122,27 @@ function getInput(name, pattern, defaultValue) {
* @returns {Promise<string>} 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";
Expand All @@ -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;
}

Expand Down
78 changes: 78 additions & 0 deletions test/setup.test.js
Original file line number Diff line number Diff line change
@@ -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");

Expand Down Expand Up @@ -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);
}
});