diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index e25ed3bf..98af5769 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -16,7 +16,7 @@ jobs: - name: Build run: cargo build --verbose - name: Run tests - run: cargo test --verbose + run: cargo test -- --test-threads=1 - name: Clippy run: cargo clippy --all -- -D warnings - name: Check formatting diff --git a/.gitignore b/.gitignore index 1e8c0852..a99258d0 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ database settings.env settings.toml + +.metals diff --git a/command/Cargo.lock b/command/Cargo.lock index 8db4a4b3..bd76470f 100644 --- a/command/Cargo.lock +++ b/command/Cargo.lock @@ -34,6 +34,7 @@ dependencies = [ "cliclack", "local-ip-address", "serde", + "serde_json", "toml", ] @@ -106,6 +107,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + [[package]] name = "lazy_static" version = "1.4.0" @@ -203,24 +210,41 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "ryu" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" + [[package]] name = "serde" -version = "1.0.196" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.196" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.50", +] + +[[package]] +name = "serde_json" +version = "1.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +dependencies = [ + "itoa", + "ryu", + "serde", ] [[package]] @@ -251,9 +275,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.49" +version = "2.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915aea9e586f80826ee59f8453c1101f9d1c4b3964cd2460185ee8e299ada496" +checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb" dependencies = [ "proc-macro2", "quote", @@ -288,7 +312,7 @@ checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.50", ] [[package]] @@ -358,7 +382,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.3", ] [[package]] @@ -378,17 +402,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "d380ba1dc7187569a8a9e91ed34b8ccfc33123bbacb8c0aed2d1ad7f3ef2dc5f" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm 0.52.3", + "windows_aarch64_msvc 0.52.3", + "windows_i686_gnu 0.52.3", + "windows_i686_msvc 0.52.3", + "windows_x86_64_gnu 0.52.3", + "windows_x86_64_gnullvm 0.52.3", + "windows_x86_64_msvc 0.52.3", ] [[package]] @@ -399,9 +423,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "68e5dcfb9413f53afd9c8f86e56a7b4d86d9a2fa26090ea2dc9e40fba56c6ec6" [[package]] name = "windows_aarch64_msvc" @@ -411,9 +435,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "8dab469ebbc45798319e69eebf92308e541ce46760b49b18c6b3fe5e8965b30f" [[package]] name = "windows_i686_gnu" @@ -423,9 +447,9 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "2a4e9b6a7cac734a8b4138a4e1044eac3404d8326b6c0f939276560687a033fb" [[package]] name = "windows_i686_msvc" @@ -435,9 +459,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "28b0ec9c422ca95ff34a78755cfa6ad4a51371da2a5ace67500cf7ca5f232c58" [[package]] name = "windows_x86_64_gnu" @@ -447,9 +471,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "704131571ba93e89d7cd43482277d6632589b18ecf4468f591fbae0a8b101614" [[package]] name = "windows_x86_64_gnullvm" @@ -459,9 +483,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "42079295511643151e98d61c38c0acc444e52dd42ab456f7ccfd5152e8ecf21c" [[package]] name = "windows_x86_64_msvc" @@ -471,15 +495,15 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6" [[package]] name = "winnow" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d90f4e0f530c4c69f62b80d839e9ef3855edc9cba471a160c4d692deed62b401" +checksum = "7a4191c47f15cc3ec71fcb4913cb83d58def65dd3787610213c649283b5ce178" dependencies = [ "memchr", ] @@ -501,5 +525,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.50", ] diff --git a/command/Cargo.toml b/command/Cargo.toml index 6a0d02b4..2eb2f136 100644 --- a/command/Cargo.toml +++ b/command/Cargo.toml @@ -8,5 +8,6 @@ edition = "2021" [dependencies] cliclack = "0.1.6" local-ip-address = "0.5.6" -serde = { version = "1.0.193", features = ["derive"] } +serde = { version = "1", features = ["derive"] } +serde_json = "1" toml = "0.8.8" diff --git a/command/src/main.rs b/command/src/main.rs index b8ec3cb0..de9d089f 100644 --- a/command/src/main.rs +++ b/command/src/main.rs @@ -2,11 +2,12 @@ use cliclack::{ confirm, input, intro, - log::{self, info}, + log::{self, info, step}, multiselect, note, outro, select, spinner, }; use local_ip_address::local_ip; use serde::{Deserialize, Serialize}; +use std::process::Command; use std::{ format, io::Error, @@ -137,21 +138,49 @@ impl Repository { struct Gitpod { domain: String, url: String, + workspace_context: GitpodWorkspaceContext, +} + +#[derive(Debug, Default, Deserialize, PartialEq)] +struct GitpodWorkspaceContext { + envvars: Option>, +} + +#[derive(Debug, Deserialize, PartialEq)] +struct GitpodEnvVar { + name: String, + value: String, } impl Gitpod { fn load() -> Self { - let workspace_url = std::env::var("GITPOD_WORKSPACE_URL").expect("Not running in Gitpod"); + let workspace_url = + std::env::var("GITPOD_WORKSPACE_URL").expect("Missing env GITPOD_WORKSPACE_URL"); + + let workspace_context: GitpodWorkspaceContext = serde_json::from_str( + &std::env::var("GITPOD_WORKSPACE_CONTEXT") + .expect("Missing env GITPOD_WORKSPACE_CONTEXT"), + ) + .expect("Failed to parse GITPOD_WORKSPACE_CONTEXT"); Self { domain: workspace_url.replace("https://", "8080-"), url: workspace_url.replace("https://", "https://8080-"), + workspace_context, } } fn is_host() -> bool { std::env::var("GITPOD_WORKSPACE_URL").is_ok() } + + fn get_context_for(&self, name: &str) -> Option<&str> { + self.workspace_context + .envvars + .as_ref() + .and_then(|envvars| envvars.iter().find(|envvar| envvar.name == name)) + .map(|envvar| envvar.value.as_str()) + } } #[derive(Default, Clone, Eq, PartialEq, Debug)] @@ -257,11 +286,11 @@ fn setup(mut config: Config) -> std::io::Result<()> { progress.start(&format!("Cloning {}...", repo.full_name())); if repo.clone_path().read_dir()?.next().is_some() { - progress.stop(format!("Already cloned {} ✓", repo.full_name())); + progress.stop(format!("✓ Already cloned {}", repo.full_name())); continue; } - let mut cmd = std::process::Command::new("git"); + let mut cmd = Command::new("git"); cmd.arg("clone") .arg("--origin") .arg("upstream") @@ -271,15 +300,18 @@ fn setup(mut config: Config) -> std::io::Result<()> { .arg(repo.url()) .arg(repo.clone_path()); - let output = cmd.output().unwrap(); + let output = cmd.output()?; assert!( output.status.success(), - "Failed to clone repo: {} - {:?}", - repo.full_name(), - output + "Failed to clone repo: {} - {output:?}", + repo.full_name() ); - progress.stop(format!("Cloned {} ✓", repo.full_name())); + progress.stop(format!("✓ Cloned {}", repo.full_name())); + } + + if Gitpod::is_host() { + gitpod_checkout_pr()?; } outro("Starting services...") @@ -314,6 +346,49 @@ fn create_placeholder_dirs() { }); } +fn gitpod_checkout_pr() -> std::io::Result<()> { + let gitpod = Gitpod::load(); + + let Some(pr_no) = gitpod.get_context_for("LILA_PR") else { + return step("No lila PR specified, using default branch"); + }; + + let pr_url = format!("https://github.com/lichess-org/lila/pull/{pr_no}"); + let branch_name = format!("pr-{pr_no}"); + + let mut progress = spinner(); + progress.start(&format!("Checking out lila PR #{pr_no}: {pr_url}...")); + + let mut cmd = Command::new("git"); + cmd.current_dir("repos/lila") + .arg("fetch") + .arg("upstream") + .arg(format!("pull/{pr_no}/head:{branch_name}")) + .arg("--depth") + .arg("25") + .arg("--recurse-submodules"); + + let output = cmd.output()?; + assert!( + output.status.success(), + "Failed to fetch upstream PR #{pr_no} - {output:?}", + ); + + let mut cmd = Command::new("git"); + cmd.current_dir("repos/lila") + .arg("checkout") + .arg(&branch_name); + + let output = cmd.output()?; + assert!( + output.status.success(), + "Failed to checkout PR branch {branch_name} - {output:?}", + ); + + progress.stop(format!("✓ Checked out PR #{pr_no} - {pr_url}")); + Ok(()) +} + #[allow(clippy::too_many_lines)] fn prompt_for_optional_services() -> Result>, Error> { multiselect( @@ -492,13 +567,10 @@ fn mobile_setup(mut config: Config) -> std::io::Result<()> { intro("On your Android phone, open Developer Options > Wireless Debugging")?; let phone_ip = match config.phone_ip { - Some(ip) => input("Your phone's private IP address") - .default_input(&ip) - .interact()?, - None => input("Your phone's private IP address") - .placeholder("192.168.x.x or 10.x.x.x") - .interact()?, - }; + Some(ip) => input("Your phone's private IP address").default_input(&ip), + None => input("Your phone's private IP address").placeholder("192.168.x.x or 10.x.x.x"), + } + .interact()?; let connection_port: u16 = input("Connection port") .validate(|input: &String| validate_string_length(input, 5)) @@ -523,9 +595,10 @@ fn mobile_setup(mut config: Config) -> std::io::Result<()> { } fn validate_string_length(input: &str, length: usize) -> Result<(), String> { - match input.len() { - len if len == length => Ok(()), - _ => Err(format!("Value should be {length} digits in length")), + if input.len() == length { + Ok(()) + } else { + Err(format!("Value should be {length} digits in length")) } } @@ -610,6 +683,7 @@ mod tests { "GITPOD_WORKSPACE_URL", "https://lichessorg-liladocker-abc123.ws-us123.gitpod.io", ); + std::env::set_var("GITPOD_WORKSPACE_CONTEXT", "{}"); let gitpod = Gitpod::load(); assert_eq!( @@ -620,5 +694,60 @@ mod tests { gitpod.url, "https://8080-lichessorg-liladocker-abc123.ws-us123.gitpod.io" ); + assert_eq!(gitpod.workspace_context, GitpodWorkspaceContext::default()); + assert_eq!(gitpod.get_context_for("LILA_PR"), None); + } + + #[test] + fn test_gitpod_lila_url_with_pr_context() { + std::env::set_var( + "GITPOD_WORKSPACE_URL", + "https://lichessorg-liladocker-abc123.ws-us123.gitpod.io", + ); + std::env::set_var( + "GITPOD_WORKSPACE_CONTEXT", + r#"{"envvars":[{"name":"LILA_PR","value":"12345"}]}"#, + ); + + let gitpod = Gitpod::load(); + + assert_eq!( + gitpod.workspace_context, + GitpodWorkspaceContext { + envvars: Some(vec![GitpodEnvVar { + name: "LILA_PR".to_string(), + value: "12345".to_string(), + }]) + } + ); + + assert_eq!(gitpod.get_context_for("LILA_PR"), Some("12345")); + } + + #[test] + fn test_gitpod_lila_url_with_context_but_no_pr() { + std::env::set_var( + "GITPOD_WORKSPACE_URL", + "https://lichessorg-liladocker-abc123.ws-us123.gitpod.io", + ); + std::env::set_var( + "GITPOD_WORKSPACE_CONTEXT", + r#"{"envvars":[{"name":"FOO","value":"BAR"}]}"#, + ); + + let gitpod = Gitpod::load(); + + assert_eq!( + gitpod.workspace_context, + GitpodWorkspaceContext { + envvars: Some(vec![GitpodEnvVar { + name: "FOO".to_string(), + value: "BAR".to_string(), + }]) + } + ); + + assert_eq!(gitpod.get_context_for("FOO"), Some("BAR")); + assert_eq!(gitpod.get_context_for("LILA_PR"), None); } } diff --git a/docs/gitpod-testing.md b/docs/gitpod-testing.md new file mode 100644 index 00000000..cb0506fb --- /dev/null +++ b/docs/gitpod-testing.md @@ -0,0 +1,17 @@ +## Testing the auto PR checkout locally + +```bash +cd command + +## test with no PR specified +GITPOD_WORKSPACE_URL=https://example.com \ +GITPOD_WORKSPACE_CONTEXT={} \ +cargo run -- setup + +## test with PR specified +GITPOD_WORKSPACE_URL=https://example.com \ +GITPOD_WORKSPACE_CONTEXT='{"envvars":[{"name":"LILA_PR","value":"14738"}]}' \ +cargo run -- setup +``` + +Verify with the `repos` folder in `./command/` (not the other main `repos` folder at the root level)