Skip to content
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

Display reported build information in a badge and on crate version pages #764

Closed

Conversation

shepmaster
Copy link
Member

@shepmaster shepmaster commented Jun 8, 2017

(This is a replacement of #540)

This goes with rust-lang/cargo#5099. @carols10cents worked on these too :)

This PR adds a table that stores recorded crate version, rust version, target, and pass/fail as reported by crate owners using the cargo publish-build-info command.

The purpose is to enable crate authors to automatically report to potential crate users on the Rust version and platform compatibility of each version of the crate.

There's documentation in the cargo PR for doc.crates.io about how this feature is intended to work.

The badges, as described in that documentation, will display the latest version of the most stable channel that reported any pass results for the max version of that crate. Here's what that will look like, including the hover text:

Stable:

screen shot 2017-01-30 at 4 39 04 pm

Beta:

screen shot 2017-01-30 at 4 35 44 pm

Nightly:

screen shot 2017-01-30 at 4 40 53 pm

Each crate version page will display more detail, but not all of the possible information that collection supports. It's in a new section above the download stats graph as a table showing the most recent stable, beta, and nightly with any reported results for the Tier 1 platforms using 64 bit architectures:

screen shot 2017-06-08 at 11 53 40 am

There's also a new page with full information for the 64-bit tier 1 platforms, showing more detailed information:

screen shot 2017-06-08 at 1 06 56 pm

Some enhancements to the display that we've thought about but haven't implemented yet:

  • Have dropdowns for all the available versions for each channel
  • Have a dropdown to select from additional targets
  • Show icons or badges or colors instead of or in addition to the table

We also thought about, but didn't implement:

  • Ember tests for the UI
  • Adding a different API token that would only be valid for publishing build info
  • Showing the max build version badge on crate page in the sidebar

We're excited to hear what anyone thinks, and of course happy to answer questions or make modifications!

src/version.rs Outdated
build_info::table.primary_key(),
do_update()
.set((build_info::passed.eq(excluded(build_info::passed)),
build_info::updated_at.eq(now_utc().to_timespec())))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated_at is handled by database triggers everywhere else. Should we do the same here? (adding SELECT diesel_manage_updated_at('build_info'); to your migration will have Diesel set up the trigger automatically.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes, good point. We should be using the db triggers here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will the triggers also handle created_at?

src/version.rs Outdated
ChannelVersion::Beta(date) => beta.push(date),
ChannelVersion::Nightly(date) => nightly.push(date),
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think this amount of code is worth it just to avoid the one allocation of an intermediate vec? What do you think about this instead?

let rust_versions = build_infos.into_iter().map(|row| row.rust_version.parse::<ChannelVersion>()).collect::<Result<Vec<_>, _>>()?;
let nightly = rust_versions.iter().filter_map(|version| match *version {
    ChannelVersion::Nightly(date) => Some(date),
    _ => None,
}).max();
let beta = rust_versions.iter().filter_map(|version| match *version {
    ChannelVersion::Beta(date) => Some(date),
    _ => None,
}).max();
let stable = rust_versions.into_iter().filter_map(|version| match version {
    ChannelVersion::Stable(semver) => Some(semver),
    _ => None,
}).max();

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see which part of the proposed code only keeps the most recent value?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean by "most recent"? The code simply grabs the maximum of each one, does it not?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, there's a max call at the end of each of those. I'm not the biggest fan of that syntax for that reason. I see what you mean now though.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just to avoid the one allocation of an intermediate vec

To be honest, I've never really worked on mission-critical infrastructure supporting the entire ecosystem behind a language, so I don't really know when it's appropriate. From the comment, I assume you think it's not, so I'll defer to your greater experience.

src/version.rs Outdated
passed: bool,
}

pub const BUILD_INFO_FIELDS: (build_info::version_id, build_info::rust_version, build_info::target, build_info::passed) = (build_info::version_id, build_info::rust_version, build_info::target, build_info::passed);
Copy link
Contributor

@sgrif sgrif Jun 8, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we even need the created_at and updated_at fields if we aren't selecting them or using them for filtering anywhere?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to have them, I could see wanting to display them somewhere, but they're useful to me for auditing purposes.

src/version.rs Outdated
}
}

