├── .gitignore ├── README.md ├── assets └── images │ └── jenkins_weather │ ├── health-00to19.gif │ ├── health-20to39.gif │ ├── health-40to59.gif │ ├── health-60to79.gif │ └── health-80plus.gif ├── config.ru ├── dashboards └── jenkins.erb ├── jobs ├── jenkins_jobs.rb ├── jenkins_jobstates.rb ├── jenkins_queue.rb └── jenkins_weather.rb └── widgets ├── jenkins_jobs ├── jenkins_jobs.coffee ├── jenkins_jobs.html └── jenkins_jobs.scss ├── jenkins_jobstates ├── jenkins_jobstates.coffee ├── jenkins_jobstates.html └── jenkins_jobstates.scss ├── jenkins_queue ├── jenkins_queue.coffee ├── jenkins_queue.html └── jenkins_queue.scss └── jenkins_weather ├── jenkins_weather.coffee ├── jenkins_weather.html └── jenkins_weather.scss /.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mneudert/dashing-jenkins/1af351ebe5a7ba764237ed23e011a224b5cd9bcc/.gitignore -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dashing-Jenkins 2 | 3 | [Dashing](http://shopify.github.com/dashing/) widgets displaying [Jenkins](http://jenkins-ci.org/) API data. 4 | 5 | ## Installation 6 | 7 | 1. copy the job files of all widgets you want to use to your dashboards jobs folder. 8 | 2. configure *url* and *view* in your dashboards *config.ru*. 9 | 3. copy the corresponding assets to your dashboards assets folder. 10 | 4. copy the corresponding html snippets from the demo dashboard to your own dashboard. 11 | 5. copy the corresponding widgets to your dashboards widgets folder. 12 | 6. ??? 13 | 7. profit! 14 | 15 | ## Sources 16 | 17 | ### Weather Icons 18 | 19 | Taken from local jenkins installation. 20 | 21 | With just a little modification in the jenkins_weather.scss you could of course load them from a jenkins instance instead of the dashboard host. 22 | -------------------------------------------------------------------------------- /assets/images/jenkins_weather/health-00to19.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mneudert/dashing-jenkins/1af351ebe5a7ba764237ed23e011a224b5cd9bcc/assets/images/jenkins_weather/health-00to19.gif -------------------------------------------------------------------------------- /assets/images/jenkins_weather/health-20to39.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mneudert/dashing-jenkins/1af351ebe5a7ba764237ed23e011a224b5cd9bcc/assets/images/jenkins_weather/health-20to39.gif -------------------------------------------------------------------------------- /assets/images/jenkins_weather/health-40to59.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mneudert/dashing-jenkins/1af351ebe5a7ba764237ed23e011a224b5cd9bcc/assets/images/jenkins_weather/health-40to59.gif -------------------------------------------------------------------------------- /assets/images/jenkins_weather/health-60to79.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mneudert/dashing-jenkins/1af351ebe5a7ba764237ed23e011a224b5cd9bcc/assets/images/jenkins_weather/health-60to79.gif -------------------------------------------------------------------------------- /assets/images/jenkins_weather/health-80plus.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mneudert/dashing-jenkins/1af351ebe5a7ba764237ed23e011a224b5cd9bcc/assets/images/jenkins_weather/health-80plus.gif -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | require 'dashing' 2 | 3 | configure do 4 | set :jenkins, { 5 | 'url' => 'https://jenkins.domain:8081/path/to/index', 6 | 'view' => 'All' 7 | } 8 | end 9 | 10 | map Sinatra::Application.assets_prefix do 11 | run Sinatra::Application.sprockets 12 | end 13 | 14 | run Sinatra::Application 15 | -------------------------------------------------------------------------------- /dashboards/jenkins.erb: -------------------------------------------------------------------------------- 1 | <% content_for(:title) { "Jenkins Dashboard" } %> 2 | 3 |
4 | 21 |
-------------------------------------------------------------------------------- /jobs/jenkins_jobs.rb: -------------------------------------------------------------------------------- 1 | require 'net/http' 2 | require 'json' 3 | 4 | http = nil 5 | 6 | SCHEDULER.every '2m', :first_in => 0 do 7 | if not defined? settings.jenkins? 8 | next 9 | end 10 | 11 | if nil == http 12 | url = URI.parse(settings.jenkins['url']) 13 | http = Net::HTTP.new(url.host, url.port) 14 | 15 | if ('https' == url.scheme) 16 | http.use_ssl = true 17 | http.verify_mode = OpenSSL::SSL::VERIFY_NONE 18 | end 19 | end 20 | 21 | api_url = '%s/view/%s/api/json?tree=jobs[name,color]' \ 22 | % [ settings.jenkins['url'].chomp('/'), settings.jenkins['view'] ] 23 | response = http.request(Net::HTTP::Get.new(api_url)) 24 | jobs = JSON.parse(response.body)['jobs'] 25 | 26 | if jobs.empty? 27 | next 28 | end 29 | 30 | jobs.map! { |job| 31 | color = 'grey' 32 | 33 | case job['color'] 34 | when 'blue', 'blue_anime' 35 | color = 'blue' 36 | when 'red', 'red_anime' 37 | color = 'red' 38 | end 39 | 40 | { name: job['name'], state: color } 41 | } 42 | 43 | jobs.sort_by { |job| job['name'] } 44 | 45 | send_event('jenkins_jobs', { jobs: jobs }) 46 | end 47 | -------------------------------------------------------------------------------- /jobs/jenkins_jobstates.rb: -------------------------------------------------------------------------------- 1 | require 'net/http' 2 | require 'json' 3 | 4 | http = nil 5 | 6 | SCHEDULER.every '2m', :first_in => 0 do 7 | if not defined? settings.jenkins? 8 | next 9 | end 10 | 11 | if nil == http 12 | url = URI.parse(settings.jenkins['url']) 13 | http = Net::HTTP.new(url.host, url.port) 14 | 15 | if ('https' == url.scheme) 16 | http.use_ssl = true 17 | http.verify_mode = OpenSSL::SSL::VERIFY_NONE 18 | end 19 | end 20 | 21 | api_url = '%s/view/%s/api/json?tree=jobs[color]' \ 22 | % [ settings.jenkins['url'].chomp('/'), settings.jenkins['view'] ] 23 | response = http.request(Net::HTTP::Get.new(api_url)) 24 | jobs = JSON.parse(response.body)['jobs'] 25 | 26 | if jobs.empty? 27 | next 28 | end 29 | 30 | blue = 0 31 | red = 0 32 | grey = 0 33 | 34 | jobs.each { |job| 35 | case job['color'] 36 | when 'blue', 'blue_anime' 37 | blue += 1 38 | when 'red', 'red_anime' 39 | red += 1 40 | else 41 | grey += 1 42 | end 43 | } 44 | 45 | send_event('jenkins_jobstates', { blue: blue, red: red, grey: grey }) 46 | end 47 | -------------------------------------------------------------------------------- /jobs/jenkins_queue.rb: -------------------------------------------------------------------------------- 1 | require 'net/http' 2 | require 'json' 3 | 4 | http = nil 5 | 6 | SCHEDULER.every '2m', :first_in => 0 do 7 | if not defined? settings.jenkins? 8 | next 9 | end 10 | 11 | if nil == http 12 | url = URI.parse(settings.jenkins['url']) 13 | http = Net::HTTP.new(url.host, url.port) 14 | 15 | if ('https' == url.scheme) 16 | http.use_ssl = true 17 | http.verify_mode = OpenSSL::SSL::VERIFY_NONE 18 | end 19 | end 20 | 21 | api_url = '%s/queue/api/json?tree=items[inQueueSince,task[color,name]]' \ 22 | % [ settings.jenkins['url'].chomp('/') ] 23 | response = http.request(Net::HTTP::Get.new(api_url)) 24 | items = JSON.parse(response.body)['items'] 25 | 26 | if items.empty? 27 | next 28 | end 29 | 30 | items.sort_by { |item| item['inQueueSince'] } 31 | items.reverse! 32 | 33 | items = items[0..7] 34 | 35 | items.map! { |item| 36 | color = 'grey' 37 | 38 | case item['task']['color'] 39 | when 'blue', 'blue_anime' 40 | color = 'blue' 41 | when 'red', 'red_anime' 42 | color = 'red' 43 | end 44 | 45 | { name: item['task']['name'], state: color } 46 | } 47 | 48 | send_event('jenkins_queue', { items: items }) 49 | end 50 | -------------------------------------------------------------------------------- /jobs/jenkins_weather.rb: -------------------------------------------------------------------------------- 1 | require 'json' 2 | require 'net/http' 3 | require 'uri' 4 | 5 | http = nil 6 | 7 | SCHEDULER.every '2m', :first_in => 0 do 8 | if not defined? settings.jenkins? 9 | next 10 | end 11 | 12 | if nil == http 13 | url = URI.parse(settings.jenkins['url']) 14 | http = Net::HTTP.new(url.host, url.port) 15 | 16 | if ('https' == url.scheme) 17 | http.use_ssl = true 18 | http.verify_mode = OpenSSL::SSL::VERIFY_NONE 19 | end 20 | end 21 | 22 | api_url = '%s/view/%s/api/json?tree=jobs[healthReport[iconUrl]]' \ 23 | % [ settings.jenkins['url'].chomp('/'), settings.jenkins['view'] ] 24 | response = http.request(Net::HTTP::Get.new(api_url)) 25 | jobs = JSON.parse(response.body)['jobs'] 26 | 27 | if jobs.empty? 28 | next 29 | end 30 | 31 | report = { 32 | '80plus' => 0, 33 | '60to79' => 0, 34 | '40to59' => 0, 35 | '20to39' => 0, 36 | '00to19' => 0 37 | } 38 | 39 | jobs.each { |job| 40 | next if not job['healthReport'] 41 | next if not job['healthReport'][0] 42 | next if not job['healthReport'][0]['iconUrl'] 43 | 44 | case job['healthReport'][0]['iconUrl'] 45 | when 'health-80plus.png' 46 | report['80plus'] += 1 47 | when 'health-60to79.png' 48 | report['60to79'] += 1 49 | when 'health-40to59.png' 50 | report['40to59'] += 1 51 | when 'health-20to39.png' 52 | report['20to39'] += 1 53 | when 'health-00to19.png' 54 | report['00to19'] += 1 55 | end 56 | } 57 | 58 | send_event('jenkins_weather', { weather: report }) 59 | end 60 | -------------------------------------------------------------------------------- /widgets/jenkins_jobs/jenkins_jobs.coffee: -------------------------------------------------------------------------------- 1 | class Dashing.Jenkins_Jobs extends Dashing.Widget 2 | -------------------------------------------------------------------------------- /widgets/jenkins_jobs/jenkins_jobs.html: -------------------------------------------------------------------------------- 1 |

2 | 3 | -------------------------------------------------------------------------------- /widgets/jenkins_jobs/jenkins_jobs.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Widget-jenkins-jobs styles 3 | // ---------------------------------------------------------------------------- 4 | .widget-jenkins-jobs { 5 | background-color: lightgrey; 6 | padding-bottom: 0px !important; 7 | padding-left: 0px !important; 8 | padding-right: 0px !important; 9 | 10 | .title { 11 | color: black; 12 | } 13 | 14 | .job { 15 | border-radius: 8px; 16 | float: left; 17 | padding: 4px 0px; 18 | } 19 | 20 | .job[data-state="blue"] { 21 | background-color: green; 22 | } 23 | 24 | .job[data-state="red"] { 25 | background-color: red; 26 | } 27 | 28 | .job[data-state="grey"] { 29 | background-color: grey; 30 | } 31 | 32 | .job span { 33 | padding: 0px 4px; 34 | } 35 | } 36 | 37 | .widget-jenkins-jobs[data-cols="4"] { 38 | .job { 39 | margin-bottom: 1%; 40 | margin-left: 1%; 41 | width: 23.75%; 42 | } 43 | 44 | .job:nth-child(4n + 1) { 45 | clear: left; 46 | } 47 | } 48 | 49 | .widget-jenkins-jobs[data-cols="5"] { 50 | .job { 51 | margin-bottom: 1%; 52 | margin-left: 1%; 53 | width: 18.8%; 54 | } 55 | 56 | .job:nth-child(5n + 1) { 57 | clear: left; 58 | } 59 | } 60 | 61 | .widget-jenkins-jobs[data-cols="6"] { 62 | .job { 63 | margin-bottom: 1%; 64 | margin-left: 1%; 65 | width: 15.5%; 66 | } 67 | 68 | .job:nth-child(6n + 1) { 69 | clear: left; 70 | } 71 | } 72 | 73 | .widget-jenkins-jobs[data-cols="7"] { 74 | .job { 75 | margin-bottom: 1%; 76 | margin-left: 1%; 77 | width: 13.14285%; 78 | } 79 | 80 | .job:nth-child(7n + 1) { 81 | clear: left; 82 | } 83 | } 84 | 85 | .widget-jenkins-jobs[data-cols="8"] { 86 | .job { 87 | margin-bottom: 1%; 88 | margin-left: 1%; 89 | width: 11.375%; 90 | } 91 | 92 | .job:nth-child(8n + 1) { 93 | clear: left; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /widgets/jenkins_jobstates/jenkins_jobstates.coffee: -------------------------------------------------------------------------------- 1 | class Dashing.Jenkins_Jobstates extends Dashing.Widget 2 | -------------------------------------------------------------------------------- /widgets/jenkins_jobstates/jenkins_jobstates.html: -------------------------------------------------------------------------------- 1 |

2 | 3 |
4 | 5 | 6 | 7 |
-------------------------------------------------------------------------------- /widgets/jenkins_jobstates/jenkins_jobstates.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Widget-jenkins-jobstates styles 3 | // ---------------------------------------------------------------------------- 4 | .widget-jenkins-jobstates { 5 | background-color: lightgrey; 6 | 7 | .title { 8 | color: black; 9 | } 10 | 11 | .jobs { 12 | margin: 0 auto 13 | } 14 | 15 | .job { 16 | border-radius: 24px; 17 | display: inline-block; 18 | height: 60px; 19 | line-height: 60px; 20 | margin-left: 12px; 21 | text-align: center; 22 | width: 60px; 23 | } 24 | 25 | .job:first-child { 26 | margin-left: 0; 27 | } 28 | 29 | .job[data-state="blue"] { 30 | background-color: green; 31 | } 32 | 33 | .job[data-state="red"] { 34 | background-color: red; 35 | } 36 | 37 | .job[data-state="grey"] { 38 | background-color: grey; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /widgets/jenkins_queue/jenkins_queue.coffee: -------------------------------------------------------------------------------- 1 | class Dashing.Jenkins_Queue extends Dashing.Widget 2 | -------------------------------------------------------------------------------- /widgets/jenkins_queue/jenkins_queue.html: -------------------------------------------------------------------------------- 1 |

2 | 3 |
    4 |
  1. 5 | 6 |
  2. 7 |
-------------------------------------------------------------------------------- /widgets/jenkins_queue/jenkins_queue.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Widget-jenkins-queue styles 3 | // ---------------------------------------------------------------------------- 4 | .widget-jenkins-queue { 5 | background-color: lightgrey; 6 | 7 | .title { 8 | color: black; 9 | } 10 | 11 | .queue { 12 | counter-reset: item 13 | } 14 | 15 | .job { 16 | border-radius: 8px; 17 | float: left; 18 | list-style-type: none; 19 | margin: 4px; 20 | padding: 6px; 21 | } 22 | 23 | .job:before { 24 | content: counter(item) ". "; 25 | counter-increment: item; 26 | color: black; 27 | } 28 | 29 | .job[data-state="blue"] { 30 | background-color: green; 31 | } 32 | 33 | .job[data-state="red"] { 34 | background-color: red; 35 | } 36 | 37 | .job[data-state="grey"] { 38 | background-color: grey; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /widgets/jenkins_weather/jenkins_weather.coffee: -------------------------------------------------------------------------------- 1 | class Dashing.Jenkins_Weather extends Dashing.Widget 2 | -------------------------------------------------------------------------------- /widgets/jenkins_weather/jenkins_weather.html: -------------------------------------------------------------------------------- 1 |

2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 |
-------------------------------------------------------------------------------- /widgets/jenkins_weather/jenkins_weather.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Widget-jenkins-weather styles 3 | // ---------------------------------------------------------------------------- 4 | .widget-jenkins-weather { 5 | background-color: lightgrey; 6 | 7 | .title { 8 | color: black; 9 | } 10 | 11 | .ranges { 12 | margin: 0 auto 13 | } 14 | 15 | .range { 16 | background-color: grey; 17 | background-position: 4px 4px; 18 | background-repeat: no-repeat; 19 | border-radius: 24px; 20 | display: inline-block; 21 | height: 40px; 22 | line-height: 40px; 23 | margin-bottom: 12px; 24 | margin-left: 12px; 25 | text-align: right; 26 | padding-right: 12px; 27 | width: 60px; 28 | } 29 | 30 | .range:first-child { 31 | margin-left: 0; 32 | } 33 | 34 | .range[data-range="80plus"] { 35 | background-image: url("/assets/jenkins_weather/health-80plus.gif") 36 | } 37 | 38 | .range[data-range="60to79"] { 39 | background-image: url("/assets/jenkins_weather/health-60to79.gif") 40 | } 41 | 42 | .range[data-range="40to59"] { 43 | background-image: url("/assets/jenkins_weather/health-40to59.gif") 44 | } 45 | 46 | .range[data-range="20to39"] { 47 | background-image: url("/assets/jenkins_weather/health-20to39.gif") 48 | } 49 | 50 | .range[data-range="00to19"] { 51 | background-image: url("/assets/jenkins_weather/health-00to19.gif") 52 | } 53 | } 54 | --------------------------------------------------------------------------------