Compare commits

...

389 Commits

Author SHA1 Message Date
R. Tyler Croy 1718f6f970
Ensure that only the right blueocean-parent- tags get considered for dockerizing 2017-01-11 21:15:51 -08:00
R. Tyler Croy 6e5295d671 Simplify the Dockerfile and build.sh to pull the latest released plugin instead. (#703)
No need for the maven chicanery
2017-01-12 16:08:28 +11:00
R. Tyler Croy de58a651ef Update the build.sh to use a more proper tag sort and avoid allocating a tty (#702) 2017-01-12 14:48:46 +11:00
Vivek Pandey c9ad47e4d2 [maven-release-plugin] prepare for next development iteration 2017-01-11 09:27:29 -08:00
Vivek Pandey 8968b8ab10 [maven-release-plugin] prepare release blueocean-parent-1.0.0-b16 2017-01-11 09:27:22 -08:00
Thorsten Scherler 471cded940 [JENKINS-38495_Add_waiting_for_inputstate_to_favourite_card] Implemen… (#689)
* [JENKINS-38495_Add_waiting_for_inputstate_to_favourite_card] Implement a second filter for all input pipelines

* eslint - formating changes and fix offences

* [JENKINS-38495_Add_waiting_for_inputstate_to_favourite_card] fix translation (mixed up)

* [JENKINS-38495_Add_waiting_for_inputstate_to_favourite_card] Only show the input stage list when there are some and only show once a pipeline

* eslint - formating changes and fix offences

* [JENKINS-38495_Add_waiting_for_inputstate_to_favourite_card] do not show header when we do not have any paused inputs

* [JENKINS-38495_Add_waiting_for_inputstate_to_favourite_card] Fix various issues raised in the PR. - add new line - fix bottom margin - slim down code

* [JENKINS-38495_Add_waiting_for_inputstate_to_favourite_card] slim down
2017-01-11 13:14:13 +01:00
Tom Fennelly 3d889247f9 [FIX JENKINS-40932] combine css (#694)
* Use JDL with combined CSS

* Rebase against upstream master

* jdl 0.0.100
2017-01-11 11:22:26 +00:00
Cliff Meyers 39e09e5be8 Hotfix/close modal goes to activity (#699)
* fix a bug where closing the Run Details modal would navigate back to /activity instead of the previous route in many cases (from Dashboard, from Branches tab)

* publish beta core-js; update deps

* tick core-js version after publishing 0.0.44; update deps
2017-01-10 21:35:56 -05:00
R. Tyler Croy bd7928e114 Set up the Docker 'official' Pipeline to execute weekly (#697)
References JENKINS-39804
2017-01-11 12:04:20 +11:00
Vivek Pandey bd44052dd3 Fix time sensitive failing tests (#696) 2017-01-10 15:59:46 -08:00
Keith Zantow d57bb210c7 Bump JDL to 0.0.99 (#691) 2017-01-10 18:10:50 -05:00
Ivan Meredith 9574ed6b06 Move null check into predicate function (#693)
* Move null check into predicate function

* Simplify null check
2017-01-11 11:52:08 +13:00
Vivek Pandey 623935beab JENKINS-39658# Include failed step's error to it's log (#682)
* JENKINS-39658# Include failed step's error to it's log

* Added header before closing writer

* Replaced random sleep by predictable wait

* Show error of steps with ErrorAction but no log
2017-01-10 08:48:59 -08:00
Vivek Pandey 057e34bf1a [maven-release-plugin] prepare for next development iteration 2017-01-09 17:05:18 -08:00
Vivek Pandey 45bf09a359 [maven-release-plugin] prepare release blueocean-parent-1.0.0-b15 2017-01-09 17:05:10 -08:00
James William Dumay f7b63b40b1 Exclude poorly performing FlowGraphAction from action proxy results (#675) 2017-01-10 09:23:56 +11:00
Cliff Meyers fe65e25e6a Bug/jenkins 40662 run details close problem (#677)
* [JENKINS-40662] fix deep link to run details / closing modal doesn't go back problem by detecting if the prior URL is run details and bailing to activity instead

* Revert "[JENKINS-40662] fix deep link to run details / closing modal doesn't go back problem by detecting if the prior URL is run details and bailing to activity instead"

This reverts commit 56061749e75dff55810012287af3c0541d56a9b0.

* [JENKINS-40662] bake some intelligence into the LocationService so it handles a 'REPLACE' correctly

* [JENKINS-40662] don't set the "previous" route if the history change was a "REPLACE" (also for Redux store)

* [JENKINS-40662] publish new beta blueocean-core-js; update deps

* [JENKINS-40662] publish beta blueocean-core-js; update the deps

* [JENKINS-40662] publish beta blueocean-core-js; update the deps

* [JENKINS-40662] tick blueocean-core-js version after publishing 0.0.43

* [JENKINS-40662] use prod version of blueocean-core-js
2017-01-09 13:57:45 -05:00
Thorsten Scherler 962d6a9a53 [FIX-JENKINS-40648_Input_state_isnt_updated_in_activity_branch_listing_fr… (#678)
* [JENKINS-40648_Input_state_isnt_updated_in_activity_branch_listing_from_SSE] WIP implementing first step to let sse inform about running state

* [JENKINS-40648_Input_state_isnt_updated_in_activity_branch_listing_from_SSE] refactor naming and use MessageEnricher

* [JENKINS-40648_Input_state_isnt_updated_in_activity_branch_listing_from_SSE] java is no javascript

* [JENKINS-40648_Input_state_isnt_updated_in_activity_branch_listing_from_SSE] fix property

* [JENKINS-40648_Input_state_isnt_updated_in_activity_branch_listing_from_SSE] when pipeline is paused we fire a job event to inform subscriber on the job channel

* [JENKINS-40648_Input_state_isnt_updated_in_activity_branch_listing_from_SSE] remove log statements and prepare for unpause event

* [JENKINS-40648_Input_state_isnt_updated_in_activity_branch_listing_from_SSE] publish new merged version of core-js

* JENKINS-40809# Publish job_run_unpaused event on input submission

* Doc improvements

* [JENKINS-40648_Input_state_isnt_updated_in_activity_branch_listing_from_SSE] use explicit this

* [JENKINS-40648_Input_state_isnt_updated_in_activity_branch_listing_from_SSE] Add note about what we are doing and resolve TODO note

* [JENKINS-40648_Input_state_isnt_updated_in_activity_branch_listing_from_SSE] Fix feedback regarding naming of property. Add multiple events to update karaoke from sse events since we are now subscriped to the job channel. Fixes multiple glitches in the matrix, ..I mean karaoke mode. ;) not perfect but we will refactor to mobx so IMHO good as it gets for now.

* eslint - formating changes and fix offences

* Minor refactoring to change BlueOCeanGraphListener to PipelineInputStepListener

* eslint - formating changes and fix offences

* [JENKINS-40648_Input_state_isnt_updated_in_activity_branch_listing_from_SSE] publish merged version of core-js

* [JENKINS-40648_Input_state_isnt_updated_in_activity_branch_listing_from_SSE] Using released version of sse-gateway

* [JENKINS-40648_Input_state_isnt_updated_in_activity_branch_listing_from_SSE] Fix listener for favorite cards

* [JENKINS-40648_Input_state_isnt_updated_in_activity_branch_listing_from_SSE] Fix Favorite service to display pause state
2017-01-09 17:02:13 +01:00
Tom Fennelly 68d5f0bd92 [FIX JENKINS-40080] Cache class info in browser storage (#683)
* Add basic browser storage API wrapper to core-js

* Cache classes info in blowser storage

* @jenkins-cd/blueocean-core-js 0.0.41-tfbeta1

* Catch lang detector failures and fallback to the fallback lang.

Need this now because we are using localStorage, as does the i18next package. We are initializing window.localStorage if it's not there already (as is the case in tests). This is causing i18next to baulk.

* Fix CapabilityTestUtils.js

* core-js dependants to 0.0.42 for publish

* core-js dependants to 0.0.42 on dependants
2017-01-09 12:58:59 +00:00
Ivan Meredith dee7566562 [JENKINS-37244] Consistent run terminology (#680)
* [JENKINS-37244] Change build messages to run

* Fix spelling mistake

* Rename run header to runHeader

* Rename run header to runHeader
2017-01-09 17:46:47 +13:00
Ivan Meredith 4779755e2c [JENKINS-40854] Regression in run button on activity page (#688)
* [JENKINS-40854] Change RunButton to be able to use different classes for its button"

* Bump core-js version

* Release core-js
2017-01-09 17:46:18 +13:00
Tom Fennelly c5555ff84f Update BlueOceanWebURLBuilder to only navigate to MBP folder types Vs any old folder (#685) 2017-01-06 16:30:36 +00:00
James William Dumay 9bdb6e39a1 Just return what the artifact zip file path would be to avoid adding ~6ms lookup of artifacts for each run (#674) 2017-01-06 08:06:25 +11:00
Tom Fennelly 673cd48d57 Fix encoding of job name in "Open Blue Ocean" URLs (#676) 2017-01-05 20:19:12 +00:00
Nicolas Ménard da55ff39c5 [FIX JENKINS-40730] Fix mistakes in French translations. (#681) 2017-01-05 22:39:39 +11:00
Cliff Meyers 52f2ea7fab bug/core-js-fixes (#647)
* [JENKINS-38598] allow error in response body to be read; null check in Utils.clone

* publish latest core-js changes as beta so we can test in ATH

* tick blueocean-core-js version after publishing 0.0.40

* use prod version of blueocean-core-js
2017-01-04 12:13:28 -05:00
Josh McDonald 0ce8479cc1 JENKINS-39762 - Update application header (#668)
* JENKINS-39762 - Update application header

 * Pull in updated JDL, and add new components to replace existing headers.
 * Add Blue Ocean logo component to use for betas.
 * Add storybook entries to demonstrate usage.

Squashed commit of the following:

    josh/JENKINS-39762-new-headers * lint
    josh/JENKINS-39762-new-headers * ResultPageHeader working nicely, no build details shown in storybooks
    josh/JENKINS-39762-new-headers * Progess on ResultPageHeader, still losing result somewhere :(
    josh/JENKINS-39762-new-headers * Improvments to ContentPageHeader, show weather icon in story
    josh/JENKINS-39762-new-headers * Fix JDL version
    josh/JENKINS-39762-new-headers * Clean up the rest of the storybook remnants from web
    josh/JENKINS-39762-new-headers * Move components and stories to -dashboard
    josh/JENKINS-39762-new-headers * ehsuydjmntws5rty67hauw5r6yjthna
    josh/JENKINS-39762-new-headers * tweak storybook to get by with a npm run bundle rather than full mvn build which is way slower and requirequires lint
    josh/JENKINS-39762-new-headers * Add logo as a component, rejigger storybooks to also reference module-local styles
    josh/JENKINS-39762-new-headers * Add storybooks to -web
    josh/JENKINS-39762-new-headers * Pull in JDL beta, switch out PageTabs for PageTabsOld for transition

* feature/JENKINS-39762-new-headers * Restore access to module css in storybook
2017-01-04 13:52:39 +10:00
Vivek Pandey c1ca81c98d Task/jenkins 39789 Github organizations API (#673)
* Adding routable API to blueocean api path

* JENKINS-38848# Credential GET API

- Allows plugins to serve their object graphs from /organizations/:id/ API.
- Doesn't quite work for credentials plugin as for POST requests it requires form submissions

* Credential search API

- credential reponse description elements defaults to displayName:domain:type.

* Fixed links

* Organization route extensibility simplified

- OrganizationAction is all one needs to expose it's object graph inside organization route
- ApiRoute remains to be extension point to be added at root of bluocean route

* Added missing file

* Added domain to credential object.

* Credential creation API

* JENKINS-39790# SCM API to validate access token

* Refactoring. Moved OrganizationRoute to blueocean-rest.

* Typo fixed

* JENKINS-39789# github organizations API

* Expose list of ScmOrganization as container.

* Use guava to transform from GHOrganization to ScmOrganization.

* JENKINS-39788# SCM Repositories in an org API

* Create domain 'github-domain' in user scope credntial store if it doesn't exist.

* Fixed routing issue, doc improvments.

* Include github user org and flag if a github org is jenkins org pipeline
2017-01-03 12:41:41 -08:00
Thorsten Scherler 2fe57cb944 [master] tick version 2017-01-03 14:31:30 +01:00
Thorsten Scherler 7e42efa005 Jenkins 38494 user can see the input form on an input step (#662)
* [JENKINS-38492_Update_result_header_to_show_waiting_for_input_state] add new state pause as symbol for pipelineGraph

* [JENKINS-38492_Update_result_header_to_show_waiting_for_input_state] use jdl lib that contains paused implementation

* [JENKINS-38492_Update_result_header_to_show_waiting_for_input_state] use published version of jdl

* [JENKINS-38492_Update_result_header_to_show_waiting_for_input_state] activate pause state

* [JENKINS-38492_Update_result_header_to_show_waiting_for_input_state] update version of jdl

* [JENKINS-38492_Update_result_header_to_show_waiting_for_input_state] In case we are using pause as running state we want the buton read abort

* [JENKINS-38492_Update_result_header_to_show_waiting_for_input_state] bump version

* [JENKINS-38492_Update_result_header_to_show_waiting_for_input_state] New translation key

* [JENKINS-38492_Update_result_header_to_show_waiting_for_input_state] Use unpublished core version to change behaviour on the Cancel button

* [JENKINS-38494_User_can_see_the_input_form_on_an_input_step] add paused to the running state so it will be focused

* [JENKINS-38494_User_can_see_the_input_form_on_an_input_step] Pass input down the pipe. When paused stop karaoke mode.

* [JENKINS-38494_User_can_see_the_input_form_on_an_input_step] create a new component for input steps

* [JENKINS-38494_User_can_see_the_input_form_on_an_input_step] starting to add input content

* eslint - formating changes and fix offences

* [JENKINS-38494_User_can_see_the_input_form_on_an_input_step] Add first implementation of the full input. Next step reder them as an input form

* [JENKINS-38494_User_can_see_the_input_form_on_an_input_step] add target to start storybook under a different port

* [JENKINS-38494_User_can_see_the_input_form_on_an_input_step] add new story for the input steps

* [JENKINS-38494_User_can_see_the_input_form_on_an_input_step] Implement first version of components to request input

* [JENKINS-38494_User_can_see_the_input_form_on_an_input_step] Fix issues that storybook complained in the console

* [JENKINS-38494_User_can_see_the_input_form_on_an_input_step] use jdl version that contains password field

* eslint - formating changes and fix offences

* [JENKINS-38494_User_can_see_the_input_form_on_an_input_step] Use latest jdl and pass name of parameter to later be able to send the form

* eslint - formating changes and fix offences

* JENKINS-40587# Input step submit API

* 'parameter' in the input is array, renamed to 'parameters'

* Doc improvement

* Explicitly set InputStep id to avoid possible conflict

* Doc fix

* [JENKINS-38494_User_can_see_the_input_form_on_an_input_step] First working version to submit input

* [JENKINS-38494_User_can_see_the_input_form_on_an_input_step] sync to current stand

* [JENKINS-38494_User_can_see_the_input_form_on_an_input_step] better comments. implement cancel button. Fix lint errors

* [JENKINS-38494_User_can_see_the_input_form_on_an_input_step] Implement styling for form. add comments to the code. New i18n key for cancel.

* [JENKINS-38494_User_can_see_the_input_form_on_an_input_step] add check to valid the response is valid

* [JENKINS-38494_User_can_see_the_input_form_on_an_input_step] add hooks for ATH

* [JENKINS-38494_User_can_see_the_input_form_on_an_input_step] Drop Object.value since it is not supported by enough browser

* as it is christmas, fixing lint

* [JENKINS-38494_User_can_see_the_input_form_on_an_input_step] using unpublished core-js version to fix weird merge result reported by @michaelneale

* [JENKINS-38494_User_can_see_the_input_form_on_an_input_step] Bump versions and used the new updated ones

* [JENKINS-38494_User_can_see_the_input_form_on_an_input_step] shrinkcrap took another hour of my time. Worst tool ever!

* [JENKINS-38494_User_can_see_the_input_form_on_an_input_step] comment the cancel button as requested by James
2017-01-03 14:20:21 +01:00
Nicolas Ménard 7d040148ad [JENKINS-39225] Add French translations. (#671) 2017-01-03 07:51:49 +11:00
Cliff Meyers ecfaab3426 Task/jenkins 40256 integrate new form controls (#666)
* allow i18n to mocked in tests; enable this via a "test-entrypoint" that runs before all tests

* build and test core-js as part of main CI build

* try avoiding optional deps

* fiddling

* more fiddling

* more fiddling

* [JENKINS-40256] clean up core-js package.json/shrink to use exact versions; remove JDL dep; switch to @jenkins-cd/react-material-icons@1.0.0

* [JENKINS-40256] change to @jenkins-cd/react-material-icons@1.0.0

* [JENKINS-40256] use core-js that has no JDL dep; use latest JDL (mostly for new button styles)

* [JENKINS-40256] style refinements for new JDL buttons

* [JENKINS-40256] use prod version of JDL

* [JENKINS-40256] use prod release of blueocean-core-js

* [JENKINS-40256] try to recover from shrink merge pain

* [JENKINS-40256] npm insanity fixed?

* [JENKINS-40256] use the legacy tab controls to avoid ATH breakage

* [JENKINS-40256] add a semantic class name for "New Pipeline" link to make ATH less brittle
2016-12-30 09:21:53 -05:00
Vivek Pandey 9d708122fd JENKINS-39790# SCM API to validate access token (#672)
* JENKINS-39790# SCM API to validate access token

* Typo fixed

* Create domain 'github-domain' in user scope credntial store if it doesn't exist.
2016-12-30 06:16:29 -08:00
Vivek Pandey 71b813e2c6 Feature/jenkins 38848: Credential enumeration API (#593)
* Adding routable API to blueocean api path

* JENKINS-38848# Credential GET API

- Allows plugins to serve their object graphs from /organizations/:id/ API.
- Doesn't quite work for credentials plugin as for POST requests it requires form submissions

* Credential search API

- credential reponse description elements defaults to displayName:domain:type.

* Fixed links

* Organization route extensibility simplified

- OrganizationAction is all one needs to expose it's object graph inside organization route
- ApiRoute remains to be extension point to be added at root of bluocean route

* Added missing file

* Added domain to credential object.

* Credential creation API

* Refactoring. Moved OrganizationRoute to blueocean-rest.
2016-12-29 23:49:16 +05:30
Cliff Meyers 873c0c2f85 tick core-js version after publish 2016-12-23 12:19:14 -05:00
Cliff Meyers d79fc44735 remove jdl dep from core-js (#670) 2016-12-23 12:12:40 -05:00
Vivek Pandey 6191fdfed8 Story/jenkins 38804 Parameterized pipeline API (#669)
* JENKINS-38804# Parameterized pipeline API

* Trigger pipeline build using parameters

* doc fixes
2016-12-23 11:07:41 +05:30
Cliff Meyers b5b4fdfb0e Bug/fix core js tests (#665)
* allow i18n to mocked in tests; enable this via a "test-entrypoint" that runs before all tests

* build and test core-js as part of main CI build

* try avoiding optional deps

* fiddling

* more fiddling

* more fiddling

* trying a new image with more modern node

* need more tools

* This one already exists and may work better

* remove prepublish from core-js

* simplified install / build for core-js

* remove stage for core-js

* fix some package/shrink validation errors in core-js

* mock i18n in a simpler way that just returns the localization key back

* Revert "remove prepublish from core-js"

This reverts commit 7cb913df356d27461b71dd12e3686891b313cb4e.

* just run the full build

* ensure app works with latest published core-js

* release of core-js

* use prod release of core-js
2016-12-22 16:19:56 -05:00
Vivek Pandey 556a5d56e6 JENKINS-40587# Input step submit API (#664)
* JENKINS-40587# Input step submit API

* 'parameter' in the input is array, renamed to 'parameters'

* Doc improvement

* Explicitly set InputStep id to avoid possible conflict

* Doc fix
2016-12-22 12:09:48 +05:30
Vivek Pandey f6cbe5f9f8 JENKINS-38847# API to create multi-branch pipeline for given SCM (#573)
* JENKINS-38847# API to create multi-branch pipeline for given SCM

* s/orgName/name/

* Moved github specific code in to separate module for now

Eventually it should be moved out in it's own repo

* Added user cause to github org folder run

* API to re-run and get run details of organization folder.

* git pipeline creation implemented

* Updated parent pom versions

* Fixed bugs

- Duplicate github folder name gives 400 error. Fixes 500 error.
- orgName should default to github folder name. Fixes NPE.
- Render latestRun correctly

* Validate credentialId and return 400 if invalid

* rescan (/pipelines/:id/runs/1/replay) and queue api implementation

- Fixed regression where null credentialId was resulting in 400 error

* Pipeline creation extensibility refactor

* Further simplified creation extensibility

- An implementor of pipeline implements instance of BluePipelineCreateRequest and handles creation.
- Reuqest must have $class element set to the fully qualified class name of concrete instance of BluePipelineCreateRequest

* Update API for github and git SCM

* Stapler bug triggers JsonBody annotation processer resulting in failure.

- Workarounded it by removing @JsonBody annotation from update() method
- Bug in @TreeResponse interceptor fixed to correctly dispatch based on
  explicit HttpVerbInterceptor and default correct in case there are none

* Updated parent pom version and implemented missing super class methods

* Changed git and guthub plugin names to follow new standard

* Sepcific types for org folders.

Pulled in from PR: https://github.com/jenkinsci/blueocean-plugin/pull/661
2016-12-21 20:31:41 +05:30
vivek ad98a3ed44 Task/jenkins 38150 Handle declarative pipeline preparatory steps and postBuild stage (#608)
* JENKINS-39463# Integrate pipeline-model-definition 0.7.1

* Reverted tests as they should pass eventually once JENKINS-39631 is resolved.

* Build with declarative plugin snashot build for further testing.

* Updated released declarative pipeline version 0.6

* JENKINS-38150# pre and post synthetic stage handling.

* Use constants from pipeline-model-definition plugin

* Declarative plugin version 0.7.1 and workflow-api plugin version 2.7
2016-12-21 18:01:08 +05:30
Thorsten Scherler 917c232192 [Update_version_numbers_of_jdl_and_cjs] Update version numbers (#663) 2016-12-19 02:35:14 -08:00
Thorsten Scherler d619bdbf5c FIX Jenkins 38492 update result header to show waiting for input state (#660)
* [JENKINS-38492_Update_result_header_to_show_waiting_for_input_state] add new state pause as symbol for pipelineGraph

* [JENKINS-38492_Update_result_header_to_show_waiting_for_input_state] use jdl lib that contains paused implementation

* [JENKINS-38492_Update_result_header_to_show_waiting_for_input_state] use published version of jdl

* [JENKINS-38492_Update_result_header_to_show_waiting_for_input_state] activate pause state

* [JENKINS-38492_Update_result_header_to_show_waiting_for_input_state] update version of jdl

* [JENKINS-38492_Update_result_header_to_show_waiting_for_input_state] In case we are using pause as running state we want the buton read abort

* [JENKINS-38492_Update_result_header_to_show_waiting_for_input_state] bump version

* [JENKINS-38492_Update_result_header_to_show_waiting_for_input_state] New translation key

* [JENKINS-38492_Update_result_header_to_show_waiting_for_input_state] Use unpublished core version to change behaviour on the Cancel button

* [JENKINS-38492_Update_result_header_to_show_waiting_for_input_state] remove obsolete code
2016-12-19 01:35:52 -08:00
Michael Neale 54547fd93a [maven-release-plugin] prepare for next development iteration 2016-12-16 11:54:40 +11:00
Michael Neale 00384c1da0 [maven-release-plugin] prepare release blueocean-parent-1.0.0-b14 2016-12-16 11:54:31 +11:00
Tom Fennelly e25ae38a26 [FIX JENKINS-39625] Page preloading with pipeline state - Runs (#613)
* Introduce PageStatePreloader extension point

* Added BlueoceanUrl to blueocean-commons

* Updates to BlueoceanUrl

* Added PipelineStatePreloader to dashboard

* PageStatePreloader impl for js-extensions data

* RESTFetchPreloader

* CapabilityAugmenter changes to handle an Array of _class names

* Client side mods to handle prefetchdata

* Updated deps for core-js

* Add a test for PipelineStatePreloader

* Rename BlueoceanUrl to BlueOceanUrl

* Rename BlueOceanUrl to BlueUrlTokenizer

* Added a comment to BlueUrlTokenizer

* Fix Javadoc errors

* core-js version 0.0.35

* Updating to @jenkins-cd/blueocean-core-js@0.0.35 on dependants
2016-12-15 15:50:42 +00:00
Ivan Meredith dc4e9006dc [JENKINS-40463] Show pager when data is loaded (#659) 2016-12-15 15:11:11 +11:00
Michael Neale 8755cc193e fetchMore is no longer used.. also someone needs a test for this (#658) 2016-12-15 10:17:41 +11:00
Ivan Meredith 9bf0b0c45f [JENKINS-39542] Test results trimming null string fix. (#656)
* [JENKINS-39542] trim() being called on null in TestReport

* test case

* Fix Tests

* Fix linting
2016-12-14 18:37:48 +13:00
Yoann Dubreuil 38163fb388 Jenkinsfile to build the official docker image (#652) 2016-12-12 07:58:08 -08:00
Sujeevan Vijayakumaran cb3cffcb95 [JENKINS-39225] Improved (reverted) German translation of Branch(es) … (#654)
* [JENKINS-39225] Improved (reverted) German translation of Branch(es) and Pull-Request(s)

* [JENKINS-39225] Improved (reverted) German translation of Build(s)
2016-12-12 03:27:38 -08:00
Sujeevan Vijayakumaran 7bd50f2474 Small fixes for i18n documentation (#655)
* Removed HEADSUP since it looks not relevant anymore

* Replaced 'jenkins' with 'Jenkins'

* Fixed broken Link to create pull-requests

* Fixed broken image in i18n documentation
2016-12-12 01:03:13 -08:00
James William Dumay f8a134cc04 Rename modules so they follow a more friendly scheme when viewed in the update center (#649) 2016-12-12 15:03:52 +11:00
Ivan Meredith 458c2a363c [JENKINS-39907] Hardcode default org to always be jenkins (#653) 2016-12-09 16:32:37 +13:00
Michael Neale 6f0c796f3a [maven-release-plugin] prepare for next development iteration 2016-12-09 13:12:38 +11:00
Michael Neale 57f3219f1f [maven-release-plugin] prepare release blueocean-parent-1.0.0-b13 2016-12-09 13:12:23 +11:00
Ivan Meredith c4ab96013e [JENKINS-40296] Display latest commit messages (#651) 2016-12-09 13:01:24 +13:00
Ivan Meredith b2cd597a1d [JENKINS-39761] Artifact perfomance changes (#643)
* [JENKINS-39761] Artifact perfomance changes

* Use mobxUtils.fromPromise

* Correct UI element positioning

* Donot show the artifacts are limited until we reach 100

* Test for backend

* Add documentation to ActivityService

* Install updated JDL

* Rework backend

* Remove unused imports

* Remove unneeded BlueArtifacts

* Fix tests

* Release beta version of core-js

* Fix backend tests

* Change how files are generated in tests

* Use base to create files

* Fixes from PR comments

* Fetch 101 so that we know if there are more

* Update artifacts when job completes

* Fix unit test

* Update npm versions
2016-12-08 13:56:05 +13:00
vivek e47c2bd0f4 Story/jenkins 38491 Status of stages/parallels waiting for input (#645)
* JENKINS-38491# Status of stages/parallels waiting for input

* Status of Run, Node and Steps done, tests added.

* Fixed build error

* Removed unused WAITING_FOR_INPUT result

* minor refactoring, fix

* workaround for missing jdl/styles for paused state
2016-12-07 10:23:35 +11:00
Tom Fennelly 63c876b598 Fix for null return on request.getPageInfo() in ResourceCacheControl (#644) 2016-12-06 09:55:01 +11:00
James William Dumay dff8076cf4 Update README.md 2016-12-06 08:47:17 +11:00
James William Dumay d68a909932 Add Rollbar badge 2016-12-06 08:46:26 +11:00
vivek b7c8316381 JENKINS-40135# NPE fix with non-block stages without steps in between (#646)
Root cause is MemoryFlowChunk.getLastNode() returns null, its supposed to be @Nonnull.
JENKINS-40200 raised, as work around null check is added.
2016-12-03 10:58:45 -08:00
Tom Fennelly c584c6a2ed Move the i18n rest endpoint under "rest" (#642)
* Move the i18n rest endpoint under "rest"

Keeping my buddy Vivek happy !!

* Totoally skip i18n api integration tests of the JenkinsRule barfs and doesn't load the plugins.

* js-extensions 0.0.32

* blueocean-core-js 0.0.32

* Updated ui plugins to release versions of js-extensions and core-js
2016-12-01 22:42:34 -08:00
Keith Zantow f42d71cc1b JENKINS-39852 - loading worm not finishing (#641) 2016-12-01 13:57:15 -08:00
Thorsten Scherler 7a5feac040 FIX Jenkins 39229 regression initial stage run does not show graph (#638)
* [JENKINS-39229_Regression_Initial_stage_run_does_not_show_graph] Using Util to test for stage

* [JENKINS-39229_Regression_Initial_stage_run_does_not_show_graph] Fix generation of stage_id

* [JENKINS-39229_Regression_Initial_stage_run_does_not_show_graph] remove debug statement
2016-12-01 09:52:39 -08:00
Josh McDonald 296ed38541 bug/JENKINS-39927-table-indent-regression * Add a util className to add left-right table padding to tables without default padding for <a>s (#640) 2016-12-01 19:06:56 +10:00
Tom Fennelly 2a0ae40645 Add block in BlueI18nTest to wait until the JenkinsRule has the plugins installed (#639)
this is a pain
2016-11-30 18:17:39 -08:00
James William Dumay 4b44e6dcee JENKINS-36907 Raw encode the organization name (the instance display … (#634) 2016-12-01 10:50:34 +11:00
James William Dumay 969f1a189c Update README.md (#635) 2016-12-01 09:44:53 +11:00
Tom Fennelly e41c758e12 [JENKINS-39891] i18n plugin for Blue Ocean (#621)
* i18n plugin skeleton

* i18n plugin wired into aggregator

* BlueI18n.getBundleParams

* BlueI18n.getBundle

* browser cacheable

* Add plugin version info to JenkinsJSExtensions

... and clean up rewrite coding poop

* Change ExtensionStore-spec#makeExtensionStore to perform the mockDataLoad too

remove some noise from the tests

* ExtensionStore.getPluginVersion

* Changes to get blue ocean using the new i18n plugin

... not working though, yet.

* More changes to get blue ocean using the new i18n plugin

... still not working, but getting closer ... issue now is with the initialization of i18next, for which I need to touch base with Thorsten.

* More changes to get blue ocean using the new i18n plugin

... kinda working now from the pov of it loading bundles etc ... getting strange 404 errors though on e.g. the activity page.

* More i18n fun

* Set language from i18next translate instance

* print error when config not located

* Fix wrong bundle name

* Disabling AbstractRunImplTest.replayRunTestMB because of JENKINS-40084

* Fix plugin tests

* Fix JenkinsJSExtensionsTest

* parse the original request URL (undeoced) in the i18n plugin

* Update js-builder to 0.0.50

* js-extensions 0.0.32-tfbeta1

* Remove stray character from dodgy merge

* @jenkins-cd/js-extensions v 0.0.32-tfbeta1 on core-js

* @jenkins-cd/blueocean-core-js v 0.0.32-tfbeta1

* Updating versions of @jenkins-cd/blueocean-core-js  @jenkins-cd/js-extensions on the plugins

* Remove the lang from the rest call and let the API get it from the headers set by the browser

* Put lang back into the rest url

* Setting of package versions in package.json and shrinkwrap.json

* Fix js-builder version in js-extensions

* Fix npm deps

* Remove unused import

* Stop i18ext from loading dev bundle

* Set the translator language from the default detector Vs expecting it to be on the I18n instance
2016-11-30 13:18:10 -08:00
Yoann Dubreuil 6283c1375e [JENKINS-39804] Script to build the official BlueOcean image (#628) 2016-11-30 09:16:53 +01:00
Cliff Meyers 2dd5cca04c Bug/jenkins 39890 tables no ellipses (#614)
* [JENKINS-39890] introduce some components for dealing with embedding Links inside of Tables - still a WIP

* [JENKINS-39890] use a fault handler instead of a catch() block so that React errors aren't swallowed

* [JENKINS-39890] use updated JDL table

* [JENKINS-39890] disable default padding from JDL table; remove "line-height hack" from a tag to force height

* [JENKINS-39890] fix errors from last merge

* [JENKINS-39890] pull in latest table from JDL; adjust Dashboard and Activity to use correct classes

* [JENKINS-39890] fix broken link in "branch" cell; remove obsolete padding from table cell

* [JENKINS-39890] switch over Branches tab to use CellLink API; adjust padding issues

* [JENKINS-39890] switch over PR's tab to use CellLink API; tweak actions alignment

* [JENKINS-39890] remove obsolete classes

* [JENKINS-39890] fix lint

* [JENKINS-39890] fix broken unit tests

* [JENKINS-39890] use a stateless functional component

* [JENKINS-39890] bring back "id" attribute for rows to avoid ATH failures
2016-11-29 13:08:12 -08:00
Ivan Meredith ca2bdb08b4 [JENKINS-39992] Fix has() on Pager (#630)
* [JENKINS-39992] Fix has() on Pager

* Update blueocean-web's core-js

* Release core-js
2016-11-29 09:19:54 +13:00
Cliff Meyers 2c7f1c6790 release prod version of latest js-extensions; update blueocean to use it (#631) 2016-11-28 11:25:47 -05:00
Thorsten Scherler 1c13b6393b [FIX JENKINS-40027_REGRESSION_cant_open_branches_that_have_/_in_the_name]… (#627)
* [JENKINS-40027_REGRESSION_cant_open_branches_that_have_/_in_the_name] fix path

* [JENKINS-40027_REGRESSION_cant_open_branches_that_have_/_in_the_name] ShrinkCrap

* [JENKINS-40027_REGRESSION_cant_open_branches_that_have_/_in_the_name] silly change to trigger ATH

* Add tests and use encodeURIComponent

* Publish interim version of core-js
2016-11-28 19:37:07 +13:00
James William Dumay f0e9fde8c3 Update PULL_REQUEST_TEMPLATE 2016-11-28 15:04:26 +11:00
Thorsten Scherler b9ee8603dd FIX Jenkins 39809 404 pages appear only in pipeline page children (#622)
* [JENKINS-39809_404_pages_appear_only_in_pipelinePage_children] start move not found to core

* [JENKINS-39809_404_pages_appear_only_in_pipelinePage_children] use new location

* [trashed_JENKINS-39809_404_pages_appear_only_in_pipelinePage_children] Use https://github.com/jenkinsci/blueocean-plugin/pull/616 to fix extension bug. thx tfennellyg ds

* [trashed_JENKINS-39809_404_pages_appear_only_in_pipelinePage_children] Fix deps for the builder and remove dups in package.son

* [trashed_JENKINS-39809_404_pages_appear_only_in_pipelinePage_children] fix imports

* [trashed_JENKINS-39809_404_pages_appear_only_in_pipelinePage_children] add FIXME note and raise version

* [trashed_JENKINS-39809_404_pages_appear_only_in_pipelinePage_children] Use notFound component

* [trashed_JENKINS-39809_404_pages_appear_only_in_pipelinePage_children] use new plugin version

* eslint - formating changes and fix offences

* [JENKINS-39809_404_pages_appear_only_in_pipelinePage_children] remove unused imports

* [JENKINS-39809_404_pages_appear_only_in_pipelinePage_children] I hate shrinkCrap so much. I have lost so many hours on that crap tool. Unbelievable

* Fix grammar
2016-11-25 11:31:40 +01:00
Thorsten Scherler 87faf166ad [JENKINS-39989_Core-js_had(s)_broken_lint_and_tests_-_should_be_run_as_part_of_pipeline] Add preinstall hook that run the tests (#625) 2016-11-24 20:44:24 +01:00
Ivan Meredith 7be34f65cf [JENKINS-39979] Check for multibranch capabilities sooner (#623) 2016-11-24 20:29:56 +13:00
Ivan Meredith 8600c1b91d [JENKINS-39981] Show activities properly when no activities exist (#624)
* [JENKINS-39981] Show activities properly when no activities exist

* Fix linting

* Add tests. Fix Tests. Fix Logic
2016-11-24 17:24:30 +13:00
Cliff Meyers 1a576d3c11 Feature/jenkins 38595 create pipeline scm list (#572)
* [JENKINS-38594] wire up new "create-pipeline" route; stub out directory for new "Creation" submodule

* [JENKINS-38594] update CreatePipeline to use a placeholder dialog until component is ready; integrate with existing router / background code so the existing screen's DOM will display under the dialog

* [JENKINS-38594] refine logic for handling background for enter/leave of create-pipeline

* [JENKINS-38595] enhance ExtensionRenderer so that extensions can be force reloaded / re-rendered

* [JENKINS-38595] tick up js-ext deps

* [JENKINS-38595] visual components for multi-step flows

* [JENKINS-38595] ExtensionPoint APIs for contributing to SCM provider list and workflow steps in Create Pipeline

* [JENKINS-38594] delint

* [JENKINS-38594] code refactoring to improve comprehensibility

* [JENKINS-38595] visual refinements to step indicators

* [JENKINS-38595] quick and dirty version of Git creation flow to test out new MultiStepFlow / FlowStep APIs

* [JENKINS-38594] add some security util methods; add some defense against NPE in config

* [JENKINS-38594] only show the "New Pipeline" link if the user has permission

* [JENKINS-38595] simplify down the extension point API to use a single XP; lay the groundwork for "re-entrant" flow; still needs to sandbox rendered content

* [JENKINS-38595] extract the "sandboxing" of rendering (via ReactDOM.render) so that we can have more flexibility with how we render untrusted React code

* [JENKINS-38595] add proper error handling and sandboxed rendering to the extension points in Create Pipeline flow

* [JENKINS-38595] fix a bug where the "activeStep" was falling out of sync w/ props.children and yielding incorrect statuses for steps; now we just track the current step index which is much simpler anyways

* [JENKINS-38595] add support for "percent complete" for steps

* [JENKINS-38595] add a "GitCreationManager" to centralize logic that needs sharing between the two steps; enhance "CompletedStep" to display its progress via percent complete

* [JENKINS-38595] delint

* [JENKINS-38595] fix failing test

* [JENKINS-38595] comments and renaming

* [JENKINS-38595] add support for className on SandboxedComponent; eliminate duplicate ContextBridge; update deps after beta publish

* [JENKINS-38595] CSS tweak

* [JENKINS-38595] more comments

* [JENKINS-38595] rename for clarity

* [JENKINS-38594] add a "blueCreate" query string switch to turn on new UI conditionally; by defaul the Jenkins classic UI will be used

* [JENKINS-38594] fix accidental disabling of hibernate page refresh

* [JENKINS-38595] republish merged js-extensions to fix missing "SandboxedComponent" error

* [JENKINS-38595] remove the temp dialog and use a full-screen Dialog instead
2016-11-23 15:07:20 -05:00
Thorsten Scherler e8b7e06a6d Mobx conversion regressions in core js (#620)
* eslint - formating changes and fix offences

* [Mobx_conversion_regressions_in_core-js] publish core version and use it

* [Mobx_conversion_regressions_in_core-js] bump version

* [Mobx_conversion_regressions_in_core-js] add prepublish target again

* [Mobx_conversion_regressions_in_core-js] I hate you so much shrinkCrap!
2016-11-23 14:50:18 +01:00
Ivan Meredith b2877037ff [JENKINS-37250] Redux to MobX conversion
* Added new services and models to deal with core data of pipelines and activities
* Fetching now has deduplication by default
* Fetching now adds capabilities to data where relevant
2016-11-23 17:59:08 +13:00
Alexander Shorin fab21e9c1e Fix border color for try blue ocean button (#617)
This is #598 follow up fix.
2016-11-23 12:14:30 +11:00
Cliff Meyers 5de4d5c846 [JENKINS-39890] use a fault handler instead of a catch() block so that React errors aren't swallowed (#615) 2016-11-22 12:05:04 -05:00
vivek 5715b8aaf0 JENKINS-39661# /runs and /activity performance fix (#604)
* JENKINS-39661# /runs and /activity performance fix

* Incorporated Keith and James feedback.

* Multibranch pipeline /activities and /runs API optimization

* Failing test fix.

* Incorporated Tom and Keith feedback.

Also fixed a bug in multi-branch across branch pagination

* For a multi-branch project fetch max up to 250 runs for each branch.

This value can be changed using JVM proerty MAX_MBP_RUNS_ROWS.
2016-11-22 08:00:19 -08:00
vivek f4e549bc9c JENKINS-39463# Integrate pipeline-model-definition 0.6 (#594)
* JENKINS-39463# Integrate pipeline-model-definition 0.5

* Reverted tests as they should pass eventually once JENKINS-39631 is resolved.

* Build with declarative plugin snashot build for further testing.

* Updated released declarative pipeline version 0.6

* Use NOT_BUILT to represent result and state for step/stages not built.
2016-11-21 20:25:29 -08:00
Thorsten Scherler 26e99db91a Jenkins 39846 REGRESSION: incorrect capitalisation and stop button not visible in running build due to i18n (#607)
* [JENKINS-39846_regressions_caption_defaultFallback_i18n] Fix captions for web

* [JENKINS-39846_regressions_caption_defaultFallback_i18n] fix import of I18n lib

* [JENKINS-39846_regressions_caption_defaultFallback_i18n] Implement default configuration if no windows is defined and use it in test (removing i18next as direct dep)

* [JENKINS-39846_regressions_caption_defaultFallback_i18n] Use updated version of the core-js

* [JENKINS-39846_regressions_caption_defaultFallback_i18n] raise version afer publish
2016-11-21 11:19:58 +01:00
Thorsten Scherler d1a80909c9 Jenkins 39849 failed requests for i18n bundles (#606)
* [JENKINS-39849_failed_requests_for_i18n_bundles] remove debug code

* [JENKINS-39849_failed_requests_for_i18n_bundles] prevent double slash

* [JENKINS-39849_failed_requests_for_i18n_bundles] using new core-js version
2016-11-21 10:14:08 +01:00
Josh McDonald 81cc2eba57 bug/JENKINS-39853-table-actions * Partial fix for Dashboard table issues (#611) 2016-11-21 15:36:26 +10:00
Michael Neale affc7bfee4 fix for cog and anon JENKINS-39889 (#610) 2016-11-21 10:24:33 +11:00
vivek 6b743eafd1 JENKINS-39775# EmptyStackException fix (#605) 2016-11-17 19:30:14 -08:00
James William Dumay 648e6579f0 Update autofavorite and display url plugin versions (#603) 2016-11-18 09:43:10 +11:00
Thorsten Scherler b5c311b488 [master] use stable versions 2016-11-17 20:02:35 +01:00
Thorsten Scherler ed76c2cb72 [FIX JENKINS-39225] Internationalisation for Blue Ocean and JDL (#556)
* [JENKINS-35845] WIP first steps with i18n

* eslint - formating changes and fix offences

* [JENKINS-35845] first basic working version with 2 languages

* [JENKINS-35845] WIP adding patched backend to investigate

* [JENKINS-35845] Assumes https://github.com/jenkinsci/jenkins/pull/2586 to be applied. We know get the translations from the standard jenkins way, but needs changes in the core for now

* [JENKINS-35845] remove testing class

* [JENKINS-35845] WIP fixing integration of jenkins i18n keys with dot in them

* [JENKINS-35845] move i18n to core-js and using it from within web

* [JENKINS-35845] follow jenkins convention/pattern for storing locale

* [JENKINS-35845] WIP implement translation in core-js runbutton and toastUtil. Prefix translations with bo.web. remove sample code

* [JENKINS-35845] WIP starting to translate dashboard

* [JENKINS-35845] WIP added german translation and finished dashboard. Currently working on making moment respect the locale

* [JENKINS-35845] Use latest jdl

* [JENKINS-35845] fix locale retrieval

* [JENKINS-35845] update stories to use the new i18n functions. Create story to test readableDate and timeDuration. In node this works fine now traking down wht not in dashboard

* [JENKINS-35845] Add spanish translations

* [JENKINS-35845] better translation for spanish (thank you @Dario) as well updated some german translations

* [JENKINS-35845] Fix german translations with feedback from @daniel

* [JENKINS-35845] Fix test view and finish translation

* [JENKINS-35845] Fix german translation. fix result views. fix some lint issues.

* [JENKINS-35845] create a compose function to wire different functions together

* [JENKINS-35845] better documentation and remove debug string

* [JENKINS-35845] add documentation about i18n and linking contributing and i18n docu in principal readme.

* [JENKINS-35845] updte docu

* [JENKINS-35845] Pass locale and translation function down the component tree. Fix links to not drop query parameters.

* [JENKINS-35845] Fix links to not drop query

* [JENKINS-35845] use the url config util to get correct path to jenkins

* [JENKINS-35845] WIP security commit - refactor i18n class to support listener and subscribe to i18nChanges. Will allow to drop react specific integration via react-i18next.

* [JENKINS-35845] WIP security commit

* [JENKINS-35845] remove debug statement

* [JENKINS-35845] fix import

* Task/jenkins 35845 i18n key rename (#579)

* [JENKINS-35845] rework i18n keys for home page

* [JENKINS-35845] l10n for activity tab

* [JENKINS-35845] l10n for branches tab

* [JENKINS-35845] l10n for pull requests tab

* [JENKINS-35845] l10n for run details -> pipeline

* [JENKINS-35845] l10n for run details -> changes, tests, artifacts

* [JENKINS-35845] l10n for run details header changes

* [JENKINS-35845] pagination; fix typo causing error

* [JENKINS-35845] order keys

* [JENKINS-35845] WIP security commit to be able to merge master

* eslint - formating changes and fix offences

* [JENKINS-35845] Remove dep to snapshot-jenkins and implement fallback to default values. Fix tests of all related projects.

* [JENKINS-35845] remove duplicate variable

* [JENKINS-35845] fix test by adding polyfy again

* eslint - formating changes and fix offences

* [JENKINS-35845] Fix last test

* [JENKINS-35845] fix tests for dashboard

* [JENKINS-35845] sync version numbers
2016-11-17 19:15:26 +01:00
Keith Zantow 1ce912340a [FIX JENKINS-37516] - js-extensions tests were broken (#549)
* JENKINS-37516 - js-extensions tests were broken
2016-11-17 11:09:30 -05:00
James William Dumay e29025df6a JENKINS-39764 Generate URL to user avatar on User resource (#601) 2016-11-17 10:35:20 +11:00
Michael Neale b0d66b6b7d Time to spring clean the readme/devdocs (#602) 2016-11-17 10:12:18 +11:00
Tom Fennelly 89aee848ad [FIX JENKINS-39624] Pre-load user data on page config object (#600)
* Simple Stapler ModelObjectSerializer

* Inject the full user object into the Jenkins config object

* temp delete of shrinkwrap so it won't wreck my head during dev ... will revert later

* Mods to get user from AppConfig Vs making a fetch

* Make default user "anonymous"

* remove AnonUser

* temp delete of shrinkwrap so it won't wreck my head during dev ... will revert later

* Refactoring to move User into core-js and ...

... cleanup some other things relating to the mixing of globally injected data.

* Fixups for anonymous user

* Revert "temp delete of shrinkwrap so it won't wreck my head during dev ... will revert later"

This reverts commit 99fb1e023c2e47b4075ee85dbf94c768887d902c.

* Revert "temp delete of shrinkwrap so it won't wreck my head during dev ... will revert later"

This reverts commit 27f3ff9464a86ad7180bf048690f90b86caba3cd.

* core-js v 0.0.22

* core-js v 0.0.22 update for dependants
2016-11-16 15:59:51 +00:00
Vivek Pandey 81a67eac66 [maven-release-plugin] prepare for next development iteration 2016-11-14 17:29:05 -08:00
Vivek Pandey d556c1ed52 [maven-release-plugin] prepare release blueocean-parent-1.0.0-b12 2016-11-14 17:29:00 -08:00
Ivan Meredith 3f283a2360 [JENKINS-39336] Add links to items for activities (#595)
* [JENKINS-39336] Add links to  items for activities

Adding links to all items in the row allows the item to be
opened in a new tab with right click.

* Fix tests

* Remove styling from hidden anchor tags
2016-11-15 13:34:40 +13:00
Alexander Shorin e8955bfcf8 Use global style colors for try ocean button (#598)
When custom Jenkins styling is used, like jenkins-contrib-themes[1], 
the "Try" button breaks the style and looks alien[2]. This patch fixes 
this[3] by removing blue background, allowing to reuse global one.

[1]: https://jenkins-contrib-themes.github.io/jenkins-material-theme
[2]: http://i.imgur.com/R8B6sKS.png
[3]: http://i.imgur.com/hwxJwBj.png
2016-11-15 11:33:46 +11:00
James Dumay f1eef22c4d Bundle autofavorite 0.3 2016-11-15 09:21:44 +11:00
Ivan Meredith 826cb28bb2 [JENKINS-39202] PR column renames (#597) 2016-11-15 10:40:09 +13:00
Josh McDonald 5c356b1d9a JENKINS-39627 Alignment of cog is off on Result (#596)
* bug/JENKINS-39627-buttonbarlayout * Adjustments to button bar and settings icon to fix layout

* bug/JENKINS-39627-buttonbarlayout * Fix eslint 'errors'
2016-11-14 13:00:55 +10:00
James Dumay 953a8421d1 Disable autofavoriting for the next release 2016-11-14 13:04:54 +11:00
James Dumay 474ae49dd4 Revert "Remove impl on abstract class"
This reverts commit 94cd748cd3.
2016-11-11 13:55:29 +11:00
James Dumay 94cd748cd3 Remove impl on abstract class 2016-11-11 13:53:43 +11:00
James Dumay da1f25b662 Move getActivities to abstract class 2016-11-11 13:53:11 +11:00
Thorsten Scherler 1ba6c8edb0 [FIX JENKINS-39587] refactor to not use context but use standard properties (#590)
* [zendesk40306] refactor to not use context but use standard properties

* [zendesk40306] fix test

* [zendesk40306] remove blank line

* [zendesk40306] Drop more context usage and fixed infinitve loop

* [zendesk40306] fix tests

* eslint - formating changes and fix offences
2016-11-09 17:04:23 +01:00
James Dumay 7b7d2858e5 Bump favorite to 2.0.2 2016-11-09 08:46:41 +11:00
Max Knee e0d86c083c Updating contribution docs (#589) 2016-11-08 13:44:53 +00:00
James Dumay f8750cc0e7 Switch from deprecated method 2016-11-08 09:47:21 +11:00
James William Dumay 8ce8915b04 JENKINS-36377 bundle blueocean-autofavorite (#586) 2016-11-07 09:52:38 +11:00
James William Dumay 5377fb6038 JENKINS-36377 Upgrade to new favourites API (#580) 2016-11-05 15:30:29 +11:00
Michael Neale 1688221437 link to classic config screen JENKINS-39338 (#583)
* working on it

* move to url utils

* test coverage and lint

* use url utils not window

* Check if logged in before showing links

* use fullName not fullDisplayName
2016-11-04 14:06:51 +00:00
Tom Fennelly 6872affa3e [FIX JENKINS-39339] contextual try Blue Ocean button (#582)
* Try blue ocean jumping to job

Working woth multibrach ... still need to test/verify other job types plus test folders etc

* Need to escape the fullName for non multibrach too

* Only encode job fullName path separators

Coz the rest of the name is already encoded

* Refactor to create BlueOceanWebURLBuilder

* Added blueocean-events/index.jelly for IDE test runs

JenkinsRule is not able to find plugins if they do not have an index.jelly :)

* freestyle job unit test

and fix url encoding to match Javascript

* multibranch job unit test

* Change try blue ocean to a link
2016-11-04 08:59:05 +00:00
Scott Busche 3e4a7bb850 [blueocean-core] Swap from gulp-clean to del (#584)
gulp-clean is deprecated in favor of using del, swap to del. This has the
nice side effect of removing a few dependencies as del was already a
dependency of another package
2016-11-03 21:50:36 +00:00
James William Dumay 2b0ce3fb6d Upgrade rollbar to 1.9.2 (#575) 2016-11-04 07:22:56 +11:00
Cliff Meyers 1316346503 Feature/jenkins 38594 create pipeline begin (#567)
* [JENKINS-38594] wire up new "create-pipeline" route; stub out directory for new "Creation" submodule

* [JENKINS-38594] update CreatePipeline to use a placeholder dialog until component is ready; integrate with existing router / background code so the existing screen's DOM will display under the dialog

* [JENKINS-38594] refine logic for handling background for enter/leave of create-pipeline

* [JENKINS-38594] delint

* [JENKINS-38594] code refactoring to improve comprehensibility

* [JENKINS-38594] add some security util methods; add some defense against NPE in config

* [JENKINS-38594] only show the "New Pipeline" link if the user has permission

* [JENKINS-38594] add a "blueCreate" query string switch to turn on new UI conditionally; by defaul the Jenkins classic UI will be used

* [JENKINS-38594] fix accidental disabling of hibernate page refresh

* tick version number after releasing 0.0.21

* [JENKINS-38594] use prod release of blueocean-core-js
2016-11-03 10:29:36 -04:00
Michael Neale 789da3dd7d Bug/jenkins 39425 empty stages (#581)
* Candidate fix for NPE

* only check for firstExecuted
2016-11-03 10:48:49 +11:00
Vivek Pandey ab145f1620 [maven-release-plugin] prepare for next development iteration 2016-10-31 17:14:25 -07:00
Vivek Pandey 915bc218b7 [maven-release-plugin] prepare release blueocean-parent-1.0.0-b11 2016-10-31 17:14:19 -07:00
James William Dumay 6cd2155c75 JENKINS-39206 - BlueOcean Display URL Plugin (#574) 2016-10-31 10:24:00 +11:00
Cliff Meyers 0608e5a026 [JENKINS-39295] fix a bug where the icons for the "expandable path" component were the wrong color on dark backgrounds (#576) 2016-10-28 09:03:56 -04:00
vivek 849fc8650b JENKINS-39296# Fix to show step's status correctly (#577) 2016-10-27 21:39:28 -07:00
Josh McDonald 47f591995b bug/JENKINS-39201 * Fix for showing UNSTABLE nodes correctly, + tests (#570) 2016-10-26 14:53:48 +10:00
Vivek Pandey 6de837223b [maven-release-plugin] prepare for next development iteration 2016-10-24 16:40:49 -07:00
Vivek Pandey 24467ad959 [maven-release-plugin] prepare release blueocean-parent-1.0.0-b10 2016-10-24 16:40:44 -07:00
Cliff Meyers 72610dcd0b [JENKINS-38023] fix tests that began failing in 38023 but were only exposed in CI after changes in 38013 (#571) 2016-10-24 18:33:06 -04:00
Tom Fennelly 624eb0bb0d [FIX JENKINS-38013] Fix bloated JavaScript bundles (#569)
* Updating the node and npm versions

* Added .watch_trigger ignore

* js-builder 0.0.47

* Drop node and npm versions back to 6.4.0 and 3.10.3

* Update blueocean-config build

* Update blueocean-dashboard build

* Update blueocean-personalization build

* Updated js-builder version in js-extensions

This does not require a new build of js-extensions. It's just to keep the checkdeps happy

* Fix FavoritesSseListener initialization to not throw SSE connection exception when there's no window etc e.g. in a test

* Updated docs to remove references to Gulp
2016-10-24 11:13:41 +01:00
Cliff Meyers de288bef83 Bug/jenkins 38023 show expandable path (#563)
* [JENKINS-38023] pull in new JDL with ExpandablePath component

* [JENKINS-38023] implement "ExpandablePath" on dashboard screens

* [JENKINS-38023] tick up JDL version

* [JENKINS-38023] fix tests that broken when introducing ExpandablePath; port to Enzyme; delint

* [JENKINS-38023] disable test that was broken (yet not failing build?) due to some an error about SSE connection being undefined

* [JENKINS-38023] tweak favorites card, pipeline page header and run details header to show displayName if available; still needs a fix for favorited branch as the displayName returned is the branch name, not the parent pipeline's displayName

* [JENKINS-38023] use new "fullDisplayName" property instead of trying to concatenate fullName and displayName

* Added fullDisplayName to BluePipeline model.

fullDisplayName is similar to fullName, except each segment is displayName if present.
Note: each segment is delimited by '/' and each segment is url encoded

* [JENKINS-38023] handle URI-encoded path elements; handle multibranch in Favorite card

* Fix to properly url encode display name

* [JENKINS-38023] use official JDL release
2016-10-21 10:13:49 -04:00
vivek 2cbc261e3b Task/jenkins 37667 Adopt Bismuth API to build pipeline node DAG (#557) 2016-10-20 17:05:24 -07:00
Tom Fennelly 6750113028 Fix IE adjunt loading (#568)
This broke because of a change we made in js-builder a few weeks ago wrt how it generates the paths to bundle assets
2016-10-18 11:42:09 +01:00
Vivek Pandey 8346066673 [maven-release-plugin] prepare for next development iteration 2016-10-17 16:28:34 -07:00
Vivek Pandey baf2bd6e98 [maven-release-plugin] prepare release blueocean-parent-1.0.0-b09 2016-10-17 16:28:28 -07:00
vivek 16a97412fd JENKINS-38335# Slow search API response fix (#566)
numberOfRunningPipelines is very expensive call, pretty much results in to
iteraring over all builds in the pipeline. With Search API, it gets worse as we
recursively iterate over each job/pipeline and determine if its running or not.
There is no esay way in Jenkins to compute this value optimally.

There is no UI code that needs this field and also numberOfQueuedPipelines field
is not used. As part of this fix, I am removing these fields from Pipeline API response.
2016-10-14 09:45:12 -07:00
Cliff Meyers 3d14e84406 improve docs for shrinkwrap (#565) 2016-10-14 11:44:57 +11:00
Ivan Meredith 5686ec1f5f Add an example for a pullrequest (#564) 2016-10-14 11:44:39 +11:00
Yoann Dubreuil 4ad4370873 Don't run hpi:assemble-dependencies again (#561)
It's already done in blueocean/pom.xml to make plugins available
for building the development Docker image.
2016-10-13 08:15:02 +02:00
Tom Fennelly b959a490bc [FIX JENKINS-37206 and JENKINS-38925] New SSE Gateway client for better headless sse for ATH stability (#562)
* Updates to use the latest SSE client API (stateless)

* Get the build passing - sse and core-js fixes and tweaks

* [JENKINS-38925] ensure shrink is updated to use new core-js and remove sse-gateway from dashboard/personalization

* core-js version 0.0.20

* core-js version 0.0.20 on dependants
2016-10-12 19:38:16 +01:00
Adrien Lecharpentier 0e622a706f Prefer the displayName rather than the fullName for prettier display (#559)
* Have a pretty display on the job names

* Make sure the displayName is used to show the job

* displayName doesn't contain path to the job

* displayName doesn't contain the path to the job

On request of @scherler
2016-10-11 12:37:53 +02:00
Tom Fennelly 079f73a04d Update to use the new plugin parent pom (2.17) (#558)
* Update to use the new plugin parent pom (2.17)

* Adding explicit config of `maven-surefire-plugin`

This is to work around https://issues.jenkins-ci.org/browse/JENKINS-38888
2016-10-11 10:45:37 +01:00
Michael Neale 97102f3c70 [maven-release-plugin] prepare for next development iteration 2016-10-11 16:17:13 +11:00
Michael Neale 9927f75074 [maven-release-plugin] prepare release blueocean-parent-1.0.0-b08 2016-10-11 16:17:01 +11:00
Keith Zantow 628acf9cc6 [FIX JENKINS-37024] fix detail modal background (#534)
* JENKINS-37024 - keep existing background when displaying/hiding modal
* Fix erroneous fetch from RunDetails
* Bump js-extensions version & publish
2016-10-10 23:52:19 -04:00
Thorsten Scherler 2cf97088a7 [master] raise version of jdl and fix shrinkCrap. 2016-10-10 13:36:43 +02:00
Thorsten Scherler ac7f45a9e7 [master] raise version numbers and dependencies 2016-10-10 13:19:34 +02:00
Thorsten Scherler 40e04c273d FIX JENKINS-38673 (#552)
* [updateDeps] Raise all versions of deps besides the router, if we raise the router we need to fix the code

* eslint - formating changes and fix offences

* [updateDeps] fix bug in freestyle project for the full log url

* [updateDeps] Update readme about the experience I made updating the deps

* [JENKINS-38673] fix declarations and add note about the dev flag to be used
2016-10-10 12:16:24 +02:00
Thorsten Scherler 434966d4af [ncuUpdateScript] add a small script to update our main plugins (#555) 2016-10-06 14:57:25 +02:00
Thorsten Scherler 8a6a4f2ecd FIX JENKINS-38248 Log should only be linkable from its line number (#550)
* [JENKINS-38248] refactor code to use tables and use the first column as line number to link

* eslint - formating changes and fix offences

* [JENKINS-38248] Fix bug for collapsing steps when focused by hash. Before they would never collapse.
2016-10-06 14:54:33 +02:00
Yoann Dubreuil f70e55df71 Speed up Maven downloads by using a proxy and increasing DL thread pool size (#554) 2016-10-05 17:31:39 +02:00
Tom Fennelly c21741b563 [JENKINS-38013] Set the Cache-Control header for static and adjunct resources (#551)
* Set the Cache-Control header for static and adjunct resources

* Eliminate Expires header as suggested by kzantow

* Don't enable ResourceCacheControl if running with hpi:run

* ResourceCacheControl unit test
2016-10-05 11:57:34 +01:00
Thorsten Scherler 9ccbedff8d [JENKINS-37017] Implement full title in detail view (#548) 2016-10-03 22:23:05 +02:00
Thorsten Scherler fc8bb5958b [FIX JENKINS-37925] change full log link in steps to the backend log (#545)
* [JENKINS-37925] change full log link in steps to the backend log

* eslint - formating changes and fix offences
2016-10-03 22:22:46 +02:00
Thorsten Scherler 6329564926 [JENKINS-36781] fix commit id in detail view (#546) 2016-10-03 22:22:26 +02:00
Thorsten Scherler 633a010e3b [npeFix] prevent npe on edges (#547) 2016-10-03 22:21:58 +02:00
Tom Fennelly c9c1f9b618 Fix ATH build trigger param names (#544) 2016-09-29 17:43:31 +01:00
paladox 89d5a2d0be Update es6-promise to 3.3.1 (#541)
* Update es6-promise to 3.3.1

See changelog at https://github.com/stefanpenner/es6-promise/blob/master/CHANGELOG.md#331

Difference between 3.2.1 and 3.3.1.

* Update es6-promise to 3.3.1

* Update es6-promise to 3.3.1
2016-09-29 11:05:08 +02:00
Thorsten Scherler 5564709a0c [JENKINS-38009] in running mode we do not want to expand the failure node (#532) 2016-09-29 11:03:01 +02:00
Thorsten Scherler 83f71acb95 FIX JENKINS-38056 Duration of running Pipeline is not live (#531)
* [JENKINS-38056] raise version for preview

* [JENKINS-38056] better indication of passing time in running jobs

* [JENKINS-38056] need to install the dep due to shrinkwrap

* [JENKINS-38056] need to install the dep due to shrinkwrap

* eslint - formating changes and fix offences

* [JENKINS-38056] make shrinkwrap happy

* [JENKINS-38056] fix npm shrinkwrap

* [JENKINS-38056] use published of jdl now, since I just published it. This way I simply can merge and do not have to tick the version in master.
2016-09-28 20:04:23 +02:00
Thorsten Scherler 4d1c750a6b [binHelper] Implement script to install a dep in various places, incl… (#542)
* [binHelper] Implement script to install a dep in various places, includes removing the node_modules folder ogf the lib to be installed

* [binHelper] add hint to checkXXX scripts to use cleanInstall
2016-09-28 17:14:42 +02:00
Michael Neale 4299e97b1c Update Jenkinsfile (#535) 2016-09-28 13:13:14 +10:00
Tom Fennelly 9ee1fe3d9c Assemble and archive plugins (#533)
* Assemble and archive the HPI plugins that the ATH can use

* No need to pass the JOB_NAME

* ooops ... run assembly from inside the aggregator

* might get it eventually
2016-09-27 20:18:50 +01:00
Cliff Meyers ca8ec532cd Bug/jenkins 37425 run stop toast changes (#515)
* [JENKINS-36687] build a utility function that converts a Run's HAL URL to the corresponding UI URL, to avoid encoding bugs

* [JENKINS-36687] comments

* [JENKINS-36687] add support for styles and assets in blueocean-core-js; add some assets for testing and in prep of "Run Pipeline" refactor

* [JENKINS-36687] rework the blueocean-web build so it can support multiple "libraries" (e.g. JDL and CoreJS) in the src/main/webapp/assets directory

* [JENKINS-36687] amend .gitignore

* [JENKINS-36687] UrlBuilder: more tests, better errors

* [JENKINS-36687] typos ;)

* [JENKINS-36687] fix blueocean-core-js versions in package.json (after publish)

* [JENKINS-36687] formatting

* [JENKINS-36687] begin migrating SseBus to blueocean-core-js so it can be shared across plugins; support multiple subscriptions; still needs testing

* [JENKINS-36687] clean up new SseBus code; fix some issues that appeard to be caused by two SSE connections being made using the same clientId

* [JENKINS-36687] switch blueocean-personalization to use SseBus from blueocean-core-js

* [JENKINS-36687] new "RunButton" work in progress

* [JENKINS-36687] style button as a tag rather than button to avoid JDL style collisions with button tag

* [JENKINS-36687] stop button state; refinement of styling and positioning

* [JENKINS-36687] create API utility to encapsulate fetch logic

* [JENKINS-36687] wire up start / stop API to the RunButton; refine toast messaging

* [JENKINS-36687] fix some broken logic in SSE bus that was adding a duplicate listener for each subscription added

* [JENKINS-36687] simplify listener logic; wire up logic for "started" and "stopped" toasts

* [JENKINS-36687] brute force fix for "duplicate toasts" bug that just prevents them from being added; in place until we find a more elegant fix in upstream code

* [JENKINS-36687] strip out "awaiting job event" code in favor of fix within ToastService (for now)

* [JENKINS-36687] implement "stopping" state for RunButton

* [JENKINS-36687] customize label of Run button

* [JENKINS-36687] minor refinements to labels

* [JENKINS-35796] add the "Stop" button to the Run Details screen

* [JENKINS-36687] fix regression in ToastService spec

* tick version after publishing 0.0.3-beta4; update deps

* [JENKINS-36974] add the "stop" button for running builds to the Activity tab

* [JENKINS-36974] enhance RunButton to support multiple "buttonTypes" so it can toggle or show only run or stop; update Activity tab to use new RunButton at top for non multi-branch

* [JENKINS-36974] stop button hover state

* [JENKINS-36974] remove old assets

* [JENKINS-36974] delint

* [JENKINS-36974] kill debugger

* [JENKINS-36687] use new RunButton implementation on Branches tab

* [JENKINS-36974] use new RunButton implementation on PR tab; fix dead "OPEN" link in Toast when launching from Branches

* [JENKINS-36974] remove obsolete components / tests

* [JENKINS-35794] add new API method for replaying a build; add temp utilities for parsing response

* [JENKINS-35794] add new ReplayButton component for replaying failed builds; still a WIP

* [JENKINS-35794] add Replay button to Activity tab; still needs to handle navigation in Toast

* [JENKINS-35794] refactor some parsing logic into shared funcs

* [JENKINS-35794] convert a queue item / REST URL to a valid Run Details interface URL

* tick version after publishing 0.0.3-beta9

* [JENKINS-35794] wire up Toast's "OPEN" link for Replay button on Activity

* [JENKINS-35794] fix bug in logic when checking result / state

* [JENKINS-35974] prevent a dup Toast issue

* tick version after publishing 0.0.3-beta11

* [JENKINS-35789] add Re-run button to Run Details

* [JENKINS-36687] update RunsApi to use new JWT-enabled Fetch library

* [JENKINS-36687] update SseBus to use new JWT-enabled Fetch

* [JENKINS-36687] eliminate the old "UrlConfig" in favor of the new urlconfig

* [JENKINS-36687] migrate "cleanSlashes" into generic utils

* [JENKINS-36687] delint

* [JENKINS-36687] delint tests

* [JENKINS-36687] delinting done

* [JENKINS-36687] ensure lint and tests run before publish; tick up version numbers

* [JENKINS-35789] hide the Re-run button if the job is not simple or multi-branch pipeline

* [JENKINS-35789] test more scenarios for buildRunDetailsUrlFromQueue

* [JENKINS-37425] use a git-flow style branch name in test

* [JENKINS-37425] start of a new utility for fetching capabilities metadata

* [JENKINS-37425] fix a really weird bug that was causing an extra "/jenkins/blue" to be prepended to the beginnng of the browser URL after navigating

* [JENKINS-37425] simplify the RunButton to immediately fire off a "Started" Toast by using data from the REST API response rather than waiting for the SSE event; also eliminate the "Stopped" Toast that required SSE integration as well

* [JENKINS-37425] work in progress; clicking Re-run on the Run Details page does not update the page correctly due to some Router jankiness

* [JENKINS-35789] fix a bug where clicking the Re-run button on Run Details would not appopriately trigger a refetch of the Run

* [JENKINS-36687] better method name / comments

* [JENKNS-37425] continue refining Toast behavior; make reusable function for creating "Run Started" toast

* [JENKNS-37425] fix a bug where clicking "Open" on Toast initiated from Activity page would result in bad url / 404

* [JENKINS-37519] CapabilitiesStore that can load and cache capabilities from server; ported over from other branch

* [JENKINS-37519] let the CapabilityStore delegate to an CapabilityApi for remote calls; tests for CapabilityStore

* [JENKINS-37519] start of CapabilityAugmenter: basic tree walking in place; needs more filtering, proper fetch integration, actual tests

* [JENKINS-37519] better names for things in augmenter; tests

* [JENKINS-37519] finish impl of augmentCapabilities; write a basic test for it

* [JENKINS-37519] delint

* [JENKINS-37519] add another more elaborate test case for 'augmentCapabilities'

* [JENKINS-37519] handle the scenario where the capabilities weren't loaded more gracefully (and warn)

* [JENKINS-37519] add an "enum" of a few available capabilities

* [JENKINS-37519] export an instance of CapabilityAugmenter for use by clients

* [JENKINS-37519] fix a bug in the augmenter where a cycle caused an infinite loop

* [JENKINS-37519] add some perf logging so we can benchmark

* [JENKINS-37519] jsdoc

* [JENKINS-37519] add the ability to conditionally include the 'actions' property (since the data can be quite large)

* [JENKINS-37519] add "Capable" utility so we can attach a "can" method to each object; make available in Augmenter as well

* [JENKINS-37519] clean up warnings

* [JENKINS-37519] add logic to CapabilityApi to strip out duplicates

* [JENKINS-37519] enable the parsing of 'actions' by default so behavior is consistent (per kzantow) and because performance impact appears minimal

* [JENKINS-37519] don't log warnings by default; turn off perf metrics in the test suite

* JENKINS-38136# capability map API changed to POST

- Also pulls in well known capabilities in to a class as constant
- Fixes typo in PullRequest capability

* Doc update

* [JENKINS-37519] use POST for capabilities fetch, since a very long class list could break for a GET

* [JENKINS-37519] delint

* [JENKINS-37831] tick up versions for blueocean-core-js

* [JENKINS-37831] add capabilities fetching into some of the key fetches in dashboard and personalization

* [JENKINS-37831] disable favoriting of matrix jobs

* [JENKINS-37831] delint

* [JENKINS-37425] remove old version of capability apis; refactor logic to create "run started" toast into ToastUtils (per tfennelly); remove logic that compensating for JENKINS-37746

* [JENKINS-37425] tick version for core-js after publish of 0.0.17-beta3; update deps to use

* [JENKINS-37425] remove logic in DashboardCards listener that was creating "started" and "stopped" toasts; prevents extra toasts firing when run button on other screens is used

* [JENKINS-37425] fix a bug where simple pipeline's "OPEN" link was bad because of faulty multibranch logic; fix a bug where branch names were not decoded in Toast

* [JENKINS-37425] update a test that was not actually using a nested job URL

* [JENKINS-37425] update favorite card to use Run/ReplayButton; tests need to be fixed after PR #502 and #504 are merged to master

* [JENKINS-37425] update RunButton to use an embedded SVG instead of 4 distinct SVG assets for its different states; apply :hover coloring for the "dark" themes of Run/ReplayButton

* [JENKINS-37425] make "capable" a bit more flexible in terms of params it will accept

* [JENKINS-37425] fix a regression in this branch where clicking "Re-run" from Run Details would not automatically navigate the user to the Run Details screen for the new run; bake permissions logic into ReplayButton to hide itself if not a pipeline

* [JENKINS-37425] another test for URL builder

* [JENKINS-37425] delint

* [JENKINS-37425] make UrlBuilder available so that PipelineCard can use it to build the run details URL

* [JENKINS-37425] remove IfCapability check around ReplayButton since it will hide itself for non-pipelines

* [JENKINS-37425] remove obselete file that was refactored into ToastUtils

* [JENKINS-37425] delint; fix broken tests

* [JENKINS-37425] remove redundant code from personalization that is now handled directly by RunButton / ReplayButton; delint

* [JENKINS-37425] simplify logic in render method to "isRunFailed" method; fix a bug on Run Details where replaying multiple times would not work because the button was not having its state reset when bound to a new Run

* tick up versions after publish blueocean-core-js 0.0.17-beta5

* [JENKINS-37425] refactor display logic for favorite cards from DashboardCards down into PipelineCard - where it's more sensible to be; update all shrinkwraps

* [JENKINS-37425] delint

* tick up versions after publish blueocean-core-js 0.0.18
2016-09-27 12:23:04 -04:00
Tom Fennelly fee4d570a8 Trigger the ATH, with matching branch name support (#530)
* Trigger the ATH, with matching branch name support

* Don't allow a failed attempt to build ATH branch fail the build

* Add #!groovy

* Removed stray characters at the end of echo

and created triggerATH function
2016-09-27 15:23:34 +01:00
Cliff Meyers c6cf76ddc1 Task/jenkins 37715 validate package shrinkwrap jsons (#511)
* [JENKINS-37715] write a node script to validate package.json and npm-shrinkwrap.json for consistency

* [JENKINS-37715] test out the new "validatedeps" script for all shrinkwrapped modules

* [JENKINS-37715] move "checkdeps" to bin; adjust pathing

* [JENKINS-37715] move validatedeps to bin; add some docs

* [JENKINS-37715] move "validatedeps" to bin and rename to "checkshrinkwrap" to make the intent a bit more clear; just hardcode the projects we want to check rather than using args to simplify code

* [JENKINS-37715] try to fix a pathing issue

* [JENKINS-37715] instead of looking for bad chars in version string, check for the pattern we expect  (1.2.3 or 1.2.3-beta5)
2016-09-27 09:18:39 -04:00
Yoann Dubreuil 945198cf34 Standardize dogfood and small fixes (#527)
Depends on a fixed Jenkins LTS version instead of latest ensure reproducible builds.
Don't bypass installation wizard by default as it's here to enforce security.

Add a way to add Git build data into the Docker image
2016-09-27 11:04:04 +02:00
Thorsten Scherler b331bdce9c [FIX JENKINS-37793] Developer can see meaningful page titles (#529)
* [JENKINS-37793] Implement setTitle via higher order component. Adopt tests

* [JENKINS-37793] feedback from @i386

* [JENKINS-37793] add default organisation

* [JENKINS-37793] change seperator
2016-09-27 10:53:39 +02:00
Michael Neale 2474e0eae6 [maven-release-plugin] prepare for next development iteration 2016-09-27 11:01:22 +10:00
Michael Neale 57f77580e6 [maven-release-plugin] prepare release blueocean-parent-1.0.0-b07 2016-09-27 11:01:13 +10:00
Thorsten Scherler 306287176f [JENKINS-38329] REGRESSION Errors on activity and branches tabs (#528)
* [JENKINS-38329] do not fetch pr or branches in case we are not supporting them otherwise we will run into failures the next time. Further in general we should never try to fetch something where we know before hand that it does not exit.

* [JENKINS-38329] remove log statement

* [JENKINS-38329] make sure to remove the branch/pr data when unmounting

* eslint - formating changes and fix offences

* [JENKINS-38329] fix tests
2016-09-26 20:10:14 +02:00
Thorsten Scherler 8690946acd [JENKINS-38044] when logArray is not present and we are in karaoke, try to fetch again (#522) 2016-09-26 10:27:35 +02:00
Josh McDonald 452759e5ee JENKINS-38321-dashboard-new-button * Update dependency on js-extensions to latest, add an ExtensionPoint for the 'new pipeline' action with default of old behaviour (#526) 2016-09-26 14:24:38 +10:00
Josh McDonald 21c57745a3 jsx-dep-fix-20160926 * Fix missing dev dependency from package.json (#525) 2016-09-26 11:47:49 +10:00
Josh McDonald c5541b5c40 Squashed commit of the following: (#521)
josh/JENKINS-38247-notbuilt-indicator * Lint
    josh/JENKINS-38247-notbuilt-indicator * Fix the bug when watching a live build transition to failed, and add
    josh/JENKINS-38247-notbuilt-indicator * Fixed when loading, but not yet when watching a build go from running -> finished
2016-09-26 10:57:42 +10:00
Josh McDonald feb23a4def js-x-vbump-20160926 * Bump js-extensions version out of beta before publishing to npm (#524) 2016-09-26 09:41:48 +10:00
Tom Fennelly ad04e53ddf [JENKINS-38252] Updated SSE Gateway plugin to 1.10 (#512)
* Updated SSE Gateway plugin to 1.10-SNAPSHOT

* Updated SSE Gateway plugin to 1.10
2016-09-24 12:48:58 +01:00
Cliff Meyers 44a460b7e6 Bug/jenkins 37831 matrix favoriting (#497)
* [JENKINS-37519] CapabilitiesStore that can load and cache capabilities from server; ported over from other branch

* [JENKINS-37519] let the CapabilityStore delegate to an CapabilityApi for remote calls; tests for CapabilityStore

* [JENKINS-37519] start of CapabilityAugmenter: basic tree walking in place; needs more filtering, proper fetch integration, actual tests

* [JENKINS-37519] better names for things in augmenter; tests

* [JENKINS-37519] finish impl of augmentCapabilities; write a basic test for it

* [JENKINS-37519] delint

* [JENKINS-37519] add another more elaborate test case for 'augmentCapabilities'

* [JENKINS-37519] handle the scenario where the capabilities weren't loaded more gracefully (and warn)

* [JENKINS-37519] add an "enum" of a few available capabilities

* [JENKINS-37519] export an instance of CapabilityAugmenter for use by clients

* [JENKINS-37519] fix a bug in the augmenter where a cycle caused an infinite loop

* [JENKINS-37519] add some perf logging so we can benchmark

* [JENKINS-37519] jsdoc

* [JENKINS-37519] add the ability to conditionally include the 'actions' property (since the data can be quite large)

* [JENKINS-37519] add "Capable" utility so we can attach a "can" method to each object; make available in Augmenter as well

* [JENKINS-37519] clean up warnings

* [JENKINS-37519] add logic to CapabilityApi to strip out duplicates

* [JENKINS-37519] enable the parsing of 'actions' by default so behavior is consistent (per kzantow) and because performance impact appears minimal

* [JENKINS-37519] don't log warnings by default; turn off perf metrics in the test suite

* JENKINS-38136# capability map API changed to POST

- Also pulls in well known capabilities in to a class as constant
- Fixes typo in PullRequest capability

* Doc update

* [JENKINS-37519] use POST for capabilities fetch, since a very long class list could break for a GET

* [JENKINS-37519] delint

* [JENKINS-37831] tick up versions for blueocean-core-js

* [JENKINS-37831] add capabilities fetching into some of the key fetches in dashboard and personalization

* [JENKINS-37831] disable favoriting of matrix jobs

* [JENKINS-37831] delint
2016-09-23 09:43:26 -04:00
Thorsten Scherler e2905f7eca [FIX JENKINS-37753] REGRESSION: Steps showing up as incomplete when they are in fact complete (#520)
* [JENKINS-37753] Remove all steps and logs from cache as soon we are finished with the run, this makes sure that they will be refetched after the run ended.

* [JENKINS-37753] fix typo and comment console.log

* [JENKINS-37753] fix typo

* [JENKINS-37753] lint fix and fix comments on PR
2016-09-23 13:04:59 +02:00
Josh McDonald 883e341b51 feature/JENKINS-38321-js-extensions (#516)
* feature/JENKINS-38321-js-extensions

 * Update ExtensionRenderer to show children as placeholders if no extensions are present
 * Tests for ExtensionRenderer, and a few tweaks for testability because JS
 * Improvements to checkdeps script backported from editor branch

* feature/JENKINS-38321-js-extensions * Wind-back changes to Dashboard because js-extensions isn't yet published
2016-09-23 16:10:59 +10:00
Michael Neale 6b4ac8f294 Jenkins can abort but take a while for state to be finalised, so no use testing (#519) 2016-09-23 11:12:57 +10:00
Yoann Dubreuil 419d13e14f [JENKINS-37141] rework Docker build for development image - take 2 (#513)
* [JENKINS-37141] rework Docker build for development image - take 2

I just discovered hpi:assemble-dependencies, which is exactly what is
needed to bundle all plugins into a folder with can be used for the
Docker build. A dedicated module is also not needed at all (it was
required by hpi:bundle-plugins). It's a bit unfortunate to discover
this now, but better late than never..

Although matrix-auth and antisamy-markup-formatter are not needed to run
BlueOcean, it's good to have them around when the development image is
started with demo data.
2016-09-22 08:55:43 +02:00
Cliff Meyers f1475e883a update shrinkwrap, missed in last PR #504 (#514) 2016-09-21 16:25:37 -04:00
Cliff Meyers bf64c574dd Feature/jenkins 37877 pipeline permissions (#504)
* [JENKINS-37877] add a utility for doing security checks that will avoid magic strings scattered throughout all plugins

* [JENKINS-37877] security for RunButton

* [JENKINS-37877] fix a strange "Security is undefined accessing permit" error when running karma tests - only observed once we added ReplayButton-spec and imported ReplayButton

* [JENKINS-37877] security for ReplayButton

* [JENKINS-37877] fix peerDep error

* [JENKINS-37877] delint

* [JENKINS-37877] tick up blueocean-core-js to get new permission-aware Run/ReplayButton

* [JENKINS-37877] ensure Run button hides correctly on Branches & Pull Requests tabs; tweak the alignment of actions

* [JENKINS-37877] remove some old styling and assets from the old "RunPipeline" component

* [JENKINS-37877] tick up core-js version after publish; update dependent packages
2016-09-21 15:55:04 -04:00
Cliff Meyers c1b92d5742 Task/jenkins 37791 run favorite reordering (#502)
* [JENKINS-37519] CapabilitiesStore that can load and cache capabilities from server; ported over from other branch

* [JENKINS-37519] let the CapabilityStore delegate to an CapabilityApi for remote calls; tests for CapabilityStore

* [JENKINS-37519] start of CapabilityAugmenter: basic tree walking in place; needs more filtering, proper fetch integration, actual tests

* [JENKINS-37519] better names for things in augmenter; tests

* [JENKINS-37519] finish impl of augmentCapabilities; write a basic test for it

* [JENKINS-37519] delint

* [JENKINS-37519] add another more elaborate test case for 'augmentCapabilities'

* [JENKINS-37519] handle the scenario where the capabilities weren't loaded more gracefully (and warn)

* [JENKINS-37519] add an "enum" of a few available capabilities

* [JENKINS-37519] export an instance of CapabilityAugmenter for use by clients

* [JENKINS-37519] fix a bug in the augmenter where a cycle caused an infinite loop

* [JENKINS-37519] add some perf logging so we can benchmark

* [JENKINS-37519] jsdoc

* [JENKINS-37519] add the ability to conditionally include the 'actions' property (since the data can be quite large)

* [JENKINS-37519] add "Capable" utility so we can attach a "can" method to each object; make available in Augmenter as well

* [JENKINS-37519] clean up warnings

* [JENKINS-37519] add logic to CapabilityApi to strip out duplicates

* [JENKINS-37519] enable the parsing of 'actions' by default so behavior is consistent (per kzantow) and because performance impact appears minimal

* [JENKINS-37519] don't log warnings by default; turn off perf metrics in the test suite

* JENKINS-38136# capability map API changed to POST

- Also pulls in well known capabilities in to a class as constant
- Fixes typo in PullRequest capability

* Doc update

* [JENKINS-37519] use POST for capabilities fetch, since a very long class list could break for a GET

* [JENKINS-37791] get new capabilities API

* [JENKINS-37791] clean up lint warnings

* [JENKINS-37791] test data can just be in a JSON file

* [JENKINS-37791] refactor sort logic to utility

* [JENKINS-37791] add capabilities augmenter logic to favorites API calls

* [JENKINS-37791] remove all the custom capabilities code for favorites cards

* [JENKINS-37519] expose "capable" as static func so it can be used in place of "can" instance func that will get lost during clones

* [JENKINS-37791] switch to "capable" function to work around issues with "can" instance function getting lost after clones

* [JENKINS-37791] fix and enable previously broken tests

* [JENKINS-37791] only re-sort the favorites cards when an "important" change happens (add, remove, or build "finishes")

* [JENKINS-37791] ensure the favorites cards re-sort when navigating away and back to dashboard

* [JENKINS-37791] comments / cleanup / delint

* [JENKINS-37791] rework tests since sorting logic now lives in helper rather than DashboardCards component
2016-09-21 13:49:28 -04:00
Yoann Dubreuil 742580e53d [JENKINS-37141] rework Dockerfile for development image (#396) 2016-09-21 12:08:16 +02:00
Cliff Meyers d075f7d56f task/JENKINS-37835 remove from queue (#492)
* [JENKINS-37835] implement removing an item from a queue; tested with freestyle jobs and works well

* [JENKINS-37835] package.json publish tick dance party

* [JENKINS-37835] fix an inconsistency in version numbers

* [JENKINS-37835] fix a bug where the pagination button was disappearing after cancelling a queued build (due to lack of copying over the fetch markers)

* [JENKINS-37835] use default build to lint/test for prepublish

* [JENKINS-37835] tick up version for blueocean-core-js

* [JENKINS-37835] fix accidental disabling of "refresh after hibernate" code

* tick blueocean-core-js version after publishing 0.0.16
2016-09-20 14:00:48 -04:00
Keith Zantow 83b81b6ee4 JENKINS-38300 Fix pull requests within an org folder (#510) 2016-09-20 11:05:52 -04:00
Cliff Meyers 6207d331c0 [JENKINS-37281] replace unnecessary StopPropagation component with simple stopProp func (#498) 2016-09-20 10:22:41 -04:00
Cliff Meyers aee80261d8 Task/jenkins 37519 fetch capabilities (#488)
* [JENKINS-37519] CapabilitiesStore that can load and cache capabilities from server; ported over from other branch

* [JENKINS-37519] let the CapabilityStore delegate to an CapabilityApi for remote calls; tests for CapabilityStore

* [JENKINS-37519] start of CapabilityAugmenter: basic tree walking in place; needs more filtering, proper fetch integration, actual tests

* [JENKINS-37519] better names for things in augmenter; tests

* [JENKINS-37519] finish impl of augmentCapabilities; write a basic test for it

* [JENKINS-37519] delint

* [JENKINS-37519] add another more elaborate test case for 'augmentCapabilities'

* [JENKINS-37519] handle the scenario where the capabilities weren't loaded more gracefully (and warn)

* [JENKINS-37519] add an "enum" of a few available capabilities

* [JENKINS-37519] export an instance of CapabilityAugmenter for use by clients

* [JENKINS-37519] fix a bug in the augmenter where a cycle caused an infinite loop

* [JENKINS-37519] add some perf logging so we can benchmark

* [JENKINS-37519] jsdoc

* [JENKINS-37519] add the ability to conditionally include the 'actions' property (since the data can be quite large)

* [JENKINS-37519] add "Capable" utility so we can attach a "can" method to each object; make available in Augmenter as well

* [JENKINS-37519] clean up warnings

* [JENKINS-37519] add logic to CapabilityApi to strip out duplicates

* [JENKINS-37519] enable the parsing of 'actions' by default so behavior is consistent (per kzantow) and because performance impact appears minimal

* [JENKINS-37519] don't log warnings by default; turn off perf metrics in the test suite

* JENKINS-38136# capability map API changed to POST

- Also pulls in well known capabilities in to a class as constant
- Fixes typo in PullRequest capability

* Doc update

* [JENKINS-37519] use POST for capabilities fetch, since a very long class list could break for a GET

* [JENKINS-37519] expose "capable" as static func so it can be used in place of "can" instance func that will get lost during clones

* tick version

* [JENKINS-37519] remove decoration of data with "can" method since it's easy to lose during a clone; update test to use "capable" util instead

* [JENKINS-37519] delint; jsdoc cleanup; fix test spec name

* tick blueocean-core-js version after publishing 0.0.15
2016-09-20 10:21:54 -04:00
Thorsten Scherler cd83f83a3f FIX Jenkins-37962 REGRESSION: parallel karaoke not allowing branch selection or completing correctly (#499)
* [JENKINS-37962] enable logging

* JENKINS-38176# fix for steps API for incomplete parallels

* [JENKINS-37962] fix issue to select parallel branches

* update npm-shinkwrap to reflect changes made to package.json's after merge conflict

* [JENKINS-37962] if we click in a parallel we stop following as well

* eslint - formating changes and fix offences

* [hotfix/JENKINS-38345] reset classList the correct way

* [JENKINS-37962] remove unused file

* [JENKINS-37962] Workaround the problem with the new syntax in parallel

* [JENKINS-37962] extract function as ask by tom

* [JENKINS-37962] more common comparision as ask by tom
2016-09-20 15:04:13 +02:00
vivek a78bc763d0 JENKINS-38136# capability map API changed to POST (#494)
* JENKINS-38136# capability map API changed to POST

- Also pulls in well known capabilities in to a class as constant
- Fixes typo in PullRequest capability

* Doc update
2016-09-19 16:12:11 -07:00
Cliff Meyers 710652d539 docs for shrinkwrap (#509)
* docs for shrinkwrap

* docs for shrinkwrap
2016-09-19 11:23:29 -04:00
Thorsten Scherler aa5364591f [hotfix/JENKINS-38345] REGRESSION remove classList (#507)
* [hotfix/JENKINS-38345] reset classList the correct way

* Lint fix

* [hotfix/JENKINS-38345] make sure we have a classList
2016-09-19 16:20:12 +02:00
Ivan Meredith e8e9d0ddec Disable 2 flaky tests (#506)
* Disable 2 flaky tests

* Actually disable test
2016-09-19 14:46:22 +12:00
Thorsten Scherler 6040ecb4d1 [JENKINS-38240] loaction is a floater, make sure to use the one we altered (#501) 2016-09-16 11:26:05 +02:00
vivek 3ff03598b4 JENKINS-38176# fix for steps API for incomplete parallels (#495) 2016-09-15 07:45:40 -07:00
Cliff Meyers f675a3b679 update npm-shinkwrap to reflect changes made to package.json's after merge conflict (#500) 2016-09-15 10:34:51 -04:00
Cliff Meyers ee87f2b3e5 task/jenkins 37715 npm shrinkwrap (#493)
* [JENKINS-37715] fix a peerDep conflict in slint-plugin-react that was preventing build of shrinkwrap for dashboard; make some minor style refinements that are needed to pass the revised rules

* [JENKINS-37715] eliminate a peer dep conflict on js-test which wasn't used anyway

* [JENKINS-37715] roll back eslint-plugin-react to 4.0.1 for compat w/ jenkins-js-eslint-config; revert recent rules changes to pass lint

* [JENKINS-37715] remove the ^ operator from all devDep versions; create shrinkwrap

* [JENKINS-37715] personalization: remove the ^ operator from all devDep versions and use "latest" per current install; create shrinkwrap

* [JENKINS-37715] web: remove the ^ operator from all devDep versions and use "latest" per current install; create shrinkwrap

* [JENKINS-37715] config: remove the ^ operator from all devDep versions and use "latest" per current install; create shrinkwrap; add editor config

* [JENKINS-37715] fix className that got lost during merge
2016-09-15 08:58:32 -04:00
Vivek Pandey f052917fb8 [maven-release-plugin] prepare for next development iteration 2016-09-13 22:48:51 -07:00
Vivek Pandey d47c3c3503 [maven-release-plugin] prepare release blueocean-parent-1.0.0-b06 2016-09-13 22:48:46 -07:00
vivek ae00541d5b JENKINS-38178# Use LTS baseline 2.7.1 (#496) 2016-09-13 15:13:43 -07:00
Tom Fennelly 1216a86f66 Getting the ATH running (#491)
* Updated deps .... getting the ATH running

* Don't escape maven artifactId in extensions CSS adjunct path

* Fix dependencies

* js-extension 0.0.24-beta5

* fix js-builder peer dep

* build js-extensions web resources into the same dir as the javascript

* Fix js-extensions version in dashboard

* Update js-extensions deps

* Update SSE Gateway dep to 1.9

* Update core-js to latest deps

* Dependency updates for js-modules, js-builder and sse-gateway

* Fixed blueocean-config NPM deps

* js-extensions 0.0.24

* blueocean-core-js 0.0.14

* Updated local npm package dep versions
2016-09-13 20:24:12 +01:00
Vivek Pandey 9ee76a434f [maven-release-plugin] prepare for next development iteration 2016-09-09 13:11:51 -07:00
Vivek Pandey 2af8aab224 [maven-release-plugin] prepare release blueocean-parent-1.0.0-b05 2016-09-09 13:11:45 -07:00
Ivan Meredith 9647650252 [JENKINS-38039] branch ordering (#489)
* An attempt at ordering branches

* Order branches by favorites and then activity

* Fix style issues
2016-09-09 12:18:38 +12:00
vivek 8cfe6e4962 JENKINS-37917# Folder/MB permissions should include start and stop as well (#487) 2016-09-08 16:45:29 -07:00
Tom Fennelly c078efb2cf Pass the run instance to 'jenkins.pipeline.run.result' implementation. (#473) 2016-09-09 00:16:33 +01:00
Vivek Pandey ec2f03716e [maven-release-plugin] prepare for next development iteration 2016-09-08 16:10:35 -07:00
Vivek Pandey f8fc5aa93b [maven-release-plugin] prepare release blueocean-parent-1.0.0-b04 2016-09-08 16:10:29 -07:00
vivek 01400d785a JENKINS-38012# Fix related to Kyoto related failure handling (#483)
* JENKINS-38012# Fix related to Kyoto related failure handling

* Bumped up kyoto pluing to 0.2

- Removed workaround that got fixed in JENKINS-38049

* Handle failure of stages when there is also postBuild block.

* Disable debug log
2016-09-08 14:45:09 -07:00
Keith Zantow b913d2c0d8 [FIX JENKINS-37743] - fix the page loading animation (#475)
* JENKINS-37743 - fix the page loading animation
* Refactor state usage in MultiBranch.jsx and PullRequests.jsx
2016-09-08 16:24:58 -04:00
vivek 8a8db539d2 JENKINS-37763# numberOfRunningPipelines and numberOfQueuedPipelines a… (#468)
* JENKINS-37763# numberOfRunningPipelines and numberOfQueuedPipelines added to pipeline object

* Doc improvement
2016-09-08 09:38:56 -07:00
Keith Zantow 243914c622 JENKINS-37746 - Org folders need to be excluded from BO list (#477) 2016-09-08 09:42:40 -04:00
Thorsten Scherler 4da297d4cc [JENKINS-38006] Fix condition which had been implemented wrong (#476) 2016-09-08 10:05:25 +02:00
vivek 44bace7dc4 JENKINS-37202# Set pagination by default (#486) 2016-09-08 00:29:44 -07:00
vivek 0875637afb JENKINS-38016# Create ApiRoutable extensions lazily - after all plugins are installed (#484) 2016-09-07 22:54:09 -07:00
vivek dfff029465 JENKINS-38036# Regression fix with anonymous read permission on loading BO UI (#485) 2016-09-07 22:52:39 -07:00
Ivan Meredith 281e1a2f63 [JENKINS-36906 & JENKINS-37832] Login related things (#480)
* [JENKINS-36906] add temporary Login and Logout buttons; still need proper security applied

* Show login/logout buttons and refresh page when user changes or jenkins is restarted

* Release beta of core-js

* Only put header on if we are not using JWT

* Fix imports and indentation

* Fix linting issues

* Change hashing impl
2016-09-08 16:37:28 +12:00
Keith Zantow f98dc57807 [FIX JENKINS-37166] - update test result console style (#481)
* JENKINS-37166 - update test result console style

* More style changes
2016-09-08 12:39:35 +10:00
Cliff Meyers 418269930a [JENKINS-36973] fix icon that got lost in merge (#482) 2016-09-07 21:54:48 -04:00
Cliff Meyers 3ddb6804be Bug/jenkins 37603 favorite hover state (#478)
* add editorconfig file and format package.json's

* [JENKINS-37603] pull in new Favorites control w/ hover states from JDL; refine some styling in dashboard to prevent class name clashing
2016-09-07 20:53:47 -04:00
Vivek Pandey 16f2da9d22 [maven-release-plugin] prepare for next development iteration 2016-09-07 10:11:15 -07:00
Vivek Pandey ecb790d723 [maven-release-plugin] prepare release blueocean-parent-1.0.0-b03 2016-09-07 10:11:08 -07:00
Cliff Meyers e781d8d6fd Feature/jenkins 36973 favorite stop build (#430)
* [JENKINS-36905] get 'run' and 'replay' buttons displaying in Pipeline Card; tweak CSS for actions area to adjust some layout issues

* [JENKINS-36905] make "cleanSlashes" a shared util

* [JENKINS-36905] add "cleanSlashes" calls

* [JENKINS-36905] wire up "replay" and "run" buttons for favorites cards

* [JENKINS-36905] show run button for "unknown" status (typically a newly-created project that's never been run)

* [JENKINS-36905] control display of re-run button via capabilities API

* [JENKINS-36905] delint

* [JENKINS-36973] display a "stop" button for builds that are queued or running

* [JENKINS-36905] delint

* JENKINS-36376# Adding missing capabilities

- Refactored pipeline factory to resolve WorkflowJob and moved to pipeline module
- Capability added to branch, pipeline, non-pipeline jobs etc.

* [JENKINS-36905] finish capabilities logic to show/hide the replay button for pipeline vs. freestyle jobs

* [JENKINS-36905] remove debugger

* [JENKINS-36905] fix broken "rerun" button test

* [JENKINS-36905] fix test that broke once ClassMetadataStore was used inside of it

* [JENKINS-36905] add TODOS and make tweak to MetadataUtils.bindCapability

* [JENKINS-36973] rename some of the elements in the PipelineCard; beef up unit tests

* [JENKINS-36973] wire up API call to stop build; add "stopping" state to pipeline card

* [JENKINS-36199] enable MobX in blueocean-web

* [JENKINS-36199] really awful quick and dirty POC for Toaster w/ MobX in blueocean-web; needs heavy refactoring but committing as a checkpoint for "this code works, kinda"

* [JENKINS-36199] remove the toasts when appropriate

* [JENKINS-36199] setup a singleton'd ToastService inside of blueocean-core-js

* [JENKINS-36199] dependencies update

* [JENKINS-36199] set up blueocean-web test and personalization to use same ToastService instance (still very much WIP)

* [JENKINS-36199] create self-contained "ToastDrawer" in blueocean-web for display of toats; clean up old code used in testing

* [JENKINS-36199] assign Toast.id automatically if not added; add some tests

* [JENKINS-36199] remove old "Test" component now that some real tests have been added

* [JENKINS-36199] port over "RunPipeline" to use new ToastService API; fix some bugs caused by mismatched encoding on branch names

* [JENKINS-36198] remove special Toast CSS that is no longer needed with impl of JENKINS-36199

* [JENKINS-36199] fix silly mistake where mobx was specified in wrong dependency list

* [JENKINS-36199] update deps on blueocean-core-js to use published beta; delint

* [JENKINS-36199] use published version of JDL

* [JENKINS-36199] discard some changes that came in from the "stop build" feature branch that "toaster" was forked off; comments

* [JENKINS-36199] tick JDL version to get margin-top fix; tighten up spacing along bottom of dashboard cards

* [JENKINS-36199] don't hide the "queued" toast immediately once we receive a "run" toast, just let them both auto-dismiss if necessary

* [JENKINS-36199] improve unit test for "removeToast"

* [JENKINS-36199] make "blueocean-core-js" a default imported library for plugin builds

* [JENKINS-36199] safer logic for decoding branch name

* [JENKINS-36199] safer logic for decoding branch name

* [JENKINS-36973] fix icon that went messing in bad merge

* [JENKINS-36973] switch to using a blocking stop in the API call; remove an unnecesary check during props change that was causing stop button's disabled state to become stale

* [JENKINS-36973] wire up toasts for builds that have queued and started

* [JENKINS-36973] minor cleanup

* [JENKINS-36973] build proper URL to run details view

* [JENKINS-36973] add toasts for "stopping..." and "stopped' build states

* [JENKINS-36973] prevent against a potential (unconfirmed) leak of DashboardCards

* [JENKINS-36973] optimize svg

* [JENKINS-36973] delint

* [JENKINS-36973] fix a bug that caused the "replay" button to be hidden; add a "Queued" toast when replay is pressed
2016-09-07 12:18:34 -04:00
Thorsten Scherler c1a354010b [JENKINS-37954] Running job has incorrect stage selected before steps start executing (#471)
* [JENKINS-37954] chow queued state in case of pipeline running/queued

* [JENKINS-37954] remove obsolete file, should never been committed

* [JENKINS-37954] refactor code and implement pipelineGraph focused on first node when queued
2016-09-07 10:47:05 +02:00
Vivek Pandey 8bcbc7273a [maven-release-plugin] prepare for next development iteration 2016-09-06 18:13:09 -07:00
Vivek Pandey 3f9cea5c4c [maven-release-plugin] prepare release blueocean-parent-1.0.0-b02 2016-09-06 18:13:00 -07:00
vivek 09700bc407 JENKINS-37744# Support forr Kyoto plugin (#474)
- Upgrades Jenkins core to 2.7.3 LTS
- Fixes regression where if JWT is disabled and anonymous read is not allowed access
  to /blue/... results in 403, resulting in to classic redirecting user to login page
2016-09-06 17:53:19 -07:00
vivek ab49ee788e Task/jenkins 37323 (#467)
* JENKINS-37323# Support for block scoped stage

* Ignore nested stage and branch

- Beefed up tests
- nested stage/branches steps are included in the steps API

* Using released version 2.2 of pipeline-stage-step plugin
2016-09-06 14:57:58 -07:00
vivek 0d64ef6bd8 JENKINS-37830# API to remove a queued item (#463)
* JENKINS-37830# API to remove a queued item

* [JENKINS-37830] switch the UI code that invokes the "run build" and "replay build" REST APIs to use method=POST instead of PUT

* [JENKINS-37830] publish new Core JS which includes PUT -> POST changes for "run" and "replay"; also fix FavoritesActions' "replay" code which was accidentally left as PUT in prior commit
2016-09-06 14:47:46 -07:00
Cliff Meyers 3072d3cc70 Task/jenkins 37685 toaster mobx fixes (#469)
* upgrade mobx version; add devtools

* change ToastService to modify 'toasts' array in-place, and fix a bug in ToastDrawer where it wasn't updating because no observables were actually referenced

* tick Core JS version after publish
2016-09-06 15:47:33 -04:00
Cliff Meyers af9d03d3b3 Bug/jenkins 37702 unbuilt pipeline unknown status (#465)
* [JENKINS-37702] restyle the "queued" state of the Favorite card

* [JENKINS-37702] default Favorites with no latest run to the "NOT_BUILT" status

* [JENKINS-37702] default Favorites with no latest run to the "NOT_BUILT" status
2016-09-06 12:32:59 -04:00
Thorsten Scherler 4e2cbc28d8 [JENKINS-37666] fix weirdness in collapse/expand (#470) 2016-09-06 09:05:24 +02:00
Vivek Pandey fdd874477e [maven-release-plugin] prepare for next development iteration 2016-09-02 13:03:48 -07:00
Vivek Pandey 36f5918e7b [maven-release-plugin] prepare release blueocean-parent-1.0.0-b01 2016-09-02 13:03:41 -07:00
Cliff Meyers 81f3dee521 [JENKINS-37673] fix a bug where smaller page widths were forcing the SVG indicator in the Favorite card to get resized too smaller (in all browsers) and also clipped (in Safari Tech Preview) (#464) 2016-09-02 14:13:21 -04:00
Cliff Meyers fbd1ebdee2 Feature/jenkins 35789 35794 stop and rerun builds (#448)
* [JENKINS-36687] build a utility function that converts a Run's HAL URL to the corresponding UI URL, to avoid encoding bugs

* [JENKINS-36687] comments

* [JENKINS-36687] add support for styles and assets in blueocean-core-js; add some assets for testing and in prep of "Run Pipeline" refactor

* [JENKINS-36687] rework the blueocean-web build so it can support multiple "libraries" (e.g. JDL and CoreJS) in the src/main/webapp/assets directory

* [JENKINS-36687] amend .gitignore

* [JENKINS-36687] UrlBuilder: more tests, better errors

* [JENKINS-36687] typos ;)

* [JENKINS-36687] fix blueocean-core-js versions in package.json (after publish)

* [JENKINS-36687] formatting

* [JENKINS-36687] begin migrating SseBus to blueocean-core-js so it can be shared across plugins; support multiple subscriptions; still needs testing

* [JENKINS-36687] clean up new SseBus code; fix some issues that appeard to be caused by two SSE connections being made using the same clientId

* [JENKINS-36687] switch blueocean-personalization to use SseBus from blueocean-core-js

* [JENKINS-36687] new "RunButton" work in progress

* [JENKINS-36687] style button as a tag rather than button to avoid JDL style collisions with button tag

* [JENKINS-36687] stop button state; refinement of styling and positioning

* [JENKINS-36687] create API utility to encapsulate fetch logic

* [JENKINS-36687] wire up start / stop API to the RunButton; refine toast messaging

* [JENKINS-36687] fix some broken logic in SSE bus that was adding a duplicate listener for each subscription added

* [JENKINS-36687] simplify listener logic; wire up logic for "started" and "stopped" toasts

* [JENKINS-36687] brute force fix for "duplicate toasts" bug that just prevents them from being added; in place until we find a more elegant fix in upstream code

* [JENKINS-36687] strip out "awaiting job event" code in favor of fix within ToastService (for now)

* [JENKINS-36687] implement "stopping" state for RunButton

* [JENKINS-36687] customize label of Run button

* [JENKINS-36687] minor refinements to labels

* [JENKINS-35796] add the "Stop" button to the Run Details screen

* [JENKINS-36687] fix regression in ToastService spec

* tick version after publishing 0.0.3-beta4; update deps

* [JENKINS-36974] add the "stop" button for running builds to the Activity tab

* [JENKINS-36974] enhance RunButton to support multiple "buttonTypes" so it can toggle or show only run or stop; update Activity tab to use new RunButton at top for non multi-branch

* [JENKINS-36974] stop button hover state

* [JENKINS-36974] remove old assets

* [JENKINS-36974] delint

* [JENKINS-36974] kill debugger

* [JENKINS-36687] use new RunButton implementation on Branches tab

* [JENKINS-36974] use new RunButton implementation on PR tab; fix dead "OPEN" link in Toast when launching from Branches

* [JENKINS-36974] remove obsolete components / tests

* [JENKINS-35794] add new API method for replaying a build; add temp utilities for parsing response

* [JENKINS-35794] add new ReplayButton component for replaying failed builds; still a WIP

* [JENKINS-35794] add Replay button to Activity tab; still needs to handle navigation in Toast

* [JENKINS-35794] refactor some parsing logic into shared funcs

* [JENKINS-35794] convert a queue item / REST URL to a valid Run Details interface URL

* tick version after publishing 0.0.3-beta9

* [JENKINS-35794] wire up Toast's "OPEN" link for Replay button on Activity

* [JENKINS-35794] fix bug in logic when checking result / state

* [JENKINS-35974] prevent a dup Toast issue

* tick version after publishing 0.0.3-beta11

* [JENKINS-35789] add Re-run button to Run Details

* [JENKINS-36687] update RunsApi to use new JWT-enabled Fetch library

* [JENKINS-36687] update SseBus to use new JWT-enabled Fetch

* [JENKINS-36687] eliminate the old "UrlConfig" in favor of the new urlconfig

* [JENKINS-36687] migrate "cleanSlashes" into generic utils

* [JENKINS-36687] delint

* [JENKINS-36687] delint tests

* [JENKINS-36687] delinting done

* [JENKINS-36687] ensure lint and tests run before publish; tick up version numbers

* [JENKINS-35789] hide the Re-run button if the job is not simple or multi-branch pipeline

* [JENKINS-35789] test more scenarios for buildRunDetailsUrlFromQueue

* [JENKINS-35789] fix a bug where clicking the Re-run button on Run Details would not appopriately trigger a refetch of the Run

* [JENKINS-36687] better method name / comments

* released blueocean-core-js 0.0.9 before merging PR
2016-09-02 11:02:52 -04:00
Thorsten Scherler 7bfe44768a [master] use published version of jdl 2016-09-02 11:27:31 +02:00
Thorsten Scherler ef04196dd3 [FIX JENKINS-36660] hooks for AT (#433)
* [JENKINS-36660] hooks for AT

* [JENKINS-36660] fix version conflict
2016-09-02 11:24:37 +02:00
Thorsten Scherler 3826bd9b9f [FIX JENKINS-35847] Developer should see a condensed version of the change summary (#455)
* [JENKINS-35847] implement condense version if overflow is detected

* [JENKINS-35847] Refactor to use state instead of DOM manipulations

* [JENKINS-35847] we have the property already

* [JENKINS-35847] better naming and in place properties usage
2016-09-02 10:44:59 +02:00
vivek c713cf4f15 JENKINS-37612# Cleanup POM and prepare for beta (#442)
* JENKINS-37612# Cleanup POM and prepare for beta

* Changed references from bluecean-plugin to blueocean

* Added Module :: suffix
2016-09-01 23:08:46 -07:00
Ivan Meredith 52b8b14d4f [JENKINS-37837] JWT feature flag (#462)
* Disable JWT by default, turned on with a feature flag

* Release core-js

* PR fixes

* Syntax error

* Add config.js to git

* Release core-js 0.0.8

* Set system prop on test to enable jwt

* Set system prop on test to enable jwt
2016-09-02 16:07:07 +12:00
vivek a0d9e5a0b2 JENKINS-37873# Self link fix for branch queue items (#460) 2016-09-01 19:13:57 -07:00
Tom Fennelly 1d0ce63b2c [JENKINS-35735] Fixes for IE compatibility (#447)
* IE doesn't have JSON.parse. No need for it here anyway.

* Ad babel polyfills for Internet Explorer

* stash

* Get IE "basically" working ... there are some issues

... but not issues that make it unusable imo

* Fix flexbox "Site" footer - so it would work on IE

* More ... Fix flexbox "Site" footer - so it would work on IE

* Update js-test NPM versions

* Polyfill for Error.captureStackTrace

* Check for incompatible IE and redirect to an error page

* Updating version before PR #447 merge
2016-09-01 16:36:44 +01:00
Ivan Meredith 197517d34e Release core-js (#458) 2016-09-01 20:37:15 +12:00
Ivan Meredith b6d0840772 [JENKINS-37859] Allow for 60s of clock skew (#456)
* [JENKINS-37859] Allow 60s of clock skew

* Bump core-js version
2016-09-01 14:42:55 +12:00
Ivan Meredith 88cbacd967 [JENKINS-37838] Calls to backend MUST end with / (#454)
* [JENKINS-37838] Calls to backend MUST end with /

* Append / to all urls in function
2016-09-01 12:37:36 +12:00
Keith Zantow 898c368656 JENKINS-37829 - More reliable run matching for SSE events (#452) 2016-08-31 09:36:08 -04:00
James William Dumay 229796da80 Update CONTRIBUTING.md (#451) 2016-08-31 11:16:36 +01:00
Ivan Meredith 7b58c5f582 JENKINS-35783# JWT support (#392)
* JENKINS-35783# JWT support

* TOC update

* Fix for failure during permission check

Added missing GrantedAuthorities to JwtAuthenticationToken.

* Curl wrapper to call APIs with JWT token

* Initial frontend commit for JWT

* Add braces to a function call

* Refactor some http into core-js

* Major refactoring of frontend code to use JWT

* Fix lint issues

* Add missing files

* Set core-js to version 0.0.1-beta1

* Bump verison of core-js

* Fix tests and lints

* Bump versions

* Fix linting

* Add some documentation

* Refactor core-js

* Bump core-js version

* Recommit files I renamed

* Bump core-js version

* Update SSE bus with correct function calls

* Added docs to invoke API in browser with JWT enabled.

* Fixes for PR comments

* Add utils.js

* Bump core-js version

* Remove console.log

* Make token set anonymous user corrently

* Revert "Make token set anonymous user corrently"

This reverts commit e95bc044dd.

* Polyfill es6-promise for Promises in actions.js

* commit

* New js-builder version with fix for deduped modules

* Another new js-builder version with change to require search transform

* Bump core-js version

* Fix linting

* Fix for anonymous user authentication

* Starting a run had missing authorization check

* Bump JWT vesion

* Relase version of core-js

* Fix double blue in urls

* Fix linting

* Fix tests
2016-08-31 13:42:41 +12:00
Ivan Meredith 575cd6af01 Fix tests 2016-08-31 13:21:31 +12:00
Ivan Meredith d4f5e4bb87 Fix linting 2016-08-31 13:00:21 +12:00
Ivan Meredith 1013b07c4b Fix double blue in urls 2016-08-31 12:43:52 +12:00
Ivan Meredith 136e512d73 Relase version of core-js 2016-08-31 11:51:26 +12:00
vivek 206af8bfa5 JENKINS-37826# include Matrix project in list of project types that doesn't need flattening (#450) 2016-08-30 16:23:12 -07:00
Ivan Meredith 8fa02c4ffa Merge remote-tracking branch 'origin/master' into story/JENKINS-35783 2016-08-31 10:57:36 +12:00
Keith Zantow c6f4b9b085 JENKINS-37799 - issue matching individual pipelines (#449) 2016-08-31 08:48:48 +10:00
Ivan Meredith f391a84f0a Bump JWT vesion 2016-08-31 10:03:24 +12:00
Ivan Meredith fd0b8cfa2d Merge remote-tracking branch 'origin' into story/JENKINS-35783 2016-08-31 09:18:33 +12:00
Vivek Pandey 94f08c2dd5 Starting a run had missing authorization check 2016-08-30 13:52:23 -07:00
Vivek Pandey dff4d45443 Merge branch 'story/JENKINS-35783' of github.com:jenkinsci/blueocean-plugin into story/JENKINS-35783 2016-08-30 11:52:33 -07:00
Vivek Pandey 2f2296f433 Fix for anonymous user authentication 2016-08-30 11:51:16 -07:00
Tom Fennelly bff98212b7 [FIX JENKINS-37714] js-builder bundle transform not taking dedupe into account when translating fullPaths to Ids (#444)
* [FIX JENKINS-37714] js-builder bundle transform not taking dedupe into account when translating fullPaths to Ids

* Updated js-test dependency to fix peer dep
2016-08-30 16:50:41 +01:00
vivek 3194e75943 JENKINS-37312# NPE fix that results in 'Failed to write numberOfFailingBranches' (#445) 2016-08-29 20:21:53 -07:00
Ivan Meredith f1e8092f02 Fix linting 2016-08-30 14:46:05 +12:00
Ivan Meredith bd06cc34ec Bump core-js version 2016-08-30 14:43:35 +12:00
Ivan Meredith 986fd51fc6 Merge remote-tracking branch 'origin/master' into story/JENKINS-35783 2016-08-30 14:39:13 +12:00
Vivek Pandey 91c3169280 [maven-release-plugin] prepare for next development iteration 2016-08-29 18:29:03 -07:00
Vivek Pandey 815501c0d0 [maven-release-plugin] prepare release blueocean-parent-1.0-alpha-9 2016-08-29 18:28:58 -07:00
James Dumay b358bb3e5d Updated padding slightly to give favourite stack more space 2016-08-30 10:23:41 +10:00
Keith Zantow 9ce99c7501 [FIX JENKINS-37708] queued item regression (#441)
* JENKINS-37712 - branch encoding was wrong
* Workaround issue where a queued run without a runId cannot be fetched
* Add estimated ID to queued SSE events
* Fix queued item not being displayed properly
2016-08-29 19:54:25 -04:00
Keith Zantow 9617f7f60e JENKINS-37712 - branch encoding was wrong (#439) 2016-08-29 19:00:05 -04:00
Keith Zantow f8efce4ab1 JENKINS-37722 - SSE updates were matching against runId only (#443) 2016-08-29 10:04:50 -04:00
Tom Fennelly c40c6277c6 [FIX JENKINS-36054] Lengthen page reload time tolerance from 2 to 20 seconds (#438)
* Revert "Hibernation change has been going haywire on dogfood and on local master with random refreshes. (#435)"

This reverts commit e50a21bd81.

* Lengthen page reload time tolerance from 2 to 20 seconds

The risk of having it too long is that someone could, in theory, bring a machine into and out of hibernation fast enough for us to ignore it (< 20s ?).
2016-08-29 12:10:49 +01:00
tfennelly fd8eb0afae Another new js-builder version with change to require search transform 2016-08-26 20:32:19 +01:00
tfennelly 3df6c9de7a New js-builder version with fix for deduped modules 2016-08-26 15:41:57 +01:00
Keith Zantow 092fc6d588 [FIX JENKINS-37544] - storybook updates (#436)
* [FIX JENKINS-37544] - storybook updates
2016-08-26 10:23:33 -04:00
Thorsten Scherler 9a8d1a77f7 [hotfix/JENKINS-37704] fix typo when things were renamed (#437) 2016-08-26 12:21:42 +02:00
Ivan Meredith 2082074c91 commit 2016-08-26 20:04:18 +12:00
Keith Zantow a230a129a5 [FIXED JENKINS-35826] pagination (#398)
* Almost working pagintion prototype

* increment the "currentPage" from the store's "runs" property since that is what's referenced when we make the fetch; ignore the "pageStart" property for now since it's not being passed through correctly

* fix a pagination bug; set page size = 25

* refine "show more" button

* fix for run details

* Basic pagination working

* Cleanup

* Lint fixes

* Fixing tests & refactoring duedupe

* Fix remaining lint & test issues

* Fix bo-web test

* Cookies not passed through fetch, auth was failing

* Fix folder fetching

* Fix a couple bad links

* Misc. fixups

* Paginate branches & pull requests, fix some other random issues

* Refactor container filtering to use extensible mechanism

* Fix folders affecting pagination

* Move ContainerFilter to blueocean-rest-impl

* Last tweak to ContainerFilter to support multiple filters properly

* Add es6-promise so IE isn't b0rked

* Fix all pipeline vs org pipeline mismatches

* Made progress on tests & action rewrites

* Fix lint

* Fix push-event tests

* All tests passing!!!

* Remove state usage from OrganizationPipelines

* Fix a few obvious things

* Fix favorites on header

* Revert changes to paths

* Fix lint issues

* Remove unused vars

* Return to original location from run details

* Undoing unnecessary changes

* Revert code changes due to run detail URL changes

* Fix ATH fail

* SSE all working FTW!

* Address remaining PR comments

* Update fetch size to 25
2016-08-25 23:56:43 -04:00
Keith Zantow b1b60a3b6a Revert "Jenkins 36133 Animate in steps when stage node is clicked (#421)" (#434)
This reverts commit 68cd8cd122.
2016-08-25 22:26:56 -04:00
James William Dumay e50a21bd81 Hibernation change has been going haywire on dogfood and on local master with random refreshes. (#435)
Revert "Page reload after hibernate (#429)"

This reverts commit 9eaadfbb18.
2016-08-26 12:23:12 +10:00
Cliff Meyers 3ef99dd7e8 Hotfix/fix js extensions version (#432)
* construct parent dir structure

* tick js-extensions after publishing 0.0.22

* tick up js-extensions versions
2016-08-25 16:17:56 -04:00
Thorsten Scherler 0f6f79bb0e [JENKINS-37640] Add artifacts to the runRecord (#431) 2016-08-25 13:28:21 +02:00
Thorsten Scherler 68cd8cd122 Jenkins 36133 Animate in steps when stage node is clicked (#421)
* [JENKINS-36642] Implement alternative view, so you can always use the logConsole view as in freestyle, even if you are looking at steps

* [JENKINS-36642] cleaner code

* [JENKINS-36642] reset query when you go back, we may want to store the whole location object in previous so we have a perfect prev.

* [JENKINS-36642] Add more comments

* [JENKINS-36642] Remove link, but leave functionality.

* checkstyle - formating changes

* [JENKINS-36642] in case the nodes have no steps that provide any information we show the console view directly

* [JENKINS-36642] edge case no stages only steps should not show logConsole

* [JENKINS-36642] fix linking, we will point to the progress bar when we link to the full log and not to the first line in the log. this way we focus the loading and not waiting until it has ended.

* [JENKINS-36133] first tests

* [JENKINS-36133] WIP playing around with transactions

* [JENKINS-36642] prevent showing up the logConsole on running jobs

* [JENKINS-36642] Only if the run is finished we may show the log console view

* [maven-release-plugin] prepare release blueocean-parent-1.0-alpha-8

* [maven-release-plugin] prepare for next development iteration

* [JENKINS-36642] prevent showing up the logConsole on running jobs

* [JENKINS-36642] Only if the run is finished we may show the log console view

* [JENKINS-36133] unique key for transition groups of the different nodes. better naming

* [JENKINS-36133] smoother animations

* eslint - formating changes and fix offences

* [JENKINS-36133] fix version

* [JENKINS-37021] Commit messages are missing on the activity view (#422)

* [JENKINS-37021] fix message

* [JENKINS-37021] use last commit from changeset

* [JENKINS-35890] Developer can click the commit ID on the Changes tab of the Run result screen to view the full commit details (#423)

* [JENKINS-35890] Link in changeset to the configured url

* [JENKINS-35890] Only create link when we have a url, otherwise we render only id

* [JENKINS-35890] remove test string

* [JENKINS-35890] Fix tests

* [JENKINS-37605] using encoded name (#426)
2016-08-25 10:14:30 +02:00
Tom Fennelly 9eaadfbb18 Page reload after hibernate (#429) 2016-08-25 08:20:00 +01:00
Ivan Meredith 17fa7af60c Merge remote-tracking branch 'origin/master' into story/JENKINS-35783 2016-08-25 19:14:43 +12:00
Ivan Meredith 2e6d111351 Polyfill es6-promise for Promises in actions.js 2016-08-25 19:03:26 +12:00
Cliff Meyers 17e620effd Feature/jenkins 36199 toaster (#427)
* [JENKINS-36905] get 'run' and 'replay' buttons displaying in Pipeline Card; tweak CSS for actions area to adjust some layout issues

* [JENKINS-36905] make "cleanSlashes" a shared util

* [JENKINS-36905] add "cleanSlashes" calls

* [JENKINS-36905] wire up "replay" and "run" buttons for favorites cards

* [JENKINS-36905] show run button for "unknown" status (typically a newly-created project that's never been run)

* [JENKINS-36905] control display of re-run button via capabilities API

* [JENKINS-36905] delint

* [JENKINS-36973] display a "stop" button for builds that are queued or running

* [JENKINS-36199] enable MobX in blueocean-web

* [JENKINS-36199] really awful quick and dirty POC for Toaster w/ MobX in blueocean-web; needs heavy refactoring but committing as a checkpoint for "this code works, kinda"

* [JENKINS-36199] remove the toasts when appropriate

* [JENKINS-36199] setup a singleton'd ToastService inside of blueocean-core-js

* [JENKINS-36199] dependencies update

* [JENKINS-36199] set up blueocean-web test and personalization to use same ToastService instance (still very much WIP)

* [JENKINS-36199] create self-contained "ToastDrawer" in blueocean-web for display of toats; clean up old code used in testing

* [JENKINS-36199] assign Toast.id automatically if not added; add some tests

* [JENKINS-36199] remove old "Test" component now that some real tests have been added

* [JENKINS-36199] port over "RunPipeline" to use new ToastService API; fix some bugs caused by mismatched encoding on branch names

* [JENKINS-36198] remove special Toast CSS that is no longer needed with impl of JENKINS-36199

* [JENKINS-36199] fix silly mistake where mobx was specified in wrong dependency list

* [JENKINS-36199] update deps on blueocean-core-js to use published beta; delint

* [JENKINS-36199] use published version of JDL

* [JENKINS-36199] discard some changes that came in from the "stop build" feature branch that "toaster" was forked off; comments

* [JENKINS-36199] tick JDL version to get margin-top fix; tighten up spacing along bottom of dashboard cards

* [JENKINS-36199] don't hide the "queued" toast immediately once we receive a "run" toast, just let them both auto-dismiss if necessary

* [JENKINS-36199] improve unit test for "removeToast"

* [JENKINS-36199] make "blueocean-core-js" a default imported library for plugin builds

* [JENKINS-36199] safer logic for decoding branch name

* [JENKINS-36199] safer logic for decoding branch name

* [JENKINS-36199] ensure decoded branch names are displayed in Toasts
2016-08-25 00:13:55 -04:00
Ivan Meredith 857364479d Revert "Make token set anonymous user corrently"
This reverts commit e95bc044dd.
2016-08-25 14:49:46 +12:00
Ivan Meredith e95bc044dd Make token set anonymous user corrently 2016-08-25 14:19:00 +12:00
vivek 9c6ccbdf63 JENKINS-36980# Handle NPE from actions gracefully (#428) 2016-08-24 17:24:14 -07:00
Ivan Meredith 9b56aa3e8a Remove console.log 2016-08-25 10:47:02 +12:00
Ivan Meredith 59b9eafd03 Bump core-js version 2016-08-25 10:23:45 +12:00
Ivan Meredith e42684b368 Merge branch 'story/JENKINS-35783' of github.com:jenkinsci/blueocean-plugin into story/JENKINS-35783 2016-08-25 10:12:45 +12:00
Ivan Meredith 21ae9ecbff Add utils.js 2016-08-25 10:12:14 +12:00
Ivan Meredith 8fa663e685 Fixes for PR comments 2016-08-25 10:11:37 +12:00
Vivek Pandey af1f628489 Added docs to invoke API in browser with JWT enabled. 2016-08-24 14:06:13 -07:00
Tom Fennelly d91d5d7c19 [JENKINS-37006] Improved module resolution/loading/sharing across browser bundles (#416)
* Change to use the new import mechanism

* Get js-extensions tests passing ... disabled failing tests

... and run tests after dist

* Changes for new module import/export and sharing

* Updated module versions for new import/export modules sharing

* Modified checkdeps to not check sse-gateway and also added other BO pacs

* Explicit export and import of react-addons-css-transition-group for personalization

Because it dips down into the internals of react, causing it to drag in a copy of react/lib/React, screwing things up.

Obviously need to do more in the builder to find situations where code is dipping down into react subcomponents.

* No longer an explicit require of react-addons-css-transition-group

Added "aliases" support on js-bundles imports so we can catch modules trying to load react through the back door.

* Updated npm versions for upstream merge
2016-08-24 11:27:16 +01:00
Ivan Meredith 0374e0ecb4 Update SSE bus with correct function calls 2016-08-24 16:19:27 +12:00
Ivan Meredith 4f9a2ba12d Bump core-js version 2016-08-24 14:04:27 +12:00
Ivan Meredith 1d730ff5c9 Recommit files I renamed 2016-08-24 14:03:06 +12:00
Ivan Meredith 5acc432c50 Bump core-js version 2016-08-24 13:40:06 +12:00
Ivan Meredith 3ee681ec72 Refactor core-js 2016-08-24 13:35:43 +12:00
Thorsten Scherler d3f95ceb4f [JENKINS-37605] using encoded name (#426) 2016-08-23 16:52:10 +02:00
Thorsten Scherler 67cc62155e [JENKINS-35890] Developer can click the commit ID on the Changes tab of the Run result screen to view the full commit details (#423)
* [JENKINS-35890] Link in changeset to the configured url

* [JENKINS-35890] Only create link when we have a url, otherwise we render only id

* [JENKINS-35890] remove test string

* [JENKINS-35890] Fix tests
2016-08-23 16:49:12 +02:00
Thorsten Scherler 9ac41d292a [JENKINS-37021] Commit messages are missing on the activity view (#422)
* [JENKINS-37021] fix message

* [JENKINS-37021] use last commit from changeset
2016-08-23 14:56:44 +02:00
Thorsten Scherler c9d420f83d [JENKINS-36642] Implement alternative view, so you can always use the… (#413)
* [JENKINS-36642] Implement alternative view, so you can always use the logConsole view as in freestyle, even if you are looking at steps

* [JENKINS-36642] cleaner code

* [JENKINS-36642] reset query when you go back, we may want to store the whole location object in previous so we have a perfect prev.

* [JENKINS-36642] Add more comments

* [JENKINS-36642] Remove link, but leave functionality.

* checkstyle - formating changes

* [JENKINS-36642] in case the nodes have no steps that provide any information we show the console view directly

* [JENKINS-36642] edge case no stages only steps should not show logConsole

* [JENKINS-36642] fix linking, we will point to the progress bar when we link to the full log and not to the first line in the log. this way we focus the loading and not waiting until it has ended.

* [JENKINS-36642] prevent showing up the logConsole on running jobs

* [JENKINS-36642] Only if the run is finished we may show the log console view
2016-08-23 11:48:15 +02:00
Thorsten Scherler 51a340de63 [JENKINS-36676] using the same linking as in the branches tab (#424) 2016-08-23 11:12:02 +02:00
Ivan Meredith b9464a874c Add some documentation 2016-08-23 15:53:17 +12:00
Ivan Meredith 9a15765a12 Fix linting 2016-08-23 15:05:41 +12:00
Ivan Meredith 4dc626053d Bump versions 2016-08-23 14:39:26 +12:00
Ivan Meredith dca9a345b2 Merge remote-tracking branch 'origin/master' into story/JENKINS-35783 2016-08-23 13:08:45 +12:00
Ivan Meredith 4103fcfb98 Fix tests and lints 2016-08-23 12:54:16 +12:00
Cliff Meyers f03f20f091 Feature/jenkins 36905 favorite card run rerun (#417)
* [JENKINS-36905] get 'run' and 'replay' buttons displaying in Pipeline Card; tweak CSS for actions area to adjust some layout issues

* [JENKINS-36905] make "cleanSlashes" a shared util

* [JENKINS-36905] add "cleanSlashes" calls

* [JENKINS-36905] wire up "replay" and "run" buttons for favorites cards

* [JENKINS-36905] show run button for "unknown" status (typically a newly-created project that's never been run)

* [JENKINS-36905] control display of re-run button via capabilities API

* [JENKINS-36905] delint

* JENKINS-36376# Adding missing capabilities

- Refactored pipeline factory to resolve WorkflowJob and moved to pipeline module
- Capability added to branch, pipeline, non-pipeline jobs etc.

* [JENKINS-36905] finish capabilities logic to show/hide the replay button for pipeline vs. freestyle jobs

* [JENKINS-36905] remove debugger

* [JENKINS-36905] fix broken "rerun" button test

* [JENKINS-36905] fix test that broke once ClassMetadataStore was used inside of it

* [JENKINS-36905] add TODOS and make tweak to MetadataUtils.bindCapability

* use correct icon and try PUT instead

* [JENKINS-36905] fix some URL's that were incorrect and possibly causing 403's
2016-08-22 20:46:08 -04:00
Thorsten Scherler 3230730393 [hotfix/preventNPE] Prevent NPE (#420)
* [hotfix/preventNPE] Prevent NPE

* eslint - formating changes and fix offences
2016-08-22 17:07:19 +02:00
Ivan Meredith f864f701b4 Bump verison of core-js 2016-08-22 09:21:12 +12:00
Ivan Meredith 3001e11d53 Set core-js to version 0.0.1-beta1 2016-08-21 19:41:08 +12:00
Vivek Pandey dc3e155cab [maven-release-plugin] prepare for next development iteration 2016-08-19 16:39:39 -07:00
Vivek Pandey 8ca97f2820 [maven-release-plugin] prepare release blueocean-parent-1.0-alpha-8 2016-08-19 16:39:34 -07:00
Thorsten Scherler 5132008c19 [JENKINS-36773] in case of folder we show the full path (#418) 2016-08-19 10:21:34 +02:00
Ivan Meredith 280176cda1 Add missing files 2016-08-19 15:30:16 +12:00
Ivan Meredith d8c4c1b1a3 Fix lint issues 2016-08-19 15:27:26 +12:00
Ivan Meredith aa4b8bd1df Major refactoring of frontend code to use JWT 2016-08-19 15:27:22 +12:00
Ivan Meredith 6792a9e93f Refactor some http into core-js 2016-08-19 15:25:06 +12:00
Ivan Meredith efce1a99d3 Add braces to a function call 2016-08-19 15:25:05 +12:00
Ivan Meredith 853c170d3c Initial frontend commit for JWT 2016-08-19 15:25:05 +12:00
Vivek Pandey 49da081db2 Curl wrapper to call APIs with JWT token 2016-08-19 15:25:05 +12:00
Vivek Pandey 25a0e60be5 Fix for failure during permission check
Added missing GrantedAuthorities to JwtAuthenticationToken.
2016-08-19 15:25:05 +12:00
Vivek Pandey 47a9f5fe3d TOC update 2016-08-19 15:25:05 +12:00
Vivek Pandey 014901a89f JENKINS-35783# JWT support 2016-08-19 15:25:00 +12:00
Thorsten Scherler 08e10f81ab [JENKINS-37075] make log heading bold (#419) 2016-08-19 00:06:45 +02:00
Thorsten Scherler 3f5242b7a2 [JENKINS-36772] Update changes tab style so message can span multiple lines (#415)
* [JENKINS-36772] allow multiple line breaks in message

* [JENKINS-36772] vertical align should be top

* eslint - formating changes and fix offences
2016-08-18 14:06:28 +02:00
Thorsten Scherler e663ce5812 Jenkins 37505 RunDetailsTestView: Uncaught TypeError: Cannot read property 'self' of undefined (#414)
* eslint - formating changes and fix offences

* [JENKINS-37505] Fix issue by adding _links to the run record
2016-08-18 13:39:09 +02:00
vivek 05537ffadc JENKINS-37104# Matrix project handling (#412)
* JENKINS-37104# Matrix project handling

- Bare minimum support for Matrix project. Ensures it doesn't break.
- For matrix project, self href points to classic URL path ("/job/:name")
- Adds hudson.matrix.MatrixProject capability
- UI to check hudson.matrix.MatrixProject capability and if present create a link using returned self href,
  so that when user clicks on it, he gets redirected to classic Matrix job page.

* Reverting removal of matrix project test dependency, favorite tests fail as Favorite plugin idepends on matrix project plugin and has reference to MatrixConfiguration.
2016-08-17 19:31:17 -07:00
vivek 40c56c0801 Task/jenkins 36229 (#405)
* JENKINS-36229# User permission for pipeline

* BlueOcean config support

- renames blueocean-analytics-tools to bluocean-config
- defines blueocean config object and makes it available to JS code as $blueoceanConfig object
2016-08-17 12:00:17 -07:00
Cliff Meyers e58f6ce693 Merge pull request #409 from jenkinsci/bug/JENKINS-36851-hide-favorites-anonymous
Bug/jenkins 36851 hide favorites anonymous
2016-08-17 09:46:58 -04:00
Thorsten Scherler b6c9d2b0ba [JENKINS-36171] Steps with no logs should not be expandable (#410)
* [JENKINS-36171] test whether the step has the capabilty for logs. If not do not make the resultItem expandable.

* [JENKINS-36171] add fixme note about usage for capability
2016-08-17 12:46:01 +02:00
Thorsten Scherler 1620591944 Jenkins 36771 Developer should see an indeterminate progress bar when the step is loading (#408)
* [JENKINS-36771] cosmetic change

* [JENKINS-36771] first version not meeting the design, but working for all cases

* [JENKINS-36771] only showing logConsole when loading has finished
2016-08-17 12:02:35 +02:00
vivek 8f0e2b2315 JENKINS-37429# Fix circular dependency that cause errors during plugin installation (#411) 2016-08-16 18:09:05 -07:00
vivek 00521c87ca JENKINS-36376# Adding missing capabilities (#407)
- Refactored pipeline factory to resolve WorkflowJob and moved to pipeline module
- Capability added to branch, pipeline, non-pipeline jobs etc.
2016-08-16 10:51:33 -07:00
Cliff Meyers 9f3602408d [JENKINS-36851] remove console.warn 2016-08-16 09:39:44 -04:00
Cliff Meyers ee7a567c52 Merge branch 'master' into bug/JENKINS-36851-hide-favorites-anonymous 2016-08-16 09:33:26 -04:00
Cliff Meyers df13370eb8 Merge pull request #400 from jenkinsci/feature/JENKINS-36582-favorites-realtime
Feature/jenkins 36582 favorites realtime
2016-08-16 07:41:14 -04:00
Cliff Meyers c04365c918 [JENKINS-36582] fix a bug where Cookie header was not set when making request 2016-08-15 21:33:39 -04:00
Cliff Meyers c876c6e346 [JENKINS-36581] delint 2016-08-15 12:04:02 -04:00
Cliff Meyers c9c5b41d9d [JENKINS-36581] if the user is anonymous, hide all favorites components by short-circuiting the display code in FavoritesProvider 2016-08-15 10:18:41 -04:00
Cliff Meyers a40726470c [JENKINS-36582] fix URL for fetching job data; an extra "/blue" was present in the URL which would cause errors in some environments 2016-08-15 09:27:37 -04:00
Vivek Pandey ee2906d315 [maven-release-plugin] prepare for next development iteration 2016-08-12 14:40:46 -07:00
Vivek Pandey 521a495317 [maven-release-plugin] prepare release blueocean-parent-1.0-alpha-7 2016-08-12 14:40:42 -07:00
Cliff Meyers 458ec7930f [JENKINS-36582] comments 2016-08-12 14:29:03 -04:00
Cliff Meyers 96a449e37c [JENKINS-36582] reintroduce the listener so it can stay registered irrespective of how the user navigates around through the app; this fixes a bug where the user would navigate away, miss a SSE event and then have stale data on the dashboard 2016-08-12 14:23:34 -04:00
Cliff Meyers 49c554f70d [JENKINS-36582] move logic for registering and filtering job listeners to the Dashboard component since it's unnecessary for it to run on the other screens w/ a Favorites icon; allows removal of "FavoritesSseListener" which was a bit of a kludgy singleton to prevent dup registrations 2016-08-12 11:45:50 -04:00
Cliff Meyers 6566ada191 Merge pull request #404 from jenkinsci/experiment/blueocean-commons-js
stub out Blue Ocean Commons JS
2016-08-12 10:03:17 -04:00
Cliff Meyers d42b6ebe30 Merge branch 'master' into feature/JENKINS-36582-favorites-realtime 2016-08-12 09:40:33 -04:00
Cliff Meyers aa457e708a enable simple build validation - more to come 2016-08-12 09:34:08 -04:00
Cliff Meyers a7c9d93f87 rename model from blueocean-commons-js to blueocean-core-js 2016-08-12 09:33:54 -04:00
Cliff Meyers d5af8f974b [JENKINS-36582] enhance SseBus so the client can decide which events it's interested in, to prevent unnecessary REST API calls that augment data which would ultimately be thrown away 2016-08-11 13:18:51 -04:00
vivek 1c8a6f6581 JENKINS-36976# stop API with blocking call feature (#399) 2016-08-11 08:42:07 -07:00
Cliff Meyers bfabaa1e6a stubbing out new Blue Ocean Commons JS project based off JDL structure and gulpfile 2016-08-11 08:57:25 -04:00
Ivan Meredith 128267428a [FIXED JENKINS-37321] Source class details from uberclassloader (#401) 2016-08-11 12:21:33 +12:00
Cliff Meyers dfe2f86744 Merge branch 'master' into feature/JENKINS-36582-favorites-realtime
# Conflicts:
#	blueocean-dashboard/package.json
#	blueocean-personalization/package.json
#	blueocean-web/package.json
2016-08-10 14:14:21 -04:00
Cliff Meyers bc535774b7 [JENKINS-36582] ensure URL for fetching job data is correct 2016-08-10 12:58:45 -04:00
Ivan Meredith 5cf1fab9ad [JENKINS-36067] Hide branch col for non-multibranch activity view (#353)
* [JENKINS-36067] Hide branch col for non-multibranch activity view

* Fix linting issues

* Remove stray (?) call to fetchCapabilitiesIfNeeded in Activity.jsx

* Tweak IfCapability to use ClassMetadatStore from js-extensions

It's still making multiple rest API calls though because js-extensions is not being clever about batching up parallel/inflight requests. Instead it is issuing them all.

* Smarten up ClassMetadatStore wrt REST api calls

... so that it's not issuing a ton of parallel rest API calls to get the same type metadata

* Initial Commit of IfCapability Component

* Make Activity use capibilites too

* Fixed linting
* Added Capabilites.js to list capibilities

* Remove redux capabilites stuff

* Fix spelling mistake in file name

* Compose capabilites into lifecycle

* More changes to capabilityStore

* Fix spelling mistake of IfCapibility

* Remove capabilies from State record

* Fix linting

* Fix activity spec

* Add docs

* Rename _class to className

* Fix merge conflict

* Fix some stupid stuff

* Fix activity spec
2016-08-10 23:52:30 +12:00
Tom Fennelly 1fe80545b7 Set NODE_ENV via js-extensions js-builder plugin (#394)
... defaulting to "production" mode so as to disable react dev tools, which seems to grind Firefox to a crawl
2016-08-10 12:42:56 +01:00
Cliff Meyers 3a3da80a08 [JENKINS-36582] fix a bug where the URL for a queued job was not correct and would not update favorite cards correctly 2016-08-09 19:45:08 -04:00
Cliff Meyers 965a8bc923 [JENKINS-36852] delint and cleanup 2016-08-09 14:29:55 -04:00
Cliff Meyers ddac8e7c2e [JENKINS-36582] comments 2016-08-09 14:27:14 -04:00
Cliff Meyers 243fcbc0e1 [JENKINS-36582] tick JDL version to get fix for status indicator used in PipelineCard 2016-08-09 13:54:33 -04:00
Cliff Meyers 9ef2b591c5 Merge branch 'master' into feature/JENKINS-36582-favorites-realtime 2016-08-09 11:49:59 -04:00
Cliff Meyers 001f9ff20c Merge pull request #386 from jenkinsci/feature/JENKINS-37023-pipeline-card-linking
Feature/jenkins 37023 pipeline card linking
2016-08-09 11:47:52 -04:00
Cliff Meyers 7da4051c1f [JENKINS-37023] refactor code to eliminate the ill-advised "StopPropagation" component in Personalization (per Josh's feedback) 2016-08-09 11:12:48 -04:00
Cliff Meyers 08d182fdf4 Merge branch 'master' into feature/JENKINS-37023-pipeline-card-linking
# Conflicts:
#	blueocean-dashboard/package.json
#	blueocean-personalization/package.json
#	blueocean-web/package.json
2016-08-09 10:49:53 -04:00
Ivan Meredith 05ac87f86f Fixed karoke + pipeline data bloat (#397)
* `this` is now bound for _onSseEvent
* pipeline data now no longer inlines activies
2016-08-09 16:31:08 +12:00
R. Tyler Croy 74826326fb Add a little noscript about JavaScript (#395)
Fixes JENKINS-36021
2016-08-09 07:28:19 +10:00
Cliff Meyers 895177c9fb [JENKINS-37023] fix js-extensions that was incorrectly merged 2016-08-02 08:01:42 -04:00
Cliff Meyers de36bfab87 [JENKINS-36582] add store logic to update the favorite cards' job status in real time 2016-07-29 20:07:48 -04:00
Cliff Meyers 93a0968449 [JENKINS-36582] comments 2016-07-29 20:06:26 -04:00
Cliff Meyers 820d9d7c9f [JENKINS-36582] add a singleton that prevents duplicate registrations to the SseBus, wired up to the FavoritesProvider; add an action that allows job run data to flow to store 2016-07-29 20:06:10 -04:00
Cliff Meyers 7e1a81e928 [JENKINS-36582] add a wrapper around the SSE Gateway that fetches additional data via REST and sends more detailed data back to the client. decouples this logic from access to store 2016-07-29 20:04:44 -04:00
Cliff Meyers fcc0a421a4 [JENKINS-36582] change animation timing, per James 2016-07-29 19:57:45 -04:00
Cliff Meyers d94f197878 Merge branch 'master' into feature/JENKINS-37023-pipeline-card-linking
# Conflicts:
#	blueocean-dashboard/package.json
#	blueocean-personalization/package.json
#	blueocean-web/package.json
2016-07-29 12:45:05 -04:00
Cliff Meyers fb22a820d5 [JENKINS-37023] allow clicking anywhere on the card itself to open the run details; allow clicking on the title to open the activity view 2016-07-29 12:40:22 -04:00
Cliff Meyers 3e00e91d42 [JENKINS-37023] pass down the router explicitly 2016-07-29 12:38:13 -04:00
Cliff Meyers d087530e52 [JENKINS-37023] tick JDL to get updated Favorite component 2016-07-29 12:37:15 -04:00
535 changed files with 67955 additions and 5071 deletions

View File

@ -1,8 +1,3 @@
*
!blueocean-commons/target/blueocean-commons.hpi
!blueocean-dashboard/target/blueocean-dashboard.hpi
!blueocean-plugin/target/blueocean.hpi
!blueocean-rest/target/blueocean-rest.hpi
!blueocean-rest-impl/target/blueocean-rest-impl.hpi
!blueocean-web/target/blueocean-web.hpi
!docker-demo/
!blueocean/target/plugins
!docker

5
.gitignore vendored
View File

@ -14,5 +14,8 @@ npm-debug.log
**/.settings/
**/.project
**/.classpath
blueocean-web/src/main/webapp/
blueocean-web/src/main/webapp/assets
.build_container
.watch_trigger
.vscode
jsconfig.json

View File

@ -2,7 +2,7 @@
Blue Ocean is a Jenkins project that aspires to be well known and loved by Jenkins users.
Thus, similar contributing guidelines apply as to Jenkins itself.
For information on contributing to Jenkins, check out the https://wiki.jenkins-ci.org/display/JENKINS/contributing and https://wiki.jenkins-ci.org/display/JENKINS/Extend+Jenkins wiki pages over at the official https://wiki.jenkins-ci.org. They will help you get started with contributing to Jenkins.
For information on contributing to Jenkins, check out the https://wiki.jenkins-ci.org/display/JENKINS/Beginners+Guide+to+Contributing and https://wiki.jenkins-ci.org/display/JENKINS/Extend+Jenkins wiki pages over at the official https://wiki.jenkins-ci.org. They will help you get started with contributing to Jenkins.
## Changes and pull requests
@ -22,7 +22,9 @@ Once the PR is no longer a __work-in-progress__, is building fine (according to
Avoid "bike shed" discussions about styles or whitespace unless it really impacts the changeset. The contributor can be encouraged to apply editor automation in future (if it is available).
Squashing commits: if there are messy intermediate commits it is nice to squash things for the reviewer (but not mandatory). Always think about how to make it quick and easy for a reviewer (perhaps with more smaller PR's if needed).
# Merging
All commits should be squashed before being merged to `master` to preserve a sane history. The Merge button on the PR screen has been configured to do this automatically.
# Code Style

View File

@ -1,33 +1,26 @@
FROM jenkinsci/jenkins:latest
#
# Before building this Dockerfile, BlueOcean needs to be built locally using Maven
# You can build everything needed and this Dockerfile by invoking `bin/build-in-docker.sh -m`
#
COPY blueocean-commons/target/blueocean-commons.hpi /usr/share/jenkins/ref/plugins/
COPY blueocean-dashboard/target/blueocean-dashboard.hpi /usr/share/jenkins/ref/plugins/
COPY blueocean-plugin/target/blueocean.hpi /usr/share/jenkins/ref/plugins/blueocean-plugin.hpi
COPY blueocean-rest/target/blueocean-rest.hpi /usr/share/jenkins/ref/plugins/
COPY blueocean-rest-impl/target/blueocean-rest-impl.hpi /usr/share/jenkins/ref/plugins/
COPY blueocean-web/target/blueocean-web.hpi /usr/share/jenkins/ref/plugins/
# Should be kept in sync with jenkins.properties of pom.xml
# Patch version is not to be considered, we prefer to base the image off the latest LTS of the line
# and keep the dependency on the baseline in pom.xml
FROM jenkins:2.7.4
USER root
RUN cd /usr/share/jenkins/ref/plugins/; \
install-plugins.sh blueocean-commons \
blueocean-dashboard \
blueocean-plugin \
blueocean-rest \
blueocean-web \
workflow-aggregator \
docker-workflow \
pipeline-utility-steps \
pipeline-stage-view \
git \
antisamy-markup-formatter \
matrix-auth # for security, you know
COPY blueocean/target/plugins /usr/share/jenkins/ref/plugins/
# Force use of latest blueocean plugin, until this one is published and users can rely on update center for updates
RUN for f in /usr/share/jenkins/ref/plugins/blueocean-*.hpi; do mv "$f" "$f.override"; done
RUN for f in /usr/share/jenkins/ref/plugins/*.hpi; do mv "$f" "${f%%hpi}jpi"; done
RUN install-plugins.sh antisamy-markup-formatter matrix-auth # for security, you know
# See JENKINS-34035 - disable upgrade wizard
RUN echo -n 2.0 > /usr/share/jenkins/ref/jenkins.install.UpgradeWizard.state && \
echo -n 2.0 > /usr/share/jenkins/ref/jenkins.install.InstallUtil.lastExecVersion
# Force use of locally built blueocean plugin
RUN for f in /usr/share/jenkins/ref/plugins/blueocean-*.jpi; do mv "$f" "$f.override"; done
# let scripts customize the reference Jenkins folder. Used in bin/build-in-docker to inject the git build data
COPY docker/ref /usr/share/jenkins/ref
RUN npm install -g npm@3.10.9
USER jenkins

67
Dockerfile.build Normal file
View File

@ -0,0 +1,67 @@
FROM ubuntu:16.04
ENV MAVEN_VERSION 3.3.3
ENV NODE_VERSION 6.4.0
USER root
RUN apt-get update
#========================
# Miscellaneous packages
#========================
RUN apt-get update -qqy \
&& apt-get -qqy --no-install-recommends install \
sudo \
openjdk-8-jdk \
tar \
zip xz-utils \
curl wget \
git \
build-essential \
python \
iputils-ping \
&& rm -rf /var/lib/apt/lists/* \
&& sed -i 's/securerandom\.source=file:\/dev\/random/securerandom\.source=file:\/dev\/urandom/' ./usr/lib/jvm/java-8-openjdk-amd64/jre/lib/security/java.security
#==========
# Maven
#==========
RUN curl -fsSL http://archive.apache.org/dist/maven/maven-3/$MAVEN_VERSION/binaries/apache-maven-$MAVEN_VERSION-bin.tar.gz | tar xzf - -C /usr/share \
&& mv /usr/share/apache-maven-$MAVEN_VERSION /usr/share/maven \
&& ln -s /usr/share/maven/bin/mvn /usr/bin/mvn
ENV MAVEN_HOME /usr/share/maven
#===============
# Node and NPM
#===============
RUN wget --no-verbose https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz -O /opt/nodejs.tar.xz
RUN tar -C /usr/local --strip-components 1 -xJf /opt/nodejs.tar.xz
RUN mkdir /.npm && chmod 777 /.npm
#=============================================
# Misc packages needed by the ATH
#=============================================
RUN apt-get update -qqy \
&& apt-get -qqy --no-install-recommends install \
libxml2-utils \
libssl-dev \
&& rm -rf /var/lib/apt/lists/*
#========================================
# Add normal user with passwordless sudo
#========================================
RUN sudo useradd bouser --shell /bin/bash --create-home \
&& sudo usermod -a -G sudo bouser \
&& echo 'ALL ALL = (ALL) NOPASSWD: ALL' >> /etc/sudoers \
&& echo 'bouser:secret' | chpasswd
USER bouser
WORKDIR /home/bouser
#========================================
# Configure the local git user.
#========================================
RUN git config --global user.name "John Doe"
RUN git config --global user.email johndoe@example.com

45
Jenkinsfile vendored
View File

@ -1,14 +1,28 @@
#!groovy
// only 20 builds
properties([buildDiscarder(logRotator(artifactNumToKeepStr: '20', numToKeepStr: '20'))])
node {
deleteDir()
checkout scm
sh 'docker build -t blueocean_build_env - < Dockerfile.build'
docker.image('cloudbees/java-build-tools').inside {
configFileProvider([configFile(fileId: 'blueocean-maven-settings', targetLocation: 'settings.xml')]) {
docker.image('blueocean_build_env').inside {
withEnv(['GIT_COMMITTER_EMAIL=me@hatescake.com','GIT_COMMITTER_NAME=Hates','GIT_AUTHOR_NAME=Cake','GIT_AUTHOR_EMAIL=hates@cake.com']) {
try {
sh "mvn clean install -B -DcleanNode -Dmaven.test.failure.ignore"
sh "node checkdeps.js"
sh 'npm --prefix ./blueocean-core-js install'
sh 'npm --prefix ./blueocean-core-js run gulp'
sh "mvn clean install -B -DcleanNode -Dmaven.test.failure.ignore -s settings.xml -Dmaven.artifact.threads=30"
sh "node ./bin/checkdeps.js"
sh "node ./bin/checkshrinkwrap.js"
step([$class: 'JUnitResultArchiver', testResults: '**/target/surefire-reports/TEST-*.xml'])
step([$class: 'ArtifactArchiver', artifacts: '*/target/*.hpi'])
triggerATH();
} catch(err) {
currentBuild.result = "FAILURE"
} finally {
@ -17,6 +31,31 @@ node {
}
}
}
}
}
def triggerATH() {
// Assemble and archive the HPI plugins that the ATH should use.
// The ATH build can copy this artifact and use it, saving the time it
// would otherwise spend building and assembling again.
sh 'cd blueocean && tar -czvf target/ath-plugins.tar.gz target/plugins'
archiveArtifacts artifacts: 'blueocean/target/ath-plugins.tar.gz'
// Trigger the ATH, but don't wait for it.
try {
echo "Will attempt to run the ATH with the same branch name i.e. '${env.BRANCH_NAME}'."
build(job: "ATH-Jenkinsfile/${env.BRANCH_NAME}",
parameters: [string(name: 'BLUEOCEAN_BRANCH_NAME', value: "${env.BRANCH_NAME}"),
string(name: 'BUILD_NUM', value: "${env.BUILD_NUMBER}")],
wait: false)
} catch (e1) {
echo "Failed to run the ATH with the same branch name i.e. '${env.BRANCH_NAME}'. Will try running the ATH 'master' branch."
build(job: "ATH-Jenkinsfile/master",
parameters: [string(name: 'BLUEOCEAN_BRANCH_NAME', value: "${env.BRANCH_NAME}"),
string(name: 'BUILD_NUM', value: "${env.BUILD_NUMBER}")],
wait: false)
}
}
def sendhipchat() {

View File

@ -5,13 +5,13 @@ See [JENKINS-XXXXX](https://issues.jenkins-ci.org/browse/JENKINS-XXXXX).
# Submitter checklist
- [ ] Link to JIRA ticket in description, if appropriate.
- [ ] Change is code complete and matches issue description
- [ ] Apppropriate unit or acceptance tests or explaination to why this change has no tests
- [ ] Appropriate unit or acceptance tests or explanation to why this change has no tests
- [ ] Reviewer's manual test instructions provided in PR description. See Reviewer's first task below.
- [ ] Ran Acceptance Test Harness against PR changes.
# Reviewer checklist
- [ ] Run the changes and verified the change matches the issue description
- [ ] Reviewed the code
- [ ] Verified that the appropriate tests have been written or valid explaination given
- [ ] Verified that the appropriate tests have been written or valid explanation given
@jenkinsci/code-reviewers @reviewbybees
@reviewbybees

146
README.md
View File

@ -1,51 +1,65 @@
Blue Ocean is the next generation user experience for Jenkins.
# Blue Ocean
Blue Ocean is the next generation user experience for Jenkins. You can learn more about its features and roadmap on the [Blue Ocean project website](https://jenkins.io/projects/blueocean/).
It is a multi-module maven project with a few Jenkins plugins.
Join the community and [![Gitter](https://badges.gitter.im/jenkinsci/blueocean-plugin.svg)](https://gitter.im/jenkinsci/blueocean-plugin?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
Read it:
We would also like to thank [![Rollbar](https://d26gfdfi90p7cf.cloudfront.net/rollbar-badge.144534.o.png)](http://rollbar.com) for providing us error reporting.
# Get Blue Ocean
Blue Ocean is [available from the Jenkins update center](https://jenkins.io/projects/blueocean/#use-the-beta) for Jenkins 2.7.1 and above.
# Reporting bugs and feature requests
We use the [Jenkins JIRA](https://issues.jenkins-ci.org/) to log all bugs and feature requests. Create a [new account](https://accounts.jenkins.io/), [browse to JIRA](https://issues.jenkins-ci.org/) and login with your account then create a new issue with the component `blueocean-plugin`.
# For Developers
It is a multi-module maven project made up of a few Jenkins plugins. There is an aggregator plugin in the "blueocean" module.
CONTRIBUTIONS ALWAYS WELCOME NO MATTER HOW BIG OR SMALL.
Some background reading:
https://jenkins.io/blog/2016/05/26/introducing-blue-ocean/
This is in the main Update Center for Jenkins. Install the plugin called "BlueOcean beta" (and let it install its dependencies). The instructions here are for contributors to Blue Ocean and the morbidly curious. Expect refactoring.
![Pirate logo, because it's ocean and stuff](logo-yarrr.png)
![Pirate logo, because it's ocean and stuff](docu/pix/logo-yarrr.png)
Yarr...
# Modules of note
## blueocean-dashboard
## Modules of note
Blue Ocean Dashboard plugin. Currently contains the bulk of the Blue Ocean user interface. This is mostly client side JavaScript built with ES6 and React.
A quick tour of some of the modules (not all). Consult README.md in respective modules for more info.
## blueocean-plugin
### blueocean-dashboard
Acts as an aggregator plugin, making it an easy place from which to run Blue Ocean via `hpi:run`.
Blue Ocean Dashboard plugin. Currently contains a lot of the core of the Blue Ocean user interface and extension points. This is mostly client side JavaScript built with ES6 and React.
__NOTE__: As already stated, this plugin is likely to be refactored in the near future.
### blueocean
An aggregator plugin, making it an easy place from which to run Blue Ocean via `hpi:run`.
### blueocean-rest
Java interfaces and classes that specify the definition of the REST API that blueocean uses. See the README.md within this module for detail on this api.
### blueocean-rest-impl
Provides the default implementation of the core REST Apis defined in the `blueocean-rest` plugin. It comes with only freestyle job support.
## blueocean-rest
Java interfaces and classes that specify the definition of the REST API. See the README within this module for more information.
## blueocean-rest-impl
Provides the default implementation of the core REST Apis defined in the `blueocean-rest` plugin. It comes with only free style job support.
## blueocean-pipeline-api-impl
### blueocean-pipeline-api-impl
Provides implementation of Pipeline apis for Jenkins pipeline and multi-branch job types support
### blueocean-web
## blueocean-web
Core Web infrastructure that bootstraps BlueOcean UI and integrates REST API core blueocean-rest, and serves up the core javascript libraries.
Core Web infrastructure that bootstraps BlueOcean UI and integrates REST API core blueocean-rest.
## blueocean-analytics-tools
Plugin to inject analytics tools as HTML header in blueocean UI.
# Building and running
## Building and running
At a minimum you will need JVM and Maven installed, if you are doing active JavaScript development, installing NodeJS is a good idea too.
## Build everything (from root directory)
Builds all maven modules (run this the first time you check things out, at least)
@ -60,28 +74,28 @@ For now, you'll need to skip the tests if __building on Windows__, so be sure to
$ mvn clean install -DskipTests
```
## Running Blue Ocean
### Running Blue Ocean
```
$ mvn -f blueocean-plugin/pom.xml hpi:run
$ mvn -f blueocean/pom.xml hpi:run
```
Then open http://localhost:8080/jenkins/blue to start using Blue Ocean.
The Jenkins Classic UI exists side-by-side at its usual place at http://localhost:8080/jenkins.
# Browser compatibility
## Browser compatibility
The obviously goal is for Blue Ocean to be runnable on all browsers on all platforms. We're not there yet, but getting
closer. The ultimate goal will be to have browser support in line with the [Jenkins Browser Compatibility Matrix](https://wiki.jenkins-ci.org/display/JENKINS/Browser+Compatibility+Matrix).
List of browsers where we know Blue Ocean is not yet runnable:
* Internet Explorer (all versions) on Windows
* Internet Explorer < 11 on Windows (the aim is to keep IE 11 working, but help is needed to maintain a Windows test environment in the pipeline)
* AmigaOS
# Developing
## Developing
Follow the steps above for getting it running first.
@ -90,18 +104,68 @@ Look in following README's for:
* ``blueocean-rest`` for how to navigate the rest api.
* ``blueocean-rest-impl`` for more details on how to actively develop this plugin for backend codebases.
### Tools needed
### Building plugins for Blue Ocean
Blue Ocean plugins use the same plugin mechanism as Jenkins for distribution and installation, but involve a lot more Javascript if they have GUI elements.
The best way to get started is to look at the tutorial and Yeoman starter project here:
https://www.npmjs.com/package/generator-blueocean-usain
The usual plugin guide also applies for Jenkins: https://wiki.jenkins-ci.org/display/JENKINS/Plugin+tutorial#Plugintutorial-CreatingaNewPlugin
Ask for help in #jenkins-ci or on the mailing list if you are working on a plugin.
#### Tools needed
*Maven* is used for most building. The project is configured to grab all the tools you need from the JavaScript ecosystem to get started.
If you are working on the Javascript, you will need node and gulp installed.
If you are working on the Javascript, you will need node installed, look at the version in the pom.xml for the minimum version required.
__NOTE__: look in the README.md of the respective modules for more detailed dev docs.
#### NPM and shrinkwrap
- Ensure your npm is 3.10.8+ as this release fixes some important bugs with shrinkwrap, notably #11735 in [notes](https://github.com/npm/npm/releases/tag/v3.10.8)
- Don't edit package.json directly; use npm install to ensure that both package.json and npm-shrinkwrap.json are updated.
- To add or update a dependency:
- `npm install packageName@3.2.1 -S -E`
- To add or update a devDependency:
- `npm install packageName@3.2.1 -D -E`
- If you are handling a merge conflict in package.json, resolve the conflict in the file as normal. Then use
the appropriate command to update each conflicting dependency to ensure shrinkwrap is updated.
- To remove a dependency:
- `npm uninstall packageName -S`
- To remove a devDependency:
- `npm uninstall packageName -D`
- If you ever need to create a shrinkwrap for the first time, use `npm shrinkwrap --dev` to ensure devDependencies are
included in the shrinkwrap.
Full docs on [npm shrinkwrap](https://docs.npmjs.com/cli/shrinkwrap)
Information on [building with shrinkwrap](https://docs.npmjs.com/cli/shrinkwrap#building-shrinkwrapped-packages)
In case you want to update your dependencies with something like ```npm-check-updates``` make sure you follow the simple steps:
```
ncu -a
rm -rf node_modules npm-shrinkwrap.json
npm i
npm shrinkwrap --dev
```
# Debug and live reload with IntelliJ
## Contributing - help wanted
### i18n - Sprechen Sie Deutsch?
We have full i18n support in our plugins. Please read the [i18n documentation](./docu/I18N.md) on how you can provide new translations and how to work with i18n.
### contributing guidelines
Want to get involve with blueocean? See our [contributing guidelines](./CONTRIBUTING.md) for more informations.
## Debug and live reload with IntelliJ
Automatically deploys changes to an instance of blueocean that is run with hpi:run.
1. Enable class reloading: Preferences > Build, Execution, Deployment > Debugger > HotSwap
@ -114,12 +178,14 @@ Automatically deploys changes to an instance of blueocean that is run with hpi:r
* Runner > VM Options: `-Dblueocean.config.file=../app.properties`
3. Debug new configuration, and after compilation the class file will be reloaded
# Help
## Help
Need help?
You can chat to folks on #jenkins-ux on freenode (IRC). You can also email the jenkins-dev email list (google group: https://groups.google.com/forum/#!forum/jenkinsci-dev) - but ensure you use the prefix [Blue Ocean] in your subject line when posting.
# Presentations
## Presentations
Advanced front end development with react, redux and stuff by @scherler: https://docs.google.com/presentation/d/1dbaYTIGjGT9xX1JnWnaqjMumq94M9nGwljfMQaVtUFc/edit?usp=sharing
Watch @i386 and @jenkinsci on Twitter for frequent updates and news.

View File

@ -1,9 +1,23 @@
#!/usr/bin/env bash
set -eu -o pipefail
HERE="$(cd -P "$( dirname "${BASH_SOURCE[0]}" )" && pwd)"
PROJECT_ROOT="$(cd -P "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd)"
setup_nice_output() {
bold=""
underline=""
standout=""
normal=""
black=""
red=""
green=""
yellow=""
blue=""
magenta=""
cyan=""
white=""
# check if stdout is a terminal...
if [ -t 1 ]; then
@ -30,13 +44,13 @@ setup_nice_output() {
new_build_container() {
local build_image=$1; shift
build_container=$(docker create -i -v "$HERE":/build -w /build "$build_image" /bin/cat)
echo "$build_container" > "$HERE/.build_container"
build_container=$(docker create -i -v "$PROJECT_ROOT":/build -w /build "$build_image" /bin/cat)
echo "$build_container" > "$PROJECT_ROOT/.build_container"
}
delete_build_container() {
docker rm "$build_container"
rm "$HERE/.build_container"
rm "$PROJECT_ROOT/.build_container"
}
stop_build_container() {
@ -53,8 +67,8 @@ stop_trap() {
prepare_build_container() {
local build_image=$1; shift
if [[ -f $HERE/.build_container ]]; then
read -r build_container < "$HERE/.build_container"
if [[ -f $PROJECT_ROOT/.build_container ]]; then
read -r build_container < "$PROJECT_ROOT/.build_container"
else
new_build_container "$build_image"
return
@ -64,7 +78,7 @@ prepare_build_container() {
echo "${yellow}=> ${normal}Removing old build container ${build_container}"
docker kill "$build_container" || true
docker rm "$build_container" || true
rm "$HERE/.build_container"
rm "$PROJECT_ROOT/.build_container"
else
local state; state=$(docker inspect --format="{{ .State.Status }}" "$build_container")
if [[ $? -ne 0 || "$state" != "exited" ]]; then
@ -98,21 +112,33 @@ build_inside() {
stop_build_container
}
build-git-description() {
local head="$(git rev-parse --verify HEAD)"
echo "BlueOcean plugins built from commit <a href=\"https://github.com/jenkinsci/blueocean-plugin/commit/${head}\">${head}</a>"
local pr="$(git show-ref | sed -n "s|^$head refs/remotes/.*/pr/\(.*\)$|\1|p")"
if [[ ! -z $pr ]]; then
echo ", <a href=\"https://github.com/jenkinsci/blueocean-plugin/pull/${pr}\">Pull Request ${pr}</a><br>"
fi
}
make_image() {
echo "${yellow}=> ${normal}Building BlueOcean docker image ${tag_name}"
(cd "$HERE" && docker build -t "$tag_name" . )
echo "${yellow}=> ${normal}Building BlueOcean docker development image ${tag_name}"
(cd "$PROJECT_ROOT" && docker build -t "$tag_name" . )
}
build_commands="mvn clean install -B -DcleanNode -Dmaven.test.failure.ignore"
tag_name=blueocean-local
tag_name="blueocean-dev:local"
usage() {
cat <<EOF
usage: $(basename $0) [-c|--clean] [-m|--make-image[=tag_name]] [-h|--help] [BUILD_COMMAND]
usage: $(basename $0) [-c|--clean] [-m|--make-image[=tag_name]] [-g|--git-data] [-h|--help] [BUILD_COMMAND]
Build BlueOcean plugin suite locally like it would be in Jenkins, by isolating the build
inside a Docker container. Requires a local Docker daemon to work.
Can also create a BlueOcean docker image if '-m' is passed.
Create a BlueOcean docker dev image with Dockerfile if '-m' is passed and inject git revision data
to it if '-g' is passed.
In order to speed up builds, the build container is kept between builds in order to keep
Maven / NPM caches. It can be cleaned up with '-c' option.
@ -125,6 +151,7 @@ EOF
clean=false
make_image=false
git_data=false
for i in "$@"; do
case $i in
@ -144,6 +171,10 @@ for i in "$@"; do
make_image=true
shift # past argument=value
;;
-g|--git-data)
git_data=true
shift # past argument=value
;;
*)
break
;;
@ -154,6 +185,13 @@ if [[ $# -ne 0 ]]; then build_commands="$*"; fi
setup_nice_output
build_inside "cloudbees/java-build-tools"
if [[ "$git_data" = true ]]; then
mkdir -p "$PROJECT_ROOT/docker/ref/init.groovy.d"
cat > "$PROJECT_ROOT/docker/ref/init.groovy.d/build_data.groovy" <<EOF
jenkins.model.Jenkins.instance.setSystemMessage('''$(build-git-description)''')
EOF
fi
if [[ "$make_image" = true ]]; then
make_image
fi

34
checkdeps.js → bin/checkdeps.js Normal file → Executable file
View File

@ -12,7 +12,10 @@
Any conflicting PROD dependencies will be printed on STDERR, and it will exit(1)
If no conflicts, or only PEER/DEV conflicts, normal exit(0)
Any conflicting DEV or PEER dependencies for packages in @jenkins-cd/ npm group will be printed on STDERR,
and it will exit(1)
If no conflicts, or only non-jenkins PEER/DEV conflicts, normal exit(0)
**********************************************************************************************
*********************************************************************************************/
@ -39,26 +42,30 @@ function initEntry(dependency, version) {
var packageFiles = [];
packageFiles.push(require("./blueocean-dashboard/package.json"));
packageFiles.push(require("./blueocean-web/package.json"));
packageFiles.push(require("../blueocean-dashboard/package.json"));
packageFiles.push(require("../blueocean-web/package.json"));
packageFiles.push(require("../blueocean-personalization/package.json"));
packageFiles.push(require("../blueocean-config/package.json"));
packageFiles.push(require("../js-extensions/package.json"));
// Add some expected dependencies, so we go another level deep just for these
packageFiles.push(require("./blueocean-dashboard/node_modules/@jenkins-cd/design-language/package.json"));
packageFiles.push(require("./blueocean-dashboard/node_modules/@jenkins-cd/sse-gateway/package.json"));
packageFiles.push(require("./blueocean-dashboard/node_modules/@jenkins-cd/js-extensions/package.json"));
packageFiles.push(require("../blueocean-dashboard/node_modules/@jenkins-cd/design-language/package.json"));
packageFiles.forEach(packageFile => {
addDependencies("prod", packageFile.dependencies);
// addDependencies("dev", packageFile.devDependencies);
// addDependencies("peer", packageFile.peerDependencies);
addDependencies("prod", packageFile.dependencies, true);
addDependencies("dev", packageFile.devDependencies, false);
addDependencies("peer", packageFile.peerDependencies, false);
function addDependencies(kind, deps, includeNonJenkins) {
function addDependencies(kind, deps) {
if (deps) {
Object.keys(deps).forEach(dependency => {
const version = deps[dependency];
initEntry(dependency, version);
allDependencies[dependency][version].push(packageFile.name + " (" + kind + ")");
if (includeNonJenkins || dependency.startsWith("@jenkins-cd")) {
const version = deps[dependency];
initEntry(dependency, version);
allDependencies[dependency][version].push(packageFile.name + " (" + kind + ")");
}
});
}
}
@ -80,5 +87,6 @@ Object.keys(allDependencies).forEach(dependency => {
if (errs.length) {
console.error(JSON.stringify(errs, null, 4));
console.log('You can use bin/cleanInstall to install the dominant dependency in various places.');
process.exitCode = 1;
}

113
bin/checkshrinkwrap.js Executable file
View File

@ -0,0 +1,113 @@
#!/usr/bin/env node
/*********************************************************************************************
**********************************************************************************************
Checks for imprecise version numbers in package.json, and compares deps/devDeps between package/shrinkwrap jsons.
Usage:
node checkshrinkwrap.js
- Any imprecise version number (e.g. ~, ^, >=, etc) in package.json "dependencies" or "devDependencies" will fail
- Any dependency in package.json but not in npm-shrinkwrap.json will fail
- Any version mismatch in the above will fail
All failures will exit(1). Otherwise, normal exit(0)
**********************************************************************************************
*********************************************************************************************/
const fs = require('fs');
// match "1.2.3" or "1.2.3-beta5"
const PRECISE_VERSION_CHARS_PATTERN = /^\d+\.\d+\.\d+(-[A-Za-z0-9]+)*$/;
const start = new Date().getTime();
checkProject('../blueocean-dashboard');
checkProject('../blueocean-personalization');
checkProject('../blueocean-web');
checkProject('../blueocean-config');
checkProject('../blueocean-core-js');
const ellapsed = new Date().getTime() - start;
console.log(`all dependencies look good! took ${ellapsed}ms`);
// done!
function checkProject(pathToProject) {
const resolvedPath = buildPath(`${__dirname}/${pathToProject}`);
console.log(`validating dependencies in ${resolvedPath}`);
const packageJsonPath = buildPath(`${resolvedPath}/package.json`);
const shrinkwrapJsonPath = buildPath(`${resolvedPath}/npm-shrinkwrap.json`);
const packages = require(packageJsonPath);
const packageDeps = packages.dependencies;
const packageDevDeps = packages.devDependencies;
checkImpreciseDependencies(packageDeps);
checkImpreciseDependencies(packageDevDeps);
checkDuplicateDependencies(packageDeps, packageDevDeps);
const allDeps = Object.assign({}, packageDeps, packageDevDeps);
const shrinkwrap = require(shrinkwrapJsonPath);
validateDepsAgainstShrinkwrap(allDeps, shrinkwrap);
console.log('success!');
}
function buildPath(path) {
try {
return fs.realpathSync(path);
} catch (error) {
console.error(`ERROR: Could not find ${path}`);
process.exit(1);
}
}
function checkImpreciseDependencies(dependencies) {
const badDeps = [];
Object.keys(dependencies).forEach(name => {
const version = dependencies[name];
if (!PRECISE_VERSION_CHARS_PATTERN.test(version)) {
badDeps.push(`${name}@${version}`);
}
});
if (badDeps.length) {
badDeps.forEach(dep => console.error(`${dep} must use precise version`));
console.error(`did you use 'npm install dep --save/-dev -E' ?`)
process.exit(1);
}
}
function checkDuplicateDependencies(depList1, depList2) {
const keys1 = Object.keys(depList1);
const keys2 = Object.keys(depList2);
const duplicates = keys1.concat(keys2).filter((name, index, allKeys) => index !== allKeys.indexOf(name));
if (duplicates.length) {
duplicates.forEach(name => console.error(`${name} is already defined in 'dependencies'; remove from 'devDependencies'`));
process.exit(1);
}
}
function validateDepsAgainstShrinkwrap(allDeps, shrinkwrap) {
const badDeps = [];
const shrinkDeps = shrinkwrap.dependencies;
Object.keys(allDeps).forEach(name => {
const version = allDeps[name];
if (!shrinkDeps[name]) {
badDeps.push(`${name}@${version} missing in shrinkwrap`);
} else if (shrinkDeps[name].version !== version) {
badDeps.push(`${name} should be ${version} but found ${shrinkDeps[name].version}`);
}
});
if (badDeps.length) {
badDeps.forEach(message => console.error(message));
console.log('You can use bin/cleanInstall to install the dominant dependency in various places.');
process.exit(1);
}
}

86
bin/cleanInstall.js Executable file
View File

@ -0,0 +1,86 @@
#!/usr/bin/env node
const fs = require('fs');
const async = require('async');
const exec = require('child_process').exec;
const prompt = require('prompt');
const start = new Date().getTime();
const directories = ['../blueocean-dashboard', '../blueocean-personalization', '../blueocean-web'];
prompt.start();
prompt.get({
properties: {
package: {
message: `PACKAGE to install?`,
required: true,
},
version: {
message: `VERSION to install?`,
required: true,
}
}
}, function (err, result) {
// Log the results.
console.log('Command-line input received:');
console.log('package: ' + result.package);
console.log('version: ' + result.version);
// const lib = '@jenkins-cd/design-language';
// const version = '0.0.79-unpublishedthor1';
async.mapSeries(directories, function (elem, callback) {
console.log('Current element', elem);
removeAndInstall(elem, result.package, result.version, callback);
}, function (err, result) {
if (err) {
console.error('Something went wrong', err);
}
const ellapsed = new Date().getTime() - start;
console.log(`Install look good! took ${ellapsed}ms`);
process.exit(0);
});
});
function buildPath(path) {
try {
return fs.realpathSync(path);
} catch (error) {
console.error(`ERROR: Could not find ${path}`);
return null;
}
}
function removeAndInstall(pathToProject, lib, version, callback) {
const resolvedPath = buildPath(`${__dirname}/${pathToProject}`);
const removeDir = buildPath(`${resolvedPath}/node_modules/` + lib);
if (removeDir !== null) {
console.log(`remove dir in ${removeDir}`);
deleteFolderRecursive(removeDir);
}
process.chdir(resolvedPath);
console.log('In directory ' + process.cwd());
install(lib + '@' + version, callback);
}
//remove folder Syncronously
function deleteFolderRecursive(path) {
if (fs.existsSync(path)) {
fs.readdirSync(path).forEach(function (file, index) {
var curPath = path + "/" + file;
if (fs.lstatSync(curPath).isDirectory()) { // recurse
deleteFolderRecursive(curPath);
} else { // delete file
fs.unlinkSync(curPath);
}
});
fs.rmdirSync(path);
}
}
function install(packages, callback) {
console.log('installing ', packages);
const child = exec('npm install ' + packages + ' --save -E',
function (error, stdout, stderr) {
if (error !== null) {
callback(error);
}
callback(error, stdout);
});
}

30
bin/git-helper.sh Executable file
View File

@ -0,0 +1,30 @@
#!/usr/bin/env bash
set -eu -o pipefail
PROJECT_ROOT="$(cd -P "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd)"
usage() {
cat <<EOF
usage: $(basename "$0") COMMAND ARGS
Helper script to query Git
Commands:
pr-id Find the Pull Request id
Assume that 'git fetch --progress https://github.com/jenkinsci/blueocean-plugin.git +refs/pull/*/head:refs/remotes/origin/pr/*' already ran
EOF
exit 0
}
pr-id() {
local github_remote=origin
local head=$(git rev-parse --verify HEAD)
git show-ref | sed -n "s|^$head refs/remotes/${github_remote}/pr/\(.*\)$|\1|p"
}
command_name="$1"; shift; case "$command_name" in
pr-id)
pr-id "$@"
;;
*)
usage
esac

83
bin/jwtcurl.sh Executable file
View File

@ -0,0 +1,83 @@
#!/usr/bin/env bash
##
#
# Usage
#
# jwtcurl [-u username:password] [-b BASE_URL] "[-X GET|POST|PUT|DELETE] BO_API_URL"
#
# Options:
# -v: verbose output
# -u: basic auth parameter in username:password format
# -b: base url of jenkins without trailing slash. e.g. http://localhost:8080/jenkins or https://blueocean.io
#
# Note: You need to enclose last argument in double quotes if you are passing arguments to curl.
#
# Examples:
#
# Anonymous user:
#
# jwtcurl http://localhost:8080/jenkins/blue/rest/organizations/jenkins/pipelines/p1/
#
# User with credentials:
#
# jwtcurl -u admin:admin http://localhost:8080/jenkins/blue/rest/organizations/jenkins/pipelines/p1/
#
# Use base url other than http://localhost:8080/jenkins
#
# jwtcurl -u admin:admin -b https://myjenkinshost http://localhost:8080/jenkins/blue/rest/organizations/jenkins/pipelines/p1/
#
# Author: Vivek Pandey
#
##
if [ $# -eq 0 ]
then
echo "Usage: jwtcurl [-v] [-u username:password] [-b BASE_URL] \"-X [GET|POST|PUT|DELETE] BO_API_URL\""
exit 1;
fi
while [[ $# -gt 1 ]]
do
key="$1"
case $key in
-u)
CREDENTIAL="-u $2"
shift
;;
-b)
BASE_URL="$2"
shift
;;
-v)
VERBOSE="$2"
shift
;;
*)
# unknown option
;;
esac
shift
done
if [ ! -z "$VERBOSE" ]; then
SETX="set -x"
CURL_VERBOSE="-v"
fi
if [ -z "${BASE_URL}" ]; then
BASE_URL=http://localhost:8080/jenkins
fi
${SETX}
TOKEN=$(curl ${CURL_VERBOSE} -s -X GET ${CREDENTIAL} -I ${BASE_URL}/jwt-auth/token | awk 'BEGIN {FS=": "}/^X-BLUEOCEAN-JWT/{print $2}'|sed $'s/\r//')
if [ -z "${TOKEN}" ]; then
echo "Failed to get JWT token"
echo $?
exit 1
fi
curl ${CURL_VERBOSE} -H "Authorization: Bearer ${TOKEN}" $@

7
bin/ncuUpdate Executable file
View File

@ -0,0 +1,7 @@
#!/bin/bash
home=`pwd`
modules=('blueocean-dashboard' 'blueocean-personalization' 'blueocean-web')
echo $home
for i in "${modules[@]}"; do
cd $home/$i; ncu -a; rm -rf node_modules npm-shrinkwrap.json; npm i; npm shrinkwrap; cd $home
done

18
bin/package.json Normal file
View File

@ -0,0 +1,18 @@
{
"name": "bin",
"version": "1.0.0",
"description": "",
"main": "cleanInstall.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"async": "^2.0.1",
"prompt": "^1.0.0"
},
"dependencies": {
"@jenkins-cd/design-language": "0.0.79-unpublishedthor1"
}
}

View File

@ -1,21 +0,0 @@
> Analytics Tools to be injected in to BlueOcean UI
# RollBar
* Enable RollBar
RollBar is disabled by default. Use BLUEOCEAN_ROLLBAR_ENABLED JVM property to enable.
````
mvn hpi:run -DBLUEOCEAN_ROLLBAR_ENABLED=true
````
## Usage ...
try {
foo();
$blueocean_Rollbar.debug('foo() called');
} catch (e) {
$blueocean_Rollbar.error('Problem calling foo()', e);
}

View File

@ -1,7 +0,0 @@
var builder = require('@jenkins-cd/js-builder');
//
// Create the rollbar bundle.
// See https://github.com/jenkinsci/js-builder
//
builder.bundle('src/main/js/rollbar.js');

View File

@ -1,20 +0,0 @@
{
"name": "blueocean-analytics-tools",
"version": "0.0.1",
"description": "Analytics tools that gets injected in BlueOcean UI",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Vivek Pandey <vivek.pandey@gmail.com> (https://github.com/vivek)",
"license": "MIT",
"dependencies": {
"@jenkins-cd/js-modules": "0.0.5",
"rollbar-browser": "1.9.1"
},
"devDependencies": {
"@jenkins-cd/js-builder": "0.0.35",
"babel-eslint": "^6.1.2",
"gulp": "3.9.1",
"eslint-plugin-react": "^5.0.1"
}
}

View File

@ -1,22 +0,0 @@
package io.jenkins.blueocean.analyticstools;
import hudson.Extension;
import io.jenkins.blueocean.BluePageDecorator;
import jenkins.model.Jenkins;
/**
* @author Vivek Pandey
*/
@Extension(ordinal = 10)
public class AnalyticsTools extends BluePageDecorator {
public boolean isRollBarEnabled(){
return Boolean.getBoolean("BLUEOCEAN_ROLLBAR_ENABLED");
}
/** gives Blueocean plugin version. blueocean-web being core module is looked at to determine the version */
public String getBlueOceanPluginVersion(){
return Jenkins.getInstance().getPlugin("blueocean-web").getWrapper().getVersion();
}
}

View File

@ -1,25 +0,0 @@
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler">
<j:if test="${it.rollBarEnabled}">
<!--
Still use a small bit of JS to inject the Rollbar access token.
I think this is ok for now because we're setting a global anyway - see
comment at the end of src/main/js/rollbar.js
-->
<script>
(function () {
window.$$blueocean_pluginVersion = '${it.blueOceanPluginVersion}';
})();
</script>
<!--
Running the plugin build (or just "gulp" from the command line) will
generate a browser bundle of what's in src/main/js/rollbar.js and we
can load that using an adjunct as follows.
See gulpfile.js and see the output from running the "gulp" command.
-->
<st:adjunct includes="org.jenkins.ui.jsmodules.blueocean_analytics_tools.rollbar"/>
</j:if>
</j:jelly>

View File

@ -0,0 +1,3 @@
# Blue Ocean commons
Common utilities used across modules. Lets try to keep this not a dumping ground ;)

View File

@ -5,14 +5,14 @@
<parent>
<groupId>io.jenkins.blueocean</groupId>
<artifactId>blueocean-parent</artifactId>
<version>1.0-alpha-7-SNAPSHOT</version>
<version>1.0.0-b17-SNAPSHOT</version>
</parent>
<artifactId>blueocean-commons</artifactId>
<packaging>hpi</packaging>
<name>BlueOcean :: Commons API</name>
<name>Common API for Blue Ocean</name>
<url>https://wiki.jenkins-ci.org/display/JENKINS/Blue+Ocean+Plugin</url>
<dependencies>
<dependency>

View File

@ -0,0 +1,13 @@
package io.jenkins.blueocean.commons;
/**
* Common place put system properties that are used by blueocean modules.
*
* @author Ivan Meredith
*/
public class BlueOceanConfigProperties {
public static final boolean ROLLBAR_ENABLED = Boolean.getBoolean("BLUEOCEAN_ROLLBAR_ENABLED");
public static final boolean BLUEOCEAN_FEATURE_JWT_AUTHENTICATION = Boolean.getBoolean("BLUEOCEAN_FEATURE_JWT_AUTHENTICATION");
}

View File

@ -0,0 +1,294 @@
/*
* The MIT License
*
* Copyright (c) 2016, CloudBees, Inc.
*
* 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.
*/
package io.jenkins.blueocean.commons;
import hudson.model.Run;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.StaplerRequest;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* General purpose Blue Ocean UI URL parser.
* <p>
* This class performs a "best effort" attempt to parse a URL as a Blue Ocean
* URL, extracting what it thinks are the relevant "parts" and making available via the
* {@link #getPart(UrlPart)} and {@link #hasPart(UrlPart)} functions.
* <p>
* See TBD comment on {@link UrlPart}.
*
* @author <a href="mailto:tom.fennelly@gmail.com">tom.fennelly@gmail.com</a>
*/
@Restricted(NoExternalUse.class) // Internal use only for now because there's a fair chance we'll change how it works. See TBD comment on UrlPart.
public class BlueUrlTokenizer {
private static final Set<String> PIPELINE_TABS =
new LinkedHashSet<>(Arrays.asList("activity", "branches", "pr"));
private static final Set<String> PIPELINE_RUN_DETAIL_TABS =
new LinkedHashSet<>(Arrays.asList("pipeline", "changes", "tests", "artifacts"));
private Map<UrlPart, String> urlParts = new LinkedHashMap<>();
private UrlPart lastPart;
/**
* Enum of URL "parts".
* <p>
* Use {@link #getPart(UrlPart)} to get a specific URL "part",
* or call {@link #hasPart(UrlPart)} to check for it's existence.
* <p>
* *** TBD: decide whether to stick with this model, or to switch to more of a straight getters/setters style on the {@link BlueUrlTokenizer} instance.
* Reason for trying this approach ("parts" enum) is that I (TF) think the straight properties style with getters/setters
* would get messy as we add support for parsing more URL paths/parts i.e. a getters/setters API explosion.
* That said ... not sure I love this approach either, hence marked BlueoceanUrl as @Restricted(NoExternalUse.class). Let's suck it
* and see for a bit and change if it sucks :)
*/
public enum UrlPart {
/**
* Main blue ocean pipelines dashboard.
* i.e. /blue/pipelines/
*/
DASHBOARD_PIPELINES,
/**
* A URL pointing at a page associated with an "organization" resource.
* e.g. /blue/organizations/jenkins/...
* <p>
* Call Use {@link #getPart(UrlPart)} to get the organization name.
*/
ORGANIZATION,
/**
* A URL pointing at a pipeline.
* e.g. /blue/organizations/jenkins/f1%2Ff3%20with%20spaces%2Ff3%20pipeline/...
* <p>
* Call Use {@link #getPart(UrlPart)} to get the pipeline name. Note that the URL
* may have additional parts (e.g. {@link UrlPart#PIPELINE_TAB} or {@link UrlPart#PIPELINE_RUN_DETAIL}).
*/
PIPELINE,
/**
* A URL pointing at a pipeline tab.
* e.g. /blue/organizations/jenkins/f1%2Ff3%20with%20spaces%2Ff3%20pipeline/activity
* <p>
* Call Use {@link #getPart(UrlPart)} to get the tab name.
*/
PIPELINE_TAB,
/**
* A URL pointing at a pipeline Run Details.
* e.g. // e.g. /blue/organizations/jenkins/f1%2Ff3%20with%20spaces%2Ff3%20pipeline/detail/...
* <p>
* See {@link #BRANCH} for sub-component of this URL.
*/
PIPELINE_RUN_DETAIL,
/**
* A URL pointing at a pipeline Run Details for a specific branch.
* e.g. // e.g. /blue/organizations/jenkins/f1%2Ff3%20with%20spaces%2Ff3%20pipeline/detail/magic-branch-X/...
* <p>
* See {@link #PIPELINE_RUN_DETAIL_ID} for sub-component of this URL.
* <p>
* Call Use {@link #getPart(UrlPart)} to get the branch name.
*/
BRANCH,
/**
* A URL pointing at a pipeline Run Details for a specific run of a specific branch.
* e.g. // e.g. /blue/organizations/jenkins/f1%2Ff3%20with%20spaces%2Ff3%20pipeline/detail/magic-branch-X/55/...
* <p>
* See {@link #PIPELINE_RUN_DETAIL_ID} for sub-component of this URL.
* <p>
* Call Use {@link #getPart(UrlPart)} to get the {@link Run} ID.
*/
PIPELINE_RUN_DETAIL_ID,
/**
* A URL pointing at one of the tabs on a pipeline Run Details for a specific run of a specific branch.
* e.g. // e.g. /blue/organizations/jenkins/f1%2Ff3%20with%20spaces%2Ff3%20pipeline/detail/magic-branch-X/55/artifacts
* <p>
* Call Use {@link #getPart(UrlPart)} to get the tab name.
*/
PIPELINE_RUN_DETAIL_TAB,
}
private BlueUrlTokenizer() {
}
/**
* Parse the {@link Stapler#getCurrentRequest() current Stapler request} and return a {@link BlueUrlTokenizer} instance
* iff the URL is a Blue Ocean UI URL.
*
* @return A {@link BlueUrlTokenizer} instance iff the URL is a Blue Ocean UI URL, otherwise {@code null}.
* @throws IllegalStateException Called outside the scope of an active {@link StaplerRequest}.
*/
public static @CheckForNull
BlueUrlTokenizer parseCurrentRequest() throws IllegalStateException {
StaplerRequest currentRequest = Stapler.getCurrentRequest();
if (currentRequest == null) {
throw new IllegalStateException("Illegal call to BlueoceanUrl.parseCurrentRequest outside the scope of an active StaplerRequest.");
}
String path = currentRequest.getOriginalRequestURI();
String contextPath = currentRequest.getContextPath();
path = path.substring(contextPath.length());
return parse(path);
}
/**
* Parse the supplied URL string and return a {@link BlueUrlTokenizer} instance
* iff the URL is a Blue Ocean UI URL.
*
* @param url The URL to be parsed. The URL must not be decoded in any way, so as to ensure
* that no URL component data is lost.
* @return A {@link BlueUrlTokenizer} instance iff the URL is a Blue Ocean UI URL, otherwise {@code null}.
*/
public static @CheckForNull
BlueUrlTokenizer parse(@Nonnull String url) {
Iterator<String> urlTokens = extractTokens(url);
//
// Yes, the following code is quite ugly, but it's easy enough to understand atm.
// Unless this gets a lot more detailed, please don't get super clever ideas about using
// some fancy-pants abstractions/patterns/3rd-party-libs for parsing the URL that, while
// might make the code look neater structurally, also makes the code logic a lot harder
// to follow (without using a debugger).
//
if (urlTokens.hasNext()) {
if (urlTokens.next().equalsIgnoreCase("blue")) {
BlueUrlTokenizer blueUrlTokenizer = new BlueUrlTokenizer();
if (urlTokens.hasNext()) {
String next = urlTokens.next();
if (next.equalsIgnoreCase("pipelines")) {
// i.e. /blue/pipelines/
blueUrlTokenizer.addPart(UrlPart.DASHBOARD_PIPELINES, next);
} else if (next.equalsIgnoreCase("organizations")) {
// i.e. /blue/organizations/...
if (urlTokens.hasNext()) {
// e.g. /blue/organizations/jenkins/...
blueUrlTokenizer.addPart(UrlPart.ORGANIZATION, urlTokens.next());
if (urlTokens.hasNext()) {
// e.g. /blue/organizations/jenkins/f1%2Ff3%20with%20spaces%2Ff3%20pipeline/...
blueUrlTokenizer.addPart(UrlPart.PIPELINE, urlDecode(urlTokens.next()));
if (urlTokens.hasNext()) {
next = urlTokens.next();
if (next.equalsIgnoreCase("detail")) {
// e.g. /blue/organizations/jenkins/f1%2Ff3%20with%20spaces%2Ff3%20pipeline/detail/...
blueUrlTokenizer.addPart(UrlPart.PIPELINE_RUN_DETAIL, next);
if (urlTokens.hasNext()) {
// e.g. /blue/organizations/jenkins/f1%2Ff3%20with%20spaces%2Ff3%20pipeline/detail/magic-branch-X/...
blueUrlTokenizer.addPart(UrlPart.BRANCH, urlDecode(urlTokens.next()));
if (urlTokens.hasNext()) {
// e.g. /blue/organizations/jenkins/f1%2Ff3%20with%20spaces%2Ff3%20pipeline/detail/magic-branch-X/55/...
blueUrlTokenizer.addPart(UrlPart.PIPELINE_RUN_DETAIL_ID, urlDecode(urlTokens.next()));
if (urlTokens.hasNext()) {
next = urlTokens.next();
if (PIPELINE_RUN_DETAIL_TABS.contains(next.toLowerCase())) {
// e.g. /blue/organizations/jenkins/f1%2Ff3%20with%20spaces%2Ff3%20pipeline/detail/magic-branch-X/55/pipeline
blueUrlTokenizer.addPart(UrlPart.PIPELINE_RUN_DETAIL_TAB, next.toLowerCase());
}
}
}
}
} else if (PIPELINE_TABS.contains(next.toLowerCase())) {
// e.g. /blue/organizations/jenkins/f1%2Ff3%20with%20spaces%2Ff3%20pipeline/activity/
blueUrlTokenizer.addPart(UrlPart.PIPELINE_TAB, next.toLowerCase());
}
}
}
}
}
}
return blueUrlTokenizer;
}
}
return null;
}
private void addPart(@Nonnull UrlPart urlPart, @Nonnull String value) {
urlParts.put(urlPart, value);
this.lastPart = urlPart;
}
public boolean hasPart(@Nonnull UrlPart urlPart) {
return urlParts.containsKey(urlPart);
}
public @CheckForNull String getPart(@Nonnull UrlPart urlPart) {
return urlParts.get(urlPart);
}
/**
* Get the last {@link UrlPart} for the URL.
* @return The last {@link UrlPart} for the URL.
*/
public @CheckForNull UrlPart getLastPart() {
return this.lastPart;
}
public boolean lastPartIs(@Nonnull UrlPart urlPart) {
return this.lastPart == urlPart;
}
public boolean lastPartIs(@Nonnull UrlPart urlPart, @Nonnull String value) {
if (this.lastPart == urlPart) {
return getPart(this.lastPart).equals(value);
}
return false;
}
private static String urlDecode(String string) {
try {
return URLDecoder.decode(string, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException("Unexpected UnsupportedEncodingException for UTF-8.");
}
}
private static Iterator<String> extractTokens(String url) {
String[] uncleanedTokens = url.split("/");
List<String> cleanedTokens = new ArrayList<>();
for (String uncleanedToken : uncleanedTokens) {
if (uncleanedToken.length() != 0) {
cleanedTokens.add(uncleanedToken);
}
}
return cleanedTokens.iterator();
}
}

View File

@ -1,5 +1,6 @@
package io.jenkins.blueocean.commons;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonProcessingException;
@ -7,6 +8,7 @@ import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.introspect.VisibilityChecker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -60,7 +62,12 @@ public class JsonConverter{
mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.setVisibilityChecker(
new VisibilityChecker.Std(JsonAutoDetect.Visibility.NONE,
JsonAutoDetect.Visibility.NONE,
JsonAutoDetect.Visibility.NONE,
JsonAutoDetect.Visibility.NONE,
JsonAutoDetect.Visibility.ANY));
return mapper;
}
}

View File

@ -0,0 +1,61 @@
/*
* The MIT License
*
* Copyright (c) 2016, CloudBees, Inc.
*
* 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.
*/
package io.jenkins.blueocean.commons;
import hudson.ExtensionList;
import org.apache.tools.ant.ExtensionPoint;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
/**
* Page state "preloader" extension point.
* <p>
* Allows the loading page's JavaScript blueocean global scope to
* be pre-populated with data that we know the page is going to need, thereby
* providing a mechanism for eliminating the request overhead for that data.
*
* @author <a href="mailto:tom.fennelly@gmail.com">tom.fennelly@gmail.com</a>
*/
public abstract class PageStatePreloader extends ExtensionPoint {
/**
* Get the JavaScript object graph path at shiwh the state is to be stored.
* @return The JavaScript object graph path at shiwh the state is to be stored.
*/
@Nonnull
public abstract String getStatePropertyPath();
/**
* Get the state JSON to be set in the page's JavaScript blueocean global scope.
* @return The state JSON to be set in the page's JavaScript blueocean global
* scope, or {@code null} if no data is to be set of this page.
*/
@CheckForNull
public abstract String getStateJson();
public static ExtensionList<PageStatePreloader> all() {
return ExtensionList.lookup(PageStatePreloader.class);
}
}

View File

@ -0,0 +1,98 @@
/*
* The MIT License
*
* Copyright (c) 2016, CloudBees, Inc.
*
* 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.
*/
package io.jenkins.blueocean.commons;
import net.sf.json.JSONObject;
import javax.annotation.Nonnull;
/**
* REST prefetch data preloader.
* <p>
* Pre-populates the page with REST data, allowing the client side {@code Fetch}
* module (see {@code Fetch} module in the {@code @jenkins-cd/blueocean-core-js NPM packages})
* to avoid the REST API call overhead.
* <p>
* Create implementations of this class (and annotate with {@code @Extension}) for data that
* we know is going to be needed by the page.
*
* @author <a href="mailto:tom.fennelly@gmail.com">tom.fennelly@gmail.com</a>
*/
public abstract class RESTFetchPreloader extends PageStatePreloader {
/**
* {@inheritDoc}
*/
@Override
public final String getStatePropertyPath() {
return "prefetchdata." + getClass().getSimpleName();
}
/**
* {@inheritDoc}
*/
@Override
public final String getStateJson() {
BlueUrlTokenizer blueUrl = BlueUrlTokenizer.parseCurrentRequest();
if (blueUrl == null) {
// Not a Blue Ocean page, so nothing to be added.
return null;
}
FetchData fetchData = getFetchData(blueUrl);
if (fetchData != null) {
return fetchData.toJSON();
}
return null;
}
protected abstract FetchData getFetchData(@Nonnull BlueUrlTokenizer blueUrl);
public static final class FetchData {
private String restUrl;
private String data;
public FetchData(@Nonnull String restUrl, @Nonnull String data) {
this.restUrl = restUrl;
this.data = data;
}
public String getRestUrl() {
return restUrl;
}
public String getData() {
return data;
}
public String toJSON() {
JSONObject json = new JSONObject();
json.put("restUrl", restUrl);
json.put("data", data);
return json.toString();
}
}
}

View File

@ -0,0 +1,73 @@
/*
* The MIT License
*
* Copyright (c) 2016, CloudBees, Inc.
*
* 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.
*/
package io.jenkins.blueocean.commons.stapler;
import org.kohsuke.stapler.export.ExportConfig;
import org.kohsuke.stapler.export.Flavor;
import org.kohsuke.stapler.export.Model;
import org.kohsuke.stapler.export.ModelBuilder;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
/**
* Simple Jenkins Model Object serializer.
*
* @author <a href="mailto:tom.fennelly@gmail.com">tom.fennelly@gmail.com</a>
*/
public class ModelObjectSerializer {
private static ExportConfig config = new ExportConfig();
private ModelObjectSerializer() {
}
/**
* Serialize the supplied object to JSON and return as a {@link String}.
* @param object The object to serialize.
* @return The JSON as a {@link String}.
* @throws IOException Error serializing model object.
*/
@Nonnull
public static String toJson(@Nonnull Object object) throws IOException {
try (StringWriter writer = new StringWriter()) {
toJson(object, writer);
return writer.toString();
}
}
/**
* Serialize the supplied object to JSON and write to the supplied {@link Writer}.
* @param object The object to serialize.
* @param writer The writer to output to.
* @throws IOException Error serializing model object.
*/
public static void toJson(@Nonnull Object object, @Nonnull Writer writer) throws IOException {
Model model = new ModelBuilder().get(object.getClass());
model.writeTo(object, Flavor.JSON.createDataWriter(object, writer, config));
writer.flush();
}
}

View File

@ -1,14 +1,17 @@
package io.jenkins.blueocean.commons.stapler;
import hudson.model.Api;
import org.kohsuke.stapler.CancelRequestHandlingException;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.interceptor.Interceptor;
import org.kohsuke.stapler.interceptor.InterceptorAnnotation;
import org.kohsuke.stapler.verb.HttpVerbInterceptor;
import javax.servlet.ServletException;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.reflect.InvocationTargetException;
@ -31,14 +34,41 @@ public @interface TreeResponse {
public Object invoke(StaplerRequest request, StaplerResponse response, Object instance, Object[] arguments)
throws IllegalAccessException, InvocationTargetException, ServletException {
final Object resp = target.invoke(request, response, instance, arguments);
/**
* If request.method and HTTP verb annotations {@link GET}, {@link POST}, {@link PUT} and {@link DELETE}
* do not match it skips invoking this target. If there no such annotations present then GET as default is
* assumed and request is dispatched to target.
*/
if (matches(request)) {
final Object resp = target.invoke(request, response, instance, arguments);
return new HttpResponse() {
@Override
public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node) throws IOException, ServletException {
new Api(resp).doJson(req, rsp);
return new HttpResponse() {
@Override
public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node) throws IOException, ServletException {
new Api(resp).doJson(req, rsp);
}
};
}else{
throw new CancelRequestHandlingException();
}
}
private boolean matches(StaplerRequest request) {
String method = request.getMethod();
for (Annotation a : target.getAnnotations()) {
Class<? extends Annotation> t = a.annotationType();
InterceptorAnnotation ia = t.getAnnotation(InterceptorAnnotation.class);
if (ia !=null && ia.value()==HttpVerbInterceptor.class) {
return t.getName().endsWith(method);
}
};
}
//by default we treat it as GET
if(method.equals("GET")){
return true;
}
return false;
}
}
}

View File

@ -0,0 +1,71 @@
/*
* The MIT License
*
* Copyright (c) 2016, CloudBees, Inc.
*
* 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.
*/
package io.jenkins.blueocean.commons;
import org.junit.Assert;
import org.junit.Test;
/**
* @author <a href="mailto:tom.fennelly@gmail.com">tom.fennelly@gmail.com</a>
*/
public class BlueUrlTokenizerTest {
@Test
public void test_MalformedURLException() {
Assert.assertNull(BlueUrlTokenizer.parse("/a"));
}
@Test
public void test() {
BlueUrlTokenizer blueUrl;
blueUrl = BlueUrlTokenizer.parse("/blue/pipelines/");
Assert.assertEquals("pipelines", blueUrl.getPart(BlueUrlTokenizer.UrlPart.DASHBOARD_PIPELINES));
Assert.assertEquals(BlueUrlTokenizer.UrlPart.DASHBOARD_PIPELINES, blueUrl.getLastPart());
Assert.assertTrue(blueUrl.lastPartIs(BlueUrlTokenizer.UrlPart.DASHBOARD_PIPELINES));
blueUrl = BlueUrlTokenizer.parse("/blue/organizations/jenkins/f1%2Ff3%20with%20spaces%2Ff3%20pipeline/activity/");
Assert.assertFalse(blueUrl.hasPart(BlueUrlTokenizer.UrlPart.DASHBOARD_PIPELINES));
Assert.assertTrue(blueUrl.hasPart(BlueUrlTokenizer.UrlPart.ORGANIZATION));
Assert.assertEquals("jenkins", blueUrl.getPart(BlueUrlTokenizer.UrlPart.ORGANIZATION));
Assert.assertEquals("f1/f3 with spaces/f3 pipeline", blueUrl.getPart(BlueUrlTokenizer.UrlPart.PIPELINE));
Assert.assertEquals("activity", blueUrl.getPart(BlueUrlTokenizer.UrlPart.PIPELINE_TAB));
Assert.assertEquals(BlueUrlTokenizer.UrlPart.PIPELINE_TAB, blueUrl.getLastPart());
Assert.assertTrue(blueUrl.lastPartIs(BlueUrlTokenizer.UrlPart.PIPELINE_TAB, "activity"));
blueUrl = BlueUrlTokenizer.parse("/blue/organizations/jenkins/f1%2Ff3%20with%20spaces%2Ff3%20pipeline/detail/magic-branch-X/55/pipeline");
Assert.assertFalse(blueUrl.hasPart(BlueUrlTokenizer.UrlPart.DASHBOARD_PIPELINES));
Assert.assertTrue(blueUrl.hasPart(BlueUrlTokenizer.UrlPart.ORGANIZATION));
Assert.assertEquals("jenkins", blueUrl.getPart(BlueUrlTokenizer.UrlPart.ORGANIZATION));
Assert.assertEquals("f1/f3 with spaces/f3 pipeline", blueUrl.getPart(BlueUrlTokenizer.UrlPart.PIPELINE));
Assert.assertFalse(blueUrl.hasPart(BlueUrlTokenizer.UrlPart.PIPELINE_TAB));
Assert.assertTrue(blueUrl.hasPart(BlueUrlTokenizer.UrlPart.PIPELINE_RUN_DETAIL));
Assert.assertEquals("magic-branch-X", blueUrl.getPart(BlueUrlTokenizer.UrlPart.BRANCH));
Assert.assertEquals("55", blueUrl.getPart(BlueUrlTokenizer.UrlPart.PIPELINE_RUN_DETAIL_ID));
Assert.assertEquals("pipeline", blueUrl.getPart(BlueUrlTokenizer.UrlPart.PIPELINE_RUN_DETAIL_TAB));
Assert.assertEquals(BlueUrlTokenizer.UrlPart.PIPELINE_RUN_DETAIL_TAB, blueUrl.getLastPart());
Assert.assertTrue(blueUrl.lastPartIs(BlueUrlTokenizer.UrlPart.PIPELINE_RUN_DETAIL_TAB, "pipeline"));
}
}

View File

@ -0,0 +1,55 @@
/*
* The MIT License
*
* Copyright (c) 2016, CloudBees, Inc.
*
* 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.
*/
package io.jenkins.blueocean.commons.stapler;
import net.sf.json.JSONObject;
import org.junit.Assert;
import org.junit.Test;
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.export.ExportedBean;
import java.io.IOException;
/**
* @author <a href="mailto:tom.fennelly@gmail.com">tom.fennelly@gmail.com</a>
*/
public class ModelObjectSerializerTest {
@Test
public void test_json() throws IOException {
String xJson = ModelObjectSerializer.toJson(new X());
JSONObject jsonObj = JSONObject.fromObject(xJson);
Assert.assertEquals(X.class.getName(), jsonObj.getString("_class"));
Assert.assertEquals("xVal", jsonObj.getString("val"));
}
@ExportedBean
public static class X {
@Exported
public String val() {
return "xVal";
}
}
}

View File

@ -0,0 +1,13 @@
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
[*.md]
trim_trailing_whitespace = false

View File

@ -0,0 +1 @@
Simple marker file to tell maven to execute node build profiles.

View File

@ -0,0 +1,39 @@
> BlueOcean Config plugin
# BlueOcean configuration
BlueOcean configuration is injected as $blueOceanConfig JS object. It's in the following JSON format:
{
"version" : "1.0-alpha-7-SNAPSHOT (private-33ee8e40-vivek)",
"jenkinsConfig" : {
"version" : "2.2",
"security" : {
"authorizationStrategy" : {
"allowAnonymousRead" : true
},
"enabled" : true
}
}
}
# RollBar
* Enable RollBar
RollBar is disabled by default. Use BLUEOCEAN_ROLLBAR_ENABLED JVM property to enable.
````
mvn hpi:run -DBLUEOCEAN_ROLLBAR_ENABLED=true
````
## Usage ...
try {
foo();
$blueocean_Rollbar.debug('foo() called');
} catch (e) {
$blueocean_Rollbar.error('Problem calling foo()', e);
}

4409
blueocean-config/npm-shrinkwrap.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,27 @@
{
"name": "blueocean-config",
"version": "0.0.1",
"description": "Configuration injected in BlueOcean UI",
"scripts": {
"build": "jjsbuilder",
"mvnbuild": "jjsbuilder --tasks bundle",
"mvntest": "jjsbuilder --tasks test,lint"
},
"author": "Vivek Pandey <vivek.pandey@gmail.com> (https://github.com/vivek)",
"license": "MIT",
"dependencies": {
"@jenkins-cd/js-modules": "0.0.8",
"rollbar-browser": "1.9.2"
},
"devDependencies": {
"@jenkins-cd/js-builder": "0.0.50",
"babel-eslint": "6.1.2",
"eslint-plugin-react": "4.3.0",
"gulp": "3.9.1"
},
"jenkinscd": {
"bundle": [
"src/main/js/rollbar.js"
]
}
}

View File

@ -5,13 +5,13 @@
<parent>
<groupId>io.jenkins.blueocean</groupId>
<artifactId>blueocean-parent</artifactId>
<version>1.0-alpha-7-SNAPSHOT</version>
<version>1.0.0-b17-SNAPSHOT</version>
</parent>
<artifactId>blueocean-analytics-tools</artifactId>
<artifactId>blueocean-config</artifactId>
<packaging>hpi</packaging>
<name>BlueOcean :: Analytics Tools</name>
<name>Config API for Blue Ocean</name>
<url>https://wiki.jenkins-ci.org/display/JENKINS/Blue+Ocean+Plugin</url>
<dependencies>
@ -19,5 +19,13 @@
<groupId>${project.groupId}</groupId>
<artifactId>blueocean-web</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>blueocean-rest-impl</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>blueocean-commons</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,71 @@
package io.jenkins.blueocean.config;
import hudson.Extension;
import hudson.security.AuthorizationStrategy;
import hudson.security.FullControlOnceLoggedInAuthorizationStrategy;
import hudson.security.SecurityRealm;
import io.jenkins.blueocean.commons.BlueOceanConfigProperties;
import io.jenkins.blueocean.commons.PageStatePreloader;
import jenkins.model.Jenkins;
import net.sf.json.util.JSONBuilder;
import java.io.StringWriter;
import java.util.logging.Logger;
/**
* @author Vivek Pandey
*/
@Extension
public class BlueOceanConfigStatePreloader extends PageStatePreloader {
private static final Logger LOGGER = Logger.getLogger(BlueOceanConfigStatePreloader.class.getName());
/**
* {@inheritDoc}
*/
@Override
public String getStatePropertyPath() {
return "config";
}
/**
* {@inheritDoc}
*/
@Override
public String getStateJson() {
StringWriter writer = new StringWriter();
Jenkins jenkins = Jenkins.getInstance();
String version = Jenkins.getVersion() != null ? Jenkins.getVersion().toString() : Jenkins.VERSION;
AuthorizationStrategy authorizationStrategy = jenkins.getAuthorizationStrategy();
boolean allowAnonymousRead = true;
if(authorizationStrategy instanceof FullControlOnceLoggedInAuthorizationStrategy){
allowAnonymousRead = ((FullControlOnceLoggedInAuthorizationStrategy) authorizationStrategy).isAllowAnonymousRead();
}
new JSONBuilder(writer)
.object()
.key("version").value(getBlueOceanPluginVersion())
.key("jenkinsConfig")
.object()
.key("version").value(version)
.key("security")
.object()
.key("enabled").value(jenkins.isUseSecurity())
.key("loginUrl").value(jenkins.getSecurityRealm() == SecurityRealm.NO_AUTHENTICATION ? null : jenkins.getSecurityRealm().getLoginUrl())
.key("authorizationStrategy").object()
.key("allowAnonymousRead").value(allowAnonymousRead)
.endObject()
.key("enableJWT").value(BlueOceanConfigProperties.BLUEOCEAN_FEATURE_JWT_AUTHENTICATION)
.endObject()
.endObject()
.endObject();
return writer.toString();
}
/** gives Blueocean plugin version. blueocean-web being core module is looked at to determine the version */
private String getBlueOceanPluginVersion(){
return Jenkins.getInstance().getPlugin("blueocean-web").getWrapper().getVersion();
}
}

View File

@ -21,27 +21,24 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.jenkins.blueocean.jsextensions;
package io.jenkins.blueocean.config;
import java.io.IOException;
import java.io.StringWriter;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import org.apache.commons.io.IOUtils;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.WebMethod;
import org.kohsuke.stapler.verb.GET;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -49,8 +46,6 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import hudson.Extension;
import hudson.PluginWrapper;
import hudson.util.HttpResponses;
import io.jenkins.blueocean.RootRoutable;
import io.jenkins.blueocean.rest.model.BlueExtensionClass;
import io.jenkins.blueocean.rest.model.BlueExtensionClassContainer;
import jenkins.model.Jenkins;
@ -58,14 +53,18 @@ import net.sf.json.JSONArray;
/**
* Utility class for gathering {@code jenkins-js-extension} data.
*
* @author <a href="mailto:tom.fennelly@gmail.com">tom.fennelly@gmail.com</a>
*/
@Extension
@Restricted(NoExternalUse.class)
@SuppressWarnings({"rawtypes","unchecked"})
public class JenkinsJSExtensions implements RootRoutable {
public class JenkinsJSExtensions {
private static final Logger LOGGER = LoggerFactory.getLogger(JenkinsJSExtensions.class);
private static final String PLUGIN_ID = "hpiPluginId";
private static final String PLUGIN_VER = "hpiPluginVer";
private static final String PLUGIN_EXT = "extensions";
private static final ObjectMapper mapper = new ObjectMapper();
/**
@ -79,25 +78,13 @@ public class JenkinsJSExtensions implements RootRoutable {
private static final Map<String, Object> jsExtensionCache = new ConcurrentHashMap<>();
public JenkinsJSExtensions() {
}
/**
* For the location in the API: /blue/js-extensions
*/
@Override
public String getUrlName() {
return "js-extensions";
}
/**
* Return the actual data, from /js-extensions
*/
@WebMethod(name="") @GET
public HttpResponse doData() {
public static JSONArray getExtensionsData() {
Object jsExtensionData = getJenkinsJSExtensionData();
JSONArray jsExtensionDataJson = JSONArray.fromObject(jsExtensionData);
return HttpResponses.okJSON(jsExtensionDataJson);
return jsExtensionDataJson;
}
/*protected*/ static Collection<Object> getJenkinsJSExtensionData() {
@ -106,7 +93,7 @@ public class JenkinsJSExtensions implements RootRoutable {
}
private static String getGav(Map ext){
return ext.get("hpiPluginId") != null ? (String)ext.get("hpiPluginId") : null;
return (String) ext.get(PLUGIN_ID);
}
private static void refreshCacheIfNeeded(){
@ -122,54 +109,86 @@ public class JenkinsJSExtensions implements RootRoutable {
refreshCache(pluginCache);
}
for (PluginWrapper pluginWrapper : pluginCache) {
//skip if not active
if (!pluginWrapper.isActive()) {
continue;
}
//skip probing plugin if already read
if (jsExtensionCache.get(pluginWrapper.getLongName()) != null) {
continue;
}
try {
Enumeration<URL> dataResources = pluginWrapper.classLoader.getResources("jenkins-js-extension.json");
boolean hasDefinedExtensions = false;
while (dataResources.hasMoreElements()) {
URL dataRes = dataResources.nextElement();
StringWriter fileContentBuffer = new StringWriter();
LOGGER.debug("Reading 'jenkins-js-extension.json' from '{}'.", dataRes);
try {
IOUtils.copy(dataRes.openStream(), fileContentBuffer, Charset.forName("UTF-8"));
Map<?,List<Map>> extensionData = mapper.readValue(dataRes.openStream(), Map.class);
List<Map> extensions = (List<Map>)extensionData.get("extensions");
try (InputStream dataResStream = dataRes.openStream()) {
Map<String, Object> extensionData = mapper.readValue(dataResStream, Map.class);
String pluginId = getGav(extensionData);
if (pluginId != null) {
// Skip if the plugin name specified on the extension data does not match the name
// on the PluginWrapper for this iteration. This can happen for e.g. aggregator
// plugins, in which case you'll be seeing extension resources on it's dependencies.
// We can skip these here because we will process those plugins themselves in a
// future/past iteration of this loop.
if (!pluginId.equals(pluginWrapper.getShortName())) {
continue;
}
} else {
LOGGER.error(String.format("Plugin %s JS extension has missing hpiPluginId", pluginWrapper.getLongName()));
continue;
}
List<Map> extensions = (List<Map>) extensionData.get(PLUGIN_EXT);
for (Map extension : extensions) {
try {
String type = (String)extension.get("type");
String type = (String) extension.get("type");
if (type != null) {
BlueExtensionClassContainer extensionClassContainer
= Jenkins.getInstance().getExtensionList(BlueExtensionClassContainer.class).get(0);
Map classInfo = (Map)mergeObjects(extensionClassContainer.get(type));
List classInfoClasses = (List)classInfo.get("_classes");
Map classInfo = (Map) mergeObjects(extensionClassContainer.get(type));
List classInfoClasses = (List) classInfo.get("_classes");
classInfoClasses.add(0, type);
extension.put("_class", type);
extension.put("_classes", classInfoClasses);
}
} catch(Exception e) {
} catch (Exception e) {
LOGGER.error("An error occurred when attempting to read type information from jenkins-js-extension.json from: " + dataRes, e);
}
}
String pluginId = getGav(extensionData);
if (pluginId != null) {
jsExtensionCache.put(pluginId, mergeObjects(extensionData));
} else {
LOGGER.error(String.format("Plugin %s JS extension has missing hpiPluginId", pluginWrapper.getLongName()));
}
} catch (Exception e) {
LOGGER.error("Error reading 'jenkins-js-extension.json' from '" + dataRes + "'. Extensions defined in the host plugin will not be active.", e);
extensionData.put(PLUGIN_VER, pluginWrapper.getVersion());
jsExtensionCache.put(pluginId, mergeObjects(extensionData));
hasDefinedExtensions = true;
}
}
if (!hasDefinedExtensions) {
// Manufacture an entry for all plugins that do not have any defined
// extensions. This adds some info about the plugin that the UI might
// need access to e.g. the plugin version.
Map<String, Object> extensionData = new LinkedHashMap<>();
extensionData.put(PLUGIN_ID, pluginWrapper.getShortName());
extensionData.put(PLUGIN_VER, pluginWrapper.getVersion());
extensionData.put(PLUGIN_EXT, Collections.emptyList());
jsExtensionCache.put(pluginWrapper.getShortName(), mergeObjects(extensionData));
}
} catch (IOException e) {
LOGGER.error(String.format("Error locating jenkins-js-extension.json for plugin %s", pluginWrapper.getLongName()));
}
}
}
//
// ***********************************************************************************************************
// TODO: Someone needs to write some docs on this function, explaining what it is doing and why it's needed.
// ***********************************************************************************************************
//
private static Object mergeObjects(Object incoming) {
if (incoming instanceof Map) {
Map m = new HashMap();

View File

@ -0,0 +1,27 @@
package io.jenkins.blueocean.config;
import hudson.Extension;
import io.jenkins.blueocean.commons.PageStatePreloader;
/**
* {@link PageStatePreloader} for js-extensions data.
*/
@Extension
public class JenkinsJSExtensionsStatePreloader extends PageStatePreloader {
/**
* {@inheritDoc}
*/
@Override
public String getStatePropertyPath() {
return "jsExtensions";
}
/**
* {@inheritDoc}
*/
@Override
public String getStateJson() {
return JenkinsJSExtensions.getExtensionsData().toString();
}
}

View File

@ -0,0 +1,16 @@
package io.jenkins.blueocean.config;
import hudson.Extension;
import io.jenkins.blueocean.BluePageDecorator;
import io.jenkins.blueocean.commons.BlueOceanConfigProperties;
/**
* @author Vivek Pandey
*/
@Extension(ordinal = 10)
public class RollbarDecorator extends BluePageDecorator {
public boolean isRollBarEnabled(){
return BlueOceanConfigProperties.ROLLBAR_ENABLED;
}
}

View File

@ -28,7 +28,7 @@ var transformer = function (payload) {
var _rollbarConfig = {
accessToken: '81f3134dedf44871b9cc0a347b1313df',
captureUncaught: true,
code_version: window.$blueocean_pluginVersion, // see header.jelly
code_version: window.$blueocean.config.version, // see header.jelly
source_map_enabled: true,
guess_uncaught_frames: true,
transform: transformer

View File

@ -0,0 +1,12 @@
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler">
<j:if test="${it.rollBarEnabled}">
<!--
Running the plugin build (or just "gulp" from the command line) will
generate a browser bundle of what's in src/main/js/rollbar.js and we
can load that using an adjunct as follows.
See gulpfile.js and see the output from running the "gulp" command.
-->
<st:adjunct includes="org.jenkins.ui.jsmodules.blueocean_config.rollbar"/>
</j:if>
</j:jelly>

View File

@ -0,0 +1,8 @@
{
"presets": [
"es2015", "react", "stage-0"
],
"plugins": [
"transform-decorators-legacy"
]
}

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,9 @@
{
"extends": "@jenkins-cd/jenkins/react",
"rules": {
"react/jsx-no-bind": 0,
"no-unused-vars": [2, {"varsIgnorePattern": "^React$"}],
"max-len": [1, 160, 4],
"experimentalDecorators": 0
}
}

8
blueocean-core-js/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
node_modules/
npm-debug.log
.DS_Store
dist
*.iml
.idea/
/.project
reports

View File

View File

@ -0,0 +1,3 @@
# Blue Ocean Core JS
This is an npm module that contains common javascript libraries/utilities that are used across modules and plugins and blue oceans.

View File

@ -0,0 +1,135 @@
"use strict";
/*
Build file for Jenkins Blue Ocean Commons JavaScript.
*/
const gulp = require('gulp');
const gutil = require('gulp-util');
const sourcemaps = require('gulp-sourcemaps');
const babel = require('gulp-babel');
const less = require('gulp-less');
const rename = require('gulp-rename');
const copy = require('gulp-copy');
const del = require('del');
const runSequence = require('run-sequence');
const lint = require('gulp-eslint');
const Karma = require('karma').Server;
const fs = require('fs');
// Options, src/dest folders, etc
const config = {
clean: ["dist", "licenses", "reports"],
react: {
sources: "src/**/*.{js,jsx}",
dest: "dist"
},
less: {
sources: "src/less/core.less",
watch: 'src/less/**/*.{less,css}',
dest: "dist/assets/css",
},
copy: {
less_assets: {
sources: "src/less/**/*.svg",
dest: "dist/assets/css"
}
},
test: {
sources: "test/**/*-spec.{js,jsx}"
}
};
// Watch all
gulp.task("watch", ["clean-build"], () => {
gulp.watch(config.react.sources, ["compile-react"]);
gulp.watch(config.less.watch, ["less"]);
});
// Default to all
gulp.task("default", () =>
runSequence("clean", "lint", "test", "build", "validate"));
// Clean and build only, for watching
gulp.task("clean-build", () =>
runSequence("clean", "build", "validate"));
// Clean
gulp.task("clean", () =>
del(config.clean));
// Testing
gulp.task("lint", () => (
gulp.src([config.react.sources, config.test.sources])
.pipe(lint())
.pipe(lint.format())
.pipe(lint.failAfterError())
));
gulp.task("test", ['test-karma']);
gulp.task("test-debug", ['test-karma-debug']);
gulp.task("test-karma", (done) => {
new Karma({
configFile: __dirname + '/karma.conf.js',
}, done).start();
});
gulp.task("test-karma-debug", (done) => {
new Karma({
configFile: __dirname + '/karma.conf.js',
colors: true,
autoWatch: true,
singleRun: false,
browsers: ['Chrome'],
}, done).start();
});
// Build all
gulp.task("build", ["compile-react", "less", "copy"]);
// Compile react sources
gulp.task("compile-react", () =>
gulp.src(config.react.sources)
.pipe(sourcemaps.init())
.pipe(babel(config.react.babel))
.pipe(sourcemaps.write("."))
.pipe(gulp.dest(config.react.dest)));
gulp.task("less", () =>
gulp.src(config.less.sources)
.pipe(sourcemaps.init())
.pipe(less())
.pipe(rename("blueocean-core-js.css"))
.pipe(sourcemaps.write("."))
.pipe(gulp.dest(config.less.dest)));
gulp.task("copy", ["copy-less-assets"]);
gulp.task("copy-less-assets", () =>
gulp.src(config.copy.less_assets.sources)
.pipe(copy(config.copy.less_assets.dest, { prefix: 2 })));
// Validate contents
gulp.task("validate", () => {
const paths = [
config.react.dest,
];
for (const path of paths) {
try {
fs.statSync(path);
} catch (err) {
gutil.log('Error occurred during validation; see stack trace for details');
throw err;
}
}
});

View File

@ -0,0 +1,68 @@
// Karma configuration
// Generated on Wed Jun 01 2016 16:04:37 GMT-0400 (EDT)
module.exports = function (config) {
config.set({
basePath: '',
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ['mocha', 'browserify'],
junitReporter: {
outputDir: 'reports'
},
// include only tests here; browserify will find the rest
files: [
'test/js/test-entrypoint.js',
'test/**/*-spec.+(js|jsx)'
],
exclude: [],
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
'test/js/test-entrypoint.js': ['browserify'],
'test/**/*-spec.+(js|jsx)': ['browserify']
},
browserify: {
debug: true,
transform: ['babelify'],
extensions: ['.js', '.jsx'],
// needed for enzyme
configure: function (bundle) {
bundle.on('prebundle', function () {
bundle.external('react/addons');
bundle.external('react/lib/ReactContext');
bundle.external('react/lib/ExecutionEnvironment');
});
}
},
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['mocha','junit'],
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: false,
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: false,
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ['PhantomJS'],
// if true, Karma captures browsers, runs the tests and exits
singleRun: true,
// how many browser should be started simultaneous
concurrency: Infinity
})
};

6612
blueocean-core-js/npm-shrinkwrap.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,86 @@
{
"name": "@jenkins-cd/blueocean-core-js",
"version": "0.0.45-unpublished",
"description": "Shared JavaScript libraries for use with Jenkins Blue Ocean",
"main": "dist/js/index.js",
"scripts": {
"gulp": "gulp",
"test": "gulp test",
"prepublish": "gulp"
},
"author": "Cliff Meyers <cmeyers@cloudbees.com> (https://www.cloudbees.com/)",
"contributors": [
"Cliff Meyers <cliff.meyers@gmail.com>"
],
"license": "MIT",
"files": [
"LICENSE",
"README.md",
"licenses",
"dist"
],
"repository": {
"type": "git",
"url": "https://github.com/jenkinsci/blueocean-plugin.git"
},
"dependencies": {
"@jenkins-cd/js-extensions": "0.0.32",
"@jenkins-cd/js-modules": "0.0.8",
"@jenkins-cd/react-material-icons": "1.0.0",
"@jenkins-cd/sse-gateway": "0.0.10",
"@jenkins-cd/storage": "0.0.3",
"es6-promise": "4.0.5",
"i18next": "3.5.2",
"i18next-browser-languagedetector": "1.0.0",
"i18next-xhr-backend": "1.2.0",
"isomorphic-fetch": "2.2.1",
"jsonwebtoken": "7.1.9",
"mobx": "2.6.0",
"mobx-utils": "1.1.2",
"pem-jwk": "1.5.1",
"react-router": "3.0.0"
},
"peerDependencies": {
"react": "^15.1.0",
"react-dom": "^15.1.0"
},
"devDependencies": {
"@jenkins-cd/eslint-config-jenkins": "0.0.2",
"babel-eslint": "7.0.0",
"babel-plugin-transform-decorators-legacy": "1.3.4",
"babel-polyfill": "6.16.0",
"babel-preset-es2015": "6.16.0",
"babel-preset-react": "6.16.0",
"babel-preset-stage-0": "6.16.0",
"babelify": "7.3.0",
"browserify": "13.1.0",
"chai": "3.5.0",
"del": "2.2.2",
"enzyme": "2.4.1",
"eslint": "2.13.1",
"eslint-plugin-react": "4.3.0",
"flow-bin": "0.34.0",
"gulp": "3.9.1",
"gulp-babel": "6.1.2",
"gulp-copy": "0.0.2",
"gulp-eslint": "3.0.1",
"gulp-less": "3.1.0",
"gulp-rename": "1.2.2",
"gulp-sourcemaps": "1.6.0",
"karma": "1.3.0",
"karma-browserify": "5.1.0",
"karma-chrome-launcher": "2.0.0",
"karma-junit-reporter": "1.1.0",
"karma-mocha": "1.2.0",
"karma-mocha-reporter": "2.2.0",
"karma-phantomjs-launcher": "1.0.2",
"mocha": "3.1.0",
"phantomjs-prebuilt": "2.1.12",
"react": "15.3.2",
"react-addons-test-utils": "15.3.2",
"react-dom": "15.3.2",
"run-sequence": "1.2.2",
"sinon": "1.17.6",
"watchify": "3.7.0"
}
}

View File

@ -0,0 +1,21 @@
import React from 'react';
import Fullscreen from './Fullscreen';
import { Link } from 'react-router';
import i18nTranslator from './i18n/i18n';
const translate = i18nTranslator('blueocean-web');
/**
* Simple component to render a fullscreen 404 page
*/
export default () => (
<Fullscreen className="not-found">
<div className="message-box">
<h3>{translate('Not.found.heading', {
defaultValue: 'Page not found (404)',
})}</h3>
<div className="message">{translate('Not.found.message', { defaultValue: 'Jenkins could not find the page you were looking for. Check the URL for errors or press the back button.' })}</div>
<div className="actions"><Link to="/" className="btn">{translate('Open.dashboard', { defaultValue: 'Open Dashboard' })}</Link></div>
</div>
</Fullscreen>
);

View File

@ -0,0 +1,80 @@
/**
* Created by cmeyers on 8/18/16.
*/
import { action, observable, computed } from 'mobx';
/**
* Holds one or more toasts in state for display in UI.
*/
export class ToastService {
@observable toasts = [];
/**
* Creates a new toast that is added to the list.
*
* @param toast object with the following shape:
* {
* text: string, message text to display
* action: string, text for action link
* onActionClick: function, callback to invoke when action link is clicked
* onDismiss: function, callback to invoke when toast is dismissed (immediately, or after timeout)
* dismissDelay: number, duration in millis after which to auto-dismiss this Toast
* id: unique identifier (optional, will be autogenerated if ommitted)
* }
* @returns {number} unique ID of toast
*/
@action
newToast(toast) {
// prevent duplicate toasts from appearing when multiple UI elements
// are listening for an event that triggers creation of a toast
if (this._hasDuplicate(toast)) {
return null;
}
const newToast = toast;
if (!newToast.id) {
newToast.id = Math.random() * Math.pow(10, 16);
}
this.toasts.push(newToast);
return newToast.id;
}
/**
* Removes a toast with the matching value of toast.id.
*
* @param toast
*/
@action
removeToast(toast) {
this.toasts = this.toasts.filter((item) =>
toast.id !== item.id
);
}
@computed
get count() {
return this.toasts ? this.toasts.length : 0;
}
/**
* Returns true if a toast with the same 'text' and 'action' already exists
* @param newToast
* @returns {boolean}
* @private
*/
_hasDuplicate(newToast) {
for (const toast of this.toasts) {
if (toast.text === newToast.text &&
toast.action === newToast.action) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,55 @@
/**
* Created by cmeyers on 9/21/16.
*/
import { ToastService as toastService } from './index';
import { buildRunDetailsUrlFromQueue } from './UrlBuilder';
import i18nTranslator from './i18n/i18n';
const CAPABILITY_MULTIBRANCH_PIPELINE = 'io.jenkins.blueocean.rest.model.BlueMultiBranchPipeline';
const CAPABILITY_MULTIBRANCH_BRANCH = 'io.jenkins.blueocean.rest.model.BlueBranch';
export default {
/**
*
* @param runnable
* @param runInfo
* @param toastAction
*/
createRunStartedToast: (runnable, runInfo, toastAction) => {
const translate = i18nTranslator('blueocean-web');
const isMultiBranch = runnable._capabilities.some(capability => (
[CAPABILITY_MULTIBRANCH_PIPELINE, CAPABILITY_MULTIBRANCH_BRANCH].indexOf(capability) !== -1
));
const runId = runInfo.expectedBuildNumber;
const runDetailsUrl = buildRunDetailsUrlFromQueue(
runInfo._links.self.href,
isMultiBranch,
runId,
);
const name = decodeURIComponent(runnable.name);
const text = translate('toast.run.started', {
0: name,
1: runId,
defaultValue: 'Started "{0}" #{1}',
});
const caption = translate('toast.run.open', {
defaultValue: 'Open',
});
toastService.newToast({
text,
action: caption,
onActionClick: () => {
if (toastAction) {
toastAction(runDetailsUrl);
}
},
});
return runDetailsUrl;
},
};

View File

@ -0,0 +1,107 @@
/**
* Created by cmeyers on 8/25/16.
*/
const extractRestUrl = (subject) => {
let restUrl = null;
if (typeof subject === 'object') {
if (subject && subject._links && subject._links.self) {
restUrl = subject._links.self.href;
}
} else if (typeof subject === 'string') {
restUrl = subject;
}
if (!restUrl) {
throw new Error('Could not find input URL');
}
return restUrl;
};
/**
* Return a new array with leading and trailing whitespace elements removed.
*
* @param {Array} tokens
* @returns {Array}
*/
const trimEmptyTokens = (tokens) => {
const copy = tokens.slice();
if (copy[0] === '') {
copy.shift();
}
if (copy[copy.length - 1] === '') {
copy.pop();
}
return copy;
};
/**
* Builds the proper URL to view Run Details for the specified run.
* Run is either a run object with "_links.self.href" property, or the URL itself.
*
* @param {object|string} run
* @returns {string}
*/
export const buildRunDetailsUrl = (run) => {
const restUrl = extractRestUrl(run);
const tokens = trimEmptyTokens(restUrl.split('/'));
// given the following URL '/blue/rest/organizations/jenkins/pipelines/folder1/pipelines/folder2/pipelines/folder3/pipelines/jdl-2
// /branches/experiment%252Fbuild-locally-docker/runs/21/
const organizationName = tokens[3];
const isMultiBranch = tokens[tokens.length - 4] === 'branches';
const fullNameStart = 4;
const fullNameEnd = !isMultiBranch ? tokens.length - 2 : tokens.length - 4;
// grab the tokens that make up the full name, then filter out the even values ('/pipelines')
// so the clean folder path is returned, e.g. folder1/folder2/folder3/jdl-2
const fullName = tokens
.slice(fullNameStart, fullNameEnd)
.filter((name, index) => index % 2 === 1)
.join('/');
const pipelineName = tokens[fullNameEnd - 1];
const branchName = isMultiBranch ? tokens[tokens.length - 3] : '';
const runId = tokens[tokens.length - 1];
const detailName = isMultiBranch ? decodeURIComponent(branchName) : pipelineName;
// fail fast
if (!organizationName || !fullName || !detailName || !runId) {
throw new Error('Could not extract URI components');
}
return `/organizations/${organizationName}` +
`/${encodeURIComponent(fullName)}/detail` +
`/${detailName}/${runId}/pipeline`;
};
export const buildRunDetailsUrlFromQueue = (queueItem, isMultiBranch, expectedBuildNumber) => {
const restUrl = extractRestUrl(queueItem);
const tokens = trimEmptyTokens(restUrl.split('/'));
// given the following URL '/blue/rest/organizations/jenkins/pipelines/jenkinsfile-experiments/pipelines/PR-2/queue/31/'
// modify the 'queue' URL so it looks like a 'runs' URL
tokens[tokens.length - 2] = 'runs';
// replace the queue number with the expected runId
tokens[tokens.length - 1] = expectedBuildNumber;
// if multi-branch, change the last value of 'pipelines' to 'branches' so it looks like a multibranch REST URL
if (isMultiBranch) {
tokens[tokens.length - 4] = 'branches';
}
return buildRunDetailsUrl(tokens.join('/'));
};
export default {
buildRunDetailsUrl, buildRunDetailsUrlFromQueue,
};

View File

@ -0,0 +1,34 @@
/**
* Created by cmeyers on 7/8/16.
*/
import { blueocean } from './scopes';
export class User {
constructor(blueUser = {}) {
this._class = blueUser._class;
this._links = blueUser._links;
this.email = blueUser.email;
this.fullName = blueUser.fullName;
this.id = blueUser.id || 'anonymous';
}
isAnonymous() {
return (this.id === 'anonymous');
}
static current() {
return CURRENT; // eslint-disable-line no-use-before-define
}
}
let CURRENT = new User(blueocean.user);
/* eslint-disable */
export const TestUtil = {
setCurrent: function (user) {
CURRENT = new User(user);
},
};
/* eslint-enable */

View File

@ -0,0 +1,35 @@
/**
* Created by cmeyers on 9/8/16.
*/
import { Fetch } from '../fetch';
import config from '../urlconfig';
import utils from '../utils';
export class CapabilityApi {
/**
* Fetch the capabilities for one or more class names.
*
* @param {Array} classNames
* @returns {Promise} with fulfilled {object} keyed by className, with an array of string capability names.
* @private
*/
fetchCapabilities(classNames) {
const noDuplicates = classNames.filter((item, index, self) => self.indexOf(item) === index);
const path = config.getJenkinsRootURL();
const classesUrl = utils.cleanSlashes(`${path}/blue/rest/classes/`);
const fetchOptions = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(
{ q: noDuplicates }
),
};
return Fetch.fetchJSON(classesUrl, { disableCapabilites: true, fetchOptions });
}
}

View File

@ -0,0 +1,162 @@
/**
* Created by cmeyers on 9/8/16.
*/
const addClass = (clazz, classMap) => {
let className;
if (Array.isArray(clazz._class)) {
// If it's an array of class names, just take the first.
// TODO: Hmmm ... not sure if this is the right thing to do when we have an array of class names.
// Not sure what the array is about tbh. What are the relationships?
if (clazz._class.length > 0) {
className = clazz._class[0];
} else {
return;
}
} else {
className = clazz._class;
}
if (!classMap[className]) {
// eslint-disable-next-line no-param-reassign
classMap[className] = [];
}
classMap[className].push(clazz);
};
const canWalk = (item) => item && (typeof item === 'object' || Array.isArray(item));
const DEFAULT_IGNORED_PROPS = ['_links'];
/**
* Decorate an object graph with a '_capabilities' property for each object with a valid '_class'
* Usage:
* import { capabilityAugmenter } from '@jenkins-cd/blueocean-core-js';
* const augmentCapability = capabilityAugmenter.augmentCapability;
*
* fetch(url, fetchOptions)
* .then(data => augmentCapability(data));
*/
export class CapabilityAugmenter {
constructor(capabilityStore) {
this._capabilityStore = capabilityStore;
this._perfLoggingEnabled = false;
this._warnLoggingEnabled = false;
}
/**
* Add "_capabilities" data or all objects with a "_class" property.
*
* @param {object|Array} data
* @returns {Promise}
*/
augmentCapabilities(data) {
const classMap = this._findClassesInTree(data);
return this._resolveCapabilities(data, classMap);
}
enablePerfLogging() {
this._perfLoggingEnabled = true;
}
enableWarningLogging() {
this._warnLoggingEnabled = true;
}
/**
* Find all of the distinct "_class" values in supplied object.
*
* @param {object|Array} data
* @returns {object} key= "_class" name, value= array of all objects of that class.
* @private
*/
_findClassesInTree(data) {
const classMap = {};
const nodesToWalk = [data];
const nodesAlreadyWalked = [];
const ignoredProps = DEFAULT_IGNORED_PROPS.slice();
const started = new Date().getTime();
let node = nodesToWalk.shift();
while (node) {
nodesAlreadyWalked.push(node);
// save a ref to the class so we can attach capabilities later
if (typeof node === 'object' && node._class) {
addClass(node, classMap);
}
const nodeKeys = Object.keys(node);
for (const key of nodeKeys) {
const value = node[key];
// walk this node at a later iteration as long as
// - we didn't already walk it (cycles cause an infinite loop otherwise)
// - the property name isn't on the blacklist
if (canWalk(value) && nodesAlreadyWalked.indexOf(value) === -1 && ignoredProps.indexOf(key) === -1) {
nodesToWalk.push(value);
}
}
node = nodesToWalk.shift();
}
if (this._perfLoggingEnabled) {
console.debug(`augmenter.parse: ${new Date().getTime() - started}ms`);
}
return classMap;
}
_resolveCapabilities(data, classMap) {
const classNames = Object.keys(classMap);
return this._capabilityStore.resolveCapabilities(...classNames)
.then(capabilitiesMap => this._injectCapabilities(classMap, capabilitiesMap))
.then(() => data);
}
/**
* Add the capabilities to the "_capabilities" property of all objects in the class map.
*
* @param classMap
* @param capabilitiesMap
* @returns {object} classMap
* @private
*/
_injectCapabilities(classMap, capabilitiesMap) {
const started = new Date().getTime();
const unresolved = [];
for (const className of Object.keys(classMap)) {
for (const target of classMap[className]) {
const capabilities = capabilitiesMap[className];
if (!capabilities && unresolved.indexOf(className) === -1) {
unresolved.push(className);
}
target._capabilities = capabilities || [];
}
}
if (this._perfLoggingEnabled) {
console.debug(`augmenter.inject: ${new Date().getTime() - started}ms`);
}
if (this._warnLoggingEnabled) {
for (const className of unresolved) {
console.warn(`could not resolve capabilities for ${className}; an error may have occurred during lookup`);
}
}
return classMap;
}
}

View File

@ -0,0 +1,100 @@
/**
* Created by cmeyers on 8/31/16.
*/
import es6Promise from 'es6-promise'; es6Promise.polyfill();
import { installInfo } from '../storage';
// Create a dedicated storage namespace that we use to store classes
// info in the browser, eliminating client REST call overhead for classes
// info. This storage namespace will be auto-cleared if the jesnkins version
// changes, or if the active plugins change.
const classesInfoNS = installInfo.subspace('classesInfo');
/**
* Retrieves capability metadata for class names.
* Uses an internal cache to minimize REST API calls.
*/
export class CapabilityStore {
constructor(capabilityApi) {
this._localStore = {};
this._capabilityApi = capabilityApi;
}
/**
* Fetch the capabilities for one or more class names.
* Will used cached values if available.
*
* @param classNames
* @returns {Promise} with fulfilled {object} keyed by className, with an array of string capability names.
*/
resolveCapabilities(...classNames) {
const result = {};
const classesToFetch = [];
// determine which class names are already in the cache and which aren't
for (const className of classNames) {
const classInfo = this._getStoredClassInfo(className);
if (classInfo) {
result[className] = classInfo;
} else {
classesToFetch.push(className);
}
}
// if nothing to fetch, just return an immediately fulfilled Promise
if (classesToFetch.length === 0) {
return new Promise(resolve => resolve(result));
}
// fetch the capabilities and then merge that with the values already in the cache
return this._fetchCapabilities(classesToFetch)
.then(fetchedCapabilities => Object.assign(result, fetchedCapabilities));
}
/**
* Fetch the capabilities for one or more class names.
*
* @param classNames
* @returns {Promise} with fulfilled {object} keyed by className, with an array of string capability names.
* @private
*/
_fetchCapabilities(classNames) {
return this._capabilityApi.fetchCapabilities(classNames)
.then(data => this._storeCapabilities(data.map));
}
/**
* Store the values in the cache and return it.
*
* @param map
* @returns {object} keyed by className, with an array of string capability names.
* @private
*/
_storeCapabilities(map) {
const storedCapabilities = {};
Object.keys(map).forEach(className => {
const capabilities = map[className];
this._localStore[className] = storedCapabilities[className] = capabilities.classes.slice();
// Also store in the browser so we don't have to look
// up this info again (unless the storage namespace is
// cleared due to jenkins or plugin changes).
classesInfoNS.set(className, this._localStore[className]);
});
return storedCapabilities;
}
_getStoredClassInfo(className) {
if (!this._localStore[className]) {
// If we don't have a copy of the class info in the localStore,
// check the browser storage and copy it into the localStore.
// We still want to use the localStore because it holds deserialized
// copies of the class info, which means that a localStore lookup
// would be lower overhead and probably faster.
this._localStore[className] = classesInfoNS.get(className);
}
return this._localStore[className];
}
}

View File

@ -0,0 +1,57 @@
/**
* Created by cmeyers on 9/9/16.
*/
/**
* Determines whether the supplied object has at least one of the supplied capabilities.
*
* As capabilities are typically name-spaced, this method will match on long or short names, e.g.
* given: _capabilities = ['a.b.Capability1']
* passing either 'a.b.Capability1' or 'Capability1' will match
*
* @param {object} subject
* @param {...string} capabilityNames
* @returns {boolean}
*/
export const capable = (subject, ...capabilityNames) => {
if (subject && subject._capabilities) {
// in case an array was passed in, flatten it out
const flattenedCapabilities = [].concat(...capabilityNames);
// find the intersection of subject's caps with the passed-in caps
const longNameMatches = flattenedCapabilities.filter(longName => subject._capabilities.indexOf(longName) !== -1);
if (longNameMatches.length > 0) {
return true;
}
// build short form of subject's caps, then find intersection
const shortNames = subject._capabilities.map(longName => longName.split('.').slice(-1).join(''));
const shortNameMatches = flattenedCapabilities.filter(longName => shortNames.indexOf(longName) !== -1);
if (shortNameMatches.length > 0) {
return true;
}
}
return false;
};
/**
* Provides some convenience methods for host object of _class/_capabilities
*/
export class Capable {
/**
* Determines whether the host object has the supplied capability.
* As capabilities are typically name-spaced, this method will match on long or short names, e.g.
* if _capabilities = ['a.b.Capability1']
* passing either 'a.b.Capability1' or 'Capability1' will match
*
* @param {string} capabilityName
* @returns {boolean}
*/
can(capabilityName) {
return capable(this, capabilityName);
}
}
export default new Capable();

View File

@ -0,0 +1,15 @@
/**
* Created by cmeyers on 9/8/16.
*/
import { CapabilityApi } from './CapabilityApi';
import { CapabilityStore } from './CapabilityStore';
import { CapabilityAugmenter } from './CapabilityAugmenter';
const api = new CapabilityApi();
const store = new CapabilityStore(api);
const augmenter = new CapabilityAugmenter(store);
// export as named singletons
export { store as capabilityStore };
export { augmenter as capabilityAugmenter };
export { capable } from './Capable';

View File

@ -0,0 +1,106 @@
/**
* Created by cmeyers on 8/30/16.
*/
import React, { Component, PropTypes } from 'react';
import { Icon } from '@jenkins-cd/react-material-icons';
import { capable, RunApi as runApi, ToastUtils } from '../index';
import Security from '../security';
const { permit } = Security;
const stopProp = (event) => {
event.stopPropagation();
};
const CAPABILITY_MULTIBRANCH_PIPELINE = 'io.jenkins.blueocean.rest.model.BlueMultiBranchPipeline';
const CAPABILITY_MULTIBRANCH_BRANCH = 'io.jenkins.blueocean.rest.model.BlueBranch';
const CAPABILITY_SIMPLE_PIPELINE = 'org.jenkinsci.plugins.workflow.job.WorkflowJob';
const PIPELINE_CAPABILITIES = [CAPABILITY_SIMPLE_PIPELINE, CAPABILITY_MULTIBRANCH_PIPELINE, CAPABILITY_MULTIBRANCH_BRANCH];
function isRunFailed(run) {
const failureResults = ['FAILURE', 'ABORTED'];
return !!(run && run.result && failureResults.indexOf(run.result.toUpperCase()) !== -1);
}
/**
* ReplayButton allows a pipeline or branch to be re-run when in a failure state.
*/
export class ReplayButton extends Component {
constructor(props) {
super(props);
this.state = {
replaying: false,
};
}
componentWillReceiveProps(nextProps) {
const statusChanged = isRunFailed(this.props.latestRun) !== isRunFailed(nextProps.latestRun);
if (statusChanged) {
this.setState({
replaying: false,
});
}
}
_onReplayClick() {
if (this.state.replaying) {
return;
}
this.setState({
replaying: true,
});
runApi.replayRun(this.props.latestRun)
.then(runInfo => ToastUtils.createRunStartedToast(this.props.runnable, runInfo, this.props.onNavigation))
.then(runDetailsUrl => this._afterReplayStarted(runDetailsUrl));
}
_afterReplayStarted(runDetailsUrl) {
if (this.props.autoNavigate && this.props.onNavigation) {
this.props.onNavigation(runDetailsUrl);
}
}
render() {
if (!this.props.runnable || !this.props.latestRun) {
return null;
}
const outerClass = this.props.className ? this.props.className : '';
const outerClassNames = outerClass.split(' ');
const innerButtonClass = outerClassNames.indexOf('icon-button') === -1 ? 'btn inverse' : '';
const isFailed = isRunFailed(this.props.latestRun);
const isPipeline = capable(this.props.runnable, PIPELINE_CAPABILITIES);
const hasPermission = permit(this.props.runnable).start();
const replayLabel = 'Re-run';
if (!isFailed || !isPipeline || !hasPermission) {
return null;
}
return (
<div className={`replay-button-component ${outerClass}`} onClick={(event => stopProp(event))}>
<a className={`replay-button ${innerButtonClass}`} title={replayLabel} onClick={() => this._onReplayClick()}>
<Icon size={24} icon="replay" />
<span className="button-label">{replayLabel}</span>
</a>
</div>
);
}
}
ReplayButton.propTypes = {
className: PropTypes.string,
runnable: PropTypes.object,
latestRun: PropTypes.object,
autoNavigate: PropTypes.bool,
onNavigation: PropTypes.func,
};

View File

@ -0,0 +1,156 @@
/**
* Created by cmeyers on 8/26/16.
*/
import React, { Component, PropTypes } from 'react';
import { Icon } from '@jenkins-cd/react-material-icons';
import {
RunApi as runApi,
ToastService as toastService,
ToastUtils,
} from '../';
import Security from '../security';
import i18nTranslator from '../i18n/i18n';
const translate = i18nTranslator('blueocean-web');
const { permit } = Security;
const stopProp = (event) => {
event.stopPropagation();
};
/**
* Run Buttons allows a pipeline or branch to be run and also be stopped thereafter.
*/
export class RunButton extends Component {
constructor(props) {
super(props);
this.state = {
running: false,
stopping: false,
};
}
componentWillReceiveProps(nextProps) {
this._updateState(nextProps);
}
_updateState(nextProps) {
const oldStatus = this.props.latestRun && this.props.latestRun.state || '';
const newStatus = nextProps.latestRun && nextProps.latestRun.state || '';
// if the state of the run changed, then assume it's no longer trying to stop
if (oldStatus !== newStatus) {
this.setState({
stopping: false,
});
}
}
_onRunClick() {
runApi.startRun(this.props.runnable)
.then((runInfo) => ToastUtils.createRunStartedToast(this.props.runnable, runInfo, this.props.onNavigation));
}
_onStopClick() {
if (this.state.stopping) {
return;
}
this.setState({
stopping: true,
});
if (this.props.latestRun.state === 'QUEUED') {
runApi.removeFromQueue(this.props.latestRun);
} else {
runApi.stopRun(this.props.latestRun);
}
const name = decodeURIComponent(this.props.runnable.name);
const runId = this.props.latestRun.id;
const text = translate('toast.run.stopping', {
0: name,
1: runId,
defaultValue: 'Stoppping "{0}" #{1}',
});
toastService.newToast({ text });
}
render() {
const outerClass = this.props.className ? this.props.className : '';
const outerClassNames = outerClass.split(' ');
const innerButtonClass = outerClassNames.indexOf('icon-button') === -1 ? this.props.innerButtonClasses : '';
const stopClass = this.state.stopping ? 'stopping' : '';
const status = this.props.latestRun ? this.props.latestRun.state : '';
const isPaused = status.toLowerCase() === 'paused';
const runningStatus = status && (isPaused || status.toLowerCase() === 'running' || status.toLowerCase() === 'queued');
let showRunButton = this.props.buttonType === 'run-only' || (this.props.buttonType === 'toggle' && !runningStatus);
let showStopButton = runningStatus && (this.props.buttonType === 'toggle' || this.props.buttonType === 'stop-only');
showRunButton = showRunButton && permit(this.props.runnable).start();
showStopButton = showStopButton && permit(this.props.runnable).stop();
const runLabel = this.props.runText || translate('toast.run', {
defaultValue: 'Run',
});
let stopLabel = this.state.stopping ? translate('toast.stopping', {
defaultValue: 'Stopping ...',
}) : translate('toast.stop', {
defaultValue: 'Stop',
});
if (isPaused && !this.state.stopping) {
stopLabel = translate('toast.abort', { defaultValue: 'Abort' });
}
if (!showRunButton && !showStopButton) {
return null;
}
return (
<div className={`run-button-component ${outerClass}`} onClick={(event => stopProp(event))}>
{ showRunButton &&
<a className={`run-button ${innerButtonClass}`} title={runLabel} onClick={() => this._onRunClick()}>
<Icon size={24} icon="play_circle_outline" />
<span className="button-label">{runLabel}</span>
</a>
}
{ showStopButton &&
<a className={`stop-button ${innerButtonClass} ${stopClass}`} title={stopLabel} onClick={() => this._onStopClick()}>
{ /* eslint-disable max-len */ }
<svg className="svg-icon" width="20" height="20" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<g fill="none" fill-rule="evenodd">
<path d="M-2-2h24v24H-2z" />
<path className="svg-icon-inner" d="M10 0C4.48 0 0 4.48 0 10s4.48 10 10 10 10-4.48 10-10S15.52 0 10 0zM7 7h6v6H7V7zm3 11c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z" fill="#4A90E2" />
</g>
</svg>
{ /* eslint-enable max-len */ }
<span className="button-label">{stopLabel}</span>
</a>
}
</div>
);
}
}
RunButton.propTypes = {
buttonType: PropTypes.oneOf('toggle', 'stop-only', 'run-only'),
className: PropTypes.string,
runnable: PropTypes.object,
latestRun: PropTypes.object,
onNavigation: PropTypes.func,
runText: PropTypes.string,
innerButtonClasses: PropTypes.string,
};
RunButton.defaultProps = {
buttonType: 'toggle',
innerButtonClasses: 'btn inverse',
};

View File

@ -0,0 +1,34 @@
/**
* This config object comes from blueocean-config.
*/
import { blueocean } from './scopes';
const config = blueocean.config || {};
export default {
getJenkinsConfig() {
return config.jenkinsConfig || {};
},
getSecurityConfig() {
return this.getJenkinsConfig().security || {};
},
isJWTEnabled() {
return !!this.getSecurityConfig().enableJWT;
},
getLoginUrl() {
return this.getSecurityConfig().loginUrl;
},
/**
* Set a new "jenkinsConfig" object.
* Useful for testing in a headless environment.
* @param newConfig
* @private
*/
_setJenkinsConfig(newConfig) {
config.jenkinsConfig = newConfig;
},
};

View File

@ -0,0 +1,308 @@
import es6Promise from 'es6-promise'; es6Promise.polyfill();
import jwt from './jwt';
import isoFetch from 'isomorphic-fetch';
import utils from './utils';
import config from './config';
import dedupe from './utils/dedupe-calls';
import urlconfig from './urlconfig';
import { prefetchdata } from './scopes';
const Promise = es6Promise.Promise;
import { capabilityAugmenter } from './capability/index';
let refreshToken = null;
export const FetchFunctions = {
checkRefreshHeader(response) {
const _refreshToken = response.headers.get('X-Blueocean-Refresher');
// No token in response, lets just ignore.
if (!_refreshToken) {
return response;
}
// First time we have seen a refresh token, early exit.
if (!refreshToken) {
refreshToken = _refreshToken;
return response;
}
// We need to refresh the page now!
if (refreshToken !== _refreshToken) {
utils.refreshPage();
throw new Error('refreshing apge');
}
return response;
},
/**
* This method checks for for 2XX http codes. Throws error it it is not.
* This should only be used if not using fetch or fetchJson.
*/
checkStatus(response) {
if (response.status >= 300 || response.status < 200) {
const error = new Error(response.statusText);
error.response = response;
throw error;
}
return response;
},
/**
* Adds same-origin option to the fetch.
*/
sameOriginFetchOption(options = {}) {
const newOpts = utils.clone(options);
newOpts.credentials = newOpts.credentials || 'same-origin';
return newOpts;
},
/**
* Enhances the fetchOptions with the JWT bearer token. Will only be needed
* if not using fetch or fetchJson.
*/
jwtFetchOption(token, options = {}) {
const newOpts = utils.clone(options);
newOpts.headers = newOpts.headers || {};
newOpts.headers.Authorization = newOpts.headers.Authorization || `Bearer ${token}`;
return newOpts;
},
/**
* REturns the json body from the response. It is only needed if
* you are using FetchUtils.fetch
*
* Usage:
* FetchUtils.fetch(..).then(FetchUtils.parseJSON)
*/
parseJSON(response) {
return response.json()
// FIXME: workaround for status=200 w/ empty response body that causes error in Chrome
// server should probably return HTTP 204 instead
.catch((error) => {
if (error.message === 'Unexpected end of JSON input') {
return {};
}
throw error;
});
},
/* eslint-disable no-param-reassign */
/**
* Parses the response body for the error generated in checkStatus.
*/
parseErrorJson(error) {
return error.response.json().then(
body => {
error.responseBody = body;
throw error;
},
() => {
error.responseBody = null;
throw error;
});
},
/* eslint-enable no-param-reassign */
/**
* Error function helper to log errors to console.
*
* Usage;
* fetchJson(..).catch(FetchUtils.consoleError)
*/
consoleError(error) {
console.error(error); // eslint-disable-line no-console
},
/**
* Error function helper to call a callback on a rejected promise.
* if callback is null, log to console). Use .catch() if you know it
* will not be null though.
*
* Usage;
* fetchJson(..).catch(FetchUtils.onError(error => //do something)
*/
onError(errorFunc) {
return error => {
if (errorFunc) {
errorFunc(error);
} else {
FetchFunctions.consoleError(error);
}
};
},
/**
* Raw fetch that returns the json body.
*
* This method is semi-private, under normal conditions it should not be
* used as it does not include the JWT bearer token
*
* @param {string} url - The URL to fetch from.
* @param {Object} [options]
* @param {function} [options.onSuccess] - Optional callback success function.
* @param {function} [options.onError] - Optional error callback.
* @param {Object} [options.fetchOptions] - Optional isomorphic-fetch options.
* @returns JSON body
*/
rawFetchJSON(url, { onSuccess, onError, fetchOptions, disableDedupe } = {}) {
const request = () => {
let future = getPrefetchedDataFuture(url); // eslint-disable-line no-use-before-define
if (!future) {
future = isoFetch(url, FetchFunctions.sameOriginFetchOption(fetchOptions))
.then(FetchFunctions.checkRefreshHeader)
.then(FetchFunctions.checkStatus)
.then(FetchFunctions.parseJSON, FetchFunctions.parseErrorJson);
}
if (onSuccess) {
return future.then(onSuccess).catch(FetchFunctions.onError(onError));
}
return future;
};
if (disableDedupe) {
return request();
}
return dedupe(url, request);
},
/**
* Raw fetch.
*
* This method is semi-private, under normal conditions it should not be
* used as it does not include the JWT bearer token
*
* @param {string} url - The URL to fetch from.
* @param {Object} [options]
* @param {function} [options.onSuccess] - Optional callback success function.
* @param {function} [options.onError] - Optional error callback.
* @param {Object} [options.fetchOptions] - Optional isomorphic-fetch options.
* @returns fetch response
*/
rawFetch(url, { onSuccess, onError, fetchOptions, disableDedupe } = {}) {
const request = () => {
let future = getPrefetchedDataFuture(url); // eslint-disable-line no-use-before-define
if (!future) {
future = isoFetch(url, FetchFunctions.sameOriginFetchOption(fetchOptions))
.then(FetchFunctions.checkRefreshHeader)
.then(FetchFunctions.checkStatus);
}
if (onSuccess) {
return future.then(onSuccess).catch(FetchFunctions.onError(onError));
}
return future;
};
if (disableDedupe) {
return request();
}
return dedupe(url, request);
},
};
export const Fetch = {
/**
* Fetch JSON data.
* <p>
* Utility function that can be mocked for testing.
*
* @param {string} url - The URL to fetch from.
* @param {Object} [options]
* @param {function} [options.onSuccess] - Optional callback success function.
* @param {function} [options.onError] - Optional error callback.
* @param {Object} [options.fetchOptions] - Optional isomorphic-fetch options.
* @returns JSON body.
*/
fetchJSON(url, { onSuccess, onError, fetchOptions, disableCapabilites } = {}) {
let fixedUrl = url;
if (urlconfig.getJenkinsRootURL() !== '' && !url.startsWith(urlconfig.getJenkinsRootURL())) {
fixedUrl = `${urlconfig.getJenkinsRootURL()}${url}`;
}
let future;
if (!config.isJWTEnabled()) {
future = FetchFunctions.rawFetchJSON(fixedUrl, { onSuccess, onError, fetchOptions });
} else {
future = jwt.getToken()
.then(token => FetchFunctions.rawFetchJSON(fixedUrl, {
onSuccess,
onError,
fetchOptions: FetchFunctions.jwtFetchOption(token, fetchOptions),
}));
}
if (!disableCapabilites) {
return future.then(data => capabilityAugmenter.augmentCapabilities(utils.clone(data)));
}
return future;
},
/**
* Fetch data.
* <p>
* Utility function that can be mocked for testing.
*
* @param {string} url - The URL to fetch from.
* @param {Object} [options]
* @param {function} [options.onSuccess] - Optional callback success function.
* @param {function} [options.onError] - Optional error callback.
* @param {Object} [options.fetchOptions] - Optional isomorphic-fetch options.
* @returns fetch body.
*/
fetch(url, { onSuccess, onError, fetchOptions } = {}) {
let fixedUrl = url;
if (urlconfig.getJenkinsRootURL() !== '' && !url.startsWith(urlconfig.getJenkinsRootURL())) {
fixedUrl = `${urlconfig.getJenkinsRootURL()}${url}`;
}
if (!config.isJWTEnabled()) {
return FetchFunctions.rawFetch(fixedUrl, { onSuccess, onError, fetchOptions });
}
return jwt.getToken()
.then(token => FetchFunctions.rawFetch(fixedUrl, {
onSuccess,
onError,
fetchOptions: FetchFunctions.jwtFetchOption(token, fetchOptions),
}));
},
};
function trimRestUrl(url) {
const REST_PREFIX = 'blue/rest/organizations';
const prefixOffset = url.indexOf(REST_PREFIX);
if (prefixOffset !== -1) {
return url.substring(prefixOffset);
}
return url;
}
function getPrefetchedDataFuture(url) {
const trimmedUrl = trimRestUrl(url);
for (const prop in prefetchdata) {
if (prefetchdata.hasOwnProperty(prop)) {
const preFetchEntry = prefetchdata[prop];
if (preFetchEntry.restUrl && preFetchEntry.data) {
// If the trimmed/normalized rest URL matches the url arg supplied
// to the function, construct a pre-resolved future object containing
// the prefetched data as the value.
if (trimRestUrl(preFetchEntry.restUrl) === trimmedUrl) {
try {
return Promise.resolve(JSON.parse(preFetchEntry.data));
} finally {
// Delete the preFetchEntry i.e. we only use these entries once. So, this
// works only for the first request for the data at that URL. Subsequent
// calls on that REST endpoint will result in a proper fetch. A local
// store needs to be used (redux/mobx etc) if you want to avoid multiple calls
// for the same data. This is not a caching layer/mechanism !!!
delete prefetchdata[prop];
}
}
}
}
}
return undefined;
}

View File

@ -0,0 +1,158 @@
import i18next from 'i18next';
import LngDetector from 'i18next-browser-languagedetector';
import XHR from 'i18next-xhr-backend';
import { store } from '@jenkins-cd/js-extensions';
import urlConfig from '../urlconfig';
/**
* Init language detector, we are going to use first queryString and then the navigator prefered language
*/
export const defaultLngDetector = new LngDetector(null, {
// order and from where user language should be detected
order: ['querystring', 'navigator'],
// keys or params to lookup language from
lookupQuerystring: 'language',
});
const prefix = urlConfig.getJenkinsRootURL() || '';
const FALLBACK_LANG = '';
function newPluginXHR(pluginName) {
let pluginVersion = store.getPluginVersion(pluginName);
if (!pluginVersion) {
throw new Error(`Unable to create an i18n instance for plugin "${pluginName}". This plugin is not currently installed, or is disabled.`);
}
pluginVersion = encodeURIComponent(pluginVersion);
const loadPath = `${prefix}/blue/rest/i18n/${pluginName}/${pluginVersion}/{ns}/{lng}`;
return new XHR(null, {
loadPath,
allowMultiLoading: false,
parse: (data) => {
// we need to parse the response and then extract the data since the rest is garbage for us
const response = JSON.parse(data);
return response.data;
},
});
}
/**
* Create a instance of i18next and init it
* in case we are in test mode and run unit test, we deliver a i18next instance that are not using any backend nor language detection
* @param backend {object} - the backend XHR invoker we want to use
* @param lngDetector {object} - the component that detects which language we want to display
* @param options {object} - general options for i18next
* @see defaultOptions
*/
const i18nextInstance = (backend, lngDetector = defaultLngDetector, options) => {
if (!backend) {
throw new Error('Invalid call to create a new i18next instance. No backend XHR invoker supplied.');
}
if (!options) {
throw new Error('Invalid call to create a new i18next instance. No i18next options supplied.');
}
return i18next.createInstance()
.use(backend)
.use(lngDetector)
.init(options);
};
const translatorCache = {};
let useMockFallback = false;
const assertPluginNameDefined = (pluginName) => {
if (!pluginName) {
throw new Error('"pluginName" arg cannot be null/blank');
}
};
const toDefaultNamespace = (pluginName) => {
assertPluginNameDefined(pluginName);
// Replace all hyphen chars with a dot.
return `jenkins.plugins.${pluginName.replace(/-/g, '.')}.Messages`;
};
/**
* Create an i18next instance for accessing i18n resource bundles
* in the named plugin namespace.
* @param pluginName The name of the plugin.
* @param namespace The resource bundle namespace. Optional, defaulting to
* the plugin's default resource bundle e.g. "jenkins.plugins.blueocean.web.Messages"
* for the "blueocean-web" plugin and "jenkins.plugins.blueocean.dashboard.Messages"
* for the "blueocean-dashboard" plugin.
* @return An i18n instance.
*/
const pluginI18next = (pluginName, namespace = toDefaultNamespace(pluginName)) => {
assertPluginNameDefined(pluginName);
const initOptions = {
ns: [namespace],
defaultNS: namespace,
keySeparator: false, // we do not have any nested keys in properties files
debug: false,
fallbackLng: FALLBACK_LANG,
load: 'currentOnly',
interpolation: {
prefix: '{',
suffix: '}',
escapeValue: false, // not needed for react!!
},
};
return i18nextInstance(newPluginXHR(pluginName), defaultLngDetector, initOptions);
};
function buildCacheKey(pluginName, namespace = toDefaultNamespace(pluginName)) {
return `${pluginName}:${namespace}`;
}
/**
* Create an i18n Translator instance for accessing i18n resource bundles
* in the named plugin namespace.
* @param pluginName The name of the plugin.
* @param namespace The resource bundle namespace. Optional, defaulting to
* the plugin's default resource bundle e.g. "jenkins.plugins.blueocean.web.Messages"
* for the "blueocean-web" plugin and "jenkins.plugins.blueocean.dashboard.Messages"
* for the "blueocean-dashboard" plugin.
* @return An i18n Translator instance.
*/
export default function i18nTranslator(pluginName, namespace) {
assertPluginNameDefined(pluginName);
const translatorCacheKey = buildCacheKey(pluginName, namespace);
let translator = translatorCache[translatorCacheKey];
if (translator) {
return translator;
}
if (useMockFallback) {
return function mockTranslate(key) {
return key;
};
}
const I18n = pluginI18next(pluginName, namespace);
// Create and cache the translator instance.
let detectedLang;
try {
detectedLang = defaultLngDetector.detect();
} catch (e) {
detectedLang = FALLBACK_LANG;
}
translator = I18n.getFixedT(detectedLang, namespace);
translatorCache[translatorCacheKey] = translator;
return translator;
}
export function enableMocks() {
useMockFallback = true;
}
export function disableMocks() {
useMockFallback = false;
}

View File

@ -0,0 +1,65 @@
/**
* Created by cmeyers on 8/18/16.
*/
import { Fetch } from './fetch';
import * as sse from '@jenkins-cd/sse-gateway';
import { RunApi } from './rest/RunApi';
import { SseBus } from './sse/SseBus';
import { ToastService } from './ToastService';
// export i18n provider
export i18nTranslator, { defaultLngDetector } from './i18n/i18n';
export { Fetch, FetchFunctions } from './fetch';
export UrlBuilder from './UrlBuilder';
export UrlConfig from './urlconfig';
export JWT from './jwt';
export TestUtils from './testutils';
export ToastUtils from './ToastUtils';
export Utils from './utils';
export { User } from './User';
export AppConfig from './config';
export Security from './security';
export Paths from './paths/index';
import { Pager, PagerService, PipelineService, SSEService, ActivityService, DefaultSSEHandler, LocationService } from './services/index';
export { Pager, PagerService, PipelineService, SSEService, ActivityService };
export Fullscreen from './Fullscreen';
export NotFound from './NotFound';
export { ReplayButton } from './components/ReplayButton';
export { RunButton } from './components/RunButton';
// Create and export the SSE connection that will be shared by other
// Blue Ocean components via this package.
export const sseConnection = sse.connect('jenkins-blueocean-core-js');
// export services as a singleton so all plugins will use the same instance
// capabilities
export { capable, capabilityStore, capabilityAugmenter } from './capability/index';
// limit to single instance so that duplicate REST calls aren't made as events come in
const sseBus = new SseBus(sseConnection, Fetch.fetchJSON);
export { sseBus as SseBus };
// required so new toasts are routed to the instance used in blueocean-web
const toastService = new ToastService();
export { toastService as ToastService };
const runApi = new RunApi();
export { runApi as RunApi };
export const pagerService = new PagerService();
export const sseService = new SSEService(sseConnection);
export const activityService = new ActivityService(pagerService);
export const pipelineService = new PipelineService(pagerService, activityService);
export const locationService = new LocationService();
const defaultSSEhandler = new DefaultSSEHandler(pipelineService, activityService, pagerService);
sseService.registerHandler(defaultSSEhandler.handleEvents);

View File

@ -0,0 +1,101 @@
import es6Promise from 'es6-promise'; es6Promise.polyfill();
import fetch from 'isomorphic-fetch';
import jwt from 'jsonwebtoken';
import { BlueUrl as UrlUtils } from './urlconfig';
import { FetchFunctions } from './fetch';
import { jwk2pem } from 'pem-jwk';
let storedToken = null;
let publicKeyStore = null;
let tokenFetchPromise = null;
const CLOCK_SKEW_SECONDS = 60;
export default {
/**
* Fetches the JWT token. This token is cached for a default of 25mins.
* If it is within 5mins or expiry it will fetch a new one.
*/
fetchJWT() {
if (storedToken && storedToken.exp) {
const diff = storedToken.exp - Math.trunc(new Date().getTime() / 1000);
// refetch token if we are within 60s of it exp
if (diff < CLOCK_SKEW_SECONDS) {
tokenFetchPromise = null;
}
}
if (!tokenFetchPromise) {
tokenFetchPromise = fetch(`${UrlUtils.getJenkinsRootURL()}/jwt-auth/token`, { credentials: 'same-origin' })
.then(this.checkStatus)
.then(response => {
const token = response.headers.get('X-BLUEOCEAN-JWT');
if (token) {
return token;
}
throw new Error('Could not fetch jwt_token');
});
}
return tokenFetchPromise;
},
/**
* Verifies the token using the public key.
*/
verifyToken(token, certObject) {
return new Promise((resolve, reject) =>
jwt.verify(token, jwk2pem(certObject), { algorithms: [certObject.alg], clockTolerance: CLOCK_SKEW_SECONDS }, (err, payload) => {
if (err) {
reject(err);
} else {
resolve(payload);
}
}));
},
/**
* Fetches the public key that is used to verify tokens.
*/
fetchJWTPublicKey(token) {
const decoded = jwt.decode(token, { complete: true });
const url = `${UrlUtils.getJenkinsRootURL()}/jwt-auth/jwks/${decoded.header.kid}/`;
if (!publicKeyStore) {
publicKeyStore = fetch(url, { credentials: 'same-origin' })
.then(FetchFunctions.checkStatus)
.then(FetchFunctions.parseJSON)
.then(cert => this.verifyToken(token, cert)
.then(payload => ({
token,
payload,
})));
}
return publicKeyStore;
},
/**
* Puts the token into global storage for later use.
*/
storeToken(data) {
storedToken = data.payload;
return data;
},
/**
* Use this function if you want the payload from the token.
*/
getTokenWithPayload() {
return this.fetchJWT()
.then(FetchFunctions.checkStatus)
.then(token => this.fetchJWTPublicKey(token))
.then(data => this.storeToken(data));
},
/**
* Gets the token from te server and verifies it.
*/
getToken() {
return this.getTokenWithPayload().then(token => token.token);
},
};

View File

@ -0,0 +1,37 @@
import { observable, action, asMap } from 'mobx';
export class DataBunker {
@observable _data = asMap();
constructor(keyFn, mapperFn) {
this._keyFn = keyFn;
this._mapperFn = mapperFn;
if (!mapperFn) {
// identity function.
this._mapperFn = x => x;
}
}
@action
setItem(item) {
const keyItem = this._keyFn(item);
const mappedItem = this._mapperFn(item);
this._data.set(keyItem, mappedItem);
return mappedItem;
}
setItems(items) {
return items.map(item => this.setItem(item));
}
getItem(key) {
return this._data.get(key);
}
@action
removeItem(key) {
console.log('before', this._data);
console.log('successful', this._data.delete(key));
console.log('after', this._data);
}
}

View File

@ -0,0 +1,38 @@
// @flow
export type LinkObject = {
[ id: string ] : { href: string}
}
export type ActivityModel = {
organization: string,
pipeline: string,
_links: LinkObject,
changeSet: Object,
durationInMillis: number,
estimatedDurationInMillis: number,
id: string,
result: string,
state: string,
startTime: string,
endTime: string;
commitId: string
}
export type PipelineModel = {
name: string,
fullName: string,
organization: string,
numberOfSuccessfulBranches: number,
numberOfFailingBranches: number,
numberOfSuccessfulPullRequests: number,
numberOfFailingPullRequests: number,
displayName: string,
weatherScore: string,
_links: LinkObject,
_class: string,
latestRun: ActivityModel
}
export type BranchModel = PipelineModel & {
pullRequest: Object
}

View File

View File

@ -0,0 +1,4 @@
import rest from './rest';
export default {
rest,
};

View File

@ -0,0 +1,47 @@
/**
* This object defines rest paths
*/
export default {
_convertSlashes(pipeline) {
return pipeline.replace(/\//g, '/pipelines/');
},
apiRoot() {
return '/blue/rest';
},
organizationPipelines(organizationName) {
return `${this.apiRoot()}/search/?q=type:pipeline;organization:${encodeURIComponent(organizationName)};excludedFromFlattening:jenkins.branch.MultiBranchProject,hudson.matrix.MatrixProject&filter=no-folders`;
},
allPipelines() {
return `${this.apiRoot()}/search/?q=type:pipeline;excludedFromFlattening:jenkins.branch.MultiBranchProject,hudson.matrix.MatrixProject&filter=no-folders`;
},
activities(organization, pipeline) {
return `${this.apiRoot()}/organizations/${encodeURIComponent(organization)}/pipelines/${pipeline}/activities/`;
},
run({ organization, pipeline, branch, runId }) {
if (branch) {
return `${this.pipeline(organization, pipeline)}branches/${encodeURIComponent(encodeURIComponent(branch))}/runs/${runId}/`;
}
return `${this.pipeline(organization, pipeline)}runs/${runId}/`;
},
pipeline(organization, pipeline) {
return `${this.apiRoot()}/organizations/${encodeURIComponent(organization)}/pipelines/${this._convertSlashes(pipeline)}/`;
},
branches(organization, pipeline) {
return `${this.apiRoot()}/organizations/${encodeURIComponent(organization)}/pipelines/${pipeline}/branches/?filter=origin`;
},
pullRequests(organization, pipeline) {
return `${this.apiRoot()}/organizations/${encodeURIComponent(organization)}/pipelines/${pipeline}/branches/?filter=pull-requests`;
},
queuedItem(organization, pipeline, queueId) {
return `${this.pipeline(organization, pipeline)}queue/${queueId}/`;
},
};

View File

@ -0,0 +1,78 @@
/**
* Created by cmeyers on 8/29/16.
*/
import { Fetch } from '../fetch';
import config from '../urlconfig';
import utils from '../utils';
export class RunApi {
startRun(item) {
const path = config.getJenkinsRootURL();
const runUrl = utils.cleanSlashes(`${path}/${item._links.self.href}/runs/`);
const fetchOptions = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
};
return Fetch.fetchJSON(runUrl, { fetchOptions });
}
stopRun(run) {
const path = config.getJenkinsRootURL();
const runUrl = run._links.self.href;
const stopUrl = utils.cleanSlashes(`${path}/${runUrl}/stop/?blocking=true&timeOutInSecs=10`);
const fetchOptions = {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
};
return Fetch.fetch(stopUrl, { fetchOptions });
}
removeFromQueue(queueItem) {
const path = config.getJenkinsRootURL();
let queueItemUrl;
// a queue item is a "pseudo run" with the queue href attached via _item
if (queueItem._item && queueItem._item._links) {
queueItemUrl = queueItem._item._links.self.href;
} else {
console.warn('could not extract data to remove item from queue; aborting');
return null;
}
const removeQueueUrl = utils.cleanSlashes(`${path}/${queueItemUrl}`);
const fetchOptions = {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
},
};
return Fetch.fetch(removeQueueUrl, { fetchOptions });
}
replayRun(run) {
const path = config.getJenkinsRootURL();
const runUrl = run._links.self.href;
const replayPipelineUrl = utils.cleanSlashes(`${path}/${runUrl}/replay/`);
const fetchOptions = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
};
return Fetch.fetchJSON(replayPipelineUrl, { fetchOptions });
}
}

View File

@ -0,0 +1,34 @@
/*
* The MIT License
*
* Copyright (c) 2016, CloudBees, Inc.
*
* 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.
*/
export const root = (typeof self === 'object' && self.self === self && self) ||
(typeof global === 'object' && global.global === global && global) || this;
//
// See blueocean-config/src/main/java/io/jenkins/blueocean/config/BlueOceanConfig.java
// and blueocean-config/src/main/resources/io/jenkins/blueocean/config/BlueOceanConfig/header.jelly
//
export const blueocean = (root.$blueocean || {});
export const prefetchdata = (blueocean.prefetchdata || {});

View File

@ -0,0 +1,41 @@
/**
* Created by cmeyers on 9/16/16.
*/
import config from './config';
import { User } from './User';
/**
* Returns a key of permissions functions that each return boolean to indicate authorization.
* Usage:
* permit(pipeline).create();
*
* @param subject
* @returns {{read: (function()), create: (function()), start: (function()), stop: (function())}}
*/
const permit = (subject) => {
const checkPermissions = (permissionName) => (
subject && subject.permissions && !!subject.permissions[permissionName]
);
return {
read: () => checkPermissions('read'),
create: () => checkPermissions('create'),
start: () => checkPermissions('start'),
stop: () => checkPermissions('stop'),
};
};
function isSecurityEnabled() {
return !!config.getSecurityConfig().enabled;
}
function isAnonymousUser() {
return User.current().isAnonymous();
}
export default {
permit,
isSecurityEnabled,
isAnonymousUser,
};

View File

@ -0,0 +1,178 @@
import { Pager } from './Pager';
import RestPaths from '../paths/rest';
import { Fetch } from '../fetch';
import { BunkerService } from './BunkerService';
import utils from '../utils';
import mobxUtils from 'mobx-utils';
/*
* This class provides activity related services.
*
* @export
* @class ActivityService
* @extends {BunkerService}
*/
export class ActivityService extends BunkerService {
/**
* Generates a pager key for [@link PagerService] to store the [@link Pager] under.
*
* @param {string} organization Jenkins organization that this pager belongs to.
* @param {string} pipeline Pipeline that this pager belongs to.
* @returns {string} key for [@link PagerService]
*/
pagerKey(organization, pipeline) {
return `Activities/${organization}-${pipeline}`;
}
/**
* Gets the activity pager
*
* @param {string} organization Jenkins organization that this pager belongs to.
* @param {string} pipeline Pipeline that this pager belongs to.
* @returns {Pager} Pager for this pipelne.
*/
activityPager(organization, pipeline) {
return this.pagerService.getPager({
key: this.pagerKey(organization, pipeline),
/**
* Lazily generate the pager incase its needed.
*/
lazyPager: () => new Pager(RestPaths.activities(organization, pipeline), 25, this),
});
}
/**
* Maps queued data into a psudeorun
*
* @see _mapQueueToPsuedoRun
*
* @param {Object} data Raw data from extenal source.
* @returns A run or psudeorun.
*/
bunkerMapper(data) {
return this._mapQueueToPsuedoRun(data);
}
/**
* Gets an activity from the store.
*
* @param {string} href Self href for activity.
* @returns {object} Mobx computed value
*/
getActivity(href) {
return this.getItem(href);
}
/**
* Fetches an activity from rest api.
*
* Note: This only works for activities that are not in the queue.
*
* @param {string} href self href of activity.
* @param {boolean} useCache Use the cache to lookup data or always fetch a new one.
* @param {boolean} overrideQueuedState Hack to make SSE work. Not use unless you know what you are doing!!!
* @returns {Promise} Promise of fetched data.
*/
fetchActivity(href, { useCache, overrideQueuedState } = {}) {
if (useCache && this.hasItem(href)) {
return Promise.resolve(this.getItem(href));
}
return Fetch.fetchJSON(href)
.then(data => {
// Should really have dedupe on methods like these, but for now
// just clone data so that we dont modify other instances.
const run = utils.clone(data);
// Ugly hack to make SSE work.
if (overrideQueuedState) {
run.state = 'RUNNING';
run.result = 'UNKNOWN';
}
return this.setItem(run);
})
.catch(err => {
console.log('There has been an error while trying to get the data.', err); // FIXME: Ivan what is the way to return an "error" opbject so underlying component are aware of the problem and can react
});
}
/**
* Fetches artifacts for a given run.
*
* @param {string} runHref The href of the run to fetcfh artifacts for.
* @returns {Object} Object containing zipFile link and list of artifacts.
*/
fetchArtifacts(runHref) {
return mobxUtils.fromPromise(Fetch.fetchJSON(`${runHref}artifacts/?start=0&limit=101`));
}
/**
* This function maps a queue item into a run instancce.
*
* We do this because the api returns us queued items as well
* as runs and its easier to deal with them if they are modeled
* as the same thing. If the raw data is needed if can be fetched
* from _item.
*
* @param {object} run Raw data from api.
* @returns psudeorun
*/
_mapQueueToPsuedoRun(run) {
if (run._class === 'io.jenkins.blueocean.service.embedded.rest.QueueItemImpl') {
return {
id: String(run.expectedBuildNumber),
state: 'QUEUED',
pipeline: run.pipeline,
type: 'QueuedItem',
result: 'UNKNOWN',
job_run_queueId: run.id,
enQueueTime: run.queuedTime,
organization: run.organization,
changeSet: [],
_links: {
self: {
href: `${run._links.parent.href}runs/${run.expectedBuildNumber}/`,
},
parent: {
href: run._links.parent.href,
},
},
_item: run,
};
}
return run;
}
/**
* Calculate an expected build number for a queued item.
*
* TODO: Enhance SSE so that this is done server side.
*
* @param {any} event SSE event.
* @returns {number} Expected build number
*/
getExpectedBuildNumber(event) {
const runs = this._data.values();
const eventJobUrl = event.blueocean_job_rest_url;
let nextId = 0;
for (let i = 0; i < runs.length; i++) {
const run = runs[i];
if (eventJobUrl !== run._links.parent.href) {
continue;
}
if (run.job_run_queueId === event.job_run_queueId) {
// We already have a "dummy" record for this queued job
// run. No need to create another i.e. ignore this event.
return run.id;
}
if (parseInt(run.id, 10) > nextId) { // figure out the next id, expectedBuildNumber
nextId = parseInt(run.id, 10);
}
}
return nextId + 1;
}
}

View File

@ -0,0 +1,115 @@
import { observable, computed, action, asMap } from 'mobx';
/**
* Abstract class used by services that need to store data in a key/value store.
*
* It is designed to store json objects from a rest api.
*
* @export
* @class BunkerService
*/
export class BunkerService {
/**
* Private mobx map for storing data.
*/
@observable _data = asMap();
/**
* Creates an instance of BunkerService.
*
* @param {PagerService} pagerService
*/
constructor(pagerService) {
this.pagerService = pagerService;
}
/**
* Extracts the key to store the daata under out of the object
*
* Default impl uses the self href link in BlueOcuean objects.
*
* @param {object} data Data to be stored once it has been passed through
* [@link bunkerMapper]
* @returns {any} The key for the store.
*/
bunkerKey(data) {
return data._links.self.href;
}
/**
* Maps the data from the source into what needs to be stored.
*
* Default impl is identity.
*
* @param {object} data Raw data from external source.
* @returns {object} Modified data object.
*/
bunkerMapper(data) {
return data;
}
/**
* Helper function that will make all pagers using this bunker refetch the data
* they are displaying. Useful if sorting changes (for example a new item is added).
*/
refreshPagers() {
this.pagerService.refresh(this);
}
/**
* Sets an item in the store.
*
* It uses the [@link bunkerKey] and [@link bunkerMapper] to generate the key/value
* to be stored.
*
* @param {Object} item Raw data from extenal source.
* @returns {Object} item mapped by [@link bunkerMapper]. It is also a mobx computed value.
*/
@action
setItem(item) {
const mappedItem = observable(this.bunkerMapper(item));
const keyItem = this.bunkerKey(mappedItem);
this._data.set(keyItem, mappedItem);
return this.getItem(keyItem);
}
/**
* Sets an array on item in the store. Calls [@link setItem] for even item in array.
*
* @param {Object[]} items Array of items to set.
* @returns {Object[]} Array of mobx computed values from store.
*/
setItems(items) {
return items.map(item => this.setItem(item));
}
/**
* Gets item from store.
*
* @param {any} key Key of item in store.
* @returns {Object} Mobx computed value of value in store.
*/
getItem(key) {
return computed(() => this._data.get(key)).get();
}
/**
* Removes item from store.
*
* @param {any} key Key of item in store.
*/
@action
removeItem(key) {
this._data.delete(key);
}
/**
* Tests to see if item exists in store.
*
* @param {any} key Key of item in store.
* @returns {boolean} true if item exists in store.
*/
hasItem(key) {
return this._data.has(key);
}
}

View File

@ -0,0 +1,113 @@
export class DefaultSSEHandler {
constructor(pipelineService, activityService, pagerService) {
this.pipelineService = pipelineService;
this.activityService = activityService;
this.pagerService = pagerService;
}
handleEvents = (event) => {
switch (event.jenkins_event) {
case 'job_run_paused':
case 'job_run_unpaused':
this.updateJob(event);
break;
case 'job_crud_created':
// Refetch pagers here. This will pull in the newly created pipeline into the bunker.
this.pipelineService.refreshPagers();
break;
case 'job_crud_deleted':
// Remove directly from bunker. No need to refresh bunkers as it will just show one less item.
this.pipelineService.removeItem(event.blueocean_job_rest_url);
break;
case 'job_crud_renamed':
// TODO: Implement this.
// Seems to be that SSE fires an updated event for the old job,
// then a rename for the new one. This is somewhat confusing for us.
break;
case 'job_run_queue_buildable':
case 'job_run_queue_enter':
this.queueEnter(event);
break;
case 'job_run_queue_left':
// this.props.processJobLeftQueueEvent(eventCopy);
break;
case 'job_run_queue_blocked': {
break;
}
case 'job_run_started': {
this.updateJob(event, true);
break;
}
case 'job_run_ended': {
this.updateJob(event);
break;
}
default :
// Else ignore the event.
}
};
updateJob(event, overrideQueuedState) {
// const queueId = event.job_run_queueId;
// const queueSelf = `${event.blueocean_job_rest_url}queue/${queueId}/`;
const runSelf = `${event.blueocean_job_rest_url}runs/${event.jenkins_object_id}/`;
const key = this.activityService.pagerKey(event.jenkins_org, event.blueocean_job_pipeline_name);
const pager = this.pagerService.getPager({ key });
this.activityService.fetchActivity(runSelf, { overrideQueuedState }).then(d => {
if (pager && !pager.has(runSelf)) {
pager.insert(runSelf);
}
this.pipelineService.updateLatestRun(d);
});
}
queueCancel(event) {
if (event.job_run_status === 'CANCELLED') {
const queueId = event.job_run_queueId;
const self = `${event.blueocean_job_rest_url}queue/${queueId}/`;
this.activityService.removeItem(self);
}
}
queueEnter(event) {
const queueId = event.job_run_queueId;
const self = `${event.blueocean_job_rest_url}queue/${queueId}/`;
const id = this.activityService.getExpectedBuildNumber(event);
const runSelf = `${event.blueocean_job_rest_url}runs/${id}/`;
const newRun = {
id,
_links: {
self: {
href: runSelf,
},
parent: {
href: event.blueocean_job_rest_url,
},
},
job_run_queueId: queueId,
pipeline: event.blueocean_job_branch_name,
result: 'UNKNOWN',
state: 'QUEUED',
_item: {
_links: {
self: {
href: self,
},
parent: {
href: event.blueocean_job_rest_url,
},
},
},
};
this.activityService.setItem(newRun);
const key = this.activityService.pagerKey(event.jenkins_org, event.blueocean_job_pipeline_name);
const pager = this.pagerService.getPager({ key });
if (pager) {
pager.insert(self);
}
}
}

View File

@ -0,0 +1,17 @@
import { observable, action } from 'mobx';
/**
* Stores the previous and current location pathnames.
*/
export default class LocationService {
@observable current;
@observable previous;
@action setCurrent(newLocation) {
if (newLocation.action !== 'REPLACE') {
this.previous = this.current;
}
this.current = newLocation.pathname;
}
}

View File

@ -0,0 +1,160 @@
import { observable, action, computed } from 'mobx';
import { Fetch } from '../fetch';
/**
* Provide a pagination function for the generic
* blueocean pagination
*
* @export
* @param {string} url - Base url to paginate.
* @returns {function} - Function that provides pagincated urls.
*/
export function paginateUrl(url) {
const sep = url.indexOf('?') >= 0 ? '&' : '?';
return (start, limit) => `${url}${sep}start=${start}&limit=${limit}`;
}
/**
* The pager fetches pages of data from the BlueOcean api. It fetches pages of data, then
* inserts them into the [@link BunkerService], and stores the href from the data.
*
* MobX computes a data field from the hrefs backed by the backend cache. This allows for SSE events
* to be proporgated to the pager.
*
* @export
* @class Pager
*/
export class Pager {
/**
* List of deisplayed items hrefs.
*/
@observable hrefs = [];
/**
* pager is fetching data.
*/
@observable pending = false;
/**
* Will be set in an error occurs.
*/
@observable error = null;
/**
* The latest page the pager has fetched.
*/
@observable currentPage = 0;
/**
* More pages to fetch.
*/
@observable hasMore = true;
/**
* Mobx computed value that creates an array of objects from the list of hrefs stored. If either the
* bunker changes, or the hrefs change, this is recalculated and will trigger a react reaction.
*
* If item does not exist in bunker, then we just ignore it.
* @readonly
* @type {Array<Object>}
*/
@computed
get data() {
return this.hrefs.map(href => this.bunker.getItem(href)).filter(item => item !== undefined);
}
/**
* Creates an instance of Pager and fetches the first page.
*
* @param {string} url - Base url of collectin to fetch
* @param {number} pageSize - Page size to fetch during one load.
* @param {BunkerService} bunker - Data store
* @param {UrlProvider} [urlProvider=paginateUrl]
*/
constructor(url, pageSize, bunker, urlProvider = paginateUrl) {
this.pageSize = pageSize;
this.url = url;
this.urlProvider = urlProvider;
this.pagedUrl = this.urlProvider(url);
this.pageSize = pageSize;
this.bunker = bunker;
// Fetch the first page so that the user does not have to.
this.fetchNextPage();
}
/**
* Fetches the next page from the backend.
*
* @returns {Promise}
*/
@action
fetchNextPage() {
// Get the next page's url.'
const url = this.pagedUrl(this.currentPage * this.pageSize, this.pageSize + 1);
this.pending = true;
return Fetch.fetchJSON(url)
.then(action('Process pager data', data => {
// Store item in bunker.
const saved = this.bunker.setItems(data);
// 1 extra item is fetched because need to know if there are more packages. So
// slice off the last item, then map all items to just be hrefs.
const trimmedHrefs = saved.slice(0, this.pageSize).map(item => item._links.self.href);
// Append the new Hrefs to the existing ones.
this.hrefs = this.hrefs.concat(trimmedHrefs);
// True if we fetch more items than the page size.
this.hasMore = data.length > this.pageSize;
this.currentPage = this.currentPage + 1;
this.pending = false;
})).catch(err => {
console.error('Error fetching page', err);
action('set error', () => { this.error = err; });
});
}
/**
* Refreshes the Hrefs for the pager. It also stores the latest data in the [@link BunkerService]
*
* This might be called if something like sorting of a list changes.
*
* @returns {Promise}
*/
@action
refresh() {
const url = this.pagedUrl(0, this.currentPage * this.pageSize + 1);
this.pending = true;
return Fetch.fetchJSON(url) // Fetch data
.then(action('set data', data => {
this.bunker.setItems(data);
this.hrefs = data.slice(0, this.pageSize).map(x => x._links.self.href);
this.hasMore = data.length > this.pageSize;
this.currentPage = this.currentPage + 1;
this.pending = false;
})).catch(err => {
console.error('Error fetching page', err);
this.err = err;
});
}
/**
* Inserts an href into the list. This will cause a reaction render for the paged list of data.
*
* @param {string} href - href of item to display
* @param {number} [pos=0] - Position to insert it. Default is first item.
*/
@action
insert(href, pos = 0) {
this.hrefs.splice(pos, 0, href);
}
/**
* Href exists in pager.
*
* @param {string} href
* @returns {boolean} - True if this pager does have this href
*/
has(href) {
return this.hrefs.indexOf(href) > -1;
}
}

View File

@ -0,0 +1,102 @@
/**
* This service manages the various instances of pagers that currently exist.
*
* TODO: Currently a new pager is created for any new list of items to be paged. Cleanup may be
* required to stop memory leakes. However Pagers don't store more data so this may not be an issue.'
*
* @export
* @class PagerService
*/
export class PagerService {
/**
* MobX map to hold [@link Pager]'s'
*/
_pagerMap = new Map();
/**
* Registers a pager with the PagerService.
*
* Namespacing strings is prefered to stop colisions. E.g. Activity/$org-$pipeline.
*
* @param {any} key - Key to register the pager under.
* @param {Pager} pager - pager to regiser.
*/
registerPager(key, pager) {
if (this._pagerMap.has(key)) {
throw new Error(`Pager '${key}' already exits in PagerService`);
}
this._pagerMap.set(key, pager);
}
/**
* Removes pager from the cache.
*
* @param {any} key
*/
removePager(key) {
if (this._pagerMap.has(key)) {
this._pagerMap.delete(key);
}
}
/**
* Lazily creates a pager. Do this because pager fetches the first page when it is created.
*
* @callback lazyPager
* @returns {Pager}
*/
/**
* Gets a pager from the cache.
*
* @param {Object} options
* @param {any} options.key - Key to store pager under.
* @param {lazyPager} options.lazyPager - function to lazily crete the pager.
* @returns {Pager}
*/
getPager({ key, lazyPager }) {
if (this._pagerMap.has(key)) {
return this._pagerMap.get(key);
}
if (lazyPager) {
const pager = lazyPager();
this.registerPager(key, pager);
return pager;
}
return null;
}
/**
* Refetches the list of items the pagers that use a specific [@link BunkerService] to display.
* This is done in the case of reordering.
*
* TODO: Make this more targetted.
*
* @param {BunkerService} bunkerService A service that extends [@link BunkerService]
*/
refresh(bunkerService) {
this._pagerMap.forEach(pager => {
if (bunkerService === pager.bunker) {
pager.refresh();
}
});
}
/**
* Gets all pagers for a [@link BunkerService]
*
* @param {BunkerService} bunker
* @returns {Pager[]}
*/
getPagers(bunker) {
const ret = [];
this._pagerMap.forEach(pager => {
if (bunker === pager.bunker) {
ret.push(bunker);
}
});
return ret;
}
}

View File

@ -0,0 +1,137 @@
import { Pager } from './Pager';
import RestPaths from '../paths/rest';
import { Fetch } from '../fetch';
import utils from '../utils';
import { BunkerService } from './BunkerService';
import { action } from 'mobx';
/**
* This class handles pipeline related data. This includes pipelines, branches and pullrequeusts as they are
* all pipelines in the backend.
*
* @export
* @class PipelineService
* @extends {BunkerService}
*/
export class PipelineService extends BunkerService {
/**
* Creates an instance of PipelineService.
*
* @param {PagerService} pagerService
* @param {ActivityService} activityService
*/
constructor(pagerService, activityService) {
super(pagerService);
this.activityService = activityService;
}
/**
* Gets pager for /blue/pipelines
*
* @returns {Pager}
*/
allPipelinesPager() {
return this.pagerService.getPager({
key: 'PipelinesAll',
lazyPager: () => new Pager(RestPaths.allPipelines(), 25, this),
});
}
/**
* Gets pager for /blue/organization/:organization/pipelines
*
* @param {strinb} organization organization pager belongs to.
* @returns {Pager}
*/
organiztionPipelinesPager(organization) {
return this.pagerService.getPager({
key: `Pipelines/${organization}`,
lazyPager: () => new Pager(RestPaths.organizationPipelines(organization), 25, this),
});
}
/**
* Gets pager for /blue/organization/:organization/pipelines/:pipeline/branches
*
* @param {string} organization
* @param {string} pipeline
* @returns {Pager}
*/
branchPager(organization: string, pipeline: string) {
return this.pagerService.getPager({
key: `Branches/${organization}-${pipeline}`,
lazyPager: () => new Pager(RestPaths.branches(organization, pipeline), 25, this),
});
}
/**
* Gets pager for /blue/organization/:organization/pipelines/:pipeline/pullRequests
*
* @param {string} organization
* @param {string} pipeline
* @returns {Pager}
*/
prPager(organization: string, pipeline: string) {
return this.pagerService.getPager({
key: `PRs/${organization}-${pipeline}`,
lazyPager: () => new Pager(RestPaths.pullRequests(organization, pipeline), 25, this),
});
}
/**
* Adds the latest run to the [@link ActivityService], and sets the latestRun as a mobx computed value.
*
* @param {Object} pipelineData Raw data from backend.
* @return {Object} mapped pipelineData with latestRun set to be a mobx computed value.
*/
bunkerMapper = (pipelineData) => {
const data = utils.clone(pipelineData);
const latestRun = data.latestRun;
if (latestRun) {
data.latestRun = this.activityService.setItem(latestRun);
}
return data;
}
/**
* Gets a pipeline from the store
*
* @param {string} href - Self href of the pipeline.
* @returns {Object} - Mobx computed value of the pipeline.
*/
getPipeline(href) {
return this.getItem(href);
}
/**
* Fetches pipeline from the backend and stores it in
*
* @param {string} href - Self href of the pipeline.
* @param {Object} options
* @param {boolean} options.useCache - If true fetch from the store if it exists.
* @returns
*/
fetchPipeline(href, { useCache } = {}) {
if (useCache && this.hasItem(href)) {
return Promise.resolve(this.getItem(href));
}
return Fetch.fetchJSON(href).then(data => this.setItem(data));
}
/**
* MobX Action to update the latest run on a pipeline. Use for SSE. This will cause a reaction
* and rerender anything that uses the latest run of this pipeline.
*
* @param {Object} run An activity from activityService.getItem().
*/
@action
updateLatestRun(run) {
const pipeline = this.getItem(run._links.parent.href);
if (pipeline) {
pipeline.latestRun = run;
}
}
}

View File

@ -0,0 +1,21 @@
export class SSEService {
constructor(connection) {
this.connection = connection;
this._handlers = [];
}
_initListeners() {
if (!this.jobListener) {
this.jobListener = this.connection.subscribe('job', (event) => {
this._handleJobEvent(event);
});
}
}
registerHandler(handlerFn) {
this._handlers.push(handlerFn);
}
_handleJobEvent(event) {
this._handlers.forEach(handler => handler(event));
}
}

View File

@ -0,0 +1,8 @@
// @flow
export { PagerService } from './PagerService';
export { PipelineService } from './PipelineService';
export { SSEService } from './SSEService';
export { Pager } from './Pager';
export { ActivityService } from './ActivityService';
export { DefaultSSEHandler } from './DefaultSSEHandler';
export LocationService from './LocationService';

View File

@ -0,0 +1,170 @@
/**
* Created by cmeyers on 7/29/16.
*/
import defaultFetch from 'isomorphic-fetch';
import config from '../urlconfig';
import utils from '../utils';
/**
* Wraps the SSE Gateway and fetches data related to events from REST API.
*/
export class SseBus {
constructor(connection, fetch) {
this.id = this._random();
this.connection = connection;
this.fetch = fetch || defaultFetch;
this.externalListeners = {};
this.sseListeners = {};
}
dispose() {
Object.keys(this.sseListeners).forEach((token) => {
this.unsubscribe(token);
});
this.externalListeners = {};
this.sseListeners = {};
}
/**
* Subscribe to job events.
* @param callback func to invoke with job data
* @param jobFilter func invoked for each job event, return false to suppress callback invocation
* @returns {number} unsubscribe token
*/
subscribeToJob(callback, jobFilter) {
const id = this._random();
this.externalListeners[id] = {
listener: callback,
filter: jobFilter,
};
if (!this.sseListeners.job) {
const sseListener = this.connection.subscribe('job', (event) => {
this._handleJobEvent(event);
});
this.sseListeners.job = sseListener;
}
return id;
}
unsubscribe(token) {
delete this.externalListeners[token];
if (Object.keys(this.externalListeners).length === 0) {
this.connection.unsubscribe(this.sseListeners.job);
delete this.sseListeners.job;
}
}
_handleJobEvent(event) {
const subscriptions = Object
.keys(this.externalListeners)
.map(subId => this.externalListeners[subId]);
const interestedListeners = subscriptions
.filter(sub => sub.filter(event))
.map(sub => sub.listener);
// if no filters are interested in the event, bail
if (interestedListeners.length === 0) {
return;
}
switch (event.jenkins_event) {
case 'job_crud_created':
case 'job_crud_deleted':
case 'job_crud_renamed':
this._refetchPipelines();
break;
case 'job_run_queue_buildable':
case 'job_run_queue_enter':
this._enqueueJob(event, interestedListeners);
break;
case 'job_run_queue_left':
case 'job_run_queue_blocked': {
break;
}
case 'job_run_paused':
case 'job_run_unpaused':
case 'job_run_started': {
this._updateJob(event, interestedListeners);
break;
}
case 'job_run_ended': {
this._updateJob(event, interestedListeners);
break;
}
default :
// Else ignore the event.
}
}
_refetchPipelines() {
// TODO: implement once migration into commons JS
}
_enqueueJob(event, listeners) {
const queuedRun = {};
queuedRun.pipeline = event.job_ismultibranch ?
event.blueocean_job_branch_name :
event.blueocean_job_pipeline_name;
const runUrl = utils.cleanSlashes(`${event.blueocean_job_rest_url}/runs/${event.job_run_queueId}`);
queuedRun._links = {
self: {
href: runUrl,
},
};
queuedRun.state = 'QUEUED';
queuedRun.result = 'UNKNOWN';
for (const listener of listeners) {
listener(queuedRun, event);
}
}
_updateJob(event, listeners) {
const baseUrl = config.getJenkinsRootURL();
const url = utils.cleanSlashes(`${baseUrl}/${event.blueocean_job_rest_url}/runs/${event.jenkins_object_id}`);
this.fetch(url)
.then((data) => {
const updatedRun = utils.clone(data);
// FIXME: Talk to CMeyers why we cannot use the data.state?
// in many cases the SSE and subsequent REST call occur so quickly
// that the run's state is stale. force the state to the correct value.
if (event.jenkins_event === 'job_run_ended') {
updatedRun.state = 'FINISHED';
} else if (event.jenkins_event === 'job_run_paused') {
updatedRun.state = 'PAUSED';
} else {
updatedRun.state = 'RUNNING';
}
for (const listener of listeners) {
listener(updatedRun, event);
}
});
}
_updateMultiBranchPipelineBranches() {
// TODO: implement once migration into commons JS
}
/**
* @returns {number}
* @private
*/
_random() {
return Math.random() * Math.pow(10, 16);
}
}

View File

@ -0,0 +1,123 @@
/*
* The MIT License
*
* Copyright (c) 2017, CloudBees, Inc.
*
* 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.
*/
/**
* Client-side local storage for Blue Ocean.
* See https://tfennelly.github.io/jenkins-js-storage/
*/
import * as storage from '@jenkins-cd/storage';
import { blueocean } from './scopes';
import config from './config';
export const jenkinsNS = storage.jenkinsNamespace();
export const installInfo = jenkinsNS.subspace('installInfo');
/**
* Check do we need to clear the jenkinsNS.
* <p>
* Simple process of checking if the version OR list of plugins
* stored (from the last change) have changed based on what was
* delivered with this page.
* <p>
* Internal use only. Exported for testing purposes only.
* @param {string} installVersion The version of the Jenkins instance that's running now
* i.e. that loaded this page.
* @param {Array} installPluginList The list of active plugins installed in the Jenkins instance
* that's running now i.e. that loaded this page.
* @private
*/
export const _clearJenkinsNS = (installVersion, installPluginList) => {
// Info about the Jenkins that was running the last time we loaded this page.
const storedVersion = installInfo.get('version');
const storedPluginList = installInfo.get('plugins');
const doClear = (because) => {
jenkinsNS.clear();
installInfo.set('version', installVersion);
installInfo.set('plugins', installPluginList);
installInfo.set('lastcleared', {
at: Date.now(),
because,
});
};
try {
if (storedVersion && storedPluginList) {
// compare the Jenkins version
if (installVersion !== storedVersion) {
doClear(`Jenkins versions did not match. installVersion: ${installVersion}, storedVersion: ${storedVersion}`);
} else {
// compare the plugin lists
if (installPluginList.length !== storedPluginList.length) {
// Different number of active plugins.
// No need to check the names and versions.
doClear('Different number of active plugins');
} else {
// Same number of plugins. Lets check that they all
// match up i.e. that we can find each plugin in each list and
// that the versions match.
try {
installPluginList.forEach((installedPlugin) => {
let found = false;
storedPluginList.forEach((storedPlugin) => {
if (storedPlugin.hpiPluginId === installedPlugin.hpiPluginId) {
// same plugin.
found = true;
// Check the versions.
if (storedPlugin.hpiPluginVer !== installedPlugin.hpiPluginVer) {
throw new Error(`Different plugin versions for plugin ${installedPlugin.hpiPluginId}`);
}
}
});
if (!found) {
throw new Error(`New plugin installed ${installedPlugin.hpiPluginId}`);
}
});
} catch (e) {
// One of the plugins has been updated or removed.
// See Errors thrown inside above try/catch.
doClear(e.message);
}
}
}
} else {
// Theoretically no need to clear in this case,
// but lets do it anyway.
doClear('No Jenkins info stored. Clearing anyway, just in case.');
}
} catch (e) {
console.error('Unexpected error while checking/clearing Jenkins instance client-side storage. Clearing as a precaution.', e);
doClear(`Unexpected error while checking/clearing Jenkins instance client-side storage: ${e.message}`);
}
};
// Call the clear function automatically.
const installVersion = config.getJenkinsConfig().version;
const installPluginList = blueocean.jsExtensions;
if (installVersion && installPluginList) {
_clearJenkinsNS(installVersion, installPluginList);
} else {
console.warn('Unexpected state. Blue Ocean preload state not on page as expected. This is okay if running in a test.');
}

View File

@ -0,0 +1,62 @@
import { Fetch, FetchFunctions } from './fetch';
// default impls
const fetchJSON = Fetch.fetchJSON;
const fetch = Fetch.fetch;
export default {
/**
* Switches fetch functions with arbitrary replacements.
* Useful for test spies.
*
* @param _fetchJSON
* @param _fetch
*/
patchFetch(_fetchJSON, _fetch) {
Fetch.fetchJSON = _fetchJSON;
Fetch.fetch = _fetch;
},
/**
* Switches fetch functions for ones that dont use JWT. Needed
* for running tests.
*/
patchFetchNoJWT() {
Fetch.fetchJSON = FetchFunctions.rawFetchJSON;
Fetch.fetch = FetchFunctions.rawFetch;
},
/**
* Restores original fetch functions.
*/
restoreFetch() {
Fetch.fetchJSON = fetchJSON;
Fetch.fetch = fetch;
},
/**
* Patches fetch functions with a resolved promise. This will make all fetch calls return
* this data.
*
* Usage
*
* TestUtils.patchFetchWithData((url, options) => {
* assert.equals(url,"someurl")
* return { mydata: 5 }
* })
*/
patchFetchWithData(dataFn) {
Fetch.fetchJSON = Fetch.fetch = (url, options) => {
const { onSuccess, onError } = options || {};
const data = Promise.resolve(dataFn(url, options));
if (onSuccess) {
return data.then(onSuccess).catch(FetchFunctions.onError(onError));
}
return data;
};
},
};

View File

@ -0,0 +1,40 @@
let blueOceanAppURL = '/';
let jenkinsRootURL = '';
let loaded = false;
function loadConfig() {
try {
const headElement = document.getElementsByTagName('head')[0];
// Look up where the Blue Ocean app is hosted
blueOceanAppURL = headElement.getAttribute('data-appurl');
if (typeof blueOceanAppURL !== 'string') {
blueOceanAppURL = '/';
}
jenkinsRootURL = headElement.getAttribute('data-rooturl');
loaded = true;
} catch (error) {
// eslint-disable-next-line no-console
console.warn('error reading attributes from document; urls will be empty', error);
loaded = false;
}
}
export default {
getJenkinsRootURL() {
if (!loaded) {
loadConfig();
}
return jenkinsRootURL;
},
getBlueOceanAppURL() {
if (!loaded) {
loadConfig();
}
return blueOceanAppURL;
},
};

View File

@ -0,0 +1,37 @@
// @flow
/**
* Trims duplicate forward slashes to a single slash and adds trailing slash if needed.
* @param url
* @returns {string}
*/
const cleanSlashes = (url: string) => {
if (url.indexOf('//') !== -1) {
let cleanUrl = url.replace('//', '/');
cleanUrl = cleanUrl.substr(-1) === '/' ?
cleanUrl : `${cleanUrl}/`;
return cleanSlashes(cleanUrl);
}
return url;
};
export default {
cleanSlashes,
clone(obj: Object) {
if (!obj) return obj;
return JSON.parse(JSON.stringify(obj));
},
windowOrGlobal() {
return (typeof self === 'object' && self.self === self && self) ||
(typeof global === 'object' && global.global === global && global) ||
this;
},
refreshPage() {
if (this.windowOrGlobal().location.reload) {
this.windowOrGlobal().location.reload(true);
}
},
};

View File

@ -0,0 +1,50 @@
import { Promise } from 'es6-promise';
/**
* DuplicateCallTracker maintains active calls against a particular key
*/
export class DeDupeCallTracker {
constructor() {
/**
* Onload callbacks cache. Used to ensure we don't
* issue multiple in-parallel requests for the same
* class metadata.
*/
this.promises = {};
}
/**
* Generalization of duplicate request consolidation:
*
* @key: key to use to track the duplicate requests
* @promiseCreator: function that will return an initial promise, e.g. () => fetch(...)
* @return a Promise
*/
dedupe(key, promiseCreator) {
// get active or create
return this.promises[key] || (this.promises[key] =
promiseCreator()
.then((data) => {
delete this.promises[key];
return data;
})
.catch((err) => {
delete this.promises[key];
return Promise.reject(err);
})
);
}
}
const deDupeCallTracker = new DeDupeCallTracker();
/**
* Generalization of duplicate request consolidation:
*
* @key: key to use to track the duplicate requests
* @promiseCreator: function that will return an initial promise, e.g. () => fetch(...)
* @return a Promise
*/
export default function dedupe(key, promiseCreator) {
return deDupeCallTracker.dedupe(key, promiseCreator);
}

View File

@ -0,0 +1,3 @@
@import 'variables';
@import 'replay-button';
@import 'run-button';

View File

@ -0,0 +1,64 @@
.replay-button-component {
display: inline-block;
.replay-button {
display: inline-block;
cursor: pointer;
}
// default (text) display
.svg-icon {
display: none;
}
// icon display
&.icon-button {
height: 24px;
.replay-button {
width: 24px;
height: 24px;
background: transparent;
border: 0;
position: relative;
.svg-icon {
display: block;
position: absolute;
top: 0;
left: 0;
}
}
.button-label {
display: none;
}
}
// default (light) themeing
.svg-icon {
fill: @brand-primary;
&:hover {
fill: @brand-primary-dark;
}
}
// dark themeing
&.dark {
.btn {
background: transparent;
color: white;
border-color: white;
}
.svg-icon {
fill: white;
&:hover {
fill: #CCC;
}
}
}
}

View File

@ -0,0 +1,83 @@
.run-button-component {
display: inline-block;
.run-button, .stop-button {
display: inline-block;
cursor: pointer;
}
// default (text) display
.svg-icon {
display: none;
}
// icon display
&.icon-button {
height: 24px;
.run-button, .stop-button {
width: 24px;
height: 24px;
background: transparent;
border: 0;
position: relative;
.svg-icon {
display: block;
position: absolute;
top: 0;
left: 0;
}
}
.stop-button {
/* asset has different padding than material-icon asset used in .run-button: nudge it slightly */
top: 2px;
left: 2px;
&.stopping {
opacity: 0.4;
cursor: auto;
}
}
.button-label {
display: none;
}
}
// default (light) themeing
.svg-icon, .svg-icon-inner {
fill: @brand-primary;
}
.svg-icon:hover {
fill: @brand-primary-dark;
.svg-icon-inner {
fill: @brand-primary-dark;
}
}
// dark themeing
&.dark {
.btn {
background: transparent;
color: white;
border-color: white;
}
.svg-icon, .svg-icon-inner {
fill: white;
}
.svg-icon:hover {
fill: #CCC;
.svg-icon-inner {
fill: #CCC;
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More