encodable_build_info.ordering.insert(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think using the ordermap crate instead of a hashmap could simplify this code?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we'd still want the "stable", "beta", "nightly" keys for the JSON... or am I misunderstanding? How would ordermap instead of a hashmap simplify this code?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We wouldn't need to maintain this ordering field, since OrderMap preserves insertion order. Even if we still need ordering for the front-end, it'd just be .keys().collect() to get the corresponding vec.

Though on second reading, that's not even what this code is doing. Since it's in a BTreeSet, it's just going to be sorting the strings in ascending order. Is that the desired behavior? It seems like we could just sort them when we're ready to display them in that case.

Copy link
Member Author

@shepmaster shepmaster Jul 27, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated the function with an example of the output as well as the rationale behind it. In short: no, I don't think the ordermap crate would help here.

As far as I understand it, JSON and JavaScript do not guarantee the order of objects. The ordering field exists to avoid requiring the frontend to understand how to sort by date and/or semver.

While OrderMap preserves insertion order, our iteration order over the returned rows is not guaranteed to be anything useful, therefore the insertion order is not valuable.

We aren't relying on the BTree part, just the Set part; we could just have easily used a HashSet. I only use BTree* stuff as a favor to @gankro, who told me that B-Trees are just all-around-cooler.

Additionally, the sets don't contain Strings — the stable one contains version numbers and the others two contain dates. We only convert to a string to serialize.

src/version.rs Outdated
/// Handles the `POST /crates/:crate_id/:version/build_info` route.
pub fn publish_build_info(req: &mut Request) -> CargoResult<Response> {
let mut body = String::new();
try!(req.body().read_to_string(&mut body));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

??

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?!

src/krate.rs Outdated
}

#[cfg_attr(feature = "lint", allow(too_many_arguments))]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think we should listen to this lint here, and start to refactor this out a bit? encodable feels like it's becoming a bit of a dumping ground.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do think we should refactor this, I am agnostic about whether it should be done in this PR or in a separate PR

src/version.rs Outdated
#[derive(Insertable, AsChangeset)]
#[table_name="build_info"]
#[primary_key(version_id, rust_version, target)]
struct NewBuildInfo {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we expect this table to be relatively stable, it might be worth just dumping this struct and adding #[derive(Insertable, AsChangeset)] onto BuildInfo.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds good.

@shepmaster
Copy link
Member Author

shepmaster commented Jun 15, 2017

I've rebased to address some conflicts and applied rustfmt to each commit. @sgrif I've added a commit that does your proposed max finding. What are your thoughts on it?

I'm still going to address these comments:

I just wanted to save them from being hidden

@shepmaster
Copy link
Member Author

@sgrif

function diesel_manage_updated_at(unknown) does not exist

hints?

@shepmaster shepmaster force-pushed the build-info-detailed-page branch 3 times, most recently from 5f8c398 to f86a363 Compare July 27, 2017 20:45
@shepmaster
Copy link
Member Author

Ah, diesel-rs/diesel#991

@shepmaster shepmaster force-pushed the build-info-detailed-page branch from f86a363 to 956b1a9 Compare July 28, 2017 13:55
@shepmaster
Copy link
Member Author

All talking points addressed. Ready for next round of review.

Copy link
Contributor

@vignesh-sankaran vignesh-sankaran left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we have doc comments added to all new public and private types?

src/upload.rs Outdated
@@ -55,6 +57,19 @@ pub struct CrateDependency {
pub kind: Option<DependencyKind>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct VersionBuildInfo {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we add a doc comment here?

src/version.rs Outdated
#[belongs_to(Version)]
#[table_name = "build_info"]
#[primary_key(version_id, rust_version, target)]
pub struct BuildInfo {
Copy link
Contributor

@vignesh-sankaran vignesh-sankaran Aug 1, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we have a doc comment here as well as for all structs, enums, and functions in this module?

If the latest version builds on any stable version, show that it
builds on stable. If not and it builds on any beta version, show
that. If not and it builds on any nightly version, show
that. Otherwise, don't show any badge.
@carols10cents carols10cents force-pushed the build-info-detailed-page branch from aacad02 to c492420 Compare February 6, 2018 17:14
@carols10cents
Copy link
Member

Ok, I've rebased and revived this old thing, in the start of an effort to actually get through all the open PRs.

I'd love reviews from:

  • @sgrif to make sure I updated the diesel parts properly
  • @jtgeibel to see if I put things in the right spots in the new MVC layout, there are a few things i'm not sure about
  • @shepmaster to make sure the original functionality we intended is still there

whenever yinz have time <3

@jtgeibel
Copy link
Member

jtgeibel commented Mar 1, 2018

@carols10cents I've finally gotten around to looking at this. In terms of MVC style, I have no issues.

I'm testing this PR in a staging area with the cargo PR as well. Here are a few things I've run into:

  • The crate.png image has been replaced with SVG, but the build-info page still references the PNG.
  • I've pushed build info for stable, beta, and nightly. I'm only seeing the information for stable.
    • At first I pushed nightly build info. This showed up as a badge on the browse page, but didn't show up on the crate's page.
    • Once I pushed stable build info, the summary showed up on the crate's page, but only for stable. The summary page and build-info details page are both missing information on beta and nightly. All the data is present in the API response, so I think this is a frontend issue.

For reference, the staging area is here.

carols10cents and others added 2 commits February 28, 2018 20:31
The NaiveDate type doesn't include any time or timezone information, so
I'm explicitly formatting as year-month-day ("%Y-%m-%d").
@jtgeibel
Copy link
Member

jtgeibel commented Mar 1, 2018

@carols10cents I've pushed a commit that should pass your new test. I'm explicitly formatting as "%Y-%m-%d" and tweaked the test a bit to match. I'm still reviewing but will probably r+ this weekend.

Copy link
Contributor

@sgrif sgrif left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rust side generally looks good to me. I'd like to take a second look at the parsing/encoding stuff when I'm less tired. I don't know enough about Ember to comment on that end.


/// `MaxBuildInfo` in JSON form.
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct EncodableMaxVersionBuildInfo {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Every field on MaxBuildVersionInfo implements Serialize, doesn't it? Is there a reason we need this as a separate struct?

/// The columns to select from the `build_info` table. The table also stores `created_at` and
/// `updated_at` metadata for each row, but we're not displaying those anywhere so we're not
/// bothering to select them.
pub const BUILD_INFO_FIELDS: (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are the created_at and updated_at fields ever used at all? Is it worth even having them in the first place?

/// Stores information about whether this version built on the specified Rust version and target.
pub struct BuildInfo {
version_id: i32,
pub rust_version: String,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this always a semver version? Is it worth pointing at dtolnay/semver#169 to make things easier here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sgrif currently this is storing the full version string, such as rustc 1.15.0-beta.5 (10893a9a3 2017-01-19). It may be better to parse this string when the build info is submitted, but I'm not sure how to best store the ParsedRustChannelVersion enum in the database. Maybe 3 columns, rust_channel (enum), rust_version (semver), and rust_date (date) where we have a semver or a date, but not both on any given row. Seems complicated, but I'd also prefer not to store a bunch of full rustc version strings.

pub enum ParsedRustChannelVersion {
    Stable(semver::Version),
    Beta(NaiveDate),
    Nightly(NaiveDate),
}

This should fix a regression introduced in this commit series.  We
should add more tests for the search route.
@jtgeibel jtgeibel force-pushed the build-info-detailed-page branch from baadd1d to 6c9794d Compare March 8, 2018 05:01
@vignesh-sankaran
Copy link
Contributor

@jtgeibel @shepmaster Did we still want to merge this in? This probably needs a rework given that the codebase has been refactored since the PR was opened.

@carols10cents
Copy link
Member

Ugh, yeah, I'm going to close.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants