diff --git a/__tests__/cache.test.ts b/__tests__/cache.test.ts new file mode 100644 index 00000000..fbc6dac0 --- /dev/null +++ b/__tests__/cache.test.ts @@ -0,0 +1,181 @@ +import { mkdtempSync } from 'fs'; +import { tmpdir } from 'os'; +import { join } from 'path'; +import { restore, save } from '../src/cache'; +import * as fs from 'fs'; +import * as os from 'os'; +import * as core from '@actions/core'; +import * as cache from '@actions/cache'; + +describe('dependency cache', () => { + const ORIGINAL_RUNNER_OS = process.env['RUNNER_OS']; + const ORIGINAL_GITHUB_WORKSPACE = process.env['GITHUB_WORKSPACE']; + const ORIGINAL_CWD = process.cwd(); + let workspace: string; + let spyInfo: jest.SpyInstance>; + let spyWarning: jest.SpyInstance>; + + beforeEach(() => { + workspace = mkdtempSync(join(tmpdir(), 'setup-java-cache-')); + switch (os.platform()) { + case 'darwin': + process.env['RUNNER_OS'] = 'macOS'; + break; + case 'win32': + process.env['RUNNER_OS'] = 'Windows'; + break; + case 'linux': + process.env['RUNNER_OS'] = 'Linux'; + break; + default: + throw new Error(`unknown platform: ${os.platform()}`); + } + process.chdir(workspace); + // This hack is necessary because @actions/glob ignores files not in the GITHUB_WORKSPACE + // https://git.io/Jcxig + process.env['GITHUB_WORKSPACE'] = projectRoot(workspace); + }); + + beforeEach(() => { + spyInfo = jest.spyOn(core, 'info'); + spyWarning = jest.spyOn(core, 'warning'); + }); + + afterEach(() => { + process.chdir(ORIGINAL_CWD); + process.env['GITHUB_WORKSPACE'] = ORIGINAL_GITHUB_WORKSPACE; + process.env['RUNNER_OS'] = ORIGINAL_RUNNER_OS; + }); + + describe('restore', () => { + let spyCacheRestore: jest.SpyInstance< + ReturnType, + Parameters + >; + + beforeEach(() => { + spyCacheRestore = jest + .spyOn(cache, 'restoreCache') + .mockImplementation((paths: string[], primaryKey: string) => Promise.resolve(undefined)); + }); + + it('throws error if unsupported package manager specified', () => { + return expect(restore('ant')).rejects.toThrowError('unknown package manager specified: ant'); + }); + + describe('for maven', () => { + it('warns if no pom.xml found', async () => { + await restore('maven'); + expect(spyWarning).toBeCalledWith( + `No file in ${projectRoot( + workspace + )} matched to [**/pom.xml], make sure you have checked out the target repository` + ); + }); + it('downloads cache', async () => { + createFile(join(workspace, 'pom.xml')); + + await restore('maven'); + expect(spyCacheRestore).toBeCalled(); + expect(spyWarning).not.toBeCalled(); + expect(spyInfo).toBeCalledWith('maven cache is not found'); + }); + }); + describe('for gradle', () => { + it('warns if no build.gradle found', async () => { + await restore('gradle'); + expect(spyWarning).toBeCalledWith( + `No file in ${projectRoot( + workspace + )} matched to [**/*.gradle*,**/gradle-wrapper.properties], make sure you have checked out the target repository` + ); + }); + it('downloads cache based on build.gradle', async () => { + createFile(join(workspace, 'build.gradle')); + + await restore('gradle'); + expect(spyCacheRestore).toBeCalled(); + expect(spyWarning).not.toBeCalled(); + expect(spyInfo).toBeCalledWith('gradle cache is not found'); + }); + it('downloads cache based on build.gradle.kts', async () => { + createFile(join(workspace, 'build.gradle.kts')); + + await restore('gradle'); + expect(spyCacheRestore).toBeCalled(); + expect(spyWarning).not.toBeCalled(); + expect(spyInfo).toBeCalledWith('gradle cache is not found'); + }); + }); + }); + describe('save', () => { + let spyCacheSave: jest.SpyInstance< + ReturnType, + Parameters + >; + + beforeEach(() => { + spyCacheSave = jest + .spyOn(cache, 'saveCache') + .mockImplementation((paths: string[], key: string) => Promise.resolve(0)); + }); + + it('throws error if unsupported package manager specified', () => { + return expect(save('ant')).rejects.toThrowError('unknown package manager specified: ant'); + }); + + describe('for maven', () => { + it('uploads cache even if no pom.xml found', async () => { + await save('maven'); + expect(spyCacheSave).toBeCalled(); + expect(spyWarning).not.toBeCalled(); + expect(spyInfo).toBeCalledWith(expect.stringMatching(/^Cache saved with the key:.*/)); + }); + it('uploads cache', async () => { + createFile(join(workspace, 'pom.xml')); + + await save('maven'); + expect(spyCacheSave).toBeCalled(); + expect(spyWarning).not.toBeCalled(); + expect(spyInfo).toBeCalledWith(expect.stringMatching(/^Cache saved with the key:.*/)); + }); + }); + describe('for gradle', () => { + it('uploads cache even if no build.gradle found', async () => { + await save('gradle'); + expect(spyCacheSave).toBeCalled(); + expect(spyWarning).not.toBeCalled(); + expect(spyInfo).toBeCalledWith(expect.stringMatching(/^Cache saved with the key:.*/)); + }); + it('uploads cache based on build.gradle', async () => { + createFile(join(workspace, 'build.gradle')); + + await save('gradle'); + expect(spyCacheSave).toBeCalled(); + expect(spyWarning).not.toBeCalled(); + expect(spyInfo).toBeCalledWith(expect.stringMatching(/^Cache saved with the key:.*/)); + }); + it('uploads cache based on build.gradle.kts', async () => { + createFile(join(workspace, 'build.gradle.kts')); + + await save('gradle'); + expect(spyCacheSave).toBeCalled(); + expect(spyWarning).not.toBeCalled(); + expect(spyInfo).toBeCalledWith(expect.stringMatching(/^Cache saved with the key:.*/)); + }); + }); + }); +}); + +function createFile(path: string) { + core.info(`created a file at ${path}`); + fs.writeFileSync(path, ''); +} + +function projectRoot(workspace: string): string { + if (os.platform() === 'darwin') { + return `/private${workspace}`; + } else { + return workspace; + } +} diff --git a/package-lock.json b/package-lock.json index 434d6be9..8c293542 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,38 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@actions/cache": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@actions/cache/-/cache-1.0.7.tgz", + "integrity": "sha512-MY69kxuubqUFq84pFlu8m6Poxl5sR/xyhpC4JEvno7Yg9ASYdGizEmKgt0m8ovewpYKf15UAOcSC0hzS+DuosA==", + "requires": { + "@actions/core": "^1.2.6", + "@actions/exec": "^1.0.1", + "@actions/glob": "^0.1.0", + "@actions/http-client": "^1.0.9", + "@actions/io": "^1.0.1", + "@azure/ms-rest-js": "^2.0.7", + "@azure/storage-blob": "^12.1.2", + "semver": "^6.1.0", + "uuid": "^3.3.3" + }, + "dependencies": { + "@actions/glob": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@actions/glob/-/glob-0.1.2.tgz", + "integrity": "sha512-SclLR7Ia5sEqjkJTPs7Sd86maMDw43p769YxBOxvPvEWuPEhpAnBsQfENOpXjFYMmhCqd127bmf+YdvJqVqR4A==", + "requires": { + "@actions/core": "^1.2.6", + "minimatch": "^3.0.4" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, "@actions/core": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.6.tgz", @@ -17,6 +49,15 @@ "@actions/io": "^1.0.1" } }, + "@actions/glob": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@actions/glob/-/glob-0.2.0.tgz", + "integrity": "sha512-mqE2a7I66kxcvsdwxs/filQwZsq25IfktMaviGfDB51v6Q3bvxnV7mFsZnvYtLhqGZbPxwBnH8AD3UYaOWb//w==", + "requires": { + "@actions/core": "^1.2.6", + "minimatch": "^3.0.4" + } + }, "@actions/http-client": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.9.tgz", @@ -50,6 +91,205 @@ } } }, + "@azure/abort-controller": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.0.4.tgz", + "integrity": "sha512-lNUmDRVGpanCsiUN3NWxFTdwmdFI53xwhkTFfHDGTYk46ca7Ind3nanJc+U6Zj9Tv+9nTCWRBscWEW1DyKOpTw==", + "requires": { + "tslib": "^2.0.0" + }, + "dependencies": { + "tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" + } + } + }, + "@azure/core-asynciterator-polyfill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.0.tgz", + "integrity": "sha512-kmv8CGrPfN9SwMwrkiBK9VTQYxdFQEGe0BmQk+M8io56P9KNzpAxcWE/1fxJj7uouwN4kXF0BHW8DNlgx+wtCg==" + }, + "@azure/core-auth": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.3.2.tgz", + "integrity": "sha512-7CU6DmCHIZp5ZPiZ9r3J17lTKMmYsm/zGvNkjArQwPkrLlZ1TZ+EUYfGgh2X31OLMVAQCTJZW4cXHJi02EbJnA==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "tslib": "^2.2.0" + }, + "dependencies": { + "tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" + } + } + }, + "@azure/core-http": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@azure/core-http/-/core-http-1.2.6.tgz", + "integrity": "sha512-odtH7UMKtekc5YQ86xg9GlVHNXR6pq2JgJ5FBo7/jbOjNGdBqcrIVrZx2bevXVJz/uUTSx6vUf62gzTXTfqYSQ==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-asynciterator-polyfill": "^1.0.0", + "@azure/core-auth": "^1.3.0", + "@azure/core-tracing": "1.0.0-preview.11", + "@azure/logger": "^1.0.0", + "@types/node-fetch": "^2.5.0", + "@types/tunnel": "^0.0.1", + "form-data": "^3.0.0", + "node-fetch": "^2.6.0", + "process": "^0.11.10", + "tough-cookie": "^4.0.0", + "tslib": "^2.2.0", + "tunnel": "^0.0.6", + "uuid": "^8.3.0", + "xml2js": "^0.4.19" + }, + "dependencies": { + "form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "tough-cookie": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", + "requires": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" + } + }, + "tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + } + } + }, + "@azure/core-lro": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-1.0.5.tgz", + "integrity": "sha512-0EFCFZxARrIoLWMIRt4vuqconRVIO2Iin7nFBfJiYCCbKp5eEmxutNk8uqudPmG0XFl5YqlVh68/al/vbE5OOg==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-http": "^1.2.0", + "@azure/core-tracing": "1.0.0-preview.11", + "events": "^3.0.0", + "tslib": "^2.0.0" + }, + "dependencies": { + "tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" + } + } + }, + "@azure/core-paging": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.1.3.tgz", + "integrity": "sha512-his7Ah40ThEYORSpIAwuh6B8wkGwO/zG7gqVtmSE4WAJ46e36zUDXTKReUCLBDc6HmjjApQQxxcRFy5FruG79A==", + "requires": { + "@azure/core-asynciterator-polyfill": "^1.0.0" + } + }, + "@azure/core-tracing": { + "version": "1.0.0-preview.11", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.11.tgz", + "integrity": "sha512-frF0pJc9HTmKncVokhBxCqipjbql02DThQ1ZJ9wLi7SDMLdPAFyDI5xZNzX5guLz+/DtPkY+SGK2li9FIXqshQ==", + "requires": { + "@opencensus/web-types": "0.0.7", + "@opentelemetry/api": "1.0.0-rc.0", + "tslib": "^2.0.0" + }, + "dependencies": { + "tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" + } + } + }, + "@azure/logger": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.2.tgz", + "integrity": "sha512-YZNjNV0vL3nN2nedmcjQBcpCTo3oqceXmgiQtEm6fLpucjRZyQKAQruhCmCpRlB1iykqKJJ/Y8CDmT5rIE6IJw==", + "requires": { + "tslib": "^2.0.0" + }, + "dependencies": { + "tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" + } + } + }, + "@azure/ms-rest-js": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@azure/ms-rest-js/-/ms-rest-js-2.5.2.tgz", + "integrity": "sha512-9nCuuoYwHZEZw1t0MVtENH+c1k2R4maYAlBBDSZhZu6bEucyfYUUigNXXKjt2cFBt4sO+sTzi0uI0f/fiPFr+Q==", + "requires": { + "@azure/core-auth": "^1.1.4", + "abort-controller": "^3.0.0", + "form-data": "^2.5.0", + "node-fetch": "^2.6.0", + "tough-cookie": "^3.0.1", + "tslib": "^1.10.0", + "tunnel": "0.0.6", + "uuid": "^3.3.2", + "xml2js": "^0.4.19" + }, + "dependencies": { + "form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + } + } + }, + "@azure/storage-blob": { + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.6.0.tgz", + "integrity": "sha512-cAzsae+5ZdhugQfIT7o5SlVyF2Sc+HygZdPO41ZYdXklfGUyEt+5K4PyM5HQDc0MTVt6x7+waXcaAXT2eF9E6A==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-http": "^1.2.0", + "@azure/core-lro": "^1.0.2", + "@azure/core-paging": "^1.1.1", + "@azure/core-tracing": "1.0.0-preview.11", + "@azure/logger": "^1.0.0", + "events": "^3.0.0", + "tslib": "^2.0.0" + }, + "dependencies": { + "tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" + } + } + }, "@babel/code-frame": { "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", @@ -740,6 +980,16 @@ "resolved": "https://registry.npmjs.org/@oozcitak/util/-/util-8.3.8.tgz", "integrity": "sha512-T8TbSnGsxo6TDBJx/Sgv/BlVJL3tshxZP7Aq5R1mSnM5OcHY2dQaxLMu2+E8u3gN0MLOzdjurqN4ZRVuzQycOQ==" }, + "@opencensus/web-types": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/@opencensus/web-types/-/web-types-0.0.7.tgz", + "integrity": "sha512-xB+w7ZDAu3YBzqH44rCmG9/RlrOmFuDPt/bpf17eJr8eZSrLt7nc7LnWdxM9Mmoj/YKMHpxRg28txu3TcpiL+g==" + }, + "@opentelemetry/api": { + "version": "1.0.0-rc.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.0.0-rc.0.tgz", + "integrity": "sha512-iXKByCMfrlO5S6Oh97BuM56tM2cIBB0XsL/vWF/AtJrJEKx4MC/Xdu0xDsGXMGcNWpqF7ujMsjjnp0+UHBwnDQ==" + }, "@sinonjs/commons": { "version": "1.8.2", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.2.tgz", @@ -845,8 +1095,28 @@ "@types/node": { "version": "12.20.4", "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.4.tgz", - "integrity": "sha512-xRCgeE0Q4pT5UZ189TJ3SpYuX/QGl6QIAOAIeDSbAVAd2gX1NxSZup4jNVK7cxIeP8KDSbJgcckun495isP1jQ==", - "dev": true + "integrity": "sha512-xRCgeE0Q4pT5UZ189TJ3SpYuX/QGl6QIAOAIeDSbAVAd2gX1NxSZup4jNVK7cxIeP8KDSbJgcckun495isP1jQ==" + }, + "@types/node-fetch": { + "version": "2.5.11", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.11.tgz", + "integrity": "sha512-2upCKaqVZETDRb8A2VTaRymqFBEgH8u6yr96b/u3+1uQEPDRo3mJLEiPk7vdXBHRtjwkjqzFYMJXrt0Z9QsYjQ==", + "requires": { + "@types/node": "*", + "form-data": "^3.0.0" + }, + "dependencies": { + "form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } + } }, "@types/normalize-package-data": { "version": "2.4.0", @@ -872,6 +1142,14 @@ "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", "dev": true }, + "@types/tunnel": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.1.tgz", + "integrity": "sha512-AOqu6bQu5MSWwYvehMXLukFHnupHrpZ8nvgae5Ggie9UwzDR1CCwoXgSSWNZJuyOlCdfdsWMA5F2LlmvyoTv8A==", + "requires": { + "@types/node": "*" + } + }, "@types/yargs": { "version": "15.0.13", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.13.tgz", @@ -899,6 +1177,14 @@ "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", "dev": true }, + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "requires": { + "event-target-shim": "^5.0.0" + } + }, "acorn": { "version": "7.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", @@ -1031,8 +1317,7 @@ "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "atob": { "version": "2.1.2", @@ -1126,8 +1411,7 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "base": { "version": "0.11.2", @@ -1197,7 +1481,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1414,7 +1697,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "requires": { "delayed-stream": "~1.0.0" } @@ -1428,8 +1710,7 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "convert-source-map": { "version": "1.7.0", @@ -1605,8 +1886,7 @@ "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, "detect-newline": { "version": "3.1.0", @@ -1725,6 +2005,16 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, + "event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" + }, "exec-sh": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz", @@ -2251,8 +2541,7 @@ "ip-regex": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", - "dev": true + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=" }, "is-accessor-descriptor": { "version": "0.1.6", @@ -3282,14 +3571,12 @@ "mime-db": { "version": "1.46.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.46.0.tgz", - "integrity": "sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==", - "dev": true + "integrity": "sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==" }, "mime-types": { "version": "2.1.29", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.29.tgz", "integrity": "sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==", - "dev": true, "requires": { "mime-db": "1.46.0" } @@ -3304,7 +3591,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -3379,6 +3665,11 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + }, "node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -3703,6 +3994,11 @@ "react-is": "^17.0.1" } }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" + }, "prompts": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.0.tgz", @@ -3716,8 +4012,7 @@ "psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", - "dev": true + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" }, "pump": { "version": "3.0.0", @@ -3732,8 +4027,7 @@ "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, "qs": { "version": "6.5.2", @@ -4104,6 +4398,11 @@ } } }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, "saxes": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", @@ -4617,7 +4916,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", - "dev": true, "requires": { "ip-regex": "^2.1.0", "psl": "^1.1.28", @@ -4659,6 +4957,11 @@ } } }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, "tunnel": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", @@ -4727,6 +5030,11 @@ "set-value": "^2.0.1" } }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + }, "unset-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", @@ -4954,6 +5262,20 @@ "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", "dev": true }, + "xml2js": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", + "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + } + }, + "xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" + }, "xmlbuilder2": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/xmlbuilder2/-/xmlbuilder2-2.4.0.tgz", diff --git a/package.json b/package.json index 00ddd557..c14f855c 100644 --- a/package.json +++ b/package.json @@ -24,8 +24,10 @@ "author": "GitHub", "license": "MIT", "dependencies": { + "@actions/cache": "^1.0.7", "@actions/core": "^1.2.6", "@actions/exec": "^1.0.4", + "@actions/glob": "^0.2.0", "@actions/http-client": "^1.0.9", "@actions/io": "^1.0.2", "@actions/tool-cache": "^1.6.1", diff --git a/src/cache.ts b/src/cache.ts new file mode 100644 index 00000000..c7cc4ccd --- /dev/null +++ b/src/cache.ts @@ -0,0 +1,110 @@ +/** + * @fileoverview this file provides methods handling dependency cache + */ + +import { join } from 'path'; +import os from 'os'; +import * as cache from '@actions/cache'; +import * as core from '@actions/core'; +import * as glob from '@actions/glob'; + +const STATE_CACHE_PRIMARY_KEY = 'cache-primary-key'; +const CACHE_MATCHED_KEY = 'cache-matched-key'; + +interface PackageManager { + id: 'maven' | 'gradle'; + /** + * Paths of the file that specify the files to cache. + */ + path: string[]; + pattern: string[]; +} +const supportedPackageManager: PackageManager[] = [ + { + id: 'maven', + path: [join(os.homedir(), '.m2', 'repository')], + // https://github.com/actions/cache/blob/0638051e9af2c23d10bb70fa9beffcad6cff9ce3/examples.md#java---maven + pattern: ['**/pom.xml'] + }, + { + id: 'gradle', + path: [join(os.homedir(), '.gradle', 'caches'), join(os.homedir(), '.gradle', 'wrapper')], + // https://github.com/actions/cache/blob/0638051e9af2c23d10bb70fa9beffcad6cff9ce3/examples.md#java---gradle + pattern: ['**/*.gradle*', '**/gradle-wrapper.properties'] + } +]; + +function findPackageManager(id: string): PackageManager { + const packageManager = supportedPackageManager.find(packageManager => packageManager.id === id); + if (packageManager === undefined) { + throw new Error(`unknown package manager specified: ${id}`); + } + return packageManager; +} + +/** + * A function that generates a cache key to use. + * Format of the generated key will be "${{ platform }}-${{ id }}-${{ fileHash }}"". + * If there is no file matched to {@link PackageManager.path}, the generated key ends with a dash (-). + * @see {@link https://docs.github.com/en/actions/guides/caching-dependencies-to-speed-up-workflows#matching-a-cache-key|spec of cache key} + */ +async function computeCacheKey(packageManager: PackageManager) { + return `${process.env['RUNNER_OS']}-${packageManager.id}-${await glob.hashFiles( + packageManager.pattern.join('\n') + )}`; +} + +/** + * Restore the dependency cache + * @param id ID of the package manager, should be "maven" or "gradle" + */ +export async function restore(id: string) { + const packageManager = findPackageManager(id); + const primaryKey = await computeCacheKey(packageManager); + + core.debug(`primary key is ${primaryKey}`); + core.saveState(STATE_CACHE_PRIMARY_KEY, primaryKey); + if (primaryKey.endsWith('-')) { + core.warning( + `No file in ${process.cwd()} matched to [${ + packageManager.pattern + }], make sure you have checked out the target repository` + ); + return; + } + + const matchedKey = await cache.restoreCache(packageManager.path, primaryKey, [ + `${process.env['RUNNER_OS']}-${id}` + ]); + if (matchedKey) { + core.saveState(CACHE_MATCHED_KEY, matchedKey); + core.info(`Cache restored from key: ${matchedKey}`); + } else { + core.info(`${packageManager.id} cache is not found`); + } +} + +/** + * Save the dependency cache + * @param id ID of the package manager, should be "maven" or "gradle" + */ +export async function save(id: string) { + const packageManager = findPackageManager(id); + const primaryKey = await computeCacheKey(packageManager); + const matchedKey = core.getState(CACHE_MATCHED_KEY); + if (matchedKey === primaryKey) { + // no change in target directories + core.info(`Cache hit occurred on the primary key ${primaryKey}, not saving cache.`); + return; + } + try { + await cache.saveCache(packageManager.path, primaryKey); + core.info(`Cache saved with the key: ${primaryKey}`); + } catch (error) { + if (error.name === cache.ReserveCacheError.name) { + core.info(error.message); + } else { + throw error; + } + } +}