Skip to content
This repository has been archived by the owner on Mar 24, 2022. It is now read-only.

Commit

Permalink
TeamCity 7 Builds with dependencies #25732377
Browse files Browse the repository at this point in the history
  • Loading branch information
Dirk Kelly & Matt Parker committed Mar 28, 2012
1 parent a5b673a commit 0eba94d
Show file tree
Hide file tree
Showing 13 changed files with 951 additions and 84 deletions.
8 changes: 4 additions & 4 deletions app/models/project_status.rb
Expand Up @@ -21,16 +21,16 @@ def match?(status)
!status.online
end
end

def in_words
if self.online
if self.success
return SUCCESS
SUCCESS
else
return FAILURE
FAILURE
end
else
return OFFLINE
OFFLINE
end
end

Expand Down
85 changes: 85 additions & 0 deletions app/models/team_city_build.rb
@@ -0,0 +1,85 @@
class TeamCityBuild < TeamCityRestProject

attr_writer :build_status_fetcher, :build_type_fetcher

def build_id
feed_url.match(/id:([^)]*)/)[1]
end

def online?
status.online? && children.all?(&:online?)
end

def success?
green? && children.all?(&:green?)
end

def red?
!green? || children.any?(&:red?)
end

def green?
super && children.all?(&:green?)
end

def building?
build_status.building? || children.any?(&:building?)
end

def status
latest_status || live_status
end

def live_status
ProjectStatus.new.tap do |s|
s.online = build_status.online?
s.success = build_status.green?
s.published_at = publish_date
end
end

def parse_project_status(*)
live_status
end

def parse_building_status(*)
build_status
end

def publish_date
date = build_status.publish_date
children.each do |child|
if child.publish_date > date
date = child.publish_date
end
end
date
end

def children
TeamCityChildBuilder.parse(self, build_type_fetcher.call)
rescue Net::HTTPError
[]
end

private

def build_status
@build_status ||= build_status_fetcher.call
end

def build_status_fetcher
@build_status_fetcher ||= proc { TeamCityBuildStatus.new self }
end

def build_type_fetcher
@build_type_fetcher ||= proc {
UrlRetriever.retrieve_content_at(build_type_url, auth_username, auth_password)
}
end

def build_type_url
uri = URI(feed_url)
"#{uri.scheme}://#{uri.host}:#{uri.port}/httpAuth/app/rest/buildTypes/id:#{build_id}"
end
end
58 changes: 58 additions & 0 deletions app/models/team_city_build_status.rb
@@ -0,0 +1,58 @@
class TeamCityBuildStatus
attr_reader :build

def initialize(build)
@build = build
end

def online?
fetch
@online
end

def publish_date
if online?
Time.parse build_response['startDate']
else
nil
end
end

def green?
online? && @green
end

def red?
online? && !green? && !building?
end

def building?
online? && @building
end

private
def fetch
if build_response
@building = build_response["running"] == "true"
@green = build_response["status"] == "SUCCESS"
@online = true
else
@online = false
end
rescue Net::HTTPError
@online = false
end

def build_response
@build_response || fetch_build_response
end

def fetch_build_response
response = UrlRetriever.retrieve_content_at(build.feed_url, build.auth_username, build.auth_password)
response = Hash.from_xml response

@build_response = response['builds']['build']
@build_response = @build_response.first if @build_response.kind_of? Array
@build_response
end
end
48 changes: 24 additions & 24 deletions app/views/dashboards/_project.html.erb
@@ -1,37 +1,37 @@
<%
if project.red?
background = 'redbox'
background = 'redbox-aggregate' if project.is_a? AggregateProject
if project.building?
status_class = 'project_status red building'
else
status_class = 'project_status red'
end
elsif project.green?
background = 'greenbox'
background = 'greenbox-aggregate' if project.is_a? AggregateProject
if project.building?
status_class = 'project_status green building'
else
status_class = 'project_status green'
end
else
background = 'bluebox'
background = 'bluebox-aggregate' if project.is_a? AggregateProject
status_class = 'project_status offline'
end
%>
if !project.online?
background = 'bluebox'
background = 'bluebox-aggregate' if project.is_a? AggregateProject
status_class = 'project_status offline'
elsif project.red?
background = 'redbox'
background = 'redbox-aggregate' if project.is_a? AggregateProject
if project.building?
status_class = 'project_status red building'
else
status_class = 'project_status red'
end
elsif project.green?
background = 'greenbox'
background = 'greenbox-aggregate' if project.is_a? AggregateProject
if project.building?
status_class = 'project_status green building'
else
status_class = 'project_status green'
end
end
-%>

<div class="box <%= background %>" project_id="<%= project.id %>" >
<div class="project_name <%= project.green? ? 'success' : 'error' %>">
<h1><%= link_to(h(project.name), project.url) %></h1>
</div>

<% relative_status_messages_for(project).each do |css_class, message| %>
<% relative_status_messages_for(project).each do |css_class, message| -%>
<div class="<%= css_class %>">
<%= message %>
</div>
<% end %>
<% end -%>

