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

JENKINS-40979 add trigger cause messages #996

Merged
merged 19 commits into from
Apr 27, 2017
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import hudson.ExtensionList;
import hudson.PluginWrapper;
import hudson.model.Action;
import hudson.model.Item;
import hudson.model.Run;
import io.jenkins.blueocean.commons.stapler.export.DataWriter;
import io.jenkins.blueocean.commons.stapler.export.ExportConfig;
import io.jenkins.blueocean.commons.stapler.export.ExportInterceptor;
Expand Down Expand Up @@ -158,6 +160,9 @@ public Object getValue(Property property, Object model, ExportConfig config) thr
printError(model.getClass(), e);
return SKIP;
}
} else if (model instanceof Item || model instanceof Run) {
Copy link
Contributor Author

@i386 i386 Apr 23, 2017

Choose a reason for hiding this comment

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

@vivek I have skipped serialisation of Item and Run objects in case they are returned by a Cause object. These are evil for performance reasons anyway. I have checked implementations in core and I can't see any that do this but I've added it to be on the safe side (who knows what plugins will do).

Copy link
Collaborator

Choose a reason for hiding this comment

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

Its a hack but should be fine until we find something else pulling in something else during serialization thats not item or run. We can revisit if its trouble later on.

// We should skip any models that are Jenkins Item or Run objects as these are known to be evil
return SKIP;
}
return ExportInterceptor.DEFAULT.getValue(property, model, config);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public TreePruner accept(Object node, Property prop) {

// for merge properties, the current restrictions on the property names should
// still apply to the child TreePruner
if (prop.merge)
if (prop.merge && child != null)
Copy link
Contributor Author

@i386 i386 Apr 23, 2017

Choose a reason for hiding this comment

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

@vivek Use of merge in the 'Cause' @Exported(name="cause", merge = true) caused this NPE. This check fixes it but perhaps this should go upstream?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah, please open a PR in stapler with this fix.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Collaborator

Choose a reason for hiding this comment

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

👍

child = new FilteringTreePruner(predicate,child);

return child;
Expand Down
19 changes: 10 additions & 9 deletions blueocean-dashboard/src/main/js/components/Branches.jsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
import React, { Component, PropTypes } from 'react';
import {
CommitHash,
ReadableDate,
WeatherIcon,
} from '@jenkins-cd/design-language';
import { RunButton, LiveStatusIndicator } from '@jenkins-cd/blueocean-core-js';
import { CommitHash, ReadableDate, WeatherIcon } from '@jenkins-cd/design-language';
import { LiveStatusIndicator, RunButton } from '@jenkins-cd/blueocean-core-js';
import Extensions from '@jenkins-cd/js-extensions';
import { observer } from 'mobx-react';
import { CellRow, CellLink } from './CellLink';
import { CellLink, CellRow } from './CellLink';

import { buildRunDetailsUrl } from '../util/UrlUtils';

Expand Down Expand Up @@ -73,7 +69,12 @@ export default class Branches extends Component {
const cleanBranchName = decodeURIComponent(branch.name);
const runDetailsUrl = buildRunDetailsUrl(branch.organization, pipeline.fullName, cleanBranchName, latestRun.id, 'pipeline');

const { msg } = (latestRun.changeSet && latestRun.changeSet.length > 0) ? (latestRun.changeSet[latestRun.changeSet.length - 1] || {}) : {};
// If there is no changeset, show the first cause otherwise show nothing (-)
const message = latestRun.changeSet && latestRun.changeSet.length > 0
&& latestRun.changeSet[latestRun.changeSet.length - 1].msg
|| (latestRun.causes.length > 0 && latestRun.causes[0].shortDescription)
|| '-';

return (
<CellRow linkUrl={runDetailsUrl} id={`${cleanBranchName}-${latestRun.id}`}>
<CellLink disableDefaultPadding>
Expand All @@ -89,7 +90,7 @@ export default class Branches extends Component {
</CellLink>
<CellLink>{cleanBranchName}</CellLink>
<CellLink><CommitHash commitId={latestRun.commitId} /></CellLink>
<CellLink>{msg || '-'}</CellLink>
<CellLink><span className="message">{message}</span></CellLink>
<CellLink>
<ReadableDate
date={latestRun.endTime}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import React, { Component, PropTypes } from 'react';
import { Icon } from '@jenkins-cd/react-material-icons';
import { logging, TimeManager, AppConfig } from '@jenkins-cd/blueocean-core-js';
import { AppConfig, logging, ResultPageHeader, TimeManager } from '@jenkins-cd/blueocean-core-js';
import { ExpandablePath, ReadableDate, TimeDuration } from '@jenkins-cd/design-language';
import ChangeSetToAuthors from './ChangeSetToAuthors';
import { ResultPageHeader } from '@jenkins-cd/blueocean-core-js';
import { Link } from 'react-router';
import { buildPipelineUrl } from '../util/UrlUtils';

Expand All @@ -28,7 +27,7 @@ class RunDetailsHeader extends Component {
}, skewMillis);
this.durationMillis = durationMillis;
}

render() {
const {
data: run,
Expand Down Expand Up @@ -152,6 +151,9 @@ class RunDetailsHeader extends Component {
</div>
);

const causeMessage = (run && run.causes.length > 0 && run.causes[0].shortDescription) || null;
const cause = (<div className="causes">{causeMessage}</div>);

return (
<ResultPageHeader startTime={ startTime }
estimatedDurationInMillis={ estimatedDurationInMillis }
Expand All @@ -170,8 +172,9 @@ class RunDetailsHeader extends Component {
{ durationDetails }
{ endTimeDetails }
</div>
<div className="RunDetailsHeader-authors">
<div className="RunDetailsHeader-messages">
<ChangeSetToAuthors changeSet={ changeSet } onAuthorsClick={ onAuthorsClick } t={ t } />
{ cause }
</div>
</ResultPageHeader>
);
Expand Down
18 changes: 12 additions & 6 deletions blueocean-dashboard/src/main/js/components/Runs.jsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import React, { Component, PropTypes } from 'react';
import { CommitHash, ReadableDate, TimeDuration } from '@jenkins-cd/design-language';
import {
CommitHash, ReadableDate, TimeDuration,
}
from '@jenkins-cd/design-language';
import { logging, ReplayButton, RunButton, LiveStatusIndicator, TimeHarmonizer as timeHarmonizer } from '@jenkins-cd/blueocean-core-js';
LiveStatusIndicator,
logging,
ReplayButton,
RunButton,
TimeHarmonizer as timeHarmonizer
} from '@jenkins-cd/blueocean-core-js';
import Extensions from '@jenkins-cd/js-extensions';

import { MULTIBRANCH_PIPELINE, SIMPLE_PIPELINE } from '../Capabilities';
import { buildRunDetailsUrl } from '../util/UrlUtils';
import IfCapability from './IfCapability';
import { CellRow, CellLink } from './CellLink';
import { CellLink, CellRow } from './CellLink';

const logger = logging.logger('io.jenkins.blueocean.dashboard.Runs');
/*
Expand Down Expand Up @@ -57,6 +60,9 @@ export class Runs extends Component {
router.push(location);
};

// If there is no changeset, show the first cause otherwise show nothing (-)
const message = changeset && changeset.msg || (run.causes.length > 0 && run.causes[0].shortDescription) || '-';

return (
<CellRow id={`${pipeline.name}-${run.id}`} linkUrl={runDetailsUrl}>
<CellLink>
Expand All @@ -72,7 +78,7 @@ export class Runs extends Component {
<IfCapability className={pipeline._class} capability={MULTIBRANCH_PIPELINE} >
<CellLink linkUrl={runDetailsUrl}>{decodeURIComponent(run.pipeline)}</CellLink>
</IfCapability>
<CellLink>{changeset && changeset.msg || '-'}</CellLink>
<CellLink>{message}</CellLink>
<CellLink>
<TimeDuration
millis={durationMillis}
Expand Down
1 change: 1 addition & 0 deletions blueocean-dashboard/src/main/js/components/records.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export class RunRecord extends Record({
_capabilities: [],
_links: null,
changeSet: ChangeSetRecord,
causes: [],
causeOfBlockage: null,
artifacts: null,
durationInMillis: null,
Expand Down
6 changes: 3 additions & 3 deletions blueocean-dashboard/src/main/less/run-details-header.less
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
.RunDetailsHeader-title,
.RunDetailsHeader-sources,
.RunDetailsHeader-authors,
.RunDetailsHeader-messages,
.RunDetailsHeader-times {
a {
cursor: pointer;
}
}

.RunDetailsHeader-sources,
.RunDetailsHeader-authors,
.RunDetailsHeader-messages,
.RunDetailsHeader-times
{
height:100%;
Expand Down Expand Up @@ -66,7 +66,7 @@
}
}

.RunDetailsHeader-authors {
.RunDetailsHeader-messages {
flex-grow: 1;
}

Expand Down
102 changes: 56 additions & 46 deletions blueocean-dashboard/src/test/js/activity-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { shallow } from 'enzyme';
import { Activity } from '../../main/js/components/Activity.jsx';
import { CapabilityRecord } from '../../main/js/components/Capability.jsx';

import { pipelines } from './data/pipelines/pipelinesSingle';


const
data = [
Expand All @@ -21,7 +23,8 @@ const
"startTime": "2016-03-04T13:59:53.374+0100",
"state": "FINISHED",
"type": "WorkflowRun",
"commitId": "444196ac6afbd3e417f1d46ebfb3c4f0aac0c165"
"commitId": "444196ac6afbd3e417f1d46ebfb3c4f0aac0c165",
"causes": [{"_class": "jenkins.branch.BranchIndexingCause", "shortDescription": "Branch indexing"}],
},
{
"changeSet": [],
Expand All @@ -36,7 +39,8 @@ const
"startTime": "2016-03-04T13:59:54.138+0100",
"state": "FINISHED",
"type": "WorkflowRun",
"commitId": "f8eeb35c03e52c17c27824fa77fa6b0f03a93625"
"commitId": "f8eeb35c03e52c17c27824fa77fa6b0f03a93625",
"causes": [{"_class": "jenkins.branch.BranchIndexingCause", "shortDescription": "Branch indexing"}],
},
{
"changeSet": [
Expand Down Expand Up @@ -74,7 +78,8 @@ const
"startTime": "2016-03-04T14:18:55.490+0100",
"state": "FINISHED",
"type": "WorkflowRun",
"commitId": "746cf27525b7b1d615de408ca86786613ccf7548"
"commitId": "746cf27525b7b1d615de408ca86786613ccf7548",
"causes": [{"_class": "jenkins.branch.BranchIndexingCause", "shortDescription": "Branch indexing"}],
},
{
"changeSet": [
Expand Down Expand Up @@ -112,7 +117,8 @@ const
"startTime": "2016-03-04T14:28:09.322+0100",
"state": "FINISHED",
"type": "WorkflowRun",
"commitId": "a2f0801fec8bad98663f0df5e9110261820e8c4e"
"commitId": "a2f0801fec8bad98663f0df5e9110261820e8c4e",
"causes": [{"_class": "jenkins.branch.BranchIndexingCause", "shortDescription": "Branch indexing"}],
},
{
"changeSet": [],
Expand All @@ -127,73 +133,77 @@ const
"startTime": "2016-03-09T16:01:23.767+0100",
"state": "FINISHED",
"type": "WorkflowRun",
"commitId": "a2f0801fec8bad98663f0df5e9110261820e8c4e"
"commitId": "a2f0801fec8bad98663f0df5e9110261820e8c4e",
"causes": [{"_class": "jenkins.branch.BranchIndexingCause", "shortDescription": "Branch indexing"}],
}
];

const context = {
params: {},
config: {
getServerBrowserTimeSkewMillis: () => 0
},
activityService: {
params: {},
config: {
getServerBrowserTimeSkewMillis: () => 0,
},
activityService: {
activityPager() {
return {
data: data
}
}
}
return { data: data };
},
},
};

const contextNoData = {
params: {},
config: {
getServerBrowserTimeSkewMillis: () => 0
},
activityService: {
params: {},
config: {
getServerBrowserTimeSkewMillis: () => 0,
},
activityService: {
activityPager() {
return {
data: [],
pending: true,
}
}
}
};
const pipeline = {
_class: "some.class"
return {
data: [],
pending: true,
};
},
},
};
const pipeline = pipelines[0];

const capabilities = {
'some.class': new CapabilityRecord({})
'some.class': new CapabilityRecord({}),
};
data.$success = true; // fetch flag

const t = () => {};

describe("Activity", () => {

it("render the Activity with data", () => {
const wrapper = shallow(<Activity t={ ()=>{} } runs={data} pipeline={pipeline} capabilities={capabilities}/>, { context });

// does data renders?
assert.isNotNull(wrapper);
assert.equal(wrapper.find('NewComponent').length, data.length)
});
describe('Activity', () => {
it('render the Activity with data', () => {
const wrapper = shallow(<Activity t={t} runs={data} pipeline={pipeline} capabilities={capabilities} />, { context });

it("does not render without data", () => {
const wrapper = shallow(<Activity pipeline={pipeline} t={ ()=>{} } capabilities={capabilities}/>, { context: contextNoData});
assert.equal(wrapper.find('NewComponent').length, 0)
// does data renders?
assert.isNotNull(wrapper);
assert.equal(wrapper.find('NewComponent').length, data.length);
});

});
it('does not render without data', () => {
const wrapper = shallow(<Activity pipeline={pipeline} t={ () => {} } capabilities={capabilities} />, { context: contextNoData });
assert.equal(wrapper.find('NewComponent').length, 0);
});
});

describe('Pipeline -> Activity List', () => {
it('should not duplicate changeset messages', () => {
it('should contain cause', () => {
const wrapper = shallow(<Activity t={t} runs={data} pipeline={pipeline} capabilities={capabilities} />, { context });
assert.isNotNull(wrapper);
const runs = wrapper.find('NewComponent');
assert.isNotNull(runs);
const run1 = runs.at(0).html(); // has cause
const run3 = runs.at(2).html(); // has changeset

const wrapper = shallow(<Activity t={ ()=>{} } runs={data} pipeline={pipeline} capabilities={capabilities} />, { context });
assert(run1.indexOf('Branch indexing') >= 0, 'should have cause message');
assert(run3.indexOf('Update Jenkinsfile') >= 0, 'should have changset message');
});

it('should not duplicate changeset messages', () => {
const wrapper = shallow(<Activity t={t} runs={data} pipeline={pipeline} capabilities={capabilities} />, { context });
assert.isNotNull(wrapper);

const runs = wrapper.find('NewComponent');
assert.isNotNull(runs);

Expand Down
Loading