@ronaldphilipsen/local-action 0.0.0-stage → 7.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.checkov.yml +3 -0
- package/.env.example +379 -0
- package/.github/codeql/codeql-config.yml +7 -0
- package/.github/copilot-instructions.md +79 -0
- package/.github/dependabot.yml +32 -0
- package/.github/workflows/codeql-analysis.yml +53 -0
- package/.github/workflows/continuous-delivery.yml +93 -0
- package/.github/workflows/continuous-integration.yml +382 -0
- package/.github/workflows/linter.yml +47 -0
- package/.github/workflows/release.yaml +99 -0
- package/.github/workflows/version-check.yml +44 -0
- package/.grype.yml +151 -0
- package/.markdown-lint.yml +7 -0
- package/.mega-linter.yml +188 -0
- package/.node-version +1 -0
- package/.prettierignore +4 -0
- package/.prettierrc.yml +14 -0
- package/.vscode/launch.json +15 -0
- package/.vscode/mcp.json +9 -0
- package/.vscode/settings.json +15 -0
- package/.vscode/tasks.json +51 -0
- package/.yaml-lint.yml +10 -0
- package/CHANGELOG.md +75 -0
- package/CODEOWNERS +3 -0
- package/CODE_OF_CONDUCT.md +74 -0
- package/CONTRIBUTING.md +140 -0
- package/LICENSE +21 -0
- package/README.md +285 -2
- package/SECURITY.md +42 -0
- package/SUPPORT.md +19 -0
- package/__fixtures__/@actions/core.ts +14 -0
- package/__fixtures__/@actions/exec.ts +9 -0
- package/__fixtures__/@actions/github.ts +3 -0
- package/__fixtures__/@actions/http-client.ts +9 -0
- package/__fixtures__/@actions/io.ts +9 -0
- package/__fixtures__/@octokit/rest.ts +18 -0
- package/__fixtures__/crypto.ts +7 -0
- package/__fixtures__/fs.ts +27 -0
- package/__fixtures__/javascript/no-export/.env.fixture +2 -0
- package/__fixtures__/javascript/no-export/action.yml +16 -0
- package/__fixtures__/javascript/no-export/node_modules/.gitkeep +0 -0
- package/__fixtures__/javascript/no-export/package.json +12 -0
- package/__fixtures__/javascript/no-export/src/index.js +0 -0
- package/__fixtures__/javascript/no-export/src/main.js +0 -0
- package/__fixtures__/javascript/no-import/.env.fixture +2 -0
- package/__fixtures__/javascript/no-import/action.yml +16 -0
- package/__fixtures__/javascript/no-import/node_modules/.gitkeep +0 -0
- package/__fixtures__/javascript/no-import/package.json +12 -0
- package/__fixtures__/javascript/no-import/src/index.js +3 -0
- package/__fixtures__/javascript/no-import/src/main.js +7 -0
- package/__fixtures__/javascript/success/.env.fixture +2 -0
- package/__fixtures__/javascript/success/action.yml +16 -0
- package/__fixtures__/javascript/success/node_modules/.gitkeep +0 -0
- package/__fixtures__/javascript/success/package.json +12 -0
- package/__fixtures__/javascript/success/post/index.js +3 -0
- package/__fixtures__/javascript/success/post/main.js +13 -0
- package/__fixtures__/javascript/success/pre/index.js +3 -0
- package/__fixtures__/javascript/success/pre/main.js +13 -0
- package/__fixtures__/javascript/success/src/index.js +3 -0
- package/__fixtures__/javascript/success/src/main.js +13 -0
- package/__fixtures__/javascript-esm/no-import/.env.fixture +2 -0
- package/__fixtures__/javascript-esm/no-import/action.yml +16 -0
- package/__fixtures__/javascript-esm/no-import/node_modules/.gitkeep +0 -0
- package/__fixtures__/javascript-esm/no-import/package.json +13 -0
- package/__fixtures__/javascript-esm/no-import/src/index.js +3 -0
- package/__fixtures__/javascript-esm/no-import/src/main.js +7 -0
- package/__fixtures__/javascript-esm/success/.env.fixture +2 -0
- package/__fixtures__/javascript-esm/success/action.yml +16 -0
- package/__fixtures__/javascript-esm/success/node_modules/.gitkeep +0 -0
- package/__fixtures__/javascript-esm/success/package.json +13 -0
- package/__fixtures__/javascript-esm/success/post/index.js +3 -0
- package/__fixtures__/javascript-esm/success/post/main.js +13 -0
- package/__fixtures__/javascript-esm/success/pre/index.js +3 -0
- package/__fixtures__/javascript-esm/success/pre/main.js +13 -0
- package/__fixtures__/javascript-esm/success/src/index.js +3 -0
- package/__fixtures__/javascript-esm/success/src/main.js +13 -0
- package/__fixtures__/path.ts +7 -0
- package/__fixtures__/payloads/issue_opened.json +33 -0
- package/__fixtures__/stream/promises.ts +3 -0
- package/__fixtures__/tsconfig-paths.ts +4 -0
- package/__fixtures__/typescript/no-import/.env.fixture +2 -0
- package/__fixtures__/typescript/no-import/action.yml +16 -0
- package/__fixtures__/typescript/no-import/node_modules/.gitkeep +0 -0
- package/__fixtures__/typescript/no-import/package.json +15 -0
- package/__fixtures__/typescript/no-import/src/index.ts +3 -0
- package/__fixtures__/typescript/no-import/src/main.ts +3 -0
- package/__fixtures__/typescript/no-import/tsconfig.json +20 -0
- package/__fixtures__/typescript/success/.env.fixture +2 -0
- package/__fixtures__/typescript/success/action.yml +16 -0
- package/__fixtures__/typescript/success/node_modules/.gitkeep +0 -0
- package/__fixtures__/typescript/success/package.json +15 -0
- package/__fixtures__/typescript/success/post/index.ts +3 -0
- package/__fixtures__/typescript/success/post/main.ts +9 -0
- package/__fixtures__/typescript/success/pre/index.ts +3 -0
- package/__fixtures__/typescript/success/pre/main.ts +9 -0
- package/__fixtures__/typescript/success/src/index.ts +3 -0
- package/__fixtures__/typescript/success/src/main.ts +9 -0
- package/__fixtures__/typescript/success/tsconfig.json +20 -0
- package/__fixtures__/typescript/success-yaml/.env.fixture +2 -0
- package/__fixtures__/typescript/success-yaml/action.yaml +16 -0
- package/__fixtures__/typescript/success-yaml/node_modules/.gitkeep +0 -0
- package/__fixtures__/typescript/success-yaml/package.json +15 -0
- package/__fixtures__/typescript/success-yaml/post/index.ts +3 -0
- package/__fixtures__/typescript/success-yaml/post/main.ts +9 -0
- package/__fixtures__/typescript/success-yaml/pre/index.ts +3 -0
- package/__fixtures__/typescript/success-yaml/pre/main.ts +9 -0
- package/__fixtures__/typescript/success-yaml/src/index.ts +3 -0
- package/__fixtures__/typescript/success-yaml/src/main.ts +9 -0
- package/__fixtures__/typescript/success-yaml/tsconfig.json +20 -0
- package/__fixtures__/typescript-esm/no-export/.env.fixture +2 -0
- package/__fixtures__/typescript-esm/no-export/action.yml +17 -0
- package/__fixtures__/typescript-esm/no-export/node_modules/.gitkeep +0 -0
- package/__fixtures__/typescript-esm/no-export/package.json +17 -0
- package/__fixtures__/typescript-esm/no-export/src/index.ts +0 -0
- package/__fixtures__/typescript-esm/no-export/src/main.ts +0 -0
- package/__fixtures__/typescript-esm/no-export/tsconfig.json +25 -0
- package/__fixtures__/typescript-esm/no-import/.env.fixture +2 -0
- package/__fixtures__/typescript-esm/no-import/action.yml +17 -0
- package/__fixtures__/typescript-esm/no-import/node_modules/.gitkeep +0 -0
- package/__fixtures__/typescript-esm/no-import/package.json +17 -0
- package/__fixtures__/typescript-esm/no-import/src/index.ts +3 -0
- package/__fixtures__/typescript-esm/no-import/src/main.ts +3 -0
- package/__fixtures__/typescript-esm/no-import/tsconfig.json +25 -0
- package/__fixtures__/typescript-esm/success/.env.fixture +2 -0
- package/__fixtures__/typescript-esm/success/action.yml +17 -0
- package/__fixtures__/typescript-esm/success/node_modules/.gitkeep +0 -0
- package/__fixtures__/typescript-esm/success/package.json +17 -0
- package/__fixtures__/typescript-esm/success/post/index.ts +3 -0
- package/__fixtures__/typescript-esm/success/post/main.ts +9 -0
- package/__fixtures__/typescript-esm/success/pre/index.ts +3 -0
- package/__fixtures__/typescript-esm/success/pre/main.ts +9 -0
- package/__fixtures__/typescript-esm/success/src/index.ts +3 -0
- package/__fixtures__/typescript-esm/success/src/main.ts +9 -0
- package/__fixtures__/typescript-esm/success/tsconfig.json +25 -0
- package/__fixtures__/typescript-esm/tsconfig-comments/.env.fixture +2 -0
- package/__fixtures__/typescript-esm/tsconfig-comments/action.yml +17 -0
- package/__fixtures__/typescript-esm/tsconfig-comments/node_modules/.gitkeep +0 -0
- package/__fixtures__/typescript-esm/tsconfig-comments/package.json +17 -0
- package/__fixtures__/typescript-esm/tsconfig-comments/src/index.ts +3 -0
- package/__fixtures__/typescript-esm/tsconfig-comments/src/main.ts +9 -0
- package/__fixtures__/typescript-esm/tsconfig-comments/tsconfig.json +21 -0
- package/__tests__/command.test.ts +224 -0
- package/__tests__/stubs/artifact/internal/client.test.ts +340 -0
- package/__tests__/stubs/artifact/internal/delete/delete-artifact.test.ts +152 -0
- package/__tests__/stubs/artifact/internal/download/download-artifact.test.ts +119 -0
- package/__tests__/stubs/artifact/internal/find/get-artifact.test.ts +237 -0
- package/__tests__/stubs/artifact/internal/find/list-artifacts.test.ts +218 -0
- package/__tests__/stubs/artifact/internal/find/retry-options.test.ts +54 -0
- package/__tests__/stubs/artifact/internal/shared/config.test.ts +91 -0
- package/__tests__/stubs/artifact/internal/shared/errors.test.ts +38 -0
- package/__tests__/stubs/artifact/internal/shared/user-agent.test.ts +22 -0
- package/__tests__/stubs/artifact/internal/upload/path-and-artifact-name-validation.test.ts +53 -0
- package/__tests__/stubs/artifact/internal/upload/upload-artifact.test.ts +126 -0
- package/__tests__/stubs/artifact/internal/upload/upload-zip-specification.test.ts +49 -0
- package/__tests__/stubs/cache/cache.test.ts +357 -0
- package/__tests__/stubs/cache/errors.test.ts +55 -0
- package/__tests__/stubs/cache/internal/cacheUtils.test.ts +109 -0
- package/__tests__/stubs/cache/internal/constants.test.ts +81 -0
- package/__tests__/stubs/cache/internal/tar.test.ts +129 -0
- package/__tests__/stubs/core/core.test.ts +760 -0
- package/__tests__/stubs/core/path-utils.test.ts +45 -0
- package/__tests__/stubs/core/platform.test.ts +89 -0
- package/__tests__/stubs/core/summary.test.ts +455 -0
- package/__tests__/stubs/env.test.ts +72 -0
- package/__tests__/stubs/github/context.test.ts +73 -0
- package/__tests__/stubs/github/github.test.ts +15 -0
- package/__tests__/stubs/github/internal/utils.test.ts +98 -0
- package/__tests__/stubs/github/utils.test.ts +16 -0
- package/__tests__/utils/config.test.ts +78 -0
- package/__tests__/utils/output.test.ts +32 -0
- package/__tests__/utils/package.test.ts +33 -0
- package/badges/coverage.svg +1 -0
- package/bin/local-action.js +121 -0
- package/docs/create-a-job-summary.md +32 -0
- package/docs/debugging-in-vscode.md +27 -0
- package/docs/img/job-summary.png +0 -0
- package/docs/supported-functionality.md +126 -0
- package/eslint.config.mjs +74 -0
- package/jest.config.ts +41 -0
- package/package.json +93 -4
- package/src/command.ts +171 -0
- package/src/commands/run.ts +616 -0
- package/src/index.ts +21 -0
- package/src/stubs/artifact/artifact.ts +17 -0
- package/src/stubs/artifact/internal/client.ts +400 -0
- package/src/stubs/artifact/internal/delete/delete-artifact.ts +126 -0
- package/src/stubs/artifact/internal/download/download-artifact.ts +309 -0
- package/src/stubs/artifact/internal/find/get-artifact.ts +135 -0
- package/src/stubs/artifact/internal/find/list-artifacts.ts +188 -0
- package/src/stubs/artifact/internal/find/retry-options.ts +57 -0
- package/src/stubs/artifact/internal/shared/config.ts +64 -0
- package/src/stubs/artifact/internal/shared/errors.ts +53 -0
- package/src/stubs/artifact/internal/shared/interfaces.ts +195 -0
- package/src/stubs/artifact/internal/shared/user-agent.ts +13 -0
- package/src/stubs/artifact/internal/upload/path-and-artifact-name-validation.ts +92 -0
- package/src/stubs/artifact/internal/upload/upload-artifact.ts +127 -0
- package/src/stubs/artifact/internal/upload/upload-zip-specification.ts +108 -0
- package/src/stubs/artifact/internal/upload/zip.ts +122 -0
- package/src/stubs/cache/cache.ts +334 -0
- package/src/stubs/cache/errors.ts +31 -0
- package/src/stubs/cache/interfaces.ts +4 -0
- package/src/stubs/cache/internal/cacheUtils.ts +207 -0
- package/src/stubs/cache/internal/constants.ts +45 -0
- package/src/stubs/cache/internal/tar.ts +374 -0
- package/src/stubs/cache/options.ts +91 -0
- package/src/stubs/core/core.ts +743 -0
- package/src/stubs/core/path-utils.ts +39 -0
- package/src/stubs/core/platform.ts +132 -0
- package/src/stubs/core/summary.ts +399 -0
- package/src/stubs/env.ts +40 -0
- package/src/stubs/github/context.ts +94 -0
- package/src/stubs/github/github.ts +29 -0
- package/src/stubs/github/interfaces.ts +52 -0
- package/src/stubs/github/internal/utils.ts +83 -0
- package/src/stubs/github/utils.ts +52 -0
- package/src/types/quibble.d.ts +5 -0
- package/src/types.ts +116 -0
- package/src/utils/config.ts +47 -0
- package/src/utils/output.ts +20 -0
- package/src/utils/package.ts +38 -0
- package/tsconfig.base.json +29 -0
- package/tsconfig.eslint.json +22 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Last Reviewed Commit: https://github.com/actions/toolkit/blob/ee91adfbc4f4d1470e176a8d568c0cbdabaf98e0/packages/artifact/src/internal/upload/zip.ts
|
|
3
|
+
* Last Reviewed Date: 2026-01-16
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import archiver from 'archiver'
|
|
7
|
+
import { realpath } from 'fs/promises'
|
|
8
|
+
import * as stream from 'stream'
|
|
9
|
+
import * as core from '../../../core/core.js'
|
|
10
|
+
import { getUploadChunkSize } from '../shared/config.js'
|
|
11
|
+
import type { UploadZipSpecification } from './upload-zip-specification.js'
|
|
12
|
+
|
|
13
|
+
/* istanbul ignore next */
|
|
14
|
+
export const DEFAULT_COMPRESSION_LEVEL = 6
|
|
15
|
+
|
|
16
|
+
// Custom stream transformer so we can set the highWaterMark property
|
|
17
|
+
// See https://github.com/nodejs/node/issues/8855
|
|
18
|
+
/* istanbul ignore next */
|
|
19
|
+
export class ZipUploadStream extends stream.Transform {
|
|
20
|
+
constructor(bufferSize: number) {
|
|
21
|
+
super({
|
|
22
|
+
highWaterMark: bufferSize
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
_transform(chunk: any, enc: any, cb: any): void {
|
|
27
|
+
cb(null, chunk)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Creates a zip upload stream.
|
|
33
|
+
*
|
|
34
|
+
* @param uploadSpecification Upload Specification
|
|
35
|
+
* @param compressionLevel Compression Level
|
|
36
|
+
* @returns Zip Upload Stream
|
|
37
|
+
*/
|
|
38
|
+
/* istanbul ignore next */
|
|
39
|
+
export async function createZipUploadStream(
|
|
40
|
+
uploadSpecification: UploadZipSpecification[],
|
|
41
|
+
compressionLevel: number = DEFAULT_COMPRESSION_LEVEL
|
|
42
|
+
): Promise<ZipUploadStream> {
|
|
43
|
+
core.debug(
|
|
44
|
+
`Creating Artifact archive with compressionLevel: ${compressionLevel}`
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
const zip = archiver.create('zip', {
|
|
48
|
+
highWaterMark: getUploadChunkSize(),
|
|
49
|
+
zlib: { level: compressionLevel }
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
// register callbacks for various events during the zip lifecycle
|
|
53
|
+
zip.on('error', zipErrorCallback)
|
|
54
|
+
zip.on('warning', zipWarningCallback)
|
|
55
|
+
zip.on('finish', zipFinishCallback)
|
|
56
|
+
zip.on('end', zipEndCallback)
|
|
57
|
+
|
|
58
|
+
for (const file of uploadSpecification) {
|
|
59
|
+
if (file.sourcePath !== null) {
|
|
60
|
+
// Check if symlink and resolve the source path
|
|
61
|
+
let sourcePath = file.sourcePath
|
|
62
|
+
if (file.stats.isSymbolicLink())
|
|
63
|
+
sourcePath = await realpath(file.sourcePath)
|
|
64
|
+
|
|
65
|
+
// Add the file to the zip
|
|
66
|
+
zip.file(sourcePath, {
|
|
67
|
+
name: file.destinationPath
|
|
68
|
+
})
|
|
69
|
+
} else {
|
|
70
|
+
// Add a directory to the zip
|
|
71
|
+
zip.append('', { name: file.destinationPath })
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const bufferSize = getUploadChunkSize()
|
|
76
|
+
const zipUploadStream = new ZipUploadStream(bufferSize)
|
|
77
|
+
|
|
78
|
+
core.debug(
|
|
79
|
+
`Zip write high watermark value ${zipUploadStream.writableHighWaterMark}`
|
|
80
|
+
)
|
|
81
|
+
core.debug(
|
|
82
|
+
`Zip read high watermark value ${zipUploadStream.readableHighWaterMark}`
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
zip.pipe(zipUploadStream)
|
|
86
|
+
zip.finalize()
|
|
87
|
+
|
|
88
|
+
return zipUploadStream
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* istanbul ignore next */
|
|
92
|
+
const zipErrorCallback = (error: any): void => {
|
|
93
|
+
core.error('An error has occurred while creating the zip file for upload')
|
|
94
|
+
core.info(error)
|
|
95
|
+
|
|
96
|
+
throw new Error('An error has occurred during zip creation for the artifact')
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/* istanbul ignore next */
|
|
100
|
+
const zipWarningCallback = (error: any): void => {
|
|
101
|
+
if (error.code === 'ENOENT') {
|
|
102
|
+
core.warning(
|
|
103
|
+
'ENOENT warning during artifact zip creation. No such file or directory'
|
|
104
|
+
)
|
|
105
|
+
core.info(error)
|
|
106
|
+
} else {
|
|
107
|
+
core.warning(
|
|
108
|
+
`A non-blocking warning has occurred during artifact zip creation: ${error.code}`
|
|
109
|
+
)
|
|
110
|
+
core.info(error)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/* istanbul ignore next */
|
|
115
|
+
const zipFinishCallback = (): void => {
|
|
116
|
+
core.debug('Zip stream for upload has finished.')
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/* istanbul ignore next */
|
|
120
|
+
const zipEndCallback = (): void => {
|
|
121
|
+
core.debug('Zip stream for upload has ended.')
|
|
122
|
+
}
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Last Reviewed Commit: 06f7fd9df15088233d2593af69ea710eb2fc516b
|
|
3
|
+
* Last Reviewed Date: 2026-01-16
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as core from '@actions/core'
|
|
7
|
+
import * as fs from 'fs'
|
|
8
|
+
import * as path from 'path'
|
|
9
|
+
import { EnvMeta } from '../env.js'
|
|
10
|
+
import { ReserveCacheError, ValidationError } from './errors.js'
|
|
11
|
+
import * as utils from './internal/cacheUtils.js'
|
|
12
|
+
import { createTar, extractTar, listTar } from './internal/tar.js'
|
|
13
|
+
import { DownloadOptions, UploadOptions } from './options.js'
|
|
14
|
+
|
|
15
|
+
export const CACHE_STUBS = {
|
|
16
|
+
restoreCache,
|
|
17
|
+
saveCache
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Validates at least one path is provided.
|
|
22
|
+
*
|
|
23
|
+
* @param paths List of paths to check
|
|
24
|
+
*/
|
|
25
|
+
/* istanbul ignore next */
|
|
26
|
+
function checkPaths(paths: string[]): void {
|
|
27
|
+
if (!paths || paths.length === 0)
|
|
28
|
+
throw new ValidationError(
|
|
29
|
+
`Path Validation Error: At least one directory or file path is required`
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Validates the provided key is sufficient length and contains valid characters
|
|
35
|
+
*
|
|
36
|
+
* @param key Key to check
|
|
37
|
+
*/
|
|
38
|
+
/* istanbul ignore next */
|
|
39
|
+
function checkKey(key: string): void {
|
|
40
|
+
if (key.length > 512)
|
|
41
|
+
throw new ValidationError(
|
|
42
|
+
`Key Validation Error: ${key} cannot be larger than 512 characters.`
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
const regex = /^[^,]*$/
|
|
46
|
+
|
|
47
|
+
if (!regex.test(key))
|
|
48
|
+
throw new ValidationError(
|
|
49
|
+
`Key Validation Error: ${key} cannot contain commas.`
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Restores cache from keys
|
|
55
|
+
*
|
|
56
|
+
* @param paths List of file paths to restore from the cache
|
|
57
|
+
* @param primaryKey Explicit key for restoring the cache. Lookup is done with prefix matching.
|
|
58
|
+
* @param restoreKeys Optional ordered list of keys to use for restoring the cache if no cache hit occurred for primaryKey
|
|
59
|
+
* @param downloadOptions Cache download options
|
|
60
|
+
* @param enableCrossOsArchive Optional boolean enabled to restore on Windows any cache created on any platform
|
|
61
|
+
* @returns Key for the cache hit, otherwise undefined
|
|
62
|
+
*
|
|
63
|
+
* @remarks
|
|
64
|
+
*
|
|
65
|
+
* - Checks for required environment variables.
|
|
66
|
+
* - Caches are only managed locally...removed check for service version.
|
|
67
|
+
*/
|
|
68
|
+
export async function restoreCache(
|
|
69
|
+
paths: string[],
|
|
70
|
+
primaryKey: string,
|
|
71
|
+
restoreKeys?: string[],
|
|
72
|
+
options?: DownloadOptions,
|
|
73
|
+
enableCrossOsArchive = false
|
|
74
|
+
): Promise<string | undefined> {
|
|
75
|
+
if (!process.env.LOCAL_ACTION_WORKSPACE)
|
|
76
|
+
throw new Error(
|
|
77
|
+
'LOCAL_ACTION_WORKSPACE must be set when interacting with @actions/cache!'
|
|
78
|
+
)
|
|
79
|
+
if (!process.env.LOCAL_ACTION_CACHE_PATH)
|
|
80
|
+
throw new Error(
|
|
81
|
+
'LOCAL_ACTION_CACHE_PATH must be set when interacting with @actions/cache!'
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
checkPaths(paths)
|
|
85
|
+
|
|
86
|
+
return await restoreCacheFromFilesystem(
|
|
87
|
+
paths,
|
|
88
|
+
primaryKey,
|
|
89
|
+
restoreKeys,
|
|
90
|
+
options,
|
|
91
|
+
enableCrossOsArchive
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Restores cache from local filesystem
|
|
97
|
+
*
|
|
98
|
+
* @param paths List of file paths to restore from the cache
|
|
99
|
+
* @param primaryKey Explicit key for restoring the cache. Lookup is done with prefix matching
|
|
100
|
+
* @param restoreKeys Optional ordered list of keys to use for restoring the cache if no cache hit occurred for primaryKey
|
|
101
|
+
* @param downloadOptions Cache download options
|
|
102
|
+
* @param enableCrossOsArchive Optional boolean enabled to restore on Windows any cache created on any platform
|
|
103
|
+
* @returns Key for the cache hit, otherwise undefined
|
|
104
|
+
*
|
|
105
|
+
* @remarks
|
|
106
|
+
*
|
|
107
|
+
* - Replaces v1 and v2 caching implementations.
|
|
108
|
+
* - Download options, other than `lookupOnly`, are ignored.
|
|
109
|
+
*/
|
|
110
|
+
async function restoreCacheFromFilesystem(
|
|
111
|
+
paths: string[],
|
|
112
|
+
primaryKey: string,
|
|
113
|
+
restoreKeys?: string[],
|
|
114
|
+
options?: DownloadOptions,
|
|
115
|
+
/* istanbul ignore next */
|
|
116
|
+
enableCrossOsArchive = false
|
|
117
|
+
): Promise<string | undefined> {
|
|
118
|
+
const keys = [primaryKey, ...(restoreKeys ?? [])]
|
|
119
|
+
|
|
120
|
+
core.debug('Resolved Keys:')
|
|
121
|
+
core.debug(JSON.stringify(keys))
|
|
122
|
+
|
|
123
|
+
if (keys.length > 10)
|
|
124
|
+
throw new ValidationError(
|
|
125
|
+
`Key Validation Error: Keys are limited to a maximum of 10.`
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
for (const key of keys) checkKey(key)
|
|
129
|
+
|
|
130
|
+
let archivePath: string
|
|
131
|
+
let matchedKey = undefined
|
|
132
|
+
let matchedCache = undefined
|
|
133
|
+
|
|
134
|
+
try {
|
|
135
|
+
const compressionMethod = await utils.getCompressionMethod()
|
|
136
|
+
const cacheVersion = utils.getCacheVersion(
|
|
137
|
+
paths,
|
|
138
|
+
compressionMethod,
|
|
139
|
+
enableCrossOsArchive
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
const caches = fs
|
|
143
|
+
.readdirSync(process.env.LOCAL_ACTION_CACHE_PATH!)
|
|
144
|
+
.filter(file => file.endsWith('.cache'))
|
|
145
|
+
.map(file => file.replace('.cache', ''))
|
|
146
|
+
|
|
147
|
+
// Check the caches for a matching key
|
|
148
|
+
for (const key of keys) {
|
|
149
|
+
const cache = caches.find(c => c === key || c.startsWith(key))
|
|
150
|
+
|
|
151
|
+
if (cache) {
|
|
152
|
+
matchedKey = key
|
|
153
|
+
matchedCache = cache
|
|
154
|
+
break
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (matchedCache === undefined) {
|
|
159
|
+
core.debug(
|
|
160
|
+
`Cache not found for version ${cacheVersion} of keys: ${keys.join(
|
|
161
|
+
', '
|
|
162
|
+
)}`
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
return undefined
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (primaryKey !== matchedKey)
|
|
169
|
+
core.info(`Cache hit for restore-key: ${matchedKey}`)
|
|
170
|
+
else core.info(`Cache hit for: ${matchedKey}`)
|
|
171
|
+
|
|
172
|
+
if (options?.lookupOnly) {
|
|
173
|
+
core.info('Lookup only - skipping download')
|
|
174
|
+
return matchedKey
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
archivePath = path.join(
|
|
178
|
+
process.env.LOCAL_ACTION_CACHE_PATH!,
|
|
179
|
+
`${matchedCache}.cache`
|
|
180
|
+
)
|
|
181
|
+
core.debug(`Archive path: ${archivePath}`)
|
|
182
|
+
|
|
183
|
+
const archiveFileSize = utils.getArchiveFileSizeInBytes(archivePath)
|
|
184
|
+
core.info(
|
|
185
|
+
`Cache Size: ~${Math.round(
|
|
186
|
+
archiveFileSize / (1024 * 1024)
|
|
187
|
+
)} MB (${archiveFileSize} B)`
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
if (core.isDebug()) await listTar(archivePath, compressionMethod)
|
|
191
|
+
await extractTar(archivePath, compressionMethod)
|
|
192
|
+
|
|
193
|
+
core.info('Cache restored successfully')
|
|
194
|
+
return matchedKey
|
|
195
|
+
} catch (error: any) {
|
|
196
|
+
core.warning(`Failed to restore: ${error.message}`)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return undefined
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Saves a list of files with the specified key
|
|
204
|
+
*
|
|
205
|
+
* @param paths List of file paths to be cached
|
|
206
|
+
* @param key Explicit key for restoring the cache
|
|
207
|
+
* @param enableCrossOsArchive Optional boolean enabled to save cache on Windows which could be restored on any platform
|
|
208
|
+
* @param options Cache upload options
|
|
209
|
+
* @returns Cache ID if the cache was saved successfully
|
|
210
|
+
* @throws Error if save fails
|
|
211
|
+
*
|
|
212
|
+
* @remarks
|
|
213
|
+
*
|
|
214
|
+
* - Checks for required environment variables.
|
|
215
|
+
* - Caches are only managed locally...removed check for service version.
|
|
216
|
+
*/
|
|
217
|
+
/* istanbul ignore next */
|
|
218
|
+
export async function saveCache(
|
|
219
|
+
paths: string[],
|
|
220
|
+
key: string,
|
|
221
|
+
options?: UploadOptions,
|
|
222
|
+
enableCrossOsArchive = false
|
|
223
|
+
): Promise<number> {
|
|
224
|
+
if (!process.env.LOCAL_ACTION_WORKSPACE)
|
|
225
|
+
throw new Error(
|
|
226
|
+
'LOCAL_ACTION_WORKSPACE must be set when interacting with @actions/cache!'
|
|
227
|
+
)
|
|
228
|
+
if (!process.env.LOCAL_ACTION_CACHE_PATH)
|
|
229
|
+
throw new Error(
|
|
230
|
+
'LOCAL_ACTION_CACHE_PATH must be set when interacting with @actions/cache!'
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
checkPaths(paths)
|
|
234
|
+
checkKey(key)
|
|
235
|
+
|
|
236
|
+
return await saveCacheToFilesystem(paths, key, options, enableCrossOsArchive)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Saves cache to local filesystem
|
|
241
|
+
*
|
|
242
|
+
* @param paths List of file paths to be cached
|
|
243
|
+
* @param key Explicit key for restoring the cache
|
|
244
|
+
* @param options Cache upload options
|
|
245
|
+
* @param enableCrossOsArchive Optional boolean enabled to save cache on Windows which could be restored on any platform
|
|
246
|
+
* @returns Cache ID if the cache was saved successfully
|
|
247
|
+
*
|
|
248
|
+
* @remarks
|
|
249
|
+
*
|
|
250
|
+
* - Replaces v1 and v2 caching implementations.
|
|
251
|
+
* - Azure SDK settings and upload options are ignored.
|
|
252
|
+
* - Cache reserving is replaced with file conflict checks.
|
|
253
|
+
*/
|
|
254
|
+
async function saveCacheToFilesystem(
|
|
255
|
+
paths: string[],
|
|
256
|
+
key: string,
|
|
257
|
+
options?: UploadOptions,
|
|
258
|
+
/* istanbul ignore next */
|
|
259
|
+
enableCrossOsArchive = false
|
|
260
|
+
): Promise<number> {
|
|
261
|
+
const compressionMethod = await utils.getCompressionMethod()
|
|
262
|
+
const cachePaths = await utils.resolvePaths(paths)
|
|
263
|
+
|
|
264
|
+
core.debug('Cache Paths:')
|
|
265
|
+
core.debug(`${JSON.stringify(cachePaths)}`)
|
|
266
|
+
|
|
267
|
+
if (cachePaths.length === 0)
|
|
268
|
+
throw new Error(
|
|
269
|
+
`Path Validation Error: Path(s) specified in the action for caching do(es) not exist, hence no cache is being saved.`
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
const archiveFolder = await utils.createTempDirectory()
|
|
273
|
+
const archivePath = path.join(
|
|
274
|
+
archiveFolder,
|
|
275
|
+
utils.getCacheFileName(compressionMethod)
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
core.debug(`Archive Path: ${archivePath}`)
|
|
279
|
+
|
|
280
|
+
try {
|
|
281
|
+
await createTar(archiveFolder, cachePaths, compressionMethod)
|
|
282
|
+
|
|
283
|
+
if (core.isDebug()) await listTar(archivePath, compressionMethod)
|
|
284
|
+
|
|
285
|
+
const archiveSizeBytes = utils.getArchiveFileSizeInBytes(archivePath)
|
|
286
|
+
core.debug(`File Size: ${archiveSizeBytes}`)
|
|
287
|
+
|
|
288
|
+
// Attempt to reserve the cache. In local-action, this is done by checking
|
|
289
|
+
// if there is an existing file with the same name. If so, we fail the save.
|
|
290
|
+
core.debug('Reserving Cache')
|
|
291
|
+
const version = utils.getCacheVersion(
|
|
292
|
+
paths,
|
|
293
|
+
compressionMethod,
|
|
294
|
+
enableCrossOsArchive
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
if (
|
|
298
|
+
fs.existsSync(
|
|
299
|
+
path.join(
|
|
300
|
+
process.env.LOCAL_ACTION_CACHE_PATH!,
|
|
301
|
+
`${key}-${version}.cache`
|
|
302
|
+
)
|
|
303
|
+
)
|
|
304
|
+
)
|
|
305
|
+
throw new ReserveCacheError(
|
|
306
|
+
`Unable to reserve cache with key ${key}, another job may be creating this cache.`
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
// Copy the archive to the cache folder. In local-action, this is simply
|
|
310
|
+
// copying the file.
|
|
311
|
+
core.debug(`Attempting to upload cache located at: ${archivePath}`)
|
|
312
|
+
fs.copyFileSync(
|
|
313
|
+
archivePath,
|
|
314
|
+
path.join(process.env.LOCAL_ACTION_CACHE_PATH!, `${key}-${version}.cache`)
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
// Generate a cache ID. This can just be the next available ID based on
|
|
318
|
+
// existing caches.
|
|
319
|
+
return (
|
|
320
|
+
Math.max(...Object.keys(EnvMeta.caches).map(key => parseInt(key))) + 1
|
|
321
|
+
)
|
|
322
|
+
} catch (error: any) {
|
|
323
|
+
core.warning(`Failed to save: ${error.message}`)
|
|
324
|
+
} finally {
|
|
325
|
+
// Try to delete the original archive to save space
|
|
326
|
+
try {
|
|
327
|
+
await utils.unlinkFile(archivePath)
|
|
328
|
+
} catch (error) {
|
|
329
|
+
core.debug(`Failed to delete archive: ${error}`)
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return -1
|
|
334
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Caching validation error
|
|
3
|
+
*/
|
|
4
|
+
export class ValidationError extends Error {
|
|
5
|
+
constructor(message: string) {
|
|
6
|
+
super(message)
|
|
7
|
+
this.name = 'ValidationError'
|
|
8
|
+
Object.setPrototypeOf(this, ValidationError.prototype)
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Cache reservation error
|
|
14
|
+
*/
|
|
15
|
+
export class ReserveCacheError extends Error {
|
|
16
|
+
constructor(message: string) {
|
|
17
|
+
super(message)
|
|
18
|
+
this.name = 'ReserveCacheError'
|
|
19
|
+
Object.setPrototypeOf(this, ReserveCacheError.prototype)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Invalid response error
|
|
25
|
+
*/
|
|
26
|
+
export class InvalidResponseError extends Error {
|
|
27
|
+
constructor(message: string) {
|
|
28
|
+
super(message)
|
|
29
|
+
this.name = 'InvalidResponseError'
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Last Reviewed Commit: 267841d7bd659368f7382ca22d5ca0b2af68f0b3
|
|
3
|
+
* Last Reviewed Date: 2026-01-16
|
|
4
|
+
*
|
|
5
|
+
* @remarks
|
|
6
|
+
*
|
|
7
|
+
* - Deleted assertDefined function
|
|
8
|
+
* - Deleted getRuntimeToken function
|
|
9
|
+
*/
|
|
10
|
+
import * as core from '@actions/core'
|
|
11
|
+
import * as exec from '@actions/exec'
|
|
12
|
+
import * as glob from '@actions/glob'
|
|
13
|
+
import * as io from '@actions/io'
|
|
14
|
+
import * as crypto from 'crypto'
|
|
15
|
+
import * as fs from 'fs'
|
|
16
|
+
import * as path from 'path'
|
|
17
|
+
import * as semver from 'semver'
|
|
18
|
+
import * as util from 'util'
|
|
19
|
+
import {
|
|
20
|
+
CacheFilename,
|
|
21
|
+
CompressionMethod,
|
|
22
|
+
GnuTarPathOnWindows
|
|
23
|
+
} from './constants.js'
|
|
24
|
+
|
|
25
|
+
const versionSalt = '1.0'
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Creates a temporary directory and returns the path
|
|
29
|
+
*
|
|
30
|
+
* @returns Path to the temporary directory
|
|
31
|
+
*
|
|
32
|
+
* @remarks
|
|
33
|
+
*
|
|
34
|
+
* - Replaces location with LOCAL_ACTION_WORKSPACE/actions/temp
|
|
35
|
+
*/
|
|
36
|
+
export async function createTempDirectory(): Promise<string> {
|
|
37
|
+
const tempDirectory = path.join(
|
|
38
|
+
process.env.LOCAL_ACTION_WORKSPACE!,
|
|
39
|
+
'actions',
|
|
40
|
+
'temp'
|
|
41
|
+
)
|
|
42
|
+
const dest = path.join(tempDirectory, crypto.randomUUID())
|
|
43
|
+
|
|
44
|
+
await io.mkdirP(dest)
|
|
45
|
+
|
|
46
|
+
return dest
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Gets the size of the archive file in bytes
|
|
51
|
+
*
|
|
52
|
+
* @param filePath Path to the archive file
|
|
53
|
+
* @returns Size of the archive file in bytes
|
|
54
|
+
*/
|
|
55
|
+
/* istanbul ignore next */
|
|
56
|
+
export function getArchiveFileSizeInBytes(filePath: string): number {
|
|
57
|
+
return fs.statSync(filePath).size
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Resolves the paths to be cached
|
|
62
|
+
*
|
|
63
|
+
* @param patterns Array of glob patterns
|
|
64
|
+
* @returns Array of resolved paths
|
|
65
|
+
*/
|
|
66
|
+
/* istanbul ignore next */
|
|
67
|
+
export async function resolvePaths(patterns: string[]): Promise<string[]> {
|
|
68
|
+
const paths: string[] = []
|
|
69
|
+
const workspace = process.env['GITHUB_WORKSPACE'] ?? process.cwd()
|
|
70
|
+
const globber = await glob.create(patterns.join('\n'), {
|
|
71
|
+
implicitDescendants: false
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
for await (const file of globber.globGenerator()) {
|
|
75
|
+
const relativeFile = path
|
|
76
|
+
.relative(workspace, file)
|
|
77
|
+
.replace(new RegExp(`\\${path.sep}`, 'g'), '/')
|
|
78
|
+
core.debug(`Matched: ${relativeFile}`)
|
|
79
|
+
|
|
80
|
+
// Paths are made relative so the tar entries are all relative to the root
|
|
81
|
+
// of the workspace.
|
|
82
|
+
if (relativeFile === '') paths.push('.')
|
|
83
|
+
else paths.push(`${relativeFile}`)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return paths
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Deletes a file
|
|
91
|
+
*
|
|
92
|
+
* @param filePath Path to the file
|
|
93
|
+
* @returns Promise that resolves when the file is deleted
|
|
94
|
+
*/
|
|
95
|
+
/* istanbul ignore next */
|
|
96
|
+
export async function unlinkFile(filePath: fs.PathLike): Promise<void> {
|
|
97
|
+
return util.promisify(fs.unlink)(filePath)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Gets the version of an application by executing it with the provided
|
|
102
|
+
* arguments
|
|
103
|
+
*
|
|
104
|
+
* @param app Application to get the version of
|
|
105
|
+
* @param additionalArgs Additional arguments to pass to the application
|
|
106
|
+
* @returns Version of the application
|
|
107
|
+
*/
|
|
108
|
+
/* istanbul ignore next */
|
|
109
|
+
async function getVersion(
|
|
110
|
+
app: string,
|
|
111
|
+
additionalArgs: string[] = []
|
|
112
|
+
): Promise<string> {
|
|
113
|
+
let versionOutput = ''
|
|
114
|
+
additionalArgs.push('--version')
|
|
115
|
+
core.debug(`Checking ${app} ${additionalArgs.join(' ')}`)
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
await exec.exec(`${app}`, additionalArgs, {
|
|
119
|
+
ignoreReturnCode: true,
|
|
120
|
+
silent: true,
|
|
121
|
+
listeners: {
|
|
122
|
+
stdout: (data: Buffer): string => (versionOutput += data.toString()),
|
|
123
|
+
stderr: (data: Buffer): string => (versionOutput += data.toString())
|
|
124
|
+
}
|
|
125
|
+
})
|
|
126
|
+
} catch (err: any) {
|
|
127
|
+
core.debug(err.message)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
versionOutput = versionOutput.trim()
|
|
131
|
+
core.debug(versionOutput)
|
|
132
|
+
|
|
133
|
+
return versionOutput
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Gets the compression method to use when creating caches.
|
|
138
|
+
*
|
|
139
|
+
* Use zstandard if possible to maximize cache performance.
|
|
140
|
+
*/
|
|
141
|
+
/* istanbul ignore next */
|
|
142
|
+
export async function getCompressionMethod(): Promise<CompressionMethod> {
|
|
143
|
+
const versionOutput = await getVersion('zstd', ['--quiet'])
|
|
144
|
+
const version = semver.clean(versionOutput)
|
|
145
|
+
|
|
146
|
+
core.debug(`zstd version: ${version}`)
|
|
147
|
+
|
|
148
|
+
if (versionOutput === '') return CompressionMethod.Gzip
|
|
149
|
+
else return CompressionMethod.ZstdWithoutLong
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Gets the cache file name based on the compression method
|
|
154
|
+
*
|
|
155
|
+
* @param compressionMethod Compression method
|
|
156
|
+
* @returns Cache file name
|
|
157
|
+
*/
|
|
158
|
+
/* istanbul ignore next */
|
|
159
|
+
export function getCacheFileName(compressionMethod: CompressionMethod): string {
|
|
160
|
+
return compressionMethod === CompressionMethod.Gzip
|
|
161
|
+
? CacheFilename.Gzip
|
|
162
|
+
: CacheFilename.Zstd
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Gets the path to GNU tar on Windows, if available
|
|
167
|
+
*
|
|
168
|
+
* @returns Path to GNU tar on Windows, or empty string if not available
|
|
169
|
+
*/
|
|
170
|
+
/* istanbul ignore next */
|
|
171
|
+
export async function getGnuTarPathOnWindows(): Promise<string> {
|
|
172
|
+
if (fs.existsSync(GnuTarPathOnWindows)) return GnuTarPathOnWindows
|
|
173
|
+
|
|
174
|
+
const versionOutput = await getVersion('tar')
|
|
175
|
+
return versionOutput.toLowerCase().includes('gnu tar') ? io.which('tar') : ''
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Get the cache version
|
|
180
|
+
*
|
|
181
|
+
* @param paths Paths included in the cache
|
|
182
|
+
* @param compressionMethod Compression method
|
|
183
|
+
* @param enableCrossOsArchive Enable cross OS archive
|
|
184
|
+
* @returns Cache version
|
|
185
|
+
*/
|
|
186
|
+
/* istanbul ignore next */
|
|
187
|
+
export function getCacheVersion(
|
|
188
|
+
paths: string[],
|
|
189
|
+
compressionMethod?: CompressionMethod,
|
|
190
|
+
enableCrossOsArchive = false
|
|
191
|
+
): string {
|
|
192
|
+
// don't pass changes upstream
|
|
193
|
+
const components = paths.slice()
|
|
194
|
+
|
|
195
|
+
// Add compression method to cache version to restore
|
|
196
|
+
// compressed cache as per compression method
|
|
197
|
+
if (compressionMethod) components.push(compressionMethod)
|
|
198
|
+
|
|
199
|
+
// Only check for windows platforms if enableCrossOsArchive is false
|
|
200
|
+
if (process.platform === 'win32' && !enableCrossOsArchive)
|
|
201
|
+
components.push('windows-only')
|
|
202
|
+
|
|
203
|
+
// Add salt to cache version to support breaking changes in cache entry
|
|
204
|
+
components.push(versionSalt)
|
|
205
|
+
|
|
206
|
+
return crypto.createHash('sha256').update(components.join('|')).digest('hex')
|
|
207
|
+
}
|