mirror of
https://github.com/Swatinem/rust-cache
synced 2026-11-02 21:13:59 +00:00
In a virtual workspace the root `Cargo.toml` is not a package, so the cache key hashed only the members' manifests and left out the root's `[profile]`, `[patch]` and `[workspace.dependencies]` (#268). Editing one of those, such as a `[profile.dev]` opt-level, kept the same key: every run restored the stale cache as an exact hit, rebuilt what the edit invalidated, and saved nothing, so the rebuild repeated on every run until something else changed the key. This change hashes the root manifest of the workspace `cargo metadata` reports, along with its members' manifests. It ignores `workspace.package.version` and the paths and versions of path dependencies in `[workspace.dependencies]`, as it already does for members, so a release version bump still keeps the key. It also reads `Cargo.lock` from that root, so a `workspaces` entry that points at a member directory now hashes the lockfile that builds it. A workspace whose root is a package keeps its key, and a virtual workspace gets a new one once.
72 lines
2.4 KiB
TypeScript
72 lines
2.4 KiB
TypeScript
import * as core from "@actions/core";
|
|
import path from "path";
|
|
|
|
import { getCmdOutput } from "./utils.js";
|
|
|
|
const SAVE_TARGETS = new Set(["lib", "cdylib", "dylib", "rlib", "staticlib", "proc-macro"]);
|
|
|
|
export class Workspace {
|
|
constructor(
|
|
public root: string,
|
|
public target: string,
|
|
) {}
|
|
|
|
async getPackages(
|
|
cmdFormat: string,
|
|
filter: (p: Meta["packages"][0]) => boolean,
|
|
extraArgs?: string,
|
|
): Promise<{ packages: Packages; workspaceRoot: string }> {
|
|
const cmd = "cargo metadata --all-features --format-version 1" + (extraArgs ? ` ${extraArgs}` : "");
|
|
let packages: Packages = [];
|
|
let workspaceRoot = this.root;
|
|
try {
|
|
core.debug(`collecting metadata for "${this.root}"`);
|
|
const meta: Meta = JSON.parse(
|
|
await getCmdOutput(cmdFormat, cmd, {
|
|
cwd: this.root,
|
|
env: { ...process.env, CARGO_ENCODED_RUSTFLAGS: "" },
|
|
}),
|
|
);
|
|
core.debug(`workspace "${this.root}" has ${meta.packages.length} packages`);
|
|
workspaceRoot = meta.workspace_root;
|
|
for (const pkg of meta.packages.filter(filter)) {
|
|
const targets = pkg.targets.filter((t) => t.kind.some((kind) => SAVE_TARGETS.has(kind))).map((t) => t.name);
|
|
packages.push({ name: pkg.name, version: pkg.version, targets, path: path.dirname(pkg.manifest_path) });
|
|
}
|
|
} catch (err) {
|
|
console.error(err);
|
|
}
|
|
return { packages, workspaceRoot };
|
|
}
|
|
|
|
public async getPackagesOutsideWorkspaceRoot(cmdFormat: string): Promise<Packages> {
|
|
const filter = (pkg: Meta["packages"][0]) => !pkg.manifest_path.startsWith(this.root);
|
|
return (await this.getPackages(cmdFormat, filter)).packages;
|
|
}
|
|
|
|
/** The workspace's members, and the root directory of the cargo workspace they belong to. */
|
|
public async getWorkspaceMembers(cmdFormat: string): Promise<{ members: Packages; workspaceRoot: string }> {
|
|
const { packages, workspaceRoot } = await this.getPackages(cmdFormat, (_) => true, "--no-deps");
|
|
return { members: packages, workspaceRoot };
|
|
}
|
|
}
|
|
|
|
export interface PackageDefinition {
|
|
name: string;
|
|
version: string;
|
|
path: string;
|
|
targets: Array<string>;
|
|
}
|
|
|
|
export type Packages = Array<PackageDefinition>;
|
|
|
|
interface Meta {
|
|
workspace_root: string;
|
|
packages: Array<{
|
|
name: string;
|
|
version: string;
|
|
manifest_path: string;
|
|
targets: Array<{ kind: Array<string>; name: string }>;
|
|
}>;
|
|
}
|