From e2ba4ea73cff4766156dd739a2db40dc716664c4 Mon Sep 17 00:00:00 2001 From: Jheison Martinez Bolivar Date: Tue, 27 Oct 2026 12:20:14 -0500 Subject: [PATCH 1/3] fix(deps): replace the deprecated serde_yaml with serde-saphyr so cargo deny passes --- Cargo.lock | 127 ++++++++++++++++++++++++++++++++++++++++++-------- Cargo.toml | 2 +- src/checks.rs | 4 +- 3 files changed, 110 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9108705..bfbe496 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,6 +18,17 @@ dependencies = [ "generic-array", ] +[[package]] +name = "annotate-snippets" +version = "0.12.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a5b0a5ab8561c603f16c3b78c24a386b1f70e7fc1b1b1e8c15738828e4a176" +dependencies = [ + "anstyle", + "memchr", + "unicode-width", +] + [[package]] name = "anstream" version = "1.0.0" @@ -74,12 +85,24 @@ version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" +[[package]] +name = "arraydeque" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236" + [[package]] name = "atomic-waker" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + [[package]] name = "base64" version = "0.22.1" @@ -221,6 +244,12 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "core_detect" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f8f80099a98041a3d1622845c271458a2d73e688351bf3cb999266764b81d48" + [[package]] name = "cpufeatures" version = "0.2.17" @@ -421,6 +450,29 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" +[[package]] +name = "encoding_rs" +version = "0.8.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e985e0451871ad22fb8d2b6b076e2028a502a0d3950998c2c5c0a4f9b5d9679" +dependencies = [ + "cfg-if", + "core_detect", + "multiversion_no_op", + "rustversion", + "scopeguard", + "simdutf8", +] + +[[package]] +name = "encoding_rs_io" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba3fe847045ecff794b9c138293a80db914678c453ad63fbf0c6a9eb6e00b22" +dependencies = [ + "encoding_rs", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -595,8 +647,8 @@ dependencies = [ "inquire", "reqwest", "serde", + "serde-saphyr", "serde_json", - "serde_yaml", "sha2", "tar", "tempfile", @@ -606,6 +658,16 @@ dependencies = [ "whoami", ] +[[package]] +name = "granit-parser" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e20f99e46474f56bd905c56e817ebddcf377a611f94c53ac4649e4d3fa3c0cd0" +dependencies = [ + "arraydeque", + "smallvec", +] + [[package]] name = "hashbrown" version = "0.17.1" @@ -966,6 +1028,27 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "multiversion_no_op" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743fb55ba31b18fb1ecef6bdc9aa2743314978ac084044301a7eee33fb99a20d" + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "once_cell" version = "1.21.4" @@ -1364,6 +1447,23 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-saphyr" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8050abb251097357e24aff63ba2c52a6309ecb7d23a5474023df960a02694d8" +dependencies = [ + "annotate-snippets", + "base64", + "encoding_rs_io", + "granit-parser", + "nohash-hasher", + "num-traits", + "serde_core", + "smallvec", + "zmij", +] + [[package]] name = "serde_core" version = "1.0.228" @@ -1418,19 +1518,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_yaml" -version = "0.9.34+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" -dependencies = [ - "indexmap", - "itoa", - "ryu", - "serde", - "unsafe-libyaml", -] - [[package]] name = "sha2" version = "0.10.9" @@ -1485,6 +1572,12 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + [[package]] name = "slab" version = "0.4.12" @@ -1803,12 +1896,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "unsafe-libyaml" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" - [[package]] name = "untrusted" version = "0.9.0" diff --git a/Cargo.toml b/Cargo.toml index 924388b..3c49788 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ crypto_box = "0.9" blake2 = "0.10" crypto_secretbox = "0.1" whoami = "1" -serde_yaml = "0.9.34" +serde-saphyr = "1.3" sha2 = "0.10" [dev-dependencies] diff --git a/src/checks.rs b/src/checks.rs index bce71e5..72184e0 100644 --- a/src/checks.rs +++ b/src/checks.rs @@ -39,7 +39,7 @@ pub fn derive_required_contexts(files: &[WorkflowSource]) -> Vec { .iter() .filter(|f| is_workflow_path(f.path)) .filter_map(|f| { - serde_yaml::from_str::(f.content) + serde_saphyr::from_str::(f.content) .ok() .map(|w| (f.path, w)) }) @@ -78,7 +78,7 @@ pub fn derive_required_contexts(files: &[WorkflowSource]) -> Vec { /// workflows. Used to fetch just those extra files when the caller only has /// one workflow's content in hand (e.g. reading a live repo). pub fn referenced_local_workflows(content: &str) -> Vec { - let Ok(workflow) = serde_yaml::from_str::(content) else { + let Ok(workflow) = serde_saphyr::from_str::(content) else { return Vec::new(); }; workflow From b81ca42bc07df77194969182972df9d6fe6847bd Mon Sep 17 00:00:00 2001 From: Jheison Martinez Bolivar Date: Thu, 12 Nov 2026 15:41:45 -0500 Subject: [PATCH 2/3] fix(wizard): finish setting up a private repo when the plan has no branch protection --- src/wizard/execution.rs | 52 +++++++++++++++++++---------------- src/wizard/execution/tests.rs | 16 +++++++++++ 2 files changed, 45 insertions(+), 23 deletions(-) diff --git a/src/wizard/execution.rs b/src/wizard/execution.rs index 47b49c1..c603c97 100644 --- a/src/wizard/execution.rs +++ b/src/wizard/execution.rs @@ -235,34 +235,40 @@ fn apply_wizard_protections_step( .collect(); let required_contexts = crate::checks::derive_required_contexts(&workflow_sources); - progress.run( - &format!("apply branch protection ({})", c.default_branch), - || { - branches::apply_branch_protection( - client, - &c.owner, - &c.name, - &c.default_branch, - &required_contexts, - )?; - Ok(()) - }, - )?; + protect_branch(progress, client, c, &c.default_branch, &required_contexts)?; if c.create_develop { - progress.run("apply branch protection (develop)", || { - branches::apply_branch_protection( - client, - &c.owner, - &c.name, - "develop", - &required_contexts, - )?; - Ok(()) - })?; + protect_branch(progress, client, c, "develop", &required_contexts)?; } Ok(()) } +/// Protect one branch. A 403 means the account's plan has no branch protection +/// for this repo (a private repo on a free plan): the wizard says so and goes on +/// to labels, topics, teams and secrets instead of leaving the repo half set up. +/// Any other error still stops the wizard. +fn protect_branch( + progress: &mut StepCounter, + client: &GithubClient, + c: &WizardConfig, + branch: &str, + required_contexts: &[String], +) -> Result<()> { + let result = progress.run(&format!("apply branch protection ({branch})"), || { + branches::apply_branch_protection(client, &c.owner, &c.name, branch, required_contexts) + }); + match result { + Err(e) if format!("{e:#}").contains("403") => { + println!("skipped"); + println!(" ⚠ GitHub refused branch protection (403): a private repo on a free plan has none."); + println!( + " Protect {branch} by hand if the plan changes, or make the repo public." + ); + Ok(()) + } + other => other, + } +} + /// Step 6 — labels. fn sync_wizard_labels_step( progress: &mut StepCounter, diff --git a/src/wizard/execution/tests.rs b/src/wizard/execution/tests.rs index 88015e5..d31cdcf 100644 --- a/src/wizard/execution/tests.rs +++ b/src/wizard/execution/tests.rs @@ -358,6 +358,22 @@ fn apply_wizard_protections_step_puts_main_and_develop() { )); } +#[test] +fn apply_wizard_protections_step_skips_a_plan_refusal() { + let (url, log) = start_recording_mock_server(protections_handler(403)); + let client = mock_client(&url); + let mut config = minimal_config(); + config.create_develop = true; + let files = workflow_files(); + let mut progress = StepCounter::new(4, false); + apply_wizard_protections_step(&mut progress, &client, &config, &files).unwrap(); + assert!(has_call( + &log, + "PUT", + "/repos/owner/repo/branches/develop/protection" + )); +} + #[test] fn apply_wizard_protections_step_propagates_error() { let (url, log) = start_recording_mock_server(protections_handler(500)); From ec38cad8e0a7e9c51da713ad8b24dac0d6bb9c64 Mon Sep 17 00:00:00 2001 From: Jheison Martinez Bolivar Date: Thu, 12 Nov 2026 20:33:20 -0500 Subject: [PATCH 3/3] chore(release): bump ghscaff to 0.7.2 --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bfbe496..b89b61a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -634,7 +634,7 @@ dependencies = [ [[package]] name = "ghscaff" -version = "0.7.1" +version = "0.7.2" dependencies = [ "anyhow", "base64", diff --git a/Cargo.toml b/Cargo.toml index 3c49788..3f47122 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ghscaff" -version = "0.7.1" +version = "0.7.2" edition = "2021" description = "Interactive CLI wizard for creating and configuring GitHub repositories" license = "MIT"