Use manifests for external cache to avoid overfetching

This commit is contained in:
Alex Rodionov 2023-01-11 13:40:42 -08:00
parent 74154140df
commit 3c0903ad5b
10 changed files with 3105 additions and 131 deletions

View file

@ -53,13 +53,13 @@ Default [`${{ github.token }}`][5].
### Simple configuration ### Simple configuration
```yaml ```yaml
- uses: p0deje/setup-bazel@0.1.0 - uses: p0deje/setup-bazel@0.2.0
``` ```
### Additional `.bazelrc` options ### Additional `.bazelrc` options
```yaml ```yaml
- uses: p0deje/setup-bazel@0.1.0 - uses: p0deje/setup-bazel@0.2.0
with: with:
bazelrc: | bazelrc: |
build --show_timestamps build --show_timestamps
@ -68,7 +68,7 @@ Default [`${{ github.token }}`][5].
### Full caching enabled ### Full caching enabled
```yaml ```yaml
- uses: p0deje/setup-bazel@0.1.0 - uses: p0deje/setup-bazel@0.2.0
with: with:
bazelisk-cache: true bazelisk-cache: true
disk-cache: true disk-cache: true
@ -79,7 +79,7 @@ Default [`${{ github.token }}`][5].
### Separate disk cache between workflows ### Separate disk cache between workflows
```yaml ```yaml
- uses: p0deje/setup-bazel@0.1.0 - uses: p0deje/setup-bazel@0.2.0
with: with:
disk-cache: ${{ github.workflow }}} disk-cache: ${{ github.workflow }}}
``` ```
@ -87,9 +87,10 @@ Default [`${{ github.token }}`][5].
### Cache external repository based on different files ### Cache external repository based on different files
```yaml ```yaml
- uses: p0deje/setup-bazel@0.1.0 - uses: p0deje/setup-bazel@0.2.0
with: with:
external-cache: | external-cache: |
manifest:
npm: package-lock.json npm: package-lock.json
``` ```

View file

@ -29,10 +29,6 @@ inputs:
description: Cache repositories based on WORKSPACE description: Cache repositories based on WORKSPACE
required: false required: false
default: false default: false
token:
description: GitHub token to use for external cache
required: false
default: ${{ github.token }}
runs: runs:
using: node16 using: node16

View file

@ -2,6 +2,7 @@ const fs = require('fs')
const os = require('os') const os = require('os')
const yaml = require('yaml') const yaml = require('yaml')
const core = require('@actions/core') const core = require('@actions/core')
const github = require('@actions/github')
const cacheVersion = core.getInput('cache-version') const cacheVersion = core.getInput('cache-version')
const externalCacheConfig = yaml.parse(core.getInput('external-cache')) const externalCacheConfig = yaml.parse(core.getInput('external-cache'))
@ -57,29 +58,38 @@ if (googleCredentials.length > 0 && !googleCredentialsSaved) {
const bazelExternal = core.toPosixPath(`${bazelOutputBase}/external`) const bazelExternal = core.toPosixPath(`${bazelOutputBase}/external`)
const externalCache = {} const externalCache = {}
if (externalCacheConfig) { if (externalCacheConfig) {
const { workflow, job } = github.context
const manifestName = externalCacheConfig.name ||
`${workflow.toLowerCase().replaceAll(/[ /]/g, '-')}-${job}`
externalCache.enabled = true externalCache.enabled = true
externalCache.minSize = 10 // MB externalCache.minSize = 10 // MB
externalCache.baseCacheKey = `${baseCacheKey}-external-` externalCache.baseCacheKey = `${baseCacheKey}-external-`
externalCache.regexp = `^${baseCacheKey}-external-(?<name>.+)-[a-z0-9]+$` externalCache.manifest = {
files: [
'WORKSPACE.bazel',
'WORKSPACE'
],
name: `external-${manifestName}-manifest`,
path: `${os.tmpdir()}/external-cache-manifest.txt`
}
externalCache.default = { externalCache.default = {
files: [ files: [
'WORKSPACE.bazel', 'WORKSPACE.bazel',
'WORKSPACE' 'WORKSPACE'
] ],
} name: (name) => { return `external-${name}` },
externalCache.name = (name) => { paths: (name) => {
return `external-${name}`
}
externalCache.paths = (name) => {
return [ return [
`${bazelExternal}/@${name}.marker`, `${bazelExternal}/@${name}.marker`,
`${bazelExternal}/${name}` `${bazelExternal}/${name}`
] ]
} }
}
for (const name in externalCacheConfig) { for (const name in externalCacheConfig.manifest) {
externalCache[name] = { externalCache[name] = {
files: Array(externalCacheConfig[name]).flat() files: Array(externalCacheConfig.manifest[name]).flat()
} }
} }
} }
@ -118,5 +128,4 @@ module.exports = {
name: 'repository', name: 'repository',
paths: [bazelRepository] paths: [bazelRepository]
}, },
token: core.getInput('token')
} }

