teamcity_service.rb 4.04 KB
Newer Older
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
1 2 3 4
# == Schema Information
#
# Table name: services
#
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
5 6 7 8 9 10 11 12 13
#  id                    :integer          not null, primary key
#  type                  :string(255)
#  title                 :string(255)
#  project_id            :integer
#  created_at            :datetime
#  updated_at            :datetime
#  active                :boolean          default(FALSE), not null
#  properties            :text
#  template              :boolean          default(FALSE)
14 15 16 17
#  push_events           :boolean          default(TRUE)
#  issues_events         :boolean          default(TRUE)
#  merge_requests_events :boolean          default(TRUE)
#  tag_push_events       :boolean          default(TRUE)
18
#  note_events           :boolean          default(TRUE), not null
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
19 20
#

21 22 23 24 25
class TeamcityService < CiService
  include HTTParty

  prop_accessor :teamcity_url, :build_type, :username, :password

26 27
  validates :teamcity_url,
    presence: true,
28
    format: { with: /\A#{URI.regexp}\z/ }, if: :activated?
29
  validates :build_type, presence: true, if: :activated?
30 31 32 33 34 35
  validates :username,
    presence: true,
    if: ->(service) { service.password? }, if: :activated?
  validates :password,
    presence: true,
    if: ->(service) { service.username? }, if: :activated?
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64

  attr_accessor :response

  after_save :compose_service_hook, if: :activated?

  def compose_service_hook
    hook = service_hook || build_service_hook
    hook.save
  end

  def title
    'JetBrains TeamCity CI'
  end

  def description
    'A continuous integration and build server'
  end

  def help
    'The build configuration in Teamcity must use the build format '\
    'number %build.vcs.number% '\
    'you will also want to configure monitoring of all branches so merge '\
    'requests build, that setting is in the vsc root advanced settings.'
  end

  def to_param
    'teamcity'
  end

65 66 67 68
  def supported_events
    %w(push)
  end

69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
  def fields
    [
      { type: 'text', name: 'teamcity_url',
        placeholder: 'TeamCity root URL like https://teamcity.example.com' },
      { type: 'text', name: 'build_type',
        placeholder: 'Build configuration ID' },
      { type: 'text', name: 'username',
        placeholder: 'A user with permissions to trigger a manual build' },
      { type: 'password', name: 'password' },
    ]
  end

  def build_info(sha)
    url = URI.parse("#{teamcity_url}/httpAuth/app/rest/builds/"\
                    "branch:unspecified:any,number:#{sha}")
    auth = {
      username: username,
      password: password,
    }
    @response = HTTParty.get("#{url}", verify: false, basic_auth: auth)
  end

Valery Sizov's avatar
Valery Sizov committed
91
  def build_page(sha, ref)
92 93 94 95 96 97 98 99 100 101 102 103 104 105
    build_info(sha) if @response.nil? || !@response.code

    if @response.code != 200
      # If actual build link can't be determined,
      # send user to build summary page.
      "#{teamcity_url}/viewLog.html?buildTypeId=#{build_type}"
    else
      # If actual build link is available, go to build result page.
      built_id = @response['build']['id']
      "#{teamcity_url}/viewLog.html?buildId=#{built_id}"\
      "&buildTypeId=#{build_type}"
    end
  end

Valery Sizov's avatar
Valery Sizov committed
106
  def commit_status(sha, ref)
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
    build_info(sha) if @response.nil? || !@response.code
    return :error unless @response.code == 200 || @response.code == 404

    status = if @response.code == 404
               'Pending'
             else
               @response['build']['status']
             end

    if status.include?('SUCCESS')
      'success'
    elsif status.include?('FAILURE')
      'failed'
    elsif status.include?('Pending')
      'pending'
    else
      :error
    end
  end

127
  def execute(data)
128
    return unless supported_events.include?(data[:object_kind])
129

130 131 132 133 134
    auth = {
      username: username,
      password: password,
    }

135
    branch = Gitlab::Git.ref_name(data[:ref])
136 137 138 139 140 141 142 143 144 145

    self.class.post("#{teamcity_url}/httpAuth/app/rest/buildQueue",
                    body: "<build branchName=\"#{branch}\">"\
                          "<buildType id=\"#{build_type}\"/>"\
                          '</build>',
                    headers: { 'Content-type' => 'application/xml' },
                    basic_auth: auth
        )
  end
end