add devEngines.packageManager detection logic for npm auto-caching

This commit is contained in:
Priyagupta108 2025-09-26 13:31:00 +05:30
parent 7024c214c6
commit 8f225c52ce
3 changed files with 141 additions and 27 deletions

View File

@ -285,13 +285,12 @@ describe('main tests', () => {
}); });
describe('cache feature tests', () => { describe('cache feature tests', () => {
it('Should enable caching with the resolved package manager from packageManager field in package.json when the cache input is not provided', async () => { it('Should enable caching when packageManager is npm and cache input is not provided', async () => {
inputs['package-manager-cache'] = 'true'; inputs['package-manager-cache'] = 'true';
inputs['cache'] = ''; // No cache input is provided inputs['cache'] = '';
isCacheActionAvailable.mockImplementation(() => true); isCacheActionAvailable.mockImplementation(() => true);
inSpy.mockImplementation(name => inputs[name]); inSpy.mockImplementation(name => inputs[name]);
const readFileSpy = jest.spyOn(fs, 'readFileSync'); const readFileSpy = jest.spyOn(fs, 'readFileSync');
readFileSpy.mockImplementation(() => readFileSpy.mockImplementation(() =>
JSON.stringify({ JSON.stringify({
@ -304,16 +303,106 @@ describe('main tests', () => {
expect(saveStateSpy).toHaveBeenCalledWith(expect.anything(), 'npm'); expect(saveStateSpy).toHaveBeenCalledWith(expect.anything(), 'npm');
}); });
it('Should not enable caching if the packageManager field is missing in package.json and the cache input is not provided', async () => { it('Should enable caching when devEngines.packageManager.name is "npm" and cache input is not provided', async () => {
inputs['package-manager-cache'] = 'true'; inputs['package-manager-cache'] = 'true';
inputs['cache'] = ''; // No cache input is provided inputs['cache'] = '';
isCacheActionAvailable.mockImplementation(() => true);
inSpy.mockImplementation(name => inputs[name]); inSpy.mockImplementation(name => inputs[name]);
const readFileSpy = jest.spyOn(fs, 'readFileSync'); const readFileSpy = jest.spyOn(fs, 'readFileSync');
readFileSpy.mockImplementation(() => readFileSpy.mockImplementation(() =>
JSON.stringify({ JSON.stringify({
//packageManager field is not present devEngines: {
packageManager: {name: 'npm'}
}
})
);
await main.run();
expect(saveStateSpy).toHaveBeenCalledWith(expect.anything(), 'npm');
});
it('Should enable caching when devEngines.packageManager is array and one entry has name "npm"', async () => {
inputs['package-manager-cache'] = 'true';
inputs['cache'] = '';
isCacheActionAvailable.mockImplementation(() => true);
inSpy.mockImplementation(name => inputs[name]);
const readFileSpy = jest.spyOn(fs, 'readFileSync');
readFileSpy.mockImplementation(() =>
JSON.stringify({
devEngines: {
packageManager: [{name: 'pnpm'}, {name: 'npm'}]
}
})
);
await main.run();
expect(saveStateSpy).toHaveBeenCalledWith(expect.anything(), 'npm');
});
it('Should not enable caching if packageManager is "pnpm@8.0.0" and cache input is not provided', async () => {
inputs['package-manager-cache'] = 'true';
inputs['cache'] = '';
inSpy.mockImplementation(name => inputs[name]);
const readFileSpy = jest.spyOn(fs, 'readFileSync');
readFileSpy.mockImplementation(() =>
JSON.stringify({
packageManager: 'pnpm@8.0.0'
})
);
await main.run();
expect(saveStateSpy).not.toHaveBeenCalled();
});
it('Should not enable caching if devEngines.packageManager.name is "pnpm"', async () => {
inputs['package-manager-cache'] = 'true';
inputs['cache'] = '';
inSpy.mockImplementation(name => inputs[name]);
const readFileSpy = jest.spyOn(fs, 'readFileSync');
readFileSpy.mockImplementation(() =>
JSON.stringify({
devEngines: {
packageManager: {name: 'pnpm'}
}
})
);
await main.run();
expect(saveStateSpy).not.toHaveBeenCalled();
});
it('Should not enable caching if devEngines.packageManager is array without "npm"', async () => {
inputs['package-manager-cache'] = 'true';
inputs['cache'] = '';
inSpy.mockImplementation(name => inputs[name]);
const readFileSpy = jest.spyOn(fs, 'readFileSync');
readFileSpy.mockImplementation(() =>
JSON.stringify({
devEngines: {
packageManager: [{name: 'pnpm'}, {name: 'yarn'}]
}
})
);
await main.run();
expect(saveStateSpy).not.toHaveBeenCalled();
});
it('Should not enable caching if packageManager field is missing in package.json and cache input is not provided', async () => {
inputs['package-manager-cache'] = 'true';
inputs['cache'] = '';
inSpy.mockImplementation(name => inputs[name]);
const readFileSpy = jest.spyOn(fs, 'readFileSync');
readFileSpy.mockImplementation(() =>
JSON.stringify({
// packageManager field is not present
}) })
); );
@ -324,24 +413,18 @@ describe('main tests', () => {
it('Should skip caching when package-manager-cache is false', async () => { it('Should skip caching when package-manager-cache is false', async () => {
inputs['package-manager-cache'] = 'false'; inputs['package-manager-cache'] = 'false';
inputs['cache'] = ''; // No cache input is provided inputs['cache'] = '';
inSpy.mockImplementation(name => inputs[name]); inSpy.mockImplementation(name => inputs[name]);
await main.run(); await main.run();
expect(saveStateSpy).not.toHaveBeenCalled(); expect(saveStateSpy).not.toHaveBeenCalled();
}); });
it('Should enable caching with cache input explicitly provided', async () => { it('Should enable caching with cache input explicitly provided', async () => {
inputs['package-manager-cache'] = 'true'; inputs['package-manager-cache'] = 'true';
inputs['cache'] = 'npm'; // Explicit cache input provided inputs['cache'] = 'npm';
inSpy.mockImplementation(name => inputs[name]); inSpy.mockImplementation(name => inputs[name]);
isCacheActionAvailable.mockImplementation(() => true); isCacheActionAvailable.mockImplementation(() => true);
await main.run(); await main.run();
expect(saveStateSpy).toHaveBeenCalledWith(expect.anything(), 'npm'); expect(saveStateSpy).toHaveBeenCalledWith(expect.anything(), 'npm');
}); });
}); });

27
dist/setup/index.js vendored
View File

@ -99838,17 +99838,32 @@ function resolveVersionInput() {
return version; return version;
} }
function getNameFromPackageManagerField() { function getNameFromPackageManagerField() {
// Enable auto-cache for npm var _a;
const npmRegex = /^(\^)?npm(@.*)?$/; // matches "npm", "npm@...", "^npm@..."
try { try {
const packageJson = JSON.parse(fs_1.default.readFileSync(path.join(process.env.GITHUB_WORKSPACE, 'package.json'), 'utf-8')); const packageJson = JSON.parse(fs_1.default.readFileSync(path.join(process.env.GITHUB_WORKSPACE, 'package.json'), 'utf-8'));
const pm = packageJson.packageManager; // Check devEngines.packageManager first (object or array)
if (typeof pm === 'string') { const devPM = (_a = packageJson === null || packageJson === void 0 ? void 0 : packageJson.devEngines) === null || _a === void 0 ? void 0 : _a.packageManager;
const match = pm.match(/^(?:\^)?(npm)@/); if (devPM) {
return match ? match[1] : undefined; if (Array.isArray(devPM)) {
for (const obj of devPM) {
if (typeof (obj === null || obj === void 0 ? void 0 : obj.name) === 'string' && npmRegex.test(obj.name)) {
return 'npm';
}
}
}
else if (typeof (devPM === null || devPM === void 0 ? void 0 : devPM.name) === 'string' && npmRegex.test(devPM.name)) {
return 'npm';
}
}
// Check top-level packageManager
const topLevelPM = packageJson === null || packageJson === void 0 ? void 0 : packageJson.packageManager;
if (typeof topLevelPM === 'string' && npmRegex.test(topLevelPM)) {
return 'npm';
} }
return undefined; return undefined;
} }
catch (err) { catch (_b) {
return undefined; return undefined;
} }
} }

View File

@ -138,7 +138,7 @@ function resolveVersionInput(): string {
} }
export function getNameFromPackageManagerField(): string | undefined { export function getNameFromPackageManagerField(): string | undefined {
// Enable auto-cache for npm const npmRegex = /^(\^)?npm(@.*)?$/; // matches "npm", "npm@...", "^npm@..."
try { try {
const packageJson = JSON.parse( const packageJson = JSON.parse(
fs.readFileSync( fs.readFileSync(
@ -146,13 +146,29 @@ export function getNameFromPackageManagerField(): string | undefined {
'utf-8' 'utf-8'
) )
); );
const pm = packageJson.packageManager;
if (typeof pm === 'string') { // Check devEngines.packageManager first (object or array)
const match = pm.match(/^(?:\^)?(npm)@/); const devPM = packageJson?.devEngines?.packageManager;
return match ? match[1] : undefined; if (devPM) {
if (Array.isArray(devPM)) {
for (const obj of devPM) {
if (typeof obj?.name === 'string' && npmRegex.test(obj.name)) {
return 'npm';
}
}
} else if (typeof devPM?.name === 'string' && npmRegex.test(devPM.name)) {
return 'npm';
}
} }
// Check top-level packageManager
const topLevelPM = packageJson?.packageManager;
if (typeof topLevelPM === 'string' && npmRegex.test(topLevelPM)) {
return 'npm';
}
return undefined; return undefined;
} catch (err) { } catch {
return undefined; return undefined;
} }
} }