Skip to content

Commit

Permalink
make evict-by-age parameter more general
Browse files Browse the repository at this point in the history
  • Loading branch information
planetmarshall committed Dec 28, 2024
1 parent 51af452 commit 6348297
Show file tree
Hide file tree
Showing 9 changed files with 127 additions and 19 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
evict: [true, false]
evict: ['job', '30s', '']
steps:
- uses: actions/checkout@v4
- name: Run ccache-action
Expand Down
20 changes: 20 additions & 0 deletions __tests__/common.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,26 @@ import * as common from '../src/common';
import * as core from "@actions/core";

describe('ccache common', () => {
test('parse evict age parameter in seconds', () => {
const age = '42s';
const [time, unit] = common.parseEvictAgeParameter(age);
expect(time).toEqual(42);
expect(unit).toEqual(common.AgeUnit.Seconds);
});

test('parse evict age parameter in days', () => {
const age = '28d';
const [time, unit] = common.parseEvictAgeParameter(age);
expect(time).toEqual(28);
expect(unit).toEqual(common.AgeUnit.Days);
});

test('parse evict age parameter - job', () => {
const age = 'job';
const [, unit] = common.parseEvictAgeParameter(age);
expect(unit).toEqual(common.AgeUnit.Job);
});

test('get duration of job in seconds', () => {
const stateMock = jest.spyOn(core, "getState");
const expectedAgeInSeconds = 1234;
Expand Down
13 changes: 11 additions & 2 deletions __tests__/save.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
import {AgeUnit} from "../src/common";
import * as save from '../src/save';
import * as exec from "@actions/exec";

jest.mock("@actions/exec");

describe('ccache save', () => {
test('evict old files from the cache', async () => {
test('evict old files from the cache by age in seconds', async () => {
const proc = jest.spyOn(exec, "exec");

const ageInSeconds = 42;
await save.evictOldFiles(ageInSeconds);
await save.evictOldFiles(ageInSeconds, AgeUnit.Seconds);
expect(proc).toHaveBeenCalledWith(`ccache --evict-older-than ${ageInSeconds}s`);
});

test('evict old files from the cache by age in days', async () => {
const proc = jest.spyOn(exec, "exec");

const ageInDays = 3;
await save.evictOldFiles(ageInDays, AgeUnit.Days);
expect(proc).toHaveBeenCalledWith(`ccache --evict-older-than ${ageInDays}d`);
});
});
7 changes: 4 additions & 3 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,10 @@ inputs:
empty string to disable this feature. Requires CCache 4.10+"
default: 'CCache Statistics'
evict-old-files:
description: "Evict any files not used in the current job run from the cache. Corresponds to the ccache
--evict-older-than AGE option, where AGE is the number of seconds since the job started."
default: true
description: "Corresponds to the ccache --evict-older-than AGE option, where AGE is the number of seconds or days
followed by the 's' or 'd' suffix respectively. Also supports the special value 'job' which represents the time
since the job started, which evicts all cache files that were not touched during the job run."
default: ''
runs:
using: "node20"
main: "dist/restore/index.js"
Expand Down
19 changes: 19 additions & 0 deletions dist/restore/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -58831,10 +58831,29 @@ var cache = __nccwpck_require__(5116);
;// CONCATENATED MODULE: ./src/common.ts


var AgeUnit;
(function (AgeUnit) {
AgeUnit["Seconds"] = "s";
AgeUnit["Days"] = "d";
AgeUnit["Job"] = "job";
})(AgeUnit || (AgeUnit = {}));
function getJobDurationInSeconds() {
const startTime = Number.parseInt(core.getState("startTimestamp"));
return Math.floor((Date.now() - startTime) * 0.001);
}
function parseEvictAgeParameter(age) {
const expr = /([0-9]+)([sd])|job/;
const result = age.match(expr);
if (result) {
if (result[0] !== "job") {
return [Number.parseInt(result[1]), result[2]];
}
else {
return [null, AgeUnit.Job];
}
}
throw new Error(`age parameter ${age} was not valid`);
}
/**
* Parse the output of ccache --version to extract the semantic version components
* @param ccacheOutput
Expand Down
41 changes: 35 additions & 6 deletions dist/save/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -58819,10 +58819,29 @@ var external_path_default = /*#__PURE__*/__nccwpck_require__.n(external_path_);
;// CONCATENATED MODULE: ./src/common.ts


var AgeUnit;
(function (AgeUnit) {
AgeUnit["Seconds"] = "s";
AgeUnit["Days"] = "d";
AgeUnit["Job"] = "job";
})(AgeUnit || (AgeUnit = {}));
function getJobDurationInSeconds() {
const startTime = Number.parseInt(core.getState("startTimestamp"));
return Math.floor((Date.now() - startTime) * 0.001);
}
function parseEvictAgeParameter(age) {
const expr = /([0-9]+)([sd])|job/;
const result = age.match(expr);
if (result) {
if (result[0] !== "job") {
return [Number.parseInt(result[1]), result[2]];
}
else {
return [null, AgeUnit.Job];
}
}
throw new Error(`age parameter ${age} was not valid`);
}
/**
* Parse the output of ccache --version to extract the semantic version components
* @param ccacheOutput
Expand Down Expand Up @@ -58869,6 +58888,7 @@ function cacheDir(ccacheVariant) {




async function ccacheIsEmpty(ccacheVariant, ccacheKnowsVerbosityFlag) {
if (ccacheVariant === "ccache") {
if (ccacheKnowsVerbosityFlag) {
Expand Down Expand Up @@ -58913,9 +58933,9 @@ async function hasJsonStats(ccacheVariant) {
const version = parseCCacheVersion(result.stdout);
return version != null && version[0] >= 4 && version[1] >= 10;
}
async function evictOldFiles(seconds) {
async function evictOldFiles(age, unit) {
try {
await exec.exec(`ccache --evict-older-than ${seconds}s`);
await exec.exec(`ccache --evict-older-than ${age}${unit}`);
}
catch (error) {
core.warning(`Error occurred evicting old cache files: ${error}`);
Expand Down Expand Up @@ -58953,10 +58973,19 @@ async function run(earlyExit) {
core.info("Not saving cache because 'save' is set to 'false'.");
return;
}
if (core.getState("evictOldFiles") === "true" && ccacheVariant === "ccache") {
const jobDuration = getJobDurationInSeconds();
core.debug(`Evicting cache files older than ${jobDuration} seconds`);
await evictOldFiles(jobDuration);
const evictByAge = core.getState("evictOldFiles");
if (evictByAge !== null && ccacheVariant === "ccache") {
core.debug(`Evicting cache files older than ${evictByAge} seconds`);
const [time, unit] = parseEvictAgeParameter(evictByAge);
if (unit === AgeUnit.Job) {
const duration = getJobDurationInSeconds();
core.debug(`Evicting cache files older than ${duration} seconds`);
await evictOldFiles(duration, AgeUnit.Seconds);
}
else {
core.debug(`Evicting cache files older than ${time}${unit}`);
await evictOldFiles(time, unit);
}
}
if (await ccacheIsEmpty(ccacheVariant, ccacheKnowsVerbosityFlag)) {
core.info("Not saving cache because no objects are cached.");
Expand Down
20 changes: 20 additions & 0 deletions src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,31 @@ import * as core from "@actions/core";

type Version = [number,number,number];

export enum AgeUnit {
Seconds = "s",
Days = "d",
Job = "job"
}

export function getJobDurationInSeconds() : number {
const startTime = Number.parseInt(core.getState("startTimestamp"));
return Math.floor((Date.now() - startTime) * 0.001);
}

export function parseEvictAgeParameter(age: string): [number | null, AgeUnit] {
const expr = /([0-9]+)([sd])|job/
const result = age.match(expr);
if (result) {
if (result[0] !== "job") {
return [Number.parseInt(result[1]), result[2] as AgeUnit];
} else {
return [null, AgeUnit.Job];
}
}

throw new Error(`age parameter ${age} was not valid`);
}

/**
* Parse the output of ccache --version to extract the semantic version components
* @param ccacheOutput
Expand Down
2 changes: 1 addition & 1 deletion src/restore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ async function runInner() : Promise<void> {
const ccacheVariant = core.getInput("variant");
core.saveState("startTimestamp", Date.now());
core.saveState("ccacheVariant", ccacheVariant);
core.saveState("evictOldFiles", core.getBooleanInput("evict-old-files"));
core.saveState("evictOldFiles", core.getInput("evict-old-files"));
core.saveState("shouldSave", core.getBooleanInput("save"));
core.saveState("appendTimestamp", core.getBooleanInput("append-timestamp"));
let ccachePath = await io.which(ccacheVariant);
Expand Down
22 changes: 16 additions & 6 deletions src/save.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import * as core from "@actions/core";
import * as cache from "@actions/cache";
import * as exec from "@actions/exec";
import * as common from "./common";
import {AgeUnit} from "./common";


async function ccacheIsEmpty(ccacheVariant : string, ccacheKnowsVerbosityFlag : boolean) : Promise<boolean> {
if (ccacheVariant === "ccache") {
Expand Down Expand Up @@ -53,9 +55,9 @@ async function hasJsonStats(ccacheVariant: string) : Promise<boolean> {
return version != null && version[0] >= 4 && version[1] >= 10;
}

export async function evictOldFiles(seconds : number) : Promise<void> {
export async function evictOldFiles(age : number, unit : common.AgeUnit) : Promise<void> {
try {
await exec.exec(`ccache --evict-older-than ${seconds}s`);
await exec.exec(`ccache --evict-older-than ${age}${unit}`);
}
catch (error) {
core.warning(`Error occurred evicting old cache files: ${error}`);
Expand Down Expand Up @@ -100,10 +102,18 @@ async function run(earlyExit : boolean | undefined) : Promise<void> {
return;
}

if (core.getState("evictOldFiles") === "true" && ccacheVariant === "ccache") {
const jobDuration = common.getJobDurationInSeconds();
core.debug(`Evicting cache files older than ${jobDuration} seconds`);
await evictOldFiles(jobDuration);
const evictByAge = core.getState("evictOldFiles");
if (evictByAge && ccacheVariant === "ccache") {
const [time, unit] = common.parseEvictAgeParameter(evictByAge)
if (unit === AgeUnit.Job) {
const duration = common.getJobDurationInSeconds();
core.debug(`Evicting cache files older than ${duration} seconds`);
await evictOldFiles(duration, common.AgeUnit.Seconds);
}
else {
core.debug(`Evicting cache files older than ${time}${unit}`);
await evictOldFiles(time as number, unit);
}
}

if (await ccacheIsEmpty(ccacheVariant, ccacheKnowsVerbosityFlag)) {
Expand Down

0 comments on commit 6348297

Please sign in to comment.