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:
parent
42dbaf80f9
commit
dfb7371848
5 changed files with 196 additions and 23 deletions
2
dist/index.js
vendored
2
dist/index.js
vendored
File diff suppressed because one or more lines are too long
130
src/cargo.ts
Normal file
130
src/cargo.ts
Normal 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
46
src/cross.ts
Normal 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();
|
||||
}
|
||||
}
|
11
src/input.ts
11
src/input.ts
|
@ -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,
|
||||
|
|
30
src/main.ts
30
src/main.ts
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue