Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ build-man = "run --package xtask-build-man --"
stale-label = "run --package xtask-stale-label --"
bump-check = "run --package xtask-bump-check --"
lint-docs = "run --package xtask-lint-docs --"
spellcheck = "run --package xtask-spellcheck --"

[env]
# HACK: Until this is stabilized, `snapbox`s polyfill could get confused
Expand Down
10 changes: 10 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ jobs:
- resolver
- rustfmt
- schema
- spellcheck
- test
- test_gitoxide
permissions:
Expand Down Expand Up @@ -304,3 +305,12 @@ jobs:
- uses: actions/checkout@v5
- uses: taiki-e/install-action@cargo-hack
- run: cargo hack check --all-targets --rust-version --workspace --ignore-private --locked

spellcheck:
name: Spell Check with Typos
runs-on: ubuntu-latest
steps:
- name: Checkout Actions Repository
uses: actions/checkout@v5
- name: Spell Check Repo
uses: crate-ci/[email protected]
15 changes: 13 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ cargo-test-macro = { version = "0.4.8", path = "crates/cargo-test-macro" }
cargo-test-support = { version = "0.9.1", path = "crates/cargo-test-support" }
cargo-util = { version = "0.2.26", path = "crates/cargo-util" }
cargo-util-schemas = { version = "0.10.3", path = "crates/cargo-util-schemas" }
cargo_metadata = "0.23.0"
cargo_metadata = "0.23.1"
clap = "4.5.51"
clap_complete = { version = "4.5.60", features = ["unstable-dynamic"] }
color-print = "0.3.7"
Expand Down
2 changes: 1 addition & 1 deletion crates/cargo-test-support/src/paths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ fn build_dir_ignored_path_patterns() -> Vec<String> {
// Ignore MacOS debug symbols as there are many files/directories that would clutter up
// tests few not a lot of benefit.
"[..].dSYM/[..]",
// Ignore Windows debub symbols files (.pdb)
// Ignore Windows debug symbols files (.pdb)
"[..].pdb",
]
.into_iter()
Expand Down
2 changes: 1 addition & 1 deletion crates/cargo-util-schemas/src/lockfile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ impl TomlLockfileSourceId {
.ok_or_else(|| TomlLockfileSourceIdErrorKind::InvalidSource(source.clone()))?;

// Sparse URLs store the kind prefix (sparse+) in the URL. Therefore, for sparse kinds, we
// want to use the raw `source` instead of the splitted `url`.
// want to use the raw `source` instead of the split `url`.
let url = Url::parse(if kind == "sparse" { &source } else { url }).map_err(|msg| {
TomlLockfileSourceIdErrorKind::InvalidUrl {
url: url.to_string(),
Expand Down
15 changes: 15 additions & 0 deletions crates/xtask-spellcheck/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "xtask-spellcheck"
version = "0.0.0"
edition.workspace = true
publish = false

[dependencies]
anyhow.workspace = true
cargo_metadata.workspace = true
cargo-util.workspace = true
clap.workspace = true
semver.workspace = true

[lints]
workspace = true
202 changes: 202 additions & 0 deletions crates/xtask-spellcheck/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
#![allow(clippy::disallowed_methods)]
#![allow(clippy::print_stderr)]
#![allow(clippy::print_stdout)]

use anyhow::Result;
use cargo_metadata::{Metadata, MetadataCommand};
use clap::{Arg, ArgAction};
use semver::Version;
use std::{
env, io,
path::{Path, PathBuf},
process::Command,
};

const BIN_NAME: &str = "typos";
const PKG_NAME: &str = "typos-cli";
const TYPOS_STEP_PREFIX: &str = " uses: crate-ci/typos@v";

fn main() -> anyhow::Result<()> {
let cli = cli();
exec(&cli.get_matches())?;
Ok(())
}

pub fn cli() -> clap::Command {
clap::Command::new("xtask-spellcheck")
.arg(
Arg::new("color")
.long("color")
.help("Coloring: auto, always, never")
.action(ArgAction::Set)
.value_name("WHEN")
.global(true),
)
.arg(
Arg::new("quiet")
.long("quiet")
.short('q')
.help("Do not print cargo log messages")
.action(ArgAction::SetTrue)
.global(true),
)
.arg(
Arg::new("verbose")
.long("verbose")
.short('v')
.help("Use verbose output (-vv very verbose/build.rs output)")
.action(ArgAction::Count)
.global(true),
)
.arg(
Arg::new("write-changes")
.long("write-changes")
.short('w')
.help("Write fixes out")
.action(ArgAction::SetTrue)
.global(true),
)
}

pub fn exec(matches: &clap::ArgMatches) -> Result<()> {
let mut args = vec![];

match matches.get_one::<String>("color") {
Some(c) if matches!(c.as_str(), "auto" | "always" | "never") => {
args.push("--color");
args.push(c);
}
Some(c) => {
anyhow::bail!(
"argument for --color must be auto, always, or \
never, but found `{}`",
c
);
}
_ => {}
}

if matches.get_flag("quiet") {
args.push("--quiet");
}

let verbose_count = matches.get_count("verbose");

for _ in 0..verbose_count {
args.push("--verbose");
}
if matches.get_flag("write-changes") {
args.push("--write-changes");
}

let metadata = MetadataCommand::new()
.exec()
.expect("cargo_metadata failed");

let required_version = extract_workflow_typos_version(&metadata)?;

let outdir = metadata
.build_directory
.unwrap_or_else(|| metadata.target_directory)
.as_std_path()
.join("tmp");
let workspace_root = metadata.workspace_root.as_path().as_std_path();
let bin_path = crate::ensure_version_or_cargo_install(&outdir, required_version)?;

eprintln!("running {BIN_NAME}");
Command::new(bin_path)
.current_dir(workspace_root)
.args(args)
.status()?;

Ok(())
}

fn extract_workflow_typos_version(metadata: &Metadata) -> anyhow::Result<Version> {
let ws_root = metadata.workspace_root.as_path().as_std_path();
let workflow_path = ws_root.join(".github").join("workflows").join("main.yml");
let file_content = std::fs::read_to_string(workflow_path)?;

if let Some(line) = file_content
.lines()
.find(|line| line.contains(TYPOS_STEP_PREFIX))
&& let Some(stripped) = line.strip_prefix(TYPOS_STEP_PREFIX)
&& let Ok(v) = Version::parse(stripped)
{
Ok(v)
} else {
Err(anyhow::anyhow!("Could not find typos version in workflow"))
}
}

/// If the given executable is installed with the given version, use that,
/// otherwise install via cargo.
pub fn ensure_version_or_cargo_install(
build_dir: &Path,
required_version: Version,
) -> io::Result<PathBuf> {
// Check if the user has a sufficient version already installed
let bin_path = PathBuf::from(BIN_NAME).with_extension(env::consts::EXE_EXTENSION);
if let Some(user_version) = get_typos_version(&bin_path) {
if user_version >= required_version {
return Ok(bin_path);
}
}

let tool_root_dir = build_dir.join("misc-tools");
let tool_bin_dir = tool_root_dir.join("bin");
let bin_path = tool_bin_dir
.join(BIN_NAME)
.with_extension(env::consts::EXE_EXTENSION);

// Check if we have already installed sufficient version
if let Some(misc_tools_version) = get_typos_version(&bin_path) {
if misc_tools_version >= required_version {
return Ok(bin_path);
}
}

eprintln!("required `typos` version ({required_version}) not found, building from source");

let mut cmd = Command::new("cargo");
// use --force to ensure that if the required version is bumped, we update it.
cmd.args(["install", "--locked", "--force", "--quiet"])
.arg("--root")
.arg(&tool_root_dir)
// use --target-dir to ensure we have a build cache so repeated invocations aren't slow.
.arg("--target-dir")
.arg(tool_root_dir.join("target"))
.arg(format!("{PKG_NAME}@{required_version}"))
// modify PATH so that cargo doesn't print a warning telling the user to modify the path.
.env(
"PATH",
env::join_paths(
env::split_paths(&env::var("PATH").unwrap())
.chain(std::iter::once(tool_bin_dir.clone())),
)
.expect("build dir contains invalid char"),
);

let cargo_exit_code = cmd.spawn()?.wait()?;
if !cargo_exit_code.success() {
return Err(io::Error::other("cargo install failed"));
}
assert!(
matches!(bin_path.try_exists(), Ok(true)),
"cargo install did not produce the expected binary"
);
eprintln!("finished {BIN_NAME}");
Ok(bin_path)
}

fn get_typos_version(bin: &PathBuf) -> Option<Version> {
// ignore the process exit code here and instead just let the version number check fail
if let Ok(output) = Command::new(&bin).arg("--version").output()
&& let Ok(s) = String::from_utf8(output.stdout)
&& let Some(version_str) = s.trim().split_whitespace().last()
{
Version::parse(version_str).ok()
} else {
None
}
}
6 changes: 3 additions & 3 deletions src/cargo/core/compiler/build_runner/compilation_files.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,16 +235,16 @@ impl<'a, 'gctx: 'a> CompilationFiles<'a, 'gctx> {
/// Note that some units may share the same directory, so care should be
/// taken in those cases!
fn pkg_dir(&self, unit: &Unit) -> String {
let seperator = match self.ws.gctx().cli_unstable().build_dir_new_layout {
let separator = match self.ws.gctx().cli_unstable().build_dir_new_layout {
true => "/",
false => "-",
};
let name = unit.pkg.package_id().name();
let meta = self.metas[unit];
if let Some(c_extra_filename) = meta.c_extra_filename() {
format!("{}{}{}", name, seperator, c_extra_filename)
format!("{}{}{}", name, separator, c_extra_filename)
} else {
format!("{}{}{}", name, seperator, self.target_short_hash(unit))
format!("{}{}{}", name, separator, self.target_short_hash(unit))
}
}

Expand Down
Loading