Skip to content

Commit

Permalink
Add setup-rust action
Browse files Browse the repository at this point in the history
  • Loading branch information
Systemcluster committed Nov 18, 2023
1 parent bcd8285 commit 4915d15
Show file tree
Hide file tree
Showing 13 changed files with 926 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@
### Setup Actions

- [**Setup Node**](actions/setup-node) - Setup a Node.js environment with the right package manager and package caching.
- [**Setup Rust**](actions/setup-rust) - Setup a Rust environment with the right toolchain and build caching.
115 changes: 115 additions & 0 deletions actions/setup-rust/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# Setup Rust Action

**Setup a Rust environment with the right toolchain and build caching. 🧰**

```yaml
- name: Set up Rust
uses: Systemcluster/actions@setup-rust-v0
with:
channel: stable
components: rustfmt,clippy
targets: wasm32-unknown-unknown,x86_64-unknown-linux-gnu
binaries:
- cargo-nextest
- cargo-insta
sccache: true
```
## Features
- **Installs Rust** detecting the desired toolchain by reading the `RUSTUP_TOOLCHAIN` environment variable, `rust-toolchain.toml` and `rust-toolchain` files, or by manual specification
- **Caches the Cargo registry and build artifacts** to speed up build and dependency installation
- **Installs sccache** and sets up GitHub Actions integration
- **Adds problem matchers** for `rustc`, `rustfmt` and runtime panics

## Usage

### Inputs

| Name | Type | Description | Default | Required |
| --- | --- | --- | --- | --- |
| `channel` | String | Version of Rust to install. Can be a channel like `stable`, `beta`, `nightly`, or a specific version like `1.54.0` or `nightly-2021-08-01`. | Read from the `RUSTUP_TOOLCHAIN` environment variable, `rust-toolchain` or `rust-toolchain.toml` file, and falling back to `stable`. | ✗ |
| `components` | String[] | List of components to install. Can be a list of component names like `rustfmt`, `clippy`, or `rust-src`. | Read from the `rust-toolchain.toml` file. | ✗ |
| `targets` | String[] | List of targets to install. Can be a list of target names like `wasm32-unknown-unknown`, `x86_64-unknown-linux-gnu`, or `x86_64-apple-darwin`. | Read from the `rust-toolchain.toml` file. | ✗ |
| `profile` | String | Profile to install. Can be `minimal`, `default`, or `complete`. | Read from the `rust-toolchain.toml` file and falling back to `minimal`. | ✗ |
| `binaries` | String[] | List of binaries to install. Can be a list of binary names like `cargo-nextest` or specific versions like `[email protected]`. | | ✗ |
| `directory` | String | Directory containing the Rust project. Used for detecting Rust version and configuration and for caching build artifacts. | `.` | ✗ |
| `sccache` | Boolean | Whether to set up [`sccache`](github.com/mozilla/sccache/) with GitHub Actions integration. | `true` | ✗ |
| `cache` | Boolean | Whether to cache and restore the Cargo registry and the build artifacts in the target directory. | `true` | ✗ |
| `cache-profile` | String | The build profile to cache. Can be `debug`, `release`, or any custom profile. If not specified, all profiles are cached. Requires `cache` to be `true`. | | ✗ |
| `cache-key-job` | Boolean | Whether to consider the Job ID when generating the cache key. Set to `true` to prevent sharing the cache across jobs. Requires `cache` to be `true`. | `false` | ✗ |
| `cache-key-env` | String[] | List of environment variables to consider when generating the cache key. Requires `cache` to be `true`. | | ✗ |

### Outputs

| Name | Type | Description |
| --- | --- | --- |
| `rust-version` | String | Version of Rust that was installed. |
| `rust-version-hash` | String | Commit hash of the Rust version that was installed. |
| `cache-hit` | Boolean | Whether the Cargo registry and build artifact cache was restored. |

## Details

### Rust toolchain

#### Toolchain channel

The desired Rust toolchain channel is detected in the following order:

1. Using the `channel` input if specified
2. Read from the `RUSTUP_TOOLCHAIN` environment variable if present
3. Read from a `rust-toolchain.toml` if present in `directory`
4. Read from a `rust-toolchain` file if present in `directory`
5. Falling back to `stable`

The chosen toolchain is installed with `rustup` and set as the default toolchain.

#### Toolchain components, targets, and profile

The desired toolchain components, targets, and profile are detected in the following order:

1. Using the `components`, `targets`, and `profile` inputs if specified
2. Read from the `rust-toolchain.toml` file if present in `directory`
3. Falling back to the `minimal` profile and no components or targets

Both `targets` and `components` can alternatively be specified as a single string with values separated by `,` or `;`, for example `x86_64-unknown-linux-gnu;wasm32-unknown-unknown` or `rustfmt,clippy`.