<div class="project_history">
<%= render :partial=>'dashboards/history', :locals=>{:project=>project} %>
Expand Down
3 changes: 2 additions & 1 deletion app/views/projects/_form.html.erb
Expand Up @@ -14,7 +14,8 @@
<%= f.select :type, [[CruiseControlProject.name.titleize, CruiseControlProject.name],
[HudsonProject.name.titleize, HudsonProject.name],
[TeamCityRestProject.name.titleize, TeamCityRestProject.name],
[TeamCityProject.name.titleize, TeamCityProject.name]] %>
[TeamCityProject.name.titleize, TeamCityProject.name],
["#{TeamCityBuild.name.titleize} (TC 7+)", TeamCityBuild.name]] %>
</p>

<p>
Expand Down
2 changes: 1 addition & 1 deletion lib/status_fetcher.rb
@@ -1,5 +1,5 @@
class StatusFetcher
def initialize(url_retriever = UrlRetriever.new)
def initialize(url_retriever = UrlRetriever)
@url_retriever = url_retriever
end

Expand Down
43 changes: 43 additions & 0 deletions lib/team_city_child_builder.rb
@@ -0,0 +1,43 @@
class TeamCityChildBuilder

def self.parse(parent, content)
new(parent, content).build
end

def initialize(parent, content)
@parent = parent
@content = content
end

def build
dependencies
end

private

def build_project(build_id)
TeamCityBuild.new(
:feed_url => @parent.feed_url.gsub(@parent.build_id, build_id),
:auth_username => @parent.auth_username,
:auth_password => @parent.auth_password
)
end

def dependencies
parsed_content.xpath("//snapshot-dependency").collect {|d| d.attributes["id"].to_s }.map do |id|
build_project id
end
end

def parsed_content
@parsed_content ||= Nokogiri::XML(@content)
end

def base_url
@base_url ||= "#{root_url.scheme}://#{root_url.host}:#{root_url.port}"
end

def root_url
@root_url ||= URI(parsed_content.xpath("//buildType").first.attributes["webUrl"].to_s)
end
end
15 changes: 11 additions & 4 deletions lib/url_retriever.rb
Expand Up @@ -2,7 +2,9 @@
require 'net/https'
require 'httpi'

class UrlRetriever
module UrlRetriever
extend self

def retrieve_content_at(url, username = nil, password = nil)
if username.present? && password.present?
response = do_get(url) { |get| get.basic_auth(username, password) }
Expand All @@ -12,8 +14,11 @@ def retrieve_content_at(url, username = nil, password = nil)
else
response = do_get(url)
end
raise Net::HTTPError.new("Error: got non-200 return code #{response.code} from #{url}, body = '#{response.body}'", nil) unless response.code.to_i == 200
response.body
if response.code.to_i == 200
response.body
else
raise Net::HTTPError.new("Error: got non-200 return code #{response.code} from #{url}, body = '#{response.body}'", nil)
end
end

private
Expand All @@ -32,8 +37,10 @@ def do_get(url)

yield(get) if block_given?

res = http(uri).start { |web| web.request(get)}
res = http(uri).start { |web| web.request(get) }
res
rescue Errno::ECONNREFUSED
raise Net::HTTPError.new("Error: Could not connect to the remote server", nil)
end

def digest_auth(get, response, username, password)
Expand Down
42 changes: 42 additions & 0 deletions spec/lib/team_city_child_builder_spec.rb
@@ -0,0 +1,42 @@
require 'spec_helper'

describe TeamCityChildBuilder do
let(:feed_url) { "http://localhost:8111/app/rest/builds?locator=running:all,buildType:(id:bt2)" }
let(:parent) { TeamCityBuild.new(:feed_url => feed_url, :auth_username => "john", :auth_password => "secret") }
let(:parsed) { TeamCityChildBuilder.parse(parent, content) }
let(:content) do
<<-XML
<buildType id="bt2" webUrl="http://localhost:8111/viewType.html?buildTypeId=bt2">
<snapshot-dependencies>
<snapshot-dependency id="bt3" type="snapshot_dependency" />
<snapshot-dependency id="bt5" type="snapshot_dependency" />
<snapshot-dependency id="bt9" type="snapshot_dependency" />
</snapshot-dependencies>
</buildType>
XML
end

it "assigns the correct feed_url to all children builds" do
[3,5,9].each do |i|
parsed.collect(&:feed_url).should(
include("http://localhost:8111/app/rest/builds?locator=running:all,buildType:(id:bt#{i})")
)
end
end

it "assigns the correct auth_username to all children builds" do
[3,5,9].each do |i|
parsed.collect(&:auth_username).should(
include("john")
)
end
end

it "assigns the correct auth_username to all children builds" do
[3,5,9].each do |i|
parsed.collect(&:auth_password).should(
include("secret")
)
end
end
end

0 comments on commit 0eba94d

Please sign in to comment.