From b321c7b92868c39ed00c00ebdd7e218f832a4994 Mon Sep 17 00:00:00 2001 From: Dmitry Shibanov Date: Tue, 9 Nov 2021 10:41:35 +0300 Subject: [PATCH] add unit and e2e tests --- .github/workflows/e2e-cache.yml | 97 +++++++ __tests__/cache-restore.test.ts | 177 ++++++++++++ __tests__/cache-save.test.ts | 178 ++++++++++++ __tests__/data/Pipfile.lock | 276 +++++++++++++++++++ __tests__/data/requirements-linux.txt | 12 + __tests__/data/requirements.txt | 47 ++++ action.yml | 2 +- dist/cache-save/index.js | 7 +- dist/setup/index.js | 35 ++- src/cache-distributions/cache-distributor.ts | 4 +- src/cache-distributions/pip-cache.ts | 8 +- src/cache-distributions/pipenv-cache.ts | 20 +- src/cache-save.ts | 2 +- src/utils.ts | 2 - 14 files changed, 825 insertions(+), 42 deletions(-) create mode 100644 .github/workflows/e2e-cache.yml create mode 100644 __tests__/cache-restore.test.ts create mode 100644 __tests__/cache-save.test.ts create mode 100644 __tests__/data/Pipfile.lock create mode 100644 __tests__/data/requirements-linux.txt create mode 100644 __tests__/data/requirements.txt diff --git a/.github/workflows/e2e-cache.yml b/.github/workflows/e2e-cache.yml new file mode 100644 index 00000000..a7531ffc --- /dev/null +++ b/.github/workflows/e2e-cache.yml @@ -0,0 +1,97 @@ +name: e2e-cache + +on: + pull_request: + paths-ignore: + - '**.md' + push: + branches: + - main + - releases/* + paths-ignore: + - '**.md' + +jobs: + python-pip-depencies-caching: + name: Test pip (Python ${{ matrix.python-version}}, ${{ matrix.os }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + python-version: ['3.7', '3.8', '3.9', 'pypy-3.7-v7.3.5', 'pypy-3.7-v7.x', 'pypy-2.7-v7.3.4'] + steps: + - uses: actions/checkout@v2 + - name: Clean global cache + run: pip cache purge + - name: Setup Python + uses: ./ + with: + python-version: ${{ matrix.python-version }} + cache: 'pip' + - name: Install dependencies + run: pip install -r __tests__/data/requirements.txt + + python-pipenv-depencies-caching: + name: Test pipenv (Python ${{ matrix.python-version}}, ${{ matrix.os }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + python-version: ['3.7', '3.8', '3.9', 'pypy-3.7-v7.3.5', 'pypy-3.7-v7.x', 'pypy-2.7-v7.3.4'] + steps: + - uses: actions/checkout@v2 + - name: Install pipenv + run: pip install pipenv + - name: Setup Python + uses: ./ + with: + python-version: ${{ matrix.python-version }} + cache: 'pipenv' + - name: Install dependencies + run: pipenv install + working-directory: __tests__/data + + python-pip-depencies-caching-path: + name: Test pip (Python ${{ matrix.python-version}}, ${{ matrix.os }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + python-version: ['3.7', '3.8', '3.9', 'pypy-3.7-v7.3.5', 'pypy-3.7-v7.x', 'pypy-2.7-v7.3.4'] + steps: + - uses: actions/checkout@v2 + - name: Clean global cache + run: pip cache purge + - name: Setup Python + uses: ./ + with: + python-version: ${{ matrix.python-version }} + cache: 'pip' + cache-dependency-path: __tests__/data/requirements.txt + - name: Install dependencies + run: pip install -r __tests__/data/requirements.txt + + python-pipenv-depencies-caching-path: + name: Test pipenv (Python ${{ matrix.python-version}}, ${{ matrix.os }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + python-version: ['3.7', '3.8', '3.9', 'pypy-3.7-v7.3.5', 'pypy-3.7-v7.x', 'pypy-2.7-v7.3.4'] + steps: + - uses: actions/checkout@v2 + - name: Install pipenv + run: pip install pipenv + - name: Setup Python + uses: ./ + with: + python-version: ${{ matrix.python-version }} + cache: 'pipenv' + cache-dependency-path: '**/requirements-linux.txt' + - name: Install dependencies + run: pipenv install + working-directory: __tests__/data \ No newline at end of file diff --git a/__tests__/cache-restore.test.ts b/__tests__/cache-restore.test.ts new file mode 100644 index 00000000..a82e7869 --- /dev/null +++ b/__tests__/cache-restore.test.ts @@ -0,0 +1,177 @@ +import * as core from '@actions/core'; +import * as cache from '@actions/cache'; +import * as exec from '@actions/exec'; +import {getCacheDistributor} from '../src/cache-distributions/cache-factory'; + +describe('restore-cache', () => { + const pipFileLockHash = + '67d817abcde9c72da0ed5b8f235647cb14638b9ff9d742b42e4406d2eb16fe3c'; + const requirementsHash = + 'd8110e0006d7fb5ee76365d565eef9d37df1d11598b912d3eb66d398d57a1121'; + const requirementsLinuxHash = + '2d0ff7f46b0e120e3d3294db65768b474934242637b9899b873e6283dfd16d7c'; + + // core spy + let infoSpy: jest.SpyInstance; + let warningSpy: jest.SpyInstance; + let debugSpy: jest.SpyInstance; + let saveSatetSpy: jest.SpyInstance; + let getStateSpy: jest.SpyInstance; + + // cache spy + let restoreCacheSpy: jest.SpyInstance; + + // exec spy + let getExecOutputSpy: jest.SpyInstance; + + beforeEach(() => { + process.env['RUNNER_OS'] = process.env['RUNNER_OS'] ?? 'linux'; + // process.env['GITHUB_WORKSPACE'] = path.join(__dirname, 'data'); + infoSpy = jest.spyOn(core, 'info'); + infoSpy.mockImplementation(input => undefined); + + warningSpy = jest.spyOn(core, 'warning'); + warningSpy.mockImplementation(input => undefined); + + debugSpy = jest.spyOn(core, 'debug'); + debugSpy.mockImplementation(input => undefined); + + saveSatetSpy = jest.spyOn(core, 'saveState'); + saveSatetSpy.mockImplementation(input => undefined); + + getStateSpy = jest.spyOn(core, 'getState'); + getStateSpy.mockImplementation(input => undefined); + + getExecOutputSpy = jest.spyOn(exec, 'getExecOutput'); + getExecOutputSpy.mockImplementation((input: string) => { + if (input.includes('pip')) { + return {stdout: 'pip', stderr: '', exitCode: 0}; + } + + return {stdout: '', stderr: 'Error occured', exitCode: 2}; + }); + + restoreCacheSpy = jest.spyOn(cache, 'restoreCache'); + restoreCacheSpy.mockImplementation( + (cachePaths: string[], primaryKey: string, restoreKey?: string) => { + return primaryKey; + } + ); + }); + + describe('Validate provided package manager', () => { + it.each(['npm', 'pip2', 'pip21', 'pip21.3', 'pipenv32'])( + 'Throw an error because %s is not supported', + async packageManager => { + await expect( + getCacheDistributor(packageManager, '3.8.12', undefined) + ).rejects.toThrowError( + `Caching for '${packageManager}' is not supported` + ); + } + ); + }); + + describe('Restore dependencies', () => { + it.each([ + ['pip', '3.8.12', undefined, requirementsHash], + ['pip', '3.8.12', '**/requirements-linux.txt', requirementsLinuxHash], + [ + 'pip', + '3.8.12', + '__tests__/data/requirements-linux.txt', + requirementsLinuxHash + ], + ['pip', '3.8.12', '__tests__/data/requirements.txt', requirementsHash], + ['pipenv', '3.9.1', undefined, pipFileLockHash], + ['pipenv', '3.9.12', '__tests__/data/requirements.txt', requirementsHash] + ])( + 'restored dependencies for %s by primaryKey', + async (packageManager, pythonVersion, dependencyFile, fileHash) => { + const cacheDistributor = await getCacheDistributor( + packageManager, + pythonVersion, + dependencyFile + ); + await cacheDistributor.restoreCache(); + let pythonKey = ''; + if (packageManager === 'pipenv') { + pythonKey = `python-${pythonVersion}-`; + } + + expect(infoSpy).toHaveBeenCalledWith( + `Cache restored from key: setup-python-${process.env['RUNNER_OS']}-${pythonKey}${packageManager}-${fileHash}` + ); + } + ); + + it.each([ + ['pip', '3.8.12', 'requirements-linux.txt', 'requirements-linux.txt'], + ['pip', '3.8.12', 'requirements.txt', 'requirements.txt'], + ['pipenv', '3.9.12', 'requirements.txt', 'requirements.txt'] + ])( + 'Should throw an error because dependency file is not found', + async ( + packageManager, + pythonVersion, + dependencyFile, + cacheDependencyPath + ) => { + const cacheDistributor = await getCacheDistributor( + packageManager, + pythonVersion, + dependencyFile + ); + await expect(cacheDistributor.restoreCache()).rejects.toThrowError( + `No file in ${process.cwd()} matched to [${cacheDependencyPath + .split('\n') + .join(',')}], make sure you have checked out the target repository` + ); + } + ); + }); + + describe('Dependencies changed', () => { + it.each([ + ['pip', '3.8.12', undefined, pipFileLockHash], + ['pip', '3.8.12', '**/requirements-linux.txt', pipFileLockHash], + [ + 'pip', + '3.8.12', + '__tests__/data/requirements-linux.txt', + pipFileLockHash + ], + ['pip', '3.8.12', '__tests__/data/requirements.txt', pipFileLockHash], + ['pipenv', '3.9.1', undefined, requirementsHash], + ['pipenv', '3.9.12', '__tests__/data/requirements.txt', requirementsHash] + ])( + 'restored dependencies for %s by primaryKey', + async (packageManager, pythonVersion, dependencyFile, fileHash) => { + restoreCacheSpy.mockImplementation( + (cachePaths: string[], primaryKey: string, restoreKey?: string) => { + return primaryKey !== fileHash && restoreKey ? pipFileLockHash : ''; + } + ); + const cacheDistributor = await getCacheDistributor( + packageManager, + pythonVersion, + dependencyFile + ); + await cacheDistributor.restoreCache(); + let result = ''; + if (packageManager !== 'pipenv') { + result = `Cache restored from key: ${fileHash}`; + } else { + result = 'pipenv cache is not found'; + } + + expect(infoSpy).toHaveBeenCalledWith(result); + } + ); + }); + + afterEach(() => { + jest.resetAllMocks(); + jest.clearAllMocks(); + }); +}); diff --git a/__tests__/cache-save.test.ts b/__tests__/cache-save.test.ts new file mode 100644 index 00000000..c1e700c2 --- /dev/null +++ b/__tests__/cache-save.test.ts @@ -0,0 +1,178 @@ +import * as core from '@actions/core'; +import * as cache from '@actions/cache'; +import * as exec from '@actions/exec'; +import {run} from '../src/cache-save'; +import {State} from '../src/cache-distributions/cache-distributor'; + +describe('run', () => { + const pipFileLockHash = + '67d817abcde9c72da0ed5b8f235647cb14638b9ff9d742b42e4406d2eb16fe3c'; + const requirementsHash = + 'd8110e0006d7fb5ee76365d565eef9d37df1d11598b912d3eb66d398d57a1121'; + const requirementsLinuxHash = + '2d0ff7f46b0e120e3d3294db65768b474934242637b9899b873e6283dfd16d7c'; + + // core spy + let infoSpy: jest.SpyInstance; + let warningSpy: jest.SpyInstance; + let debugSpy: jest.SpyInstance; + let saveSatetSpy: jest.SpyInstance; + let getStateSpy: jest.SpyInstance; + let getInputSpy: jest.SpyInstance; + let setFailedSpy: jest.SpyInstance; + + // cache spy + let saveCacheSpy: jest.SpyInstance; + + // exec spy + let getExecOutputSpy: jest.SpyInstance; + + let inputs = {} as any; + + beforeEach(() => { + process.env['RUNNER_OS'] = process.env['RUNNER_OS'] ?? 'linux'; + // process.env['GITHUB_WORKSPACE'] = path.join(__dirname, 'data'); + infoSpy = jest.spyOn(core, 'info'); + infoSpy.mockImplementation(input => undefined); + + warningSpy = jest.spyOn(core, 'warning'); + warningSpy.mockImplementation(input => undefined); + + debugSpy = jest.spyOn(core, 'debug'); + debugSpy.mockImplementation(input => undefined); + + saveSatetSpy = jest.spyOn(core, 'saveState'); + saveSatetSpy.mockImplementation(input => undefined); + + getStateSpy = jest.spyOn(core, 'getState'); + getStateSpy.mockImplementation(input => { + if (input === State.CACHE_PATHS) { + return JSON.stringify([__dirname]); + } + return requirementsHash; + }); + + setFailedSpy = jest.spyOn(core, 'setFailed'); + + getInputSpy = jest.spyOn(core, 'getInput'); + getInputSpy.mockImplementation(input => inputs[input]); + + getExecOutputSpy = jest.spyOn(exec, 'getExecOutput'); + getExecOutputSpy.mockImplementation((input: string) => { + if (input.includes('pip')) { + return {stdout: 'pip', stderr: '', exitCode: 0}; + } + + return {stdout: '', stderr: 'Error occured', exitCode: 2}; + }); + + saveCacheSpy = jest.spyOn(cache, 'saveCache'); + saveCacheSpy.mockImplementation(() => undefined); + }); + + describe('Package manager validation', () => { + it('Package manager is not provided, skip caching', async () => { + inputs['cache'] = ''; + await run(); + + expect(getInputSpy).toHaveBeenCalled(); + expect(infoSpy).not.toHaveBeenCalled(); + expect(saveCacheSpy).not.toHaveBeenCalled(); + expect(setFailedSpy).not.toHaveBeenCalled(); + }); + }); + + describe('Validate unchanged cache is not saved', () => { + it('should not save cache for pip', async () => { + inputs['cache'] = 'pip'; + + await run(); + + expect(getInputSpy).toHaveBeenCalled(); + expect(debugSpy).toHaveBeenCalledWith( + `paths for caching are ${__dirname}` + ); + expect(getStateSpy).toHaveBeenCalledTimes(3); + expect(infoSpy).toHaveBeenCalledWith( + `Cache hit occurred on the primary key ${requirementsHash}, not saving cache.` + ); + expect(setFailedSpy).not.toHaveBeenCalled(); + }); + + it('should not save cache for pipenv', async () => { + inputs['cache'] = 'pipenv'; + + await run(); + + expect(getInputSpy).toHaveBeenCalled(); + expect(debugSpy).toHaveBeenCalledWith( + `paths for caching are ${__dirname}` + ); + expect(getStateSpy).toHaveBeenCalledTimes(3); + expect(infoSpy).toHaveBeenCalledWith( + `Cache hit occurred on the primary key ${requirementsHash}, not saving cache.` + ); + expect(setFailedSpy).not.toHaveBeenCalled(); + }); + }); + + describe('action saves the cache', () => { + it('saves cache from pip', async () => { + inputs['cache'] = 'pip'; + getStateSpy.mockImplementation((name: string) => { + if (name === State.CACHE_MATCHED_KEY) { + return requirementsHash; + } else if (name === State.CACHE_PATHS) { + return JSON.stringify([__dirname]); + } else { + return pipFileLockHash; + } + }); + + await run(); + + expect(getInputSpy).toHaveBeenCalled(); + expect(getStateSpy).toHaveBeenCalledTimes(3); + expect(infoSpy).not.toHaveBeenCalledWith( + `Cache hit occurred on the primary key ${requirementsHash}, not saving cache.` + ); + expect(saveCacheSpy).toHaveBeenCalled(); + expect(infoSpy).toHaveBeenLastCalledWith( + `Cache saved with the key: ${pipFileLockHash}` + ); + expect(setFailedSpy).not.toHaveBeenCalled(); + }); + + it('saves cache from pipenv', async () => { + inputs['cache'] = 'pipenv'; + getStateSpy.mockImplementation((name: string) => { + if (name === State.CACHE_MATCHED_KEY) { + return pipFileLockHash; + } else if (name === State.CACHE_PATHS) { + return JSON.stringify([__dirname]); + } else { + return requirementsHash; + } + }); + + await run(); + + expect(getInputSpy).toHaveBeenCalled(); + expect(getStateSpy).toHaveBeenCalledTimes(3); + expect(infoSpy).not.toHaveBeenCalledWith( + `Cache hit occurred on the primary key ${pipFileLockHash}, not saving cache.` + ); + expect(saveCacheSpy).toHaveBeenCalled(); + expect(infoSpy).toHaveBeenLastCalledWith( + `Cache saved with the key: ${requirementsHash}` + ); + expect(setFailedSpy).not.toHaveBeenCalled(); + }); + }); + + afterEach(() => { + jest.resetAllMocks(); + jest.clearAllMocks(); + inputs = {}; + }); +}); diff --git a/__tests__/data/Pipfile.lock b/__tests__/data/Pipfile.lock new file mode 100644 index 00000000..1bd7b4b3 --- /dev/null +++ b/__tests__/data/Pipfile.lock @@ -0,0 +1,276 @@ +{ + "_meta": { + "hash": { + "sha256": "408f110354c997d8df1e17841d5335ae690f5b25f8c78040d62257f9535c6005" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.7" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "altgraph": { + "hashes": [ + "sha256:743628f2ac6a7c26f5d9223c91ed8ecbba535f506f4b6f558885a8a56a105857", + "sha256:ebf2269361b47d97b3b88e696439f6e4cbc607c17c51feb1754f90fb79839158" + ], + "version": "==0.17.2" + }, + "certifi": { + "hashes": [ + "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3", + "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41" + ], + "index": "pypi", + "version": "==2020.6.20" + }, + "chardet": { + "hashes": [ + "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", + "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" + ], + "index": "pypi", + "version": "==3.0.4" + }, + "docutils": { + "hashes": [ + "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af", + "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc" + ], + "index": "pypi", + "version": "==0.16" + }, + "future": { + "hashes": [ + "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d" + ], + "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2'", + "version": "==0.18.2" + }, + "idna": { + "hashes": [ + "sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb", + "sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa" + ], + "index": "pypi", + "version": "==2.9" + }, + "itsdangerous": { + "hashes": [ + "sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19", + "sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749" + ], + "index": "pypi", + "version": "==1.1.0" + }, + "kivy": { + "hashes": [ + "sha256:090d3ded9835a17477cd93fbdaf0a7c42ff2218981cf198ded5ad8795bc74391", + "sha256:11e85eaf6efbfa2362a3334ffdad179a1b0ca8d255cca79eaa6a2765560d4982", + "sha256:1a1ff32f8a95f1e175198cbab81fcd2596783b180d4eafe63e87d171aa7fdb5e", + "sha256:1d28b198a64c30db8d94a0488e85f3037af60d514ab0d7ad5ab45add3ab77090", + "sha256:4a5480cbf837d3780c77a4f61b32b56d22ae9f03845e7a89dd3eaef1ae5fd037", + "sha256:4d0e596f74271e901b551f77661dde238df4765484fce9f5d1c72e8022984e84", + "sha256:5c3d0f2749522d62e9cce09cd54b2d823bf1b6b644ff1f627be49de6f3e3cba0", + "sha256:815a5c0b3b72fcd81ca7b2aa0744087163ed03e4cf9ab4e7c9733cea99fc1571", + "sha256:8819a27a09871af451760cb69486ced52e830c8a0a37480f22ef5e692f12c05b", + "sha256:a687602d90c4629dd036f577ca39acb76ba581370f9d915f3cab99be818ba8ad", + "sha256:b7ef6aad43a86d8df3fb865db864e354f2155a748019f8517f69f65c1a29cb64", + "sha256:b85ccf165050cbf2ee8447671eebbc222b369b40f0e0038dd9547d49a5e37373", + "sha256:c36652caa7f6c327dee834cfc699d5962d346b7a53e54bd81abc17c314226d89", + "sha256:ece170514db3f49844a41e4c910ad9ce9bc46da6f47a49158e11266bdcc6e479", + "sha256:f3bea6e4a21991827885d04127fc6d09a0e974ecfa12da7bf5faae93562ea102", + "sha256:f835462dd9aa491272552ef079b948a088598e2e95d68bb1d885d2c3f3d4e2c3" + ], + "index": "pypi", + "version": "==1.11.1" + }, + "kivy-deps-angle": { + "hashes": [ + "sha256:50605fdd4c9fdbe9f717069734a598a9aba0afe5d3f0412afbe2ecff0326e92d", + "sha256:64ac7f33c000585dc30194e604aed925972c6b7c3848b5c3b073ae916fb0b55c", + "sha256:99c40d53582a958748e251dfbd61aa67fb85963e27529ca08a21f2f5eeed04e1", + "sha256:a2cea09e8a5e899629466403fbd540459f1cdef8d08c6c479b6607b95309be02", + "sha256:b167e19b3eea55a9a8c606a607bb909ec1bedda88deee40347c780b310155a79", + "sha256:b9d07976b0bf6bac724a42aa8ed5a8c7caa95609046db30c8f15bb731f8e4d36", + "sha256:bb4d53f15a093214adbbe205c108ede5cc0f6af6eff104c1b8c468ddaaf6400a", + "sha256:d0e7b7b9eb9669837a5d70808a7ea45f2b61961b56f9f69a233bad6bd36ce260" + ], + "index": "pypi", + "version": "==0.3.0" + }, + "kivy-deps.glew": { + "hashes": [ + "sha256:09f72ee5ef33ff273332e2a229dc97d650d29818a0189339421949e4e0f63d93", + "sha256:1e28e40017af9d081fc0fc95b4fadaf31d15e9f63478dcee1c4257d67079894e", + "sha256:45aa7f0e8d9bcf5fc1810c9c38bc20edf7dee61df81ecf62102e0f84153f924a", + "sha256:6bb435620c3187d2c61054adb9ec277ed487256b457a0a7b1491bc0cb7247e18", + "sha256:92e72fa2c425887987d1aa861c99537033dc20d68ae1c54864871f0401682586", + "sha256:ab81783a82bef88a8d2bcf8a93bc21df6b8b0db6ee551eb802727d18f9074b17", + "sha256:c843104690c0c8f3a58105c53c57f31506f6f90562c18de00bd19317cc1045a7", + "sha256:cf351aad171796f8051af8e49ec430a9aa128d8557d8643e73f2bb1e5f9c2dab", + "sha256:ee8ab67abb2c98d84feede657cae472e7723e529af07394244bdd33caafb1a38", + "sha256:ef1116d99bd9cc737cb8c0e13e676955c17d6e4d6d1af5cfccef089a430071bb" + ], + "index": "pypi", + "version": "==0.1.12" + }, + "kivy-deps.gstreamer": { + "hashes": [ + "sha256:0d9598d2d31c0e780adf4b767fa3a691123621fd0ffef94b83cf82c2da84341b", + "sha256:309eca64dee5939f16f8465e5cbb08bdde7c90ded1af6a00690c7e928326af79", + "sha256:3d53d2c84c0a997c4cac6c239b1e0a6486e533836321003dc365ec42b97a664b", + "sha256:4d996377111e854b3dea90846f9b2f98766a44529fd8b72125e18c552381d928", + "sha256:4f2ddd61d185310258d338ae80a646df7822efdd7d67e57f49dc7b87555c5d7e", + "sha256:6fa9f76afe600baa221abee31ce7dc63e653d0affe0f6c558bfc4f35af96396f", + "sha256:739cd331b9f33a822d700273674a79a3157054e9358a01a0d553f094a5f4a8c9", + "sha256:c29cfc63fe70a58dad889e631f1ba4711c9ea80103f2b2b8d670a97f093076c8", + "sha256:c4709765e2b17c6c96b46a92207b0457def147544d825654077603eaf0d424de" + ], + "index": "pypi", + "version": "==0.1.17" + }, + "kivy-deps.sdl2": { + "hashes": [ + "sha256:053f26e8c05d5545bdbc7eeb8c450b8e4410ee355792e9345af536110fe247e2", + "sha256:1b987bdd4fbbcb31baf0d7fc9584ad99912179b8968311bb7e30fbeb14e98e0d", + "sha256:228128cdd8112dc7505ac43027a770476e9ef282e0b84ca68037133cd025960b", + "sha256:2c2fd5a12a7a9afe3bb962b273561099a180edae91bb9c8f8386b72253fcae4a", + "sha256:5ce23f1a3286d6288751a12b0eaefd02f947ea101bb807e9781b964e496fc3f3", + "sha256:7928746eaed51944c10d1bb36fcefebe3d1aff1b97ba32359c2c97ba74707e1b", + "sha256:9270fa8ed5130074b167a7a3a9c85efc3cfe3c04584ab084cb6ae9e4edfa8168", + "sha256:92ed97d3247bc8ce98f336cbc940bb889310199326e9ccf251c49ae7e4b80de8", + "sha256:96e1fa89fd8b5351f2d3c26bbffd50df8d554b03fba4025ecc941d773d241698", + "sha256:c3ace0ddde0e59cdcaf260eda1daa0c05ca9bf8cd0c4ea404539de25a5dcaec7" + ], + "index": "pypi", + "version": "==0.1.22" + }, + "kivy-garden": { + "hashes": [ + "sha256:9b7d9de5efacbcd0c4b3dd873b30622a86093c9965aa47b523c7a32f3eb34610", + "sha256:c256f42788421273a08fbb0a228f0fb0e80dd86b629fb8c0920507f645be6c72" + ], + "index": "pypi", + "version": "==0.1.4" + }, + "packaging": { + "hashes": [ + "sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7", + "sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14" + ], + "index": "pypi", + "version": "==21.0" + }, + "pdf2image": { + "hashes": [ + "sha256:a0d9906f5507192210a8d5d7ead63145e9dec4bccc4564b1fb644e923913c31c" + ], + "index": "pypi", + "version": "==1.12.1" + }, + "pefile": { + "hashes": [ + "sha256:344a49e40a94e10849f0fe34dddc80f773a12b40675bf2f7be4b8be578bdd94a" + ], + "markers": "python_full_version >= '3.6.0'", + "version": "==2021.9.3" + }, + "pillow": { + "hashes": [ + "sha256:0295442429645fa16d05bd567ef5cff178482439c9aad0411d3f0ce9b88b3a6f", + "sha256:06aba4169e78c439d528fdeb34762c3b61a70813527a2c57f0540541e9f433a8", + "sha256:09d7f9e64289cb40c2c8d7ad674b2ed6105f55dc3b09aa8e4918e20a0311e7ad", + "sha256:0a80dd307a5d8440b0a08bd7b81617e04d870e40a3e46a32d9c246e54705e86f", + "sha256:1ca594126d3c4def54babee699c055a913efb01e106c309fa6b04405d474d5ae", + "sha256:25930fadde8019f374400f7986e8404c8b781ce519da27792cbe46eabec00c4d", + "sha256:431b15cffbf949e89df2f7b48528be18b78bfa5177cb3036284a5508159492b5", + "sha256:52125833b070791fcb5710fabc640fc1df07d087fc0c0f02d3661f76c23c5b8b", + "sha256:5e51ee2b8114def244384eda1c82b10e307ad9778dac5c83fb0943775a653cd8", + "sha256:612cfda94e9c8346f239bf1a4b082fdd5c8143cf82d685ba2dba76e7adeeb233", + "sha256:6d7741e65835716ceea0fd13a7d0192961212fd59e741a46bbed7a473c634ed6", + "sha256:6edb5446f44d901e8683ffb25ebdfc26988ee813da3bf91e12252b57ac163727", + "sha256:725aa6cfc66ce2857d585f06e9519a1cc0ef6d13f186ff3447ab6dff0a09bc7f", + "sha256:8dad18b69f710bf3a001d2bf3afab7c432785d94fcf819c16b5207b1cfd17d38", + "sha256:94cf49723928eb6070a892cb39d6c156f7b5a2db4e8971cb958f7b6b104fb4c4", + "sha256:97f9e7953a77d5a70f49b9a48da7776dc51e9b738151b22dacf101641594a626", + "sha256:9ad7f865eebde135d526bb3163d0b23ffff365cf87e767c649550964ad72785d", + "sha256:9c87ef410a58dd54b92424ffd7e28fd2ec65d2f7fc02b76f5e9b2067e355ebf6", + "sha256:a060cf8aa332052df2158e5a119303965be92c3da6f2d93b6878f0ebca80b2f6", + "sha256:c79f9c5fb846285f943aafeafda3358992d64f0ef58566e23484132ecd8d7d63", + "sha256:c92302a33138409e8f1ad16731568c55c9053eee71bb05b6b744067e1b62380f", + "sha256:d08b23fdb388c0715990cbc06866db554e1822c4bdcf6d4166cf30ac82df8c41", + "sha256:d350f0f2c2421e65fbc62690f26b59b0bcda1b614beb318c81e38647e0f673a1", + "sha256:e901964262a56d9ea3c2693df68bc9860b8bdda2b04768821e4c44ae797de117", + "sha256:ec29604081f10f16a7aea809ad42e27764188fc258b02259a03a8ff7ded3808d", + "sha256:edf31f1150778abd4322444c393ab9c7bd2af271dd4dafb4208fb613b1f3cdc9", + "sha256:f7e30c27477dffc3e85c2463b3e649f751789e0f6c8456099eea7ddd53be4a8a", + "sha256:ffe538682dc19cc542ae7c3e504fdf54ca7f86fb8a135e59dd6bc8627eae6cce" + ], + "index": "pypi", + "version": "==7.2" + }, + "pygments": { + "hashes": [ + "sha256:647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44", + "sha256:ff7a40b4860b727ab48fad6360eb351cc1b33cbf9b15a0f689ca5353e9463324" + ], + "index": "pypi", + "version": "==2.6.1" + }, + "pyinstaller": { + "hashes": [ + "sha256:3730fa80d088f8bb7084d32480eb87cbb4ddb64123363763cf8f2a1378c1c4b7" + ], + "index": "pypi", + "version": "==3.6" + }, + "pyparsing": { + "hashes": [ + "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1", + "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b" + ], + "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2'", + "version": "==2.4.7" + }, + "pywin32-ctypes": { + "hashes": [ + "sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942", + "sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98" + ], + "version": "==0.2.0" + }, + "requests": { + "hashes": [ + "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b", + "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898" + ], + "index": "pypi", + "version": "==2.24.0" + }, + "urllib3": { + "hashes": [ + "sha256:3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527", + "sha256:88206b0eb87e6d677d424843ac5209e3fb9d0190d0ee169599165ec25e9d9115" + ], + "index": "pypi", + "version": "==1.25.9" + }, + "xlrd": { + "hashes": [ + "sha256:546eb36cee8db40c3eaa46c351e67ffee6eeb5fa2650b71bc4c758a29a1b29b2", + "sha256:e551fb498759fa3a5384a94ccd4c3c02eb7c00ea424426e212ac0c57be9dfbde" + ], + "index": "pypi", + "version": "==1.2.0" + } + }, + "develop": {} +} \ No newline at end of file diff --git a/__tests__/data/requirements-linux.txt b/__tests__/data/requirements-linux.txt new file mode 100644 index 00000000..c9e14bba --- /dev/null +++ b/__tests__/data/requirements-linux.txt @@ -0,0 +1,12 @@ +certifi==2020.6.20 +chardet==3.0.4 +docutils==0.16 +idna==2.10 +Kivy==2.0.0rc3 +Kivy-Garden==0.1.4 +packaging==20.7 +pdf2image==1.12.1 +Pygments==2.6.1 +requests==2.24.0 +urllib3==1.25.10 +xlrd==1.2.0 \ No newline at end of file diff --git a/__tests__/data/requirements.txt b/__tests__/data/requirements.txt new file mode 100644 index 00000000..fa40ab3c --- /dev/null +++ b/__tests__/data/requirements.txt @@ -0,0 +1,47 @@ +altgraph==0.17.2 + +certifi==2020.6.20 + +chardet==3.0.4 + +docutils==0.16 + +future==0.18.2; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2' + +idna==2.9 + +itsdangerous==1.1.0 + +kivy-deps-angle==0.3.0 + +kivy-deps.glew==0.1.12 + +kivy-deps.gstreamer==0.1.17 + +kivy-deps.sdl2==0.1.22 + +kivy-garden==0.1.4 + +kivy==1.11.1 + +packaging==21.0 + +pdf2image==1.12.1 + +pefile==2021.9.3; python_full_version >= '3.6.0' + +pillow==7.2 + +pygments==2.6.1 + +pyinstaller==3.6 + +pyparsing==2.4.7; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2' + +pywin32-ctypes==0.2.0 + +requests==2.24.0 + +urllib3==1.25.9 + +xlrd==1.2.0 \ No newline at end of file diff --git a/action.yml b/action.yml index bd742381..892f1bf8 100644 --- a/action.yml +++ b/action.yml @@ -7,7 +7,7 @@ inputs: description: "Version range or exact version of a Python version to use, using SemVer's version range syntax." default: '3.x' cache: - description: 'Used to specify a package manager for caching in the default directory. Supported values: pip, pipenv' + description: 'Used to specify a package manager for caching in the default directory. Supported values: pip, pipenv.' required: false architecture: description: 'The target architecture (x86, x64) of the Python interpreter.' diff --git a/dist/cache-save/index.js b/dist/cache-save/index.js index 0de249fd..04ed408f 100644 --- a/dist/cache-save/index.js +++ b/dist/cache-save/index.js @@ -37180,8 +37180,8 @@ var State; State["CACHE_PATHS"] = "cache-paths"; })(State = exports.State || (exports.State = {})); class CacheDistributor { - constructor(toolName, cacheDependencyPath) { - this.toolName = toolName; + constructor(packageManager, cacheDependencyPath) { + this.packageManager = packageManager; this.cacheDependencyPath = cacheDependencyPath; this.CACHE_KEY_PREFIX = 'setup-python'; } @@ -37202,7 +37202,7 @@ class CacheDistributor { core.info(`Cache restored from key: ${matchedKey}`); } else { - core.info(`${this.toolName} cache is not found`); + core.info(`${this.packageManager} cache is not found`); } }); } @@ -45805,6 +45805,7 @@ function run() { } }); } +exports.run = run; function saveCache(packageManager) { return __awaiter(this, void 0, void 0, function* () { const cachePaths = JSON.parse(core.getState(cache_distributor_1.State.CACHE_PATHS)); diff --git a/dist/setup/index.js b/dist/setup/index.js index cddfc666..4fb202f9 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -7141,22 +7141,21 @@ const path = __importStar(__webpack_require__(622)); const core = __importStar(__webpack_require__(470)); const cache_distributor_1 = __importDefault(__webpack_require__(435)); class PipenvCache extends cache_distributor_1.default { - constructor(pythonVersion, patterns = 'Pipfile.lock') { + constructor(pythonVersion, patterns = '**/Pipfile.lock') { super('pipenv', patterns); this.pythonVersion = pythonVersion; this.patterns = patterns; } - getVirtualenvsPath() { - if (process.platform === 'win32') { - return '.virtualenvs'; - } - else { - return '.local/share/virtualenvs'; - } - } getCacheGlobalDirectories() { return __awaiter(this, void 0, void 0, function* () { - const resolvedPath = path.join(os.homedir(), this.getVirtualenvsPath()); + let virtualEnvRelativePath; + if (process.platform === 'win32') { + virtualEnvRelativePath = '.virtualenvs'; + } + else { + virtualEnvRelativePath = '.local/share/virtualenvs'; + } + const resolvedPath = path.join(os.homedir(), virtualEnvRelativePath); core.debug(`global cache directory path is ${resolvedPath}`); return [resolvedPath]; }); @@ -7164,7 +7163,7 @@ class PipenvCache extends cache_distributor_1.default { computeKeys() { return __awaiter(this, void 0, void 0, function* () { const hash = yield glob.hashFiles(this.patterns); - const primaryKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-python-${this.pythonVersion}-${this.toolName}-${hash}`; + const primaryKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-python-${this.pythonVersion}-${this.packageManager}-${hash}`; const restoreKey = undefined; return { primaryKey, @@ -34466,22 +34465,22 @@ class PipCache extends cache_distributor_1.default { getCacheGlobalDirectories() { return __awaiter(this, void 0, void 0, function* () { const { stdout, stderr, exitCode } = yield exec.getExecOutput('pip cache dir'); - if (stderr) { + if (exitCode && stderr) { throw new Error(`Could not get cache folder path for pip package manager`); } let resolvedPath = stdout.trim(); if (resolvedPath.includes('~')) { resolvedPath = path.join(os_1.default.homedir(), resolvedPath.slice(1)); } - core.info(`global cache directory path is ${resolvedPath}`); + core.debug(`global cache directory path is ${resolvedPath}`); return [resolvedPath]; }); } computeKeys() { return __awaiter(this, void 0, void 0, function* () { const hash = yield glob.hashFiles(this.cacheDependencyPath); - const primaryKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${this.toolName}-${hash}`; - const restoreKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${this.toolName}`; + const primaryKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${this.packageManager}-${hash}`; + const restoreKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${this.packageManager}`; return { primaryKey, restoreKey: [restoreKey] @@ -35424,8 +35423,8 @@ var State; State["CACHE_PATHS"] = "cache-paths"; })(State = exports.State || (exports.State = {})); class CacheDistributor { - constructor(toolName, cacheDependencyPath) { - this.toolName = toolName; + constructor(packageManager, cacheDependencyPath) { + this.packageManager = packageManager; this.cacheDependencyPath = cacheDependencyPath; this.CACHE_KEY_PREFIX = 'setup-python'; } @@ -35446,7 +35445,7 @@ class CacheDistributor { core.info(`Cache restored from key: ${matchedKey}`); } else { - core.info(`${this.toolName} cache is not found`); + core.info(`${this.packageManager} cache is not found`); } }); } diff --git a/src/cache-distributions/cache-distributor.ts b/src/cache-distributions/cache-distributor.ts index 7a8a6b8f..b823bfa8 100644 --- a/src/cache-distributions/cache-distributor.ts +++ b/src/cache-distributions/cache-distributor.ts @@ -9,7 +9,7 @@ export enum State { abstract class CacheDistributor { protected CACHE_KEY_PREFIX = 'setup-python'; - constructor(protected toolName: string, protected cacheDependencyPath: string) {} + constructor(protected packageManager: string, protected cacheDependencyPath: string) {} protected abstract getCacheGlobalDirectories(): Promise; protected abstract computeKeys(): Promise<{ @@ -42,7 +42,7 @@ abstract class CacheDistributor { core.saveState(State.CACHE_MATCHED_KEY, matchedKey); core.info(`Cache restored from key: ${matchedKey}`); } else { - core.info(`${this.toolName} cache is not found`); + core.info(`${this.packageManager} cache is not found`); } } } diff --git a/src/cache-distributions/pip-cache.ts b/src/cache-distributions/pip-cache.ts index f83ea2a1..81b875fd 100644 --- a/src/cache-distributions/pip-cache.ts +++ b/src/cache-distributions/pip-cache.ts @@ -17,7 +17,7 @@ class PipCache extends CacheDistributor { 'pip cache dir' ); - if (stderr) { + if (exitCode && stderr) { throw new Error( `Could not get cache folder path for pip package manager` ); @@ -29,15 +29,15 @@ class PipCache extends CacheDistributor { resolvedPath = path.join(os.homedir(), resolvedPath.slice(1)); } - core.info(`global cache directory path is ${resolvedPath}`); + core.debug(`global cache directory path is ${resolvedPath}`); return [resolvedPath]; } protected async computeKeys() { const hash = await glob.hashFiles(this.cacheDependencyPath); - const primaryKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${this.toolName}-${hash}`; - const restoreKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${this.toolName}`; + const primaryKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${this.packageManager}-${hash}`; + const restoreKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${this.packageManager}`; return { primaryKey, diff --git a/src/cache-distributions/pipenv-cache.ts b/src/cache-distributions/pipenv-cache.ts index 6fe8974c..d1ede9eb 100644 --- a/src/cache-distributions/pipenv-cache.ts +++ b/src/cache-distributions/pipenv-cache.ts @@ -8,21 +8,19 @@ import CacheDistributor from './cache-distributor'; class PipenvCache extends CacheDistributor { constructor( private pythonVersion: string, - protected patterns: string = 'Pipfile.lock' + protected patterns: string = '**/Pipfile.lock' ) { super('pipenv', patterns); } - private getVirtualenvsPath() { - if (process.platform === 'win32') { - return '.virtualenvs'; - } else { - return '.local/share/virtualenvs'; - } - } - protected async getCacheGlobalDirectories() { - const resolvedPath = path.join(os.homedir(), this.getVirtualenvsPath()); + let virtualEnvRelativePath; + if (process.platform === 'win32') { + virtualEnvRelativePath = '.virtualenvs'; + } else { + virtualEnvRelativePath = '.local/share/virtualenvs'; + } + const resolvedPath = path.join(os.homedir(), virtualEnvRelativePath); core.debug(`global cache directory path is ${resolvedPath}`); return [resolvedPath]; @@ -30,7 +28,7 @@ class PipenvCache extends CacheDistributor { protected async computeKeys() { const hash = await glob.hashFiles(this.patterns); - const primaryKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-python-${this.pythonVersion}-${this.toolName}-${hash}`; + const primaryKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-python-${this.pythonVersion}-${this.packageManager}-${hash}`; const restoreKey = undefined; return { primaryKey, diff --git a/src/cache-save.ts b/src/cache-save.ts index bb6092ea..bf73613f 100644 --- a/src/cache-save.ts +++ b/src/cache-save.ts @@ -4,7 +4,7 @@ import * as cache from '@actions/cache'; import fs from 'fs'; import {State} from './cache-distributions/cache-distributor'; -async function run() { +export async function run() { try { const cache = core.getInput('cache'); if (cache) { diff --git a/src/utils.ts b/src/utils.ts index b5e5916d..fc2be06d 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,5 +1,3 @@ -import * as core from '@actions/core'; - import fs from 'fs'; import * as path from 'path'; import * as semver from 'semver';