mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-25 01:34:02 +00:00
xtask: Add command for checking packages conform to certain standards (#15236)
This PR adds a new `xtask` command for checking that packages conform to certain standards. Still a work-in-progress, but right now it checks: - If `[lints] workspace = true` is set - If packages are using non-workspace dependencies Release Notes: - N/A
This commit is contained in:
parent
13693ff80f
commit
f2060ccbe0
8 changed files with 136 additions and 22 deletions
37
Cargo.lock
generated
37
Cargo.lock
generated
|
@ -1906,6 +1906,15 @@ dependencies = [
|
|||
"wayland-client",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "camino"
|
||||
version = "1.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cap-fs-ext"
|
||||
version = "3.0.0"
|
||||
|
@ -1983,6 +1992,29 @@ dependencies = [
|
|||
"winx",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargo-platform"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargo_metadata"
|
||||
version = "0.18.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037"
|
||||
dependencies = [
|
||||
"camino",
|
||||
"cargo-platform",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargo_toml"
|
||||
version = "0.20.2"
|
||||
|
@ -9401,6 +9433,9 @@ name = "semver"
|
|||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
|
@ -13429,9 +13464,9 @@ name = "xtask"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cargo_metadata",
|
||||
"cargo_toml",
|
||||
"clap",
|
||||
"toml 0.8.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -304,6 +304,7 @@ bitflags = "2.6.0"
|
|||
blade-graphics = { git = "https://github.com/zed-industries/blade", rev = "7e497c534d5d4a30c18d9eb182cf39eaf0aaa25e" }
|
||||
blade-macros = { git = "https://github.com/zed-industries/blade", rev = "7e497c534d5d4a30c18d9eb182cf39eaf0aaa25e" }
|
||||
blade-util = { git = "https://github.com/zed-industries/blade", rev = "7e497c534d5d4a30c18d9eb182cf39eaf0aaa25e" }
|
||||
cargo_metadata = "0.18"
|
||||
cargo_toml = "0.20"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
clap = { version = "4.4", features = ["derive"] }
|
||||
|
|
|
@ -10,6 +10,6 @@ workspace = true
|
|||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
cargo_metadata.workspace = true
|
||||
cargo_toml.workspace = true
|
||||
clap = { workspace = true, features = ["derive"] }
|
||||
toml.workspace = true
|
||||
|
|
|
@ -16,6 +16,8 @@ enum CliCommand {
|
|||
/// Runs `cargo clippy`.
|
||||
Clippy(tasks::clippy::ClippyArgs),
|
||||
Licenses(tasks::licenses::LicensesArgs),
|
||||
/// Checks that packages conform to a set of standards.
|
||||
PackageConformity(tasks::package_conformity::PackageConformityArgs),
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
|
@ -24,5 +26,8 @@ fn main() -> Result<()> {
|
|||
match args.command {
|
||||
CliCommand::Clippy(args) => tasks::clippy::run_clippy(args),
|
||||
CliCommand::Licenses(args) => tasks::licenses::run_licenses(args),
|
||||
CliCommand::PackageConformity(args) => {
|
||||
tasks::package_conformity::run_package_conformity(args)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
pub mod clippy;
|
||||
pub mod licenses;
|
||||
pub mod package_conformity;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::Result;
|
||||
use anyhow::{anyhow, Result};
|
||||
use clap::Parser;
|
||||
|
||||
use crate::workspace::load_workspace;
|
||||
|
@ -13,8 +13,11 @@ pub fn run_licenses(_args: LicensesArgs) -> Result<()> {
|
|||
|
||||
let workspace = load_workspace()?;
|
||||
|
||||
for member in workspace.members {
|
||||
let crate_dir = PathBuf::from(&member);
|
||||
for package in workspace.workspace_packages() {
|
||||
let crate_dir = package
|
||||
.manifest_path
|
||||
.parent()
|
||||
.ok_or_else(|| anyhow!("no crate directory for {}", package.name))?;
|
||||
|
||||
if let Some(license_file) = first_license_file(&crate_dir, &LICENSE_FILES) {
|
||||
if !license_file.is_symlink() {
|
||||
|
@ -24,15 +27,15 @@ pub fn run_licenses(_args: LicensesArgs) -> Result<()> {
|
|||
continue;
|
||||
}
|
||||
|
||||
println!("Missing license: {member}");
|
||||
println!("Missing license: {}", package.name);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn first_license_file(path: &Path, license_files: &[&str]) -> Option<PathBuf> {
|
||||
fn first_license_file(path: impl AsRef<Path>, license_files: &[&str]) -> Option<PathBuf> {
|
||||
for license_file in license_files {
|
||||
let path_to_license = path.join(license_file);
|
||||
let path_to_license = path.as_ref().join(license_file);
|
||||
if path_to_license.exists() {
|
||||
return Some(path_to_license);
|
||||
}
|
||||
|
|
77
tooling/xtask/src/tasks/package_conformity.rs
Normal file
77
tooling/xtask/src/tasks/package_conformity.rs
Normal file
|
@ -0,0 +1,77 @@
|
|||
use std::collections::BTreeMap;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use cargo_toml::{Dependency, Manifest};
|
||||
use clap::Parser;
|
||||
|
||||
use crate::workspace::load_workspace;
|
||||
|
||||
#[derive(Parser)]
|
||||
pub struct PackageConformityArgs {}
|
||||
|
||||
pub fn run_package_conformity(_args: PackageConformityArgs) -> Result<()> {
|
||||
let workspace = load_workspace()?;
|
||||
|
||||
let mut non_workspace_dependencies = BTreeMap::new();
|
||||
|
||||
for package in workspace.workspace_packages() {
|
||||
let is_extension = package
|
||||
.manifest_path
|
||||
.parent()
|
||||
.and_then(|parent| parent.parent())
|
||||
.map_or(false, |grandparent_dir| {
|
||||
grandparent_dir.ends_with("extensions")
|
||||
});
|
||||
|
||||
let cargo_toml = read_cargo_toml(&package.manifest_path)?;
|
||||
|
||||
let is_using_workspace_lints = cargo_toml.lints.map_or(false, |lints| lints.workspace);
|
||||
if !is_using_workspace_lints {
|
||||
eprintln!(
|
||||
"{package:?} is not using workspace lints",
|
||||
package = package.name
|
||||
);
|
||||
}
|
||||
|
||||
// Extensions should not use workspace dependencies.
|
||||
if is_extension {
|
||||
continue;
|
||||
}
|
||||
|
||||
for dependencies in [
|
||||
&cargo_toml.dependencies,
|
||||
&cargo_toml.dev_dependencies,
|
||||
&cargo_toml.build_dependencies,
|
||||
] {
|
||||
for (name, dependency) in dependencies {
|
||||
if let Dependency::Inherited(_) = dependency {
|
||||
continue;
|
||||
}
|
||||
|
||||
non_workspace_dependencies
|
||||
.entry(name.to_owned())
|
||||
.or_insert_with(Vec::new)
|
||||
.push(package.name.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (dependency, packages) in non_workspace_dependencies {
|
||||
eprintln!(
|
||||
"{dependency} is being used as a non-workspace dependency: {}",
|
||||
packages.join(", ")
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the contents of the `Cargo.toml` file at the given path.
|
||||
fn read_cargo_toml(path: impl AsRef<Path>) -> Result<Manifest> {
|
||||
let path = path.as_ref();
|
||||
let cargo_toml_bytes = fs::read(&path)?;
|
||||
Manifest::from_slice(&cargo_toml_bytes)
|
||||
.with_context(|| anyhow!("failed to read Cargo.toml at {path:?}"))
|
||||
}
|
|
@ -1,17 +1,9 @@
|
|||
use std::fs;
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use cargo_toml::{Manifest, Workspace};
|
||||
use toml;
|
||||
use anyhow::{Context, Result};
|
||||
use cargo_metadata::{Metadata, MetadataCommand};
|
||||
|
||||
/// Returns the Cargo workspace.
|
||||
pub fn load_workspace() -> Result<Workspace> {
|
||||
let workspace_cargo_toml = fs::read_to_string("Cargo.toml")?;
|
||||
let workspace_cargo_toml: Manifest = toml::from_str(&workspace_cargo_toml)?;
|
||||
|
||||
let workspace = workspace_cargo_toml
|
||||
.workspace
|
||||
.ok_or_else(|| anyhow!("top-level Cargo.toml is not a Cargo workspace"))?;
|
||||
|
||||
Ok(workspace)
|
||||
pub fn load_workspace() -> Result<Metadata> {
|
||||
MetadataCommand::new()
|
||||
.exec()
|
||||
.context("failed to load cargo metadata")
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue