Compare commits

...

3 Commits

Author SHA1 Message Date
Pelle Wessman
2e4a6309eb
Merge f01118b9a5 into 1c7b2db920 2024-09-06 12:58:30 -07:00
Priya Gupta
1c7b2db920
Fix: windows arm64 setup (#1126)
* Add condition to ensure ZIP extraction targets only Windows ARM64 official archives

* Bumps micromatch from 4.0.5 to 4.0.8
2024-09-06 14:30:34 -05:00
Pelle Wessman
f01118b9a5
Cache fallback with rolling time based expiration 2024-03-13 21:15:09 +01:00
9 changed files with 57 additions and 29 deletions

View File

@ -126,6 +126,8 @@ The action defaults to search for the dependency file (`package-lock.json`, `npm
**Note:** The action does not cache `node_modules` **Note:** The action does not cache `node_modules`
Use `cache-invalidate-after-days` to change the default fallback cache invalidation of every 120 days. Set to 0 to deactivate.
See the examples of using cache for `yarn`/`pnpm` and `cache-dependency-path` input in the [Advanced usage](docs/advanced-usage.md#caching-packages-data) guide. See the examples of using cache for `yarn`/`pnpm` and `cache-dependency-path` input in the [Advanced usage](docs/advanced-usage.md#caching-packages-data) guide.
**Caching npm dependencies:** **Caching npm dependencies:**

View File

@ -132,13 +132,13 @@ describe('cache-restore', () => {
} }
}); });
await restoreCache(packageManager, ''); await restoreCache(packageManager, '', '0');
expect(hashFilesSpy).toHaveBeenCalled(); expect(hashFilesSpy).toHaveBeenCalled();
expect(infoSpy).toHaveBeenCalledWith( expect(infoSpy).toHaveBeenCalledWith(
`Cache restored from key: node-cache-${platform}-${packageManager}-${fileHash}` `Cache restored from key: ${platform}-0-setup-node-${packageManager}-${fileHash}`
); );
expect(infoSpy).not.toHaveBeenCalledWith( expect(infoSpy).not.toHaveBeenCalledWith(
`${packageManager} cache is not found` `Cache not found for input keys: ${platform}-0-setup-node-${packageManager}-${fileHash}, ${platform}-0-setup-node-${packageManager}-`
); );
expect(setOutputSpy).toHaveBeenCalledWith('cache-hit', true); expect(setOutputSpy).toHaveBeenCalledWith('cache-hit', true);
} }
@ -163,10 +163,10 @@ describe('cache-restore', () => {
}); });
restoreCacheSpy.mockImplementationOnce(() => undefined); restoreCacheSpy.mockImplementationOnce(() => undefined);
await restoreCache(packageManager, ''); await restoreCache(packageManager, '', '0');
expect(hashFilesSpy).toHaveBeenCalled(); expect(hashFilesSpy).toHaveBeenCalled();
expect(infoSpy).toHaveBeenCalledWith( expect(infoSpy).toHaveBeenCalledWith(
`${packageManager} cache is not found` `Cache not found for input keys: ${platform}-0-setup-node-${packageManager}-${fileHash}, ${platform}-0-setup-node-${packageManager}-`
); );
expect(setOutputSpy).toHaveBeenCalledWith('cache-hit', false); expect(setOutputSpy).toHaveBeenCalledWith('cache-hit', false);
} }

View File

@ -25,6 +25,8 @@ inputs:
description: 'Used to specify a package manager for caching in the default directory. Supported values: npm, yarn, pnpm.' description: 'Used to specify a package manager for caching in the default directory. Supported values: npm, yarn, pnpm.'
cache-dependency-path: cache-dependency-path:
description: 'Used to specify the path to a dependency file: package-lock.json, yarn.lock, etc. Supports wildcards or a list of file names for caching multiple dependencies.' description: 'Used to specify the path to a dependency file: package-lock.json, yarn.lock, etc. Supports wildcards or a list of file names for caching multiple dependencies.'
cache-invalidate-after-days:
description: 'Used to control how often the fallback cache is invalidated automatically.'
# TODO: add input to control forcing to pull from cloud or dist. # TODO: add input to control forcing to pull from cloud or dist.
# escape valve for someone having issues or needing the absolute latest which isn't cached yet # escape valve for someone having issues or needing the absolute latest which isn't cached yet
outputs: outputs:

29
dist/setup/index.js vendored
View File

@ -93305,7 +93305,7 @@ const path_1 = __importDefault(__nccwpck_require__(1017));
const fs_1 = __importDefault(__nccwpck_require__(7147)); const fs_1 = __importDefault(__nccwpck_require__(7147));
const constants_1 = __nccwpck_require__(9042); const constants_1 = __nccwpck_require__(9042);
const cache_utils_1 = __nccwpck_require__(1678); const cache_utils_1 = __nccwpck_require__(1678);
const restoreCache = (packageManager, cacheDependencyPath) => __awaiter(void 0, void 0, void 0, function* () { const restoreCache = (packageManager, cacheDependencyPath, cacheInvalidateAfterDays) => __awaiter(void 0, void 0, void 0, function* () {
const packageManagerInfo = yield (0, cache_utils_1.getPackageManagerInfo)(packageManager); const packageManagerInfo = yield (0, cache_utils_1.getPackageManagerInfo)(packageManager);
if (!packageManagerInfo) { if (!packageManagerInfo) {
throw new Error(`Caching for '${packageManager}' is not supported`); throw new Error(`Caching for '${packageManager}' is not supported`);
@ -93320,22 +93320,30 @@ const restoreCache = (packageManager, cacheDependencyPath) => __awaiter(void 0,
if (!fileHash) { if (!fileHash) {
throw new Error('Some specified paths were not resolved, unable to cache dependencies.'); throw new Error('Some specified paths were not resolved, unable to cache dependencies.');
} }
const keyPrefix = `node-cache-${platform}-${packageManager}`; const numericCacheInvalidateAfterDays = cacheInvalidateAfterDays && cacheInvalidateAfterDays === '0'
? 0
: (parseInt(cacheInvalidateAfterDays || '', 10) || 120);
const timedInvalidationPrefix = numericCacheInvalidateAfterDays
? Math.floor(Date.now() / (1000 * 60 * 60 * 24 * numericCacheInvalidateAfterDays)) % 1000 // % 1000 to get a rolling prefix between 0 and 999 rather than a possibly infinitely large
: 0;
const keyPrefixBase = `node-cache-${platform}-${packageManager}`;
const keyPrefix = `${keyPrefixBase}-${timedInvalidationPrefix}`;
const primaryKey = `${keyPrefix}-${fileHash}`; const primaryKey = `${keyPrefix}-${fileHash}`;
const restoreKeys = [`${keyPrefix}-`];
core.debug(`primary key is ${primaryKey}`); core.debug(`primary key is ${primaryKey}`);
core.saveState(constants_1.State.CachePrimaryKey, primaryKey); core.saveState(constants_1.State.CachePrimaryKey, primaryKey);
const isManagedByYarnBerry = yield (0, cache_utils_1.repoHasYarnBerryManagedDependencies)(packageManagerInfo, cacheDependencyPath); const isManagedByYarnBerry = yield (0, cache_utils_1.repoHasYarnBerryManagedDependencies)(packageManagerInfo, cacheDependencyPath);
let cacheKey; let cacheKey;
if (isManagedByYarnBerry) { if (isManagedByYarnBerry) {
core.info('All dependencies are managed locally by yarn3, the previous cache can be used'); core.info('All dependencies are managed locally by yarn3, the previous cache can be used');
cacheKey = yield cache.restoreCache(cachePaths, primaryKey, [keyPrefix]); cacheKey = yield cache.restoreCache(cachePaths, primaryKey, [keyPrefixBase]);
} }
else { else {
cacheKey = yield cache.restoreCache(cachePaths, primaryKey); cacheKey = yield cache.restoreCache(cachePaths, primaryKey, restoreKeys);
} }
core.setOutput('cache-hit', Boolean(cacheKey)); core.setOutput('cache-hit', Boolean(cacheKey));
if (!cacheKey) { if (!cacheKey) {
core.info(`${packageManager} cache is not found`); core.info(`Cache not found for input keys: ${[primaryKey, ...restoreKeys].join(', ')}`);
return; return;
} }
core.saveState(constants_1.State.CacheMatchedKey, cacheKey); core.saveState(constants_1.State.CacheMatchedKey, cacheKey);
@ -93883,7 +93891,7 @@ class BaseDistribution {
} }
throw err; throw err;
} }
const toolPath = yield this.extractArchive(downloadPath, info); const toolPath = yield this.extractArchive(downloadPath, info, true);
core.info('Done'); core.info('Done');
return toolPath; return toolPath;
}); });
@ -93933,7 +93941,7 @@ class BaseDistribution {
return toolPath; return toolPath;
}); });
} }
extractArchive(downloadPath, info) { extractArchive(downloadPath, info, isOfficialArchive) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
// //
// Extract // Extract
@ -93948,7 +93956,7 @@ class BaseDistribution {
// on Windows runners without PowerShell Core. // on Windows runners without PowerShell Core.
// //
// For default PowerShell Windows it should contain extension type to unpack it. // For default PowerShell Windows it should contain extension type to unpack it.
if (extension === '.zip') { if (extension === '.zip' && isOfficialArchive) {
const renamedArchive = `${downloadPath}.zip`; const renamedArchive = `${downloadPath}.zip`;
fs_1.default.renameSync(downloadPath, renamedArchive); fs_1.default.renameSync(downloadPath, renamedArchive);
extPath = yield tc.extractZip(renamedArchive); extPath = yield tc.extractZip(renamedArchive);
@ -94186,7 +94194,7 @@ class OfficialBuilds extends base_distribution_1.default {
core.info(`Acquiring ${versionInfo.resolvedVersion} - ${versionInfo.arch} from ${versionInfo.downloadUrl}`); core.info(`Acquiring ${versionInfo.resolvedVersion} - ${versionInfo.arch} from ${versionInfo.downloadUrl}`);
downloadPath = yield tc.downloadTool(versionInfo.downloadUrl, undefined, this.nodeInfo.auth); downloadPath = yield tc.downloadTool(versionInfo.downloadUrl, undefined, this.nodeInfo.auth);
if (downloadPath) { if (downloadPath) {
toolPath = yield this.extractArchive(downloadPath, versionInfo); toolPath = yield this.extractArchive(downloadPath, versionInfo, false);
} }
} }
else { else {
@ -94466,7 +94474,8 @@ function run() {
if (cache && (0, cache_utils_1.isCacheFeatureAvailable)()) { if (cache && (0, cache_utils_1.isCacheFeatureAvailable)()) {
core.saveState(constants_1.State.CachePackageManager, cache); core.saveState(constants_1.State.CachePackageManager, cache);
const cacheDependencyPath = core.getInput('cache-dependency-path'); const cacheDependencyPath = core.getInput('cache-dependency-path');
yield (0, cache_restore_1.restoreCache)(cache, cacheDependencyPath); const cacheInvalidateAfterDays = core.getInput('cache-invalidate-after-days');
yield (0, cache_restore_1.restoreCache)(cache, cacheDependencyPath, cacheInvalidateAfterDays);
} }
const matchersPath = path.join(__dirname, '../..', '.github'); const matchersPath = path.join(__dirname, '../..', '.github');
core.info(`##[add-matcher]${path.join(matchersPath, 'tsc.json')}`); core.info(`##[add-matcher]${path.join(matchersPath, 'tsc.json')}`);

8
package-lock.json generated
View File

@ -4429,12 +4429,12 @@
} }
}, },
"node_modules/micromatch": { "node_modules/micromatch": {
"version": "4.0.5", "version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
"integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"braces": "^3.0.2", "braces": "^3.0.3",
"picomatch": "^2.3.1" "picomatch": "^2.3.1"
}, },
"engines": { "engines": {

View File

@ -14,7 +14,8 @@ import {
export const restoreCache = async ( export const restoreCache = async (
packageManager: string, packageManager: string,
cacheDependencyPath: string cacheDependencyPath: string,
cacheInvalidateAfterDays?: string
) => { ) => {
const packageManagerInfo = await getPackageManagerInfo(packageManager); const packageManagerInfo = await getPackageManagerInfo(packageManager);
if (!packageManagerInfo) { if (!packageManagerInfo) {
@ -37,9 +38,17 @@ export const restoreCache = async (
'Some specified paths were not resolved, unable to cache dependencies.' 'Some specified paths were not resolved, unable to cache dependencies.'
); );
} }
const numericCacheInvalidateAfterDays = cacheInvalidateAfterDays && cacheInvalidateAfterDays === '0'
? 0
: (parseInt(cacheInvalidateAfterDays || '', 10) || 120)
const timedInvalidationPrefix = numericCacheInvalidateAfterDays
? Math.floor(Date.now() / (1000 * 60 * 60 * 24 * numericCacheInvalidateAfterDays)) % 1000 // % 1000 to get a rolling prefix between 0 and 999 rather than a possibly infinitely large
: 0;
const keyPrefix = `node-cache-${platform}-${packageManager}`; const keyPrefixBase = `node-cache-${platform}-${packageManager}`;
const keyPrefix = `${keyPrefixBase}-${timedInvalidationPrefix}`;
const primaryKey = `${keyPrefix}-${fileHash}`; const primaryKey = `${keyPrefix}-${fileHash}`;
const restoreKeys = [`${keyPrefix}-`];
core.debug(`primary key is ${primaryKey}`); core.debug(`primary key is ${primaryKey}`);
core.saveState(State.CachePrimaryKey, primaryKey); core.saveState(State.CachePrimaryKey, primaryKey);
@ -53,15 +62,15 @@ export const restoreCache = async (
core.info( core.info(
'All dependencies are managed locally by yarn3, the previous cache can be used' 'All dependencies are managed locally by yarn3, the previous cache can be used'
); );
cacheKey = await cache.restoreCache(cachePaths, primaryKey, [keyPrefix]); cacheKey = await cache.restoreCache(cachePaths, primaryKey, [keyPrefixBase]);
} else { } else {
cacheKey = await cache.restoreCache(cachePaths, primaryKey); cacheKey = await cache.restoreCache(cachePaths, primaryKey, restoreKeys);
} }
core.setOutput('cache-hit', Boolean(cacheKey)); core.setOutput('cache-hit', Boolean(cacheKey));
if (!cacheKey) { if (!cacheKey) {
core.info(`${packageManager} cache is not found`); core.info(`Cache not found for input keys: ${[primaryKey, ...restoreKeys].join(', ')}`);
return; return;
} }

View File

@ -150,7 +150,7 @@ export default abstract class BaseDistribution {
throw err; throw err;
} }
const toolPath = await this.extractArchive(downloadPath, info); const toolPath = await this.extractArchive(downloadPath, info, true);
core.info('Done'); core.info('Done');
return toolPath; return toolPath;
@ -210,7 +210,8 @@ export default abstract class BaseDistribution {
protected async extractArchive( protected async extractArchive(
downloadPath: string, downloadPath: string,
info: INodeVersionInfo | null info: INodeVersionInfo | null,
isOfficialArchive?: boolean
) { ) {
// //
// Extract // Extract
@ -225,7 +226,7 @@ export default abstract class BaseDistribution {
// on Windows runners without PowerShell Core. // on Windows runners without PowerShell Core.
// //
// For default PowerShell Windows it should contain extension type to unpack it. // For default PowerShell Windows it should contain extension type to unpack it.
if (extension === '.zip') { if (extension === '.zip' && isOfficialArchive) {
const renamedArchive = `${downloadPath}.zip`; const renamedArchive = `${downloadPath}.zip`;
fs.renameSync(downloadPath, renamedArchive); fs.renameSync(downloadPath, renamedArchive);
extPath = await tc.extractZip(renamedArchive); extPath = await tc.extractZip(renamedArchive);

View File

@ -88,7 +88,11 @@ export default class OfficialBuilds extends BaseDistribution {
); );
if (downloadPath) { if (downloadPath) {
toolPath = await this.extractArchive(downloadPath, versionInfo); toolPath = await this.extractArchive(
downloadPath,
versionInfo,
false
);
} }
} else { } else {
core.info( core.info(

View File

@ -62,7 +62,8 @@ export async function run() {
if (cache && isCacheFeatureAvailable()) { if (cache && isCacheFeatureAvailable()) {
core.saveState(State.CachePackageManager, cache); core.saveState(State.CachePackageManager, cache);
const cacheDependencyPath = core.getInput('cache-dependency-path'); const cacheDependencyPath = core.getInput('cache-dependency-path');
await restoreCache(cache, cacheDependencyPath); const cacheInvalidateAfterDays = core.getInput('cache-invalidate-after-days');
await restoreCache(cache, cacheDependencyPath, cacheInvalidateAfterDays);
} }
const matchersPath = path.join(__dirname, '../..', '.github'); const matchersPath = path.join(__dirname, '../..', '.github');