Listen to git commits for a specific repo and run a shell command
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

at 652a09925cfee5b90875e8c52503c0fa280e42b7 214 lines 6.8 kB view raw
1fn help() { 2 println!( 3 "Help: tangled-on-commit 4Listen for commits on a specified repository and execute a shell command. 5 6CLI Arguments: 7 `tangled-on-commit (-h | --help)` 8 Displays this message 9 10 `tangled-on-commit` 11 No specified handle, repo, or command. Falls back to config/env 12 13 `tangled-on-commit SHELL` 14 Uses config/env for handle and repo 15 16 `tangled-on-commit @HANDLE SHELL` 17 Uses config/env for repo 18 19 `tangled-on-commit REPO SHELL` 20 Uses config/env for handle 21 22 `tangled-on-commit @HANDLE/REPO SHELL` 23 `tangled-on-commit HANDLE REPO SHELL` 24 No config/env 25 26JSON: 27 Loads the file `tangled-on-commit.json` from cwd if it exists. 28 Reads keys \"handle\", \"repo_name\", and \"shell\". 29 Unknown keys are ignored and any key can be ommitted 30 JSON is used if the arguments aren't passed to the CLI 31 32Env: 33 Loads the environment variables `TANGLED_ON_COMMIT_HANDLE` and `TANGLED_ON_COMMIT_REPO_NAME` 34 Shell cannot be set by environment variables. 35 Env variables are used if relevant keys are ommitted an arguments aren't passed to the CLI. 36" 37 ) 38} 39 40#[derive(Debug)] 41pub struct Config { 42 pub handle: String, 43 pub repo_name: String, 44 pub shell: String, 45} 46 47pub fn load_config() -> Result<Config, ()> { 48 // load config from cli args if present 49 // if omitted, fallback to a local `tangled-on-commit.json` file 50 // if key omitted or file not found, fall back to env 51 // if env omitted, error out and quit 52 // note: shell is not loaded from env (to avoid the user unknowingly executing scripts) 53 54 // if any args are `-h` || `--help` display help and quit 55 for arg in std::env::args() { 56 if arg == "-h" || arg == "--help" { 57 help(); 58 return Err(()); 59 } 60 } 61 62 // if 0 args are passed, skip 63 // if 1 arg is passed, its the shell command 64 // if 2 args are passed: 65 // if arg 1 starts in @ its a handle 66 // if it contains a / the contents after the slash is the reponame 67 // else its the reponame 68 // arg 2 is always the shell command 69 // if 3 args are passed its handle repo shell 70 // if more args are passed its an error 71 72 let mut handle: Option<String> = None; 73 let mut repo_name: Option<String> = None; 74 let mut shell: Option<String> = None; 75 match std::env::args().collect::<Vec<_>>().len() { 76 // 0 args (std env args includes this script) 77 1 => {} 78 2 => { 79 shell = Some( 80 std::env::args() 81 .last() 82 .expect("Invalid state: 2 `Some` std::env::args() but found no Some values"), 83 ) 84 } 85 3 => { 86 // load args and consume first 87 let mut args = std::env::args(); 88 args.next(); 89 90 if let Some(val) = args.next() { 91 if val.starts_with("@") { 92 if val.contains("/") { 93 let entries: Vec<_> = val.split("/").collect(); 94 if entries.len() != 2 { 95 return Err(()); 96 } 97 handle = Some(entries[0][1..].to_string()); 98 repo_name = Some(entries[1].to_string()); 99 } else { 100 handle = Some(val[1..].to_string()); 101 } 102 } else { 103 repo_name = Some(val) 104 }; 105 } 106 shell = Some( 107 args.next() 108 .expect("Invalid state: 3 `Some` std::env::args() but only found 2"), 109 ); 110 } 111 4 => { 112 // load args and consume first 113 let mut args = std::env::args(); 114 args.next(); 115 116 handle = Some( 117 args.next() 118 .expect("Invalid state: 4 `Some` std::env::args() but only found 1"), 119 ); 120 repo_name = Some( 121 args.next() 122 .expect("Invalid state: 4 `Some` std::env::args() but only found 2"), 123 ); 124 shell = Some( 125 args.next() 126 .expect("Invalid state: 4 `Some` std::env::args() but only found 3"), 127 ); 128 } 129 _ => { 130 // err 131 } 132 } 133 134 if let Some(ref handle) = handle 135 && let Some(ref repo_name) = repo_name 136 && let Some(ref shell) = shell 137 { 138 return Ok(Config { 139 handle: handle.to_string(), 140 repo_name: repo_name.to_string(), 141 shell: shell.to_string(), 142 }); 143 } 144 145 // now load config 146 if let Ok(file) = std::fs::read_to_string("./tangled-on-commit.json") { 147 if let Ok(parsed) = json::parse(&file) { 148 if handle.is_none() 149 && let Some(json_handle) = parsed["handle"].as_str() 150 { 151 handle = Some(String::from(json_handle)) 152 } 153 if repo_name.is_none() 154 && let Some(json_repo_name) = parsed["repo_name"].as_str() 155 { 156 repo_name = Some(String::from(json_repo_name)) 157 } 158 if shell.is_none() 159 && let Some(json_shell) = parsed["shell"].as_str() 160 { 161 shell = Some(String::from(json_shell)) 162 } 163 } 164 } 165 166 if let Some(ref handle) = handle 167 && let Some(ref repo_name) = repo_name 168 && let Some(ref shell) = shell 169 { 170 return Ok(Config { 171 handle: handle.to_string(), 172 repo_name: repo_name.to_string(), 173 shell: shell.to_string(), 174 }); 175 } 176 177 // now load from env 178 if handle.is_none() 179 && let Ok(env_handle) = std::env::var("TANGLED_ON_COMMIT_HANDLE") 180 { 181 handle = Some(String::from(env_handle)) 182 } 183 if repo_name.is_none() 184 && let Ok(env_repo_name) = std::env::var("TANGLED_ON_COMMIT_REPO_NAME") 185 { 186 repo_name = Some(String::from(env_repo_name)) 187 } 188 189 if let Some(ref handle) = handle 190 && let Some(ref repo_name) = repo_name 191 && let Some(ref shell) = shell 192 { 193 return Ok(Config { 194 handle: handle.to_string(), 195 repo_name: repo_name.to_string(), 196 shell: shell.to_string(), 197 }); 198 } 199 200 // couldnt resolve every value 201 // print an error and quit 202 println!("Unable to find a value for every setting. Missing values for:"); 203 if handle.is_none() { 204 println!("- handle"); 205 } 206 if repo_name.is_none() { 207 println!("- repo_name"); 208 } 209 if shell.is_none() { 210 println!("- shell"); 211 } 212 println!("\nRun `tangled-on-commit --help` to see the help message"); 213 return Err(()); 214}