aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cyw43/release.toml5
-rw-r--r--embassy-boot/CHANGELOG.md11
-rw-r--r--embassy-embedded-hal/release.toml5
-rw-r--r--embassy-executor/release.toml5
-rw-r--r--embassy-futures/release.toml5
-rw-r--r--embassy-net-adin1110/CHANGELOG.md11
-rw-r--r--embassy-net-driver-channel/release.toml5
-rw-r--r--embassy-net-driver/CHANGELOG.md3
-rw-r--r--embassy-net-esp-hosted/CHANGELOG.md11
-rw-r--r--embassy-net-nrf91/CHANGELOG.md13
-rw-r--r--embassy-net-nrf91/Cargo.toml4
-rw-r--r--embassy-net-ppp/CHANGELOG.md3
-rw-r--r--embassy-net-wiznet/CHANGELOG.md13
-rw-r--r--embassy-net/CHANGELOG.md3
-rw-r--r--embassy-nrf/release.toml5
-rw-r--r--embassy-release/src/main.rs303
-rw-r--r--embassy-rp/release.toml5
-rw-r--r--embassy-stm32/release.toml5
-rw-r--r--embassy-sync/CHANGELOG.md3
-rw-r--r--embassy-time-driver/release.toml5
-rw-r--r--embassy-time-queue-utils/CHANGELOG.md3
-rw-r--r--embassy-time/release.toml5
-rw-r--r--embassy-usb-dfu/CHANGELOG.md11
-rw-r--r--embassy-usb-driver/release.toml5
-rw-r--r--embassy-usb-logger/CHANGELOG.md3
-rw-r--r--embassy-usb-synopsys-otg/CHANGELOG.md3
-rw-r--r--embassy-usb/release.toml5
-rw-r--r--release/Cargo.toml (renamed from embassy-release/Cargo.toml)3
-rw-r--r--release/config.toml45
-rw-r--r--release/release.toml (renamed from cyw43-pio/release.toml)0
-rw-r--r--release/src/main.rs519
31 files changed, 655 insertions, 370 deletions
diff --git a/cyw43/release.toml b/cyw43/release.toml
deleted file mode 100644
index fb6feaf21..000000000
--- a/cyw43/release.toml
+++ /dev/null
@@ -1,5 +0,0 @@
1pre-release-replacements = [
2 {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1},
3 {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1},
4 {file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## Unreleased - ReleaseDate\n", exactly=1},
5]
diff --git a/embassy-boot/CHANGELOG.md b/embassy-boot/CHANGELOG.md
new file mode 100644
index 000000000..7042ad14c
--- /dev/null
+++ b/embassy-boot/CHANGELOG.md
@@ -0,0 +1,11 @@
1# Changelog
2
3All notable changes to this project will be documented in this file.
4
5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
8<!-- next-header -->
9## Unreleased - ReleaseDate
10
11- First release with changelog.
diff --git a/embassy-embedded-hal/release.toml b/embassy-embedded-hal/release.toml
deleted file mode 100644
index fb6feaf21..000000000
--- a/embassy-embedded-hal/release.toml
+++ /dev/null
@@ -1,5 +0,0 @@
1pre-release-replacements = [
2 {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1},
3 {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1},
4 {file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## Unreleased - ReleaseDate\n", exactly=1},
5]
diff --git a/embassy-executor/release.toml b/embassy-executor/release.toml
deleted file mode 100644
index fb6feaf21..000000000
--- a/embassy-executor/release.toml
+++ /dev/null
@@ -1,5 +0,0 @@
1pre-release-replacements = [
2 {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1},
3 {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1},
4 {file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## Unreleased - ReleaseDate\n", exactly=1},
5]
diff --git a/embassy-futures/release.toml b/embassy-futures/release.toml
deleted file mode 100644
index fb6feaf21..000000000
--- a/embassy-futures/release.toml
+++ /dev/null
@@ -1,5 +0,0 @@
1pre-release-replacements = [
2 {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1},
3 {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1},
4 {file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## Unreleased - ReleaseDate\n", exactly=1},
5]
diff --git a/embassy-net-adin1110/CHANGELOG.md b/embassy-net-adin1110/CHANGELOG.md
new file mode 100644
index 000000000..7042ad14c
--- /dev/null
+++ b/embassy-net-adin1110/CHANGELOG.md
@@ -0,0 +1,11 @@
1# Changelog
2
3All notable changes to this project will be documented in this file.
4
5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
8<!-- next-header -->
9## Unreleased - ReleaseDate
10
11- First release with changelog.
diff --git a/embassy-net-driver-channel/release.toml b/embassy-net-driver-channel/release.toml
deleted file mode 100644
index fb6feaf21..000000000
--- a/embassy-net-driver-channel/release.toml
+++ /dev/null
@@ -1,5 +0,0 @@
1pre-release-replacements = [
2 {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1},
3 {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1},
4 {file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## Unreleased - ReleaseDate\n", exactly=1},
5]
diff --git a/embassy-net-driver/CHANGELOG.md b/embassy-net-driver/CHANGELOG.md
index 165461eff..0c7c27d6e 100644
--- a/embassy-net-driver/CHANGELOG.md
+++ b/embassy-net-driver/CHANGELOG.md
@@ -5,6 +5,9 @@ All notable changes to this project will be documented in this file.
5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 7
8<!-- next-header -->
9## Unreleased - ReleaseDate
10
8## 0.2.0 - 2023-10-18 11## 0.2.0 - 2023-10-18
9 12
10- Added support for IEEE 802.15.4 mediums. 13- Added support for IEEE 802.15.4 mediums.
diff --git a/embassy-net-esp-hosted/CHANGELOG.md b/embassy-net-esp-hosted/CHANGELOG.md
new file mode 100644
index 000000000..7042ad14c
--- /dev/null
+++ b/embassy-net-esp-hosted/CHANGELOG.md
@@ -0,0 +1,11 @@
1# Changelog
2
3All notable changes to this project will be documented in this file.
4
5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
8<!-- next-header -->
9## Unreleased - ReleaseDate
10
11- First release with changelog.
diff --git a/embassy-net-nrf91/CHANGELOG.md b/embassy-net-nrf91/CHANGELOG.md
new file mode 100644
index 000000000..52cbf5ef3
--- /dev/null
+++ b/embassy-net-nrf91/CHANGELOG.md
@@ -0,0 +1,13 @@
1# Changelog
2
3All notable changes to this project will be documented in this file.
4
5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
8<!-- next-header -->
9## Unreleased - ReleaseDate
10
11## 0.1.1 - 2025-08-14
12
13- First release with changelog.
diff --git a/embassy-net-nrf91/Cargo.toml b/embassy-net-nrf91/Cargo.toml
index 9201dc84c..387627491 100644
--- a/embassy-net-nrf91/Cargo.toml
+++ b/embassy-net-nrf91/Cargo.toml
@@ -1,6 +1,6 @@
1[package] 1[package]
2name = "embassy-net-nrf91" 2name = "embassy-net-nrf91"
3version = "0.1.0" 3version = "0.1.1"
4edition = "2021" 4edition = "2021"
5description = "embassy-net driver for Nordic nRF91-series cellular modems" 5description = "embassy-net driver for Nordic nRF91-series cellular modems"
6keywords = ["embedded", "nrf91", "embassy-net", "cellular"] 6keywords = ["embedded", "nrf91", "embassy-net", "cellular"]
@@ -36,4 +36,4 @@ target = "thumbv7em-none-eabi"
36features = ["defmt", "nrf-pac/nrf9160"] 36features = ["defmt", "nrf-pac/nrf9160"]
37 37
38[package.metadata.docs.rs] 38[package.metadata.docs.rs]
39features = ["defmt"] 39features = ["defmt", "nrf-pac/nrf9160"]
diff --git a/embassy-net-ppp/CHANGELOG.md b/embassy-net-ppp/CHANGELOG.md
index 6ce64ddcb..b364608d2 100644
--- a/embassy-net-ppp/CHANGELOG.md
+++ b/embassy-net-ppp/CHANGELOG.md
@@ -5,6 +5,9 @@ All notable changes to this project will be documented in this file.
5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 7
8<!-- next-header -->
9## Unreleased - ReleaseDate
10
8## 0.2.0 - 2025-01-12 11## 0.2.0 - 2025-01-12
9 12
10- Update `ppproto` to v0.2. 13- Update `ppproto` to v0.2.
diff --git a/embassy-net-wiznet/CHANGELOG.md b/embassy-net-wiznet/CHANGELOG.md
new file mode 100644
index 000000000..52cbf5ef3
--- /dev/null
+++ b/embassy-net-wiznet/CHANGELOG.md
@@ -0,0 +1,13 @@
1# Changelog
2
3All notable changes to this project will be documented in this file.
4
5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
8<!-- next-header -->
9## Unreleased - ReleaseDate
10
11## 0.1.1 - 2025-08-14
12
13- First release with changelog.
diff --git a/embassy-net/CHANGELOG.md b/embassy-net/CHANGELOG.md
index 8773772ce..39bc6c0f0 100644
--- a/embassy-net/CHANGELOG.md
+++ b/embassy-net/CHANGELOG.md
@@ -5,7 +5,8 @@ All notable changes to this project will be documented in this file.
5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 7
8## Unreleased 8<!-- next-header -->
9## Unreleased - ReleaseDate
9 10
10No unreleased changes yet... Quick, go send a PR! 11No unreleased changes yet... Quick, go send a PR!
11 12
diff --git a/embassy-nrf/release.toml b/embassy-nrf/release.toml
deleted file mode 100644
index fb6feaf21..000000000
--- a/embassy-nrf/release.toml
+++ /dev/null
@@ -1,5 +0,0 @@
1pre-release-replacements = [
2 {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1},
3 {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1},
4 {file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## Unreleased - ReleaseDate\n", exactly=1},
5]
diff --git a/embassy-release/src/main.rs b/embassy-release/src/main.rs
deleted file mode 100644
index 321d3872c..000000000
--- a/embassy-release/src/main.rs
+++ /dev/null
@@ -1,303 +0,0 @@
1use std::collections::{HashMap, HashSet};
2use std::fs;
3use std::path::{Path, PathBuf};
4use std::process::Command as ProcessCommand;
5
6use clap::{Parser, Subcommand, ValueEnum};
7use regex::Regex;
8use serde::Deserialize;
9use toml_edit::{DocumentMut, Item, Value};
10use walkdir::WalkDir;
11
12/// Tool to traverse and operate on intra-repo Rust crate dependencies
13#[derive(Parser, Debug)]
14#[command(author, version, about)]
15struct Args {
16 /// Path to the root crate
17 #[arg(value_name = "CRATE_PATH")]
18 crate_path: PathBuf,
19
20 /// Command to perform on each crate
21 #[command(subcommand)]
22 command: Command,
23
24 /// Traversal order
25 #[arg(short, long, default_value = "post")]
26 order: TraversalOrder,
27}
28
29#[derive(Debug, Clone, ValueEnum, PartialEq)]
30enum TraversalOrder {
31 Pre,
32 Post,
33}
34
35#[derive(Debug, Subcommand)]
36enum Command {
37 /// Print all dependencies
38 Dependencies,
39
40 /// Release crate
41 Release {
42 #[command(subcommand)]
43 kind: ReleaseKind,
44 },
45}
46
47#[derive(Debug, Subcommand, Clone, Copy, PartialEq)]
48enum ReleaseKind {
49 Patch,
50 Minor,
51}
52
53#[derive(Debug, Deserialize)]
54struct CargoToml {
55 package: Option<Package>,
56 dependencies: Option<Deps>,
57}
58
59#[derive(Debug, Deserialize)]
60struct Package {
61 name: String,
62 version: Option<String>,
63}
64
65#[derive(Debug, Deserialize)]
66#[serde(untagged)]
67enum Dep {
68 Version(String),
69 DetailedTable(HashMap<String, toml::Value>),
70}
71
72type Deps = std::collections::HashMap<String, Dep>;
73
74#[derive(Debug, Deserialize)]
75struct CrateConfig {
76 features: Option<Vec<String>>,
77 target: Option<String>,
78}
79
80type ReleaseConfig = HashMap<String, CrateConfig>;
81
82fn find_path_deps(cargo_path: &Path) -> Vec<PathBuf> {
83 let content = fs::read_to_string(cargo_path).unwrap_or_else(|_| {
84 panic!("Failed to read {:?}", cargo_path);
85 });
86 let parsed: CargoToml = toml::from_str(&content).unwrap_or_else(|e| {
87 panic!("Failed to parse {:?}: {}", cargo_path, e);
88 });
89
90 let mut paths = vec![];
91 if let Some(deps) = parsed.dependencies {
92 for (_name, dep) in deps {
93 match dep {
94 Dep::Version(_) => {
95 // External dependency — skip
96 }
97 Dep::DetailedTable(table) => {
98 if let Some(toml::Value::String(path)) = table.get("path") {
99 let dep_path = cargo_path.parent().unwrap().join(path).canonicalize().unwrap();
100 paths.push(dep_path);
101 }
102 }
103 }
104 }
105 }
106
107 paths
108}
109
110fn visit_recursive(
111 root_crate: &Path,
112 visited: &mut HashSet<PathBuf>,
113 output: &mut Vec<PathBuf>,
114 order: &TraversalOrder,
115) {
116 if !visited.insert(root_crate.to_path_buf()) {
117 return;
118 }
119
120 let cargo_toml = root_crate.join("Cargo.toml");
121 let deps = find_path_deps(&cargo_toml);
122
123 if *order == TraversalOrder::Pre {
124 output.push(root_crate.to_path_buf());
125 }
126
127 let mut deps_sorted = deps;
128 deps_sorted.sort();
129 for dep in deps_sorted {
130 visit_recursive(&dep, visited, output, order);
131 }
132
133 if *order == TraversalOrder::Post {
134 output.push(root_crate.to_path_buf());
135 }
136}
137
138fn get_crate_metadata(crate_path: &Path) -> Option<(String, String)> {
139 let cargo_toml = crate_path.join("Cargo.toml");
140 let content = fs::read_to_string(&cargo_toml).ok()?;
141 let parsed: CargoToml = toml::from_str(&content).ok()?;
142 let pkg = parsed.package?;
143 let name = pkg.name;
144 let version = pkg.version?;
145 Some((name, version))
146}
147
148fn load_release_config() -> ReleaseConfig {
149 let config_path = PathBuf::from("release/config.toml");
150 if !config_path.exists() {
151 return HashMap::new();
152 }
153 let content = fs::read_to_string(&config_path).expect("Failed to read release/config.toml");
154 toml::from_str(&content).expect("Invalid TOML format in release/config.toml")
155}
156
157fn bump_dependency_versions(crate_name: &str, new_version: &str) -> Result<(), String> {
158 let mut cargo_files: Vec<PathBuf> = WalkDir::new(".")
159 .into_iter()
160 .filter_map(Result::ok)
161 .filter(|e| e.file_name() == "Cargo.toml")
162 .map(|e| e.into_path())
163 .collect();
164
165 cargo_files.sort();
166
167 for path in cargo_files {
168 let content = fs::read_to_string(&path).map_err(|e| format!("Failed to read {}: {}", path.display(), e))?;
169
170 let mut doc: DocumentMut = content
171 .parse()
172 .map_err(|e| format!("Failed to parse TOML in {}: {}", path.display(), e))?;
173
174 let mut changed = false;
175
176 for section in ["dependencies", "dev-dependencies", "build-dependencies"] {
177 if let Some(Item::Table(dep_table)) = doc.get_mut(section) {
178 if let Some(item) = dep_table.get_mut(crate_name) {
179 match item {
180 // e.g., foo = "0.1.0"
181 Item::Value(Value::String(_)) => {
182 *item = Item::Value(Value::from(new_version));
183 changed = true;
184 }
185 // e.g., foo = { version = "...", ... }
186 Item::Value(Value::InlineTable(inline)) => {
187 if inline.contains_key("version") {
188 inline["version"] = Value::from(new_version);
189 changed = true;
190 }
191 }
192 _ => {} // Leave unusual formats untouched
193 }
194 }
195 }
196 }
197
198 if changed {
199 fs::write(&path, doc.to_string()).map_err(|e| format!("Failed to write {}: {}", path.display(), e))?;
200 println!("🔧 Updated {} to {} in {}", crate_name, new_version, path.display());
201 }
202 }
203
204 Ok(())
205}
206
207fn run_release_command(
208 crate_path: &Path,
209 crate_name: &str,
210 version: &str,
211 kind: &ReleaseKind,
212 config: Option<&CrateConfig>,
213) -> Result<(), String> {
214 let kind_str = match kind {
215 ReleaseKind::Patch => "patch",
216 ReleaseKind::Minor => "minor",
217 };
218
219 if *kind == ReleaseKind::Minor {
220 bump_dependency_versions(crate_name, version)?;
221 }
222
223 let mut args: Vec<String> = vec!["release".into(), kind_str.into()];
224
225 if let Some(cfg) = config {
226 if let Some(features) = &cfg.features {
227 args.push("--features".into());
228 args.push(features.join(","));
229 }
230 if let Some(target) = &cfg.target {
231 args.push("--target".into());
232 args.push(target.clone());
233 }
234 }
235
236 let status = ProcessCommand::new("cargo")
237 .args(&args)
238 .current_dir(crate_path)
239 .status()
240 .map_err(|e| format!("Failed to run cargo release: {}", e))?;
241
242 if !status.success() {
243 return Err(format!("`cargo release {}` failed in crate {}", kind_str, crate_name));
244 }
245
246 //args.push("--execute".into());
247 //let status = ProcessCommand::new("cargo")
248 // .args(&args)
249 // .current_dir(crate_path)
250 // .status()
251 // .map_err(|e| format!("Failed to run cargo release --execute: {}", e))?;
252
253 //if !status.success() {
254 // return Err(format!(
255 // "`cargo release {kind_str} --execute` failed in crate {crate_name}"
256 // ));
257 //}
258
259 Ok(())
260}
261
262fn main() {
263 let args = Args::parse();
264 let root = args.crate_path.canonicalize().expect("Invalid root crate path");
265
266 match args.command {
267 Command::Dependencies => {
268 let mut visited = HashSet::new();
269 let mut ordered = vec![];
270 visit_recursive(&root, &mut visited, &mut ordered, &args.order);
271 for path in ordered {
272 if let Some((name, _)) = get_crate_metadata(&path) {
273 println!("{name}");
274 } else {
275 eprintln!("Warning: could not read crate name from {:?}", path);
276 }
277 }
278 }
279 Command::Release { kind } => {
280 let config = load_release_config();
281 let path = root;
282 match get_crate_metadata(&path) {
283 Some((name, version)) => {
284 println!("🚀 Releasing {name}...");
285 let crate_cfg = config.get(&name);
286 match run_release_command(&path, &name, &version, &kind, crate_cfg) {
287 Ok(_) => {
288 println!("✅ Released {name}");
289 }
290 Err(e) => {
291 eprintln!("❌ Error releasing {name}:\n{e}");
292 eprintln!("\nYou may retry with: `cargo run -- {path:?} release {kind:?}`");
293 std::process::exit(1);
294 }
295 }
296 }
297 None => {
298 eprintln!("Warning: Could not parse crate metadata in {:?}", path);
299 }
300 }
301 }
302 }
303}
diff --git a/embassy-rp/release.toml b/embassy-rp/release.toml
deleted file mode 100644
index fb6feaf21..000000000
--- a/embassy-rp/release.toml
+++ /dev/null
@@ -1,5 +0,0 @@
1pre-release-replacements = [
2 {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1},
3 {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1},
4 {file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## Unreleased - ReleaseDate\n", exactly=1},
5]
diff --git a/embassy-stm32/release.toml b/embassy-stm32/release.toml
deleted file mode 100644
index fb6feaf21..000000000
--- a/embassy-stm32/release.toml
+++ /dev/null
@@ -1,5 +0,0 @@
1pre-release-replacements = [
2 {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1},
3 {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1},
4 {file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## Unreleased - ReleaseDate\n", exactly=1},
5]
diff --git a/embassy-sync/CHANGELOG.md b/embassy-sync/CHANGELOG.md
index fa0340c68..7418ead8d 100644
--- a/embassy-sync/CHANGELOG.md
+++ b/embassy-sync/CHANGELOG.md
@@ -5,7 +5,8 @@ All notable changes to this project will be documented in this file.
5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 7
8## Unreleased 8<!-- next-header -->
9## Unreleased - ReleaseDate
9 10
10- Add `get_mut` to `LazyLock` 11- Add `get_mut` to `LazyLock`
11- Add more `Debug` impls to `embassy-sync`, particularly on `OnceLock` 12- Add more `Debug` impls to `embassy-sync`, particularly on `OnceLock`
diff --git a/embassy-time-driver/release.toml b/embassy-time-driver/release.toml
deleted file mode 100644
index fb6feaf21..000000000
--- a/embassy-time-driver/release.toml
+++ /dev/null
@@ -1,5 +0,0 @@
1pre-release-replacements = [
2 {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1},
3 {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1},
4 {file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## Unreleased - ReleaseDate\n", exactly=1},
5]
diff --git a/embassy-time-queue-utils/CHANGELOG.md b/embassy-time-queue-utils/CHANGELOG.md
index 26200503c..510c29d58 100644
--- a/embassy-time-queue-utils/CHANGELOG.md
+++ b/embassy-time-queue-utils/CHANGELOG.md
@@ -5,7 +5,8 @@ All notable changes to this project will be documented in this file.
5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 7
8## Unreleased 8<!-- next-header -->
9## Unreleased - ReleaseDate
9 10
10- Removed the embassy-executor dependency 11- Removed the embassy-executor dependency
11 12
diff --git a/embassy-time/release.toml b/embassy-time/release.toml
deleted file mode 100644
index fb6feaf21..000000000
--- a/embassy-time/release.toml
+++ /dev/null
@@ -1,5 +0,0 @@
1pre-release-replacements = [
2 {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1},
3 {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1},
4 {file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## Unreleased - ReleaseDate\n", exactly=1},
5]
diff --git a/embassy-usb-dfu/CHANGELOG.md b/embassy-usb-dfu/CHANGELOG.md
new file mode 100644
index 000000000..7042ad14c
--- /dev/null
+++ b/embassy-usb-dfu/CHANGELOG.md
@@ -0,0 +1,11 @@
1# Changelog
2
3All notable changes to this project will be documented in this file.
4
5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
8<!-- next-header -->
9## Unreleased - ReleaseDate
10
11- First release with changelog.
diff --git a/embassy-usb-driver/release.toml b/embassy-usb-driver/release.toml
deleted file mode 100644
index fb6feaf21..000000000
--- a/embassy-usb-driver/release.toml
+++ /dev/null
@@ -1,5 +0,0 @@
1pre-release-replacements = [
2 {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1},
3 {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1},
4 {file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## Unreleased - ReleaseDate\n", exactly=1},
5]
diff --git a/embassy-usb-logger/CHANGELOG.md b/embassy-usb-logger/CHANGELOG.md
index 79ea25839..c88b3ed6a 100644
--- a/embassy-usb-logger/CHANGELOG.md
+++ b/embassy-usb-logger/CHANGELOG.md
@@ -5,7 +5,8 @@ All notable changes to this project will be documented in this file.
5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 7
8## Unreleased 8<!-- next-header -->
9## Unreleased - ReleaseDate
9 10
10## 0.5.0 - 2025-07-22 11## 0.5.0 - 2025-07-22
11 12
diff --git a/embassy-usb-synopsys-otg/CHANGELOG.md b/embassy-usb-synopsys-otg/CHANGELOG.md
index 9913ee533..55eef0a1e 100644
--- a/embassy-usb-synopsys-otg/CHANGELOG.md
+++ b/embassy-usb-synopsys-otg/CHANGELOG.md
@@ -5,7 +5,8 @@ All notable changes to this project will be documented in this file.
5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 7
8## Unreleased 8<!-- next-header -->
9## Unreleased - ReleaseDate
9 10
10## 0.3.0 - 2025-07-22 11## 0.3.0 - 2025-07-22
11 12
diff --git a/embassy-usb/release.toml b/embassy-usb/release.toml
deleted file mode 100644
index fb6feaf21..000000000
--- a/embassy-usb/release.toml
+++ /dev/null
@@ -1,5 +0,0 @@
1pre-release-replacements = [
2 {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1},
3 {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1},
4 {file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## Unreleased - ReleaseDate\n", exactly=1},
5]
diff --git a/embassy-release/Cargo.toml b/release/Cargo.toml
index de548e650..cc1b155e2 100644
--- a/embassy-release/Cargo.toml
+++ b/release/Cargo.toml
@@ -10,3 +10,6 @@ toml = "0.8.8"
10toml_edit = { version = "0.23.1", features = ["serde"] } 10toml_edit = { version = "0.23.1", features = ["serde"] }
11serde = { version = "1.0.198", features = ["derive"] } 11serde = { version = "1.0.198", features = ["derive"] }
12regex = "1.10.4" 12regex = "1.10.4"
13anyhow = "1"
14petgraph = "0.8.2"
15semver = "1.0.26"
diff --git a/release/config.toml b/release/config.toml
index 2292f4077..6b23217fa 100644
--- a/release/config.toml
+++ b/release/config.toml
@@ -1 +1,46 @@
1
2embassy-stm32 = { features = ["defmt", "unstable-pac", "exti", "time-driver-any", "time", "stm32h755zi-cm7", "dual-bank"], target = "thumbv7em-none-eabi" }
3embassy-nrf = { features = ["nrf52840", "time", "defmt", "unstable-pac", "gpiote", "time-driver-rtc1"], target = "thumbv7em-none-eabihf" }
4
1embassy-rp = { features = ["defmt", "unstable-pac", "time-driver", "rp2040"], target = "thumbv6m-none-eabi" } 5embassy-rp = { features = ["defmt", "unstable-pac", "time-driver", "rp2040"], target = "thumbv6m-none-eabi" }
6cyw43 = { features = ["defmt", "firmware-logs"], target = "thumbv6m-none-eabi" }
7#cyw43-pio = { features = ["defmt", "embassy-rp/rp2040"], target = "thumbv6m-none-eabi" }
8
9embassy-boot = { features = ["defmt"] }
10#embassy-boot-nrf = { features = ["defmt", "embassy-nrf/nrf52840"], target = "thumbv7em-none-eabihf" }
11#embassy-boot-rp = { features = ["defmt", "embassy-rp/rp2040"], target = "thumbv6m-none-eabi" }
12#embassy-boot-stm32 = { features = ["defmt", "embassy-stm32/stm32f429zi"], target = "thumbv7em-none-eabi" }
13
14embassy-time = { features = ["defmt", "std"] }
15embassy-time-driver = { }
16embassy-time-queue-utils = { features = ["defmt"] }
17
18embassy-futures = { }
19embassy-embedded-hal = { features = ["time"] }
20embassy-hal-internal = { }
21embassy-executor = { features = ["defmt", "arch-cortex-m", "executor-thread", "executor-interrupt"], target = "thumbv7em-none-eabi" }
22embassy-executor-macros = { }
23embassy-sync = { }
24
25embassy-net = { features = ["defmt", "tcp", "udp", "raw", "dns", "icmp", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "medium-ieee802154", "multicast", "dhcpv4-hostname"] }
26embassy-net-ppp = { }
27embassy-net-esp-hosted = {}
28embassy-net-driver-channel = {}
29embassy-net-wiznet = {}
30embassy-net-nrf91 = { features = ["defmt", "nrf9160"] }
31embassy-net-driver = {}
32embassy-net-tuntap = {}
33embassy-net-adin1110 = {}
34embassy-net-enc28j60 = {}
35
36embassy-usb-driver = { }
37embassy-usb-dfu = { features = ["dfu"] }
38embassy-usb-synopsys-otg = { }
39embassy-usb = { features = ["defmt", "usbd-hid"] }
40embassy-usb-logger = { }
41
42# Unreleased
43# embassy-stm32-wpan = {}
44# embassy-imxrt = {}
45# embassy-nxp = {}
46# embassy-mspm0 = {}
diff --git a/cyw43-pio/release.toml b/release/release.toml
index fb6feaf21..fb6feaf21 100644
--- a/cyw43-pio/release.toml
+++ b/release/release.toml
diff --git a/release/src/main.rs b/release/src/main.rs
new file mode 100644
index 000000000..38bb728a8
--- /dev/null
+++ b/release/src/main.rs
@@ -0,0 +1,519 @@
1use std::collections::{BTreeMap, HashMap};
2use std::fs;
3use std::path::{Path, PathBuf};
4use std::process::Command as ProcessCommand;
5
6use anyhow::{anyhow, Result};
7use clap::{Parser, Subcommand};
8use petgraph::graph::{Graph, NodeIndex};
9use petgraph::visit::Bfs;
10use petgraph::{Directed, Direction};
11use serde::Deserialize;
12use toml_edit::{DocumentMut, Item, Value};
13
14/// Tool to traverse and operate on intra-repo Rust crate dependencies
15#[derive(Parser, Debug)]
16#[command(author, version, about)]
17struct Args {
18 /// Path to embassy repository
19 #[arg(short, long)]
20 repo: PathBuf,
21
22 /// Command to perform on each crate
23 #[command(subcommand)]
24 command: Command,
25}
26
27#[derive(Debug, Subcommand)]
28enum Command {
29 /// All crates and their direct dependencies
30 List,
31 /// List all dependencies for a crate
32 Dependencies {
33 /// Crate name to print dependencies for.
34 #[arg(value_name = "CRATE")]
35 crate_name: String,
36 },
37 /// List all dependencies for a crate
38 Dependents {
39 /// Crate name to print dependencies for.
40 #[arg(value_name = "CRATE")]
41 crate_name: String,
42 },
43
44 /// SemverCheck
45 SemverCheck {
46 /// Crate to check. Will traverse that crate an it's dependents. If not specified checks all crates.
47 #[arg(value_name = "CRATE")]
48 crate_name: String,
49 },
50 /// Prepare to release a crate and all dependents that needs updating
51 /// - Semver checks
52 /// - Bump versions and commit
53 /// - Create tag.
54 PrepareRelease {
55 /// Crate to release. Will traverse that crate an it's dependents. If not specified checks all crates.
56 #[arg(value_name = "CRATE")]
57 crate_name: String,
58 },
59}
60
61#[derive(Debug, Subcommand, Clone, Copy, PartialEq)]
62enum ReleaseKind {
63 Patch,
64 Minor,
65}
66
67#[derive(Debug, Deserialize)]
68struct CargoToml {
69 package: Option<Package>,
70 dependencies: Option<Deps>,
71}
72
73#[derive(Debug, Deserialize)]
74struct Package {
75 name: String,
76 version: Option<String>,
77}
78
79#[derive(Debug, Deserialize)]
80#[serde(untagged)]
81enum Dep {
82 Version(String),
83 DetailedTable(BTreeMap<String, toml::Value>),
84}
85
86type Deps = std::collections::BTreeMap<String, Dep>;
87
88#[derive(Debug, Clone, Deserialize)]
89struct CrateConfig {
90 features: Option<Vec<String>>,
91 target: Option<String>,
92}
93
94type ReleaseConfig = HashMap<String, CrateConfig>;
95
96fn load_release_config(repo: &Path) -> ReleaseConfig {
97 let config_path = repo.join("release/config.toml");
98 if !config_path.exists() {
99 return HashMap::new();
100 }
101 let content = fs::read_to_string(&config_path).expect("Failed to read release/config.toml");
102 toml::from_str(&content).expect("Invalid TOML format in release/config.toml")
103}
104
105fn update_version(c: &mut Crate, new_version: &str) -> Result<()> {
106 let path = &c.path;
107 c.id.version = new_version.to_string();
108 let content = fs::read_to_string(&path)?;
109 let mut doc: DocumentMut = content.parse()?;
110 for section in ["package"] {
111 if let Some(Item::Table(dep_table)) = doc.get_mut(section) {
112 dep_table.insert("version", Item::Value(Value::from(new_version)));
113 }
114 }
115 fs::write(&path, doc.to_string())?;
116 Ok(())
117}
118
119fn update_versions(to_update: &Crate, dep: &CrateId, new_version: &str) -> Result<()> {
120 let path = &to_update.path;
121 let content = fs::read_to_string(&path)?;
122 let mut doc: DocumentMut = content.parse()?;
123 let mut changed = false;
124 for section in ["dependencies", "dev-dependencies", "build-dependencies"] {
125 if let Some(Item::Table(dep_table)) = doc.get_mut(section) {
126 if let Some(item) = dep_table.get_mut(&dep.name) {
127 match item {
128 // e.g., foo = "0.1.0"
129 Item::Value(Value::String(_)) => {
130 *item = Item::Value(Value::from(new_version));
131 changed = true;
132 }
133 // e.g., foo = { version = "...", ... }
134 Item::Value(Value::InlineTable(inline)) => {
135 if inline.contains_key("version") {
136 inline["version"] = Value::from(new_version);
137 changed = true;
138 }
139 }
140 _ => {} // Leave unusual formats untouched
141 }
142 }
143 }
144 }
145
146 if changed {
147 fs::write(&path, doc.to_string())?;
148 println!("🔧 Updated {} to {} in {}", dep.name, new_version, path.display());
149 }
150 Ok(())
151}
152
153#[derive(Debug, Clone)]
154struct Crate {
155 id: CrateId,
156 path: PathBuf,
157 config: CrateConfig,
158 dependencies: Vec<CrateId>,
159}
160
161#[derive(Debug, Clone, PartialOrd, Ord)]
162struct CrateId {
163 name: String,
164 version: String,
165}
166
167impl PartialEq for CrateId {
168 fn eq(&self, other: &CrateId) -> bool {
169 self.name == other.name
170 }
171}
172
173impl Eq for CrateId {}
174impl std::hash::Hash for CrateId {
175 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
176 self.name.hash(state)
177 }
178}
179
180fn list_crates(path: &PathBuf) -> Result<BTreeMap<CrateId, Crate>> {
181 let d = std::fs::read_dir(path)?;
182 let release_config = load_release_config(path);
183 let mut crates = BTreeMap::new();
184 for c in d {
185 let entry = c?;
186 let name = entry.file_name().to_str().unwrap().to_string();
187 if entry.file_type()?.is_dir() && name.starts_with("embassy-") {
188 let entry = entry.path().join("Cargo.toml");
189 if entry.exists() {
190 let content = fs::read_to_string(&entry).unwrap_or_else(|_| {
191 panic!("Failed to read {:?}", entry);
192 });
193 let parsed: CargoToml = toml::from_str(&content).unwrap_or_else(|e| {
194 panic!("Failed to parse {:?}: {}", entry, e);
195 });
196 let p = parsed.package.unwrap();
197 let id = CrateId {
198 name: p.name.clone(),
199 version: p.version.unwrap(),
200 };
201
202 let mut dependencies = Vec::new();
203 if let Some(deps) = parsed.dependencies {
204 for (k, v) in deps {
205 if k.starts_with("embassy-") {
206 dependencies.push(CrateId {
207 name: k,
208 version: match v {
209 Dep::Version(v) => v,
210 Dep::DetailedTable(table) => {
211 table.get("version").unwrap().as_str().unwrap().to_string()
212 }
213 },
214 });
215 }
216 }
217 }
218
219 let path = path.join(entry);
220 if let Some(config) = release_config.get(&p.name) {
221 crates.insert(
222 id.clone(),
223 Crate {
224 id,
225 path,
226 dependencies,
227 config: config.clone(),
228 },
229 );
230 }
231 }
232 }
233 }
234 Ok(crates)
235}
236
237fn build_graph(crates: &BTreeMap<CrateId, Crate>) -> (Graph<CrateId, ()>, HashMap<CrateId, NodeIndex>) {
238 let mut graph = Graph::<CrateId, (), Directed>::new();
239 let mut node_indices: HashMap<CrateId, NodeIndex> = HashMap::new();
240
241 // Helper to insert or get existing node
242 let get_or_insert_node = |id: CrateId, graph: &mut Graph<CrateId, ()>, map: &mut HashMap<CrateId, NodeIndex>| {
243 if let Some(&idx) = map.get(&id) {
244 idx
245 } else {
246 let idx = graph.add_node(id.clone());
247 map.insert(id, idx);
248 idx
249 }
250 };
251
252 for krate in crates.values() {
253 get_or_insert_node(krate.id.clone(), &mut graph, &mut node_indices);
254 }
255
256 for krate in crates.values() {
257 // Insert crate node if not exists
258 let crate_idx = get_or_insert_node(krate.id.clone(), &mut graph, &mut node_indices);
259
260 // Insert dependencies and connect edges
261 for dep in krate.dependencies.iter() {
262 let dep_idx = get_or_insert_node(dep.clone(), &mut graph, &mut node_indices);
263 graph.add_edge(crate_idx, dep_idx, ());
264 }
265 }
266
267 (graph, node_indices)
268}
269
270fn main() -> Result<()> {
271 let args = Args::parse();
272
273 let root = args.repo.canonicalize()?; //.expect("Invalid root crate path");
274 let mut crates = list_crates(&root)?;
275 //println!("Crates: {:?}", crates);
276 let (mut graph, indices) = build_graph(&crates);
277
278 // use petgraph::dot::{Config, Dot};
279 // println!("{:?}", Dot::with_config(&graph, &[Config::EdgeNoLabel]));
280
281 match args.command {
282 Command::List => {
283 let ordered = petgraph::algo::toposort(&graph, None).unwrap();
284 for node in ordered.iter() {
285 if graph.neighbors_directed(*node, Direction::Incoming).count() == 0 {
286 let start = graph.node_weight(*node).unwrap();
287 let mut bfs = Bfs::new(&graph, *node);
288 while let Some(node) = bfs.next(&graph) {
289 let weight = graph.node_weight(node).unwrap();
290 if weight.name == start.name {
291 println!("+ {}-{}", weight.name, weight.version);
292 } else {
293 println!("|- {}-{}", weight.name, weight.version);
294 }
295 }
296 println!("");
297 }
298 }
299 }
300 Command::Dependencies { crate_name } => {
301 let idx = indices
302 .get(&CrateId {
303 name: crate_name.clone(),
304 version: "".to_string(),
305 })
306 .expect("unable to find crate in tree");
307 let mut bfs = Bfs::new(&graph, *idx);
308 while let Some(node) = bfs.next(&graph) {
309 let weight = graph.node_weight(node).unwrap();
310 if weight.name == crate_name {
311 println!("+ {}-{}", weight.name, weight.version);
312 } else {
313 println!("|- {}-{}", weight.name, weight.version);
314 }
315 }
316 }
317 Command::Dependents { crate_name } => {
318 let idx = indices
319 .get(&CrateId {
320 name: crate_name.clone(),
321 version: "".to_string(),
322 })
323 .expect("unable to find crate in tree");
324 let node = graph.node_weight(*idx).unwrap();
325 println!("+ {}-{}", node.name, node.version);
326 for parent in graph.neighbors_directed(*idx, Direction::Incoming) {
327 let weight = graph.node_weight(parent).unwrap();
328 println!("|- {}-{}", weight.name, weight.version);
329 }
330 }
331 Command::SemverCheck { crate_name } => {
332 let c = crates
333 .get(&CrateId {
334 name: crate_name.to_string(),
335 version: "".to_string(),
336 })
337 .unwrap();
338 check_semver(&c)?;
339 }
340 Command::PrepareRelease { crate_name } => {
341 let start = indices
342 .get(&CrateId {
343 name: crate_name.clone(),
344 version: "".to_string(),
345 })
346 .expect("unable to find crate in tree");
347
348 graph.reverse();
349
350 let mut bfs = Bfs::new(&graph, *start);
351
352 while let Some(node) = bfs.next(&graph) {
353 let weight = graph.node_weight(node).unwrap();
354 println!("Preparing {}", weight.name);
355 let mut c = crates.get_mut(weight).unwrap();
356 let ver = semver::Version::parse(&c.id.version)?;
357 let newver = if let Err(_) = check_semver(&c) {
358 println!("Semver check failed, bumping minor!");
359 semver::Version::new(ver.major, ver.minor + 1, 0)
360 } else {
361 semver::Version::new(ver.major, ver.minor, ver.patch + 1)
362 };
363
364 println!(
365 "Updating {} from {} -> {}",
366 weight.name,
367 c.id.version,
368 newver.to_string()
369 );
370 let newver = newver.to_string();
371
372 update_version(&mut c, &newver)?;
373 let c = crates.get(weight).unwrap();
374
375 // Update all nodes further down the tree
376 let mut bfs = Bfs::new(&graph, node);
377 while let Some(dep_node) = bfs.next(&graph) {
378 let dep_weight = graph.node_weight(dep_node).unwrap();
379 let dep = crates.get(dep_weight).unwrap();
380 update_versions(dep, &c.id, &newver)?;
381 }
382
383 // Update changelog
384 update_changelog(&root, &c)?;
385 }
386
387 let weight = graph.node_weight(*start).unwrap();
388 let c = crates.get(weight).unwrap();
389 publish_release(&root, &c, false)?;
390
391 println!("# Please inspect changes and run the following commands when happy:");
392
393 println!("git commit -a -m 'chore: prepare crate releases'");
394 let mut bfs = Bfs::new(&graph, *start);
395 while let Some(node) = bfs.next(&graph) {
396 let weight = graph.node_weight(node).unwrap();
397 println!("git tag {}-v{}", weight.name, weight.version);
398 }
399
400 println!("");
401 println!("# Run these commands to publish the crate and dependents:");
402
403 let mut bfs = Bfs::new(&graph, *start);
404 while let Some(node) = bfs.next(&graph) {
405 let weight = graph.node_weight(node).unwrap();
406 let c = crates.get(weight).unwrap();
407
408 let mut args: Vec<String> = vec![
409 "publish".to_string(),
410 "--manifest-path".to_string(),
411 c.path.display().to_string(),
412 ];
413
414 if let Some(features) = &c.config.features {
415 args.push("--features".into());
416 args.push(features.join(","));
417 }
418
419 if let Some(target) = &c.config.target {
420 args.push("--target".into());
421 args.push(target.clone());
422 }
423
424 /*
425 let mut dry_run = args.clone();
426 dry_run.push("--dry-run".to_string());
427
428 println!("cargo {}", dry_run.join(" "));
429 */
430 println!("cargo {}", args.join(" "));
431 }
432
433 println!("");
434 println!("# Run this command to push changes and tags:");
435 println!("git push --tags");
436 }
437 }
438 Ok(())
439}
440
441fn check_semver(c: &Crate) -> Result<()> {
442 let mut args: Vec<String> = vec![
443 "semver-checks".to_string(),
444 "--manifest-path".to_string(),
445 c.path.display().to_string(),
446 "--default-features".to_string(),
447 ];
448 if let Some(features) = &c.config.features {
449 args.push("--features".into());
450 args.push(features.join(","));
451 }
452
453 let status = ProcessCommand::new("cargo").args(&args).output()?;
454
455 println!("{}", core::str::from_utf8(&status.stdout).unwrap());
456 eprintln!("{}", core::str::from_utf8(&status.stderr).unwrap());
457 if !status.status.success() {
458 return Err(anyhow!("semver check failed"));
459 } else {
460 Ok(())
461 }
462}
463
464fn update_changelog(repo: &Path, c: &Crate) -> Result<()> {
465 let args: Vec<String> = vec![
466 "release".to_string(),
467 "replace".to_string(),
468 "--config".to_string(),
469 repo.join("release").join("release.toml").display().to_string(),
470 "--manifest-path".to_string(),
471 c.path.display().to_string(),
472 "--execute".to_string(),
473 "--no-confirm".to_string(),
474 ];
475
476 let status = ProcessCommand::new("cargo").args(&args).output()?;
477
478 println!("{}", core::str::from_utf8(&status.stdout).unwrap());
479 eprintln!("{}", core::str::from_utf8(&status.stderr).unwrap());
480 if !status.status.success() {
481 return Err(anyhow!("release replace failed"));
482 } else {
483 Ok(())
484 }
485}
486
487fn publish_release(_repo: &Path, c: &Crate, push: bool) -> Result<()> {
488 let mut args: Vec<String> = vec![
489 "publish".to_string(),
490 "--manifest-path".to_string(),
491 c.path.display().to_string(),
492 ];
493
494 if let Some(features) = &c.config.features {
495 args.push("--features".into());
496 args.push(features.join(","));
497 }
498
499 if let Some(target) = &c.config.target {
500 args.push("--target".into());
501 args.push(target.clone());
502 }
503
504 if !push {
505 args.push("--dry-run".to_string());
506 args.push("--allow-dirty".to_string());
507 args.push("--keep-going".to_string());
508 }
509
510 let status = ProcessCommand::new("cargo").args(&args).output()?;
511
512 println!("{}", core::str::from_utf8(&status.stdout).unwrap());
513 eprintln!("{}", core::str::from_utf8(&status.stderr).unwrap());
514 if !status.status.success() {
515 return Err(anyhow!("publish failed"));
516 } else {
517 Ok(())
518 }
519}