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
41 changes: 41 additions & 0 deletions app/components/badge-build-info.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import Ember from 'ember';

import { formatDay } from 'cargo/helpers/format-day';

export default Ember.Component.extend({
tagName: 'span',
classNames: ['build_info'],

build_info: Ember.computed('crate.max_build_info_stable', 'crate.max_build_info_beta', 'crate.max_build_info_nightly', function() {
if (this.get('crate.max_build_info_stable')) {
return 'stable';
} else if (this.get('crate.max_build_info_beta')) {
return 'beta';
} else if (this.get('crate.max_build_info_nightly')) {
return 'nightly';
} else {
return null;
}
}),
color: Ember.computed('build_info', function() {
if (this.get('build_info') === 'stable') {
return 'brightgreen';
} else if (this.get('build_info') === 'beta') {
return 'yellow';
} else {
return 'orange';
}
}),
version_display: Ember.computed('build_info', 'crate.max_build_info_stable', 'crate.max_build_info_beta', 'crate.max_build_info_nightly', function() {
if (this.get('build_info') === 'stable') {
return this.get('crate.max_build_info_stable');
} else if (this.get('build_info') === 'beta') {
return formatDay(this.get('crate.max_build_info_beta'));
} else {
return formatDay(this.get('crate.max_build_info_nightly'));
}
}),
version_for_shields: Ember.computed('version_display', function() {
return this.get('version_display').replace(/-/g, '--');
}),
});
49 changes: 49 additions & 0 deletions app/controllers/crate/build-info.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import Ember from 'ember';

const { computed } = Ember;

function flattenBuildInfo(buildOrdering, builds) {
if (!buildOrdering || !builds) {
return [];
}

return buildOrdering.map(version => {
const thisVersion = builds[version];

return {
version,
'x86_64-apple-darwin': thisVersion['x86_64-apple-darwin'],
'x86_64-pc-windows-gnu': thisVersion['x86_64-pc-windows-gnu'],
'x86_64-pc-windows-msvc': thisVersion['x86_64-pc-windows-msvc'],
'x86_64-unknown-linux-gnu': thisVersion['x86_64-unknown-linux-gnu'],
};
});
}

export default Ember.Controller.extend({
id: computed.alias('model.crate.id'),
name: computed.alias('model.crate.name'),
version: computed.alias('model.num'),
build_info: computed.alias('model.build_info'),
stable_build: computed('build_info.ordering.stable', 'build_info.stable', function() {
const ordering = this.get('build_info.ordering.stable');
const stable = this.get('build_info.stable');

return flattenBuildInfo(ordering, stable);
}),
beta_build: computed('build_info.ordering.beta', 'build_info.beta', function() {
const ordering = this.get('build_info.ordering.beta');
const beta = this.get('build_info.beta');

return flattenBuildInfo(ordering, beta);
}),
nightly_build: computed('build_info.ordering.nightly', 'build_info.nightly', function() {
const ordering = this.get('build_info.ordering.nightly');
const nightly = this.get('build_info.nightly');

return flattenBuildInfo(ordering, nightly);
}),
has_stable_builds: computed.gt('stable_build.length', 0),
has_beta_builds: computed.gt('beta_build.length', 0),
has_nightly_builds: computed.gt('nightly_build.length', 0),
});
13 changes: 13 additions & 0 deletions app/helpers/build-info-pass-fail.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import Ember from 'ember';

export function buildInfoPassFail(status) {
if (status === true) {
return '✅ Pass';
} else if (status === false) {
return '❌ Fail';
} else {
return '';
}
}

export default Ember.Helper.helper(params => buildInfoPassFail(params[0]));
8 changes: 8 additions & 0 deletions app/helpers/format-day.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import Ember from 'ember';
import moment from 'moment';

export function formatDay(date) {
return date ? moment(date).utc().format('YYYY-MM-DD') : null;
}

export default Ember.Helper.helper(params => formatDay(params[0]));
10 changes: 10 additions & 0 deletions app/helpers/target-icons.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import Ember from 'ember';

