Skip to content

Commit

Permalink
perf: cache when use-installer and version specified (#73)
Browse files Browse the repository at this point in the history
  • Loading branch information
hoffa authored Mar 2, 2023
1 parent 8e86593 commit e388793
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 2 deletions.
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)) {
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);
}
});

0 comments on commit e388793

Please sign in to comment.