If `targets` and `components` are specified in multiple places, the lists are merged.

### Binaries

Binaries are installed with [`cargo-binstall`](https://crates.io/crates/cargo-binstall). `cargo-cache` and `cargo-sweep` are installed by default when `cache` is `true`.

If `binaries` is empty and both `cache` and `sccache` are `false`, the installation of `cargo-binstall` is skipped.

### Caching

When the `cache` input is `true`, the Cargo store and build artifacts are cached and restored using [actions/cache](https://github.com/actions/cache).

The cache key is generated based on the following inputs:

- The `Cargo.lock` files in `directory` and all subdirectories
- The `dependencies` and `workspace.dependencies` fields in `Cargo.toml` files in `directory` and all subdirectories
- The `GITHUB_JOB` environment variable if `cache-key-job` is `true`
- The environment variables specified in `cache-key-env`
- The `os.platform()` and `os.arch()` of the runner

In case of a cache key miss, a cache matching the `os.platform()` and `os.arch()` of the runner is restored.

The following directories are cached:

- The Cargo registry (`~/.cargo/registry`)
- Before caching, the registry is cleaned with [`cargo cache --autoclean`](https://crates.io/crates/cargo-cache) and any `.cache` directories are removed
- The build artifacts (`target`)
- Before caching, the target directory is cleaned with [`cargo sweep --installed`](https://crates.io/crates/cargo-sweep), and the `examples` and `incremental` directories as well as all dep-info files (`*.d`) are removed
- If `cache-profile` is specified, all other profile directories are removed

### Sccache

When the `sccache` input is `true`, [`sccache`](https://crates.io/crates/sccache) is installed and set up with [GitHub Actions integration](https://github.com/mozilla/sccache/blob/HEAD/docs/GHA.md) by setting the following environment variables:

- `ACTIONS_CACHE_URL` is set to the cache URL provided to the action
- `ACTIONS_RUNTIME_TOKEN` is set to the token provided to the action
- `SCCACHE_PATH` is set to the path to the `sccache` binary
- `SCCACHE_GHA_ENABLED` is set to `true`
- `RUSTC_WRAPPER` is set to `sccache`
55 changes: 55 additions & 0 deletions actions/setup-rust/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: Setup Rust
description: Setup a Rust environment with the right toolchain and build caching.
author: Systemcluster

runs:
using: node20
main: dist/action.js
post: dist/action-post.js

branding:
color: white
icon: box

inputs:
channel:
required: false
description: Version of Rust to install. Can be a channel like `stable`, `beta`, `nightly`, or a specific version like `1.54.0` or `nightly-2021-08-01`. Read from the `RUSTUP_TOOLCHAIN` environment variable, `rust-toolchain` or `rust-toolchain.toml` file, and falling back to `stable`.
components:
required: false
description: List of components to install. Can be a list of component names like `rustfmt`, `clippy`, or `rust-src`. Read from the `rust-toolchain.toml` file.
targets:
required: false
description: List of targets to install. Can be a list of target names like `wasm32-unknown-unknown`, `x86_64-unknown-linux-gnu`, or `x86_64-apple-darwin`. Read from the `rust-toolchain.toml` file.
profile:
required: false
description: Profile to install. Can be `minimal`, `default`, or `complete`. Read from the `rust-toolchain.toml` file and falling back to `minimal`.
binaries:
required: false
description: List of binaries to install. Can be a list of binary names like `cargo-nextest` or specific versions like `[email protected]`.
directory:
required: false
description: Directory containing the Rust project. Used for detecting Rust version and configuration and for caching build artifacts. Defaults to `.`.
sccache:
required: false
description: Whether to set up sccache with GitHub Actions integration. Defaults to `true`.
cache:
required: false
description: Whether to cache and restore the Cargo store and the build artifacts in the target directory. Defaults to `true`.
cache-profile:
required: false
description: The build profile to cache. Can be `debug`, `release`, or any custom profile. If not specified, all profiles are cached. Requires `cache` to be `true`.
cache-key-job:
required: false
description: Whether to consider the Job ID when generating the cache key. Set to `true` to prevent sharing the cache across jobs. Requires `cache` to be `true`.
cache-key-env:
required: false
description: List of environment variables to consider when generating the cache key. Requires `cache` to be `true`.

outputs:
rust-version:
description: Version of Rust that was installed.
rust-version-hash:
description: Commit hash of the Rust version that was installed.
cache-hit:
description: Whether the Cargo store and build artifact cache was restored.
5 changes: 5 additions & 0 deletions actions/setup-rust/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import config from 'jest-config-workspace'

config.setupFiles = ['dotenv/config']
config.setupFilesAfterEnv = ['<rootDir>/jest.setup.js']
export default config
13 changes: 13 additions & 0 deletions actions/setup-rust/jest.setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
process.env['GITHUB_EVENT_NAME'] = 'push'
process.env['GITHUB_SHA'] = 'HEAD'
process.env['GITHUB_REF'] = 'refs/heads/main'
process.env['GITHUB_WORKFLOW'] = 'test'
process.env['GITHUB_ACTION'] = 'test'
process.env['GITHUB_ACTOR'] = 'test'
process.env['GITHUB_JOB'] = 'test'
process.env['GITHUB_RUN_NUMBER'] = '1'
process.env['GITHUB_RUN_ID'] = '1'
process.env['GITHUB_REPOSITORY'] ||= process.env['INPUT_REPOSITORY'] || 'github/.github'

process.env['INPUT_DIRECTORY'] ||= ''
process.env['INPUT_CACHE'] ||= 'true'
44 changes: 44 additions & 0 deletions actions/setup-rust/matcher.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"problemMatcher": [
{
"owner": "rust-compiler",
"pattern": [
{
"regexp": "^(?:\\x1B\\[[0-9;]*[a-zA-Z])*(warning|warn|error)(\\[(\\S*)\\])?(?:\\x1B\\[[0-9;]*[a-zA-Z])*: (.*?)(?:\\x1B\\[[0-9;]*[a-zA-Z])*$",
"severity": 1,
"message": 4,
"code": 3
},
{
"regexp": "^(?:\\x1B\\[[0-9;]*[a-zA-Z])*\\s+(?:\\x1B\\[[0-9;]*[a-zA-Z])*-->\\s(?:\\x1B\\[[0-9;]*[a-zA-Z])*(\\S+):(\\d+):(\\d+)(?:\\x1B\\[[0-9;]*[a-zA-Z])*$",
"file": 1,
"line": 2,
"column": 3
}
]
},
{
"owner": "rust-formatter",
"pattern": [
{
"regexp": "^(Diff in (\\S+)) at line (\\d+):",
"message": 1,
"file": 2,
"line": 3
}
]
},
{
"owner": "rust-panic",
"pattern": [
{
"regexp": "^.*panicked\\s+at\\s+'(.*)',\\s+(.*):(\\d+):(\\d+)$",
"message": 1,
"file": 2,
"line": 3,
"column": 4
}
]
}
]
}
51 changes: 51 additions & 0 deletions actions/setup-rust/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"name": "setup-rust",
"displayName": "Setup Rust Action",
"version": "0.1.0",
"description": "Setup a Rust environment with the right toolchain and build caching.",
"author": {
"name": "Christian Sdunek",
"email": "[email protected]"
},
"repository": {
"type": "git",
"url": "github:Systemcluster/actions",
"directory": "actions/setup-rust"
},
"license": "BSD-2-Clause",
"private": true,
"keywords": [
"github",
"actions"
],
"type": "module",
"sideEffects": false,
"main": "dist/main.js",
"types": "dist/main.d.ts",
"scripts": {
"build": "tsc && rollup -c",
"check": "tsc --noEmit --emitDeclarationOnly false && eslint \"**/*.ts*\"",
"test": "jest"
},
"devDependencies": {
"@types/jest": "^29.5.8",
"@types/node": "^20.9.1",
"dotenv": "^16.3.1",
"eslint": "^8.54.0",
"eslint-config-workspace": "workspace:*",
"jest": "^29.7.0",
"jest-config-workspace": "workspace:*",
"jest-mock": "^29.7.0",
"rollup": "^4.4.1",
"rollup-config-workspace": "workspace:*",
"typescript": "^5.2.2",
"typescript-config-workspace": "workspace:*"
},
"dependencies": {
"@ltd/j-toml": "^1.38.0",
"actions-utils": "workspace:*",
"detect-libc": "^2.0.2",
"glob": "^10.3.10",
"slash": "^5.1.0"
}
}
4 changes: 4 additions & 0 deletions actions/setup-rust/rollup.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import config from 'rollup-config-workspace'

config.input = ['src/action.ts', 'src/action-post.ts', 'src/main.ts']
export default config
9 changes: 9 additions & 0 deletions actions/setup-rust/src/action-post.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { setFailed } from 'actions-utils/context'

import { post } from './main.js'

try {
await post()
} catch (error: any) {
setFailed(`${(error as { message?: string }).message || (error as string)}`)
}
9 changes: 9 additions & 0 deletions actions/setup-rust/src/action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { setFailed } from 'actions-utils/context'

import { main } from './main.js'

try {
await main()
} catch (error: any) {
setFailed(`${(error as { message?: string }).message || (error as string)}`)
}
7 changes: 7 additions & 0 deletions actions/setup-rust/src/main.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { main } from './main'

describe('main', () => {
it('should complete successfully', async () => {
await expect(main(undefined, false)).resolves.toBeUndefined()
})
})
Loading

0 comments on commit 4915d15

Please sign in to comment.