save state

This commit is contained in:
Caleb Gosiak 2021-09-30 15:21:04 -05:00
parent ab5323b726
commit 0ad6d2d6f2
13 changed files with 595 additions and 1301 deletions

95
src/cache.service.ts Normal file
View file

@ -0,0 +1,95 @@
import * as utils from "@actions/cache/lib/internal/cacheUtils";
import { createTar, listTar } from "@actions/cache/lib/internal/tar";
import * as core from "@actions/core";
import { S3 } from "aws-sdk";
import filesize from "filesize";
import fs from "fs";
import * as path from "path";
export class CacheService {
private _client: S3;
private _bucket: string;
constructor(
accessKeyId: string,
secretAccessKey: string,
region: string,
bucket: string
) {
this._client = new S3({
region: region,
credentials: {
accessKeyId: accessKeyId,
secretAccessKey: secretAccessKey
}
});
this._bucket = bucket;
}
async restoreCache(
paths: string[],
primaryKey: string,
restoreKeys: string[]
): Promise<string | undefined> {
return "";
}
async saveCache(paths: string[], key: string): Promise<string> {
const compressionMethod = await utils.getCompressionMethod();
const cachePaths = await utils.resolvePaths(paths);
core.debug("Cache Paths:");
core.debug(`${JSON.stringify(cachePaths)}`);
const archiveFolder = await utils.createTempDirectory();
const archivePath = path.join(
archiveFolder,
utils.getCacheFileName(compressionMethod)
);
core.debug(`Archive Path: ${archivePath}`);
try {
await createTar(archiveFolder, cachePaths, compressionMethod);
if (core.isDebug()) {
await listTar(archivePath, compressionMethod);
}
core.info(
`Archive Size: ${filesize(fs.statSync(archivePath).size)}`
);
core.debug(`Saving Cache (ID: ${key})`);
await this.uploadToS3(key, archivePath);
} finally {
// Try to delete the archive to save space
try {
await utils.unlinkFile(archivePath);
} catch (error) {
core.debug(`Failed to delete archive: ${error}`);
}
}
return key;
}
private async uploadToS3(key: string, archivePath: string): Promise<void> {
const client = new S3();
// Read in the file, convert it to base64, store to S3
fs.readFile(archivePath, (err, data) => {
if (err) {
throw err;
}
const base64data = data.toString("base64");
client
.putObject({
Bucket: this._bucket,
Key: key,
Body: base64data
})
.send();
});
}
}

View file

@ -2,7 +2,11 @@ export enum Inputs {
Key = "key",
Path = "path",
RestoreKeys = "restore-keys",
UploadChunkSize = "upload-chunk-size"
UploadChunkSize = "upload-chunk-size",
Region = "region",
AccessKeyId = "access-key-id",
SecretAccessKey = "secret-access-key",
Bucket = "bucket"
}
export enum Outputs {

View file

@ -1,29 +1,11 @@
import * as cache from "@actions/cache";
import * as core from "@actions/core";
import { Events, Inputs, State } from "./constants";
import { CacheService } from "./cache.service";
import { Inputs, State } from "./constants";
import * as utils from "./utils/actionUtils";
async function run(): Promise<void> {
try {
if (utils.isGhes()) {
utils.logWarning(
"Cache action is not supported on GHES. See https://github.com/actions/cache/issues/505 for more details"
);
utils.setCacheHitOutput(false);
return;
}
// Validate inputs, this can cause task failure
if (!utils.isValidEvent()) {
utils.logWarning(
`Event Validation Error: The event type ${
process.env[Events.Key]
} is not supported because it's not tied to a branch or tag ref.`
);
return;
}
const primaryKey = core.getInput(Inputs.Key, { required: true });
core.saveState(State.CachePrimaryKey, primaryKey);
@ -33,6 +15,13 @@ async function run(): Promise<void> {
});
try {
const cache: CacheService = new CacheService(
core.getInput(Inputs.AccessKeyId),
core.getInput(Inputs.SecretAccessKey),
core.getInput(Inputs.Region),
core.getInput(Inputs.Bucket)
);
const cacheKey = await cache.restoreCache(
cachePaths,
primaryKey,
@ -56,12 +45,8 @@ async function run(): Promise<void> {
core.info(`Cache restored from key: ${cacheKey}`);
} catch (error) {
if (error.name === cache.ValidationError.name) {
throw error;
} else {
utils.logWarning(error.message);
utils.setCacheHitOutput(false);
}
utils.logWarning(error.message);
utils.setCacheHitOutput(false);
}
} catch (error) {
core.setFailed(error.message);

View file

@ -1,7 +1,8 @@
import * as cache from "@actions/cache";
import {} from "@actions/cache";
import * as core from "@actions/core";
import { Events, Inputs, State } from "./constants";
import { CacheService } from "./cache.service";
import { Inputs, State } from "./constants";
import * as utils from "./utils/actionUtils";
// Catch and log any unhandled exceptions. These exceptions can leak out of the uploadChunk method in
@ -11,22 +12,6 @@ process.on("uncaughtException", e => utils.logWarning(e.message));
async function run(): Promise<void> {
try {
if (utils.isGhes()) {
utils.logWarning(
"Cache action is not supported on GHES. See https://github.com/actions/cache/issues/505 for more details"
);
return;
}
if (!utils.isValidEvent()) {
utils.logWarning(
`Event Validation Error: The event type ${
process.env[Events.Key]
} is not supported because it's not tied to a branch or tag ref.`
);
return;
}
const state = utils.getCacheState();
// Inputs are re-evaluted before the post action, so we want the original key used for restore
@ -48,18 +33,16 @@ async function run(): Promise<void> {
});
try {
await cache.saveCache(cachePaths, primaryKey, {
uploadChunkSize: utils.getInputAsInt(Inputs.UploadChunkSize)
});
const cache: CacheService = new CacheService(
core.getInput(Inputs.AccessKeyId),
core.getInput(Inputs.SecretAccessKey),
core.getInput(Inputs.Region),
core.getInput(Inputs.Bucket)
);
await cache.saveCache(cachePaths, primaryKey);
core.info(`Cache saved with key: ${primaryKey}`);
} catch (error) {
if (error.name === cache.ValidationError.name) {
throw error;
} else if (error.name === cache.ReserveCacheError.name) {
core.info(error.message);
} else {
utils.logWarning(error.message);
}
utils.logWarning(error.message);
}
} catch (error) {
utils.logWarning(error.message);

View file

@ -1,30 +0,0 @@
import { Inputs } from "../constants";
// See: https://github.com/actions/toolkit/blob/master/packages/core/src/core.ts#L67
function getInputName(name: string): string {
return `INPUT_${name.replace(/ /g, "_").toUpperCase()}`;
}
export function setInput(name: string, value: string): void {
process.env[getInputName(name)] = value;
}
interface CacheInput {
path: string;
key: string;
restoreKeys?: string[];
}
export function setInputs(input: CacheInput): void {
setInput(Inputs.Path, input.path);
setInput(Inputs.Key, input.key);
input.restoreKeys &&
setInput(Inputs.RestoreKeys, input.restoreKeys.join("\n"));
}
export function clearInputs(): void {
delete process.env[getInputName(Inputs.Path)];
delete process.env[getInputName(Inputs.Key)];
delete process.env[getInputName(Inputs.RestoreKeys)];
delete process.env[getInputName(Inputs.UploadChunkSize)];
}