Compare commits

..

9 Commits

54 changed files with 1425 additions and 1999 deletions

2
.github/CODEOWNERS vendored
View File

@ -1 +1 @@
* @actions/actions-cache * @artifacts-actions

View File

@ -1,52 +0,0 @@
# `dist/index.js` is a special file in Actions.
# When you reference an action with `uses:` in a workflow,
# `index.js` is the code that will run.
# For our project, we generate this file through a build process
# from other source files.
# We need to make sure the checked-in `index.js` actually matches what we expect it to be.
name: Check dist/
on:
push:
branches:
- main
paths-ignore:
- '**.md'
pull_request:
paths-ignore:
- '**.md'
workflow_dispatch:
jobs:
check-dist:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set Node.js 12.x
uses: actions/setup-node@v1
with:
node-version: 12.x
- name: Install dependencies
run: npm ci
- name: Rebuild the dist/ directory
run: npm run build
- name: Compare the expected and actual dist/ directories
run: |
if [ "$(git diff --ignore-space-at-eol dist/ | wc -l)" -gt "0" ]; then
echo "Detected uncommitted changes after build. See status below:"
git diff
exit 1
fi
id: diff
# If index.js was different than expected, upload the expected version as an artifact
- uses: actions/upload-artifact@v2
if: ${{ failure() && steps.diff.conclusion == 'failure' }}
with:
name: dist
path: dist/

View File

@ -1,22 +0,0 @@
name: Close inactive issues
on:
schedule:
- cron: "30 8 * * *"
jobs:
close-issues:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v3
with:
days-before-issue-stale: 365
days-before-issue-close: 5
stale-issue-label: "stale"
stale-issue-message: "This issue is stale because it has been open for 365 days with no activity. Leave a comment to avoid closing this issue in 5 days."
close-issue-message: "This issue was closed because it has been inactive for 5 days since being marked as stale."
days-before-pr-stale: -1
days-before-pr-close: -1
repo-token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,12 +1,8 @@
name: Licensed name: Licensed
on: on:
push: push: {branches: main}
branches: pull_request: {branches: main}
- main
pull_request:
branches:
- main
jobs: jobs:
test: test:
@ -21,4 +17,4 @@ jobs:
curl -Lfs -o licensed.tar.gz https://github.com/github/licensed/releases/download/2.12.2/licensed-2.12.2-linux-x64.tar.gz curl -Lfs -o licensed.tar.gz https://github.com/github/licensed/releases/download/2.12.2/licensed-2.12.2-linux-x64.tar.gz
sudo tar -xzf licensed.tar.gz sudo tar -xzf licensed.tar.gz
sudo mv licensed /usr/local/bin/licensed sudo mv licensed /usr/local/bin/licensed
- run: licensed status - run: licensed status

View File

