13 Commits

Author SHA1 Message Date
Arpad Borsos
a4a1d8e7a6 1.0.1 2020-09-28 13:06:41 +02:00
Arpad Borsos
33677a20f2 add changelog 2020-09-28 12:53:56 +02:00
Arpad Borsos
1d1bff80c5 update readme 2020-09-28 12:51:40 +02:00
Arpad Borsos
08ca2ff969 make macos workaround silent 2020-09-28 12:46:36 +02:00
Arpad Borsos
ef89c3a8eb typo 2020-09-28 12:30:40 +02:00
Arpad Borsos
d45cd2b045 rebuild 2020-09-28 12:26:12 +02:00
Arpad Borsos
271ff4b692 log individual timings 2020-09-28 12:26:11 +02:00
Arpad Borsos
a6b59fa340 clean up exports 2020-09-28 12:14:11 +02:00
Arpad Borsos
e0c07d2a65 work around macos cache corruption 2020-09-28 12:08:11 +02:00
Arpad Borsos
06ff70612d remove git-db for now 2020-09-28 12:06:51 +02:00
Arpad Borsos
1304a2ec8d add ability to version caches 2020-09-28 12:00:58 +02:00
Arpad Borsos
cfcc373039 improve logging 2020-09-28 11:54:24 +02:00
Arpad Borsos
8902a8fc6c collect packages with --all-features 2020-09-28 11:44:06 +02:00
9 changed files with 542 additions and 440 deletions

8
CHANGELOG.md Normal file
View File

@@ -0,0 +1,8 @@
# Changelog
## 1.0.1
- Improved logging output.
- Make sure to consider `all-features` dependencies when pruning.
- Work around macOS cache corruption.
- Remove git-db cache for now.

View File

@@ -30,20 +30,16 @@ assumption is that we will likely recompile the own crate(s) anyway.
It also separates the cache into 4 groups, each treated differently: It also separates the cache into 4 groups, each treated differently:
- Index: `~/.cargo/registry/index/<registry>`: - Registry Index: `~/.cargo/registry/index/<registry>`:
This is always restored from its latest snapshot, and persisted based on the This is always restored from its latest snapshot, and persisted based on the
most recent revision. most recent revision.
- Registry / Cache: `~/.cargo/registry/cache/<registry>`: - Registry Cache: `~/.cargo/registry/cache/<registry>`:
Automatically keyed by the lockfile/toml hash, and is being pruned to only Automatically keyed by the lockfile/toml hash, and is being pruned to only
persist the dependencies that are being used. persist the dependencies that are being used.
- Registry / Git: `~/.cargo/registry/git/<registry>`:
Automatically keyed by the lockfile/toml hash. Pruning is still TODO.
- target: `./target` - target: `./target`
Automatically keyed by the lockfile/toml hash, and is being pruned to only Automatically keyed by the lockfile/toml hash, and is being pruned to only

45
dist/restore/index.js vendored
View File

