Skip to content

Commit 7019ae6

Browse files
committed
When extracting into a non-empty directory, cache the .zip file
Since the `@actions/cache` functionality has _no_ option to include only a given set of files (unless that very set of files is known _when restoring the cache_, not only when saving it), we have no chance of caching files directly when we write them into non-empty directories. To work around that, let's special-case non-empty directories, caching the downloaded `.zip` file instead (and obviously paying the slight performance penalty of `unzip`ping it in that scenario even when restoring the cache, which is slower than extracting a `.tgz`). Signed-off-by: Johannes Schindelin <[email protected]>
1 parent a46f349 commit 7019ae6

File tree

2 files changed

+43
-15
lines changed

2 files changed

+43
-15
lines changed

main.ts

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as core from '@actions/core'
2-
import {get} from './src/downloader'
2+
import {unzip, get} from './src/downloader'
33
import {restoreCache, saveCache} from '@actions/cache'
4-
import {readdirSync} from 'fs'
4+
import {readdirSync, unlinkSync} from 'fs'
55

66
async function run(): Promise<void> {
77
try {
@@ -26,14 +26,23 @@ async function run(): Promise<void> {
2626
}
2727
}
2828

29-
if (useCache && !isDirectoryEmpty(outputDirectory)) {
30-
throw new Error(`Directory '${outputDirectory}' not empty`)
31-
}
32-
3329
let needToDownload = true
30+
let storeZipAs: string | undefined
3431
if (useCache) {
3532
try {
36-
if (await restoreCache([outputDirectory], cacheId)) {
33+
if (!isDirectoryEmpty(outputDirectory)) {
34+
storeZipAs = `${outputDirectory}/.${cacheId}.zip`
35+
if (await restoreCache([storeZipAs], cacheId)) {
36+
await unzip(
37+
`file:${storeZipAs}`,
38+
stripPrefix,
39+
outputDirectory,
40+
verbose
41+
)
42+
core.info(`Cached ${cacheId} was successfully restored`)
43+
needToDownload = false
44+
}
45+
} else if (await restoreCache([outputDirectory], cacheId)) {
3746
core.info(`Cached ${cacheId} was successfully restored`)
3847
needToDownload = false
3948
}
@@ -45,15 +54,22 @@ async function run(): Promise<void> {
4554

4655
if (needToDownload) {
4756
core.info(`Downloading ${artifactName}`)
48-
await download(outputDirectory, verbose)
57+
await download(outputDirectory, verbose, storeZipAs)
4958

5059
try {
51-
if (useCache && !(await saveCache([outputDirectory], cacheId))) {
60+
if (
61+
useCache &&
62+
!(await saveCache([storeZipAs || outputDirectory], cacheId))
63+
) {
5264
core.warning(`Failed to cache ${cacheId}`)
5365
}
5466
} catch (e) {
5567
core.warning(`Failed to cache ${cacheId}: ${e.message}`)
5668
}
69+
70+
if (storeZipAs) {
71+
unlinkSync(storeZipAs)
72+
}
5773
}
5874
} catch (error) {
5975
core.setFailed(error.message)

src/downloader.ts

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,12 @@ function mkdirp(directoryPath: string): void {
2929
fs.mkdirSync(directoryPath, {recursive: true})
3030
}
3131

32-
async function unzip(
32+
export async function unzip(
3333
url: string,
3434
stripPrefix: string,
3535
outputDirectory: string,
36-
verbose: boolean | number
36+
verbose: boolean | number,
37+
storeZipAs?: string
3738
): Promise<string[]> {
3839
const files: string[] = []
3940
let progress =
@@ -64,6 +65,10 @@ async function unzip(
6465

6566
return new Promise<string[]>((resolve, reject) => {
6667
const callback = (res: Readable): void => {
68+
if (storeZipAs) {
69+
process.stderr.write(`Writing ${storeZipAs}\n`)
70+
res.pipe(fs.createWriteStream(storeZipAs)).on('error', reject)
71+
}
6772
res
6873
.on('error', reject)
6974
.pipe(unzipper.Parse())
@@ -88,7 +93,11 @@ async function unzip(
8893
.on('finish', progress)
8994
.on('finish', () => resolve(files))
9095
}
91-
https.get(url, callback).on('error', reject)
96+
if (url.startsWith('file:')) {
97+
callback(fs.createReadStream(url.substring('file:'.length)))
98+
} else {
99+
https.get(url, callback).on('error', reject)
100+
}
92101
})
93102
}
94103

@@ -103,7 +112,8 @@ export async function get(
103112
cacheId: string
104113
download: (
105114
outputDirectory: string,
106-
verbose?: number | boolean
115+
verbose?: number | boolean,
116+
storeZipAs?: string
107117
) => Promise<string[]>
108118
}> {
109119
if (!repository || !definitionId) {
@@ -158,7 +168,8 @@ export async function get(
158168

159169
const download = async (
160170
outputDirectory: string,
161-
verbose: number | boolean = false
171+
verbose: number | boolean = false,
172+
storeZipAs?: string
162173
): Promise<string[]> => {
163174
if (!url) {
164175
url = await getURL()
@@ -170,7 +181,8 @@ export async function get(
170181
url,
171182
stripPrefix || `${artifactName}/`,
172183
outputDirectory,
173-
verbose
184+
verbose,
185+
storeZipAs
174186
)
175187
} catch (e) {
176188
delayInSeconds *= 2

0 commit comments

Comments
 (0)