this repo has no description

Add flake.nix

+332 -84
+1
.gitignore
··· 1 1 /target 2 + .jj/
+121
Cargo.lock
··· 33 33 ] 34 34 35 35 [[package]] 36 + name = "anstream" 37 + version = "0.6.19" 38 + source = "registry+https://github.com/rust-lang/crates.io-index" 39 + checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" 40 + dependencies = [ 41 + "anstyle", 42 + "anstyle-parse", 43 + "anstyle-query", 44 + "anstyle-wincon", 45 + "colorchoice", 46 + "is_terminal_polyfill", 47 + "utf8parse", 48 + ] 49 + 50 + [[package]] 51 + name = "anstyle" 52 + version = "1.0.11" 53 + source = "registry+https://github.com/rust-lang/crates.io-index" 54 + checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" 55 + 56 + [[package]] 57 + name = "anstyle-parse" 58 + version = "0.2.7" 59 + source = "registry+https://github.com/rust-lang/crates.io-index" 60 + checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" 61 + dependencies = [ 62 + "utf8parse", 63 + ] 64 + 65 + [[package]] 66 + name = "anstyle-query" 67 + version = "1.1.3" 68 + source = "registry+https://github.com/rust-lang/crates.io-index" 69 + checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" 70 + dependencies = [ 71 + "windows-sys 0.59.0", 72 + ] 73 + 74 + [[package]] 75 + name = "anstyle-wincon" 76 + version = "3.0.9" 77 + source = "registry+https://github.com/rust-lang/crates.io-index" 78 + checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" 79 + dependencies = [ 80 + "anstyle", 81 + "once_cell_polyfill", 82 + "windows-sys 0.59.0", 83 + ] 84 + 85 + [[package]] 36 86 name = "arc-swap" 37 87 version = "1.7.1" 38 88 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 125 175 ] 126 176 127 177 [[package]] 178 + name = "clap" 179 + version = "4.5.41" 180 + source = "registry+https://github.com/rust-lang/crates.io-index" 181 + checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9" 182 + dependencies = [ 183 + "clap_builder", 184 + "clap_derive", 185 + ] 186 + 187 + [[package]] 188 + name = "clap_builder" 189 + version = "4.5.41" 190 + source = "registry+https://github.com/rust-lang/crates.io-index" 191 + checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d" 192 + dependencies = [ 193 + "anstream", 194 + "anstyle", 195 + "clap_lex", 196 + "strsim", 197 + ] 198 + 199 + [[package]] 200 + name = "clap_derive" 201 + version = "4.5.41" 202 + source = "registry+https://github.com/rust-lang/crates.io-index" 203 + checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" 204 + dependencies = [ 205 + "heck", 206 + "proc-macro2", 207 + "quote", 208 + "syn", 209 + ] 210 + 211 + [[package]] 212 + name = "clap_lex" 213 + version = "0.7.5" 214 + source = "registry+https://github.com/rust-lang/crates.io-index" 215 + checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" 216 + 217 + [[package]] 128 218 name = "color-eyre" 129 219 version = "0.6.5" 130 220 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 152 242 ] 153 243 154 244 [[package]] 245 + name = "colorchoice" 246 + version = "1.0.4" 247 + source = "registry+https://github.com/rust-lang/crates.io-index" 248 + checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" 249 + 250 + [[package]] 155 251 name = "console" 156 252 version = "0.15.11" 157 253 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 662 758 ] 663 759 664 760 [[package]] 761 + name = "is_terminal_polyfill" 762 + version = "1.70.1" 763 + source = "registry+https://github.com/rust-lang/crates.io-index" 764 + checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 765 + 766 + [[package]] 665 767 name = "itoa" 666 768 version = "1.0.15" 667 769 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 671 773 name = "jj-sync-prs" 672 774 version = "0.1.0" 673 775 dependencies = [ 776 + "clap", 674 777 "color-eyre", 675 778 "dialoguer", 676 779 "futures", ··· 862 965 checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 863 966 864 967 [[package]] 968 + name = "once_cell_polyfill" 969 + version = "1.70.1" 970 + source = "registry+https://github.com/rust-lang/crates.io-index" 971 + checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" 972 + 973 + [[package]] 865 974 name = "openssl-probe" 866 975 version = "0.1.6" 867 976 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1277 1386 checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 1278 1387 1279 1388 [[package]] 1389 + name = "strsim" 1390 + version = "0.11.1" 1391 + source = "registry+https://github.com/rust-lang/crates.io-index" 1392 + checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 1393 + 1394 + [[package]] 1280 1395 name = "subtle" 1281 1396 version = "2.6.1" 1282 1397 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1610 1725 version = "1.0.4" 1611 1726 source = "registry+https://github.com/rust-lang/crates.io-index" 1612 1727 checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" 1728 + 1729 + [[package]] 1730 + name = "utf8parse" 1731 + version = "0.2.2" 1732 + source = "registry+https://github.com/rust-lang/crates.io-index" 1733 + checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 1613 1734 1614 1735 [[package]] 1615 1736 name = "valuable"
+1
Cargo.toml
··· 4 4 edition = "2024" 5 5 6 6 [dependencies] 7 + clap = { version = "4.0", features = ["derive", "env"] } 7 8 color-eyre = "0.6.5" 8 9 dialoguer = "0.11.0" 9 10 futures = "0.3.31"
-27
commands
··· 1 - jj log --no-graph -r 'bookmarks()' -T 'bookmarks ++ "\n"' 2 - 3 - jj log --no-graph -r 'children(push-wtqmqortkzry) & bookmarks()' -T 'bookmarks ++ "\n"' 4 - 5 - jj log --no-graph -r '..push-wtqmqortkzry & ..ng-3088-button-to-create-windows-from-window-notes' 6 - 7 - jj log --no-graph -r '..bookmarks()' 8 - 9 - jj log --no-graph -r 'root().. & bookmarks()' 10 - 11 - jj -r 'main---::' 12 - 13 - jj log --no-graph -T 'change_id ++ "\n"' 14 - 15 - jj log --no-graph -r 'children(ztzxrsrskxmyopropwxolytvzwlmlylu, 1)' -T 'change_id ++ " " ++ bookmarks ++ "\n"' 16 - 17 - jj log --no-graph -r 'ztzxrsrskxmyopropwxolytvzwlmlylu' -T 'change_id ++ " " ++ bookmarks ++ "\n"' 18 - 19 - ztzxrsrskxmyopropwxolytvzwlmlylu 20 - 21 - push-wtqmqortkzry 22 - ng-3088-button-to-create-windows-from-window-notes 23 - david/debug-menu-for-camera-layers 24 - david/port-ruler-to-new-snapping-25-07-04 25 - david/move-snapping-into-core-state 26 - david/simplify-coordinate-spaces 27 - main
+61
flake.lock
··· 1 + { 2 + "nodes": { 3 + "flake-utils": { 4 + "inputs": { 5 + "systems": "systems" 6 + }, 7 + "locked": { 8 + "lastModified": 1731533236, 9 + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", 10 + "owner": "numtide", 11 + "repo": "flake-utils", 12 + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", 13 + "type": "github" 14 + }, 15 + "original": { 16 + "owner": "numtide", 17 + "repo": "flake-utils", 18 + "type": "github" 19 + } 20 + }, 21 + "nixpkgs": { 22 + "locked": { 23 + "lastModified": 1751984180, 24 + "narHash": "sha256-LwWRsENAZJKUdD3SpLluwDmdXY9F45ZEgCb0X+xgOL0=", 25 + "owner": "NixOS", 26 + "repo": "nixpkgs", 27 + "rev": "9807714d6944a957c2e036f84b0ff8caf9930bc0", 28 + "type": "github" 29 + }, 30 + "original": { 31 + "owner": "NixOS", 32 + "ref": "nixos-unstable", 33 + "repo": "nixpkgs", 34 + "type": "github" 35 + } 36 + }, 37 + "root": { 38 + "inputs": { 39 + "flake-utils": "flake-utils", 40 + "nixpkgs": "nixpkgs" 41 + } 42 + }, 43 + "systems": { 44 + "locked": { 45 + "lastModified": 1681028828, 46 + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 47 + "owner": "nix-systems", 48 + "repo": "default", 49 + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 50 + "type": "github" 51 + }, 52 + "original": { 53 + "owner": "nix-systems", 54 + "repo": "default", 55 + "type": "github" 56 + } 57 + } 58 + }, 59 + "root": "root", 60 + "version": 7 61 + }
+45
flake.nix
··· 1 + { 2 + description = "A Rust project built with Nix"; 3 + 4 + inputs = { 5 + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; 6 + flake-utils.url = "github:numtide/flake-utils"; 7 + }; 8 + 9 + outputs = { 10 + self, 11 + nixpkgs, 12 + flake-utils, 13 + }: 14 + flake-utils.lib.eachDefaultSystem ( 15 + system: let 16 + pkgs = import nixpkgs { 17 + inherit system; 18 + }; 19 + in { 20 + packages.default = pkgs.rustPlatform.buildRustPackage { 21 + pname = "jj-sync-prs"; 22 + version = "0.1.0"; 23 + 24 + src = ./.; 25 + 26 + cargoLock = { 27 + lockFile = ./Cargo.lock; 28 + }; 29 + 30 + nativeBuildInputs = [ 31 + pkgs.makeWrapper 32 + ]; 33 + 34 + buildInputs = [ 35 + pkgs.graphviz 36 + ]; 37 + 38 + postInstall = '' 39 + wrapProgram $out/bin/jj-sync-prs \ 40 + --prefix PATH : ${pkgs.lib.makeBinPath [pkgs.graphviz]} 41 + ''; 42 + }; 43 + } 44 + ); 45 + }
-15
graph.dot
··· 1 - digraph Workflow { 2 - 0 [label="main"]; 3 - 1 [label="david/simplify-coordinate-spaces"]; 4 - 2 [label="david/move-snapping-into-core-state"]; 5 - 3 [label="david/port-ruler-to-new-snapping-25-07-04"]; 6 - 4 [label="david/debug-menu-for-camera-layers"]; 7 - 5 [label="ng-3088-button-to-create-windows-from-window-notes"]; 8 - 6 [label="push-wtqmqortkzry"]; 9 - 0 -> 1; 10 - 1 -> 2; 11 - 2 -> 3; 12 - 3 -> 4; 13 - 4 -> 5; 14 - 5 -> 6; 15 - }
+1
result
··· 1 + /nix/store/pz7q3hlh7vc9b5j9r7vz560bphq3py3j-jj-sync-prs-0.1.0
+1 -1
src/graph.rs
··· 60 60 pub fn to_dot(&self) -> String { 61 61 let mut out = String::new(); 62 62 63 - writeln!(&mut out, "digraph Workflow {{").unwrap(); 63 + writeln!(&mut out, "digraph Branches {{").unwrap(); 64 64 for (i, node) in self.nodes.iter().enumerate() { 65 65 writeln!(&mut out, " {i} [label=\"{node}\"];").unwrap(); 66 66 }
+101 -41
src/main.rs
··· 1 1 use std::fmt::Write; 2 + use std::io::Write as _; 3 + use std::path::PathBuf; 2 4 use std::pin::pin; 3 5 use std::{ffi::OsStr, path::Path}; 4 6 7 + use clap::Parser; 5 8 use color_eyre::eyre::{Context as _, ContextCompat}; 6 9 use dialoguer::{Confirm, Editor}; 7 10 use futures::TryStreamExt as _; ··· 12 15 13 16 mod graph; 14 17 18 + #[derive(Parser, Debug)] 19 + #[command(name = "jj-sync-prs")] 20 + #[command(about = "Sync jujutsu branches with GitHub pull requests")] 21 + struct Args { 22 + #[command(subcommand)] 23 + subcommand: Option<Subcommand>, 24 + } 25 + 26 + #[derive(clap::Subcommand, Debug)] 27 + enum Subcommand { 28 + /// Sync branches with pull requests 29 + Sync { 30 + /// GitHub authentication token 31 + #[arg(long, env = "GH_AUTH_TOKEN")] 32 + github_token: String, 33 + }, 34 + /// Save the branch graph as an image 35 + Graph { 36 + /// File to write the image to 37 + #[arg(short, long)] 38 + out: Option<PathBuf>, 39 + }, 40 + } 41 + 15 42 #[tokio::main(flavor = "current_thread")] 16 43 async fn main() -> color_eyre::Result<()> { 17 44 color_eyre::install()?; 18 45 19 - let graph = build_branch_graph().context("failed to build graph")?; 46 + let args = Args::parse(); 20 47 21 - let repo_info = repo_info().context("failed to find repo info")?; 48 + if let Some(subcommand) = args.subcommand { 49 + run_subcommand(subcommand).await?; 50 + } else { 51 + run_subcommand(Subcommand::Sync { 52 + github_token: std::env::var("GH_AUTH_TOKEN").context("GH_AUTH_TOKEN is not set")?, 53 + }) 54 + .await?; 55 + } 22 56 23 - let token = command("gh", ["auth", "token"]).context("failed to find github auth token")?; 24 - let token = token.trim().to_owned(); 25 - let octocrab = octocrab::OctocrabBuilder::default() 26 - .personal_token(token) 27 - .build() 28 - .context("failed to build github client")?; 29 - let mut pulls = octocrab 30 - .pulls(&repo_info.owner, &repo_info.name) 31 - .list() 32 - .send() 33 - .await 34 - .context("failed to fetch pull requests")? 35 - .into_stream(&octocrab) 36 - .try_collect::<Vec<_>>() 37 - .await 38 - .context("failed to fetch all pull requests")?; 57 + Ok(()) 58 + } 39 59 40 - for stack_root in graph.iter_edges_from("main") { 41 - find_or_create_prs( 42 - stack_root, "main", &graph, &repo_info, &octocrab, &mut pulls, 43 - ) 44 - .await 45 - .context("failed to sync prs")?; 46 - } 60 + async fn run_subcommand(subcommand: Subcommand) -> color_eyre::Result<()> { 61 + match subcommand { 62 + Subcommand::Sync { github_token } => { 63 + let graph = build_branch_graph().context("failed to build graph")?; 47 64 48 - for stack_root in graph.iter_edges_from("main") { 49 - let mut comment_lines = Vec::new(); 50 - write_pr_comment(&graph, stack_root, 0, &mut comment_lines); 65 + let repo_info = repo_info().context("failed to find repo info")?; 51 66 52 - if comment_lines.len() > 1 { 53 - // if the comment contains just one line then its not 54 - // part of a stack 55 - create_or_update_comments( 56 - &comment_lines, 57 - stack_root, 58 - &graph, 59 - &pulls, 60 - &octocrab, 61 - &repo_info, 62 - ) 63 - .await 64 - .context("failed to sync stack comment")?; 67 + let octocrab = octocrab::OctocrabBuilder::default() 68 + .personal_token(github_token) 69 + .build() 70 + .context("failed to build github client")?; 71 + let mut pulls = octocrab 72 + .pulls(&repo_info.owner, &repo_info.name) 73 + .list() 74 + .send() 75 + .await 76 + .context("failed to fetch pull requests")? 77 + .into_stream(&octocrab) 78 + .try_collect::<Vec<_>>() 79 + .await 80 + .context("failed to fetch all pull requests")?; 81 + 82 + for stack_root in graph.iter_edges_from("main") { 83 + find_or_create_prs( 84 + stack_root, "main", &graph, &repo_info, &octocrab, &mut pulls, 85 + ) 86 + .await 87 + .context("failed to sync prs")?; 88 + } 89 + 90 + for stack_root in graph.iter_edges_from("main") { 91 + let mut comment_lines = Vec::new(); 92 + write_pr_comment(&graph, stack_root, 0, &mut comment_lines); 93 + 94 + if comment_lines.len() > 1 { 95 + // if the comment contains just one line then its not 96 + // part of a stack 97 + create_or_update_comments( 98 + &comment_lines, 99 + stack_root, 100 + &graph, 101 + &pulls, 102 + &octocrab, 103 + &repo_info, 104 + ) 105 + .await 106 + .context("failed to sync stack comment")?; 107 + } 108 + } 109 + } 110 + Subcommand::Graph { out } => { 111 + let graph = build_branch_graph().context("failed to build graph")?; 112 + let dot = graph.to_dot(); 113 + let (read, mut write) = std::io::pipe()?; 114 + let out = out.as_deref().unwrap_or_else(|| Path::new("branches.png")); 115 + let mut cmd = std::process::Command::new("dot"); 116 + cmd.arg("-Tpng"); 117 + cmd.arg("-o"); 118 + cmd.args(out); 119 + cmd.stdin(read); 120 + let mut child = cmd.spawn().with_context(|| format!("{cmd:?} failed"))?; 121 + write.write_all(dot.as_bytes())?; 122 + drop(write); 123 + color_eyre::eyre::ensure!(child.wait()?.success()); 124 + eprintln!("Wrote {out:?}"); 65 125 } 66 126 } 67 127
workflow.png

This is a binary file and will not be displayed.