Skip to content

start tracking when crates are yanked #322

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 17 additions & 5 deletions src/docbuilder/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,23 @@ impl DocBuilder {
// I belive this will fix ordering of queue if we get more than one crate from changes
changes.reverse();

for krate in changes.iter().filter(|k| k.kind != ChangeKind::Yanked) {
conn.execute("INSERT INTO queue (name, version) VALUES ($1, $2)",
&[&krate.name, &krate.version])
.ok();
debug!("{}-{} added into build queue", krate.name, krate.version);
for krate in &changes {
match krate.kind {
ChangeKind::Yanked => {
// FIXME: remove built doc files? set build as failed?
conn.execute("UPDATE releases SET yanked = TRUE FROM crates WHERE \
crates.id = releases.crate_id AND name = $1 AND version = $2",
&[&krate.name, &krate.version])
.ok();
debug!("{}-{} yanked", krate.name, krate.version);
}
ChangeKind::Added => {
conn.execute("INSERT INTO queue (name, version) VALUES ($1, $2)",
&[&krate.name, &krate.version])
.ok();
debug!("{}-{} added into build queue", krate.name, krate.version);
}
}
}

let queue_count = conn.query("SELECT COUNT(*) FROM queue WHERE attempt < 5", &[])
Expand Down
10 changes: 7 additions & 3 deletions src/web/crate_details.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ pub struct CrateDetails {
github_issues: Option<i32>,
metadata: MetaData,
is_library: bool,
yanked: bool,
doc_targets: Option<Json>,
license: Option<String>,
documentation_url: Option<String>,
Expand Down Expand Up @@ -82,6 +83,7 @@ impl ToJson for CrateDetails {
m.insert("github_issues".to_string(), self.github_issues.to_json());
m.insert("metadata".to_string(), self.metadata.to_json());
m.insert("is_library".to_string(), self.is_library.to_json());
m.insert("yanked".to_string(), self.yanked.to_json());
m.insert("doc_targets".to_string(), self.doc_targets.to_json());
m.insert("license".to_string(), self.license.to_json());
m.insert("documentation_url".to_string(), self.documentation_url.to_json());
Expand Down Expand Up @@ -116,6 +118,7 @@ impl CrateDetails {
crates.github_forks,
crates.github_issues,
releases.is_library,
releases.yanked,
releases.doc_targets,
releases.license,
releases.documentation_url
Expand Down Expand Up @@ -185,9 +188,10 @@ impl CrateDetails {
github_issues: rows.get(0).get(20),
metadata: metadata,
is_library: rows.get(0).get(21),
doc_targets: rows.get(0).get(22),
license: rows.get(0).get(23),
documentation_url: rows.get(0).get(24),
yanked: rows.get(0).get(22),
doc_targets: rows.get(0).get(23),
license: rows.get(0).get(24),
documentation_url: rows.get(0).get(25),
};

if let Some(repository_url) = crate_details.repository_url.clone() {
Expand Down
62 changes: 48 additions & 14 deletions src/web/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,28 @@ use iron::{self, Handler, status};
use iron::headers::{CacheControl, CacheDirective, ContentType};
use router::{Router, NoRoute};
use staticfile::Static;
use handlebars_iron::{HandlebarsEngine, DirectorySource};
use handlebars_iron::{HandlebarsEngine, DirectorySource, SourceError};
use time;
use postgres::Connection;
use semver::{Version, VersionReq};
use rustc_serialize::json::{Json, ToJson};
use std::collections::BTreeMap;
use std::collections::{BTreeMap, BTreeSet};

/// Duration of static files for staticfile and DatabaseFileHandler (in seconds)
const STATIC_FILE_CACHE_DURATION: u64 = 60 * 60 * 24 * 30 * 12; // 12 months
const STYLE_CSS: &'static str = include_str!(concat!(env!("OUT_DIR"), "/style.css"));
const OPENSEARCH_XML: &'static [u8] = include_bytes!("opensearch.xml");

fn handlebars_engine() -> Result<HandlebarsEngine, SourceError> {
// TODO: Use DocBuilderOptions for paths
let mut hbse = HandlebarsEngine::new();
hbse.add(Box::new(DirectorySource::new("./templates", ".hbs")));

// load templates
try!(hbse.reload());

Ok(hbse)
}

struct CratesfyiHandler {
shared_resource_handler: Box<Handler>,
Expand All @@ -77,14 +87,10 @@ struct CratesfyiHandler {

impl CratesfyiHandler {
fn chain<H: Handler>(base: H) -> Chain {
// TODO: Use DocBuilderOptions for paths
let mut hbse = HandlebarsEngine::new();
hbse.add(Box::new(DirectorySource::new("./templates", ".hbs")));

// load templates
if let Err(e) = hbse.reload() {
panic!("Failed to load handlebar templates: {}", e.description());
}
let hbse = match handlebars_engine() {
Ok(hbse) => hbse,
Err(e) => panic!("Failed to load handlebar templates: {}", e.description()),
};

let mut chain = Chain::new(base);
chain.link_before(pool::Pool::new());
Expand Down Expand Up @@ -301,6 +307,23 @@ fn match_version(conn: &Connection, name: &str, version: Option<&str>) -> Option
versions
};

let yanked_versions = {
let rows = conn.query("SELECT version FROM crates JOIN releases \
ON releases.crate_id = crates.id \
WHERE crates.name = $1 AND releases.yanked = true",
&[&name]).unwrap();
let mut versions = BTreeSet::<Version>::new();

for v in &rows {
let v: String = v.get(0);
if let Ok(semv) = Version::parse(&v) {
versions.insert(semv);
}
}

versions
};

// first check for exact match
// we can't expect users to use semver in query
for version in &versions {
Expand Down Expand Up @@ -335,14 +358,18 @@ fn match_version(conn: &Connection, name: &str, version: Option<&str>) -> Option
};

// semver is acting weird for '*' (any) range if a crate only have pre-release versions
// return first version if requested version is '*'
if req_version == "*" && !versions_sem.is_empty() {
return Some(format!("{}", versions_sem[0]));
// return first non-yanked version if requested version is '*'
if req_version == "*" {
return versions_sem.iter()
.find(|v| !yanked_versions.contains(v))
.map(|v| v.to_string());
}

for version in &versions_sem {
if req_sem_ver.matches(&version) {
return Some(format!("{}", version));
if !yanked_versions.contains(&version) {
return Some(version.to_string());
}
}
}

Expand Down Expand Up @@ -539,4 +566,11 @@ mod test {
assert_eq!(latest_version(&versions, "0.9.0"), Some("1.1.0".to_owned()));
assert_eq!(latest_version(&versions, "invalidversion"), None);
}

#[test]
fn test_templates_are_valid() {
if let Err(e) = handlebars_engine() {
panic!("Failed to load handlebar templates: {}", e.description());
}
}
}
39 changes: 8 additions & 31 deletions src/web/releases.rs
Original file line number Diff line number Diff line change
Expand Up @@ -485,37 +485,14 @@ pub fn search_handler(req: &mut Request) -> IronResult<Response> {


if let Some(version) = match_version(&conn, &query, None) {
// FIXME: This is a super dirty way to check if crate have rustdocs generated.
// match_version should handle this instead of this code block.
// This block is introduced to fix #163
let rustdoc_status = {
let rows = ctry!(conn.query("SELECT rustdoc_status
FROM releases
INNER JOIN crates
ON crates.id = releases.crate_id
WHERE crates.name = $1 AND releases.version = $2",
&[query, &version]));
if rows.is_empty() {
false
} else {
rows.get(0).get(0)
}
};
let url = if rustdoc_status {
ctry!(Url::parse(&format!("{}://{}:{}/{}/{}",
req.url.scheme(),
req.url.host(),
req.url.port(),
query,
version)[..]))
} else {
ctry!(Url::parse(&format!("{}://{}:{}/crate/{}/{}",
req.url.scheme(),
req.url.host(),
req.url.port(),
query,
version)[..]))
};
// If the crate doesn't have docs, the `crate/version` route will redirect to the
// crate details page instead
let url = ctry!(Url::parse(&format!("{}://{}:{}/{}/{}",
req.url.scheme(),
req.url.host(),
req.url.port(),
query,
version)[..]));
let mut resp = Response::with((status::Found, Redirect(url)));

use iron::headers::{Expires, HttpDate};
Expand Down
4 changes: 4 additions & 0 deletions templates/crate_details.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,17 @@
{{#unless is_library}}
<div class="warning">{{name}}-{{version}} is not a library.</div>
{{else}}
{{#if yanked}}
<div class="warning">{{name}}-{{version}} has been yanked.</div>
{{else}}
{{#unless build_status}}
<div class="warning">docs.rs failed to build {{name}}-{{version}}<br>Please check <a href="/crate/{{name}}/{{version}}/builds">build logs</a> and if you believe this is docs.rs' fault, report into <a href="https://github.com/rust-lang/docs.rs/issues/23">this issue report</a>.</div>
{{else}}
{{#unless rustdoc_status}}
<div class="warning">{{name}}-{{version}} doesn't have any documentation.</div>
{{/unless}}
{{/unless}}
{{/if}}
{{/unless}}
{{#if readme}}
{{{readme}}}
Expand Down