Skip to content

Instantly share code, notes, and snippets.

@makepanic
Last active May 29, 2018 14:34
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save makepanic/a87e96dfd21583b96eb3 to your computer and use it in GitHub Desktop.
Save makepanic/a87e96dfd21583b96eb3 to your computer and use it in GitHub Desktop.
Teamcity Widget for Dashing

##Preview

Description

Simple Dashing widget (and associated job) to display teamcity build info. Based on DefactoSoftware/dashing-widgets/teamcity. This widgets displays all children of a teamcity project. Each child project displays the latest build in each branch that is registered in teamcity. If a build has the state FAILURE the widgets flashes in red (see gif).

##Dependencies

teamcity-ruby-client

Add it to dashing's gemfile:

gem 'teamcity-ruby-client'

and run bundle install. Everything should work now :)

##Usage

  • Copy the team_city.coffee, team_city.html and team_city.scss files from this gist to the widgets/team_city.
  • Copy teamcity.rb to jobs/
  • Copy teamcity.yml to config/

To include the widget in a dashboard, add the following snippet to the dashboard layout file:

<li data-row="1" data-col="1" data-sizex="1" data-sizey="3">
  <div data-id="template-data-id1" data-image="/project-icon.png" data-view="TeamCity" data-unordered="true" data-title="TITLE"></div>
</li>

##Settings

Modify the teamcity.yml config file to work in your teamcity setup. See https://github.com/jperry/teamcity-ruby-client for more information on the teamcity api auth.

The MIT License (MIT)

Copyright (c) 2014 Christian

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.

class Dashing.TeamCity extends Dashing.Widget
<h1 class="title" data-bind="title"></h1>
<img data-bind-src="image | prepend '/assets'" data-bind-width="width"/>
<ul class="list-nostyle">
<li data-foreach-item="items">
<ul>
<li class="project" data-foreach-item="item.projects">
<h3 class="project-name" data-bind="item.name"></h3>
<ul class="builds" >
<li data-foreach-item="item.build_types"
data-bind-class="item.last_build.status">
<p>
<strong class="build-type-name" data-bind="item.name"></strong>
#<span data-bind="item.last_build.number"></span>
</p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p class="more-info" data-bind="moreinfo"></p>
<p class="updated-at" data-bind="updatedAtMessage"></p>
// ----------------------------------------------------------------------------
// Sass declarations
// ----------------------------------------------------------------------------
$value-color: #FFF;
$background-error-color: #E64040;
$background-passed-color: rgba(111, 214, 85, 0.66);
$background-started-color: #E7D100;
$background-no-status-color: rgba(115, 115, 115, 0.73);
$title-color: rgba(255, 255, 255, 1);
$label-color: rgba(255, 255, 255, 0.7);
$moreinfo-color: rgba(255, 255, 255, 0.7);
$background: #444;
@mixin keyframes($animation-name) {
@-webkit-keyframes #{$animation-name} {
@content;
}
@-moz-keyframes #{$animation-name} {
@content;
}
@-ms-keyframes #{$animation-name} {
@content;
}
@-o-keyframes #{$animation-name} {
@content;
}
@keyframes #{$animation-name} {
@content;
}
}
@mixin animation($args...) {
-webkit-animation: $args;
-moz-animation: $args;
-ms-animation: $args;
-o-animation: $args;
animation: $args;
}
@include keyframes(blink) {
0% { opacity: 1.0; }
50% { opacity: 0.0; }
100% { opacity: 1.0; }
}
// ----------------------------------------------------------------------------
// Widget-list styles
// ----------------------------------------------------------------------------
.widget-team-city{
background-color: $background;
vertical-align: top !important;
h1{
text-transform: uppercase;
}
h3{
font-size: 20px;
margin: 5px 0;
font-weight: lighter;
}
img {
position: absolute;
top: 30px;
left: 50px;
margin: 0 auto;
opacity: 0.05;
max-width: 80%;
max-height: 80%;
}
.title, strong{
color: $title-color;
}
ul {
margin: 0;
text-align: left;
list-style: none;
color: $label-color;
}
li {
margin-bottom: 5px;
}
.project{
.project-name{
}
}
.builds{
p{
padding: 10px;
}
li{
background-color: $background-no-status-color;
}
margin-bottom: 5px;
.SUCCESS{
background-color: $background-passed-color;
}
.FAILURE{
background-color: $background-error-color;
@include animation(blink 1s step-start 0s infinite);
}
.build-type-name{
font-size: 20px;
}
}
.updated-at {
color: rgba(0, 0, 0, 0.3);
}
.more-info {
color: $moreinfo-color;
}
}
require 'teamcity'
def update_builds(project_id)
builds = []
projects = []
TeamCity.project(:id => project_id).projects.project.each do |project|
build_types = []
TeamCity.project_buildtypes(:id => project.id).each do |build_type|
build_types.push({
:id => build_type.id,
:name => build_type.name
})
end
build_types.each do |build_type_obj|
build_type_obj_builds = TeamCity.builds(:count => 1, :buildType => build_type_obj[:id])
unless build_type_obj_builds.nil?
last_build = build_type_obj_builds.first
build_type_obj['last_build'] = {
:id => last_build.id,
:number => last_build.number,
:state => last_build.state,
:status => last_build.status
}
end
end
projects.push({
:name => project.name,
:id => project.id,
:description => project.description,
:build_types => build_types
})
end
build_info = {
:title => project_id,
:projects => projects
}
builds << build_info
builds
end
config_file = File.dirname(File.expand_path(__FILE__)) + '/../config/teamcity.yml'
config = YAML::load(File.open(config_file))
TeamCity.configure do |c|
c.endpoint = config['api_url']
c.http_user = config['http_user']
c.http_password = config['http_password']
end
SCHEDULER.every '33s', :first_in => '1s' do
if config['repositories'].nil?
puts 'No TeamCity repositories found :('
else
config['repositories'].each do |data_id, build_id|
send_event(data_id, { :items => update_builds(build_id)})
end
end
end
api_url: "https://my-teamcity-installation.tld/httpAuth/app/rest/"
http_user: "my-http-user"
http_password: "my-http-password"
repositories:
template-data-id1: project1Id
template-data-id2: project2Id
@shushiej
Copy link

