├── .editorconfig ├── .eslintrc.json ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .npmrc ├── CHANGELOG.md ├── LICENSE ├── README.md ├── __tests__ └── input.test.ts ├── action.yml ├── dist └── index.js ├── jest.config.json ├── package-lock.json ├── package.json ├── public.pem ├── src ├── download.ts ├── input.ts └── main.ts ├── tsconfig.eslint.json └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | max_line_length = 80 8 | indent_size = 4 9 | 10 | [*.yml] 11 | indent_size = 2 12 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "parserOptions": { 4 | "project": "./tsconfig.eslint.json" 5 | }, 6 | "plugins": ["@typescript-eslint"], 7 | "extends": [ 8 | "eslint:recommended", 9 | "plugin:@typescript-eslint/eslint-recommended", 10 | "plugin:@typescript-eslint/recommended-requiring-type-checking", 11 | "plugin:@typescript-eslint/recommended", 12 | "plugin:prettier/recommended", 13 | "prettier", 14 | "prettier/@typescript-eslint" 15 | ], 16 | "rules": { 17 | "@typescript-eslint/ban-ts-ignore": "off" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Continuous integration 2 | 3 | on: [pull_request, push] 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Create npm configuration 10 | run: echo "//npm.pkg.github.com/:_authToken=${token}" >> ~/.npmrc 11 | env: 12 | token: ${{ secrets.GITHUB_TOKEN }} 13 | 14 | - uses: actions/checkout@v2 15 | - run: npm ci 16 | - run: npm run lint 17 | - run: npm run build 18 | - run: npm run test 19 | 20 | install_from_tool_cache: 21 | needs: test 22 | runs-on: ${{ matrix.os }} 23 | strategy: 24 | matrix: 25 | os: 26 | - ubuntu-latest 27 | - ubuntu-16.04 28 | - macOS-latest 29 | - windows-latest 30 | steps: 31 | - uses: actions/checkout@v2 32 | - name: Install sccache 33 | uses: ./ 34 | with: 35 | crate: sccache 36 | use-tool-cache: true 37 | - run: sccache --help 38 | 39 | disabled_tool_cache: 40 | needs: test 41 | runs-on: ubuntu-latest 42 | steps: 43 | - uses: actions/checkout@v2 44 | - name: Install sccache 45 | uses: ./ 46 | with: 47 | crate: sccache 48 | - run: sccache --help 49 | 50 | install_missing_from_tool_cache: 51 | needs: test 52 | runs-on: ubuntu-latest 53 | steps: 54 | - uses: actions/checkout@v2 55 | - name: Install bat 56 | uses: ./ 57 | with: 58 | crate: bat 59 | use-tool-cache: true 60 | - run: bat package.json 61 | 62 | multiple_binary_targets: 63 | needs: test 64 | runs-on: ${{ matrix.os }} 65 | strategy: 66 | matrix: 67 | os: 68 | - ubuntu-latest 69 | - macOS-latest 70 | - windows-latest 71 | steps: 72 | - uses: actions/checkout@v2 73 | - name: Install wasm-bindgen-cli 74 | uses: ./ 75 | with: 76 | crate: wasm-bindgen-cli 77 | version: 0.2.60 78 | use-tool-cache: true 79 | - run: test -f ~/.cargo/bin/wasm-bindgen 80 | shell: bash 81 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __tests__/runner/* 2 | 3 | # Rest pulled from https://github.com/github/gitignore/blob/master/Node.gitignore 4 | # Logs 5 | logs 6 | *.log 7 | npm-debug.log* 8 | yarn-debug.log* 9 | yarn-error.log* 10 | lerna-debug.log* 11 | 12 | # Diagnostic reports (https://nodejs.org/api/report.html) 13 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 14 | 15 | # Runtime data 16 | pids 17 | *.pid 18 | *.seed 19 | *.pid.lock 20 | 21 | # Directory for instrumented libs generated by jscoverage/JSCover 22 | lib-cov 23 | 24 | # Coverage directory used by tools like istanbul 25 | coverage 26 | *.lcov 27 | 28 | # nyc test coverage 29 | .nyc_output 30 | 31 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 32 | .grunt 33 | 34 | # Bower dependency directory (https://bower.io/) 35 | bower_components 36 | 37 | # node-waf configuration 38 | .lock-wscript 39 | 40 | # Compiled binary addons (https://nodejs.org/api/addons.html) 41 | build/Release 42 | 43 | # Dependency directories 44 | node_modules/ 45 | jspm_packages/ 46 | 47 | # TypeScript v1 declaration files 48 | typings/ 49 | 50 | # TypeScript cache 51 | *.tsbuildinfo 52 | 53 | # Optional npm cache directory 54 | .npm 55 | 56 | # Optional eslint cache 57 | .eslintcache 58 | 59 | # Optional REPL history 60 | .node_repl_history 61 | 62 | # Output of 'npm pack' 63 | *.tgz 64 | 65 | # Yarn Integrity file 66 | .yarn-integrity 67 | 68 | # dotenv environment variables file 69 | .env 70 | .env.test 71 | 72 | # parcel-bundler cache (https://parceljs.org/) 73 | .cache 74 | 75 | # next.js build output 76 | .next 77 | 78 | # nuxt.js build output 79 | .nuxt 80 | 81 | # vuepress build output 82 | .vuepress/dist 83 | 84 | # Serverless directories 85 | .serverless/ 86 | 87 | # FuseBox cache 88 | .fusebox/ 89 | 90 | # DynamoDB Local files 91 | .dynamodb/ 92 | 93 | # CI build stuff 94 | ci/venv 95 | ci/build 96 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | @actions-rs:registry=https://npm.pkg.github.com 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [0.1.2] - 2020-04-18 9 | 10 | ### Added 11 | 12 | - Support for crates with multiple binaries targets 13 | 14 | ## [0.1.1] - 2020-04-07 15 | 16 | ### Fixed 17 | 18 | - Pass absolute path to the public key to the OpenSSL (#2) 19 | 20 | ## [0.1.0] - 2020-03-30 21 | 22 | ### Added 23 | 24 | - First public version 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2019 actions-rs team and contributors 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `cargo-install` Action 2 | 3 | [![Sponsoring](https://img.shields.io/badge/Support%20it-Say%20%22Thank%20you!%22-blue)](https://actions-rs.github.io/#sponsoring) 4 | ![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg) 5 | [![Gitter](https://badges.gitter.im/actions-rs/community.svg)](https://gitter.im/actions-rs/community) 6 | ![Continuous integration](https://github.com/actions-rs/install/workflows/Continuous%20integration/badge.svg) 7 | ![Dependabot enabled](https://api.dependabot.com/badges/status?host=github&repo=actions-rs/toolchain) 8 | 9 | This GitHub Action provides faster version of the `cargo install` command. 10 | 11 | ⚠ ️**NOTE: This is an experimental Action.** ⚠ 12 | 13 | **Table of Contents** 14 | 15 | * [Why?](#why) 16 | * [How does this work?](#how-does-it-work) 17 | * [Example workflow](#example-workflow) 18 | * [Inputs](#inputs) 19 | * [Tool cache](#tool-cache) 20 | * [Security considerations](#security-considerations) 21 | * [GitHub cache](#github-cache) 22 | * [License](#license) 23 | * [Contribute and support](#contribute-and-support) 24 | 25 | ## Why? 26 | 27 | If you are using binary crates (such as [`cargo-audit`](https://crates.io/crates/cargo-audit), [`grcov`](https://github.com/mozilla/grcov), [`cargo-geiger`](https://crates.io/crates/cargo-geiger) and so on) in your CI workflows, you might have noticed that compiling these crates each time is irritatingly slow. 28 | 29 | This Action speeds up the crates installation with some tricks, leading to a much faster job execution; crates are expected to be installed in a couple seconds. 30 | 31 | ## How does it work? 32 | 33 | Before calling your usual `cargo install` command, this Action 34 | attempts to download pre-build binary crate file from the binary crates cache. 35 | See [Security considerations](#security-considerations) to read more 36 | about potential caveats and usage policy. 37 | 38 | If requested crate does not exist in the crates cache storage, 39 | this Action will fall back to the usual `cargo install`.\ 40 | As soon as [actions-rs/meta#21](https://github.com/actions-rs/meta/issues/21) will be implemented, 41 | this Action will also cache compiled binary in the GitHub cache. 42 | 43 | 44 | ## Example workflow 45 | 46 | ```yaml 47 | on: [push] 48 | 49 | name: build 50 | 51 | jobs: 52 | check: 53 | name: Rust project 54 | runs-on: ubuntu-latest 55 | steps: 56 | - uses: actions/checkout@v2 57 | - uses: actions-rs/install@v0.1 58 | with: 59 | crate: cargo-audit 60 | version: latest 61 | - run: cargo audit 62 | ``` 63 | 64 | ## Inputs 65 | 66 | | Name | Required | Description | Type | Default | 67 | | ---------------- | :------: | ------------------------------------------------ | ------ | --------| 68 | | `crate` | ✓ | Binary crate name | string | | 69 | | `version` | | Crate version to install | string | latest | 70 | | `use-tool-cache` | | Use pre-compiled crates to speed-up installation | bool | false | 71 | 72 | ## Tool cache 73 | 74 | As it was mentioned in [How does it work?](#how-does-it-work) section, 75 | this Action can use external pre-compiled crates cache. 76 | 77 | In order to enable this functionaliy, you need to **explicitly** enable `use-tool-cache` input: 78 | 79 | ```yaml 80 | - uses: actions-rs/install@v0.1 81 | with: 82 | crate: cargo-audit 83 | version: latest 84 | use-tool-cache: true 85 | ``` 86 | 87 | Before enabling this input, you should acknowledge security risks 88 | of executing binaries compiled for you by a third party in your CI workflows. 89 | 90 | ### Security considerations 91 | 92 | Check the [`tool-cache`](https://github.com/actions-rs/tool-cache/) repo 93 | to understand how binary crates are built, signed and uploaded into the external cache. 94 | 95 | This Action downloads both binary file and its signature. 96 | Signature validation is proceeded by `openssl` by using public key 97 | of the same certificate used for signing files at `tool-cache` repo.\ 98 | Public key is stored in this repository at `public.pem`. 99 | 100 | If signature validation fails, binary file is removed immediately, 101 | warning issued and fall back to the `cargo install` call happens. 102 | 103 | ## License 104 | 105 | This Action is distributed under the terms of the MIT license, see [LICENSE](https://github.com/actions-rs/toolchain/blob/master/LICENSE) for details. 106 | 107 | ## Contribute and support 108 | 109 | Any contributions are welcomed! 110 | 111 | If you want to report a bug or have a feature request, 112 | check the [Contributing guide](https://github.com/actions-rs/.github/blob/master/CONTRIBUTING.md). 113 | 114 | You can also support author by funding the ongoing project work, 115 | see [Sponsoring](https://actions-rs.github.io/#sponsoring). 116 | -------------------------------------------------------------------------------- /__tests__/input.test.ts: -------------------------------------------------------------------------------- 1 | import * as input from "../src/input"; 2 | 3 | const testEnvVars = { 4 | INPUT_CRATE: "cross", 5 | INPUT_VERSION: "latest", 6 | }; 7 | 8 | describe("actions-rs/install/input", () => { 9 | beforeEach(() => { 10 | for (const key in testEnvVars) 11 | process.env[key] = testEnvVars[key as keyof typeof testEnvVars]; 12 | }); 13 | 14 | it("Parses action input into install input", () => { 15 | const result = input.get(); 16 | 17 | expect(result.crate).toBe("cross"); 18 | expect(result.version).toBe("latest"); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | name: 'rust-cargo-install' 2 | description: 'Install a Rust binary as fast as possible' 3 | author: 'actions-rs team' 4 | branding: 5 | icon: play-circle 6 | color: black 7 | inputs: 8 | crate: 9 | description: Binary crate name 10 | required: true 11 | version: 12 | description: Specify a version to install 13 | required: false 14 | default: 'latest' 15 | use-tool-cache: 16 | description: Use tool cache to speed up installation 17 | required: false 18 | default: false 19 | use-cache: 20 | description: Store installed binary in the GitHub Actions cache (not used yet) 21 | required: false 22 | default: true 23 | 24 | runs: 25 | using: 'node12' 26 | main: 'dist/index.js' 27 | -------------------------------------------------------------------------------- /jest.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "clearMocks": true, 3 | "moduleFileExtensions": ["js", "ts"], 4 | "testEnvironment": "node", 5 | "testMatch": ["**/*.test.ts"], 6 | "testRunner": "jest-circus/runner", 7 | "transform": { 8 | "^.+\\.ts$": "ts-jest" 9 | }, 10 | "verbose": true 11 | } 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rust-cargo-install", 3 | "version": "0.1.2", 4 | "description": "Install a Rust binary crate as fast as possible", 5 | "main": "lib/main.js", 6 | "scripts": { 7 | "build": "rm -rf ./dist/* && ncc build src/main.ts --minify", 8 | "format": "prettier --write 'src/**/*.ts' '__tests__/**/*.ts'", 9 | "lint": "tsc --noEmit && eslint 'src/**/*.ts' '__tests__/**/*.ts'", 10 | "watch": "rm -rf ./dist/* && ncc build src/main.ts --watch", 11 | "test": "jest -c jest.config.json" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/actions-rs/install.git" 16 | }, 17 | "keywords": [ 18 | "actions", 19 | "rust", 20 | "cargo", 21 | "install", 22 | "binary" 23 | ], 24 | "author": "actions-rs", 25 | "license": "MIT", 26 | "bugs": { 27 | "url": "https://github.com/actions-rs/install/issues" 28 | }, 29 | "homepage": "https://github.com/actions-rs/install", 30 | "dependencies": { 31 | "@actions-rs/core": "0.0.9", 32 | "@actions/core": "^1.2.3", 33 | "@actions/exec": "^1.0.3", 34 | "@actions/http-client": "^1.0.7", 35 | "@actions/tool-cache": "^1.3.3" 36 | }, 37 | "devDependencies": { 38 | "@types/jest": "^25.2.1", 39 | "@types/node": "^13.13.0", 40 | "@typescript-eslint/eslint-plugin": "^2.28.0", 41 | "@typescript-eslint/parser": "^2.28.0", 42 | "@zeit/ncc": "^0.22.1", 43 | "eslint": "^6.8.0", 44 | "eslint-config-prettier": "^6.10.1", 45 | "eslint-plugin-prettier": "^3.1.3", 46 | "jest": "^25.3.0", 47 | "jest-circus": "^25.3.0", 48 | "npm-check-updates": "^4.1.2", 49 | "prettier": "^2.0.4", 50 | "ts-jest": "^25.4.0", 51 | "typescript": "^3.8.3" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /public.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsWm/fSobUwf2FmPEVNgT 3 | h4k5DBCiBEhrGmPladEExygSwB3v3r0Qt5075uCsI+OCk39Onp5z2MB0QNUxVUFZ 4 | cCqLRh58/NYfnpakyZEzcwjitHVBRSPkpheWIMw+dHzIc6kcxJrS0TDI3qzryFBD 5 | gT1uISYlCGjoTw2UskFpFcZiEf0K+xsIFe4Fky1sqyX0HNzng+FdSb7Qav60VExS 6 | RAcqF38VTU1yxdvTrxAa+0Stb9bUqQG3FoJsWxsFmElkM7g+YpINQLSOOixRSr+u 7 | yeOlMmYQmF64XdFl2lwj4e3xLIp5ha7qhaEOFHZMHkMSvvR9o9BGN4RUNXSSM7N5 8 | pnB5CrOj2E3rnLQwsTjqyU6qQhdaA4T3pks2d9gfR313pYM/XF6g4SnjRQkgMNq7 9 | /lG11hxap8ugvF98mVbYCzN46qH7ax1Ent+JqKSv0WNou0C4lognM1RiKbUPp257 10 | MVz74xPp2cD0MZkPdFb40QqBSGGNDzilUVVVjc4/KqDgQvk99gk3OJYij3IWkfKP 11 | rf6Jd2SJWectoNFRY8PpZiiOYlFPJ/y1VMbx7NuY3w3kkzCI+FbrwAdzEpv1WrBK 12 | dwZxpLha7uq+DzlLPMvYT+J8gLQRhAwgJ6ztXGibyV/t5vm0ZS/9dX5X4DKkmSCr 13 | gOkQxnGLs0Qze8UK4nj+ebUCAwEAAQ== 14 | -----END PUBLIC KEY----- 15 | -------------------------------------------------------------------------------- /src/download.ts: -------------------------------------------------------------------------------- 1 | import os from "os"; 2 | import { promises as fs } from "fs"; 3 | import path from "path"; 4 | 5 | import * as core from "@actions/core"; 6 | import * as exec from "@actions/exec"; 7 | import * as tc from "@actions/tool-cache"; 8 | import * as http from "@actions/http-client"; 9 | 10 | const CLOUDFRONT_ROOT = "https://d1ad61wkrfbmp3.cloudfront.net"; 11 | // Path to the public key of the sign certificate. 12 | // It is resolved either from compiled `dist/index.js` during usual Action run, 13 | // or from this one file and always points to the file at the repo root. 14 | const CACHE_PUBLIC_KEY = path.resolve(__dirname, "..", "public.pem"); 15 | 16 | function getRunner(): string { 17 | const platform = os.platform() as string; 18 | switch (platform) { 19 | case "win32": 20 | return "windows-2019"; 21 | case "darwin": 22 | return "macos-10.15"; 23 | case "linux": 24 | // TODO: Is there better way to determine Actions runner OS? 25 | if (os.release().startsWith("4.15")) { 26 | return "ubuntu-16.04"; 27 | } else { 28 | return "ubuntu-18.04"; 29 | } 30 | default: 31 | throw new Error("Unsupported OS"); 32 | } 33 | } 34 | 35 | async function resolveVersion(crate: string): Promise { 36 | const url = `https://crates.io/api/v1/crates/${crate}`; 37 | const client = new http.HttpClient( 38 | "@actions-rs (https://github.com/actions-rs/)" 39 | ); 40 | 41 | const resp: any = await client.getJson(url); 42 | if (resp.result == null) { 43 | throw new Error("Unable to fetch latest crate version"); 44 | } 45 | 46 | return resp.result["crate"]["newest_version"]; 47 | } 48 | 49 | function buildUrl(crate: string, version: string): string { 50 | const runner = getRunner(); 51 | 52 | core.debug(`Determined current Actions runner OS: ${runner}`); 53 | 54 | return `${CLOUDFRONT_ROOT}/${crate}/${runner}/${crate}-${version}.zip`; 55 | } 56 | 57 | function binPath(): string { 58 | return path.join(os.homedir(), ".cargo", "bin"); 59 | } 60 | 61 | /** 62 | * Build download path 63 | */ 64 | function targetPath(crate: string): string { 65 | const filename = `${crate}.zip`; 66 | 67 | return path.join(os.tmpdir(), filename); 68 | } 69 | 70 | async function verify(crate: string, signature: string): Promise { 71 | await exec.exec("openssl", [ 72 | "dgst", 73 | "-sha256", 74 | "-verify", 75 | CACHE_PUBLIC_KEY, 76 | "-signature", 77 | signature, 78 | crate, 79 | ]); 80 | } 81 | 82 | export async function downloadFromCache( 83 | crate: string, 84 | version: string 85 | ): Promise { 86 | if (version == "latest") { 87 | core.debug(`Latest version requested for ${crate}, querying crates.io`); 88 | version = await resolveVersion(crate); 89 | core.info(`Newest ${crate} version available at crates.io: ${version}`); 90 | } 91 | const url = buildUrl(crate, version); 92 | const signatureUrl = `${url}.sig`; 93 | 94 | const path = targetPath(crate); 95 | const signaturePath = `${path}.sig`; 96 | 97 | core.debug(`Constructed S3 URL for ${crate}: ${url}`); 98 | 99 | try { 100 | await fs.access(path); 101 | 102 | core.warning(`Crate ${crate} already exist at ${path}`); 103 | } catch (error) { 104 | core.info(`Downloading ${crate} signature into ${signaturePath}`); 105 | await tc.downloadTool(signatureUrl, signaturePath); 106 | 107 | core.info(`Downloading ${crate} == ${version} into ${path}`); 108 | await tc.downloadTool(url, path); 109 | 110 | try { 111 | core.info("Starting signature verification process"); 112 | await verify(path, signaturePath); 113 | 114 | const cargoBinPath = binPath(); 115 | core.info(`Extracting files into ${cargoBinPath}`); 116 | await tc.extractZip(path, cargoBinPath); 117 | } catch (error) { 118 | core.warning( 119 | `Unable to validate signature for downloaded ${crate}!` 120 | ); 121 | 122 | // Remove downloaded files, as they are now considered dangerous now 123 | await fs.unlink(path); 124 | await fs.unlink(signaturePath); 125 | throw error; 126 | } 127 | 128 | await fs.chmod(path, 0o755); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/input.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Parse action input into a some proper thing. 3 | */ 4 | 5 | import { input } from "@actions-rs/core"; 6 | 7 | // Parsed action input 8 | export interface Input { 9 | crate: string; 10 | version: string; 11 | useToolCache: boolean; 12 | useCache: boolean; 13 | } 14 | 15 | export function get(): Input { 16 | const crate = input.getInput("crate", { required: true }); 17 | const version = input.getInput("version", { required: true }); 18 | const useToolCache = input.getInputBool("use-tool-cache") || false; 19 | const useCache = input.getInputBool("use-cache") || true; 20 | 21 | return { 22 | crate: crate, 23 | version: version, 24 | useToolCache: useToolCache, 25 | useCache: useCache, 26 | }; 27 | } 28 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import * as core from "@actions/core"; 2 | import { Cargo } from "@actions-rs/core"; 3 | 4 | import * as input from "./input"; 5 | import * as download from "./download"; 6 | 7 | interface Options { 8 | useToolCache: boolean; 9 | useCache: boolean; 10 | } 11 | 12 | async function downloadFromToolCache( 13 | crate: string, 14 | version: string 15 | ): Promise { 16 | try { 17 | await download.downloadFromCache(crate, version); 18 | } catch (error) { 19 | core.warning( 20 | `Unable to download ${crate} == ${version} from the tool cache: ${error}` 21 | ); 22 | throw error; 23 | } 24 | } 25 | 26 | export async function run( 27 | crate: string, 28 | version: string, 29 | options: Options 30 | ): Promise { 31 | try { 32 | if (options.useToolCache) { 33 | try { 34 | core.info( 35 | "Tool cache is explicitly enabled via the Action input" 36 | ); 37 | core.startGroup("Downloading from the tool cache"); 38 | 39 | return await downloadFromToolCache(crate, version); 40 | } finally { 41 | core.endGroup(); 42 | } 43 | } else { 44 | core.info( 45 | "Tool cache is disabled in the Action inputs, skipping it" 46 | ); 47 | 48 | throw new Error( 49 | "Faster installation options either failed or disabled" 50 | ); 51 | } 52 | } catch (error) { 53 | core.info("Falling back to the `cargo install` command"); 54 | const cargo = await Cargo.get(); 55 | await cargo.installCached(crate, version); 56 | } 57 | } 58 | 59 | async function main(): Promise { 60 | try { 61 | const actionInput = input.get(); 62 | 63 | await run(actionInput.crate, actionInput.version, { 64 | useToolCache: actionInput.useToolCache, 65 | useCache: actionInput.useCache, 66 | }); 67 | } catch (error) { 68 | core.setFailed(error.message); 69 | } 70 | } 71 | 72 | main(); 73 | -------------------------------------------------------------------------------- /tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": [ 4 | "src/**/*.ts", 5 | "__tests__/**/*.ts" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": false, 4 | "checkJs": false, 5 | "esModuleInterop": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "module": "commonjs", 8 | "moduleResolution": "node", 9 | "newLine": "LF", 10 | "noEmitOnError": true, 11 | "noErrorTruncation": true, 12 | "noFallthroughCasesInSwitch": true, 13 | "noImplicitAny": true, 14 | "noImplicitReturns": true, 15 | "noImplicitThis": true, 16 | "noUnusedLocals": true, 17 | "noUnusedParameters": true, 18 | "outDir": "dist", 19 | "pretty": true, 20 | "removeComments": true, 21 | "resolveJsonModule": true, 22 | "strict": true, 23 | "suppressImplicitAnyIndexErrors": false, 24 | "target": "es2018", 25 | "declaration": false, 26 | "sourceMap": false 27 | }, 28 | "include": [ 29 | "src/**/*.ts" 30 | ] 31 | } 32 | --------------------------------------------------------------------------------