diff --git a/.github/workflows/check-dist.yml b/.github/workflows/check-dist.yml new file mode 100644 index 0000000..16c2117 --- /dev/null +++ b/.github/workflows/check-dist.yml @@ -0,0 +1,52 @@ +# `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/ diff --git a/.github/workflows/licensed.yml b/.github/workflows/licensed.yml index ae62613..4c1dfe5 100644 --- a/.github/workflows/licensed.yml +++ b/.github/workflows/licensed.yml @@ -1,8 +1,12 @@ name: Licensed on: - push: {branches: main} - pull_request: {branches: main} + push: + branches: + - main + pull_request: + branches: + - main jobs: test: @@ -17,4 +21,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 sudo tar -xzf licensed.tar.gz sudo mv licensed /usr/local/bin/licensed - - run: licensed status \ No newline at end of file + - run: licensed status diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 460d7dd..a5dd3b7 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -47,17 +47,6 @@ jobs: run: npm run lint - name: Build & 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 test-save: diff --git a/__tests__/restore.test.ts b/__tests__/restore.test.ts index 446237c..4761782 100644 --- a/__tests__/restore.test.ts +++ b/__tests__/restore.test.ts @@ -68,7 +68,7 @@ test("restore on GHES should no-op", async () => { expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1); expect(setCacheHitOutputMock).toHaveBeenCalledWith(false); expect(logWarningMock).toHaveBeenCalledWith( - "Cache action is not supported on GHES" + "Cache action is not supported on GHES. See https://github.com/actions/cache/issues/505 for more details" ); }); diff --git a/__tests__/save.test.ts b/__tests__/save.test.ts index b806d50..30178a2 100644 --- a/__tests__/save.test.ts +++ b/__tests__/save.test.ts @@ -111,7 +111,7 @@ test("save on GHES should no-op", async () => { expect(saveCacheMock).toHaveBeenCalledTimes(0); expect(logWarningMock).toHaveBeenCalledWith( - "Cache action is not supported on GHES" + "Cache action is not supported on GHES. See https://github.com/actions/cache/issues/505 for more details" ); }); diff --git a/dist/restore/index.js b/dist/restore/index.js index e13e548..59dd8fb 100644 --- a/dist/restore/index.js +++ b/dist/restore/index.js @@ -49218,7 +49218,7 @@ function run() { return __awaiter(this, void 0, void 0, function* () { try { if (utils.isGhes()) { - utils.logWarning("Cache action is not supported on GHES"); + utils.logWarning("Cache action is not supported on GHES. See https://github.com/actions/cache/issues/505 for more details"); utils.setCacheHitOutput(false); return; } diff --git a/dist/save/index.js b/dist/save/index.js index 1aad367..bd78ec6 100644 --- a/dist/save/index.js +++ b/dist/save/index.js @@ -47154,11 +47154,15 @@ const cache = __importStar(__webpack_require__(692)); const core = __importStar(__webpack_require__(470)); const constants_1 = __webpack_require__(196); 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() { return __awaiter(this, void 0, void 0, function* () { try { if (utils.isGhes()) { - utils.logWarning("Cache action is not supported on GHES"); + utils.logWarning("Cache action is not supported on GHES. See https://github.com/actions/cache/issues/505 for more details"); return; } if (!utils.isValidEvent()) { diff --git a/examples.md b/examples.md index 1a11d6e..2132bee 100644 --- a/examples.md +++ b/examples.md @@ -1,40 +1,45 @@ # Examples -- [Examples](#examples) - - [C# - NuGet](#c---nuget) - - [D - DUB](#d---dub) - - [Elixir - Mix](#elixir---mix) - - [Go - Modules](#go---modules) - - [Haskell - Cabal](#haskell---cabal) - - [Java - Gradle](#java---gradle) - - [Java - Maven](#java---maven) - - [Node - npm](#node---npm) - - [macOS and Ubuntu](#macos-and-ubuntu) - - [Windows](#windows) - - [Using multiple systems and `npm config`](#using-multiple-systems-and-npm-config) - - [Node - Lerna](#node---lerna) - - [Node - Yarn](#node---yarn) - - [Node - Yarn 2](#node---yarn-2) - - [OCaml/Reason - esy](#ocamlreason---esy) - - [PHP - Composer](#php---composer) - - [Python - pip](#python---pip) - - [Simple example](#simple-example) - - [Multiple OSes in a workflow](#multiple-oss-in-a-workflow) - - [Using pip to get cache location](#using-pip-to-get-cache-location) - - [Using a script to get cache location](#using-a-script-to-get-cache-location) - - [Python - pipenv](#python---pipenv) - - [R - renv](#r---renv) - - [Simple example](#simple-example-1) - - [Multiple OSes in a workflow](#multiple-oss-in-a-workflow-1) - - [Ruby - Bundler](#ruby---bundler) - - [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) - - [* - Bazel](#---bazel) +- [C# - NuGet](#c---nuget) +- [D - DUB](#d---dub) + - [POSIX](#posix) + - [Windows](#windows) +- [Elixir - Mix](#elixir---mix) +- [Go - Modules](#go---modules) + - [Linux](#linux) + - [macOS](#macos) + - [Windows](#windows-1) +- [Haskell - Cabal](#haskell---cabal) +- [Java - Gradle](#java---gradle) +- [Java - Maven](#java---maven) +- [Node - npm](#node---npm) + - [macOS and Ubuntu](#macos-and-ubuntu) + - [Windows](#windows-2) + - [Using multiple systems and `npm config`](#using-multiple-systems-and-npm-config) +- [Node - Lerna](#node---lerna) +- [Node - Yarn](#node---yarn) +- [Node - Yarn 2](#node---yarn-2) +- [OCaml/Reason - esy](#ocamlreason---esy) +- [PHP - Composer](#php---composer) +- [Python - pip](#python---pip) + - [Simple example](#simple-example) + - [Multiple OS's in a workflow](#multiple-oss-in-a-workflow) + - [Multiple OS's in a workflow with a matrix](#multiple-oss-in-a-workflow-with-a-matrix) + - [Using pip to get cache location](#using-pip-to-get-cache-location) +- [Python - pipenv](#python---pipenv) +- [R - renv](#r---renv) + - [Simple example](#simple-example-1) + - [Multiple OS's in a workflow](#multiple-oss-in-a-workflow-1) +- [Ruby - Bundler](#ruby---bundler) +- [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) +- [* - Bazel](#---bazel) ## C# - NuGet + Using [NuGet lock files](https://docs.microsoft.com/nuget/consume-packages/package-references-in-project-files#locking-dependencies): ```yaml @@ -48,10 +53,11 @@ 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. 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 - uses: actions/cache@v2 with: - path: | + path: | ~/.nuget/packages !~/.nuget/packages/unwanted key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} @@ -112,10 +118,40 @@ steps: ## Go - Modules +### Linux + ```yaml - uses: actions/cache@v2 with: - path: ~/go/pkg/mod + path: | + ~/.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') }} restore-keys: | ${{ runner.os }}-go- @@ -138,6 +174,8 @@ We cache the elements of the Cabal store separately, as the entirety of `~/.caba ## 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 - uses: actions/cache@v2 with: @@ -240,8 +278,8 @@ The yarn cache directory will depend on your operating system and version of `ya ${{ runner.os }}-yarn- ``` - ## Node - Yarn 2 + The yarn 2 cache directory will depend on your config. See https://yarnpkg.com/configuration/yarnrc#cacheFolder for more info. ```yaml @@ -259,6 +297,7 @@ The yarn 2 cache directory will depend on your config. See https://yarnpkg.com/c ``` ## OCaml/Reason - esy + Esy allows you to export built dependencies and import pre-built dependencies. ```yaml - name: Restore Cache @@ -279,13 +318,12 @@ Esy allows you to export built dependencies and import pre-built dependencies. ...(Build job)... # Re-export dependencies if anything has changed or if it is the first time - - name: Setting dependency cache + - name: Setting dependency cache run: | esy export-dependencies if: steps.restore-cache.outputs.cache-hit != 'true' ``` - ## PHP - Composer ```yaml @@ -306,11 +344,13 @@ 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 Locations: - - Ubuntu: `~/.cache/pip` - - Windows: `~\AppData\Local\pip\Cache` - - macOS: `~/Library/Caches/pip` + +- Ubuntu: `~/.cache/pip` +- Windows: `~\AppData\Local\pip\Cache` +- macOS: `~/Library/Caches/pip` ### Simple example + ```yaml - uses: actions/cache@v2 with: @@ -396,61 +436,45 @@ jobs: ## Python - pipenv ```yaml +- name: Set up Python + # The actions/cache step below uses this id to get the exact python version + id: setup-python + uses: actions/setup-python@v2 + + ⋮ + - uses: actions/cache@v2 with: path: ~/.local/share/virtualenvs - key: ${{ runner.os }}-pipenv-${{ hashFiles('Pipfile.lock') }} - restore-keys: | - ${{ runner.os }}-pipenv- + key: ${{ runner.os }}-python-${{ steps.setup-python.outputs.python-version }}-pipenv-${{ hashFiles('Pipfile.lock') }} ``` ## R - renv -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 +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. ```yaml -- uses: actions/cache@v2 - if: startsWith(runner.os, 'Linux') +- name: Set RENV_PATHS_ROOT + shell: bash + 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: - path: ~/.local/share/renv - key: ${{ runner.os }}-renv-${{ hashFiles('**/renv.lock') }} - 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- + path: ${{ env.RENV_PATHS_ROOT }} + key: ${{ steps.get-version.outputs.os-version }}-${{ steps.get-version.outputs.r-version }}-${{ inputs.cache-version }}-${{ hashFiles('renv.lock') }} + restore-keys: ${{ steps.get-version.outputs.os-version }}-${{ steps.get-version.outputs.r-version }}-${{inputs.cache-version }}- ``` ## Ruby - Bundler @@ -475,9 +499,11 @@ whenever possible: - uses: actions/cache@v2 with: path: | - ~/.cargo/registry - ~/.cargo/git - target + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} ``` @@ -487,7 +513,7 @@ whenever possible: - name: Cache SBT uses: actions/cache@v2 with: - path: | + path: | ~/.ivy2/cache ~/.sbt key: ${{ runner.os }}-sbt-${{ hashFiles('**/build.sbt') }} diff --git a/package-lock.json b/package-lock.json index aa4f6bf..8086560 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cache", - "version": "2.1.5", + "version": "2.1.6", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -6473,9 +6473,9 @@ } }, "ws": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", - "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.3.tgz", + "integrity": "sha512-jZArVERrMsKUatIdnLzqvcfydI85dvd/Fp1u/VOpfdDWQ4c9qWXe+VIeAbQ5FrDwciAkr+lzofXLz3Kuf26AOA==", "dev": true, "requires": { "async-limiter": "~1.0.0" diff --git a/package.json b/package.json index 167fe9f..5fe21e8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cache", - "version": "2.1.5", + "version": "2.1.6", "private": true, "description": "Cache dependencies and build outputs", "main": "dist/restore/index.js", diff --git a/src/restore.ts b/src/restore.ts index 0fa6333..d411459 100644 --- a/src/restore.ts +++ b/src/restore.ts @@ -7,7 +7,9 @@ import * as utils from "./utils/actionUtils"; async function run(): Promise { try { if (utils.isGhes()) { - utils.logWarning("Cache action is not supported on GHES"); + utils.logWarning( + "Cache action is not supported on GHES. See https://github.com/actions/cache/issues/505 for more details" + ); utils.setCacheHitOutput(false); return; } diff --git a/src/save.ts b/src/save.ts index 5e52249..fc0eb73 100644 --- a/src/save.ts +++ b/src/save.ts @@ -4,10 +4,17 @@ import * as core from "@actions/core"; import { Events, Inputs, State } from "./constants"; 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 { try { if (utils.isGhes()) { - utils.logWarning("Cache action is not supported on GHES"); + utils.logWarning( + "Cache action is not supported on GHES. See https://github.com/actions/cache/issues/505 for more details" + ); return; }