diff options
| author | Jesper Jensen <jesper@jnsn.dev> | 2026-01-25 00:23:46 +0100 |
|---|---|---|
| committer | Jesper Jensen <jesper@jnsn.dev> | 2026-01-25 00:23:46 +0100 |
| commit | 678f8f30d2a4adda9535bbef15004b2ed51f410a (patch) | |
| tree | bdb65f5d266e24bbe6845e3fcc431a6b99c4adcf | |
| parent | 2dc5aed1d0181b88182e93660c2cc9acfd8dd863 (diff) | |
Add some config stuff for k8s
| -rw-r--r-- | Cargo.lock | 7 | ||||
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | src/main.rs | 298 |
3 files changed, 284 insertions, 22 deletions
@@ -155,6 +155,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] +name = "json-event-parser" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73267b6bffa5356bd46cfa89386673e9a7f62f4eb3adcb45b1bd031892357853" + +[[package]] name = "libc" version = "0.2.178" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -421,6 +427,7 @@ name = "version-bumper" version = "0.1.0" dependencies = [ "base64", + "json-event-parser", "rand", "regex", "tinyjson", @@ -5,6 +5,7 @@ edition = "2024" [dependencies] base64 = "0.22.1" +json-event-parser = "0.2.2" rand = "0.9.2" regex = "1.12.2" tinyjson = "2.5.1" diff --git a/src/main.rs b/src/main.rs index 8933c96..7cdcbaa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,7 +18,9 @@ fn help(cmd: &str) { Search FILE for docker images and suggest updates Options: ---auth <REGISTRY> <USER> <PASS> Authenticate against REGISTRY (repeatable)", +--auth <REGISTRY> <USER> <PASS> Authenticate against REGISTRY (repeatable) +--config <PATH> Read config from PATH +--scratch <DIR> Store temporary files in DIR", cmd ); } @@ -50,10 +52,13 @@ impl AuthMethod { } } +fn basic_auth(user: &str, pass: &str) -> String { + return BASE64_STANDARD.encode(format!("{}:{}", user, pass)); +} + struct AuthInfo { host: String, - username: String, - password: String, + auth: String, } enum AuthStage { @@ -106,13 +111,13 @@ impl AuthState{ let auth_header = response.headers().get("www-authenticate").unwrap(); match AuthMethod::from_header(auth_header) { Some(AuthMethod::Basic) => { - let basic_auth = format!("Basic {}", BASE64_STANDARD.encode(format!("{}:{}", self.info.username, self.info.password))); + let basic_auth = format!("Basic {}", self.info.auth); self.stage = AuthStage::Authorized(basic_auth); return Ok(()); }, Some(AuthMethod::Bearer{realm, scope, service}) => { - let basic_auth = format!("Basic {}", BASE64_STANDARD.encode(format!("{}:{}", self.info.username, self.info.password))); + let basic_auth = format!("Basic {}", self.info.auth); let url = format!("{}?service={}&scope={}", realm, service, scope); @@ -170,6 +175,12 @@ fn perform_registry_request(registry: &str, url: &str, accept: &'static str, aut return Err("Authorization failed".to_string()); } +struct Repository { + url: String, + key: Option<std::path::PathBuf>, + dest: std::path::PathBuf, +} + enum YContext { InDocument, InObject, @@ -511,6 +522,8 @@ fn main() { let mut auths = vec![]; let mut positional: Vec<&str> = vec!(); let mut overwrite = false; + let mut config_path = None; + let mut scratch_path = None; loop { match it.next().map(|x| x.as_str()) { @@ -519,8 +532,7 @@ fn main() { if let Some(registry) = it.next() && let Some(username) = it.next() && let Some(password) = it.next() { auths.push(AuthInfo { host: registry.clone(), - username: username.clone(), - password: password.clone(), + auth: basic_auth(username, password), }); } else { println!("Error: --auth requires three parameters"); @@ -528,6 +540,24 @@ fn main() { std::process::exit(1); } }, + Some("--config") => { + if let Some(path) = it.next() { + config_path = Some(std::path::PathBuf::from(path)); + } else { + println!("Error: --config requires an parameters"); + help(cmd); + std::process::exit(1); + } + }, + Some("--scratch") => { + if let Some(path) = it.next() { + scratch_path = Some(std::path::PathBuf::from(path)); + } else { + println!("Error: --scratch requires an parameters"); + help(cmd); + std::process::exit(1); + } + }, Some("-i") | Some("--inplace") => { overwrite = true; }, @@ -539,16 +569,20 @@ fn main() { }; } - - if positional.len() != 1 { - panic!("Bad arguments"); + let mut infile_paths = vec![]; + if positional.len() == 1 { + infile_paths.push(std::path::PathBuf::from(positional[0])); } - let infile_path = std::path::PathBuf::from(positional[0]); - let mut auth = Auth::new(auths); - let inpaths = if infile_path.is_dir() { - let mut unsearched = vec![infile_path.clone()]; - let mut paths = vec![]; + let mut repos = vec![]; + + let workdir = std::env::current_dir().unwrap(); + if let Some(config_path) = config_path { + if !config_path.is_dir() { + std::process::exit(1); + } + + let mut unsearched = vec![config_path]; while let Some(next) = unsearched.pop() { for child in next.read_dir().unwrap() { @@ -561,18 +595,201 @@ fn main() { continue; } - if let Some(ext) = path.extension() { - if ext == "yaml" { - paths.push(path); + if let Some(name) = path.file_name() { + match name.to_str() { + Some(".dockerconfigjson") => { + let file = std::fs::File::open(&path).unwrap(); + let mut reader = json_event_parser::ReaderJsonParser::new(file); + + match reader.parse_next() { + Ok(json_event_parser::JsonEvent::StartObject) => {}, + _ => panic!("Invalid config json"), + } + + match reader.parse_next() { + Ok(json_event_parser::JsonEvent::ObjectKey(k)) if &k == "auths" => {}, + _ => panic!("Invalid config json"), + } + + match reader.parse_next() { + Ok(json_event_parser::JsonEvent::StartObject) => {}, + _ => panic!("Invalid config json"), + } + + let mut possible_key = reader.parse_next(); + while let Ok(json_event_parser::JsonEvent::ObjectKey(k)) = possible_key { + let key = k.into_owned(); + + match reader.parse_next() { + Ok(json_event_parser::JsonEvent::StartObject) => {}, + _ => panic!("Invalid config json"), + } + + match reader.parse_next() { + Ok(json_event_parser::JsonEvent::ObjectKey(k)) if &k == "auth" => {}, + _ => panic!("Invalid config json"), + } + + match reader.parse_next() { + Ok(json_event_parser::JsonEvent::String(v)) => { + auths.push(AuthInfo { + host: key, + auth: v.into_owned(), + }); + }, + _ => panic!("Invalid config json"), + } + + match reader.parse_next() { + Ok(json_event_parser::JsonEvent::EndObject) => {}, + _ => panic!("Invalid config json"), + } + + possible_key = reader.parse_next(); + } + + match possible_key { + Ok(json_event_parser::JsonEvent::EndObject) => {}, + _ => panic!("Invalid config json"), + } + + match reader.parse_next() { + Ok(json_event_parser::JsonEvent::EndObject) => {}, + _ => panic!("Invalid config json"), + } + } + Some("repository") => { + let file = std::fs::File::open(&path).unwrap(); + let mut reader = json_event_parser::ReaderJsonParser::new(file); + + match reader.parse_next() { + Ok(json_event_parser::JsonEvent::StartObject) => {}, + _ => panic!("Invalid config json"), + } + + match reader.parse_next() { + Ok(json_event_parser::JsonEvent::ObjectKey(k)) if &k == "repositories" => {}, + _ => panic!("Invalid config json"), + } + + match reader.parse_next() { + Ok(json_event_parser::JsonEvent::StartObject) => {}, + _ => panic!("Invalid config json"), + } + + let mut possible_key = reader.parse_next(); + while let Ok(json_event_parser::JsonEvent::ObjectKey(k)) = possible_key { + let k = k.into_owned(); + match reader.parse_next() { + Ok(json_event_parser::JsonEvent::StartObject) => {}, + _ => panic!("Invalid config json"), + } + + match reader.parse_next() { + Ok(json_event_parser::JsonEvent::ObjectKey(k)) if &k == "key" => {}, + _ => panic!("Invalid config json"), + } + + let key_name = match reader.parse_next() { + Ok(json_event_parser::JsonEvent::String(v)) => { + v.into_owned() + }, + _ => panic!("Invalid config json"), + }; + + match reader.parse_next() { + Ok(json_event_parser::JsonEvent::ObjectKey(k)) if &k == "dest" => {}, + _ => panic!("Invalid config json"), + } + + let dest = match reader.parse_next() { + Ok(json_event_parser::JsonEvent::String(v)) => { + v.into_owned() + }, + _ => panic!("Invalid config json"), + }; + + match reader.parse_next() { + Ok(json_event_parser::JsonEvent::EndObject) => {}, + _ => panic!("Invalid config json"), + } + + repos.push(Repository{ + url: k, + key: Some(workdir.join(path.parent().unwrap().join(std::path::PathBuf::from(key_name)))), + dest: scratch_path.as_ref().unwrap().join(dest), + }); + + possible_key = reader.parse_next(); + } + + match possible_key { + Ok(json_event_parser::JsonEvent::EndObject) => {}, + _ => panic!("Invalid config json"), + } + + match reader.parse_next() { + Ok(json_event_parser::JsonEvent::EndObject) => {}, + _ => panic!("Invalid config json"), + } + } + Some(_) | None => {} } } } } + } + + for repo in &repos { + let mut gitcmd = std::process::Command::new("git"); + + gitcmd + .arg("clone") + .arg(&repo.url) + .arg(&repo.dest); + + if let Some(key) = &repo.key { + gitcmd.env("GIT_SSH_COMMAND", format!("ssh -F none -o IdentitiesOnly=yes -i \"{}\"", key.to_str().unwrap())); + } + + let exit = gitcmd.status() + .expect("Git command failed"); + if !exit.success() { + panic!("Git exited with failure"); + } + + infile_paths.push(repo.dest.clone()); + } + + let mut inpaths = vec!(); + for infile_path in infile_paths { + if infile_path.is_dir() { + let mut unsearched = vec![infile_path.clone()]; + + while let Some(next) = unsearched.pop() { + for child in next.read_dir().unwrap() { + let child = child.unwrap(); + let path = child.path(); + + let ft = child.file_type().unwrap(); + if ft.is_dir() { + unsearched.push(path); + continue; + } - paths - } else { - vec![infile_path] - }; + if let Some(ext) = path.extension() { + if ext == "yaml" { + inpaths.push(path); + } + } + } + } + } else { + inpaths.push(infile_path); + }; + } + + let mut auth = Auth::new(auths); for infile_path in inpaths { let mut file = std::fs::File::open(&infile_path).unwrap(); @@ -628,4 +845,41 @@ fn main() { out.commit(); } + + for repo in &repos { + let mut gitcmd = std::process::Command::new("git"); + + gitcmd + .arg("-C") + .arg(&repo.dest) + .arg("commit") + .arg("--all") + .arg("--message") + .arg("Update versions"); + + let exit = gitcmd.status() + .expect("Git command failed"); + if !exit.success() { + println!("Commit failed, presumably there were no changes"); + continue; + } + + let mut gitcmd = std::process::Command::new("git"); + gitcmd + .arg("-C") + .arg(&repo.dest) + .arg("push") + .arg("origin") + .arg("+HEAD:version-bump"); + + if let Some(key) = &repo.key { + gitcmd.env("GIT_SSH_COMMAND", format!("ssh -F none -o IdentitiesOnly=yes -i \"{}\"", key.to_str().unwrap())); + } + + let exit = gitcmd.status() + .expect("Git command failed"); + if !exit.success() { + panic!("Git exited with failure"); + } + } } |
