Automatic compilation

This commit is contained in:
github-actions[bot] 2023-04-17 23:14:03 +00:00 committed by GitHub
parent 63072407f0
commit da5e22ec5e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 122946 additions and 3519 deletions

View file

@ -1,6 +0,0 @@
coverage/
dist/
node_modules/
*.min.js
*.map
*.snap

View file

@ -1,10 +0,0 @@
/* eslint-disable sort-keys */
module.exports = {
root: true,
extends: ['moon', 'moon/node'],
parserOptions: {
project: 'tsconfig.json',
tsconfigRootDir: __dirname,
},
};

View file

@ -1,16 +0,0 @@
name: 'Pipeline'
on:
push:
branches:
- 'master'
pull_request:
jobs:
ci:
name: 'CI'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- run: npm install -g pnpm
- run: pnpm install
- run: pnpm run check

View file

@ -1,20 +0,0 @@
name: Publish
on:
release:
types: [published, edited]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.release.tag_name }}
- uses: actions/setup-node@v3
- run: npm install -g pnpm
- run: pnpm install
- run: pnpm run build
- uses: aboutte/build-and-tag-action@v2
with:
additional_files: 'dist/post/index.js'
env:
GITHUB_TOKEN: ${{ github.token }}

2
.gitignore vendored
View file

@ -1,2 +0,0 @@
node_modules/
dist/

View file

@ -1,3 +0,0 @@
# Unreleased
- Initial release.

127
README.md
View file

@ -1,127 +0,0 @@
# Setup Rust and Cargo
A GitHub action for setting up Rust and Cargo.
```yaml
jobs:
ci:
name: CI
runs-on: ubuntu-latest
steps:
# ...
- uses: moonrepo/setup-rust@v0
- run: cargo test
```
## Configuring the Rust toolchain
This action will automatically install the appropriate toolchain with `rustup` by inspecting the
`RUSTUP_TOOLCHAIN` environment variable or the `rust-toolchain.toml` (preferred) or `rust-toolchain`
configuration files. If no toolchain found, will default to `stable`.
```toml
# rust-toolchain.toml
[toolchain]
channel = "1.68.0"
```
> When loading a configuration file, only the `channel` field is used, while the other fields are
> ignored. We chose this approach, as those other fields are typically for develop/release
> workflows, but not for CI, which requires a minimal/granular setup.
The toolchain/channel can also be explicitly configured with the `toolchain` input, which takes
highest precedence.
```yaml
- uses: moonrepo/setup-rust@v0
with:
toolchain: '1.65.0'
```
### Profile and components
Furthermore, this action supports rustup profiles and components, which can be configured with the
`profile` and `components` inputs respectively. When not defined, profile defaults to `minimal`.
```yaml
- uses: moonrepo/setup-rust@v0
with:
profile: complete
```
When using components, the input requires a comma separated list of component names.
```yaml
- uses: moonrepo/setup-rust@v0
with:
components: clippy
- run: cargo clippy --workspace
```
## Installing Cargo binaries
If you require `cargo-make`, `cargo-nextest`, or other global binaries, this action supports
installing Cargo binaries through the `bins` input, which requires a comma-separated list of crate
names (`cargo-` prefix optional).
```yaml
- uses: moonrepo/setup-rust@v0
with:
bins: nextest, cargo-insta@1.28.0
```
> 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: `$GITHUB_JOB`, `Cargo.lock`, Rust
version, Rust commit hash, and operating system.
## Compared to
### `actions-rs/*`
The "official" actions have served their purpose, but after 3 years of no updates, severe lack of
maintenance, and being full of deprecation warnings, it was time to create something new.
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`.
However, this action _does not_:
- Install `rustup` if it does not exist, while `actions-rs` will. This is typically fine if using
GitHub provided runners as all Rust tooling comes pre-installed.
### `dtolnay/rust-toolchain`
This action is very similar to `dtolnay/rust-toolchain`, which was a great implementation reference,
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.

63499
dist/index.js vendored Normal file

File diff suppressed because one or more lines are too long

59447
dist/post/index.js vendored Normal file

File diff suppressed because one or more lines are too long

220
index.ts
View file

@ -1,220 +0,0 @@
import fs from 'fs';
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;
components: string[];
targets: string[];
profile: string;
}
interface ToolchainConfig {
toolchain: Partial<Toolchain>;
}
const DEFAULT_TOOLCHAIN: Toolchain = {
channel: 'stable',
components: [],
profile: 'minimal',
targets: [],
};
function parseConfig(configPath: string): Partial<Toolchain> {
const contents = fs.readFileSync(configPath, 'utf8').trim();
if (!contents.includes('[toolchain]')) {
core.debug('No [toolchain] section found, assuming legacy format');
return { channel: contents };
}
const config = TOML.parse(contents) as unknown as ToolchainConfig;
if (config.toolchain.channel) {
core.debug('Found channel in [toolchain] section');
return { channel: config.toolchain.channel };
}
core.debug('No channel found in [toolchain] section');
return {};
}
// https://rust-lang.github.io/rustup/overrides.html
function detectToolchain(): Toolchain {
core.info('Detecting toolchain');
const toolchain = { ...DEFAULT_TOOLCHAIN };
if (process.env.RUSTUP_TOOLCHAIN) {
core.info('Using toolchain from RUSTUP_TOOLCHAIN environment variable');
Object.assign(toolchain, {
channel: process.env.RUSTUP_TOOLCHAIN,
});
} else {
core.info('Loading rust-toolchain.toml or rust-toolchain file');
for (const configName of ['rust-toolchain.toml', 'rust-toolchain']) {
const configPath = path.join(process.cwd(), configName);
if (fs.existsSync(configPath)) {
core.debug(`Found ${configName}, parsing TOML`);
Object.assign(toolchain, parseConfig(configPath));
break;
}
}
}
core.info('Inheriting toolchain settings from inputs');
(Object.keys(DEFAULT_TOOLCHAIN) as (keyof typeof DEFAULT_TOOLCHAIN)[]).forEach((key) => {
const input = core.getInput(key);
if (input) {
core.debug(`Found input for ${key}: ${input}`);
if (key === 'components' || key === 'targets') {
input.split(',').forEach((part) => {
toolchain[key].push(part.trim());
});
} else {
toolchain[key] = input;
}
}
});
return toolchain;
}
async function installToolchain(toolchain: Toolchain) {
core.info('Installing toolchain with rustup');
const args = ['toolchain', 'install', toolchain.channel, '--profile', toolchain.profile];
toolchain.targets.forEach((target) => {
args.push('--target', target);
});
toolchain.components.forEach((component) => {
args.push('--component', component);
});
if (toolchain.channel === 'nightly' && toolchain.components.length > 0) {
args.push('--allow-downgrade');
}
args.push('--no-self-update');
await exec.exec('rustup', args);
await exec.exec('rustup', ['default', toolchain.channel]);
core.info('Logging installed toolchain versions');
await extractRustVersion(toolchain.channel);
}
async function downloadAndInstallBinstall(binDir: string) {
core.info('cargo-binstall does not exist, attempting to install');
let arch;
let file;
switch (process.arch) {
case 'x64':
arch = 'x86_64';
break;
case 'arm64':
arch = 'aarch64';
break;
default:
throw new Error(`Unsupported architecture: ${process.arch}`);
}
switch (process.platform) {
case 'linux':
file = `${arch}-unknown-linux-gnu.tgz`;
break;
case 'darwin':
file = `${arch}-apple-darwin.zip`;
break;
case 'win32':
file = `${arch}-pc-windows-msvc.zip`;
break;
default:
throw new Error(`Unsupported platform: ${process.platform}`);
}
const url = `https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-${file}`;
const dlPath = await tc.downloadTool(url);
if (url.endsWith('.zip')) {
await tc.extractZip(dlPath, binDir);
} else if (url.endsWith('.tgz')) {
await tc.extractTar(dlPath, binDir);
}
}
async function installBins() {
const bins = core
.getInput('bins')
.split(',')
.map((bin) => bin.trim())
.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(CARGO_HOME, 'bin');
if (!fs.existsSync(path.join(binDir, 'cargo-binstall'))) {
await downloadAndInstallBinstall(binDir);
}
await exec.exec('cargo', ['binstall', '--no-confirm', '--log-level', 'info', ...bins]);
}
async function run() {
core.info('Setting cargo environment variables');
// Disable incremental compilation
core.exportVariable('CARGO_INCREMENTAL', '0');
// Always enable colored output
core.exportVariable('CARGO_TERM_COLOR', 'always');
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);
throw error;
}
core.info('Adding ~/.cargo/bin to PATH');
core.addPath(path.join(CARGO_HOME, 'bin'));
}
void run();

View file

@ -1,37 +0,0 @@
{
"name": "@moonrepo/setup-rust",
"version": "0.3.4",
"description": "A GitHub action for setting up Rust and Cargo.",
"main": "dist/index.js",
"scripts": {
"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"
},
"repository": {
"type": "git",
"url": "https://github.com/moonrepo/setup-rust"
},
"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/io": "^1.1.3",
"@actions/tool-cache": "^2.0.1",
"@ltd/j-toml": "^1.38.0"
},
"devDependencies": {
"@types/node": "^18.15.11",
"@vercel/ncc": "^0.36.1",
"eslint": "^8.38.0",
"eslint-config-moon": "^2.0.2",
"prettier": "^2.8.7",
"prettier-config-moon": "^1.1.2",
"tsconfig-moon": "^1.3.0",
"typescript": "^5.0.4"
}
}

2891
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff

12
post.ts
View file

@ -1,12 +0,0 @@
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();

View file

@ -1 +0,0 @@
module.exports = 'prettier-config-moon';

View file

@ -1,123 +0,0 @@
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';
import * as io from '@actions/io';
import { RUST_HASH, RUST_VERSION } from './rust';
export const CARGO_HOME = process.env.CARGO_HOME ?? path.join(os.homedir(), '.cargo');
export const CACHE_ENABLED = core.getBooleanInput('cache') || cache.isFeatureAvailable();
export function getCachePaths(): string[] {
return [
// ~/.cargo/registry
path.join(CARGO_HOME, 'registry'),
// /workspace/target/debug
path.join(process.env.GITHUB_WORKSPACE ?? process.cwd(), 'target/debug'),
];
}
export function getCachePrefixes(): string[] {
return [`setup-rustcargo-${process.platform}`, 'setup-rustcargo'];
}
export async function getPrimaryCacheKey() {
const hasher = crypto.createHash('sha1');
core.info('Generating cache key');
core.debug(`Hashing Rust version = ${RUST_VERSION}`);
hasher.update(RUST_VERSION);
core.debug(`Hashing Rust commit hash = ${RUST_HASH}`);
hasher.update(RUST_HASH);
const lockHash = await glob.hashFiles('Cargo.lock');
core.debug(`Hashing Cargo.lock = ${lockHash}`);
hasher.update(lockHash);
const job = process.env.GITHUB_JOB;
if (job) {
core.debug(`Hashing GITHUB_JOB = ${job}`);
hasher.update(job);
}
return `${getCachePrefixes()[0]}-${hasher.digest('hex')}`;
}
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 indexDir = path.join(registryDir, 'index');
for (const index of fs.readdirSync(indexDir)) {
if (fs.existsSync(path.join(indexDir, index, '.git'))) {
const dir = path.join(indexDir, index, '.cache');
core.debug(`Deleting ${dir}`);
try {
// eslint-disable-next-line no-await-in-loop
await io.rmRF(dir);
} catch (error: unknown) {
core.warning(`Failed to delete ${dir}: ${error}`);
}
}
}
// .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(getCachePaths(), primaryKey);
}
export async function restoreCache() {
if (!CACHE_ENABLED) {
return;
}
core.info('Attempting to restore cache');
const primaryKey = await getPrimaryCacheKey();
const cacheKey = await cache.restoreCache(getCachePaths(), primaryKey, getCachePrefixes());
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);
}

View file

@ -1,35 +0,0 @@
/* eslint-disable import/no-mutable-exports */
import * as core from '@actions/core';
import * as exec from '@actions/exec';
export let RUST_VERSION = '';
export let RUST_HASH = '';
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) => {
if (line.startsWith('commit-hash')) {
const value = line.split(':')[1].trim();
core.setOutput('rust-hash', value);
RUST_HASH = value;
// version
} else if (line.startsWith('release')) {
const value = line.split(':')[1].trim();
core.setOutput('rust-version', value);
RUST_VERSION = value;
}
});
}

View file

@ -1,16 +0,0 @@
{
"extends": "tsconfig-moon/tsconfig.json",
"compilerOptions": {
"allowJs": true,
"module": "Node16",
"moduleResolution": "NodeNext",
"noEmit": true,
"verbatimModuleSyntax": false
},
"include": [
".eslintrc.js",
"*.js",
"*.ts",
"tests/*.ts"
]
}