@ -19,7 +19,7 @@ jobs:
build: build:
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest, windows-latest, macOS-latest] os: [ubuntu-latest, ubuntu-16.04, windows-latest, macOS-latest]
fail-fast: false fail-fast: false
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
@ -47,12 +47,23 @@ jobs:
run: npm run lint run: npm run lint
- name: Build & Test - name: Build & Test
run: npm run test run: npm run test
- name: Ensure dist/ folder is up-to-date
if: ${{ runner.os == 'Linux' }}
shell: bash
run: |
npm run build
if [ "$(git diff --ignore-space-at-eol | wc -l)" -gt "0" ]; then
echo "Detected uncommitted changes after build. See status below:"
git diff
exit 1
fi
# End to end save and restore # End to end save and restore
test-save: test-save:
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest, windows-latest, macOS-latest] os: [ubuntu-latest, ubuntu-16.04, windows-latest, macOS-latest]
fail-fast: false fail-fast: false
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
@ -75,7 +86,7 @@ jobs:
needs: test-save needs: test-save
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest, windows-latest, macOS-latest] os: [ubuntu-latest, ubuntu-16.04, windows-latest, macOS-latest]
fail-fast: false fail-fast: false
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
.licenses/npm/tslib-1.13.0.dep.yml generated Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -19,6 +19,7 @@ See ["Caching dependencies to speed up workflows"](https://help.github.com/githu
path: | path: |
~/cache ~/cache
!~/cache/exclude !~/cache/exclude
**/node_modules
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
``` ```
@ -99,7 +100,6 @@ See [Examples](examples.md) for a list of `actions/cache` implementations for us
- [OCaml/Reason - esy](./examples.md#ocamlreason---esy) - [OCaml/Reason - esy](./examples.md#ocamlreason---esy)
- [PHP - Composer](./examples.md#php---composer) - [PHP - Composer](./examples.md#php---composer)
- [Python - pip](./examples.md#python---pip) - [Python - pip](./examples.md#python---pip)
- [Python - pipenv](./examples.md#python---pipenv)
- [R - renv](./examples.md#r---renv) - [R - renv](./examples.md#r---renv)
- [Ruby - Bundler](./examples.md#ruby---bundler) - [Ruby - Bundler](./examples.md#ruby---bundler)
- [Rust - Cargo](./examples.md#rust---cargo) - [Rust - Cargo](./examples.md#rust---cargo)
@ -143,7 +143,7 @@ See [Using contexts to create cache keys](https://help.github.com/en/actions/con
## Cache Limits ## Cache Limits
A repository can have up to 10GB of caches. Once the 10GB limit is reached, older caches will be evicted based on when the cache was last accessed. Caches that are not accessed within the last week will also be evicted. A repository can have up to 5GB of caches. Once the 5GB limit is reached, older caches will be evicted based on when the cache was last accessed. Caches that are not accessed within the last week will also be evicted.
## Skipping steps based on cache-hit ## Skipping steps based on cache-hit
@ -167,12 +167,6 @@ steps:
> Note: The `id` defined in `actions/cache` must match the `id` in the `if` statement (i.e. `steps.[ID].outputs.cache-hit`) > Note: The `id` defined in `actions/cache` must match the `id` in the `if` statement (i.e. `steps.[ID].outputs.cache-hit`)
## Known limitation
- `action/cache` is currently not supported on GitHub Enterprise Server. <https://github.com/github/roadmap/issues/273> is tracking this.
Since GitHub Enterprise Server uses self-hosted runners, dependencies are typically cached on the runner by whatever dependency management tool is being used (npm, maven, etc.). This eliminates the need for explicit caching in some scenarios.
## Contributing ## Contributing
We would love for you to contribute to `actions/cache`, pull requests are welcome! Please see the [CONTRIBUTING.md](CONTRIBUTING.md) for more information. We would love for you to contribute to `actions/cache`, pull requests are welcome! Please see the [CONTRIBUTING.md](CONTRIBUTING.md) for more information.

View File

@ -17,6 +17,24 @@ afterEach(() => {
delete process.env[RefKey]; delete process.env[RefKey];
}); });
test("isGhes returns true if server url is not github.com", () => {
try {
process.env["GITHUB_SERVER_URL"] = "http://example.com";
expect(actionUtils.isGhes()).toBe(true);
} finally {
process.env["GITHUB_SERVER_URL"] = undefined;
}
});
test("isGhes returns true when server url is github.com", () => {
try {
process.env["GITHUB_SERVER_URL"] = "http://github.com";
expect(actionUtils.isGhes()).toBe(false);
} finally {
process.env["GITHUB_SERVER_URL"] = undefined;
}
});
test("isExactKeyMatch with undefined cache key returns false", () => { test("isExactKeyMatch with undefined cache key returns false", () => {
const key = "linux-rust"; const key = "linux-rust";
const cacheKey = undefined; const cacheKey = undefined;
@ -195,6 +213,39 @@ test("getInputAsArray handles empty lines correctly", () => {
expect(actionUtils.getInputAsArray("foo")).toEqual(["bar", "baz"]); expect(actionUtils.getInputAsArray("foo")).toEqual(["bar", "baz"]);
}); });
test("getInputAsArray sorts files correctly", () => {
testUtils.setInput(
"foo",
"bar\n!baz\nwaldo\nqux\nquux\ncorge\ngrault\ngarply"
);
expect(actionUtils.getInputAsArray("foo")).toEqual([
"!baz",
"bar",
"corge",
"garply",
"grault",
"quux",
"qux",
"waldo"
]);
});
test("getInputAsArray removes spaces after ! at the beginning", () => {
testUtils.setInput(
"foo",
"! bar\n! baz\n! qux\n!quux\ncorge\ngrault! garply\n!\r\t waldo"
);
expect(actionUtils.getInputAsArray("foo")).toEqual([
"!bar",
"!baz",
"!quux",
"!qux",
"!waldo",
"corge",
"grault! garply"
]);
});
test("getInputAsInt returns undefined if input not set", () => { test("getInputAsInt returns undefined if input not set", () => {
expect(actionUtils.getInputAsInt("undefined")).toBeUndefined(); expect(actionUtils.getInputAsInt("undefined")).toBeUndefined();
}); });

View File

@ -32,6 +32,8 @@ beforeAll(() => {
beforeEach(() => { beforeEach(() => {
process.env[Events.Key] = Events.Push; process.env[Events.Key] = Events.Push;
process.env[RefKey] = "refs/heads/feature-branch"; process.env[RefKey] = "refs/heads/feature-branch";
jest.spyOn(actionUtils, "isGhes").mockImplementation(() => false);
}); });
afterEach(() => { afterEach(() => {
@ -53,6 +55,23 @@ test("restore with invalid event outputs warning", async () => {
expect(failedMock).toHaveBeenCalledTimes(0); expect(failedMock).toHaveBeenCalledTimes(0);
}); });
test("restore on GHES should no-op", async () => {
jest.spyOn(actionUtils, "isGhes").mockImplementation(() => true);
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const restoreCacheMock = jest.spyOn(cache, "restoreCache");
const setCacheHitOutputMock = jest.spyOn(actionUtils, "setCacheHitOutput");
await run();
expect(restoreCacheMock).toHaveBeenCalledTimes(0);
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1);
expect(setCacheHitOutputMock).toHaveBeenCalledWith(false);
expect(logWarningMock).toHaveBeenCalledWith(
"Cache action is not supported on GHES"
);
});
test("restore with no path should fail", async () => { test("restore with no path should fail", async () => {
const failedMock = jest.spyOn(core, "setFailed"); const failedMock = jest.spyOn(core, "setFailed");
const restoreCacheMock = jest.spyOn(cache, "restoreCache"); const restoreCacheMock = jest.spyOn(cache, "restoreCache");
@ -78,7 +97,7 @@ test("restore with no key", async () => {
test("restore with too many keys should fail", async () => { test("restore with too many keys should fail", async () => {
const path = "node_modules"; const path = "node_modules";
const key = "node-test"; const key = "node-test";
const restoreKeys = [...Array(20).keys()].map(x => x.toString()); const restoreKeys = [...Array(20).keys()].map(x => x.toString()).sort();
testUtils.setInputs({ testUtils.setInputs({
path: path, path: path,
key, key,

View File

@ -52,6 +52,8 @@ beforeAll(() => {
beforeEach(() => { beforeEach(() => {
process.env[Events.Key] = Events.Push; process.env[Events.Key] = Events.Push;
process.env[RefKey] = "refs/heads/feature-branch"; process.env[RefKey] = "refs/heads/feature-branch";
jest.spyOn(actionUtils, "isGhes").mockImplementation(() => false);
}); });
afterEach(() => { afterEach(() => {
@ -99,6 +101,20 @@ test("save with no primary key in state outputs warning", async () => {
expect(failedMock).toHaveBeenCalledTimes(0); expect(failedMock).toHaveBeenCalledTimes(0);
}); });
test("save on GHES should no-op", async () => {
jest.spyOn(actionUtils, "isGhes").mockImplementation(() => true);
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const saveCacheMock = jest.spyOn(cache, "saveCache");
await run();
expect(saveCacheMock).toHaveBeenCalledTimes(0);
expect(logWarningMock).toHaveBeenCalledWith(
"Cache action is not supported on GHES"
);
});
test("save with exact match returns early", async () => { test("save with exact match returns early", async () => {
const infoMock = jest.spyOn(core, "info"); const infoMock = jest.spyOn(core, "info");
const failedMock = jest.spyOn(core, "setFailed"); const failedMock = jest.spyOn(core, "setFailed");

17
dist/restore/index.js vendored
View File

@ -36328,9 +36328,14 @@ var __importStar = (this && this.__importStar) || function (mod) {
return result; return result;
}; };
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.getInputAsInt = exports.getInputAsArray = exports.isValidEvent = exports.logWarning = exports.getCacheState = exports.setOutputAndState = exports.setCacheHitOutput = exports.setCacheState = exports.isExactKeyMatch = void 0; exports.getInputAsInt = exports.getInputAsArray = exports.isValidEvent = exports.logWarning = exports.getCacheState = exports.setOutputAndState = exports.setCacheHitOutput = exports.setCacheState = exports.isExactKeyMatch = exports.isGhes = void 0;
const core = __importStar(__webpack_require__(470)); const core = __importStar(__webpack_require__(470));
const constants_1 = __webpack_require__(196); const constants_1 = __webpack_require__(196);
function isGhes() {
const ghUrl = new URL(process.env["GITHUB_SERVER_URL"] || "https://github.com");
return ghUrl.hostname.toUpperCase() !== "GITHUB.COM";
}
exports.isGhes = isGhes;
function isExactKeyMatch(key, cacheKey) { function isExactKeyMatch(key, cacheKey) {
return !!(cacheKey && return !!(cacheKey &&
cacheKey.localeCompare(key, undefined, { cacheKey.localeCompare(key, undefined, {
@ -36376,8 +36381,9 @@ function getInputAsArray(name, options) {
return core return core
.getInput(name, options) .getInput(name, options)
.split("\n") .split("\n")
.map(s => s.trim()) .map(s => s.replace(/^!\s+/, "!").trim())
.filter(x => x !== ""); .filter(x => x !== "")
.sort();
} }
exports.getInputAsArray = getInputAsArray; exports.getInputAsArray = getInputAsArray;
function getInputAsInt(name, options) { function getInputAsInt(name, options) {
@ -46714,6 +46720,11 @@ const utils = __importStar(__webpack_require__(443));
function run() { function run() {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
try { try {
if (utils.isGhes()) {
utils.logWarning("Cache action is not supported on GHES");
utils.setCacheHitOutput(false);
return;
}
// Validate inputs, this can cause task failure // Validate inputs, this can cause task failure
if (!utils.isValidEvent()) { if (!utils.isValidEvent()) {
utils.logWarning(`Event Validation Error: The event type ${process.env[constants_1.Events.Key]} is not supported because it's not tied to a branch or tag ref.`); utils.logWarning(`Event Validation Error: The event type ${process.env[constants_1.Events.Key]} is not supported because it's not tied to a branch or tag ref.`);

21
dist/save/index.js vendored
View File

@ -36328,9 +36328,14 @@ var __importStar = (this && this.__importStar) || function (mod) {
return result; return result;
}; };
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.getInputAsInt = exports.getInputAsArray = exports.isValidEvent = exports.logWarning = exports.getCacheState = exports.setOutputAndState = exports.setCacheHitOutput = exports.setCacheState = exports.isExactKeyMatch = void 0; exports.getInputAsInt = exports.getInputAsArray = exports.isValidEvent = exports.logWarning = exports.getCacheState = exports.setOutputAndState = exports.setCacheHitOutput = exports.setCacheState = exports.isExactKeyMatch = exports.isGhes = void 0;
const core = __importStar(__webpack_require__(470)); const core = __importStar(__webpack_require__(470));
const constants_1 = __webpack_require__(196); const constants_1 = __webpack_require__(196);
function isGhes() {
const ghUrl = new URL(process.env["GITHUB_SERVER_URL"] || "https://github.com");
return ghUrl.hostname.toUpperCase() !== "GITHUB.COM";
}
exports.isGhes = isGhes;
function isExactKeyMatch(key, cacheKey) { function isExactKeyMatch(key, cacheKey) {
return !!(cacheKey && return !!(cacheKey &&
cacheKey.localeCompare(key, undefined, { cacheKey.localeCompare(key, undefined, {
@ -36376,8 +36381,9 @@ function getInputAsArray(name, options) {
return core return core
.getInput(name, options) .getInput(name, options)
.split("\n") .split("\n")
.map(s => s.trim()) .map(s => s.replace(/^!\s+/, "!").trim())
.filter(x => x !== ""); .filter(x => x !== "")
.sort();
} }
exports.getInputAsArray = getInputAsArray; exports.getInputAsArray = getInputAsArray;
function getInputAsInt(name, options) { function getInputAsInt(name, options) {
@ -44893,13 +44899,13 @@ const cache = __importStar(__webpack_require__(692));
const core = __importStar(__webpack_require__(470)); const core = __importStar(__webpack_require__(470));
const constants_1 = __webpack_require__(196); const constants_1 = __webpack_require__(196);
const utils = __importStar(__webpack_require__(443)); const utils = __importStar(__webpack_require__(443));
// Catch and log any unhandled exceptions. These exceptions can leak out of the uploadChunk method in
// @actions/toolkit when a failed upload closes the file descriptor causing any in-process reads to
// throw an uncaught exception. Instead of failing this action, just warn.
process.on("uncaughtException", e => utils.logWarning(e.message));
function run() { function run() {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
try { try {
if (utils.isGhes()) {
utils.logWarning("Cache action is not supported on GHES");
return;
}
if (!utils.isValidEvent()) { if (!utils.isValidEvent()) {
utils.logWarning(`Event Validation Error: The event type ${process.env[constants_1.Events.Key]} is not supported because it's not tied to a branch or tag ref.`); utils.logWarning(`Event Validation Error: The event type ${process.env[constants_1.Events.Key]} is not supported because it's not tied to a branch or tag ref.`);
return; return;
@ -44922,7 +44928,6 @@ function run() {
yield cache.saveCache(cachePaths, primaryKey, { yield cache.saveCache(cachePaths, primaryKey, {
uploadChunkSize: utils.getInputAsInt(constants_1.Inputs.UploadChunkSize) uploadChunkSize: utils.getInputAsInt(constants_1.Inputs.UploadChunkSize)
}); });
core.info(`Cache saved with key: ${primaryKey}`);
} }
catch (error) { catch (error) {
if (error.name === cache.ValidationError.name) { if (error.name === cache.ValidationError.name) {

View File

@ -1,44 +1,38 @@
# Examples # Examples
- [C# - NuGet](#c---nuget) - [Examples](#examples)
- [D - DUB](#d---dub) - [C# - NuGet](#c---nuget)
- [POSIX](#posix) - [D - DUB](#d---dub)
- [Windows](#windows) - [Elixir - Mix](#elixir---mix)
- [Elixir - Mix](#elixir---mix) - [Go - Modules](#go---modules)
- [Go - Modules](#go---modules) - [Haskell - Cabal](#haskell---cabal)
- [Linux](#linux) - [Java - Gradle](#java---gradle)
- [macOS](#macos) - [Java - Maven](#java---maven)
- [Windows](#windows-1) - [Node - npm](#node---npm)
- [Haskell - Cabal](#haskell---cabal) - [macOS and Ubuntu](#macos-and-ubuntu)
- [Java - Gradle](#java---gradle) - [Windows](#windows)
- [Java - Maven](#java---maven) - [Using multiple systems and `npm config`](#using-multiple-systems-and-npm-config)
- [Node - npm](#node---npm) - [Node - Lerna](#node---lerna)
- [macOS and Ubuntu](#macos-and-ubuntu) - [Node - Yarn](#node---yarn)
- [Windows](#windows-2) - [Node - Yarn 2](#node---yarn-2)
- [Using multiple systems and `npm config`](#using-multiple-systems-and-npm-config) - [OCaml/Reason - esy](#ocamlreason---esy)
- [Node - Lerna](#node---lerna) - [PHP - Composer](#php---composer)
- [Node - Yarn](#node---yarn) - [Python - pip](#python---pip)
- [Node - Yarn 2](#node---yarn-2) - [Simple example](#simple-example)
- [OCaml/Reason - esy](#ocamlreason---esy) - [Multiple OSes in a workflow](#multiple-oss-in-a-workflow)
- [PHP - Composer](#php---composer) - [Using pip to get cache location](#using-pip-to-get-cache-location)
- [Python - pip](#python---pip) - [Using a script to get cache location](#using-a-script-to-get-cache-location)
- [Simple example](#simple-example) - [R - renv](#r---renv)
- [Multiple OS's in a workflow](#multiple-oss-in-a-workflow) - [Simple example](#simple-example-1)
- [Multiple OS's in a workflow with a matrix](#multiple-oss-in-a-workflow-with-a-matrix) - [Multiple OSes in a workflow](#multiple-oss-in-a-workflow-1)
- [Using pip to get cache location](#using-pip-to-get-cache-location) - [Ruby - Bundler](#ruby---bundler)
- [Python - pipenv](#python---pipenv) - [Rust - Cargo](#rust---cargo)
- [R - renv](#r---renv) - [Scala - SBT](#scala---sbt)
- [Simple example](#simple-example-1) - [Swift, Objective-C - Carthage](#swift-objective-c---carthage)
- [Multiple OS's in a workflow](#multiple-oss-in-a-workflow-1) - [Swift, Objective-C - CocoaPods](#swift-objective-c---cocoapods)
- [Ruby - Bundler](#ruby---bundler) - [Swift - Swift Package Manager](#swift---swift-package-manager)
- [Rust - Cargo](#rust---cargo)
- [Scala - SBT](#scala---sbt)
- [Swift, Objective-C - Carthage](#swift-objective-c---carthage)
- [Swift, Objective-C - CocoaPods](#swift-objective-c---cocoapods)
- [Swift - Swift Package Manager](#swift---swift-package-manager)
## C# - NuGet ## C# - NuGet
Using [NuGet lock files](https://docs.microsoft.com/nuget/consume-packages/package-references-in-project-files#locking-dependencies): Using [NuGet lock files](https://docs.microsoft.com/nuget/consume-packages/package-references-in-project-files#locking-dependencies):
```yaml ```yaml
@ -52,11 +46,10 @@ Using [NuGet lock files](https://docs.microsoft.com/nuget/consume-packages/packa
Depending on the environment, huge packages might be pre-installed in the global cache folder. Depending on the environment, huge packages might be pre-installed in the global cache folder.
With `actions/cache@v2` you can now exclude unwanted packages with [exclude pattern](https://github.com/actions/toolkit/tree/main/packages/glob#exclude-patterns) With `actions/cache@v2` you can now exclude unwanted packages with [exclude pattern](https://github.com/actions/toolkit/tree/main/packages/glob#exclude-patterns)
```yaml ```yaml
- uses: actions/cache@v2 - uses: actions/cache@v2
with: with:
path: | path: |
~/.nuget/packages ~/.nuget/packages
!~/.nuget/packages/unwanted !~/.nuget/packages/unwanted
key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
@ -103,54 +96,21 @@ steps:
``` ```
## Elixir - Mix ## Elixir - Mix
```yaml ```yaml
- uses: actions/cache@v2 - uses: actions/cache@v2
with: with:
path: | path: deps
deps key: ${{ runner.os }}-mix-${{ hashFiles(format('{0}{1}', github.workspace, '/mix.lock')) }}
_build
key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }}
restore-keys: | restore-keys: |
${{ runner.os }}-mix- ${{ runner.os }}-mix-
``` ```
## Go - Modules ## Go - Modules
### Linux
```yaml ```yaml
- uses: actions/cache@v2 - uses: actions/cache@v2
with: with:
path: | path: ~/go/pkg/mod
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
```
### macOS
```yaml
- uses: actions/cache@v2
with:
path: |
~/Library/Caches/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
```
### Windows
```yaml
- uses: actions/cache@v2
with:
path: |
%LocalAppData%\go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: | restore-keys: |
${{ runner.os }}-go- ${{ runner.os }}-go-
@ -168,21 +128,18 @@ We cache the elements of the Cabal store separately, as the entirety of `~/.caba
~/.cabal/packages ~/.cabal/packages
~/.cabal/store ~/.cabal/store
dist-newstyle dist-newstyle
key: ${{ runner.os }}-${{ matrix.ghc }}-${{ hashFiles('**/*.cabal', '**/cabal.project', '**/cabal.project.freeze') }} key: ${{ runner.os }}-${{ matrix.ghc }}
restore-keys: ${{ runner.os }}-${{ matrix.ghc }}-
``` ```
## Java - Gradle ## Java - Gradle
>Note: Ensure no Gradle daemons are running anymore when your workflow completes. Creating the cache package might fail due to locks being held by Gradle. Refer to the [Gradle Daemon documentation](https://docs.gradle.org/current/userguide/gradle_daemon.html) on how to disable or stop the Gradle Daemons.
```yaml ```yaml
- uses: actions/cache@v2 - uses: actions/cache@v2
with: with:
path: | path: |
~/.gradle/caches ~/.gradle/caches
~/.gradle/wrapper ~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}
restore-keys: | restore-keys: |
${{ runner.os }}-gradle- ${{ runner.os }}-gradle-
``` ```
@ -237,13 +194,12 @@ If using `npm config` to retrieve the cache directory, ensure you run [actions/s
```yaml ```yaml
- name: Get npm cache directory - name: Get npm cache directory
id: npm-cache-dir id: npm-cache
run: | run: |
echo "::set-output name=dir::$(npm config get cache)" echo "::set-output name=dir::$(npm config get cache)"
- uses: actions/cache@v2 - uses: actions/cache@v2
id: npm-cache # use this to check for `cache-hit` ==> if: steps.npm-cache.outputs.cache-hit != 'true'
with: with:
path: ${{ steps.npm-cache-dir.outputs.dir }} path: ${{ steps.npm-cache.outputs.dir }}
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: | restore-keys: |
${{ runner.os }}-node- ${{ runner.os }}-node-
@ -278,8 +234,8 @@ The yarn cache directory will depend on your operating system and version of `ya
${{ runner.os }}-yarn- ${{ runner.os }}-yarn-
``` ```
## Node - Yarn 2
## Node - Yarn 2
The yarn 2 cache directory will depend on your config. See https://yarnpkg.com/configuration/yarnrc#cacheFolder for more info. The yarn 2 cache directory will depend on your config. See https://yarnpkg.com/configuration/yarnrc#cacheFolder for more info.
```yaml ```yaml
@ -297,7 +253,6 @@ The yarn 2 cache directory will depend on your config. See https://yarnpkg.com/c
``` ```
## OCaml/Reason - esy ## OCaml/Reason - esy
Esy allows you to export built dependencies and import pre-built dependencies. Esy allows you to export built dependencies and import pre-built dependencies.
```yaml ```yaml
- name: Restore Cache - name: Restore Cache
@ -318,12 +273,13 @@ Esy allows you to export built dependencies and import pre-built dependencies.
...(Build job)... ...(Build job)...
# Re-export dependencies if anything has changed or if it is the first time # Re-export dependencies if anything has changed or if it is the first time
- name: Setting dependency cache - name: Setting dependency cache
run: | run: |
esy export-dependencies esy export-dependencies
if: steps.restore-cache.outputs.cache-hit != 'true' if: steps.restore-cache.outputs.cache-hit != 'true'
``` ```
## PHP - Composer ## PHP - Composer
```yaml ```yaml
@ -344,13 +300,11 @@ Esy allows you to export built dependencies and import pre-built dependencies.
For pip, the cache directory will vary by OS. See https://pip.pypa.io/en/stable/reference/pip_install/#caching For pip, the cache directory will vary by OS. See https://pip.pypa.io/en/stable/reference/pip_install/#caching
Locations: Locations:
- Ubuntu: `~/.cache/pip`
- Ubuntu: `~/.cache/pip` - Windows: `~\AppData\Local\pip\Cache`
- Windows: `~\AppData\Local\pip\Cache` - macOS: `~/Library/Caches/pip`
- macOS: `~/Library/Caches/pip`
### Simple example ### Simple example
```yaml ```yaml
- uses: actions/cache@v2 - uses: actions/cache@v2
with: with:
@ -390,31 +344,6 @@ Replace `~/.cache/pip` with the correct `path` if not using Ubuntu.
${{ runner.os }}-pip- ${{ runner.os }}-pip-
``` ```
### Multiple OS's in a workflow with a matrix
``` yaml
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
include:
- os: ubuntu-latest
path: ~/.cache/pip
- os: macos-latest
path: ~/Library/Caches/pip
- os: windows-latest
path: ~\AppData\Local\pip\Cache
steps:
- uses: actions/cache@v2
with:
path: ${{ matrix.path }}
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
```
### Using pip to get cache location ### Using pip to get cache location
> Note: This requires pip 20.1+ > Note: This requires pip 20.1+
@ -433,64 +362,89 @@ jobs:
${{ runner.os }}-pip- ${{ runner.os }}-pip-
``` ```
## Python - pipenv ### Using a script to get cache location
> Note: This uses an internal pip API and may not always work
```yaml ```yaml
- name: Set up Python - name: Get pip cache dir
# The actions/cache step below uses this id to get the exact python version id: pip-cache
id: setup-python run: |
uses: actions/setup-python@v2 python -c "from pip._internal.locations import USER_CACHE_DIR; print('::set-output name=dir::' + USER_CACHE_DIR)"
- uses: actions/cache@v2 - uses: actions/cache@v2
with: with:
path: ~/.local/share/virtualenvs path: ${{ steps.pip-cache.outputs.dir }}
key: ${{ runner.os }}-python-${{ steps.setup-python.outputs.python-version }}-pipenv-${{ hashFiles('Pipfile.lock') }} key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
``` ```
## R - renv ## R - renv
For renv, the cache directory will vary by OS. The `RENV_PATHS_ROOT` environment variable is used to set the cache location. Have a look at https://rstudio.github.io/renv/reference/paths.html#details for more details. For renv, the cache directory will vary by OS. Look at https://rstudio.github.io/renv/articles/renv.html#cache
Locations:
- Ubuntu: `~/.local/share/renv`
- macOS: `~/Library/Application Support/renv`
- Windows: `%LOCALAPPDATA%/renv`
### Simple example
```yaml
- uses: actions/cache@v2
with:
path: ~/.local/share/renv
key: ${{ runner.os }}-renv-${{ hashFiles('**/renv.lock') }}
restore-keys: |
${{ runner.os }}-renv-
```
Replace `~/.local/share/renv` with the correct `path` if not using Ubuntu.
### Multiple OS's in a workflow
```yaml ```yaml
- name: Set RENV_PATHS_ROOT - uses: actions/cache@v2
shell: bash if: startsWith(runner.os, 'Linux')
run: |
echo "RENV_PATHS_ROOT=${{ runner.temp }}/renv" >> $GITHUB_ENV
- name: Install and activate renv
run: |
install.packages("renv")
renv::activate()
shell: Rscript {0}
- name: Get R and OS version
id: get-version
run: |
cat("##[set-output name=os-version;]", sessionInfo()$running, "\n", sep = "")
cat("##[set-output name=r-version;]", R.Version()$version.string, sep = "")
shell: Rscript {0}
- name: Restore Renv package cache
uses: actions/cache@v2
with: with:
path: ${{ env.RENV_PATHS_ROOT }} path: ~/.local/share/renv
key: ${{ steps.get-version.outputs.os-version }}-${{ steps.get-version.outputs.r-version }}-${{ inputs.cache-version }}-${{ hashFiles('renv.lock') }} key: ${{ runner.os }}-renv-${{ hashFiles('**/renv.lock') }}
restore-keys: ${{ steps.get-version.outputs.os-version }}-${{ steps.get-version.outputs.r-version }}-${{inputs.cache-version }}- restore-keys: |
${{ runner.os }}-renv-
- uses: actions/cache@v2
if: startsWith(runner.os, 'macOS')
with:
path: ~/Library/Application Support/renv
key: ${{ runner.os }}-renv-${{ hashFiles('**/renv.lock') }}
restore-keys: |
${{ runner.os }}-renv-
- uses: actions/cache@v2
if: startsWith(runner.os, 'Windows')
with:
path: ~\AppData\Local\renv
key: ${{ runner.os }}-renv-${{ hashFiles('**/renv.lock') }}
restore-keys: |
${{ runner.os }}-renv-
``` ```
## Ruby - Bundler ## Ruby - Bundler
Caching gems with Bundler correctly is not trivial and just using `actions/cache` ```yaml
is [not enough](https://github.com/ruby/setup-ruby#caching-bundle-install-manually). - uses: actions/cache@v2
with:
Instead, it is recommended to use `ruby/setup-ruby`'s path: vendor/bundle
[`bundler-cache: true` option](https://github.com/ruby/setup-ruby#caching-bundle-install-automatically) key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}
whenever possible: restore-keys: |
${{ runner.os }}-gems-
```
When dependencies are installed later in the workflow, we must specify the same path for the bundler.
```yaml ```yaml
- uses: ruby/setup-ruby@v1 - name: Bundle install
with: run: |
ruby-version: ... bundle config path vendor/bundle
bundler-cache: true bundle install --jobs 4 --retry 3
``` ```
## Rust - Cargo ## Rust - Cargo
@ -499,11 +453,9 @@ whenever possible:
- uses: actions/cache@v2 - uses: actions/cache@v2
with: with:
path: | path: |
~/.cargo/bin/ ~/.cargo/registry
~/.cargo/registry/index/ ~/.cargo/git
~/.cargo/registry/cache/ target
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
``` ```
@ -513,7 +465,7 @@ whenever possible:
- name: Cache SBT - name: Cache SBT
uses: actions/cache@v2 uses: actions/cache@v2
with: with:
path: | path: |
~/.ivy2/cache ~/.ivy2/cache
~/.sbt ~/.sbt
key: ${{ runner.os }}-sbt-${{ hashFiles('**/build.sbt') }} key: ${{ runner.os }}-sbt-${{ hashFiles('**/build.sbt') }}

2860
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "cache", "name": "cache",
"version": "2.1.7", "version": "2.1.3",
"private": true, "private": true,
"description": "Cache dependencies and build outputs", "description": "Cache dependencies and build outputs",
"main": "dist/restore/index.js", "main": "dist/restore/index.js",
@ -23,29 +23,29 @@
"author": "GitHub", "author": "GitHub",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@actions/cache": "^1.0.8", "@actions/cache": "^1.0.4",
"@actions/core": "^1.2.6", "@actions/core": "^1.2.6",
"@actions/exec": "^1.0.1", "@actions/exec": "^1.0.1",
"@actions/io": "^1.1.0" "@actions/io": "^1.0.1"
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "^24.0.13", "@types/jest": "^24.0.13",
"@types/nock": "^11.1.0", "@types/nock": "^11.1.0",
"@types/node": "^12.20.7", "@types/node": "^12.0.4",
"@typescript-eslint/eslint-plugin": "^2.7.0", "@typescript-eslint/eslint-plugin": "^2.7.0",
"@typescript-eslint/parser": "^2.7.0", "@typescript-eslint/parser": "^2.7.0",
"@zeit/ncc": "^0.20.5", "@zeit/ncc": "^0.20.5",
"eslint": "^6.6.0", "eslint": "^6.6.0",
"eslint-config-prettier": "^6.15.0", "eslint-config-prettier": "^6.5.0",
"eslint-plugin-import": "^2.22.1", "eslint-plugin-import": "^2.18.2",
"eslint-plugin-jest": "^23.20.0", "eslint-plugin-jest": "^23.0.3",
"eslint-plugin-prettier": "^3.3.1", "eslint-plugin-prettier": "^3.1.1",
"eslint-plugin-simple-import-sort": "^5.0.2", "eslint-plugin-simple-import-sort": "^5.0.2",
"jest": "^24.8.0", "jest": "^24.8.0",
"jest-circus": "^24.7.1", "jest-circus": "^24.7.1",
"nock": "^11.7.0", "nock": "^11.7.0",
"prettier": "^1.19.1", "prettier": "^1.19.1",
"ts-jest": "^26.5.4", "ts-jest": "^24.0.2",
"typescript": "^3.9.9" "typescript": "^3.7.3"
} }
} }

View File

@ -6,6 +6,12 @@ import * as utils from "./utils/actionUtils";
async function run(): Promise<void> { async function run(): Promise<void> {
try { try {
if (utils.isGhes()) {
utils.logWarning("Cache action is not supported on GHES");
utils.setCacheHitOutput(false);
return;
}
// Validate inputs, this can cause task failure // Validate inputs, this can cause task failure
if (!utils.isValidEvent()) { if (!utils.isValidEvent()) {
utils.logWarning( utils.logWarning(

View File

@ -4,13 +4,13 @@ import * as core from "@actions/core";
import { Events, Inputs, State } from "./constants"; import { Events, Inputs, State } from "./constants";
import * as utils from "./utils/actionUtils"; import * as utils from "./utils/actionUtils";
// Catch and log any unhandled exceptions. These exceptions can leak out of the uploadChunk method in
// @actions/toolkit when a failed upload closes the file descriptor causing any in-process reads to
// throw an uncaught exception. Instead of failing this action, just warn.
process.on("uncaughtException", e => utils.logWarning(e.message));
async function run(): Promise<void> { async function run(): Promise<void> {
try { try {
if (utils.isGhes()) {
utils.logWarning("Cache action is not supported on GHES");
return;
}
if (!utils.isValidEvent()) { if (!utils.isValidEvent()) {
utils.logWarning( utils.logWarning(
`Event Validation Error: The event type ${ `Event Validation Error: The event type ${
@ -44,7 +44,6 @@ async function run(): Promise<void> {
await cache.saveCache(cachePaths, primaryKey, { await cache.saveCache(cachePaths, primaryKey, {
uploadChunkSize: utils.getInputAsInt(Inputs.UploadChunkSize) uploadChunkSize: utils.getInputAsInt(Inputs.UploadChunkSize)
}); });
core.info(`Cache saved with key: ${primaryKey}`);
} catch (error) { } catch (error) {
if (error.name === cache.ValidationError.name) { if (error.name === cache.ValidationError.name) {
throw error; throw error;

View File

@ -2,6 +2,13 @@ import * as core from "@actions/core";
import { Outputs, RefKey, State } from "../constants"; import { Outputs, RefKey, State } from "../constants";
export function isGhes(): boolean {
const ghUrl = new URL(
process.env["GITHUB_SERVER_URL"] || "https://github.com"
);
return ghUrl.hostname.toUpperCase() !== "GITHUB.COM";
}
export function isExactKeyMatch(key: string, cacheKey?: string): boolean { export function isExactKeyMatch(key: string, cacheKey?: string): boolean {
return !!( return !!(
cacheKey && cacheKey &&
@ -53,8 +60,9 @@ export function getInputAsArray(
return core return core
.getInput(name, options) .getInput(name, options)
.split("\n") .split("\n")
.map(s => s.trim()) .map(s => s.replace(/^!\s+/, "!").trim())
.filter(x => x !== ""); .filter(x => x !== "")
.sort();
} }
export function getInputAsInt( export function getInputAsInt(