78
dist/main/index.js vendored
View file

@ -8,6 +8,7 @@ const fs = __nccwpck_require__(7147)
const os = __nccwpck_require__(2037) const os = __nccwpck_require__(2037)
const yaml = __nccwpck_require__(4083) const yaml = __nccwpck_require__(4083)
const core = __nccwpck_require__(2186) const core = __nccwpck_require__(2186)
const github = __nccwpck_require__(5438)
const cacheVersion = core.getInput('cache-version') const cacheVersion = core.getInput('cache-version')
const externalCacheConfig = yaml.parse(core.getInput('external-cache')) const externalCacheConfig = yaml.parse(core.getInput('external-cache'))
@ -63,29 +64,38 @@ if (googleCredentials.length > 0 && !googleCredentialsSaved) {
const bazelExternal = core.toPosixPath(`${bazelOutputBase}/external`) const bazelExternal = core.toPosixPath(`${bazelOutputBase}/external`)
const externalCache = {} const externalCache = {}
if (externalCacheConfig) { if (externalCacheConfig) {
const { workflow, job } = github.context
const manifestName = externalCacheConfig.name ||
`${workflow.toLowerCase().replaceAll(/[ /]/g, '-')}-${job}`
externalCache.enabled = true externalCache.enabled = true
externalCache.minSize = 10 // MB externalCache.minSize = 10 // MB
externalCache.baseCacheKey = `${baseCacheKey}-external-` externalCache.baseCacheKey = `${baseCacheKey}-external-`
externalCache.regexp = `^${baseCacheKey}-external-(?<name>.+)-[a-z0-9]+$` externalCache.manifest = {
files: [
'WORKSPACE.bazel',
'WORKSPACE'
],
name: `external-${manifestName}-manifest`,
path: `${os.tmpdir()}/external-cache-manifest.txt`
}
externalCache.default = { externalCache.default = {
files: [ files: [
'WORKSPACE.bazel', 'WORKSPACE.bazel',
'WORKSPACE' 'WORKSPACE'
] ],
} name: (name) => { return `external-${name}` },
externalCache.name = (name) => { paths: (name) => {
return `external-${name}`
}
externalCache.paths = (name) => {
return [ return [
`${bazelExternal}/@${name}.marker`, `${bazelExternal}/@${name}.marker`,
`${bazelExternal}/${name}` `${bazelExternal}/${name}`
] ]
} }
}
for (const name in externalCacheConfig) { for (const name in externalCacheConfig.manifest) {
externalCache[name] = { externalCache[name] = {
files: Array(externalCacheConfig[name]).flat() files: Array(externalCacheConfig.manifest[name]).flat()
} }
} }
} }
@ -124,7 +134,6 @@ module.exports = {
name: 'repository', name: 'repository',
paths: [bazelRepository] paths: [bazelRepository]
}, },
token: core.getInput('token')
} }
@ -66132,14 +66141,6 @@ module.exports = require("net");
/***/ }), /***/ }),
/***/ 9397:
/***/ ((module) => {
"use strict";
module.exports = require("node:timers/promises");
/***/ }),
/***/ 2037: /***/ 2037:
/***/ ((module) => { /***/ ((module) => {
@ -74616,10 +74617,8 @@ var __webpack_exports__ = {};
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk. // This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
(() => { (() => {
const fs = __nccwpck_require__(7147) const fs = __nccwpck_require__(7147)
const { setTimeout } = __nccwpck_require__(9397)
const core = __nccwpck_require__(2186) const core = __nccwpck_require__(2186)
const cache = __nccwpck_require__(7799) const cache = __nccwpck_require__(7799)
const github = __nccwpck_require__(5438)
const glob = __nccwpck_require__(8090) const glob = __nccwpck_require__(8090)
const config = __nccwpck_require__(5532) const config = __nccwpck_require__(5532)
@ -74661,35 +74660,28 @@ async function restoreExternalCaches (cacheConfig) {
return return
} }
const repo = github.context.repo // First fetch the manifest of external caches used.
const octokit = github.getOctokit(config.token) const path = cacheConfig.manifest.path
const { data: { actions_caches: caches } } = await octokit.rest.actions.getActionsCacheList({ await restoreCache({
owner: repo.owner, enabled: true,
repo: repo.repo, files: cacheConfig.manifest.files,
key: cacheConfig.baseCacheKey, name: cacheConfig.manifest.name,
per_page: 100 paths: [path]
}) })
const names = new Set([]) // Now restore all external caches defined in manifest
const regexp = new RegExp(cacheConfig.regexp) if (fs.existsSync(path)) {
for (const cache of caches) { const manifest = fs.readFileSync(path, { encoding: 'utf8' })
core.debug(`Cache key is ${cache.key}`) for (const name of manifest.split('\n').filter(s => s)) {
const match = cache.key.match(regexp)
if (match) {
names.add(match.groups.name)
}
}
for (const name of names) {
await restoreCache({ await restoreCache({
enabled: true, enabled: true,
files: cacheConfig[name]?.files || cacheConfig.default.files, files: cacheConfig[name]?.files || cacheConfig.default.files,
name: cacheConfig.name(name), name: cacheConfig.default.name(name),
paths: cacheConfig.paths(name) paths: cacheConfig.default.paths(name)
}) })
} }
} }
}
async function restoreCache (cacheConfig) { async function restoreCache (cacheConfig) {
if (!cacheConfig.enabled) { if (!cacheConfig.enabled) {
@ -74706,12 +74698,10 @@ async function restoreCache (cacheConfig) {
console.log(`Attempting to restore ${name} cache from ${key}`) console.log(`Attempting to restore ${name} cache from ${key}`)
const restoredKey = await setTimeout(1000, async function() { const restoredKey = await cache.restoreCache(
return await cache.restoreCache(
paths, key, [restoreKey], paths, key, [restoreKey],
{ segmentTimeoutInMs: 300000 } // 5 minutes { segmentTimeoutInMs: 300000 } // 5 minutes
) )
}())
if (restoredKey) { if (restoredKey) {
console.log(`Successfully restored cache from ${restoredKey}`) console.log(`Successfully restored cache from ${restoredKey}`)

File diff suppressed because one or more lines are too long

2999
dist/post/index.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,8 +1,6 @@
const fs = require('fs') const fs = require('fs')
const { setTimeout } = require('node:timers/promises')
const core = require('@actions/core') const core = require('@actions/core')
const cache = require('@actions/cache') const cache = require('@actions/cache')
const github = require('@actions/github')
const glob = require('@actions/glob') const glob = require('@actions/glob')
const config = require('./config') const config = require('./config')
@ -44,35 +42,28 @@ async function restoreExternalCaches (cacheConfig) {
return return
} }
const repo = github.context.repo // First fetch the manifest of external caches used.
const octokit = github.getOctokit(config.token) const path = cacheConfig.manifest.path
const { data: { actions_caches: caches } } = await octokit.rest.actions.getActionsCacheList({ await restoreCache({
owner: repo.owner, enabled: true,
repo: repo.repo, files: cacheConfig.manifest.files,
key: cacheConfig.baseCacheKey, name: cacheConfig.manifest.name,
per_page: 100 paths: [path]
}) })
const names = new Set([]) // Now restore all external caches defined in manifest
const regexp = new RegExp(cacheConfig.regexp) if (fs.existsSync(path)) {
for (const cache of caches) { const manifest = fs.readFileSync(path, { encoding: 'utf8' })
core.debug(`Cache key is ${cache.key}`) for (const name of manifest.split('\n').filter(s => s)) {
const match = cache.key.match(regexp)
if (match) {
names.add(match.groups.name)
}
}
for (const name of names) {
await restoreCache({ await restoreCache({
enabled: true, enabled: true,
files: cacheConfig[name]?.files || cacheConfig.default.files, files: cacheConfig[name]?.files || cacheConfig.default.files,
name: cacheConfig.name(name), name: cacheConfig.default.name(name),
paths: cacheConfig.paths(name) paths: cacheConfig.default.paths(name)
}) })
} }
} }
}
async function restoreCache (cacheConfig) { async function restoreCache (cacheConfig) {
if (!cacheConfig.enabled) { if (!cacheConfig.enabled) {
@ -89,12 +80,10 @@ async function restoreCache (cacheConfig) {
console.log(`Attempting to restore ${name} cache from ${key}`) console.log(`Attempting to restore ${name} cache from ${key}`)
const restoredKey = await setTimeout(1000, async function() { const restoredKey = await cache.restoreCache(
return await cache.restoreCache(
paths, key, [restoreKey], paths, key, [restoreKey],
{ segmentTimeoutInMs: 300000 } // 5 minutes { segmentTimeoutInMs: 300000 } // 5 minutes
) )
}())
if (restoredKey) { if (restoredKey) {
console.log(`Successfully restored cache from ${restoredKey}`) console.log(`Successfully restored cache from ${restoredKey}`)

View file

@ -1,6 +1,6 @@
{ {
"name": "setup-bazel", "name": "setup-bazel",
"version": "0.1.0", "version": "0.2.0",
"description": "Install and configure Bazel for GitHub Actions", "description": "Install and configure Bazel for GitHub Actions",
"main": "index.js", "main": "index.js",
"engines" : { "engines" : {

18
post.js
View file

@ -1,3 +1,4 @@
const fs = require('fs')
const path = require('path') const path = require('path')
const cache = require('@actions/cache') const cache = require('@actions/cache')
const core = require('@actions/core') const core = require('@actions/core')
@ -26,6 +27,7 @@ async function saveExternalCaches (cacheConfig) {
{ implicitDescendants: false } { implicitDescendants: false }
) )
const externalPaths = await globber.glob() const externalPaths = await globber.glob()
const savedCaches = []
for (const externalPath of externalPaths) { for (const externalPath of externalPaths) {
const size = await getFolderSize(externalPath) const size = await getFolderSize(externalPath)
@ -37,11 +39,23 @@ async function saveExternalCaches (cacheConfig) {
await saveCache({ await saveCache({
enabled: true, enabled: true,
files: cacheConfig[name]?.files || cacheConfig.default.files, files: cacheConfig[name]?.files || cacheConfig.default.files,
name: cacheConfig.name(name), name: cacheConfig.default.name(name),
paths: cacheConfig.paths(name) paths: cacheConfig.default.paths(name)
}) })
savedCaches.push(name)
} }
} }
if (savedCaches.length > 0) {
const path = cacheConfig.manifest.path
fs.writeFileSync(path, savedCaches.join('\n'))
await saveCache({
enabled: true,
files: cacheConfig.manifest.files,
name: cacheConfig.manifest.name,
paths: [path]
})
}
} }
async function saveCache (cacheConfig) { async function saveCache (cacheConfig) {