From 7a44874ce01123ed827f567fb0a2971ddda7cd8e Mon Sep 17 00:00:00 2001 From: Jonatan Date: Mon, 23 Feb 2026 09:35:24 +0100 Subject: [PATCH 01/16] add release script using xtask pattern --- .cargo/config.toml | 2 ++ Cargo.lock | 4 ++++ Cargo.toml | 5 ++++- xtask/Cargo.toml | 4 ++++ xtask/src/main.rs | 52 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 .cargo/config.toml create mode 100644 xtask/Cargo.toml create mode 100644 xtask/src/main.rs diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..35049cb --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[alias] +xtask = "run --package xtask --" diff --git a/Cargo.lock b/Cargo.lock index 48d05d2..8791047 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2832,6 +2832,10 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" +[[package]] +name = "xtask" +version = "0.1.0" + [[package]] name = "yoke" version = "0.8.1" diff --git a/Cargo.toml b/Cargo.toml index 41b512f..05785df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,4 +41,7 @@ serde_json = "1.0.149" tower = "0.5.3" bytes = "1.11.0" tower-test = "0.4.0" -jiff = "0.2.20" \ No newline at end of file +jiff = "0.2.20" + +[workspace] +members = ["xtask"] diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml new file mode 100644 index 0000000..37518d7 --- /dev/null +++ b/xtask/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "xtask" +version = "0.1.0" +edition = "2024" diff --git a/xtask/src/main.rs b/xtask/src/main.rs new file mode 100644 index 0000000..049342c --- /dev/null +++ b/xtask/src/main.rs @@ -0,0 +1,52 @@ +use std::process; + +fn main() { + let task = std::env::args().nth(1); + match task.as_deref() { + Some("check") => check_versions(), + _ => { + eprintln!("Usage: cargo xtask check"); + process::exit(1); + } + } +} + +fn read_file(path: &str) -> String { + std::fs::read_to_string(path).unwrap_or_else(|e| { + eprintln!("Failed to read {path}: {e}"); + process::exit(1); + }) +} + +fn extract_version<'a>(content: &'a str, prefix: &str) -> Option<&'a str> { + content.lines().find_map(|line| { + let line = line.trim(); + let rest = line.strip_prefix(prefix)?; + Some(rest.trim().trim_matches('"')) + }) +} + +fn check_versions() { + let cargo = read_file("Cargo.toml"); + let chart = read_file("helm/k8s-cloud-tagger/Chart.yaml"); + + let cargo_version = extract_version(&cargo, "version =") + .unwrap_or_else(|| { eprintln!("No version found in Cargo.toml"); process::exit(1) }); + let chart_version = extract_version(&chart, "version:") + .unwrap_or_else(|| { eprintln!("No version found in Chart.yaml"); process::exit(1) }); + let app_version = extract_version(&chart, "appVersion:") + .unwrap_or_else(|| { eprintln!("No appVersion found in Chart.yaml"); process::exit(1) }); + + let ok = cargo_version == chart_version && cargo_version == app_version; + + println!("Cargo.toml: {cargo_version}"); + println!("Chart version: {chart_version}"); + println!("Chart appVersion: {app_version}"); + + if ok { + println!("OK: all versions match"); + } else { + eprintln!("ERROR: versions do not match"); + process::exit(1); + } +} \ No newline at end of file From c7ed441d6f9ae741680275ab99e344638e447008 Mon Sep 17 00:00:00 2001 From: Jonatan Date: Mon, 23 Feb 2026 10:09:40 +0100 Subject: [PATCH 02/16] add release function --- xtask/src/main.rs | 84 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 80 insertions(+), 4 deletions(-) diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 049342c..4afa209 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -1,11 +1,22 @@ use std::process; +const CARGO_FILE_PATH: &str = "Cargo.toml"; +const CARGO_LOCK_FILE_PATH: &str = "Cargo.lock"; +const HELM_CHART_FILE_PATH: &str = "helm/k8s-cloud-tagger/Chart.yaml"; + fn main() { let task = std::env::args().nth(1); match task.as_deref() { Some("check") => check_versions(), + Some("release") => { + let version = std::env::args().nth(2).unwrap_or_else(|| { + eprintln!("Usage: cargo xtask release "); + process::exit(1); + }); + release(&version); + } _ => { - eprintln!("Usage: cargo xtask check"); + eprintln!("Usage: cargo xtask "); process::exit(1); } } @@ -27,8 +38,8 @@ fn extract_version<'a>(content: &'a str, prefix: &str) -> Option<&'a str> { } fn check_versions() { - let cargo = read_file("Cargo.toml"); - let chart = read_file("helm/k8s-cloud-tagger/Chart.yaml"); + let cargo = read_file(CARGO_FILE_PATH); + let chart = read_file(HELM_CHART_FILE_PATH); let cargo_version = extract_version(&cargo, "version =") .unwrap_or_else(|| { eprintln!("No version found in Cargo.toml"); process::exit(1) }); @@ -49,4 +60,69 @@ fn check_versions() { eprintln!("ERROR: versions do not match"); process::exit(1); } -} \ No newline at end of file +} + +fn release(version: &str) { + // Validate semver format x.y.z + let parts: Vec<&str> = version.split('.').collect(); + if parts.len() != 3 || parts.iter().any(|p| p.parse::().is_err()) { + eprintln!("Invalid version: {version}. Expected format: x.y.z"); + process::exit(1); + } + + bump_file(CARGO_FILE_PATH, "version", version); + bump_file(HELM_CHART_FILE_PATH, "version", version); + bump_file(HELM_CHART_FILE_PATH, "appVersion", version); + + run("cargo", &["generate-lockfile"]); + run("git", &["add", CARGO_FILE_PATH, CARGO_LOCK_FILE_PATH, HELM_CHART_FILE_PATH]); + run("git", &["commit", "--message", &format!("release v{version}")]); + run("git", &["tag", &format!("v{version}")]); + + println!("Done. Run: git push && git push --tags"); +} + +fn bump_file(path: &str, key: &str, version: &str) { + let content = read_file(path); + let prefix = if path.ends_with(".toml") { + format!("{key} = ") + } else { + format!("{key}: ") + }; + let new_content = content + .lines() + .map(|line| { + if line.trim().starts_with(&prefix) { + let indent = &line[..line.len() - line.trim_start().len()]; + if path.ends_with(".toml") { + format!("{indent}{key} = \"{version}\"") + } else { + format!("{indent}{key}: {version}") + } + } else { + line.to_string() + } + }) + .collect::>() + .join("\n") + "\n"; + + std::fs::write(path, new_content).unwrap_or_else(|e| { + eprintln!("Failed to write {path}: {e}"); + process::exit(1); + }); + println!("Updated {path}"); +} + +fn run(cmd: &str, args: &[&str]) { + let status = process::Command::new(cmd) + .args(args) + .status() + .unwrap_or_else(|e| { + eprintln!("Failed to run {cmd}: {e}"); + process::exit(1); + }); + if !status.success() { + eprintln!("Command failed: {cmd} {}", args.join(" ")); + process::exit(1); + } +} From 27e7cd835b9e007c1e873aa63cdacdf4dbfa55c1 Mon Sep 17 00:00:00 2001 From: Jonatan Date: Mon, 23 Feb 2026 10:47:07 +0100 Subject: [PATCH 03/16] disallow downgrade --- xtask/src/main.rs | 52 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 6 deletions(-) diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 4afa209..b5701ab 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -42,11 +42,11 @@ fn check_versions() { let chart = read_file(HELM_CHART_FILE_PATH); let cargo_version = extract_version(&cargo, "version =") - .unwrap_or_else(|| { eprintln!("No version found in Cargo.toml"); process::exit(1) }); + .unwrap_or_else(|| { eprintln!("No version found in {CARGO_FILE_PATH}"); process::exit(1) }); let chart_version = extract_version(&chart, "version:") - .unwrap_or_else(|| { eprintln!("No version found in Chart.yaml"); process::exit(1) }); + .unwrap_or_else(|| { eprintln!("No version found in {HELM_CHART_FILE_PATH}"); process::exit(1) }); let app_version = extract_version(&chart, "appVersion:") - .unwrap_or_else(|| { eprintln!("No appVersion found in Chart.yaml"); process::exit(1) }); + .unwrap_or_else(|| { eprintln!("No appVersion found in {HELM_CHART_FILE_PATH}"); process::exit(1) }); let ok = cargo_version == chart_version && cargo_version == app_version; @@ -63,11 +63,25 @@ fn check_versions() { } fn release(version: &str) { - // Validate semver format x.y.z - let parts: Vec<&str> = version.split('.').collect(); - if parts.len() != 3 || parts.iter().any(|p| p.parse::().is_err()) { + let new = parse_semver(version).unwrap_or_else(|| { eprintln!("Invalid version: {version}. Expected format: x.y.z"); process::exit(1); + }); + + // Check that the new version is an upgrade (downgrade not allowed) + let current_content = read_file(CARGO_FILE_PATH); + let current_str = extract_version(¤t_content, "version=").unwrap_or_else(|| { + eprintln!("Could not read current version from {CARGO_FILE_PATH}"); + process::exit(1); + }); + let current = parse_semver(current_str).unwrap_or_else(|| { + eprintln!("Could not parse current version: {current_str}"); + process::exit(1); + }); + + if current >= new { + eprintln!("New version {version} must be greater than current {current_str}"); + process::exit(1); } bump_file(CARGO_FILE_PATH, "version", version); @@ -82,6 +96,18 @@ fn release(version: &str) { println!("Done. Run: git push && git push --tags"); } +fn parse_semver(v: &str) -> Option<(u32, u32, u32)> { + let parts: Vec<&str> = v.split('.').collect(); + if parts.len() != 3 { + return None; + } + Some(( + parts[0].parse().ok()?, + parts[1].parse().ok()?, + parts[2].parse().ok()?, + )) +} + fn bump_file(path: &str, key: &str, version: &str) { let content = read_file(path); let prefix = if path.ends_with(".toml") { @@ -126,3 +152,17 @@ fn run(cmd: &str, args: &[&str]) { process::exit(1); } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_parse_semver() { + assert_eq!(parse_semver("1.2.3"), Some((1, 2, 3))); + assert_eq!(parse_semver("0.1.0"), Some((0, 1, 0))); + assert_eq!(parse_semver("1.2"), None); + assert_eq!(parse_semver("1.2.x"), None); + assert_eq!(parse_semver(""), None); + } +} From 5b35b334a00fd1fdc0503b21349a00d69223072b Mon Sep 17 00:00:00 2001 From: Jonatan Date: Mon, 23 Feb 2026 10:48:44 +0100 Subject: [PATCH 04/16] fix typo --- xtask/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xtask/src/main.rs b/xtask/src/main.rs index b5701ab..fae0269 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -70,7 +70,7 @@ fn release(version: &str) { // Check that the new version is an upgrade (downgrade not allowed) let current_content = read_file(CARGO_FILE_PATH); - let current_str = extract_version(¤t_content, "version=").unwrap_or_else(|| { + let current_str = extract_version(¤t_content, "version = ").unwrap_or_else(|| { eprintln!("Could not read current version from {CARGO_FILE_PATH}"); process::exit(1); }); From b2171cfee5e68883dc2962ecd06a91fdc9f88826 Mon Sep 17 00:00:00 2001 From: Jonatan Date: Mon, 23 Feb 2026 11:01:52 +0100 Subject: [PATCH 05/16] add changelog --- CHANGELOG.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..b3134c1 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,20 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added +- Kubernetes operator that propagates PVC labels to GCP persistent disks +- GCP label sanitisation (lowercasing, replacing invalid characters with hyphens) +- Helm chart for deployment +- Prometheus metrics for reconciliation and cloud API calls +- Health and readiness endpoints +- Kubernetes Events published on successful tagging +- Nix dev shell for reproducible development environments +- Dev image published to Quay.io +- Integration tests +- `cargo xtask` release tooling with version sync enforcement From a06466b368d78609505acc373d4ff1910762aa87 Mon Sep 17 00:00:00 2001 From: Jonatan Date: Mon, 23 Feb 2026 11:01:55 +0100 Subject: [PATCH 06/16] add comment --- xtask/src/main.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/xtask/src/main.rs b/xtask/src/main.rs index fae0269..352e138 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -79,6 +79,8 @@ fn release(version: &str) { process::exit(1); }); + // Fine for as long as we stick to x.y.z + // If we start doing 1.0.0-alpha releases we should us a parsing lib if current >= new { eprintln!("New version {version} must be greater than current {current_str}"); process::exit(1); From 83cf1afd867b42786bc90436cd92fb55cb5d319e Mon Sep 17 00:00:00 2001 From: Jonatan Date: Mon, 23 Feb 2026 13:26:52 +0100 Subject: [PATCH 07/16] check that changelog is updated --- Cargo.lock | 433 ++++++++++++++++++++++--------- Cargo.toml | 2 +- README.md | 18 +- helm/k8s-cloud-tagger/Chart.yaml | 4 +- xtask/src/main.rs | 19 +- 5 files changed, 341 insertions(+), 135 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8791047..d1fbe6c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -41,9 +41,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.100" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "async-broadcast" @@ -173,9 +173,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bitflags" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" [[package]] name = "block-buffer" @@ -188,21 +188,21 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.1" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "bytes" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "cc" -version = "1.2.53" +version = "1.2.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "755d2fce177175ffca841e9a06afdb2c4ab0f593d53b4dee48147dfaade85932" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" dependencies = [ "find-msvc-tools", "shlex", @@ -459,9 +459,9 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "find-msvc-tools" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "fnv" @@ -469,6 +469,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "foldhash" version = "0.2.0" @@ -486,9 +492,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" dependencies = [ "futures-channel", "futures-core", @@ -501,9 +507,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", "futures-sink", @@ -511,15 +517,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] name = "futures-executor" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" dependencies = [ "futures-core", "futures-task", @@ -528,15 +534,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" [[package]] name = "futures-macro" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", @@ -545,21 +551,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" [[package]] name = "futures-task" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-util" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-channel", "futures-core", @@ -569,7 +575,6 @@ dependencies = [ "futures-task", "memchr", "pin-project-lite", - "pin-utils", "slab", ] @@ -632,6 +637,19 @@ dependencies = [ "wasip2", ] +[[package]] +name = "getrandom" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", + "wasip3", +] + [[package]] name = "gloo-timers" version = "0.3.0" @@ -663,6 +681,15 @@ dependencies = [ "tracing", ] +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash 0.1.5", +] + [[package]] name = "hashbrown" version = "0.16.1" @@ -671,9 +698,15 @@ checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.2.0", ] +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hostname" version = "0.4.2" @@ -786,14 +819,13 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.19" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" dependencies = [ "base64", "bytes", "futures-channel", - "futures-core", "futures-util", "http", "http-body", @@ -913,6 +945,12 @@ dependencies = [ "zerovec", ] +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + [[package]] name = "ident_case" version = "1.0.1" @@ -947,7 +985,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.16.1", + "serde", + "serde_core", ] [[package]] @@ -974,9 +1014,9 @@ checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "jiff" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c867c356cc096b33f4981825ab281ecba3db0acefe60329f044c1789d94c6543" +checksum = "b3e3d65f018c6ae946ab16e80944b97096ed73c35b221d1c478a6c81d8f57940" dependencies = [ "jiff-static", "jiff-tzdb-platform", @@ -989,9 +1029,9 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7946b4325269738f270bb55b3c19ab5c5040525f83fd625259422a9d25d9be5" +checksum = "a17c2b211d863c7fde02cbea8a3c1a439b98e109286554f2860bdded7ff83818" dependencies = [ "proc-macro2", "quote", @@ -1037,9 +1077,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "js-sys" -version = "0.3.85" +version = "0.3.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" +checksum = "c7e709f3e3d22866f9c25b3aff01af289b18422cc8b4262fb19103ee80fe513d" dependencies = [ "once_cell", "wasm-bindgen", @@ -1082,7 +1122,7 @@ dependencies = [ [[package]] name = "k8s-cloud-tagger" -version = "0.1.0" +version = "1.0.0" dependencies = [ "anyhow", "async-trait", @@ -1125,9 +1165,9 @@ dependencies = [ [[package]] name = "kube" -version = "3.0.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dae7229247e4215781e5c5104a056e1e2163943e577f9084cf8bba7b5248f7a" +checksum = "f96b537b4c4f61fc183594edbecbbefa3037e403feac0701bb24e6eff78e0034" dependencies = [ "k8s-openapi", "kube-client", @@ -1138,9 +1178,9 @@ dependencies = [ [[package]] name = "kube-client" -version = "3.0.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "010875e291a9c0a4e076f4f9c35b97d82fd2372cb3bc713252c3d08b7e73ce5b" +checksum = "af97b8b696eb737e5694f087c498ca725b172c2a5bc3a6916328d160225537ee" dependencies = [ "base64", "bytes", @@ -1173,9 +1213,9 @@ dependencies = [ [[package]] name = "kube-core" -version = "3.0.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ac76281aa698dd34111e25b21f5f6561932a30feabab5357152be273f8a81bb" +checksum = "e7aeade7d2e9f165f96b3c1749ff01a8e2dc7ea954bd333bcfcecc37d5226bdd" dependencies = [ "derive_more", "form_urlencoded", @@ -1192,9 +1232,9 @@ dependencies = [ [[package]] name = "kube-derive" -version = "3.0.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "599c09721efcccc0e6a26e93df28c587da60ff5e099c657626fff2af0ae4cbb8" +checksum = "c98f59f4e68864624a0b993a1cc2424439ab7238eaede5c299e89943e2a093ff" dependencies = [ "darling", "proc-macro2", @@ -1206,9 +1246,9 @@ dependencies = [ [[package]] name = "kube-runtime" -version = "3.0.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db43d26700f564baf850f681f3cb0f1195d2699bd379bfa70750ecec4dcb209" +checksum = "fc158473d6d86ec22692874bd5ddccf07474eab5c6bb41f226c522e945da5244" dependencies = [ "ahash", "async-broadcast", @@ -1216,7 +1256,7 @@ dependencies = [ "backon", "educe", "futures", - "hashbrown", + "hashbrown 0.16.1", "hostname", "json-patch", "k8s-openapi", @@ -1237,17 +1277,23 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + [[package]] name = "libc" -version = "0.2.180" +version = "0.2.182" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" [[package]] name = "linux-raw-sys" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" @@ -1287,9 +1333,9 @@ checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" [[package]] name = "memchr" -version = "2.7.6" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "mime" @@ -1334,9 +1380,9 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "openssl-probe" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f50d9b3dabb09ecd771ad0aa242ca6894994c130308ca3d7684634df8037391" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] name = "ordered-float" @@ -1394,9 +1440,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.8.5" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9eb05c21a464ea704b53158d358a31e6425db2f63a1a7312268b05fe2b75f7" +checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" dependencies = [ "memchr", "ucd-trie", @@ -1404,9 +1450,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.8.5" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f9dbced329c441fa79d80472764b1a2c7e57123553b8519b36663a2fb234ed" +checksum = "11f486f1ea21e6c10ed15d5a7c77165d0ee443402f0780849d1768e7d9d6fe77" dependencies = [ "pest", "pest_generator", @@ -1414,9 +1460,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.5" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bb96d5051a78f44f43c8f712d8e810adb0ebf923fc9ed2655a7f66f63ba8ee5" +checksum = "8040c4647b13b210a963c1ed407c1ff4fdfa01c31d6d2a098218702e6664f94f" dependencies = [ "pest", "pest_meta", @@ -1427,9 +1473,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.8.5" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "602113b5b5e8621770cfd490cfd90b9f84ab29bd2b0e49ad83eb6d186cef2365" +checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220" dependencies = [ "pest", "sha2", @@ -1469,15 +1515,15 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "portable-atomic" -version = "1.13.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" [[package]] name = "portable-atomic-util" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +checksum = "7a9db96d7fa8782dd8c15ce32ffe8680bbd1e978a43bf51a34d39483540495f5" dependencies = [ "portable-atomic", ] @@ -1491,11 +1537,21 @@ dependencies = [ "zerovec", ] +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro2" -version = "1.0.103" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] @@ -1537,9 +1593,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.42" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" dependencies = [ "proc-macro2", ] @@ -1581,9 +1637,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.12.2" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", @@ -1593,9 +1649,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", @@ -1604,9 +1660,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" [[package]] name = "reqwest" @@ -1671,9 +1727,9 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ "bitflags", "errno", @@ -1764,9 +1820,9 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" [[package]] name = "same-file" @@ -1788,9 +1844,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54e910108742c57a770f492731f99be216a52fadd361b06c8fb59d74ccc267d2" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" dependencies = [ "dyn-clone", "ref-cast", @@ -1801,9 +1857,9 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4908ad288c5035a8eb12cfdf0d49270def0a268ee162b75eeee0f85d155a7c45" +checksum = "7d115b50f4aaeea07e79c1912f645c7513d81715d0420f8bc77a18c6260b307f" dependencies = [ "proc-macro2", "quote", @@ -1828,9 +1884,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "3.5.1" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ "bitflags", "core-foundation", @@ -1841,9 +1897,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.15.0" +version = "2.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" dependencies = [ "core-foundation-sys", "libc", @@ -1993,9 +2049,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" @@ -2005,9 +2061,9 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" dependencies = [ "libc", "windows-sys 0.60.2", @@ -2033,9 +2089,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.111" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -2069,7 +2125,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0136791f7c95b1f6dd99f9cc786b91bb81c3800b639b3478e561ddb7be95e5f1" dependencies = [ "fastrand", - "getrandom 0.3.4", + "getrandom 0.4.1", "once_cell", "rustix", "windows-sys 0.61.2", @@ -2363,9 +2419,15 @@ checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" [[package]] name = "unicode-ident" -version = "1.0.22" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "unsafe-libyaml" @@ -2443,11 +2505,20 @@ dependencies = [ "wit-bindgen", ] +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", +] + [[package]] name = "wasm-bindgen" -version = "0.2.108" +version = "0.2.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +checksum = "ec1adf1535672f5b7824f817792b1afd731d7e843d2d04ec8f27e8cb51edd8ac" dependencies = [ "cfg-if", "once_cell", @@ -2458,9 +2529,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.58" +version = "0.4.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" +checksum = "fe88540d1c934c4ec8e6db0afa536876c5441289d7f9f9123d4f065ac1250a6b" dependencies = [ "cfg-if", "futures-util", @@ -2472,9 +2543,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.108" +version = "0.2.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +checksum = "19e638317c08b21663aed4d2b9a2091450548954695ff4efa75bff5fa546b3b1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2482,9 +2553,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.108" +version = "0.2.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +checksum = "2c64760850114d03d5f65457e96fc988f11f01d38fbaa51b254e4ab5809102af" dependencies = [ "bumpalo", "proc-macro2", @@ -2495,18 +2566,52 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.108" +version = "0.2.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +checksum = "60eecd4fe26177cfa3339eb00b4a36445889ba3ad37080c2429879718e20ca41" dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + [[package]] name = "web-sys" -version = "0.3.85" +version = "0.3.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" +checksum = "9d6bb20ed2d9572df8584f6dc81d68a41a625cadc6f15999d649a70ce7e3597a" dependencies = [ "js-sys", "wasm-bindgen", @@ -2825,6 +2930,88 @@ name = "wit-bindgen" version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] [[package]] name = "writeable" @@ -2861,18 +3048,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.31" +version = "0.8.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" +checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.31" +version = "0.8.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" +checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517" dependencies = [ "proc-macro2", "quote", @@ -2941,6 +3128,6 @@ dependencies = [ [[package]] name = "zmij" -version = "1.0.16" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfcd145825aace48cff44a8844de64bf75feec3080e0aa5cdbde72961ae51a65" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml index 05785df..5e0a553 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "k8s-cloud-tagger" -version = "0.1.0" +version = "1.0.0" edition = "2024" [dependencies] diff --git a/README.md b/README.md index 7e8e0c0..872679e 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ IMAGE=quay.io/upgrades/k8s-cloud-tagger-dev:sha-6f4cbfe nix run .#kind-test `KEEP_CLUSTER=true` prints a message saying how to use `kubectl` in case you want to inspect the cluster. Otherwise, the cluster is deleted after the test. -## Helm +### Helm To get the raw Kubernetes manifests: @@ -162,7 +162,7 @@ kubectl annotate serviceaccount k8s-cloud-tagger \ --overwrite ``` -## Google Artifact Registry +### Google Artifact Registry If you use an autopilot cluster or just have private nodes, then it's easiest to use pkg.dev. @@ -180,9 +180,9 @@ helm upgrade k8s-cloud-tagger helm/k8s-cloud-tagger -n k8s-cloud-tagger \ --set image.tag="YOUR-FEATURE" ``` -# GCP +## GCP -## GCP label sanitisation +### GCP label sanitisation Kubernetes label keys and values can contain characters that are not valid in GCP labels. GCP labels only allow lowercase letters, digits, hyphens, and underscores (`[a-z0-9_-]`), @@ -200,3 +200,13 @@ For more detail on GCP label requirements, see the [Google Cloud labeling best p | `env: production` | `env: production` | | `upgrades.dev/managed-by: k8s-cloud-tagger` | `upgrades-dev-managed-by: k8s-cloud-tagger` | | `Team: Platform` | `team: platform` | + +## Release + +To release a new version you need to have permissions to push to `main`. + +1. Check out a new branch +1. Update [CHANGELOG.md](./CHANGELOG.md) following the [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) format and [SemVer](https://semver.org/) conventions. +1. Run `cargo xtask release `. This updates and commits the relevant files to git. +1. Raise PR, merge PR +1. When the PR is merged, a job will run that adds a git tag, and builds and pushes the release Dockerimage diff --git a/helm/k8s-cloud-tagger/Chart.yaml b/helm/k8s-cloud-tagger/Chart.yaml index 405961c..63b82c6 100644 --- a/helm/k8s-cloud-tagger/Chart.yaml +++ b/helm/k8s-cloud-tagger/Chart.yaml @@ -4,8 +4,8 @@ description: >- Watch Kubernetes resource labels and automatically propagate them as tags to AWS, GCP, and Azure cloud resources type: application -version: 0.1.0 -appVersion: 0.1.0 +version: 1.0.0 +appVersion: 1.0.0 kubeVersion: ">=1.33.0-0" home: https://github.com/upgrades-dev/k8s-cloud-tagger icon: https://raw.githubusercontent.com/upgrades-dev/k8s-cloud-tagger/main/logo.png diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 352e138..1439c47 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -3,6 +3,7 @@ use std::process; const CARGO_FILE_PATH: &str = "Cargo.toml"; const CARGO_LOCK_FILE_PATH: &str = "Cargo.lock"; const HELM_CHART_FILE_PATH: &str = "helm/k8s-cloud-tagger/Chart.yaml"; +const CHANGELOG_FILE_PATH: &str = "CHANGELOG.md"; fn main() { let task = std::env::args().nth(1); @@ -63,12 +64,11 @@ fn check_versions() { } fn release(version: &str) { + // Parse the version strings to ints let new = parse_semver(version).unwrap_or_else(|| { eprintln!("Invalid version: {version}. Expected format: x.y.z"); process::exit(1); }); - - // Check that the new version is an upgrade (downgrade not allowed) let current_content = read_file(CARGO_FILE_PATH); let current_str = extract_version(¤t_content, "version = ").unwrap_or_else(|| { eprintln!("Could not read current version from {CARGO_FILE_PATH}"); @@ -79,19 +79,28 @@ fn release(version: &str) { process::exit(1); }); - // Fine for as long as we stick to x.y.z - // If we start doing 1.0.0-alpha releases we should us a parsing lib + // Check that the new version is an upgrade (downgrade not allowed) if current >= new { + // Equality is lexicographical and works as long as we stick to x.y.z + // If we start doing 1.0.0-alpha releases we should us a parsing lib eprintln!("New version {version} must be greater than current {current_str}"); process::exit(1); } + // Check that the changelog file is updated with the new version + let changelog = read_file(CHANGELOG_FILE_PATH); + let expected_heading = format!("## [{version}] - "); + if !changelog.lines().any(|line| line.starts_with(&expected_heading)) { + eprintln!("{CHANGELOG_FILE_PATH} does not contain a section for [{version}]"); + eprintln!("Add '## [{version}] - YYYY-MM-DD' before releasing."); + process::exit(1); + } bump_file(CARGO_FILE_PATH, "version", version); bump_file(HELM_CHART_FILE_PATH, "version", version); bump_file(HELM_CHART_FILE_PATH, "appVersion", version); run("cargo", &["generate-lockfile"]); - run("git", &["add", CARGO_FILE_PATH, CARGO_LOCK_FILE_PATH, HELM_CHART_FILE_PATH]); + run("git", &["add", CARGO_FILE_PATH, CARGO_LOCK_FILE_PATH, HELM_CHART_FILE_PATH, CHANGELOG_FILE_PATH]); run("git", &["commit", "--message", &format!("release v{version}")]); run("git", &["tag", &format!("v{version}")]); From 0785490dcab8203a5ee7b3d71634a509912229b2 Mon Sep 17 00:00:00 2001 From: Jonatan Date: Mon, 23 Feb 2026 13:49:35 +0100 Subject: [PATCH 08/16] use for tag and build the idea is to not need to push directly to main --- .github/workflows/release.yml | 91 +++++++++++++++++++++++++++++++++++ README.md | 4 +- flake.nix | 9 ++++ xtask/src/main.rs | 3 +- 4 files changed, 102 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..fe5a52d --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,91 @@ +name: Release + +on: + push: + branches: [main] + +jobs: + check: + runs-on: ubuntu-latest + outputs: + is_release: ${{ steps.detect.outputs.is_release }} + version: ${{ steps.detect.outputs.version }} + steps: + - uses: actions/checkout@v6 + + - name: Detect release commit + id: detect + run: | + MSG=$(git log -1 --format=%s) + if [[ "$MSG" =~ ^release\ v([0-9]+\.[0-9]+\.[0-9]+)$ ]]; + then + echo "is_release=true" >> "$GITHUB_OUTPUT" + echo "version=${BASH_REMATCH[1]}" >> "$GITHUB_OUTPUT" + else + echo "is_release=false" >> "$GITHUB_OUTPUT" + echo "version=" >> "$GITHUB_OUTPUT" + fi + + tag-and-release: + runs-on: ubuntu-latest + needs: check + if: needs.check.outputs.is_release == 'true' + permissions: + contents: write + steps: + - uses: actions/checkout@v6 + + - name: Create git tag + env: + VERSION: ${{ needs.check.outputs.version }} + run: | + TAG="v${VERSION}" + if git ls-remote --tags origin "$TAG" | grep -qF "refs/tags/$TAG"; + then + echo "Tag $TAG already exists, skipping." + else + git tag "$TAG" + git push origin "$TAG" + fi + + - name: Setup Nix + uses: cachix/install-nix-action@v31 + with: + github_access_token: ${{ secrets.GITHUB_TOKEN }} + + - name: Restore Nix cache + uses: nix-community/cache-nix-action@v7 + with: + primary-key: nix-${{ runner.os }}-${{ hashFiles('**/Cargo.lock', 'flake.lock') }} + + - name: Build release image + run: nix build .#image + + - name: Push release image + id: push + env: + VERSION: ${{ needs.check.outputs.version }} + run: | + TAG="v${VERSION}" + IMAGE="quay.io/upgrades/k8s-cloud-tagger:${TAG}" + + nix develop -c skopeo login quay.io \ + -u "${{ secrets.QUAY_USERNAME }}" \ + -p "${{ secrets.QUAY_PASSWORD }}" + + nix develop -c skopeo copy docker-archive:result "docker://${IMAGE}" + + echo "image=${IMAGE}" >> "$GITHUB_OUTPUT" + echo "tag=${TAG}" >> "$GITHUB_OUTPUT" + + - name: Summary + run: | + cat >> "$GITHUB_STEP_SUMMARY" << EOF + ## Release ${{ needs.check.outputs.version }} + + | | | + | --- | --- | + | **Image** | \`${{ steps.push.outputs.image }}\` | + | **Tag** | \`${{ steps.push.outputs.tag }}\` | + | **Registry** | [Quay.io](https://quay.io/repository/upgrades/k8s-cloud-tagger?tab=tags) | + EOF diff --git a/README.md b/README.md index 872679e..d0fc8ea 100644 --- a/README.md +++ b/README.md @@ -203,10 +203,8 @@ For more detail on GCP label requirements, see the [Google Cloud labeling best p ## Release -To release a new version you need to have permissions to push to `main`. - 1. Check out a new branch 1. Update [CHANGELOG.md](./CHANGELOG.md) following the [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) format and [SemVer](https://semver.org/) conventions. 1. Run `cargo xtask release `. This updates and commits the relevant files to git. 1. Raise PR, merge PR -1. When the PR is merged, a job will run that adds a git tag, and builds and pushes the release Dockerimage +1. When the PR is merged, a job will run that adds a git tag, and builds and pushes the release Docker image diff --git a/flake.nix b/flake.nix index 2618b4d..e907de0 100644 --- a/flake.nix +++ b/flake.nix @@ -194,6 +194,15 @@ User = "65532:65532"; }; }; + + image = pkgs.dockerTools.buildImage { + name = "quay.io/upgrades/k8s-cloud-tagger-dev"; + tag = "latest"; + fromImage = chainguardStatic; + config = { + Entrypoint = [ "${binaryMusl}/bin/k8s-cloud-tagger" ]; + User = "65532:65532"; + }; }; # ====================================================================== diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 1439c47..d9bc38f 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -102,9 +102,8 @@ fn release(version: &str) { run("cargo", &["generate-lockfile"]); run("git", &["add", CARGO_FILE_PATH, CARGO_LOCK_FILE_PATH, HELM_CHART_FILE_PATH, CHANGELOG_FILE_PATH]); run("git", &["commit", "--message", &format!("release v{version}")]); - run("git", &["tag", &format!("v{version}")]); - println!("Done. Run: git push && git push --tags"); + println!("Done. Open a PR with this commit. CI will tag and release on merge."); } fn parse_semver(v: &str) -> Option<(u32, u32, u32)> { From 29e8dfff28e546580ff16f221d55bd6e456c99ae Mon Sep 17 00:00:00 2001 From: Jonatan Date: Mon, 23 Feb 2026 14:18:21 +0100 Subject: [PATCH 09/16] run version check in ci --- .github/workflows/ci.yml | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b7eea0a..386d515 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,6 +8,7 @@ on: - 'src/**' - 'Cargo.*' - 'flake.*' + - 'xtask/**' pull_request: branches: [main] paths: @@ -15,6 +16,7 @@ on: - 'src/**' - 'Cargo.*' - 'flake.*' + - 'xtask/**' concurrency: group: ci-${{ github.ref }} @@ -45,6 +47,18 @@ jobs: primary-key: nix-${{ runner.os }}-${{ hashFiles('**/Cargo.lock', 'flake.lock') }} - run: nix build .#checks.x86_64-linux.clippy + check-versions: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: cachix/install-nix-action@v31 + with: + github_access_token: ${{ secrets.GITHUB_TOKEN }} + - uses: nix-community/cache-nix-action@v7 + with: + primary-key: nix-${{ runner.os }}-${{ hashFiles('**/Cargo.lock', 'flake.lock') }} + - run: nix develop -c cargo xtask check + test: runs-on: ubuntu-latest steps: @@ -71,7 +85,7 @@ jobs: summary: runs-on: ubuntu-latest - needs: [fmt, clippy, test, build] + needs: [fmt, clippy, check-versions, test, build] if: always() steps: - name: Summary @@ -79,13 +93,14 @@ jobs: run: | fmt="${{ needs.fmt.result }}" clippy="${{ needs.clippy.result }}" + check_versions="${{ needs.check-versions.result }}" test="${{ needs.test.result }}" build="${{ needs.build.result }}" icon() { [[ "$1" == "success" ]] && echo "✅" || echo "❌"; } # Overall result - if [[ "$fmt" == "success" && "$clippy" == "success" && "$test" == "success" && "$build" == "success" ]]; then + if [[ "$fmt" == "success" && "$clippy" == "success" && "$check_versions" == "success" && "$test" == "success" && "$build" == "success" ]]; then header="## ✅ CI Passed" else header="## ❌ CI Failed" @@ -98,6 +113,7 @@ jobs: | --- | --- | | Formatting | $(icon "$fmt") \`cargo fmt\` | | Linting | $(icon "$clippy") \`cargo clippy\` | + | Versioning | $(icon "$check_versions") \`cargo xtask check\` | | Tests | $(icon "$test") \`cargo test\` | | Build | $(icon "$build") \`static musl binary\` | @@ -126,6 +142,17 @@ jobs: EOF fi + if [[ "$check_versions" != "success" ]]; then + cat >> "$GITHUB_STEP_SUMMARY" << 'EOF' + ### 🔧 Fix check versions warnings + + ```bash + cargo xtask check + ``` + + EOF + fi + if [[ "$test" != "success" ]]; then cat >> "$GITHUB_STEP_SUMMARY" << 'EOF' ### 🔧 Fix tests @@ -152,6 +179,7 @@ jobs: [[ \ "$fmt" == "success" && \ "$clippy" == "success" && \ + "$check_versions" == "success" && \ "$test" == "success" && \ "$build" == "success" \ ]] From ecd7ae22ea2413170e8760c30a4d242a0263c984 Mon Sep 17 00:00:00 2001 From: Jonatan Date: Mon, 23 Feb 2026 14:35:15 +0100 Subject: [PATCH 10/16] reorganise readme a bit --- README.md | 93 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 47 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index d0fc8ea..d05b9eb 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,6 @@ nix build ### Integration tests -#### Run an e2e test locally - **Note to Mac users**: The e2e runs on Mac, but the Dockerimage is built by Docker instead of Nix, due to compatibility issues. Linux users can opt in to use Docker build with `USE_DOCKER_BUILD=true`, but that is mainly for troubleshooting since it's generally slower than Nix. @@ -61,8 +59,6 @@ IMAGE=quay.io/upgrades/k8s-cloud-tagger-dev:sha-6f4cbfe nix run .#kind-test `KEEP_CLUSTER=true` prints a message saying how to use `kubectl` in case you want to inspect the cluster. Otherwise, the cluster is deleted after the test. -### Helm - To get the raw Kubernetes manifests: ```bash @@ -70,7 +66,9 @@ nix develop helm template k8s-cloud-tagger helm/k8s-cloud-tagger/ --set serviceMonitor.enabled=true ``` -### To deploy to a GKE cluster +## Deploy + +### GKE Create a low cost, minimal cluster for development: @@ -98,43 +96,10 @@ gcloud container clusters get-credentials cluster-1 \ --project "${GCP_PROJECT}" ``` -Build and push an image from your branch with the [push-dev-image](https://github.com/upgrades-dev/k8s-cloud-tagger/actions/workflows/push-dev-image.yml) GHA job. - -Install Helm chart: - -```bash -helm install k8s-cloud-tagger helm/k8s-cloud-tagger \ - --set deployment.env.RUST_LOG="debug" \ - --set cloudProvider=gcp \ - --set image.repository=quay.io/upgrades/k8s-cloud-tagger-dev \ - --set image.tag="sha-$(git rev-parse --short HEAD)" -``` - -Where the value for `image.tag` matches the tag of the image pushed to [Quay](https://quay.io/repository/upgrades/k8s-cloud-tagger-dev?tab=tags). - -#### Useful commands for GKE - -Scale down the cluster to zero (stop paying for compute): - -```bash -gcloud container clusters resize cluster-1 \ - --node-pool default-pool \ - --num-nodes 0 \ - --zone "${GCP_ZONE}" \ - --project "${GCP_PROJECT}" -``` - -Scale up again: - -```bash -gcloud container clusters resize cluster-1 \ - --node-pool default-pool \ - --num-nodes 1 \ - --zone "${GCP_ZONE}" \ - --project "${GCP_PROJECT}" -``` +#### IAM setup Grant IAM permissions to the controller's service account: + ```bash # Set your project ID once export PROJECT_ID="" @@ -162,9 +127,25 @@ kubectl annotate serviceaccount k8s-cloud-tagger \ --overwrite ``` -### Google Artifact Registry +#### Helm install + +You can let GitHub CI build and push your branch's image and push it to Quay, and have the cluster pull the image from there. If you use an autopilot cluster or just have private nodes, then it's easiest to use Google Artifact Registry. + +##### Push image to Quay + +Build and push an image from your branch with the [push-dev-image](https://github.com/upgrades-dev/k8s-cloud-tagger/actions/workflows/push-dev-image.yml) GHA job. + +```bash +helm install k8s-cloud-tagger helm/k8s-cloud-tagger \ + --set deployment.env.RUST_LOG="debug" \ + --set cloudProvider=gcp \ + --set image.repository=quay.io/upgrades/k8s-cloud-tagger-dev \ + --set image.tag="sha-$(git rev-parse --short HEAD)" +``` + +Where the value for `image.tag` matches the tag of the image pushed to [Quay](https://quay.io/repository/upgrades/k8s-cloud-tagger-dev?tab=tags). -If you use an autopilot cluster or just have private nodes, then it's easiest to use pkg.dev. +##### Push image to Google Artifact Registry ```bash nix develop @@ -176,13 +157,33 @@ docker tag quay.io/upgrades/k8s-cloud-tagger-dev:dev \ helm upgrade k8s-cloud-tagger helm/k8s-cloud-tagger -n k8s-cloud-tagger \ --set deployment.env.RUST_LOG="debug" --set cloudProvider=gcp \ - --set image.repository="${REGION}-docker.pkg.dev/${PROJECT_ID}/k8s-cloud-tagger/controller" + --set image.repository="${REGION}-docker.pkg.dev/${PROJECT_ID}/k8s-cloud-tagger/controller" \ --set image.tag="YOUR-FEATURE" ``` -## GCP +#### Useful commands + +Scale down the cluster to zero (stop paying for compute): + +```bash +gcloud container clusters resize cluster-1 \ + --node-pool default-pool \ + --num-nodes 0 \ + --zone "${GCP_ZONE}" \ + --project "${GCP_PROJECT}" +``` + +Scale up again: + +```bash +gcloud container clusters resize cluster-1 \ + --node-pool default-pool \ + --num-nodes 1 \ + --zone "${GCP_ZONE}" \ + --project "${GCP_PROJECT}" +``` -### GCP label sanitisation +#### Label sanitisation Kubernetes label keys and values can contain characters that are not valid in GCP labels. GCP labels only allow lowercase letters, digits, hyphens, and underscores (`[a-z0-9_-]`), @@ -207,4 +208,4 @@ For more detail on GCP label requirements, see the [Google Cloud labeling best p 1. Update [CHANGELOG.md](./CHANGELOG.md) following the [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) format and [SemVer](https://semver.org/) conventions. 1. Run `cargo xtask release `. This updates and commits the relevant files to git. 1. Raise PR, merge PR -1. When the PR is merged, a job will run that adds a git tag, and builds and pushes the release Docker image +1. When the PR is merged, a job will run that adds a git tag, and builds and pushes the release Docker image \ No newline at end of file From 53a42fff971c8f8a0d5075e4a9824a0942862cee Mon Sep 17 00:00:00 2001 From: Jonatan Date: Mon, 23 Feb 2026 14:39:39 +0100 Subject: [PATCH 11/16] nix syntax --- flake.nix | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index e907de0..724de06 100644 --- a/flake.nix +++ b/flake.nix @@ -196,13 +196,14 @@ }; image = pkgs.dockerTools.buildImage { - name = "quay.io/upgrades/k8s-cloud-tagger-dev"; + name = "quay.io/upgrades/k8s-cloud-tagger"; tag = "latest"; fromImage = chainguardStatic; config = { Entrypoint = [ "${binaryMusl}/bin/k8s-cloud-tagger" ]; User = "65532:65532"; }; + }; }; # ====================================================================== From 3279c6457d9a9a4d60d94064c8f464fa587e2942 Mon Sep 17 00:00:00 2001 From: Jonatan Date: Mon, 23 Feb 2026 14:40:49 +0100 Subject: [PATCH 12/16] cargo fmt --- xtask/src/main.rs | 308 +++++++++++++++++++++++++--------------------- 1 file changed, 165 insertions(+), 143 deletions(-) diff --git a/xtask/src/main.rs b/xtask/src/main.rs index d9bc38f..05a2587 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -6,173 +6,195 @@ const HELM_CHART_FILE_PATH: &str = "helm/k8s-cloud-tagger/Chart.yaml"; const CHANGELOG_FILE_PATH: &str = "CHANGELOG.md"; fn main() { - let task = std::env::args().nth(1); - match task.as_deref() { - Some("check") => check_versions(), - Some("release") => { - let version = std::env::args().nth(2).unwrap_or_else(|| { - eprintln!("Usage: cargo xtask release "); - process::exit(1); - }); - release(&version); - } - _ => { - eprintln!("Usage: cargo xtask "); - process::exit(1); + let task = std::env::args().nth(1); + match task.as_deref() { + Some("check") => check_versions(), + Some("release") => { + let version = std::env::args().nth(2).unwrap_or_else(|| { + eprintln!("Usage: cargo xtask release "); + process::exit(1); + }); + release(&version); + } + _ => { + eprintln!("Usage: cargo xtask "); + process::exit(1); + } } - } } fn read_file(path: &str) -> String { - std::fs::read_to_string(path).unwrap_or_else(|e| { - eprintln!("Failed to read {path}: {e}"); - process::exit(1); - }) + std::fs::read_to_string(path).unwrap_or_else(|e| { + eprintln!("Failed to read {path}: {e}"); + process::exit(1); + }) } fn extract_version<'a>(content: &'a str, prefix: &str) -> Option<&'a str> { - content.lines().find_map(|line| { - let line = line.trim(); - let rest = line.strip_prefix(prefix)?; - Some(rest.trim().trim_matches('"')) - }) + content.lines().find_map(|line| { + let line = line.trim(); + let rest = line.strip_prefix(prefix)?; + Some(rest.trim().trim_matches('"')) + }) } fn check_versions() { - let cargo = read_file(CARGO_FILE_PATH); - let chart = read_file(HELM_CHART_FILE_PATH); - - let cargo_version = extract_version(&cargo, "version =") - .unwrap_or_else(|| { eprintln!("No version found in {CARGO_FILE_PATH}"); process::exit(1) }); - let chart_version = extract_version(&chart, "version:") - .unwrap_or_else(|| { eprintln!("No version found in {HELM_CHART_FILE_PATH}"); process::exit(1) }); - let app_version = extract_version(&chart, "appVersion:") - .unwrap_or_else(|| { eprintln!("No appVersion found in {HELM_CHART_FILE_PATH}"); process::exit(1) }); - - let ok = cargo_version == chart_version && cargo_version == app_version; - - println!("Cargo.toml: {cargo_version}"); - println!("Chart version: {chart_version}"); - println!("Chart appVersion: {app_version}"); - - if ok { - println!("OK: all versions match"); - } else { - eprintln!("ERROR: versions do not match"); - process::exit(1); - } + let cargo = read_file(CARGO_FILE_PATH); + let chart = read_file(HELM_CHART_FILE_PATH); + + let cargo_version = extract_version(&cargo, "version =").unwrap_or_else(|| { + eprintln!("No version found in {CARGO_FILE_PATH}"); + process::exit(1) + }); + let chart_version = extract_version(&chart, "version:").unwrap_or_else(|| { + eprintln!("No version found in {HELM_CHART_FILE_PATH}"); + process::exit(1) + }); + let app_version = extract_version(&chart, "appVersion:").unwrap_or_else(|| { + eprintln!("No appVersion found in {HELM_CHART_FILE_PATH}"); + process::exit(1) + }); + + let ok = cargo_version == chart_version && cargo_version == app_version; + + println!("Cargo.toml: {cargo_version}"); + println!("Chart version: {chart_version}"); + println!("Chart appVersion: {app_version}"); + + if ok { + println!("OK: all versions match"); + } else { + eprintln!("ERROR: versions do not match"); + process::exit(1); + } } fn release(version: &str) { - // Parse the version strings to ints - let new = parse_semver(version).unwrap_or_else(|| { - eprintln!("Invalid version: {version}. Expected format: x.y.z"); - process::exit(1); - }); - let current_content = read_file(CARGO_FILE_PATH); - let current_str = extract_version(¤t_content, "version = ").unwrap_or_else(|| { - eprintln!("Could not read current version from {CARGO_FILE_PATH}"); - process::exit(1); - }); - let current = parse_semver(current_str).unwrap_or_else(|| { - eprintln!("Could not parse current version: {current_str}"); - process::exit(1); - }); - - // Check that the new version is an upgrade (downgrade not allowed) - if current >= new { - // Equality is lexicographical and works as long as we stick to x.y.z - // If we start doing 1.0.0-alpha releases we should us a parsing lib - eprintln!("New version {version} must be greater than current {current_str}"); - process::exit(1); - } - - // Check that the changelog file is updated with the new version - let changelog = read_file(CHANGELOG_FILE_PATH); - let expected_heading = format!("## [{version}] - "); - if !changelog.lines().any(|line| line.starts_with(&expected_heading)) { - eprintln!("{CHANGELOG_FILE_PATH} does not contain a section for [{version}]"); - eprintln!("Add '## [{version}] - YYYY-MM-DD' before releasing."); - process::exit(1); - } - bump_file(CARGO_FILE_PATH, "version", version); - bump_file(HELM_CHART_FILE_PATH, "version", version); - bump_file(HELM_CHART_FILE_PATH, "appVersion", version); - - run("cargo", &["generate-lockfile"]); - run("git", &["add", CARGO_FILE_PATH, CARGO_LOCK_FILE_PATH, HELM_CHART_FILE_PATH, CHANGELOG_FILE_PATH]); - run("git", &["commit", "--message", &format!("release v{version}")]); - - println!("Done. Open a PR with this commit. CI will tag and release on merge."); + // Parse the version strings to ints + let new = parse_semver(version).unwrap_or_else(|| { + eprintln!("Invalid version: {version}. Expected format: x.y.z"); + process::exit(1); + }); + let current_content = read_file(CARGO_FILE_PATH); + let current_str = extract_version(¤t_content, "version = ").unwrap_or_else(|| { + eprintln!("Could not read current version from {CARGO_FILE_PATH}"); + process::exit(1); + }); + let current = parse_semver(current_str).unwrap_or_else(|| { + eprintln!("Could not parse current version: {current_str}"); + process::exit(1); + }); + + // Check that the new version is an upgrade (downgrade not allowed) + if current >= new { + // Equality is lexicographical and works as long as we stick to x.y.z + // If we start doing 1.0.0-alpha releases we should us a parsing lib + eprintln!("New version {version} must be greater than current {current_str}"); + process::exit(1); + } + + // Check that the changelog file is updated with the new version + let changelog = read_file(CHANGELOG_FILE_PATH); + let expected_heading = format!("## [{version}] - "); + if !changelog + .lines() + .any(|line| line.starts_with(&expected_heading)) + { + eprintln!("{CHANGELOG_FILE_PATH} does not contain a section for [{version}]"); + eprintln!("Add '## [{version}] - YYYY-MM-DD' before releasing."); + process::exit(1); + } + bump_file(CARGO_FILE_PATH, "version", version); + bump_file(HELM_CHART_FILE_PATH, "version", version); + bump_file(HELM_CHART_FILE_PATH, "appVersion", version); + + run("cargo", &["generate-lockfile"]); + run( + "git", + &[ + "add", + CARGO_FILE_PATH, + CARGO_LOCK_FILE_PATH, + HELM_CHART_FILE_PATH, + CHANGELOG_FILE_PATH, + ], + ); + run( + "git", + &["commit", "--message", &format!("release v{version}")], + ); + + println!("Done. Open a PR with this commit. CI will tag and release on merge."); } fn parse_semver(v: &str) -> Option<(u32, u32, u32)> { - let parts: Vec<&str> = v.split('.').collect(); - if parts.len() != 3 { - return None; - } - Some(( - parts[0].parse().ok()?, - parts[1].parse().ok()?, - parts[2].parse().ok()?, - )) + let parts: Vec<&str> = v.split('.').collect(); + if parts.len() != 3 { + return None; + } + Some(( + parts[0].parse().ok()?, + parts[1].parse().ok()?, + parts[2].parse().ok()?, + )) } fn bump_file(path: &str, key: &str, version: &str) { - let content = read_file(path); - let prefix = if path.ends_with(".toml") { - format!("{key} = ") - } else { - format!("{key}: ") - }; - let new_content = content - .lines() - .map(|line| { - if line.trim().starts_with(&prefix) { - let indent = &line[..line.len() - line.trim_start().len()]; - if path.ends_with(".toml") { - format!("{indent}{key} = \"{version}\"") - } else { - format!("{indent}{key}: {version}") - } - } else { - line.to_string() - } - }) - .collect::>() - .join("\n") + "\n"; - - std::fs::write(path, new_content).unwrap_or_else(|e| { - eprintln!("Failed to write {path}: {e}"); - process::exit(1); - }); - println!("Updated {path}"); + let content = read_file(path); + let prefix = if path.ends_with(".toml") { + format!("{key} = ") + } else { + format!("{key}: ") + }; + let new_content = content + .lines() + .map(|line| { + if line.trim().starts_with(&prefix) { + let indent = &line[..line.len() - line.trim_start().len()]; + if path.ends_with(".toml") { + format!("{indent}{key} = \"{version}\"") + } else { + format!("{indent}{key}: {version}") + } + } else { + line.to_string() + } + }) + .collect::>() + .join("\n") + + "\n"; + + std::fs::write(path, new_content).unwrap_or_else(|e| { + eprintln!("Failed to write {path}: {e}"); + process::exit(1); + }); + println!("Updated {path}"); } fn run(cmd: &str, args: &[&str]) { - let status = process::Command::new(cmd) - .args(args) - .status() - .unwrap_or_else(|e| { - eprintln!("Failed to run {cmd}: {e}"); - process::exit(1); - }); - if !status.success() { - eprintln!("Command failed: {cmd} {}", args.join(" ")); - process::exit(1); - } + let status = process::Command::new(cmd) + .args(args) + .status() + .unwrap_or_else(|e| { + eprintln!("Failed to run {cmd}: {e}"); + process::exit(1); + }); + if !status.success() { + eprintln!("Command failed: {cmd} {}", args.join(" ")); + process::exit(1); + } } #[cfg(test)] mod tests { - use super::*; - - #[test] - fn test_parse_semver() { - assert_eq!(parse_semver("1.2.3"), Some((1, 2, 3))); - assert_eq!(parse_semver("0.1.0"), Some((0, 1, 0))); - assert_eq!(parse_semver("1.2"), None); - assert_eq!(parse_semver("1.2.x"), None); - assert_eq!(parse_semver(""), None); - } + use super::*; + + #[test] + fn test_parse_semver() { + assert_eq!(parse_semver("1.2.3"), Some((1, 2, 3))); + assert_eq!(parse_semver("0.1.0"), Some((0, 1, 0))); + assert_eq!(parse_semver("1.2"), None); + assert_eq!(parse_semver("1.2.x"), None); + assert_eq!(parse_semver(""), None); + } } From b60e8626877a6c4173b2436425d6036b15980bd4 Mon Sep 17 00:00:00 2001 From: Jonatan Date: Mon, 23 Feb 2026 14:54:44 +0100 Subject: [PATCH 13/16] print semver type as reminder --- xtask/src/main.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 05a2587..95f3d8f 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -93,6 +93,15 @@ fn release(version: &str) { process::exit(1); } + let bump_type = if new.0 > current.0 { + "major" + } else if new.1 > current.1 { + "minor" + } else { + "patch" + }; + println!("Bumping {current_str} -> {version} ({bump_type})"); + // Check that the changelog file is updated with the new version let changelog = read_file(CHANGELOG_FILE_PATH); let expected_heading = format!("## [{version}] - "); From 6c8e43ed049475ff235464362534aa43a6ba495f Mon Sep 17 00:00:00 2001 From: Jonatan Date: Mon, 23 Feb 2026 17:57:58 +0100 Subject: [PATCH 14/16] nitpicks --- README.md | 2 +- xtask/src/main.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d05b9eb..539f3b5 100644 --- a/README.md +++ b/README.md @@ -208,4 +208,4 @@ For more detail on GCP label requirements, see the [Google Cloud labeling best p 1. Update [CHANGELOG.md](./CHANGELOG.md) following the [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) format and [SemVer](https://semver.org/) conventions. 1. Run `cargo xtask release `. This updates and commits the relevant files to git. 1. Raise PR, merge PR -1. When the PR is merged, a job will run that adds a git tag, and builds and pushes the release Docker image \ No newline at end of file +1. When the PR is merged, a job will run that adds a git tag, and builds and pushes the release Docker image diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 95f3d8f..9f40d98 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -42,7 +42,7 @@ fn check_versions() { let cargo = read_file(CARGO_FILE_PATH); let chart = read_file(HELM_CHART_FILE_PATH); - let cargo_version = extract_version(&cargo, "version =").unwrap_or_else(|| { + let cargo_version = extract_version(&cargo, "version = ").unwrap_or_else(|| { eprintln!("No version found in {CARGO_FILE_PATH}"); process::exit(1) }); From 7c63c7706eac6aafabe82d367d30c7aa5f900ccb Mon Sep 17 00:00:00 2001 From: Jonatan Date: Mon, 23 Feb 2026 18:19:05 +0100 Subject: [PATCH 15/16] move release into ci file this is cleaner to make it depend on ci jobs --- .github/workflows/ci.yml | 87 +++++++++++++++++++++++++++++++++ .github/workflows/release.yml | 91 ----------------------------------- 2 files changed, 87 insertions(+), 91 deletions(-) delete mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 386d515..eacaef5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -183,3 +183,90 @@ jobs: "$test" == "success" && \ "$build" == "success" \ ]] + + detect-release: + runs-on: ubuntu-latest + needs: [summary] + if: | + github.event_name == 'push' && + github.ref == 'refs/heads/main' && + needs.summary.result == 'success' + outputs: + is_release: ${{ steps.detect.outputs.is_release }} + version: ${{ steps.detect.outputs.version }} + steps: + - uses: actions/checkout@v6 + - name: Detect release commit + id: detect + run: | + MSG=$(git log -1 --format=%s) + if [[ "$MSG" =~ ^release\ v([0-9]+\.[0-9]+\.[0-9]+)$ ]]; + then + echo "is_release=true" >> "$GITHUB_OUTPUT" + echo "version=${BASH_REMATCH[1]}" >> "$GITHUB_OUTPUT" + else + echo "is_release=false" >> "$GITHUB_OUTPUT" + fi + + tag-and-release: + runs-on: ubuntu-latest + needs: [detect-release] + if: needs.detect-release.outputs.is_release == 'true' + permissions: + contents: write + steps: + - uses: actions/checkout@v6 + + - name: Create git tag + env: + TAG: v${{ needs.detect-release.outputs.version }} + run: | + if git ls-remote --tags origin "$TAG" | grep -qF "refs/tags/$TAG"; + then + echo "Tag $TAG already exists, skipping." + else + git tag "$TAG" + git push origin "$TAG" + fi + + - name: Setup Nix + uses: cachix/install-nix-action@v31 + with: + github_access_token: ${{ secrets.GITHUB_TOKEN }} + + - name: Restore Nix cache + uses: nix-community/cache-nix-action@v7 + with: + primary-key: nix-${{ runner.os }}-${{ hashFiles('**/Cargo.lock', 'flake.lock') }} + + - name: Build release image + run: nix build .#image + + - name: Push release image + id: push + env: + VERSION: ${{ needs.detect-release.outputs.version }} + run: | + TAG="v${VERSION}" + IMAGE="quay.io/upgrades/k8s-cloud-tagger:${TAG}" + + nix develop -c skopeo login quay.io \ + -u "${{ secrets.QUAY_USERNAME }}" \ + -p "${{ secrets.QUAY_PASSWORD }}" + + nix develop -c skopeo copy docker-archive:result "docker://${IMAGE}" + + echo "image=${IMAGE}" >> "$GITHUB_OUTPUT" + echo "tag=${TAG}" >> "$GITHUB_OUTPUT" + + - name: Summary + run: | + cat >> "$GITHUB_STEP_SUMMARY" << EOF + ## Release ${{ needs.detect-release.outputs.version }} + + | | | + | --- | --- | + | **Image** | \`${{ steps.push.outputs.image }}\` | + | **Tag** | \`${{ steps.push.outputs.tag }}\` | + | **Registry** | [Quay.io](https://quay.io/repository/upgrades/k8s-cloud-tagger?tab=tags) | + EOF \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index fe5a52d..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,91 +0,0 @@ -name: Release - -on: - push: - branches: [main] - -jobs: - check: - runs-on: ubuntu-latest - outputs: - is_release: ${{ steps.detect.outputs.is_release }} - version: ${{ steps.detect.outputs.version }} - steps: - - uses: actions/checkout@v6 - - - name: Detect release commit - id: detect - run: | - MSG=$(git log -1 --format=%s) - if [[ "$MSG" =~ ^release\ v([0-9]+\.[0-9]+\.[0-9]+)$ ]]; - then - echo "is_release=true" >> "$GITHUB_OUTPUT" - echo "version=${BASH_REMATCH[1]}" >> "$GITHUB_OUTPUT" - else - echo "is_release=false" >> "$GITHUB_OUTPUT" - echo "version=" >> "$GITHUB_OUTPUT" - fi - - tag-and-release: - runs-on: ubuntu-latest - needs: check - if: needs.check.outputs.is_release == 'true' - permissions: - contents: write - steps: - - uses: actions/checkout@v6 - - - name: Create git tag - env: - VERSION: ${{ needs.check.outputs.version }} - run: | - TAG="v${VERSION}" - if git ls-remote --tags origin "$TAG" | grep -qF "refs/tags/$TAG"; - then - echo "Tag $TAG already exists, skipping." - else - git tag "$TAG" - git push origin "$TAG" - fi - - - name: Setup Nix - uses: cachix/install-nix-action@v31 - with: - github_access_token: ${{ secrets.GITHUB_TOKEN }} - - - name: Restore Nix cache - uses: nix-community/cache-nix-action@v7 - with: - primary-key: nix-${{ runner.os }}-${{ hashFiles('**/Cargo.lock', 'flake.lock') }} - - - name: Build release image - run: nix build .#image - - - name: Push release image - id: push - env: - VERSION: ${{ needs.check.outputs.version }} - run: | - TAG="v${VERSION}" - IMAGE="quay.io/upgrades/k8s-cloud-tagger:${TAG}" - - nix develop -c skopeo login quay.io \ - -u "${{ secrets.QUAY_USERNAME }}" \ - -p "${{ secrets.QUAY_PASSWORD }}" - - nix develop -c skopeo copy docker-archive:result "docker://${IMAGE}" - - echo "image=${IMAGE}" >> "$GITHUB_OUTPUT" - echo "tag=${TAG}" >> "$GITHUB_OUTPUT" - - - name: Summary - run: | - cat >> "$GITHUB_STEP_SUMMARY" << EOF - ## Release ${{ needs.check.outputs.version }} - - | | | - | --- | --- | - | **Image** | \`${{ steps.push.outputs.image }}\` | - | **Tag** | \`${{ steps.push.outputs.tag }}\` | - | **Registry** | [Quay.io](https://quay.io/repository/upgrades/k8s-cloud-tagger?tab=tags) | - EOF From 17e57b841209df604a3ff272bddb443798773cae Mon Sep 17 00:00:00 2001 From: Jonatan Date: Mon, 23 Feb 2026 18:29:50 +0100 Subject: [PATCH 16/16] revert unintentional version bump --- Cargo.lock | 2 +- Cargo.toml | 2 +- helm/k8s-cloud-tagger/Chart.yaml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d1fbe6c..0f6c2ef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1122,7 +1122,7 @@ dependencies = [ [[package]] name = "k8s-cloud-tagger" -version = "1.0.0" +version = "0.1.0" dependencies = [ "anyhow", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index 5e0a553..05785df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "k8s-cloud-tagger" -version = "1.0.0" +version = "0.1.0" edition = "2024" [dependencies] diff --git a/helm/k8s-cloud-tagger/Chart.yaml b/helm/k8s-cloud-tagger/Chart.yaml index 63b82c6..405961c 100644 --- a/helm/k8s-cloud-tagger/Chart.yaml +++ b/helm/k8s-cloud-tagger/Chart.yaml @@ -4,8 +4,8 @@ description: >- Watch Kubernetes resource labels and automatically propagate them as tags to AWS, GCP, and Azure cloud resources type: application -version: 1.0.0 -appVersion: 1.0.0 +version: 0.1.0 +appVersion: 0.1.0 kubeVersion: ">=1.33.0-0" home: https://github.com/upgrades-dev/k8s-cloud-tagger icon: https://raw.githubusercontent.com/upgrades-dev/k8s-cloud-tagger/main/logo.png