Nice Widget. Are you able to configure multiple api_url's? because Im trying to display this widget for two teamcity servers, but for some reason it only displays one of them. I do have admin access to both of them but it still doesn't work. I have two ruby files under the jobs folder that configures the two api_urls and sends them to two different dashboards. However when i type dashing start,
It keeps saying:
scheduler caught exception:
757: unexpected token at 'Error has occurred during request processing (Not Found).
Error: jetbrains.buildServer.server.rest.errors.NotFoundException: No project found by locator 'id:TEAMCITY_BRANCH_ID'. Project cannot be found by external id

That error is only send when i start dashing.

But if i run the ruby files seperately by going in the jobs folder: cd/jobs ruby teamcity1.rb it gives me what i need.

Any ideas on this?

@carlosbvio
Copy link

Hey, great work. I'm having some trouble getting the projects to display correctly. I have several projects with nested projects but looks like although it is picking up the configuration when it parses it fails and it results in just showing a particular project regardless of whether it's in the config. Noticed when the job runs it produces an error:

scheduler caught exception:
undefined method each' for nil:NilClass E:/Ruby/Ruby21-x64/dashmon/jobs/teamcity.rb:7:inupdate_builds'
E:/Ruby/Ruby21-x64/dashmon/jobs/teamcity.rb:63:in block (2 levels) in <top (required)>' E:/Ruby/Ruby21-x64/dashmon/jobs/teamcity.rb:62:ineach'
E:/Ruby/Ruby21-x64/dashmon/jobs/teamcity.rb:62:in block in <top (required)>' E:/Ruby/Ruby21-x64/lib/ruby/gems/2.1.0/gems/rufus-scheduler-2.0.24/lib/rufus/sc/jobs.rb:230:incall'
E:/Ruby/Ruby21-x64/lib/ruby/gems/2.1.0/gems/rufus-scheduler-2.0.24/lib/rufus/sc/
jobs.rb:230:in trigger_block' E:/Ruby/Ruby21-x64/lib/ruby/gems/2.1.0/gems/rufus-scheduler-2.0.24/lib/rufus/sc/jobs.rb:204:inblock in trigger'
E:/Ruby/Ruby21-x64/lib/ruby/gems/2.1.0/gems/rufus-scheduler-2.0.24/lib/rufus/sc/scheduler.rb:430:in call' E:/Ruby/Ruby21-x64/lib/ruby/gems/2.1.0/gems/rufus-scheduler-2.0.24/lib/rufus/sc/scheduler.rb:430:inblock in trigger_job'

The error is in line:

TeamCity.project(:id => project_id).projects.project.each do |project|

Seems like it's not parsing the results from TeamCity gem correctly and getting confused.

Any suggestions?

@antonrubtsov
Copy link

TeamCity.project(:id => project_id).projects.project.each do |project|
In this code project must have subproject

@nehasood9
Copy link

is it possible to display test results count along with the build status eg. tests passed/failed/ignored

@nareojha
Copy link

I am new bee. please help.
I did not have config folder in my sweet_dashboard_project folder so I have created the one and put the teamcity.yml file in there. I was not sure what needs to put under repositories so I provided 1 team city project ID in there.
I am getting the below error. Please let me know your valuable inputs to resolve this.

P.S. _ i am very new so i suspect I might have also done something wrong in the team city while creating a project.

================================================================================
scheduler caught exception:
undefined method id' for nil:NilClass C:/Users/NarendraOjha/sweet_dashboard_project/jobs/teamcity.rb:23:in block (2 levels) in update_builds'
C:/Users/NarendraOjha/sweet_dashboard_project/jobs/teamcity.rb:17:in each' C:/Users/NarendraOjha/sweet_dashboard_project/jobs/teamcity.rb:17:in block in update_builds'
C:/Users/NarendraOjha/sweet_dashboard_project/jobs/teamcity.rb:7:in each' C:/Users/NarendraOjha/sweet_dashboard_project/jobs/teamcity.rb:7:in update_builds'
C:/Users/NarendraOjha/sweet_dashboard_project/jobs/teamcity.rb:63:in block (2 levels) in <top (required)>' C:/Users/NarendraOjha/sweet_dashboard_project/jobs/teamcity.rb:62:in each'
C:/Users/NarendraOjha/sweet_dashboard_project/jobs/teamcity.rb:62:in block in <top (required)>' C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/rufus-scheduler-2.0.24/lib/rufus/sc/jobs.rb:230:in trigger_block'
C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/rufus-scheduler-2.0.24/lib/rufus/sc/jobs.rb:204:in block in trigger' C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/rufus-scheduler-2.0.24/lib/rufus/sc/scheduler.rb:430:in block in trigger_job'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment