Compare commits

...

35 Commits

Author SHA1 Message Date
kzantow 7f9cf649ef Update pom 2016-07-26 01:57:36 +09:00
kzantow 4264c72112 Fix lint issues 2016-07-26 01:48:07 +09:00
kzantow 183c83a4fa Merge remote-tracking branch 'primary/master' into first-class-project-types-prototype 2016-07-25 11:26:44 -04:00
kzantow 772540946f Tweak 2016-07-20 19:29:29 -07:00
kzantow 4083806d30 Merge branch 'first-class-project-types-prototype' into extensibility-improvements 2016-07-20 14:34:44 -07:00
kzantow 944ad55dfc Merge remote-tracking branch 'primary/master' into first-class-project-types-prototype 2016-07-19 17:26:49 -07:00
kzantow e7ba99feb7 Merge remote-tracking branch 'primary/master' into first-class-project-types-prototype 2016-07-19 15:30:52 -07:00
kzantow 15d0254bce Merge branch 'san-jose-hackfest' into first-class-project-types-prototype 2016-07-18 16:34:28 -07:00
kzantow a2fadf15c5 Run details tabs extensible 2016-07-19 08:32:37 +09:00
kzantow 13c91c75a1 Move individual run tabs to extension point 2016-07-19 07:36:13 +09:00
kzantow afd9abd767 Fix the data reference for RunDetailsPipeline 2016-07-19 07:30:28 +09:00
Cliff Meyers 37d4ba3b24 add TODO 2016-07-18 14:37:12 -07:00
Cliff Meyers 250da96ca5 fix: default route for run detail redirects to 'pipeline' 2016-07-18 14:24:32 -07:00
Cliff Meyers cc5a1a09f4 fix: get top-level route and org/orgName route to redirect to 'pipelines' route 2016-07-18 14:23:43 -07:00
Cliff Meyers 7459fc3975 fix: get /pipelines to render 2016-07-18 14:22:49 -07:00
kzantow dc9139ae39 Add extension point for run details 2016-07-18 14:13:34 -07:00
kzantow a856e6b422 Dynamic routes working! 2016-07-18 13:17:09 -07:00
kzantow 20e5fd31d2 Almost route working 2016-07-18 13:12:51 -07:00
kzantow 56d0816844 Migrate react router to obejcts 2016-07-18 13:04:52 -07:00
kzantow d90eb68572 Update JDL 2016-07-18 10:03:20 -07:00
kzantow e063944582 Try dynamic routes 2016-07-18 09:13:37 -07:00
kzantow b566f3d82c Dynamic page elements based on top-level types PoC 2016-07-18 10:00:08 +09:00
kzantow 2a808b2295 Merge remote-tracking branch 'primary/master' into JENKINS-35860-extension-point-types 2016-07-17 10:48:33 -06:00
kzantow 479bc8e994 Merge remote-tracking branch 'primary/master' into JENKINS-35860-extension-point-types 2016-07-15 10:09:00 -04:00
kzantow fdbe5a664f Separate componentType to its own file 2016-07-15 22:42:23 +09:00
kzantow 3397771a85 Burned through some npm versions to get this published with a tag, d'oh 2016-07-12 08:23:04 +09:00
kzantow a91b41e01d Update js-extensions version 2016-07-11 18:54:00 -04:00
kzantow 898323353f Split ClassMetadataStore, modify extension filtering to a common method 2016-07-12 01:27:47 +09:00
kzantow d09af3e650 Merge remote-tracking branch 'primary/master' into JENKINS-35860-extension-point-types 2016-07-11 11:02:09 -04:00
kzantow 7e21edafef Merge remote-tracking branch 'primary/master' into JENKINS-35860-extension-point-types 2016-07-06 22:46:10 -04:00
kzantow c24dfaf8c6 Address @tfennelly's comments:
* EXTENSIONS -> README
* more documentation for dataType vs. componentType
* modify usage of 'type' to 'dataType' for consistency
2016-07-05 23:47:21 +09:00
kzantow 4ebf1cfebb Merge remote-tracking branch 'primary/master' into JENKINS-35860-extension-point-types 2016-07-05 09:13:16 -04:00
kzantow f41a636492 Bump js-extensions beta version 2016-06-30 15:12:18 -04:00
kzantow 8d3c0e0893 Merge remote-tracking branch 'primary/master' into JENKINS-35860-extension-point-types 2016-06-30 15:11:17 -04:00
kzantow 7a4cc9c2bc Add component type filter for extensionPoints 2016-06-30 15:01:51 -04:00
50 changed files with 828 additions and 54 deletions

View File

@ -36,7 +36,7 @@
},
"dependencies": {
"@jenkins-cd/design-language": "0.0.64",
"@jenkins-cd/js-extensions": "0.0.19",
"@jenkins-cd/js-extensions": "0.0.20-beta-1",
"@jenkins-cd/js-modules": "0.0.5",
"@jenkins-cd/sse-gateway": "0.0.6",
"immutable": "3.8.1",

View File

@ -1,4 +1,4 @@
import { Route, Redirect, IndexRoute, IndexRedirect } from 'react-router';
import React from 'react';
import Dashboard from './Dashboard';
import OrganizationPipelines from './OrganizationPipelines';
@ -14,34 +14,60 @@ import {
RunDetailsArtifacts,
RunDetailsTests,
} from './components';
import Extensions from '@jenkins-cd/js-extensions';
const DynamicRoutes = {
path: ':pipeline/(.*)',
getChildRoutes(partialNextState, callback) {
Extensions.store.getExtensions('pipeline.routes', routes => {
callback(null, routes); // routes is array
});
},
};
export default (
<Route path="/" component={Dashboard}>
<Route path="organizations/:organization" component={OrganizationPipelines}>
<IndexRedirect to="pipelines" />
<Route path="pipelines" component={Pipelines} />
{ path: '', component: Dashboard, indexRoute: {
onEnter: ({ params }, replace) => replace('pipelines'),
},
childRoutes: [
{ path: 'organizations/:organization', component: OrganizationPipelines,
indexRoute: { onEnter: ({ params }, replace) => replace(`/organizations/${params.organization}/pipelines`) },
childRoutes: [
{ path: 'pipelines', component: Pipelines },
<Route component={PipelinePage}>
<Route path=":pipeline/branches" component={MultiBranch} />
<Route path=":pipeline/activity" component={Activity} />
<Route path=":pipeline/pr" component={PullRequests} />
{ component: PipelinePage, childRoutes: [
{ path: ':pipeline/branches', component: MultiBranch },
{ path: ':pipeline/activity', component: Activity },
{ path: ':pipeline/pr', component: PullRequests },
<Route path=":pipeline/detail/:branch/:runId" component={RunDetails}>
<IndexRedirect to="pipeline" />
<Route path="pipeline" component={RunDetailsPipeline} >
<Route path=":node" component={RunDetailsPipeline} />
</Route>
<Route path="changes" component={RunDetailsChanges} />
<Route path="tests" component={RunDetailsTests} />
<Route path="artifacts" component={RunDetailsArtifacts} />
</Route>
{ path: ':pipeline/detail/:branch/:runId', component: RunDetails,
indexRoute: {
onEnter: ({ params }, replace) => replace(
`/organizations/${params.organization}/${encodeURIComponent(params.pipeline)}/` +
`detail/${params.branch}/${params.runId}/pipeline`
),
},
childRoutes: [
{ path: 'pipeline', component: RunDetailsPipeline, childRoutes: [
{ path: ':node', component: RunDetailsPipeline },
] },
{ path: 'changes', component: RunDetailsChanges },
{ path: 'tests', component: RunDetailsTests },
{ path: 'artifacts', component: RunDetailsArtifacts },
],
},
<Redirect from=":pipeline(/*)" to=":pipeline/activity" />
</Route>
</Route>
<Route path="/pipelines" component={OrganizationPipelines}>
<IndexRoute component={Pipelines} />
</Route>
<IndexRedirect to="pipelines" />
</Route>
DynamicRoutes,
// TODO: need to convert this to onEnter - somehow?
//<Redirect from=":pipeline(/*)" to=":pipeline/activity" />
],
},
],
},
{ path: '/pipelines', component: OrganizationPipelines, indexRoute: {
component: Pipelines,
} },
],
}
);

View File

@ -1,4 +1,5 @@
import React, { Component, PropTypes } from 'react';
import Extensions, { dataType } from '@jenkins-cd/js-extensions';
import { Link } from 'react-router';
import { isFailure, isPending } from '../util/FetchStatus';
import NotFound from './NotFound';
@ -7,7 +8,6 @@ import {
PageHeader,
Title,
PageTabs,
TabLink,
WeatherIcon,
Favorite,
} from '@jenkins-cd/design-language';
@ -47,9 +47,7 @@ export default class PipelinePage extends Component {
<Favorite className="dark-yellow" />
</Title>
<PageTabs base={baseUrl}>
<TabLink to="/activity">Activity</TabLink>
<TabLink to="/branches">Branches</TabLink>
<TabLink to="/pr">Pull Requests</TabLink>
<Extensions.Renderer extensionPoint="pipeline.main.navigation" filter={dataType(pipeline._jobClass)} pipeline={pipeline} baseLink={baseUrl} />
</PageTabs>
</PageHeader>
{React.cloneElement(this.props.children, { pipeline })}

View File

@ -5,8 +5,8 @@ import {
ModalHeader,
PipelineResult,
PageTabs,
TabLink,
} from '@jenkins-cd/design-language';
import Extensions, { dataType } from '@jenkins-cd/js-extensions';
import {
actions,
@ -119,10 +119,7 @@ class RunDetails extends Component {
onAuthorsClick={() => this.navigateToChanges()}
/>
<PageTabs base={baseUrl}>
<TabLink to="/pipeline">Pipeline</TabLink>
<TabLink to="/changes">Changes</TabLink>
<TabLink to="/tests">Tests</TabLink>
<TabLink to="/artifacts">Artifacts</TabLink>
<Extensions.Renderer extensionPoint="rundetails.main.navigation" filter={dataType(currentRun)} currentRun={currentRun} baseLink={baseUrl} />
</PageTabs>
</div>
</ModalHeader>

View File

@ -268,6 +268,11 @@ export class RunDetailsPipeline extends Component {
const noSteps = !log && currentSteps && currentSteps.model && currentSteps.model.length === 0;
const shouldShowLogHeader = log !== null || !noSteps;
return (
<div>
<Extensions.Renderer
extensionPoint="jenkins.pipeline.run.details"
currentRun={this.props.result}
/>
<div ref="scrollArea">
{ nodes && nodes[nodeKey] && <Extensions.Renderer
extensionPoint="jenkins.pipeline.run.result"
@ -300,6 +305,7 @@ export class RunDetailsPipeline extends Component {
{ log && <LogConsole key={logGeneral.url} logArray={log.logArray} scrollToBottom={scrollToBottom} /> }
</div>
</div>
);
}
}

View File

@ -2,7 +2,7 @@ import React, { Component, PropTypes } from 'react';
import { EmptyStateView } from '@jenkins-cd/design-language';
import { actions as selectorActions, testResults as testResultsSelector,
connect, createSelector } from '../redux';
import Extensions, { dataType } from '@jenkins-cd/js-extensions';
import Extensions, { mostSpecificDataType } from '@jenkins-cd/js-extensions';
const EmptyState = () => (
<EmptyStateView tightSpacing>
@ -58,7 +58,7 @@ export class RunDetailsTests extends Component {
<div className="test-result-duration">Duration {testResults.duration}</div>
</div>
<Extensions.Renderer extensionPoint="jenkins.test.result" filter={dataType(testResults)} testResults={testResults} />
<Extensions.Renderer extensionPoint="jenkins.test.result" filter={mostSpecificDataType(testResults)} testResults={testResults} />
</div>);
}
}

View File

@ -0,0 +1,15 @@
import React from 'react';
import { TabLink } from '@jenkins-cd/design-language';
export default class RunDetailsArtifactsTab extends React.Component {
render() {
return (
<TabLink to={`${this.props.baseLink}/artifacts`}>Artifacts</TabLink>
);
}
}
RunDetailsArtifactsTab.propTypes = {
pipeline: React.PropTypes.any,
baseLink: React.PropTypes.string,
};

View File

@ -0,0 +1,19 @@
import React from 'react';
import { TabLink } from '@jenkins-cd/design-language';
export default class RunDetailsTabs extends React.Component {
render() {
return (
<span>
<TabLink to={`${this.props.baseLink}/pipeline`}>Pipeline</TabLink>
<TabLink to={`${this.props.baseLink}/changes`}>Changes</TabLink>
<TabLink to={`${this.props.baseLink}/tests`}>Tests</TabLink>
</span>
);
}
}
RunDetailsTabs.propTypes = {
pipeline: React.PropTypes.any,
baseLink: React.PropTypes.string,
};

View File

@ -0,0 +1,13 @@
import React from 'react';
import { TabLink } from '@jenkins-cd/design-language';
export default class PipelineActivityTab extends React.Component {
render() {
return <TabLink to={`${this.props.baseLink}/activity`}>Activity</TabLink>;
}
}
PipelineActivityTab.propTypes = {
pipeline: React.PropTypes.any,
baseLink: React.PropTypes.string,
};

View File

@ -0,0 +1,13 @@
import React from 'react';
import { TabLink } from '@jenkins-cd/design-language';
export default class PipelineBranchesTab extends React.Component {
render() {
return <TabLink to={`${this.props.baseLink}/branches`}>Branches</TabLink>;
}
}
PipelineBranchesTab.propTypes = {
pipeline: React.PropTypes.any,
baseLink: React.PropTypes.string,
};

View File

@ -0,0 +1,13 @@
import React from 'react';
import { TabLink } from '@jenkins-cd/design-language';
export default class PipelinePullRequestsTab extends React.Component {
render() {
return <TabLink to={`${this.props.baseLink}/pr`}>Pull Requests</TabLink>;
}
}
PipelinePullRequestsTab.propTypes = {
pipeline: React.PropTypes.any,
baseLink: React.PropTypes.string,
};

View File

@ -10,6 +10,24 @@ extensions:
extensionPoint: jenkins.main.stores
- component: components/PipelineRunGraph
extensionPoint: jenkins.pipeline.run.result
- component: components/tabs/PipelineActivityTab
extensionPoint: pipeline.main.navigation
dataType: hudson.model.Job
- component: components/tabs/PipelineBranchesTab
extensionPoint: pipeline.main.navigation
dataType: org.jenkinsci.plugins.workflow.job.WorkflowJob
- component: components/tabs/PipelinePullRequestsTab
extensionPoint: pipeline.main.navigation
dataType: org.jenkinsci.plugins.workflow.job.WorkflowJob
- component: components/tabs/PipelineActivityTab
extensionPoint: pipeline.main.navigation
dataType: jenkins.branch.MultiBranchProject
- component: components/tabs/PipelineBranchesTab
extensionPoint: pipeline.main.navigation
dataType: jenkins.branch.MultiBranchProject
- component: components/tabs/PipelinePullRequestsTab
extensionPoint: pipeline.main.navigation
dataType: jenkins.branch.MultiBranchProject
- component: components/testing/TestResults
extensionPoint: jenkins.test.result
dataType: hudson.tasks.test.TestResult

View File

@ -1,5 +1,6 @@
@import "variables";
@import "core";
@import "page-tabs";
@import "testing";
@import "run-pipeline";

View File

@ -0,0 +1,5 @@
.page-tabs {
> div, > div > div {
display: inline-block;
}
}

View File

@ -0,0 +1,3 @@
{
"presets": ["es2015", "stage-0", "react"]
}

View File

@ -0,0 +1,10 @@
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 4
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
max_line_length = 160

View File

@ -0,0 +1,8 @@
{
"extends": "@jenkins-cd/jenkins/react",
"rules": {
"react/jsx-no-bind": 0,
"no-unused-vars": [2, {"varsIgnorePattern": "^React$"}],
"max-len": [1, 160, 4]
}
}

View File

@ -0,0 +1,7 @@
import { configure } from '@kadira/storybook';
function loadStories() {
require('../src/main/js/stories/index');
}
configure(loadStories, module);

View File

@ -0,0 +1,16 @@
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Reset -->
<link href="/css/normalize.css" rel="stylesheet">
<!-- Icons -->
<link href="/css/latofonts.css" rel="stylesheet">
<link href="/octicons/octicons.css" rel="stylesheet">
<!-- Jenkins theme -->
<link href="/css/jenkins-design-language.css" rel="stylesheet">
<!-- styles -->
<link href="/extensions.css" rel="stylesheet">

View File

@ -0,0 +1,11 @@
const path = require('path');
module.exports = {
module: {},
resolve: {
extensions: [
'.js', // required by storybook
'', '.jsx' // for blueocean files
],
}
};

View File

@ -0,0 +1,21 @@
The MIT License
Copyright (c) 2016 CloudBees Inc and a number of other of contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

View File

@ -0,0 +1,29 @@
//
// See https://github.com/jenkinsci/js-builder
//
var builder = require('@jenkins-cd/js-builder')
.withExternalModuleMapping('react-router', 'react:react-router');
//
// Redefine the "test" task to use mocha and support es6.
// We might build this into js-builder, but is ok here
// for now.
//
builder.defineTask('test', function() {
var mocha = require('gulp-mocha');
var babel = require('babel-core/register');
builder.gulp.src('src/test/js/**/*-spec.jsx')
.pipe(mocha({
compilers: { js: babel },
})).on('error', function(e) {
if (builder.isRetest()) {
// ignore test failures if we are running retest.
return;
}
throw e;
});
});
builder.gulp.task('lint:watch', function () {
builder.gulp.watch(['src/main/js/**/*.js', 'src/main/js/**/*.jsx'], ['lint']);
});

View File

@ -0,0 +1,70 @@
{
"name": "blueocean-freestyle",
"version": "0.0.1-beta-1",
"scripts": {
"storybook": "start-storybook -p 9001 -s node_modules/@jenkins-cd/design-language/dist/assets/,target/classes/org/jenkins/ui/jsmodules/blueocean_freestyle/",
"lint": "gulp lint",
"lint:fix": "gulp lint --fixLint",
"lint:watch": "gulp lint:watch --continueOnLint",
"test": "gulp test",
"test:watch": "gulp test:watch",
"bundle": "gulp bundle",
"bundle:watch": "gulp bundle:watch"
},
"devDependencies": {
"@jenkins-cd/eslint-config-jenkins": "0.0.2",
"@jenkins-cd/js-builder": "0.0.34",
"@jenkins-cd/js-test": "1.1.1",
"@kadira/storybook": "1.34.0",
"babel": "^6.5.2",
"babel-core": "^6.7.6",
"babel-eslint": "^6.0.2",
"babel-preset-es2015": "^6.6.0",
"babel-preset-react": "^6.5.0",
"babel-preset-stage-0": "^6.5.0",
"chai": "^3.5.0",
"enzyme": "2.3.0",
"eslint": "2.8.0",
"eslint-plugin-react": "^5.0.1",
"eslint-to-editorconfig": "1.2.0",
"gulp": "^3.9.1",
"gulp-mocha": "^2.2.0",
"mocha": "^2.4.5",
"nock": "^8.0.0",
"react-addons-test-utils": "15.0.1"
},
"dependencies": {
"@jenkins-cd/design-language": "0.0.63",
"@jenkins-cd/js-extensions": "0.0.19-beta-3",
"@jenkins-cd/js-modules": "0.0.5",
"@jenkins-cd/sse-gateway": "0.0.5",
"immutable": "3.8.1",
"isomorphic-fetch": "2.2.1",
"keymirror": "0.1.1",
"moment": "2.13.0",
"moment-duration-format": "1.3.0",
"react": "15.1.0",
"react-dom": "15.1.0",
"react-material-icons-blue": "1.0.4",
"react-redux": "4.4.5",
"react-router": "2.3.0",
"redux": "3.5.2",
"redux-thunk": "2.0.1",
"reselect": "2.5.1",
"window-handle": "1.0.0"
},
"jenkinscd": {
"extDependencies": [
"@jenkins-cd/sse-gateway",
"immutable",
"isomorphic-fetch",
"react-router",
"keymirror",
"react-redux",
"react-router",
"redux",
"redux-thunk",
"reselect"
]
}
}

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>blueocean-parent</artifactId>
<groupId>io.jenkins.blueocean</groupId>
<version>1.0-alpha-5-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<name>BlueOcean :: Freestyle</name>
<artifactId>blueocean-freestyle</artifactId>
<packaging>hpi</packaging>
<url>https://wiki.jenkins-ci.org/display/JENKINS/Blue+Ocean+Plugin</url>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>blueocean-rest</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>blueocean-rest-impl</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,102 @@
package io.jenkins.blueocean.freestyle;
import java.util.Collection;
import io.jenkins.blueocean.rest.annotation.Capability;
import io.jenkins.blueocean.rest.hal.Link;
import io.jenkins.blueocean.rest.model.BlueActionProxy;
import io.jenkins.blueocean.rest.model.BlueFavorite;
import io.jenkins.blueocean.rest.model.BlueFavoriteAction;
import io.jenkins.blueocean.rest.model.BluePipeline;
import io.jenkins.blueocean.rest.model.BlueQueueContainer;
import io.jenkins.blueocean.rest.model.BlueRun;
import io.jenkins.blueocean.rest.model.BlueRunContainer;
@Capability("freestyle.project")
public class BlueFreestylePipeline extends BluePipeline {
@Override
public Link getLink() {
// TODO Auto-generated method stub
return null;
}
@Override
public BlueFavorite favorite(BlueFavoriteAction arg0) {
// TODO Auto-generated method stub
return null;
}
@Override
public Collection<BlueActionProxy> getActions() {
// TODO Auto-generated method stub
return null;
}
@Override
public String getDisplayName() {
// TODO Auto-generated method stub
return null;
}
@Override
public Long getEstimatedDurationInMillis() {
// TODO Auto-generated method stub
return null;
}
@Override
public String getFullName() {
// TODO Auto-generated method stub
return null;
}
@Override
public String getJobClass() {
// TODO Auto-generated method stub
return null;
}
@Override
public String getLastSuccessfulRun() {
// TODO Auto-generated method stub
return null;
}
@Override
public BlueRun getLatestRun() {
// TODO Auto-generated method stub
return null;
}
@Override
public String getName() {
// TODO Auto-generated method stub
return null;
}
@Override
public String getOrganization() {
// TODO Auto-generated method stub
return null;
}
@Override
public BlueQueueContainer getQueue() {
// TODO Auto-generated method stub
return null;
}
@Override
public BlueRunContainer getRuns() {
// TODO Auto-generated method stub
return null;
}
@Override
public Integer getWeatherScore() {
// TODO Auto-generated method stub
return null;
}
}

View File

@ -0,0 +1,23 @@
package io.jenkins.blueocean.freestyle;
import hudson.model.Item;
import io.jenkins.blueocean.rest.Reachable;
import io.jenkins.blueocean.rest.model.BluePipeline;
import io.jenkins.blueocean.rest.model.Resource;
import io.jenkins.blueocean.service.embedded.rest.BluePipelineFactory;
public class BlueFreestyleProjectFactory extends BluePipelineFactory {
@Override
public BluePipeline getPipeline(Item arg0, Reachable arg1) {
// TODO Auto-generated method stub
return null;
}
@Override
public Resource resolve(Item arg0, Reachable arg1, Item arg2) {
// TODO Auto-generated method stub
return null;
}
}

View File

@ -0,0 +1,143 @@
package io.jenkins.blueocean.freestyle;
import java.util.Collection;
import java.util.Date;
import io.jenkins.blueocean.rest.hal.Link;
import io.jenkins.blueocean.rest.model.BlueActionProxy;
import io.jenkins.blueocean.rest.model.BlueChangeSetEntry;
import io.jenkins.blueocean.rest.model.BluePipelineNodeContainer;
import io.jenkins.blueocean.rest.model.BluePipelineStepContainer;
import io.jenkins.blueocean.rest.model.BlueQueueItem;
import io.jenkins.blueocean.rest.model.BlueRun;
import io.jenkins.blueocean.rest.model.Container;
public class BlueFreestyleRun extends BlueRun {
@Override
public Link getLink() {
// TODO Auto-generated method stub
return null;
}
@Override
public Collection<BlueActionProxy> getActions() {
// TODO Auto-generated method stub
return null;
}
@Override
public Container<BlueArtifact> getArtifacts() {
// TODO Auto-generated method stub
return null;
}
@Override
public Container<BlueChangeSetEntry> getChangeSet() {
// TODO Auto-generated method stub
return null;
}
@Override
public Long getDurationInMillis() {
// TODO Auto-generated method stub
return null;
}
@Override
public Date getEnQueueTime() {
// TODO Auto-generated method stub
return null;
}
@Override
public Date getEndTime() {
// TODO Auto-generated method stub
return null;
}
@Override
public Long getEstimatedDurtionInMillis() {
// TODO Auto-generated method stub
return null;
}
@Override
public String getId() {
// TODO Auto-generated method stub
return null;
}
@Override
public Object getLog() {
// TODO Auto-generated method stub
return null;
}
@Override
public BluePipelineNodeContainer getNodes() {
// TODO Auto-generated method stub
return null;
}
@Override
public String getOrganization() {
// TODO Auto-generated method stub
return null;
}
@Override
public String getPipeline() {
// TODO Auto-generated method stub
return null;
}
@Override
public BlueRunResult getResult() {
// TODO Auto-generated method stub
return null;
}
@Override
public String getRunSummary() {
// TODO Auto-generated method stub
return null;
}
@Override
public Date getStartTime() {
// TODO Auto-generated method stub
return null;
}
@Override
public BlueRunState getStateObj() {
// TODO Auto-generated method stub
return null;
}
@Override
public BluePipelineStepContainer getSteps() {
// TODO Auto-generated method stub
return null;
}
@Override
public String getType() {
// TODO Auto-generated method stub
return null;
}
@Override
public BlueQueueItem replay() {
// TODO Auto-generated method stub
return null;
}
@Override
public BlueRun stop() {
// TODO Auto-generated method stub
return null;
}
}

View File

@ -0,0 +1,43 @@
package io.jenkins.blueocean.freestyle;
import java.util.Iterator;
import io.jenkins.blueocean.rest.hal.Link;
import io.jenkins.blueocean.rest.model.BluePipeline;
import io.jenkins.blueocean.rest.model.BlueQueueItem;
import io.jenkins.blueocean.rest.model.BlueRun;
import io.jenkins.blueocean.rest.model.BlueRunContainer;
public class BlueFreestyleRunContainer extends BlueRunContainer {
@Override
public Iterator<BlueRun> iterator() {
// TODO Auto-generated method stub
return null;
}
@Override
public Link getLink() {
// TODO Auto-generated method stub
return null;
}
@Override
public BlueQueueItem create() {
// TODO Auto-generated method stub
return null;
}
@Override
public BluePipeline getPipeline(String arg0) {
// TODO Auto-generated method stub
return null;
}
@Override
public BlueRun get(String arg0) {
// TODO Auto-generated method stub
return null;
}
}

View File

@ -0,0 +1,3 @@
import FreestyleDeployments from './components/FreestyleDeployments';
export default { path: 'deployments', component: FreestyleDeployments };

View File

@ -0,0 +1,3 @@
import FreestyleDeployments from './components/FreestyleDeployments';
export default { path: '/deployments', component: FreestyleDeployments };

View File

@ -0,0 +1,11 @@
import React from 'react';
export default class FreestyleDeployments extends React.Component {
render() {
return <div>Some deployments</div>;
}
}
FreestyleDeployments.contextTypes = {
pipeline: React.PropTypes.any,
};

View File

@ -0,0 +1,14 @@
import React from 'react';
import { TabLink } from '@jenkins-cd/design-language';
export default class FreestyleArtifactsTab extends React.Component {
render() {
return (
<TabLink to={`${this.props.baseLink}/freestyle-artifacts`}>Freestyle Artifacts</TabLink>
);
}
}
FreestyleArtifactsTab.propTypes = {
baseLink: React.PropTypes.string,
};

View File

@ -0,0 +1,14 @@
import React from 'react';
import { TabLink } from '@jenkins-cd/design-language';
export default class FreestyleDeploymentsTab extends React.Component {
render() {
return (
<TabLink to={`${this.props.baseLink}/deployments`}>Deployments</TabLink>
);
}
}
FreestyleDeploymentsTab.propTypes = {
baseLink: React.PropTypes.string,
};

View File

@ -0,0 +1,15 @@
# Extensions in this plugin
extensions:
- component: PipelineRoutes
extensionPoint: pipeline.routes
- component: Routes
extensionPoint: jenkins.main.routes
- component: components/tabs/FreestyleDeploymentsTab
extensionPoint: pipeline.main.navigation
dataType: hudson.model.FreeStyleProject
- component: components/tabs/FreestyleArtifactsTab
extensionPoint: rundetails.main.navigation
dataType: io.jenkins.blueocean.service.embedded.rest.FreeStyleRunImpl

View File

@ -0,0 +1 @@
// nothing to see here

View File

@ -0,0 +1,4 @@
<?jelly escape-by-default='true'?>
<div>
Blue Ocean Freestyle
</div>

View File

@ -56,6 +56,10 @@ public class MultiBranchPipelineImpl extends BlueMultiBranchPipeline {
return OrganizationImpl.INSTANCE.getName();
}
@Override
public String getJobClass() {
return mbp.getClass().getName();
}
@Override
public BlueFavorite favorite(@JsonBody BlueFavoriteAction favoriteAction) {

View File

@ -8,6 +8,7 @@ import hudson.scm.ChangeLogSet;
import hudson.scm.ChangeLogSet.Entry;
import io.jenkins.blueocean.commons.ServiceException;
import io.jenkins.blueocean.rest.Reachable;
import io.jenkins.blueocean.rest.annotation.Capability;
import io.jenkins.blueocean.rest.hal.Link;
import io.jenkins.blueocean.rest.model.BlueChangeSetEntry;
import io.jenkins.blueocean.rest.model.BluePipelineNodeContainer;
@ -32,6 +33,7 @@ import java.util.Map;
*
* @author Vivek Pandey
*/
@Capability("io.jenkins.blueocean.rest.model.BlueArtifactProvider")
public class PipelineRunImpl extends AbstractRunImpl<WorkflowRun> {
public PipelineRunImpl(WorkflowRun run, Link parent) {
super(run, parent);

View File

@ -27,6 +27,10 @@
<groupId>${project.groupId}</groupId>
<artifactId>blueocean-personalization</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>blueocean-freestyle</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>blueocean-rest</artifactId>
@ -106,6 +110,7 @@
linkHPI('blueocean-rest');
linkHPI('blueocean-commons');
linkHPI('blueocean-rest-impl');
linkHPI('blueocean-freestyle');
linkHPI('blueocean-pipeline-api-impl')
</source>
</configuration>

View File

@ -42,6 +42,11 @@ public class PipelineFolderImpl extends BluePipelineFolder {
return folder.getDisplayName();
}
@Override
public String getJobClass() {
return folder.getClass().getName();
}
@Override
public String getDisplayName() {
return folder.getDisplayName();

View File

@ -19,6 +19,7 @@ import io.jenkins.blueocean.rest.model.BlueRunContainer;
import io.jenkins.blueocean.rest.model.Resource;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.WebMethod;
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.json.JsonBody;
import org.kohsuke.stapler.verb.DELETE;
@ -47,6 +48,11 @@ public class PipelineImpl extends BluePipeline {
return job.getName();
}
@Override
public String getJobClass() {
return job.getClass().getName();
}
@Override
public String getDisplayName() {
return job.getDisplayName();

View File

@ -39,6 +39,12 @@ public abstract class BluePipeline extends Resource {
@Exported(name = NAME)
public abstract String getName();
/**
* Return the underlying job class
*/
@Exported(name="_jobClass")
public abstract String getJobClass();
/**
* @return human readable name of this pipeline
*/

View File

@ -26,7 +26,7 @@
},
"dependencies": {
"@jenkins-cd/design-language": "0.0.64",
"@jenkins-cd/js-extensions": "0.0.19",
"@jenkins-cd/js-extensions": "0.0.20-beta-1",
"@jenkins-cd/js-modules": "0.0.5",
"history": "2.0.2",
"immutable": "3.8.1",

View File

@ -62,8 +62,8 @@ function makeRoutes(routes) {
const appRoutes = [
...routes,
// FIXME: Not sure best how to set this up without the hardcoded IndexRedirect :-/
<IndexRedirect to="/pipelines" />,
<Route path="*" component={NotFound}/>
//<IndexRedirect to="/pipelines" />,
//<Route path="*" component={NotFound}/>
];
const routeProps = {
@ -71,7 +71,13 @@ function makeRoutes(routes) {
component: App
};
return React.createElement(Route, routeProps, ...appRoutes);
//return React.createElement(Route, routeProps, ...appRoutes);
return {
path: "/",
component: App,
childRoutes: routes
};
}
@ -138,7 +144,7 @@ function startApp(routes, stores) {
// Start React
render(
<Provider store={store}>
<Router history={history}>{ makeRoutes(routes) }</Router>
<Router history={history} routes={ makeRoutes(routes) }/>
</Provider>
, rootElement);
}

View File

@ -1,6 +1,6 @@
{
"name": "@jenkins-cd/js-extensions",
"version": "0.0.19",
"version": "0.0.20-beta-1",
"description": "Jenkins Extension Store",
"main": "index.js",
"files": [

View File

@ -34,7 +34,36 @@ export class ClassMetadataStore {
});
}
/**
* Matches all extensions that handle this data type or a supertype
*/
dataType(dataType) {
return (extensions, onload) => {
if (dataType && typeof(dataType) === 'object'
&& '_class' in dataType) { // handle the common API incoming data
dataType = dataType._class;
}
this.getClassMetadata(dataType, (currentTypeInfo) => {
// add all types that handle this data type
var matchingExtensions = [];
for (var i = 0; i < extensions.length; i++) {
var extension = extensions[i];
if (currentTypeInfo.classes.indexOf(extension.dataType) >= 0) {
matchingExtensions.push(extension);
}
}
onload(matchingExtensions);
});
};
}
/**
* Match the extensions based on the data type, stopping at the most specific
* type of data: this can be used for getting the most specific
* view for a particular class name, for example.
*/
mostSpecificDataType(dataType) {
return (extensions, onload) => {
if (dataType && typeof(dataType) === 'object'
&& '_class' in dataType) { // handle the common API incoming data

View File

@ -118,6 +118,10 @@ export class ExtensionRenderer extends React.Component {
/** Actually render an individual extension */
_renderExtension(element, extension) {
if (extension.type && React.Component.isPrototypeOf(extension.type)) { // already instantiated, just use it
ReactDOM.render(extension, element); // should probably bridge this
return;
}
var component = React.createElement(extension, this.props);
try {
var contextValuesAsProps = {
@ -169,6 +173,7 @@ ExtensionRenderer.propTypes = {
};
ExtensionRenderer.contextTypes = {
pipeline: React.PropTypes.any,
router: React.PropTypes.object,
config: React.PropTypes.object
};

View File

@ -48,9 +48,11 @@ export class ExtensionStore {
this._loadBundles(extensionPointId, () => this._registerComponentInstance(extensionPointId, pluginId, component, instance));
return;
}
var extension = this._findPlugin(extensionPointId, pluginId, component);
if (extension) {
extension.instance = instance;
extensions = this._findPlugins(extensionPointId, pluginId, component);
if (extensions) {
for (var extension of extensions) {
extension.instance = instance;
}
return;
}
throw new Error(`Unable to locate plugin for ${extensionPointId} / ${pluginId} / ${component}`);
@ -59,15 +61,10 @@ export class ExtensionStore {
/**
* Finds a plugin by extension point id, plugin id, component name
*/
_findPlugin(extensionPointId, pluginId, component) {
_findPlugins(extensionPointId, pluginId, component) {
var extensions = this.extensionPoints[extensionPointId];
if (extensions) {
for (var i = 0; i < extensions.length; i++) {
var extension = extensions[i];
if (extension.pluginId == pluginId && extension.component == component) {
return extension;
}
}
return extensions.filter(extension => extension.pluginId == pluginId && extension.component == component);
}
}

View File

@ -104,6 +104,7 @@
<module>blueocean-events</module>
<module>blueocean-dashboard</module>
<module>blueocean-personalization</module>
<module>blueocean-freestyle</module>
<module>blueocean-plugin</module>
</modules>
@ -154,6 +155,12 @@
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>blueocean-freestyle</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>blueocean-events</artifactId>