Skip to content

Commit fa6af38

Browse files
Implement default log and checkout roots.
1 parent 8b2420b commit fa6af38

File tree

2 files changed

+109
-12
lines changed

2 files changed

+109
-12
lines changed

README.md

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,23 @@ annotated configuration example below:
3939
## Every configuration requires a `config` section
4040

4141
[config]
42-
port = 5712 # Port to run hookshot server
43-
secret = "your v secure pass" # Key for message verification
44-
checkout_root = "/tmp/checkouts" # Directory to store checkouts. Must be writeable.
45-
log_root = "/var/logs/hookshot" # Directory to store logs. Must be writeable
46-
hostname = "10.20.30.40" # Externally accessible hostname or IP.
42+
## Port to run hookshot server. Defaults to 1469.
43+
port = 5712
44+
45+
## Key for message verification
46+
secret = "your v secure pass"
47+
48+
## Directory to store checkouts. Must be writeable. Defaults to
49+
## $XDG_DATA_HOME/hookshot/checkouts (~/.local/share/hookshots/checkouts)
50+
checkout_root = "/tmp/checkouts"
51+
52+
## Directory to store logs. Must be writeable. Defaults to
53+
## $XDG_DATA_HOME/hookshot/logs (~/.local/share/hookshots/logs)
54+
log_root = "/var/log/hookshot"
55+
56+
## Externally accessible hostname or IP. This will be sent as part of any
57+
## outgoing webhook requests so a consumer can create complete URLs.
58+
hostname = "10.20.30.40"
4759

4860
## `env.*` sections are optional. They represent extra data that will be sent to
4961
## repositories that might need extra that shouldn't be stored in the repository

src/server_config.rs

Lines changed: 92 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use std::collections::BTreeMap;
22
use std::error::Error as StdError;
3+
use std::env;
34
use std::fmt;
4-
use std::fs::File;
5+
use std::fs::{self, File};
56
use std::io::Read;
67
use std::path::Path;
78
use std::u16;
@@ -37,6 +38,7 @@ pub enum Error {
3738
InvalidEnvironmentTable,
3839
FileOpenError,
3940
FileReadError,
41+
DirectoryCreateError,
4042
}
4143

4244
impl fmt::Display for Error {
@@ -63,10 +65,43 @@ impl StdError for Error {
6365
Error::InvalidEnvironmentTable => "'env' table is invalid, check configuration",
6466
Error::FileOpenError => "could not open config file",
6567
Error::FileReadError => "could not read config file into string",
68+
Error::DirectoryCreateError => "could not create default directory",
6669
}
6770
}
6871
}
6972

73+
// See http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
74+
fn get_xdg_data_home() -> Option<String> {
75+
let empty_string = String::from("");
76+
77+
let home_dir = match env::var("HOME") {
78+
Ok(dir) => dir,
79+
Err(_) => return None,
80+
};
81+
82+
Some(match env::var("XDG_DATA_HOME") {
83+
Ok(ref dir) if *dir == empty_string => home_dir,
84+
Err(_) => format!("{}/.local/share", home_dir),
85+
Ok(dir) => dir,
86+
})
87+
}
88+
89+
fn get_default_log_dir() -> Option<String> {
90+
let xdg_data_home = match get_xdg_data_home() {
91+
None => return None,
92+
Some(dir) => dir,
93+
};
94+
Some(format!("{}/hookshot/logs", xdg_data_home))
95+
}
96+
97+
fn get_default_checkout_dir() -> Option<String> {
98+
let xdg_data_home = match get_xdg_data_home() {
99+
None => return None,
100+
Some(dir) => dir,
101+
};
102+
Some(format!("{}/hookshot/checkouts", xdg_data_home))
103+
}
104+
70105
impl ServerConfig {
71106
pub fn from_file(config_path: &Path) -> Result<ServerConfig, Error> {
72107
let mut file = match File::open(&config_path) {
@@ -81,6 +116,10 @@ impl ServerConfig {
81116
}
82117

83118
pub fn from(string: &str) -> Result<ServerConfig, Error> {
119+
let default_port = 1469;
120+
let default_checkout_dir = get_default_checkout_dir();
121+
let default_log_dir = get_default_log_dir();
122+
84123
let root = match toml::Parser::new(string).parse() {
85124
Some(value) => value,
86125
None => return Err(Error::ParseError),
@@ -96,20 +135,48 @@ impl ServerConfig {
96135
};
97136
let u16_max = u16::max_value() as i64;
98137
let port = match config.lookup("port") {
99-
None => return Err(Error::MissingPort),
138+
None => default_port,
100139
Some(&Value::Integer(port)) if port < u16_max => port as u16,
101140
_ => return Err(Error::InvalidPort),
102141
};
142+
103143
let checkout_root = match lookup_as_string(config, "checkout_root") {
104-
LookupResult::Missing => return Err(Error::MissingCheckoutRoot),
144+
LookupResult::Missing => {
145+
let checkout_root_string = match default_checkout_dir {
146+
None => return Err(Error::MissingCheckoutRoot),
147+
Some(dir) => dir,
148+
};
149+
let checkout_root = Path::new(&checkout_root_string);
150+
if let Err(_) = fs::create_dir_all(&checkout_root) {
151+
return Err(Error::DirectoryCreateError);
152+
}
153+
match VerifiedPath::directory(None, checkout_root) {
154+
Ok(v) => v,
155+
Err(_) => return Err(Error::InvalidCheckoutRoot),
156+
}
157+
}
105158
LookupResult::WrongType => return Err(Error::InvalidCheckoutRoot),
106159
LookupResult::Value(v) => match VerifiedPath::directory(None, Path::new(v)) {
107160
Ok(v) => v,
108161
Err(_) => return Err(Error::InvalidCheckoutRoot),
109162
},
110163
};
164+
111165
let log_root = match lookup_as_string(config, "log_root") {
112-
LookupResult::Missing => return Err(Error::MissingLogRoot),
166+
LookupResult::Missing => {
167+
let log_root_string = match default_log_dir {
168+
None => return Err(Error::MissingLogRoot),
169+
Some(dir) => dir,
170+
};
171+
let log_root = Path::new(&log_root_string);
172+
if let Err(_) = fs::create_dir_all(&log_root) {
173+
return Err(Error::DirectoryCreateError);
174+
}
175+
match VerifiedPath::directory(None, log_root) {
176+
Ok(v) => v,
177+
Err(_) => return Err(Error::InvalidLogRoot),
178+
}
179+
}
113180
LookupResult::WrongType => return Err(Error::InvalidLogRoot),
114181
LookupResult::Value(v) => match VerifiedPath::directory(None, Path::new(v)) {
115182
Ok(v) => v,
@@ -204,6 +271,7 @@ fn lookup_as_string<'a>(obj: &'a toml::Value, key: &'static str) -> LookupResult
204271
mod tests {
205272
use super::*;
206273
use std::path::Path;
274+
use std::env;
207275

208276
macro_rules! expect_error {
209277
( $i:ident, $error:path ) => {{
@@ -269,14 +337,16 @@ mod tests {
269337

270338
#[test]
271339
fn test_invalid_config_missing_checkout_root() {
340+
env::set_var("XDG_DATA_HOME", "/tmp");
272341
let toml = r#"
273342
[config]
274343
secret = "it's a secret to everyone"
275344
port = 5712
276345
hostname = "127.0.0.1"
277346
log_root = "/tmp"
278347
"#;
279-
expect_error!(toml, Error::MissingCheckoutRoot);
348+
let config = ServerConfig::from(&toml).unwrap();
349+
assert_eq!(config.checkout_root.path(), Path::new("/tmp/hookshot/checkouts"));
280350
}
281351

282352
#[test]
@@ -305,15 +375,17 @@ mod tests {
305375
}
306376

307377
#[test]
308-
fn test_invalid_config_missing_log_root() {
378+
fn test_config_default_log_root() {
379+
env::set_var("XDG_DATA_HOME", "/tmp");
309380
let toml = r#"
310381
[config]
311382
port = 5712
312383
hostname = "127.0.0.1"
313384
secret = "shh"
314385
checkout_root = "/tmp"
315386
"#;
316-
expect_error!(toml, Error::MissingLogRoot);
387+
let config = ServerConfig::from(&toml).unwrap();
388+
assert_eq!(config.log_root.path(), Path::new("/tmp/hookshot/logs"));
317389
}
318390

319391

@@ -343,6 +415,19 @@ mod tests {
343415
expect_error!(toml, Error::InvalidPort);
344416
}
345417

418+
#[test]
419+
fn test_config_default_port() {
420+
let toml = r#"
421+
[config]
422+
secret = "it's a secret to everyone"
423+
hostname = "127.0.0.1"
424+
checkout_root = "/tmp"
425+
log_root = "/tmp"
426+
"#;
427+
let config = ServerConfig::from(&toml).unwrap();
428+
assert_eq!(config.port, 1469);
429+
}
430+
346431
#[test]
347432
fn test_environments() {
348433
let toml = r#"

0 commit comments

Comments
 (0)