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