Compare commits

...

6 Commits

Author SHA1 Message Date
Aarni Koskela 70967fd245
Merge 8f0a0b0eda into 9c76e71650 2024-10-22 13:55:08 +05:45
aparnajyothi-y 9c76e71650
Bump pillow from 7.2 to 10.2.0 in /__tests__/data (#956)
* Update e2e-cache.yml

* Update basic-validation.yml

* Pyinstaller upgrade to 5.13.1

* pyinstaller-update

* Update basic-validation.yml

* Update e2e-cache.yml

* fix-db-alert-164-165-166

* upgrade pillow
2024-10-21 15:39:11 -05:00
John Wesley Walker III f4c5a1183d
Revise `isGhes` logic (#963)
* Revise `isGhes` logic

* ran `npm run format`

* add unit test

* ran `npm run format`
2024-10-21 11:42:17 -05:00
Aarni Koskela 8f0a0b0eda Add `cache-save: false` option 2023-12-12 15:26:56 +02:00
Aarni Koskela 7e8faf07b9 Tests: do not mock getInput with an incompatible implementation 2023-12-12 15:24:36 +02:00
Aarni Koskela 3cfd90a803 Fix subheadings in caching guide 2023-12-12 15:24:36 +02:00
9 changed files with 170 additions and 45 deletions

View File

@ -20,7 +20,6 @@ describe('run', () => {
let debugSpy: jest.SpyInstance; let debugSpy: jest.SpyInstance;
let saveStateSpy: jest.SpyInstance; let saveStateSpy: jest.SpyInstance;
let getStateSpy: jest.SpyInstance; let getStateSpy: jest.SpyInstance;
let getInputSpy: jest.SpyInstance;
let setFailedSpy: jest.SpyInstance; let setFailedSpy: jest.SpyInstance;
// cache spy // cache spy
@ -29,10 +28,17 @@ describe('run', () => {
// exec spy // exec spy
let getExecOutputSpy: jest.SpyInstance; let getExecOutputSpy: jest.SpyInstance;
let inputs = {} as any; function setInput(name: string, value: string): void {
process.env[`INPUT_${name.replace(/ /g, '_').toUpperCase()}`] = value;
}
beforeEach(() => { beforeEach(() => {
process.env['RUNNER_OS'] = process.env['RUNNER_OS'] ?? 'linux'; process.env['RUNNER_OS'] = process.env['RUNNER_OS'] ?? 'linux';
for (const key in process.env) {
if (key.startsWith('INPUT_')) {
delete process.env[key];
}
}
infoSpy = jest.spyOn(core, 'info'); infoSpy = jest.spyOn(core, 'info');
infoSpy.mockImplementation(input => undefined); infoSpy.mockImplementation(input => undefined);
@ -56,9 +62,6 @@ describe('run', () => {
setFailedSpy = jest.spyOn(core, 'setFailed'); setFailedSpy = jest.spyOn(core, 'setFailed');
getInputSpy = jest.spyOn(core, 'getInput');
getInputSpy.mockImplementation(input => inputs[input]);
getExecOutputSpy = jest.spyOn(exec, 'getExecOutput'); getExecOutputSpy = jest.spyOn(exec, 'getExecOutput');
getExecOutputSpy.mockImplementation((input: string) => { getExecOutputSpy.mockImplementation((input: string) => {
if (input.includes('pip')) { if (input.includes('pip')) {
@ -74,10 +77,9 @@ describe('run', () => {
describe('Package manager validation', () => { describe('Package manager validation', () => {
it('Package manager is not provided, skip caching', async () => { it('Package manager is not provided, skip caching', async () => {
inputs['cache'] = ''; setInput('cache', '');
await run(); await run();
expect(getInputSpy).toHaveBeenCalled();
expect(infoSpy).not.toHaveBeenCalled(); expect(infoSpy).not.toHaveBeenCalled();
expect(saveCacheSpy).not.toHaveBeenCalled(); expect(saveCacheSpy).not.toHaveBeenCalled();
expect(setFailedSpy).not.toHaveBeenCalled(); expect(setFailedSpy).not.toHaveBeenCalled();
@ -86,12 +88,11 @@ describe('run', () => {
describe('Validate unchanged cache is not saved', () => { describe('Validate unchanged cache is not saved', () => {
it('should not save cache for pip', async () => { it('should not save cache for pip', async () => {
inputs['cache'] = 'pip'; setInput('cache', 'pip');
inputs['python-version'] = '3.10.0'; setInput('python-version', '3.10.0');
await run(); await run();
expect(getInputSpy).toHaveBeenCalled();
expect(debugSpy).toHaveBeenCalledWith( expect(debugSpy).toHaveBeenCalledWith(
`paths for caching are ${__dirname}` `paths for caching are ${__dirname}`
); );
@ -103,12 +104,11 @@ describe('run', () => {
}); });
it('should not save cache for pipenv', async () => { it('should not save cache for pipenv', async () => {
inputs['cache'] = 'pipenv'; setInput('cache', 'pipenv');
inputs['python-version'] = '3.10.0'; setInput('python-version', '3.10.0');
await run(); await run();
expect(getInputSpy).toHaveBeenCalled();
expect(debugSpy).toHaveBeenCalledWith( expect(debugSpy).toHaveBeenCalledWith(
`paths for caching are ${__dirname}` `paths for caching are ${__dirname}`
); );
@ -122,8 +122,8 @@ describe('run', () => {
describe('action saves the cache', () => { describe('action saves the cache', () => {
it('saves cache from pip', async () => { it('saves cache from pip', async () => {
inputs['cache'] = 'pip'; setInput('cache', 'pip');
inputs['python-version'] = '3.10.0'; setInput('python-version', '3.10.0');
getStateSpy.mockImplementation((name: string) => { getStateSpy.mockImplementation((name: string) => {
if (name === State.CACHE_MATCHED_KEY) { if (name === State.CACHE_MATCHED_KEY) {
return requirementsHash; return requirementsHash;
@ -136,7 +136,6 @@ describe('run', () => {
await run(); await run();
expect(getInputSpy).toHaveBeenCalled();
expect(getStateSpy).toHaveBeenCalledTimes(3); expect(getStateSpy).toHaveBeenCalledTimes(3);
expect(infoSpy).not.toHaveBeenCalledWith( expect(infoSpy).not.toHaveBeenCalledWith(
`Cache hit occurred on the primary key ${requirementsHash}, not saving cache.` `Cache hit occurred on the primary key ${requirementsHash}, not saving cache.`
@ -149,8 +148,8 @@ describe('run', () => {
}); });
it('saves cache from pipenv', async () => { it('saves cache from pipenv', async () => {
inputs['cache'] = 'pipenv'; setInput('cache', 'pipenv');
inputs['python-version'] = '3.10.0'; setInput('python-version', '3.10.0');
getStateSpy.mockImplementation((name: string) => { getStateSpy.mockImplementation((name: string) => {
if (name === State.CACHE_MATCHED_KEY) { if (name === State.CACHE_MATCHED_KEY) {
return pipFileLockHash; return pipFileLockHash;
@ -163,7 +162,6 @@ describe('run', () => {
await run(); await run();
expect(getInputSpy).toHaveBeenCalled();
expect(getStateSpy).toHaveBeenCalledTimes(3); expect(getStateSpy).toHaveBeenCalledTimes(3);
expect(infoSpy).not.toHaveBeenCalledWith( expect(infoSpy).not.toHaveBeenCalledWith(
`Cache hit occurred on the primary key ${pipFileLockHash}, not saving cache.` `Cache hit occurred on the primary key ${pipFileLockHash}, not saving cache.`
@ -176,8 +174,8 @@ describe('run', () => {
}); });
it('saves cache from poetry', async () => { it('saves cache from poetry', async () => {
inputs['cache'] = 'poetry'; setInput('cache', 'poetry');
inputs['python-version'] = '3.10.0'; setInput('python-version', '3.10.0');
getStateSpy.mockImplementation((name: string) => { getStateSpy.mockImplementation((name: string) => {
if (name === State.CACHE_MATCHED_KEY) { if (name === State.CACHE_MATCHED_KEY) {
return poetryLockHash; return poetryLockHash;
@ -190,7 +188,6 @@ describe('run', () => {
await run(); await run();
expect(getInputSpy).toHaveBeenCalled();
expect(getStateSpy).toHaveBeenCalledTimes(3); expect(getStateSpy).toHaveBeenCalledTimes(3);
expect(infoSpy).not.toHaveBeenCalledWith( expect(infoSpy).not.toHaveBeenCalledWith(
`Cache hit occurred on the primary key ${poetryLockHash}, not saving cache.` `Cache hit occurred on the primary key ${poetryLockHash}, not saving cache.`
@ -203,8 +200,8 @@ describe('run', () => {
}); });
it('saves with -1 cacheId , should not fail workflow', async () => { it('saves with -1 cacheId , should not fail workflow', async () => {
inputs['cache'] = 'poetry'; setInput('cache', 'poetry');
inputs['python-version'] = '3.10.0'; setInput('python-version', '3.10.0');
getStateSpy.mockImplementation((name: string) => { getStateSpy.mockImplementation((name: string) => {
if (name === State.STATE_CACHE_PRIMARY_KEY) { if (name === State.STATE_CACHE_PRIMARY_KEY) {
return poetryLockHash; return poetryLockHash;
@ -221,7 +218,6 @@ describe('run', () => {
await run(); await run();
expect(getInputSpy).toHaveBeenCalled();
expect(getStateSpy).toHaveBeenCalledTimes(3); expect(getStateSpy).toHaveBeenCalledTimes(3);
expect(infoSpy).not.toHaveBeenCalled(); expect(infoSpy).not.toHaveBeenCalled();
expect(saveCacheSpy).toHaveBeenCalled(); expect(saveCacheSpy).toHaveBeenCalled();
@ -232,8 +228,8 @@ describe('run', () => {
}); });
it('saves with error from toolkit, should not fail the workflow', async () => { it('saves with error from toolkit, should not fail the workflow', async () => {
inputs['cache'] = 'npm'; setInput('cache', 'npm');
inputs['python-version'] = '3.10.0'; setInput('python-version', '3.10.0');
getStateSpy.mockImplementation((name: string) => { getStateSpy.mockImplementation((name: string) => {
if (name === State.STATE_CACHE_PRIMARY_KEY) { if (name === State.STATE_CACHE_PRIMARY_KEY) {
return poetryLockHash; return poetryLockHash;
@ -250,17 +246,27 @@ describe('run', () => {
await run(); await run();
expect(getInputSpy).toHaveBeenCalled();
expect(getStateSpy).toHaveBeenCalledTimes(3); expect(getStateSpy).toHaveBeenCalledTimes(3);
expect(infoSpy).not.toHaveBeenCalledWith(); expect(infoSpy).not.toHaveBeenCalledWith();
expect(saveCacheSpy).toHaveBeenCalled(); expect(saveCacheSpy).toHaveBeenCalled();
expect(setFailedSpy).not.toHaveBeenCalled(); expect(setFailedSpy).not.toHaveBeenCalled();
}); });
it('should not save the cache when requested not to', async () => {
setInput('cache', 'pip');
setInput('cache-save', 'false');
setInput('python-version', '3.10.0');
await run();
expect(infoSpy).toHaveBeenCalledWith(
'Not saving cache since `cache-save` is false'
);
expect(saveCacheSpy).not.toHaveBeenCalled();
expect(setFailedSpy).not.toHaveBeenCalled();
});
}); });
afterEach(() => { afterEach(() => {
jest.resetAllMocks(); jest.resetAllMocks();
jest.clearAllMocks(); jest.clearAllMocks();
inputs = {};
}); });
}); });

View File

@ -30,7 +30,7 @@ pdf2image==1.12.1
pefile==2021.9.3; python_full_version >= '3.6.0' pefile==2021.9.3; python_full_version >= '3.6.0'
pillow==7.2 pillow>=10.2.0
pygments==2.6.1 pygments==2.6.1

View File

@ -13,6 +13,7 @@ import {
getVersionInputFromPlainFile, getVersionInputFromPlainFile,
getVersionInputFromTomlFile, getVersionInputFromTomlFile,
getNextPageUrl, getNextPageUrl,
isGhes,
IS_WINDOWS, IS_WINDOWS,
getDownloadFileName getDownloadFileName
} from '../src/utils'; } from '../src/utils';
@ -195,3 +196,41 @@ describe('getDownloadFileName', () => {
} }
}); });
}); });
describe('isGhes', () => {
const pristineEnv = process.env;
beforeEach(() => {
jest.resetModules();
process.env = {...pristineEnv};
});
afterAll(() => {
process.env = pristineEnv;
});
it('returns false when the GITHUB_SERVER_URL environment variable is not defined', async () => {
delete process.env['GITHUB_SERVER_URL'];
expect(isGhes()).toBeFalsy();
});
it('returns false when the GITHUB_SERVER_URL environment variable is set to github.com', async () => {
process.env['GITHUB_SERVER_URL'] = 'https://github.com';
expect(isGhes()).toBeFalsy();
});
it('returns false when the GITHUB_SERVER_URL environment variable is set to a GitHub Enterprise Cloud-style URL', async () => {
process.env['GITHUB_SERVER_URL'] = 'https://contoso.ghe.com';
expect(isGhes()).toBeFalsy();
});
it('returns false when the GITHUB_SERVER_URL environment variable has a .localhost suffix', async () => {
process.env['GITHUB_SERVER_URL'] = 'https://mock-github.localhost';
expect(isGhes()).toBeFalsy();
});
it('returns true when the GITHUB_SERVER_URL environment variable is set to some other URL', async () => {
process.env['GITHUB_SERVER_URL'] = 'https://src.onpremise.fabrikam.com';
expect(isGhes()).toBeTruthy();
});
});

View File

@ -20,6 +20,9 @@ inputs:
default: ${{ github.server_url == 'https://github.com' && github.token || '' }} default: ${{ github.server_url == 'https://github.com' && github.token || '' }}
cache-dependency-path: cache-dependency-path:
description: "Used to specify the path to dependency files. Supports wildcards or a list of file names for caching multiple dependencies." description: "Used to specify the path to dependency files. Supports wildcards or a list of file names for caching multiple dependencies."
cache-save:
description: "Set this option if you want the action to save the cache after the run. Defaults to true. It can be useful to set this to false if you have e.g. optional dependencies that only some workflows require, and they should not be cached."
default: true
update-environment: update-environment:
description: "Set this option if you want the action to update environment variables." description: "Set this option if you want the action to update environment variables."
default: true default: true

View File

@ -81142,11 +81142,25 @@ function run(earlyExit) {
try { try {
const cache = core.getInput('cache'); const cache = core.getInput('cache');
if (cache) { if (cache) {
let shouldSave = true;
try {
shouldSave = core.getBooleanInput('cache-save', { required: false });
}
catch (e) {
// If we fail to parse the input, assume it's
// > "Input does not meet YAML 1.2 "core schema" specification."
// and assume it's the `true` default.
}
if (shouldSave) {
yield saveCache(cache); yield saveCache(cache);
if (earlyExit) { if (earlyExit) {
process.exit(0); process.exit(0);
} }
} }
else {
core.info('Not saving cache since `cache-save` is false');
}
}
} }
catch (error) { catch (error) {
const err = error; const err = error;

6
dist/setup/index.js vendored
View File

@ -92017,7 +92017,11 @@ function validatePythonVersionFormatForPyPy(version) {
exports.validatePythonVersionFormatForPyPy = validatePythonVersionFormatForPyPy; exports.validatePythonVersionFormatForPyPy = validatePythonVersionFormatForPyPy;
function isGhes() { function isGhes() {
const ghUrl = new URL(process.env['GITHUB_SERVER_URL'] || 'https://github.com'); const ghUrl = new URL(process.env['GITHUB_SERVER_URL'] || 'https://github.com');
return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM'; const hostname = ghUrl.hostname.trimEnd().toUpperCase();
const isGitHubHost = hostname === 'GITHUB.COM';
const isGitHubEnterpriseCloudHost = hostname.endsWith('.GHE.COM');
const isLocalHost = hostname.endsWith('.LOCALHOST');
return !isGitHubHost && !isGitHubEnterpriseCloudHost && !isLocalHost;
} }
exports.isGhes = isGhes; exports.isGhes = isGhes;
function isCacheFeatureAvailable() { function isCacheFeatureAvailable() {

View File

@ -293,7 +293,8 @@ steps:
## Caching packages ## Caching packages
**Caching pipenv dependencies:** ### Caching pipenv dependencies
```yaml ```yaml
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -306,7 +307,8 @@ steps:
- run: pipenv install - run: pipenv install
``` ```
**Caching poetry dependencies:** ### Caching poetry dependencies
```yaml ```yaml
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -321,7 +323,8 @@ steps:
``` ```
>**Note:** If the `setup-python` version does not match the version specified in `pyproject.toml` and the python version in `pyproject.toml` is less than the runner's python version, `poetry install` will default to using the runner's Python version. >**Note:** If the `setup-python` version does not match the version specified in `pyproject.toml` and the python version in `pyproject.toml` is less than the runner's python version, `poetry install` will default to using the runner's Python version.
**Using a list of file paths to cache dependencies** ### Using a list of file paths to cache dependencies
```yaml ```yaml
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -336,7 +339,9 @@ steps:
run: curl https://raw.githubusercontent.com/pypa/pipenv/master/get-pipenv.py | python run: curl https://raw.githubusercontent.com/pypa/pipenv/master/get-pipenv.py | python
- run: pipenv install - run: pipenv install
``` ```
**Using wildcard patterns to cache dependencies**
### Using wildcard patterns to cache dependencies
```yaml ```yaml
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -348,7 +353,8 @@ steps:
- run: pip install -r subdirectory/requirements-dev.txt - run: pip install -r subdirectory/requirements-dev.txt
``` ```
**Using a list of wildcard patterns to cache dependencies** ### Using a list of wildcard patterns to cache dependencies
```yaml ```yaml
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -362,7 +368,7 @@ steps:
- run: pip install -e . -r subdirectory/requirements-dev.txt - run: pip install -e . -r subdirectory/requirements-dev.txt
``` ```
**Caching projects that use setup.py:** ### Caching projects that use setup.py (or pyproject.toml)
```yaml ```yaml
steps: steps:
@ -376,6 +382,42 @@ steps:
# Or pip install -e '.[test]' to install test dependencies # Or pip install -e '.[test]' to install test dependencies
``` ```
### Skipping cache saving
For some scenarios, it may be useful to only save a given subset of dependencies,
but restore more of them for other workflows. For instance, there may be a heavy
`extras` dependency that you do not need your entire test matrix to download, but
you want to download and test it separately without it being saved in the cache
archive for all runs.
To achieve this, you can use `cache-save: false` on the run that uses the heavy
dependency.
```yaml
test:
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
with:
python-version: '3.11'
cache: 'pip'
cache-dependency-path: pyproject.toml
- run: pip install -e .
test-heavy-extra:
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
with:
python-version: '3.11'
cache: 'pip'
cache-dependency-path: pyproject.toml
cache-save: false
- run: pip install -e '.[heavy-extra]'
```
# Outputs and environment variables # Outputs and environment variables
## Outputs ## Outputs

View File

@ -11,11 +11,22 @@ export async function run(earlyExit?: boolean) {
try { try {
const cache = core.getInput('cache'); const cache = core.getInput('cache');
if (cache) { if (cache) {
let shouldSave = true;
try {
shouldSave = core.getBooleanInput('cache-save', {required: false});
} catch (e) {
// If we fail to parse the input, assume it's
// > "Input does not meet YAML 1.2 "core schema" specification."
// and assume it's the `true` default.
}
if (shouldSave) {
await saveCache(cache); await saveCache(cache);
if (earlyExit) { if (earlyExit) {
process.exit(0); process.exit(0);
} }
} else {
core.info('Not saving cache since `cache-save` is false');
}
} }
} catch (error) { } catch (error) {
const err = error as Error; const err = error as Error;

View File

@ -116,7 +116,13 @@ export function isGhes(): boolean {
const ghUrl = new URL( const ghUrl = new URL(
process.env['GITHUB_SERVER_URL'] || 'https://github.com' process.env['GITHUB_SERVER_URL'] || 'https://github.com'
); );
return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM';
const hostname = ghUrl.hostname.trimEnd().toUpperCase();
const isGitHubHost = hostname === 'GITHUB.COM';
const isGitHubEnterpriseCloudHost = hostname.endsWith('.GHE.COM');
const isLocalHost = hostname.endsWith('.LOCALHOST');
return !isGitHubHost && !isGitHubEnterpriseCloudHost && !isLocalHost;
} }
export function isCacheFeatureAvailable(): boolean { export function isCacheFeatureAvailable(): boolean {