export function targetIcons(targets) {
return Ember.String.htmlSafe(targets.map(function(target) {
const filename = target.split(' ')[0].toLowerCase();
return `<img src="/assets/${filename}.svg" alt="${target}" />`;
}).join(''));
}

export default Ember.Helper.helper(params => targetIcons(params[0]));
58 changes: 58 additions & 0 deletions app/models/build-info.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import DS from 'ember-data';
import Ember from 'ember';

const TIER1 = {
'x86_64-unknown-linux-gnu': 'Linux',
'x86_64-apple-darwin': 'macOS',
'x86_64-pc-windows-gnu': 'Windows (GNU)',
'x86_64-pc-windows-msvc': 'Windows (MSVC)',
};

const caseInsensitive = (a, b) => a.toLowerCase().localeCompare(b.toLowerCase());

export default DS.Model.extend({
version: DS.belongsTo('version', { async: true }),
ordering: DS.attr(),
stable: DS.attr(),
beta: DS.attr(),
nightly: DS.attr(),

latest_positive_results: Ember.computed('ordering', 'stable', 'beta', 'nightly', function() {
const passingTargets = results => (
Object.entries(results)
.filter(([, value]) => value === true)
.map(([key]) => TIER1[key])
.sort(caseInsensitive)
);

const positiveResults = (versionOrdering, channelResults) => {
const latestVersion = versionOrdering[versionOrdering.length - 1];
const latestResults = channelResults[latestVersion] || {};
return [latestVersion, passingTargets(latestResults)];
};

let results = {};

const addChannelToResults = (key) => {
const channelOrdering = this.get('ordering')[key];
const channelResults = this.get(key);

const [version, targets] = positiveResults(channelOrdering, channelResults);

if (targets.length > 0) {
results[key] = { version, targets };
}
};

addChannelToResults('stable');
addChannelToResults('beta');
addChannelToResults('nightly');

return results;
}),

has_any_positive_results: Ember.computed('latest_positive_results', function() {
const results = this.get('latest_positive_results');
return Object.keys(results).length > 0;
}),
});
3 changes: 3 additions & 0 deletions app/models/crate.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ export default DS.Model.extend({
documentation: DS.attr('string'),
repository: DS.attr('string'),
exact_match: DS.attr('boolean'),
max_build_info_nightly: DS.attr('date'),
max_build_info_beta: DS.attr('date'),
max_build_info_stable: DS.attr('string'),

versions: DS.hasMany('versions', { async: true }),
badges: DS.attr(),
Expand Down
1 change: 1 addition & 0 deletions app/models/version.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export default DS.Model.extend({
async: false
}),
authors: DS.hasMany('users', { async: true }),
build_info: DS.belongsTo('build-info', { async: true }),
dependencies: DS.hasMany('dependency', { async: true }),
version_downloads: DS.hasMany('version-download', { async: true }),

Expand Down
1 change: 1 addition & 0 deletions app/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Router.map(function() {
this.route('download');
this.route('versions');
this.route('version', { path: '/:version_num' });
this.route('build_info', { path: '/:version_num/build_info' });

this.route('reverse_dependencies');

Expand Down
24 changes: 24 additions & 0 deletions app/routes/crate/build_info.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import Ember from 'ember';

export default Ember.Route.extend({
model(params) {
const requestedVersion = params.version_num;
const crate = this.modelFor('crate');

// Find version model
return crate.get('versions')
.then(versions => {
const version = versions.find(version => version.get('num') === requestedVersion);
if (!version) {
this.controllerFor('application').set('nextFlashError',
`Version '${requestedVersion}' of crate '${crate.get('name')}' does not exist`);
}
return version;
});
},

serialize(model) {
let version_num = model ? model.get('num') : '';
return { version_num };
},
});
29 changes: 29 additions & 0 deletions app/styles/crate.scss
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,35 @@
}
}

.build-info {
.build-info-channel {
margin: 10px 0;
img {
width: 20px;
margin-bottom: -3px;
}
}
}

#crate-build-info {
padding-bottom: 50px;
border-bottom: 5px solid $gray-border;
margin-bottom: 30px;

.description {
margin-bottom: 30px;
}

table {
border: 1px solid $gray-border;
td, th {
border: 1px solid $gray-border;
padding: 5px 10px;
text-align: left;
}
}
}

#crate-downloads {
@include display-flex;
@include flex-wrap(wrap);
Expand Down
6 changes: 6 additions & 0 deletions app/templates/components/badge-build-info.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{{#if build_info}}
<img
src="https://img.shields.io/badge/builds_on-{{ version_for_shields }}-{{color}}.svg"
alt="Known to build on {{ build_info }} {{ version_display }}"
title="Known to build on {{ build_info }} {{ version_display }}" />
{{/if}}
1 change: 1 addition & 0 deletions app/templates/components/crate-row.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
{{#each crate.annotated_badges as |badge|}}
{{component badge.component_name badge=badge data-test-badge=badge.badge_type}}
{{/each}}
{{badge-build-info crate=crate}}
</div>
<div class='summary' data-test-description>
<span class='small'>
Expand Down
83 changes: 83 additions & 0 deletions app/templates/crate/build-info.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
{{title name}}

<div class='all-versions-back'>
{{#link-to 'crate' id}}&#11013; Back to {{ name }}{{/link-to}}
</div>

<div id='crates-heading'>
<div class="wide">
<div class='info'>
<img class='logo crate' src="/assets/crate.svg"/>
<h1>Build info for {{ name }}</h1>
<h2>{{ version }}</h2>
</div>
</div>
</div>

<div id='crate-build-info'>
{{#if has_stable_builds}}
<h3>Stable channel</h3>
<table>
<tr>
<th>Rust Version</th>
<th>Linux 64 bit</th>
<th>macOS 64 bit</th>
<th>Windows (GNU) 64 bit</th>
<th>Windows (MSVC) 64 bit</th>
</tr>
{{#each stable_build as |build|}}
<tr>
<td>{{ build.version }}</td>
<td>{{ build-info-pass-fail build.x86_64-unknown-linux-gnu }}</td>
<td>{{ build-info-pass-fail build.x86_64-apple-darwin }}</td>
<td>{{ build-info-pass-fail build.x86_64-pc-windows-gnu }}</td>
<td>{{ build-info-pass-fail build.x86_64-pc-windows-msvc }}</td>
</tr>
{{/each}}
</table>
{{/if}}

{{#if has_beta_builds}}
<h3>Beta channel</h3>
<table>
<tr>
<th>Rust Version</th>
<th>Linux 64 bit</th>
<th>macOS 64 bit</th>
<th>Windows (GNU) 64 bit</th>
<th>Windows (MSVC) 64 bit</th>
</tr>
{{#each beta_build as |build|}}
<tr>
<td>{{ format-day build.version }}</td>
<td>{{ build-info-pass-fail build.x86_64-unknown-linux-gnu }}</td>
<td>{{ build-info-pass-fail build.x86_64-apple-darwin }}</td>
<td>{{ build-info-pass-fail build.x86_64-pc-windows-gnu }}</td>
<td>{{ build-info-pass-fail build.x86_64-pc-windows-msvc }}</td>
</tr>
{{/each}}
</table>
{{/if}}

{{#if has_nightly_builds}}
<h3>Nightly channel</h3>
<table>
<tr>
<th>Rust Version</th>
<th>Linux 64 bit</th>
<th>macOS 64 bit</th>
<th>Windows (GNU) 64 bit</th>
<th>Windows (MSVC) 64 bit</th>
</tr>
{{#each nightly_build as |build|}}
<tr>
<td>{{ format-day build.version }}</td>
<td>{{ build-info-pass-fail build.x86_64-unknown-linux-gnu }}</td>
<td>{{ build-info-pass-fail build.x86_64-apple-darwin }}</td>
<td>{{ build-info-pass-fail build.x86_64-pc-windows-gnu }}</td>
<td>{{ build-info-pass-fail build.x86_64-pc-windows-msvc }}</td>
</tr>
{{/each}}
</table>
{{/if}}
</div>
Loading