Skip to content

Commit 3ff0f30

Browse files
committed
Add github_updater utility mod
1 parent a53de3f commit 3ff0f30

File tree

7 files changed

+193
-25
lines changed

7 files changed

+193
-25
lines changed

src/db/mod.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,13 @@ pub fn create_tables(conn: &Connection) -> Result<(), Error> {
2626
id SERIAL PRIMARY KEY, \
2727
name VARCHAR(255) UNIQUE NOT NULL, \
2828
latest_version_id INT DEFAULT 0, \
29-
stars INT DEFAULT 0, \
30-
issues JSON, \
3129
versions JSON DEFAULT '[]', \
3230
downloads_total INT DEFAULT 0, \
31+
github_description VARCHAR(1024) \
32+
github_stars INT DEFAULT 0, \
33+
github_forks INT DEFAULT 0, \
34+
github_issues INT DEFAULT 0, \
35+
github_last_commit TIMESTAMP \
3336
github_last_update TIMESTAMP \
3437
)",
3538
"CREATE TABLE releases ( \

src/docbuilder/chroot_builder.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ impl DocBuilder {
4040
Err(err) => info!("Failed to build package {}-{}: {}", name, version, err)
4141
}
4242
self.cache.insert(format!("{}-{}", name, version));
43-
count += 1;
4443
})
4544
}
4645

@@ -263,7 +262,7 @@ mod test {
263262
fn test_build_world() {
264263
let _ = env_logger::init();
265264
let options = DocBuilderOptions::from_prefix(PathBuf::from("../cratesfyi-prefix"));
266-
let docbuilder = DocBuilder::new(options);
265+
let mut docbuilder = DocBuilder::new(options);
267266
// This test is building WHOLE WORLD and may take forever
268267
assert!(docbuilder.build_world().is_ok());
269268
}
@@ -273,7 +272,7 @@ mod test {
273272
fn test_build_package() {
274273
let _ = env_logger::init();
275274
let options = DocBuilderOptions::from_prefix(PathBuf::from("../cratesfyi-prefix"));
276-
let docbuilder = DocBuilder::new(options);
275+
let mut docbuilder = DocBuilder::new(options);
277276
let res = docbuilder.build_package("rand", "0.3.14");
278277
assert!(res.is_ok());
279278
}

src/docbuilder/error.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::error::Error;
55
use rustc_serialize::json::BuilderError;
66
use postgres;
77
use cargo;
8+
use hyper;
89

910
#[derive(Debug)]
1011
pub enum DocBuilderError {
@@ -17,6 +18,8 @@ pub enum DocBuilderError {
1718
CargoError(Box<cargo::CargoError>),
1819
DatabaseConnectError(postgres::error::ConnectError),
1920
DatabaseError(postgres::error::Error),
21+
HyperError(hyper::Error),
22+
GenericError(String),
2023
}
2124

2225

@@ -34,6 +37,8 @@ impl fmt::Display for DocBuilderError {
3437
write!(f, "Database connection error: {}", err)
3538
}
3639
DocBuilderError::DatabaseError(ref err) => write!(f, "Database error: {}", err),
40+
DocBuilderError::HyperError(ref err) => write!(f, "hyper error: {}", err),
41+
DocBuilderError::GenericError(ref err) => write!(f, "Generic error: {}", err),
3742
}
3843
}
3944
}
@@ -51,6 +56,8 @@ impl Error for DocBuilderError {
5156
DocBuilderError::CargoError(ref err) => err.description(),
5257
DocBuilderError::DatabaseConnectError(ref err) => err.description(),
5358
DocBuilderError::DatabaseError(ref err) => err.description(),
59+
DocBuilderError::HyperError(ref err) => err.description(),
60+
DocBuilderError::GenericError(ref err) => err,
5461
}
5562
}
5663

@@ -65,6 +72,8 @@ impl Error for DocBuilderError {
6572
DocBuilderError::CargoError(ref err) => Some(err),
6673
DocBuilderError::DatabaseConnectError(ref err) => Some(err),
6774
DocBuilderError::DatabaseError(ref err) => Some(err),
75+
DocBuilderError::HyperError(ref err) => Some(err),
76+
DocBuilderError::GenericError(_) => None,
6877
}
6978
}
7079
}
@@ -101,3 +110,10 @@ impl From<postgres::error::Error> for DocBuilderError {
101110
DocBuilderError::DatabaseError(err)
102111
}
103112
}
113+
114+
115+
impl From<hyper::Error> for DocBuilderError {
116+
fn from(err: hyper::Error) -> DocBuilderError {
117+
DocBuilderError::HyperError(err)
118+
}
119+
}

src/docbuilder/mod.rs

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -81,23 +81,3 @@ impl DocBuilder {
8181
Ok(())
8282
}
8383
}
84-
85-
86-
87-
#[cfg(test)]
88-
mod test {
89-
extern crate env_logger;
90-
use ::DocBuilderOptions;
91-
use super::*;
92-
use std::path::PathBuf;
93-
94-
#[test]
95-
#[ignore]
96-
fn test_docbuilder_crates() {
97-
let _ = env_logger::init();
98-
let options = DocBuilderOptions::from_prefix(PathBuf::from("../cratesfyi-prefix"));
99-
let docbuilder = DocBuilder::new(options);
100-
let res = docbuilder.crates(|_, _| {});
101-
assert!(res.is_ok());
102-
}
103-
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub use self::docbuilder::error::DocBuilderError;
1818
pub use self::docbuilder::options::DocBuilderOptions;
1919

2020
pub mod db;
21+
pub mod utils;
2122
mod build_doc;
2223
mod copy;
2324
mod docbuilder;

src/utils/github_updater.rs

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
2+
use ::db::connect_db;
3+
use regex::Regex;
4+
use DocBuilderError;
5+
use time;
6+
7+
8+
9+
/// Fields we need use in cratesfyi
10+
#[derive(Debug)]
11+
struct GitHubFields {
12+
pub description: String,
13+
pub stars: i64,
14+
pub forks: i64,
15+
pub issues: i64,
16+
pub last_commit: time::Timespec,
17+
}
18+
19+
20+
/// Updates github fields in crates table
21+
pub fn github_updater() -> Result<(), DocBuilderError> {
22+
let conn = try!(connect_db());
23+
24+
// TODO: This query assumes repository field in Cargo.toml is
25+
// always the same across all versions of a crate
26+
for row in &try!(conn.query("SELECT DISTINCT ON (crates.name) crate.name, crates.id, \
27+
repository_url FROM crates, releases WHERE releases.crate_id \
28+
= crates.id AND repository_url ~ '^https*://github.com' AND \
29+
github_last_update < NOW() + INTERVAL '1 day'",
30+
&[])) {
31+
let crate_name: String = row.get(0);
32+
let crate_id: i32 = row.get(1);
33+
let repository_url: String = row.get(2);
34+
35+
if let Err(err) = get_github_path(&repository_url[..])
36+
.ok_or(DocBuilderError::GenericError("Failed to get github path"
37+
.to_string()))
38+
.and_then(|path| get_github_fields(&path[..]))
39+
.and_then(|fields| {
40+
conn.execute("UPDATE crates SET github_description = $1, \
41+
github_stars = $2, github_forks = $3, \
42+
github_issues = $4, github_last_commit = $5, \
43+
github_last_update = NOW() WHERE id = $6",
44+
&[&fields.description,
45+
&(fields.stars as i32),
46+
&(fields.forks as i32),
47+
&(fields.issues as i32),
48+
&(fields.last_commit),
49+
&crate_id])
50+
.map_err(DocBuilderError::DatabaseError)
51+
}) {
52+
error!("Failed to update github fields of: {} {}", crate_name, err);
53+
}
54+
}
55+
56+
Ok(())
57+
}
58+
59+
60+
fn get_github_fields(path: &str) -> Result<GitHubFields, DocBuilderError> {
61+
use rustc_serialize::json::Json;
62+
63+
let body = {
64+
use std::io::Read;
65+
use hyper::client::Client;
66+
use hyper::header::{UserAgent, Authorization, Basic};
67+
use hyper::status::StatusCode;
68+
use std::env;
69+
70+
let client = Client::new();
71+
let mut body = String::new();
72+
73+
let mut resp = try!(client.get(&format!("https://api.github.com/repos/{}", path)[..])
74+
.header(UserAgent(format!("cratesfyi/{}",
75+
env!("CARGO_PKG_VERSION"))))
76+
.header(Authorization(Basic {
77+
username: env::var("CRATESFYI_GITHUB_USERNAME")
78+
.ok()
79+
.and_then(|u| Some(u.to_string()))
80+
.unwrap_or("".to_string()),
81+
password: env::var("CRATESFYI_GITHUB_ACCESSTOKEN").ok(),
82+
}))
83+
.send());
84+
85+
if resp.status != StatusCode::Ok {
86+
return Err(DocBuilderError::GenericError("Failed to get github data".to_string()));
87+
}
88+
89+
try!(resp.read_to_string(&mut body));
90+
body
91+
};
92+
93+
let json = try!(Json::from_str(&body[..]));
94+
let obj = json.as_object().unwrap();
95+
96+
Ok(GitHubFields {
97+
description: obj.get("description").and_then(|d| d.as_string()).unwrap_or("").to_string(),
98+
stars: obj.get("stargazers_count").and_then(|d| d.as_i64()).unwrap_or(0),
99+
forks: obj.get("forks_count").and_then(|d| d.as_i64()).unwrap_or(0),
100+
issues: obj.get("open_issues").and_then(|d| d.as_i64()).unwrap_or(0),
101+
last_commit: time::strptime(obj.get("pushed_at")
102+
.and_then(|d| d.as_string())
103+
.unwrap_or(""),
104+
"%Y-%m-%dT%H:%M:%S")
105+
.unwrap_or(time::now())
106+
.to_timespec(),
107+
})
108+
}
109+
110+
111+
112+
fn get_github_path(url: &str) -> Option<String> {
113+
let re = Regex::new(r"https*://github.com/([\w_-]+)/([\w_-]+)(.git)*").unwrap();
114+
match re.captures(url) {
115+
Some(cap) => Some(format!("{}/{}", cap.at(1).unwrap(), cap.at(2).unwrap())),
116+
None => None,
117+
}
118+
}
119+
120+
121+
122+
#[cfg(test)]
123+
mod test {
124+
extern crate env_logger;
125+
use super::{get_github_path, get_github_fields, github_updater};
126+
127+
#[test]
128+
fn test_get_github_path() {
129+
assert_eq!(get_github_path("https://github.com/onur/cratesfyi"),
130+
Some("onur/cratesfyi".to_string()));
131+
assert_eq!(get_github_path("http://github.com/onur/cratesfyi"),
132+
Some("onur/cratesfyi".to_string()));
133+
assert_eq!(get_github_path("https://github.com/onur/cratesfyi.git"),
134+
Some("onur/cratesfyi".to_string()));
135+
assert_eq!(get_github_path("https://github.com/onur23cmD_M_R_L_/crates_fy-i"),
136+
Some("onur23cmD_M_R_L_/crates_fy-i".to_string()));
137+
}
138+
139+
140+
#[test]
141+
fn test_get_github_fields() {
142+
let _ = env_logger::init();
143+
let fields = get_github_fields("onur/cratesfyi");
144+
assert!(fields.is_ok());
145+
146+
let fields = fields.unwrap();
147+
assert!(fields.description != "".to_string());
148+
assert!(fields.stars >= 0);
149+
assert!(fields.forks >= 0);
150+
assert!(fields.issues >= 0);
151+
152+
use time;
153+
assert!(fields.last_commit <= time::now().to_timespec());
154+
}
155+
156+
157+
#[test]
158+
#[ignore]
159+
fn test_github_updater() {
160+
let _ = env_logger::init();
161+
assert!(github_updater().is_ok());
162+
}
163+
}

src/utils/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
//! Various utilities for cratesfyi
2+
3+
4+
pub use self::github_updater::github_updater;
5+
6+
mod github_updater;

0 commit comments

Comments
 (0)