@@ -54634,14 +54634,32 @@ async function getCaches() {
if (targetKey) { if (targetKey) {
targetKey = `${targetKey}-`; targetKey = `${targetKey}-`;
} }
const registryIndex = `v0-registry-index`;
const registryCache = `v0-registry-cache`;
const target = `v0-target-${targetKey}${rustKey}`;
return { return {
index: { path: paths.index, key: "registry-index-XXX", restoreKeys: ["registry-index"] }, index: {
cache: { path: paths.cache, key: `registry-cache-${lockHash}`, restoreKeys: ["registry-cache"] }, name: "Registry Index",
git: { path: paths.git, key: "git-db" }, path: paths.index,
key: `${registryIndex}-`,
restoreKeys: [registryIndex],
},
cache: {
name: "Registry Cache",
path: paths.cache,
key: `${registryCache}-${lockHash}`,
restoreKeys: [registryCache],
},
// git: {
// name: "Git Dependencies",
// path: paths.git,
// key: "git-db",
// },
target: { target: {
name: "Target",
path: paths.target, path: paths.target,
key: `target-${targetKey}${rustKey}-${lockHash}`, key: `${target}-${lockHash}`,
restoreKeys: [`target-${targetKey}${rustKey}`], restoreKeys: [target],
}, },
}; };
} }
@@ -54714,13 +54732,16 @@ async function run() {
try { try {
core.exportVariable("CARGO_INCREMENTAL", 0); core.exportVariable("CARGO_INCREMENTAL", 0);
const caches = await getCaches(); const caches = await getCaches();
for (const [name, { path, key, restoreKeys }] of Object.entries(caches)) { for (const [type, { name, path, key, restoreKeys }] of Object.entries(caches)) {
const start = Date.now();
core.startGroup(`Restoring ${name}`);
core.info(`Restoring to path "${path}".`);
core.info(`Using keys:\n ${[key, ...restoreKeys].join("\n ")}`);
try { try {
core.startGroup(`Restoring "${path}" from "${key}"…`);
const restoreKey = await cache.restoreCache([path], key, restoreKeys); const restoreKey = await cache.restoreCache([path], key, restoreKeys);
if (restoreKey) { if (restoreKey) {
core.info(`Restored "${path}" from cache key "${restoreKey}".`); core.info(`Restored from cache key "${restoreKey}".`);
core.saveState(name, restoreKey); core.saveState(type, restoreKey);
} }
else { else {
core.info("No cache found."); core.info("No cache found.");
@@ -54729,9 +54750,11 @@ async function run() {
catch (e) { catch (e) {
core.info(`[warning] ${e.message}`); core.info(`[warning] ${e.message}`);
} }
finally { const duration = Math.round((Date.now() - start) / 1000);
core.endGroup(); if (duration) {
core.info(`Took ${duration}s.`);
} }
core.endGroup();
} }
} }
catch (e) { catch (e) {

80
dist/save/index.js vendored
View File

@@ -54581,25 +54581,25 @@ var exec = __webpack_require__(1514);
// EXTERNAL MODULE: ./node_modules/@actions/glob/lib/glob.js // EXTERNAL MODULE: ./node_modules/@actions/glob/lib/glob.js
var glob = __webpack_require__(8090); var glob = __webpack_require__(8090);
// EXTERNAL MODULE: ./node_modules/@actions/io/lib/io.js
var io = __webpack_require__(7436);
// EXTERNAL MODULE: external "crypto" // EXTERNAL MODULE: external "crypto"
var external_crypto_ = __webpack_require__(6417); var external_crypto_ = __webpack_require__(6417);
var external_crypto_default = /*#__PURE__*/__webpack_require__.n(external_crypto_); var external_crypto_default = /*#__PURE__*/__webpack_require__.n(external_crypto_);
// EXTERNAL MODULE: ./node_modules/@actions/io/lib/io.js
var io = __webpack_require__(7436);
// EXTERNAL MODULE: external "fs" // EXTERNAL MODULE: external "fs"
var external_fs_ = __webpack_require__(5747); var external_fs_ = __webpack_require__(5747);
var external_fs_default = /*#__PURE__*/__webpack_require__.n(external_fs_); var external_fs_default = /*#__PURE__*/__webpack_require__.n(external_fs_);
// EXTERNAL MODULE: external "path"
var external_path_ = __webpack_require__(5622);
var external_path_default = /*#__PURE__*/__webpack_require__.n(external_path_);
// EXTERNAL MODULE: external "os" // EXTERNAL MODULE: external "os"
var external_os_ = __webpack_require__(2087); var external_os_ = __webpack_require__(2087);
var external_os_default = /*#__PURE__*/__webpack_require__.n(external_os_); var external_os_default = /*#__PURE__*/__webpack_require__.n(external_os_);
// EXTERNAL MODULE: external "path"
var external_path_ = __webpack_require__(5622);
var external_path_default = /*#__PURE__*/__webpack_require__.n(external_path_);
// CONCATENATED MODULE: ./src/common.ts // CONCATENATED MODULE: ./src/common.ts
var __asyncValues = (undefined && undefined.__asyncValues) || function (o) { var __asyncValues = (undefined && undefined.__asyncValues) || function (o) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
@@ -54637,14 +54637,32 @@ async function getCaches() {
if (targetKey) { if (targetKey) {
targetKey = `${targetKey}-`; targetKey = `${targetKey}-`;
} }
const registryIndex = `v0-registry-index`;
const registryCache = `v0-registry-cache`;
const target = `v0-target-${targetKey}${rustKey}`;
return { return {
index: { path: paths.index, key: "registry-index-XXX", restoreKeys: ["registry-index"] }, index: {
cache: { path: paths.cache, key: `registry-cache-${lockHash}`, restoreKeys: ["registry-cache"] }, name: "Registry Index",
git: { path: paths.git, key: "git-db" }, path: paths.index,
key: `${registryIndex}-`,
restoreKeys: [registryIndex],
},
cache: {
name: "Registry Cache",
path: paths.cache,
key: `${registryCache}-${lockHash}`,
restoreKeys: [registryCache],
},
// git: {
// name: "Git Dependencies",
// path: paths.git,
// key: "git-db",
// },
target: { target: {
name: "Target",
path: paths.target, path: paths.target,
key: `target-${targetKey}${rustKey}-${lockHash}`, key: `${target}-${lockHash}`,
restoreKeys: [`target-${targetKey}${rustKey}`], restoreKeys: [target],
}, },
}; };
} }
@@ -54720,19 +54738,22 @@ var save_asyncValues = (undefined && undefined.__asyncValues) || function (o) {
async function run() { async function run() {
if (!isValidEvent()) { if (!isValidEvent()) {
//return; return;
} }
try { try {
const caches = await getCaches(); const caches = await getCaches();
const registryName = await getRegistryName(); const registryName = await getRegistryName();
const packages = await getPackages(); const packages = await getPackages();
// TODO: remove this once https://github.com/actions/toolkit/pull/553 lands
await macOsWorkaround();
await pruneTarget(packages); await pruneTarget(packages);
if (registryName) { if (registryName) {
// save the index based on its revision // save the index based on its revision
const indexRef = await getIndexRef(registryName); const indexRef = await getIndexRef(registryName);
caches.index.key = `registry-index-${indexRef}`; caches.index.key += indexRef;
await io.rmRF(external_path_default().join(paths.index, registryName, ".cache")); await io.rmRF(external_path_default().join(paths.index, registryName, ".cache"));
await pruneRegistryCache(registryName, packages); await pruneRegistryCache(registryName, packages);
} }
@@ -54740,23 +54761,26 @@ async function run() {
delete caches.index; delete caches.index;
delete caches.cache; delete caches.cache;
} }
for (const [name, { path, key }] of Object.entries(caches)) { for (const [type, { name, path, key }] of Object.entries(caches)) {
if (core.getState(name) === key) { if (core.getState(type) === key) {
core.info(`Cache for "${path}" up-to-date.`); core.info(`${name} up-to-date.`);
continue; continue;
} }
const start = Date.now();
core.startGroup(`Saving ${name}`);
core.info(`Saving path "${path}".`);
core.info(`Using key "${key}".`);
try { try {
core.startGroup(`Saving "${path}" to cache key "${key}"…`); await cache.saveCache([path], key);
if (await cache.saveCache([path], key)) {
core.info(`Saved "${path}" to cache key "${key}".`);
}
} }
catch (e) { catch (e) {
core.info(`[warning] ${e.message}`); core.info(`[warning] ${e.message}`);
} }
finally { const duration = Math.round((Date.now() - start) / 1000);
core.endGroup(); if (duration) {
core.info(`Took ${duration}s.`);
} }
core.endGroup();
} }
} }
catch (e) { catch (e) {
@@ -54769,7 +54793,7 @@ async function getIndexRef(registryName) {
return (await getCmdOutput("git", ["rev-parse", "--short", "origin/master"], { cwd })).trim(); return (await getCmdOutput("git", ["rev-parse", "--short", "origin/master"], { cwd })).trim();
} }
async function getPackages() { async function getPackages() {
const meta = JSON.parse(await getCmdOutput("cargo", ["metadata", "--format-version", "1"])); const meta = JSON.parse(await getCmdOutput("cargo", ["metadata", "--all-features", "--format-version", "1"]));
return meta.packages.map(({ name, version }) => ({ name, version })); return meta.packages.map(({ name, version }) => ({ name, version }));
} }
async function pruneRegistryCache(registryName, packages) { async function pruneRegistryCache(registryName, packages) {
@@ -54860,6 +54884,14 @@ async function rmExcept(dirName, keepPrefix) {
finally { if (e_3) throw e_3.error; } finally { if (e_3) throw e_3.error; }
} }
} }
async function macOsWorkaround() {
try {
// Workaround for https://github.com/actions/cache/issues/403
// Also see https://github.com/rust-lang/cargo/issues/8603
await exec.exec("sudo", ["/usr/sbin/purge"], { silent: true });
}
catch (_a) { }
}
/***/ }), /***/ }),

2
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{ {
"name": "rust-cache", "name": "rust-cache",
"version": "1.0.0", "version": "1.0.1",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

@@ -1,7 +1,7 @@
{ {
"private": true, "private": true,
"name": "rust-cache", "name": "rust-cache",
"version": "1.0.0", "version": "1.0.1",
"description": "A GitHub Action that implements smart caching for rust/cargo projects", "description": "A GitHub Action that implements smart caching for rust/cargo projects",
"keywords": [ "keywords": [
"actions", "actions",

View File

@@ -14,16 +14,17 @@ export const paths = {
target: "target", target: "target",
}; };
export interface CacheConfig { interface CacheConfig {
name: string;
path: string; path: string;
key: string; key: string;
restoreKeys?: Array<string>; restoreKeys?: Array<string>;
} }
export interface Caches { interface Caches {
index: CacheConfig; index: CacheConfig;
cache: CacheConfig; cache: CacheConfig;
git: CacheConfig; // git: CacheConfig;
target: CacheConfig; target: CacheConfig;
} }
@@ -44,19 +45,38 @@ export async function getCaches(): Promise<Caches> {
if (targetKey) { if (targetKey) {
targetKey = `${targetKey}-`; targetKey = `${targetKey}-`;
} }
const registryIndex = `v0-registry-index`;
const registryCache = `v0-registry-cache`;
const target = `v0-target-${targetKey}${rustKey}`;
return { return {
index: { path: paths.index, key: "registry-index-XXX", restoreKeys: ["registry-index"] }, index: {
cache: { path: paths.cache, key: `registry-cache-${lockHash}`, restoreKeys: ["registry-cache"] }, name: "Registry Index",
git: { path: paths.git, key: "git-db" }, path: paths.index,
key: `${registryIndex}-`,
restoreKeys: [registryIndex],
},
cache: {
name: "Registry Cache",
path: paths.cache,
key: `${registryCache}-${lockHash}`,
restoreKeys: [registryCache],
},
// git: {
// name: "Git Dependencies",
// path: paths.git,
// key: "git-db",
// },
target: { target: {
name: "Target",
path: paths.target, path: paths.target,
key: `target-${targetKey}${rustKey}-${lockHash}`, key: `${target}-${lockHash}`,
restoreKeys: [`target-${targetKey}${rustKey}`], restoreKeys: [target],
}, },
}; };
} }
export async function getRustKey(): Promise<string> { async function getRustKey(): Promise<string> {
const rustc = await getRustVersion(); const rustc = await getRustVersion();
return `${rustc.release}-${rustc.host}-${rustc["commit-hash"]}`; return `${rustc.release}-${rustc.host}-${rustc["commit-hash"]}`;
} }
@@ -67,7 +87,7 @@ interface RustVersion {
"commit-hash": string; "commit-hash": string;
} }
export async function getRustVersion(): Promise<RustVersion> { async function getRustVersion(): Promise<RustVersion> {
const stdout = await getCmdOutput("rustc", ["-vV"]); const stdout = await getCmdOutput("rustc", ["-vV"]);
let splits = stdout let splits = stdout
.split(/[\n\r]+/) .split(/[\n\r]+/)
@@ -109,7 +129,7 @@ export async function getRegistryName() {
return path.basename(path.dirname(first)); return path.basename(path.dirname(first));
} }
export async function getLockfileHash() { async function getLockfileHash() {
const globber = await glob.create("**/Cargo.toml\n**/Cargo.lock", { followSymbolicLinks: false }); const globber = await glob.create("**/Cargo.toml\n**/Cargo.lock", { followSymbolicLinks: false });
const files = await globber.glob(); const files = await globber.glob();
files.sort((a, b) => a.localeCompare(b)); files.sort((a, b) => a.localeCompare(b));

View File

@@ -11,21 +11,27 @@ async function run() {
core.exportVariable("CARGO_INCREMENTAL", 0); core.exportVariable("CARGO_INCREMENTAL", 0);
const caches = await getCaches(); const caches = await getCaches();
for (const [name, { path, key, restoreKeys }] of Object.entries(caches)) { for (const [type, { name, path, key, restoreKeys }] of Object.entries(caches)) {
const start = Date.now();
core.startGroup(`Restoring ${name}`);
core.info(`Restoring to path "${path}".`);
core.info(`Using keys:\n ${[key, ...restoreKeys].join("\n ")}`);
try { try {
core.startGroup(`Restoring "${path}" from "${key}"…`);
const restoreKey = await cache.restoreCache([path], key, restoreKeys); const restoreKey = await cache.restoreCache([path], key, restoreKeys);
if (restoreKey) { if (restoreKey) {
core.info(`Restored "${path}" from cache key "${restoreKey}".`); core.info(`Restored from cache key "${restoreKey}".`);
core.saveState(name, restoreKey); core.saveState(type, restoreKey);
} else { } else {
core.info("No cache found."); core.info("No cache found.");
} }
} catch (e) { } catch (e) {
core.info(`[warning] ${e.message}`); core.info(`[warning] ${e.message}`);
} finally {
core.endGroup();
} }
const duration = Math.round((Date.now() - start) / 1000);
if (duration) {
core.info(`Took ${duration}s.`);
}
core.endGroup();
} }
} catch (e) { } catch (e) {
core.info(`[warning] ${e.message}`); core.info(`[warning] ${e.message}`);

View File

@@ -1,5 +1,6 @@
import * as cache from "@actions/cache"; import * as cache from "@actions/cache";
import * as core from "@actions/core"; import * as core from "@actions/core";
import * as exec from "@actions/exec";
import * as io from "@actions/io"; import * as io from "@actions/io";
import fs from "fs"; import fs from "fs";
import path from "path"; import path from "path";
@@ -7,7 +8,7 @@ import { getCaches, getCmdOutput, getRegistryName, isValidEvent, paths } from ".
async function run() { async function run() {
if (!isValidEvent()) { if (!isValidEvent()) {
//return; return;
} }
try { try {
@@ -15,11 +16,15 @@ async function run() {
const registryName = await getRegistryName(); const registryName = await getRegistryName();
const packages = await getPackages(); const packages = await getPackages();
// TODO: remove this once https://github.com/actions/toolkit/pull/553 lands
await macOsWorkaround();
await pruneTarget(packages); await pruneTarget(packages);
if (registryName) { if (registryName) {
// save the index based on its revision // save the index based on its revision
const indexRef = await getIndexRef(registryName); const indexRef = await getIndexRef(registryName);
caches.index.key = `registry-index-${indexRef}`; caches.index.key += indexRef;
await io.rmRF(path.join(paths.index, registryName, ".cache")); await io.rmRF(path.join(paths.index, registryName, ".cache"));
await pruneRegistryCache(registryName, packages); await pruneRegistryCache(registryName, packages);
@@ -28,21 +33,25 @@ async function run() {
delete (caches as any).cache; delete (caches as any).cache;
} }
for (const [name, { path, key }] of Object.entries(caches)) { for (const [type, { name, path, key }] of Object.entries(caches)) {
if (core.getState(name) === key) { if (core.getState(type) === key) {
core.info(`Cache for "${path}" up-to-date.`); core.info(`${name} up-to-date.`);
continue; continue;
} }
const start = Date.now();
core.startGroup(`Saving ${name}`);
core.info(`Saving path "${path}".`);
core.info(`Using key "${key}".`);
try { try {
core.startGroup(`Saving "${path}" to cache key "${key}"…`); await cache.saveCache([path], key);
if (await cache.saveCache([path], key)) {
core.info(`Saved "${path}" to cache key "${key}".`);
}
} catch (e) { } catch (e) {
core.info(`[warning] ${e.message}`); core.info(`[warning] ${e.message}`);
} finally {
core.endGroup();
} }
const duration = Math.round((Date.now() - start) / 1000);
if (duration) {
core.info(`Took ${duration}s.`);
}
core.endGroup();
} }
} catch (e) { } catch (e) {
core.info(`[warning] ${e.message}`); core.info(`[warning] ${e.message}`);
@@ -64,7 +73,7 @@ interface PackageDefinition {
type Packages = Array<PackageDefinition>; type Packages = Array<PackageDefinition>;
async function getPackages(): Promise<Packages> { async function getPackages(): Promise<Packages> {
const meta = JSON.parse(await getCmdOutput("cargo", ["metadata", "--format-version", "1"])); const meta = JSON.parse(await getCmdOutput("cargo", ["metadata", "--all-features", "--format-version", "1"]));
return meta.packages.map(({ name, version }: any) => ({ name, version })); return meta.packages.map(({ name, version }: any) => ({ name, version }));
} }
@@ -131,3 +140,11 @@ async function rmExcept(dirName: string, keepPrefix: Set<string>) {
} }
} }
} }
async function macOsWorkaround() {
try {
// Workaround for https://github.com/actions/cache/issues/403
// Also see https://github.com/rust-lang/cargo/issues/8603
await exec.exec("sudo", ["/usr/sbin/purge"], { silent: true });
} catch {}
}