diff options
| author | Dario Nieuwenhuis <[email protected]> | 2025-08-15 19:01:56 +0200 |
|---|---|---|
| committer | Ulf Lilleengen <[email protected]> | 2025-08-25 19:44:50 +0200 |
| commit | 3a6ea3a31c90179fb3cbd30c18e3a310e2ee647c (patch) | |
| tree | 9dcbd98e58bd2a0569b4f30562b51c4e677ea5b9 /release/src | |
| parent | a34e0b1ec57350cfa1d61aa6fc2eced077be5623 (diff) | |
Load all crates in the graph, honor the "publish" flag to prevent publishing examples/tests.
Diffstat (limited to 'release/src')
| -rw-r--r-- | release/src/build.rs | 4 | ||||
| -rw-r--r-- | release/src/cargo.rs | 9 | ||||
| -rw-r--r-- | release/src/main.rs | 130 | ||||
| -rw-r--r-- | release/src/types.rs | 9 |
4 files changed, 114 insertions, 38 deletions
diff --git a/release/src/build.rs b/release/src/build.rs index d1abb2aa1..7c777d36c 100644 --- a/release/src/build.rs +++ b/release/src/build.rs | |||
| @@ -34,6 +34,10 @@ pub(crate) fn build(ctx: &crate::Context, crate_name: Option<&str>) -> Result<() | |||
| 34 | args_builder = args_builder.features(&config.features); | 34 | args_builder = args_builder.features(&config.features); |
| 35 | } | 35 | } |
| 36 | 36 | ||
| 37 | if let Some(ref artifact_dir) = config.artifact_dir { | ||
| 38 | args_builder = args_builder.artifact_dir(artifact_dir); | ||
| 39 | } | ||
| 40 | |||
| 37 | batch_builder.add_command(args_builder.build()); | 41 | batch_builder.add_command(args_builder.build()); |
| 38 | } | 42 | } |
| 39 | } | 43 | } |
diff --git a/release/src/cargo.rs b/release/src/cargo.rs index 0b28458e9..826a3a8b2 100644 --- a/release/src/cargo.rs +++ b/release/src/cargo.rs | |||
| @@ -149,6 +149,15 @@ impl CargoArgsBuilder { | |||
| 149 | } | 149 | } |
| 150 | 150 | ||
| 151 | #[must_use] | 151 | #[must_use] |
| 152 | pub fn artifact_dir<S>(mut self, artifact_dir: S) -> Self | ||
| 153 | where | ||
| 154 | S: Into<String>, | ||
| 155 | { | ||
| 156 | self.args.push(format!("--artifact-dir={}", artifact_dir.into())); | ||
| 157 | self | ||
| 158 | } | ||
| 159 | |||
| 160 | #[must_use] | ||
| 152 | pub fn arg<S>(mut self, arg: S) -> Self | 161 | pub fn arg<S>(mut self, arg: S) -> Self |
| 153 | where | 162 | where |
| 154 | S: Into<String>, | 163 | S: Into<String>, |
diff --git a/release/src/main.rs b/release/src/main.rs index b1bc17255..5605ca332 100644 --- a/release/src/main.rs +++ b/release/src/main.rs | |||
| @@ -3,7 +3,7 @@ use std::fs; | |||
| 3 | use std::path::{Path, PathBuf}; | 3 | use std::path::{Path, PathBuf}; |
| 4 | use std::process::Command as ProcessCommand; | 4 | use std::process::Command as ProcessCommand; |
| 5 | 5 | ||
| 6 | use anyhow::{anyhow, Result}; | 6 | use anyhow::{anyhow, bail, Result}; |
| 7 | use clap::{Parser, Subcommand}; | 7 | use clap::{Parser, Subcommand}; |
| 8 | use log::info; | 8 | use log::info; |
| 9 | use petgraph::graph::{Graph, NodeIndex}; | 9 | use petgraph::graph::{Graph, NodeIndex}; |
| @@ -13,6 +13,25 @@ use simple_logger::SimpleLogger; | |||
| 13 | use toml_edit::{DocumentMut, Item, Value}; | 13 | use toml_edit::{DocumentMut, Item, Value}; |
| 14 | use types::*; | 14 | use types::*; |
| 15 | 15 | ||
| 16 | fn check_publish_dependencies(ctx: &Context) -> Result<()> { | ||
| 17 | for krate in ctx.crates.values() { | ||
| 18 | if krate.publish { | ||
| 19 | for dep_name in &krate.dependencies { | ||
| 20 | if let Some(dep_crate) = ctx.crates.get(dep_name) { | ||
| 21 | if !dep_crate.publish { | ||
| 22 | return Err(anyhow!( | ||
| 23 | "Publishable crate '{}' depends on non-publishable crate '{}'. This is not allowed.", | ||
| 24 | krate.name, | ||
| 25 | dep_name | ||
| 26 | )); | ||
| 27 | } | ||
| 28 | } | ||
| 29 | } | ||
| 30 | } | ||
| 31 | } | ||
| 32 | Ok(()) | ||
| 33 | } | ||
| 34 | |||
| 16 | mod build; | 35 | mod build; |
| 17 | mod cargo; | 36 | mod cargo; |
| 18 | mod semver_check; | 37 | mod semver_check; |
| @@ -56,7 +75,7 @@ enum Command { | |||
| 56 | }, | 75 | }, |
| 57 | /// SemverCheck | 76 | /// SemverCheck |
| 58 | SemverCheck { | 77 | SemverCheck { |
| 59 | /// Crate to check. Will traverse that crate an it's dependents. If not specified checks all crates. | 78 | /// Specific crate name to check |
| 60 | #[arg(value_name = "CRATE")] | 79 | #[arg(value_name = "CRATE")] |
| 61 | crate_name: String, | 80 | crate_name: String, |
| 62 | }, | 81 | }, |
| @@ -120,53 +139,72 @@ fn update_versions(to_update: &Crate, dep: &CrateId, new_version: &str) -> Resul | |||
| 120 | } | 139 | } |
| 121 | 140 | ||
| 122 | fn list_crates(root: &PathBuf) -> Result<BTreeMap<CrateId, Crate>> { | 141 | fn list_crates(root: &PathBuf) -> Result<BTreeMap<CrateId, Crate>> { |
| 123 | let d = std::fs::read_dir(root)?; | ||
| 124 | let mut crates = BTreeMap::new(); | 142 | let mut crates = BTreeMap::new(); |
| 143 | discover_crates(root, &mut crates)?; | ||
| 144 | Ok(crates) | ||
| 145 | } | ||
| 146 | |||
| 147 | fn discover_crates(dir: &PathBuf, crates: &mut BTreeMap<CrateId, Crate>) -> Result<()> { | ||
| 148 | let d = std::fs::read_dir(dir)?; | ||
| 125 | for c in d { | 149 | for c in d { |
| 126 | let entry = c?; | 150 | let entry = c?; |
| 127 | if entry.file_type()?.is_dir() { | 151 | if entry.file_type()?.is_dir() { |
| 128 | let path = root.join(entry.path()); | 152 | let path = dir.join(entry.path()); |
| 129 | let entry = path.join("Cargo.toml"); | 153 | let cargo_toml = path.join("Cargo.toml"); |
| 130 | if entry.exists() { | ||
| 131 | let content = fs::read_to_string(&entry)?; | ||
| 132 | let parsed: ParsedCrate = toml::from_str(&content)?; | ||
| 133 | let id = parsed.package.name; | ||
| 134 | 154 | ||
| 135 | let metadata = &parsed.package.metadata.embassy; | 155 | if cargo_toml.exists() { |
| 156 | let content = fs::read_to_string(&cargo_toml)?; | ||
| 136 | 157 | ||
| 137 | if metadata.skip { | 158 | // Try to parse as a crate, skip if it's a workspace |
| 138 | continue; | 159 | let parsed: Result<ParsedCrate, _> = toml::from_str(&content); |
| 139 | } | 160 | if let Ok(parsed) = parsed { |
| 161 | let id = parsed.package.name; | ||
| 140 | 162 | ||
| 141 | let mut dependencies = Vec::new(); | 163 | let metadata = &parsed.package.metadata.embassy; |
| 142 | for (k, _) in parsed.dependencies { | 164 | |
| 143 | if k.starts_with("embassy-") { | 165 | if metadata.skip { |
| 144 | dependencies.push(k); | 166 | continue; |
| 145 | } | 167 | } |
| 146 | } | ||
| 147 | 168 | ||
| 148 | let mut configs = metadata.build.clone(); | 169 | let mut dependencies = Vec::new(); |
| 149 | if configs.is_empty() { | 170 | for (k, _) in parsed.dependencies { |
| 150 | configs.push(BuildConfig { | 171 | if k.starts_with("embassy-") { |
| 151 | features: vec![], | 172 | dependencies.push(k); |
| 152 | target: None, | 173 | } |
| 153 | }) | 174 | } |
| 154 | } | 175 | |
| 176 | let mut configs = metadata.build.clone(); | ||
| 177 | if configs.is_empty() { | ||
| 178 | configs.push(BuildConfig { | ||
| 179 | features: vec![], | ||
| 180 | target: None, | ||
| 181 | artifact_dir: None, | ||
| 182 | }) | ||
| 183 | } | ||
| 155 | 184 | ||
| 156 | crates.insert( | 185 | crates.insert( |
| 157 | id.clone(), | 186 | id.clone(), |
| 158 | Crate { | 187 | Crate { |
| 159 | name: id, | 188 | name: id, |
| 160 | version: parsed.package.version, | 189 | version: parsed.package.version, |
| 161 | path, | 190 | path, |
| 162 | dependencies, | 191 | dependencies, |
| 163 | configs, | 192 | configs, |
| 164 | }, | 193 | publish: parsed.package.publish, |
| 165 | ); | 194 | }, |
| 195 | ); | ||
| 196 | } | ||
| 197 | } else { | ||
| 198 | // Recursively search subdirectories, but only for examples, tests, and docs | ||
| 199 | let file_name = entry.file_name(); | ||
| 200 | let dir_name = file_name.to_string_lossy(); | ||
| 201 | if dir_name == "examples" || dir_name == "tests" || dir_name == "docs" { | ||
| 202 | discover_crates(&path, crates)?; | ||
| 203 | } | ||
| 166 | } | 204 | } |
| 167 | } | 205 | } |
| 168 | } | 206 | } |
| 169 | Ok(crates) | 207 | Ok(()) |
| 170 | } | 208 | } |
| 171 | 209 | ||
| 172 | fn build_graph(crates: &BTreeMap<CrateId, Crate>) -> (Graph<CrateId, ()>, HashMap<CrateId, NodeIndex>) { | 210 | fn build_graph(crates: &BTreeMap<CrateId, Crate>) -> (Graph<CrateId, ()>, HashMap<CrateId, NodeIndex>) { |
| @@ -214,12 +252,17 @@ fn load_context(args: &Args) -> Result<Context> { | |||
| 214 | let crates = list_crates(&root)?; | 252 | let crates = list_crates(&root)?; |
| 215 | let (graph, indices) = build_graph(&crates); | 253 | let (graph, indices) = build_graph(&crates); |
| 216 | 254 | ||
| 217 | Ok(Context { | 255 | let ctx = Context { |
| 218 | root, | 256 | root, |
| 219 | crates, | 257 | crates, |
| 220 | graph, | 258 | graph, |
| 221 | indices, | 259 | indices, |
| 222 | }) | 260 | }; |
| 261 | |||
| 262 | // Check for publish dependency conflicts | ||
| 263 | check_publish_dependencies(&ctx)?; | ||
| 264 | |||
| 265 | Ok(ctx) | ||
| 223 | } | 266 | } |
| 224 | 267 | ||
| 225 | fn main() -> Result<()> { | 268 | fn main() -> Result<()> { |
| @@ -274,10 +317,21 @@ fn main() -> Result<()> { | |||
| 274 | } | 317 | } |
| 275 | Command::SemverCheck { crate_name } => { | 318 | Command::SemverCheck { crate_name } => { |
| 276 | let c = ctx.crates.get(&crate_name).unwrap(); | 319 | let c = ctx.crates.get(&crate_name).unwrap(); |
| 320 | if !c.publish { | ||
| 321 | bail!("Cannot run semver-check on non-publishable crate '{}'", crate_name); | ||
| 322 | } | ||
| 277 | check_semver(&c)?; | 323 | check_semver(&c)?; |
| 278 | } | 324 | } |
| 279 | Command::PrepareRelease { crate_name } => { | 325 | Command::PrepareRelease { crate_name } => { |
| 280 | let start = ctx.indices.get(&crate_name).expect("unable to find crate in tree"); | 326 | let start = ctx.indices.get(&crate_name).expect("unable to find crate in tree"); |
| 327 | |||
| 328 | // Check if the target crate is publishable | ||
| 329 | let start_weight = ctx.graph.node_weight(*start).unwrap(); | ||
| 330 | let start_crate = ctx.crates.get(start_weight).unwrap(); | ||
| 331 | if !start_crate.publish { | ||
| 332 | bail!("Cannot prepare release for non-publishable crate '{}'", crate_name); | ||
| 333 | } | ||
| 334 | |||
| 281 | let mut rgraph = ctx.graph.clone(); | 335 | let mut rgraph = ctx.graph.clone(); |
| 282 | rgraph.reverse(); | 336 | rgraph.reverse(); |
| 283 | 337 | ||
diff --git a/release/src/types.rs b/release/src/types.rs index c5b774977..4d9d440d8 100644 --- a/release/src/types.rs +++ b/release/src/types.rs | |||
| @@ -13,10 +13,16 @@ pub struct ParsedCrate { | |||
| 13 | pub struct ParsedPackage { | 13 | pub struct ParsedPackage { |
| 14 | pub name: String, | 14 | pub name: String, |
| 15 | pub version: String, | 15 | pub version: String, |
| 16 | #[serde(default = "default_publish")] | ||
| 17 | pub publish: bool, | ||
| 16 | #[serde(default)] | 18 | #[serde(default)] |
| 17 | pub metadata: Metadata, | 19 | pub metadata: Metadata, |
| 18 | } | 20 | } |
| 19 | 21 | ||
| 22 | fn default_publish() -> bool { | ||
| 23 | true | ||
| 24 | } | ||
| 25 | |||
| 20 | #[derive(Debug, Deserialize, Default)] | 26 | #[derive(Debug, Deserialize, Default)] |
| 21 | pub struct Metadata { | 27 | pub struct Metadata { |
| 22 | #[serde(default)] | 28 | #[serde(default)] |
| @@ -36,6 +42,8 @@ pub struct BuildConfig { | |||
| 36 | #[serde(default)] | 42 | #[serde(default)] |
| 37 | pub features: Vec<String>, | 43 | pub features: Vec<String>, |
| 38 | pub target: Option<String>, | 44 | pub target: Option<String>, |
| 45 | #[serde(rename = "artifact-dir")] | ||
| 46 | pub artifact_dir: Option<String>, | ||
| 39 | } | 47 | } |
| 40 | 48 | ||
| 41 | pub type CrateId = String; | 49 | pub type CrateId = String; |
| @@ -47,4 +55,5 @@ pub struct Crate { | |||
| 47 | pub path: PathBuf, | 55 | pub path: PathBuf, |
| 48 | pub dependencies: Vec<CrateId>, | 56 | pub dependencies: Vec<CrateId>, |
| 49 | pub configs: Vec<BuildConfig>, | 57 | pub configs: Vec<BuildConfig>, |
| 58 | pub publish: bool, | ||
| 50 | } | 59 | } |
