diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index e13350d..775fb59 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -13,6 +13,8 @@ jobs: - run: npm install -g pnpm - run: pnpm install - run: pnpm run build - - uses: JasonEtco/build-and-tag-action@v2 + - uses: aboutte/build-and-tag-action@v2 + with: + additional_files: 'dist/post/index.js' env: GITHUB_TOKEN: ${{ github.token }} diff --git a/README.md b/README.md index 36d5633..3adf639 100644 --- a/README.md +++ b/README.md @@ -13,13 +13,6 @@ jobs: - run: cargo test ``` -## To Do - -- [x] Install Rust toolchain with `rustup` -- [x] Install Cargo bins with `cargo-binstall` -- [ ] Cache `~/.cargo` directory (not everything) -- [ ] Cache `target` directory? - ## Configuring the Rust toolchain This action will automatically install the appropriate toolchain with `rustup` by inspecting the @@ -80,6 +73,30 @@ names (`cargo-` prefix optional). > Binaries are installed with [`cargo-binstall`](https://crates.io/crates/cargo-binstall) under the > hood. +## Caching in CI + +By default this action will cache the `~/.cargo/registry` and `/target/debug` directories to improve +CI times. To disable caching, set the `cache` input to `false`. + +```yaml +- uses: moonrepo/setup-rust@v0 + with: + cache: false +``` + +The following optimizations and considerations are taken into account when caching: + +- The `~/.cargo/bin` directory is not cached as we manage binary installation in this action via the + `bins` input. +- The `~/.cargo/git` directory is not cached as it's not necessary for CI. When required by Cargo or + a crate, a checkout will be performed on-demand. +- The `~/.cargo/registry` directory is _cleaned_ before saving the cache. This includes removing + `src`, `.cache`, and any other unnecessary files. +- Only the `/target/debug` profile is cached, as this profile is typically used for formatting, + linting, and testing. +- The following sources are hashed for the generated cache key: `Cargo.lock`, Rust version, Rust + commit hash, and operating system. + ## Compared to ### `actions-rs/*` @@ -89,6 +106,7 @@ maintenance, and being full of deprecation warnings, it was time to create somet Outside of being evergreen, this action also supports the following features: +- Automatically caches. - Installs Cargo bins. - Assumes `rustup`, `cargo`, and other commands are available globally. This allows you to use them directly in a `run` command, without having to use `actions-rs/cargo`. @@ -105,4 +123,5 @@ but this action also supports the following features: - Extracts the toolchain/channel from `rust-toolchain.toml` and `rust-toolchain` configuration files. +- Automatically caches. - Installs Cargo bins. diff --git a/action.yml b/action.yml index a7d85b1..8a52c54 100644 --- a/action.yml +++ b/action.yml @@ -6,6 +6,9 @@ description: inputs: bins: description: 'Comma-separated list of global binaries to install into Cargo.' + cache: + description: 'Toggle caching of ~/.cargo/registry and /target/debug directories.' + default: true channel: description: 'Toolchain specification/channel to install.' components: @@ -14,10 +17,19 @@ inputs: description: 'Comma-separated list of additional targets to install.' profile: description: 'Profile to install. Defaults to "minimal".' -outputs: {} +outputs: + cache-key: + description: 'The generated cache key used.' + cache-hit: + description: 'Indicates an exact match was found for the cache key.' + rust-version: + description: 'Version of the installed rustc.' + rust-hash: + description: 'Commit hash of the installed rustc.' runs: using: 'node16' main: 'dist/index.js' + post: 'dist/post/index.js' branding: icon: 'settings' color: 'orange' diff --git a/index.ts b/index.ts index effd379..16160fb 100644 --- a/index.ts +++ b/index.ts @@ -1,10 +1,11 @@ import fs from 'fs'; -import os from 'os'; import path from 'path'; import * as core from '@actions/core'; import * as exec from '@actions/exec'; import * as tc from '@actions/tool-cache'; import TOML from '@ltd/j-toml'; +import { CACHE_ENABLED, CARGO_HOME, restoreCache } from './src/cargo'; +import { extractRustVersion } from './src/rust'; interface Toolchain { channel: string; @@ -24,14 +25,6 @@ const DEFAULT_TOOLCHAIN: Toolchain = { targets: [], }; -function getCargoHome(): string { - if (process.env.CARGO_HOME) { - return process.env.CARGO_HOME; - } - - return path.join(os.homedir(), '.cargo'); -} - function parseConfig(configPath: string): Partial { const contents = fs.readFileSync(configPath, 'utf8').trim(); @@ -126,7 +119,7 @@ async function installToolchain(toolchain: Toolchain) { core.info('Logging installed toolchain versions'); - await exec.exec('rustc', [`+${toolchain.channel}`, '--version', '--verbose']); + await extractRustVersion(toolchain.channel); } async function downloadAndInstallBinstall(binDir: string) { @@ -178,13 +171,17 @@ async function installBins() { .filter(Boolean) .map((bin) => (bin.startsWith('cargo-') ? bin : `cargo-${bin}`)); + if (CACHE_ENABLED) { + bins.push('cargo-cache'); + } + if (bins.length === 0) { return; } core.info('Installing additional binaries'); - const binDir = path.join(getCargoHome(), 'bin'); + const binDir = path.join(CARGO_HOME, 'bin'); if (!fs.existsSync(path.join(binDir, 'cargo-binstall'))) { await downloadAndInstallBinstall(binDir); @@ -205,6 +202,10 @@ async function run() { try { await installToolchain(detectToolchain()); await installBins(); + + // Restore cache after the toolchain has been installed, + // as we use the rust version and commit hashes in the cache key! + await restoreCache(); } catch (error: unknown) { core.setFailed((error as Error).message); @@ -213,7 +214,7 @@ async function run() { core.info('Adding ~/.cargo/bin to PATH'); - core.addPath(path.join(getCargoHome(), 'bin')); + core.addPath(path.join(CARGO_HOME, 'bin')); } void run(); diff --git a/package.json b/package.json index 17c6cde..f639aa5 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "A GitHub action for setting up Rust and Cargo.", "main": "dist/index.js", "scripts": { - "build": "ncc build ./index.ts", + "build": "ncc build ./index.ts && ncc build ./post.ts --out ./dist/post", "check": "npm run lint && npm run typecheck", "lint": "eslint --ext .ts,.js --fix .", "typecheck": "tsc --noEmit" @@ -16,8 +16,10 @@ "author": "Miles Johnson", "license": "MIT", "dependencies": { + "@actions/cache": "^3.2.1", "@actions/core": "^1.10.0", "@actions/exec": "^1.1.1", + "@actions/glob": "^0.4.0", "@actions/tool-cache": "^2.0.1", "@ltd/j-toml": "^1.38.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d35ecbb..d56c81f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,12 +1,18 @@ lockfileVersion: '6.0' dependencies: + '@actions/cache': + specifier: ^3.2.1 + version: 3.2.1 '@actions/core': specifier: ^1.10.0 version: 1.10.0 '@actions/exec': specifier: ^1.1.1 version: 1.1.1 + '@actions/glob': + specifier: ^0.4.0 + version: 0.4.0 '@actions/tool-cache': specifier: ^2.0.1 version: 2.0.1 @@ -42,6 +48,23 @@ devDependencies: packages: + /@actions/cache@3.2.1: + resolution: {integrity: sha512-QurbMiY//02+0kN1adJkMHN44RcZ5kAXfhSnKUZmtSmhMTNqLitGArG1xOkt93NNyByTlLGAc5wIOF/dZ2ENOQ==} + dependencies: + '@actions/core': 1.10.0 + '@actions/exec': 1.1.1 + '@actions/glob': 0.1.2 + '@actions/http-client': 2.1.0 + '@actions/io': 1.1.3 + '@azure/abort-controller': 1.1.0 + '@azure/ms-rest-js': 2.6.6 + '@azure/storage-blob': 12.14.0 + semver: 6.3.0 + uuid: 3.4.0 + transitivePeerDependencies: + - encoding + dev: false + /@actions/core@1.10.0: resolution: {integrity: sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==} dependencies: @@ -55,6 +78,20 @@ packages: '@actions/io': 1.1.3 dev: false + /@actions/glob@0.1.2: + resolution: {integrity: sha512-SclLR7Ia5sEqjkJTPs7Sd86maMDw43p769YxBOxvPvEWuPEhpAnBsQfENOpXjFYMmhCqd127bmf+YdvJqVqR4A==} + dependencies: + '@actions/core': 1.10.0 + minimatch: 3.1.2 + dev: false + + /@actions/glob@0.4.0: + resolution: {integrity: sha512-+eKIGFhsFa4EBwaf/GMyzCdWrXWymGXfFmZU3FHQvYS8mPcHtTtZONbkcqqUMzw9mJ/pImEBFET1JNifhqGsAQ==} + dependencies: + '@actions/core': 1.10.0 + minimatch: 3.1.2 + dev: false + /@actions/http-client@2.1.0: resolution: {integrity: sha512-BonhODnXr3amchh4qkmjPMUO8mFi/zLaaCeCAJZqch8iQqyDnVIkySjB38VHAC8IJ+bnlgfOqlhpyCUZHlQsqw==} dependencies: @@ -76,6 +113,115 @@ packages: uuid: 3.4.0 dev: false + /@azure/abort-controller@1.1.0: + resolution: {integrity: sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==} + engines: {node: '>=12.0.0'} + dependencies: + tslib: 2.5.0 + dev: false + + /@azure/core-auth@1.4.0: + resolution: {integrity: sha512-HFrcTgmuSuukRf/EdPmqBrc5l6Q5Uu+2TbuhaKbgaCpP2TfAeiNaQPAadxO+CYBRHGUzIDteMAjFspFLDLnKVQ==} + engines: {node: '>=12.0.0'} + dependencies: + '@azure/abort-controller': 1.1.0 + tslib: 2.5.0 + dev: false + + /@azure/core-http@3.0.1: + resolution: {integrity: sha512-A3x+um3cAPgQe42Lu7Iv/x8/fNjhL/nIoEfqFxfn30EyxK6zC13n+OUxzZBRC0IzQqssqIbt4INf5YG7lYYFtw==} + engines: {node: '>=14.0.0'} + dependencies: + '@azure/abort-controller': 1.1.0 + '@azure/core-auth': 1.4.0 + '@azure/core-tracing': 1.0.0-preview.13 + '@azure/core-util': 1.3.1 + '@azure/logger': 1.0.4 + '@types/node-fetch': 2.6.3 + '@types/tunnel': 0.0.3 + form-data: 4.0.0 + node-fetch: 2.6.9 + process: 0.11.10 + tslib: 2.5.0 + tunnel: 0.0.6 + uuid: 8.3.2 + xml2js: 0.5.0 + transitivePeerDependencies: + - encoding + dev: false + + /@azure/core-lro@2.5.2: + resolution: {integrity: sha512-tucUutPhBwCPu6v16KEFYML81npEL6gnT+iwewXvK5ZD55sr0/Vw2jfQETMiKVeARRrXHB2QQ3SpxxGi1zAUWg==} + engines: {node: '>=14.0.0'} + dependencies: + '@azure/abort-controller': 1.1.0 + '@azure/core-util': 1.3.1 + '@azure/logger': 1.0.4 + tslib: 2.5.0 + dev: false + + /@azure/core-paging@1.5.0: + resolution: {integrity: sha512-zqWdVIt+2Z+3wqxEOGzR5hXFZ8MGKK52x4vFLw8n58pR6ZfKRx3EXYTxTaYxYHc/PexPUTyimcTWFJbji9Z6Iw==} + engines: {node: '>=14.0.0'} + dependencies: + tslib: 2.5.0 + dev: false + + /@azure/core-tracing@1.0.0-preview.13: + resolution: {integrity: sha512-KxDlhXyMlh2Jhj2ykX6vNEU0Vou4nHr025KoSEiz7cS3BNiHNaZcdECk/DmLkEB0as5T7b/TpRcehJ5yV6NeXQ==} + engines: {node: '>=12.0.0'} + dependencies: + '@opentelemetry/api': 1.4.1 + tslib: 2.5.0 + dev: false + + /@azure/core-util@1.3.1: + resolution: {integrity: sha512-pjfOUAb+MPLODhGuXot/Hy8wUgPD0UTqYkY3BiYcwEETrLcUCVM1t0roIvlQMgvn1lc48TGy5bsonsFpF862Jw==} + engines: {node: '>=14.0.0'} + dependencies: + '@azure/abort-controller': 1.1.0 + tslib: 2.5.0 + dev: false + + /@azure/logger@1.0.4: + resolution: {integrity: sha512-ustrPY8MryhloQj7OWGe+HrYx+aoiOxzbXTtgblbV3xwCqpzUK36phH3XNHQKj3EPonyFUuDTfR3qFhTEAuZEg==} + engines: {node: '>=14.0.0'} + dependencies: + tslib: 2.5.0 + dev: false + + /@azure/ms-rest-js@2.6.6: + resolution: {integrity: sha512-WYIda8VvrkZE68xHgOxUXvjThxNf1nnGPPe0rAljqK5HJHIZ12Pi3YhEDOn3Ge7UnwaaM3eFO0VtAy4nGVI27Q==} + dependencies: + '@azure/core-auth': 1.4.0 + abort-controller: 3.0.0 + form-data: 2.5.1 + node-fetch: 2.6.9 + tough-cookie: 3.0.1 + tslib: 1.14.1 + tunnel: 0.0.6 + uuid: 8.3.2 + xml2js: 0.5.0 + transitivePeerDependencies: + - encoding + dev: false + + /@azure/storage-blob@12.14.0: + resolution: {integrity: sha512-g8GNUDpMisGXzBeD+sKphhH5yLwesB4JkHr1U6be/X3F+cAMcyGLPD1P89g2M7wbEtUJWoikry1rlr83nNRBzg==} + engines: {node: '>=14.0.0'} + dependencies: + '@azure/abort-controller': 1.1.0 + '@azure/core-http': 3.0.1 + '@azure/core-lro': 2.5.2 + '@azure/core-paging': 1.5.0 + '@azure/core-tracing': 1.0.0-preview.13 + '@azure/logger': 1.0.4 + events: 3.3.0 + tslib: 2.5.0 + transitivePeerDependencies: + - encoding + dev: false + /@babel/code-frame@7.21.4: resolution: {integrity: sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==} engines: {node: '>=6.9.0'} @@ -199,6 +345,11 @@ packages: fastq: 1.15.0 dev: true + /@opentelemetry/api@1.4.1: + resolution: {integrity: sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA==} + engines: {node: '>=8.0.0'} + dev: false + /@tsconfig/node14@1.0.3: resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} dev: true @@ -211,9 +362,15 @@ packages: resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} dev: true + /@types/node-fetch@2.6.3: + resolution: {integrity: sha512-ETTL1mOEdq/sxUtgtOhKjyB2Irra4cjxksvcMUR5Zr4n+PxVhsCD9WS46oPbHL3et9Zde7CNRr+WUNlcHvsX+w==} + dependencies: + '@types/node': 18.15.10 + form-data: 3.0.1 + dev: false + /@types/node@18.15.10: resolution: {integrity: sha512-9avDaQJczATcXgfmMAW3MIWArOO7A+m90vuCFLr8AotWf8igO/mRoYukrk2cqZVtv38tHs33retzHEilM7FpeQ==} - dev: true /@types/normalize-package-data@2.4.1: resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} @@ -223,6 +380,12 @@ packages: resolution: {integrity: sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==} dev: true + /@types/tunnel@0.0.3: + resolution: {integrity: sha512-sOUTGn6h1SfQ+gbgqC364jLFBw2lnFqkgF3q0WovEHRLMrVD1sd5aufqi/aJObLekJO+Aq5z646U4Oxy6shXMA==} + dependencies: + '@types/node': 18.15.10 + dev: false + /@typescript-eslint/eslint-plugin@5.58.0(@typescript-eslint/parser@5.58.0)(eslint@8.36.0)(typescript@5.0.2): resolution: {integrity: sha512-vxHvLhH0qgBd3/tW6/VccptSfc8FxPQIkmNTVLWcCOVqSBvqpnKkBTYrhcGlXfSnd78azwe+PsjYFj0X34/njA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -358,6 +521,13 @@ packages: hasBin: true dev: true + /abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + dependencies: + event-target-shim: 5.0.1 + dev: false + /acorn-jsx@5.3.2(acorn@8.8.2): resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -473,6 +643,10 @@ packages: resolution: {integrity: sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==} dev: true + /asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + dev: false + /available-typed-arrays@1.0.5: resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} engines: {node: '>= 0.4'} @@ -491,14 +665,12 @@ packages: /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - dev: true /brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - dev: true /braces@3.0.2: resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} @@ -589,9 +761,15 @@ packages: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} dev: true + /combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + dependencies: + delayed-stream: 1.0.0 + dev: false + /concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - dev: true /confusing-browser-globals@1.0.11: resolution: {integrity: sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==} @@ -672,6 +850,11 @@ packages: object-keys: 1.1.1 dev: true + /delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + dev: false + /dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -1239,6 +1422,16 @@ packages: engines: {node: '>=0.10.0'} dev: true + /event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + dev: false + + /events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + dev: false + /execa@5.1.1: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} @@ -1331,6 +1524,33 @@ packages: is-callable: 1.2.7 dev: true + /form-data@2.5.1: + resolution: {integrity: sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==} + engines: {node: '>= 0.12'} + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + dev: false + + /form-data@3.0.1: + resolution: {integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==} + engines: {node: '>= 6'} + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + dev: false + + /form-data@4.0.0: + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + engines: {node: '>= 6'} + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + dev: false + /fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} dev: true @@ -1540,6 +1760,11 @@ packages: side-channel: 1.0.4 dev: true + /ip-regex@2.1.0: + resolution: {integrity: sha512-58yWmlHpp7VYfcdTwMTvwMmqx/Elfxjd9RXTDyMsbL7lLWmhMylLEqiYVLKuLzOZqVgiWXD9MfR62Vv89VRxkw==} + engines: {node: '>=4'} + dev: false + /is-arguments@1.1.1: resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} engines: {node: '>= 0.4'} @@ -1857,6 +2082,18 @@ packages: picomatch: 2.3.1 dev: true + /mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + dev: false + + /mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + dependencies: + mime-db: 1.52.0 + dev: false + /mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} @@ -1871,7 +2108,6 @@ packages: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} dependencies: brace-expansion: 1.1.11 - dev: true /minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} @@ -1893,6 +2129,18 @@ packages: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true + /node-fetch@2.6.9: + resolution: {integrity: sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + dependencies: + whatwg-url: 5.0.0 + dev: false + /node-releases@2.0.10: resolution: {integrity: sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==} dev: true @@ -2107,6 +2355,11 @@ packages: hasBin: true dev: true + /process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + dev: false + /prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} dependencies: @@ -2115,10 +2368,13 @@ packages: react-is: 16.13.1 dev: true + /psl@1.9.0: + resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} + dev: false + /punycode@2.3.0: resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} engines: {node: '>=6'} - dev: true /queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -2232,6 +2488,10 @@ packages: regexp-tree: 0.1.24 dev: true + /sax@1.2.4: + resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==} + dev: false + /semver@5.7.1: resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==} hasBin: true @@ -2418,6 +2678,19 @@ packages: is-number: 7.0.0 dev: true + /tough-cookie@3.0.1: + resolution: {integrity: sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==} + engines: {node: '>=6'} + dependencies: + ip-regex: 2.1.0 + psl: 1.9.0 + punycode: 2.3.0 + dev: false + + /tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + dev: false + /tsconfig-moon@1.3.0: resolution: {integrity: sha512-OVa+cjaKIsXIQqEWVqvF3xH6xmFOjKnbwiCmAKx2J8uq7M1KTX/NFFi/YXZdKb6YjHJhq8tvg2JsGSBTxTANcQ==} dev: true @@ -2433,7 +2706,10 @@ packages: /tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} - dev: true + + /tslib@2.5.0: + resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==} + dev: false /tsutils@3.21.0(typescript@5.0.2): resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} @@ -2530,6 +2806,17 @@ packages: spdx-expression-parse: 3.0.1 dev: true + /webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + dev: false + + /whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + dev: false + /which-boxed-primitive@1.0.2: resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} dependencies: @@ -2578,6 +2865,19 @@ packages: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} dev: true + /xml2js@0.5.0: + resolution: {integrity: sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==} + engines: {node: '>=4.0.0'} + dependencies: + sax: 1.2.4 + xmlbuilder: 11.0.1 + dev: false + + /xmlbuilder@11.0.1: + resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==} + engines: {node: '>=4.0'} + dev: false + /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} dev: true diff --git a/post.ts b/post.ts new file mode 100644 index 0000000..077c816 --- /dev/null +++ b/post.ts @@ -0,0 +1,12 @@ +import * as core from '@actions/core'; +import { saveCache } from './src/cargo'; + +async function run() { + try { + await saveCache(); + } catch (error: unknown) { + core.setFailed((error as Error).message); + } +} + +void run(); diff --git a/src/cargo.ts b/src/cargo.ts new file mode 100644 index 0000000..db480e5 --- /dev/null +++ b/src/cargo.ts @@ -0,0 +1,120 @@ +/* eslint-disable node/no-unsupported-features/node-builtins */ + +import crypto from 'crypto'; +import fs from 'fs'; +import os from 'os'; +import path from 'path'; +import * as cache from '@actions/cache'; +import * as core from '@actions/core'; +import * as exec from '@actions/exec'; +import * as glob from '@actions/glob'; + +// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing +export const CARGO_HOME = process.env.CARGO_HOME || path.join(os.homedir(), '.cargo'); + +export const CACHE_ENABLED = core.getBooleanInput('cache') || cache.isFeatureAvailable(); + +let CACHE_KEY = ''; + +export async function getPrimaryCacheKey() { + if (CACHE_KEY) { + return CACHE_KEY; + } + + core.info('Generating cache key'); + + const rustVersion = core.getState('rust-version'); + + core.debug(`Hashing Rust version = ${rustVersion}`); + + const rustHash = core.getState('rust-hash'); + + core.debug(`Hashing Rust commit hash = ${rustHash}`); + + const lockHash = await glob.hashFiles('Cargo.lock'); + + core.debug(`Hashing Cargo.lock = ${lockHash}`); + + const hasher = crypto.createHash('sha1'); + hasher.update(rustVersion); + hasher.update(rustHash); + hasher.update(lockHash); + + // eslint-disable-next-line require-atomic-updates + CACHE_KEY = `setup-rustcargo-${process.platform}-${hasher.digest('hex')}`; + + return CACHE_KEY; +} + +export function getPathsToCache(): string[] { + return [ + // ~/.cargo/registry + path.join(CARGO_HOME, 'registry'), + // /workspace/target/debug + path.join(process.cwd(), 'target/debug'), + ]; +} + +export async function cleanCargoRegistry() { + core.info('Cleaning cache before saving'); + + const registryDir = path.join(CARGO_HOME, 'registry'); + + // .cargo/registry/src - Delete entirely + await exec.exec('cargo', ['cache', '--autoclean']); + + // .cargo/registry/index - Delete .cache directories + const globber = await glob.create(path.join(registryDir, 'index/**/.cache')); + + for await (const file of globber.globGenerator()) { + core.debug(`Deleting ${file}`); + await fs.promises.unlink(file); + } + + // .cargo/registry/cache - Do nothing? +} + +export async function saveCache() { + if (!CACHE_ENABLED) { + return; + } + + const primaryKey = await getPrimaryCacheKey(); + const cacheHitKey = core.getState('cache-hit-key'); + + if (cacheHitKey === primaryKey) { + core.info(`Cache hit occured on the key ${cacheHitKey}, not saving cache`); + return; + } + + await cleanCargoRegistry(); + + core.info(`Saving cache with key ${primaryKey}`); + + await cache.saveCache(getPathsToCache(), primaryKey); +} + +export async function restoreCache() { + if (!CACHE_ENABLED) { + return; + } + + core.info('Attempting to restore cache'); + + const primaryKey = await getPrimaryCacheKey(); + + const cacheKey = await cache.restoreCache(getPathsToCache(), primaryKey, [ + `setup-rustcargo-${process.platform}`, + 'setup-rustcargo', + ]); + + if (cacheKey) { + core.saveState('cache-hit-key', cacheKey); + core.info(`Cache restored using key ${primaryKey}`); + } else { + core.warning(`Cache does not exist using key ${primaryKey}`); + } + + core.setOutput('cache-key', cacheKey ?? primaryKey); + core.setOutput('cache-hit', !!cacheKey); +} diff --git a/src/rust.ts b/src/rust.ts new file mode 100644 index 0000000..f2333b8 --- /dev/null +++ b/src/rust.ts @@ -0,0 +1,31 @@ +import * as core from '@actions/core'; +import * as exec from '@actions/exec'; + +export async function extractRustVersion(toolchain: string) { + let out = ''; + + await exec.exec('rustc', [`+${toolchain}`, '--version', '--verbose'], { + listeners: { + stdout(data: Buffer) { + out += data.toString(); + }, + }, + }); + + out.split('\n').forEach((line) => { + let key = ''; + + if (line.startsWith('commit-hash')) { + key = 'rust-hash'; + } else if (line.startsWith('release')) { + key = 'rust-version'; + } else { + return; + } + + const value = line.split(':')[1].trim(); + + core.saveState(key, value); + core.setOutput(key, value); + }); +}