mirror of
https://github.com/actions-rs/cargo.git
synced 2025-08-14 20:55:14 +00:00
Rewrite codes
This commit is contained in:
parent
42dbaf80f9
commit
dfb7371848
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.
|
* Parse action input into a some proper thing.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { input } from "@actions-rs/core";
|
import { getInput } from "@actions/core";
|
||||||
|
|
||||||
import stringArgv from "string-argv";
|
import stringArgv from "string-argv";
|
||||||
|
|
||||||
// Parsed action input
|
// Parsed action input
|
||||||
@ -15,13 +14,13 @@ export interface Input {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function get(): Input {
|
export function get(): Input {
|
||||||
const command = input.getInput("command", { required: true });
|
const command = getInput("command", { required: true });
|
||||||
const args = stringArgv(input.getInput("args"));
|
const args = stringArgv(getInput("args"));
|
||||||
let toolchain = input.getInput("toolchain");
|
let toolchain = getInput("toolchain");
|
||||||
if (toolchain.startsWith("+")) {
|
if (toolchain.startsWith("+")) {
|
||||||
toolchain = toolchain.slice(1);
|
toolchain = toolchain.slice(1);
|
||||||
}
|
}
|
||||||
const useCross = input.getInputBool("use-cross");
|
const useCross = getInput("use-cross") === "true";
|
||||||
|
|
||||||
return {
|
return {
|
||||||
command: command,
|
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";
|
export async function run(actionInput: Input): Promise<void> {
|
||||||
|
|
||||||
import * as input from "./input";
|
|
||||||
import { Cargo, Cross } from "@actions-rs/core";
|
|
||||||
|
|
||||||
export async function run(actionInput: input.Input): Promise<void> {
|
|
||||||
let program;
|
let program;
|
||||||
if (actionInput.useCross) {
|
if (actionInput.useCross) {
|
||||||
program = await Cross.getOrInstall();
|
program = await getOrInstallCross();
|
||||||
} else {
|
} else {
|
||||||
program = await Cargo.get();
|
program = await getCargo();
|
||||||
}
|
}
|
||||||
|
|
||||||
let args: string[] = [];
|
const args: string[] = [];
|
||||||
if (actionInput.toolchain) {
|
if (actionInput.toolchain) {
|
||||||
args.push(`+${actionInput.toolchain}`);
|
args.push(`+${actionInput.toolchain}`);
|
||||||
}
|
}
|
||||||
args.push(actionInput.command);
|
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> {
|
async function main(): Promise<void> {
|
||||||
const matchersPath = path.join(__dirname, ".matchers");
|
const matchersPath = join(__dirname, ".matchers");
|
||||||
console.log(`::add-matcher::${path.join(matchersPath, "rust.json")}`);
|
console.log(`::add-matcher::${join(matchersPath, "rust.json")}`);
|
||||||
|
|
||||||
const actionInput = input.get();
|
const actionInput = get();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await run(actionInput);
|
await run(actionInput);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
core.setFailed((<Error>error).message);
|
setFailed((<Error>error).message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user