0
0
Fork 0
mirror of https://github.com/actions-rs/cargo.git synced 2025-05-03 17:19:22 +03:00

Rewrite codes

This commit is contained in:
MikuroXina 2022-02-20 22:09:14 +09:00
parent 42dbaf80f9
commit dfb7371848
No known key found for this signature in database
GPG key ID: C8F29756BC44FE0A
5 changed files with 196 additions and 23 deletions

2
dist/index.js vendored

File diff suppressed because one or more lines are too long

130
src/cargo.ts Normal file
View file

@ -0,0 +1,130 @@
import {
ReserveCacheError,
ValidationError,
restoreCache,
saveCache,
} from "@actions/cache";
import core, { endGroup, info, startGroup } from "@actions/core";
import { dirname, join } from "path";
import { HttpClient } from "@actions/http-client";
import { ITypedResponse } from "@actions/http-client/interfaces";
import { exec } from "@actions/exec";
import { which } from "@actions/io";
export async function resolveVersion(crate: string): Promise<string> {
const url = `https://crates.io/api/v1/crates/${crate}`;
const client = new HttpClient("@ructions (https://github.com/ructions/)");
const resp: ITypedResponse<{
crate: {
newest_version: string;
};
}> = await client.getJson(url);
if (resp.result == null) {
throw new Error("Unable to fetch latest crate version");
}
return resp.result.crate.newest_version;
}
export async function get(): Promise<Cargo> {
try {
const path = await which("cargo", true);
return new Cargo(path);
} catch (error) {
core.error(
"cargo is not installed by default for some virtual environments, \
see https://help.github.com/en/articles/software-in-virtual-environments-for-github-actions"
);
core.error(
"To install it, use this action: https://github.com/actions-rs/toolchain"
);
throw error;
}
}
export class Cargo {
constructor(private readonly path: string) {}
call<K extends string, V>(
args: string[],
options?: Record<K, V>
): Promise<number> {
return exec(this.path, args, options);
}
async installCached(
crate: string,
{
version,
primaryKey,
restoreKeys = [],
}: {
version?: string;
primaryKey?: string;
restoreKeys?: string[];
}
): Promise<string> {
if (version === "latest") {
version = await resolveVersion(crate);
}
if (!primaryKey) {
return await this.install(crate, version);
}
const paths = [join(dirname(this.path))];
const versionKey = version ?? "latest";
const crateKeyBase = `${crate}-${versionKey}`;
const crateKey = `${crateKeyBase}-${primaryKey}`;
const crateRestoreKeys = restoreKeys.map(
(key) => `${crateKeyBase}-${key}`
);
const cacheKey = await restoreCache(paths, crateKey, crateRestoreKeys);
if (cacheKey) {
info(`Using cached \`${crate}\` with version ${versionKey}`);
return crate;
}
const res = await this.install(crate, version);
info(`Caching \`${crate}\` with key ${crateKey}`);
try {
await saveCache(paths, crateKey);
} catch (error: unknown) {
if (error instanceof ValidationError) {
throw error;
}
if (error instanceof ReserveCacheError) {
info(error.message);
} else {
const { message } = error as Error;
info(`[warning]${message}`);
}
}
return res;
}
async install(crate: string, version?: string): Promise<string> {
const args = ["install"];
if (version) {
args.push("--version", version);
}
args.push(crate);
try {
startGroup(`Installing "${crate} = ${version ?? "latest"}"`);
await this.call(args);
} finally {
endGroup();
}
return crate;
}
async findOrInstall(crate: string, version?: string): Promise<string> {
try {
return await which(crate, true);
} catch (error) {
info(`\`${crate}\` is not installed, installing it now`);
}
return this.installCached(crate, { version });
}
}

46
src/cross.ts Normal file
View file

@ -0,0 +1,46 @@
import { Cargo, get as getCargo } from "./cargo";
import { debug, endGroup } from "@actions/core";
import { tmpdir } from "os";
import { which } from "@actions/io";
export type Cross = Cargo;
export async function get(): Promise<Cross> {
const path = await which("cross", true);
return new Cargo(path);
}
export async function install(version?: string): Promise<Cross> {
const cargo = await getCargo();
// Somewhat new Rust is required to compile `cross`
// (TODO: Not sure what version exactly, should clarify)
// but if some action will set an override toolchain before this action called
// (ex. `@ructions/toolchain` with `toolchain: 1.31.0`)
// `cross` compilation will fail.
//
// In order to skip this problem and install `cross` globally
// using the pre-installed system Rust,
// we are going to jump to the tmpdir (skipping directory override that way)
// install `cross` from there and then jump back.
const cwd = process.cwd();
process.chdir(tmpdir());
try {
const path = await cargo.installCached("cross", { version });
return new Cargo(path);
} finally {
process.chdir(cwd);
endGroup();
}
}
export async function getOrInstall(): Promise<Cross> {
try {
return await get();
} catch (error: unknown) {
debug(String(error));
return install();
}
}

View file

@ -2,8 +2,7 @@
* Parse action input into a some proper thing.
*/
import { input } from "@actions-rs/core";
import { getInput } from "@actions/core";
import stringArgv from "string-argv";
// Parsed action input
@ -15,13 +14,13 @@ export interface Input {
}
export function get(): Input {
const command = input.getInput("command", { required: true });
const args = stringArgv(input.getInput("args"));
let toolchain = input.getInput("toolchain");
const command = getInput("command", { required: true });
const args = stringArgv(getInput("args"));
let toolchain = getInput("toolchain");
if (toolchain.startsWith("+")) {
toolchain = toolchain.slice(1);
}
const useCross = input.getInputBool("use-cross");
const useCross = getInput("use-cross") === "true";
return {
command: command,

View file

@ -1,38 +1,36 @@
import path from "path";
import { Input, get } from "./input";
import { get as getCargo } from "./cargo";
import { getOrInstall as getOrInstallCross } from "./cross";
import { join } from "path";
import { setFailed } from "@actions/core";
import * as core from "@actions/core";
import * as input from "./input";
import { Cargo, Cross } from "@actions-rs/core";
export async function run(actionInput: input.Input): Promise<void> {
export async function run(actionInput: Input): Promise<void> {
let program;
if (actionInput.useCross) {
program = await Cross.getOrInstall();
program = await getOrInstallCross();
} else {
program = await Cargo.get();
program = await getCargo();
}
let args: string[] = [];
const args: string[] = [];
if (actionInput.toolchain) {
args.push(`+${actionInput.toolchain}`);
}
args.push(actionInput.command);
args = args.concat(actionInput.args);
await program.call(args);
await program.call(args.concat(actionInput.args));
}
async function main(): Promise<void> {
const matchersPath = path.join(__dirname, ".matchers");
console.log(`::add-matcher::${path.join(matchersPath, "rust.json")}`);
const matchersPath = join(__dirname, ".matchers");
console.log(`::add-matcher::${join(matchersPath, "rust.json")}`);
const actionInput = input.get();
const actionInput = get();
try {
await run(actionInput);
} catch (error) {
core.setFailed((<Error>error).message);
setFailed((<Error>error).message);
}
}