ciquantum 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +21 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +18 -0
- data/Rakefile +34 -0
- data/bin/ciquantum +50 -0
- data/ciquantum.gemspec +30 -0
- data/examples/build-failed +22 -0
- data/examples/build-worked +22 -0
- data/examples/cijoe.ru +15 -0
- data/examples/cijoed +53 -0
- data/lib/ciquantum.rb +227 -0
- data/lib/ciquantum/adapter.rb +11 -0
- data/lib/ciquantum/build.rb +67 -0
- data/lib/ciquantum/commit.rb +28 -0
- data/lib/ciquantum/config.rb +43 -0
- data/lib/ciquantum/public/favicon.ico +0 -0
- data/lib/ciquantum/public/octocat.png +0 -0
- data/lib/ciquantum/public/screen.css +213 -0
- data/lib/ciquantum/queue.rb +55 -0
- data/lib/ciquantum/server.rb +117 -0
- data/lib/ciquantum/unfuddle_adapter.rb +19 -0
- data/lib/ciquantum/version.rb +3 -0
- data/lib/ciquantum/views/json.erb +16 -0
- data/lib/ciquantum/views/template.erb +89 -0
- data/test/fixtures/payload.json +52 -0
- data/test/helper.rb +48 -0
- data/test/test_ciquantum_queue.rb +28 -0
- data/test/test_hooks.rb +81 -0
- metadata +209 -0
| @@ -0,0 +1,67 @@ | |
| 1 | 
            +
            require 'yaml'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            class CIQuantum
         | 
| 4 | 
            +
              class Build < Struct.new(:project_path, :user, :project, :started_at, :finished_at, :sha, :status, :output, :pid, :branch)
         | 
| 5 | 
            +
                def initialize(*args)
         | 
| 6 | 
            +
                  super
         | 
| 7 | 
            +
                  self.started_at ||= Time.now
         | 
| 8 | 
            +
                end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                def status
         | 
| 11 | 
            +
                  return super if started_at && finished_at
         | 
| 12 | 
            +
                  :building
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                def failed?
         | 
| 16 | 
            +
                  status == :failed
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                def worked?
         | 
| 20 | 
            +
                  status == :worked
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                def building?
         | 
| 24 | 
            +
                  status == :building
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                def duration
         | 
| 28 | 
            +
                  return if building?
         | 
| 29 | 
            +
                  finished_at - started_at
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                def short_sha
         | 
| 33 | 
            +
                  if sha
         | 
| 34 | 
            +
                    sha[0,7]
         | 
| 35 | 
            +
                  else
         | 
| 36 | 
            +
                    "<unknown>"
         | 
| 37 | 
            +
                  end
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                def clean_output
         | 
| 41 | 
            +
                  output.gsub(/\e\[.+?m/, '').strip
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                def env_output
         | 
| 45 | 
            +
                  out = clean_output
         | 
| 46 | 
            +
                  out.size > 100_000 ? out[-100_000,100_000] : out
         | 
| 47 | 
            +
                end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                def commit
         | 
| 50 | 
            +
                  return if sha.nil?
         | 
| 51 | 
            +
                  @commit ||= Commit.new(sha, user, project, project_path)
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                def dump(file)
         | 
| 55 | 
            +
                  config = [user, project, started_at, finished_at, sha, status, output, pid, branch]
         | 
| 56 | 
            +
                  data = YAML.dump(config)
         | 
| 57 | 
            +
                  File.open(file, 'wb') { |io| io.write(data) }
         | 
| 58 | 
            +
                end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                def self.load(file, project_path)
         | 
| 61 | 
            +
                  if File.exist?(file)
         | 
| 62 | 
            +
                    config = YAML.load(File.read(file)).unshift(project_path)
         | 
| 63 | 
            +
                    new *config
         | 
| 64 | 
            +
                  end
         | 
| 65 | 
            +
                end
         | 
| 66 | 
            +
              end
         | 
| 67 | 
            +
            end
         | 
| @@ -0,0 +1,28 @@ | |
| 1 | 
            +
            class CIQuantum
         | 
| 2 | 
            +
              class Commit < Struct.new(:sha, :user, :project, :project_path)
         | 
| 3 | 
            +
                def url
         | 
| 4 | 
            +
                  Adapter::default_adapter.commit_url self
         | 
| 5 | 
            +
                end
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def author
         | 
| 8 | 
            +
                  raw_commit_lines.grep(/Author:/).first.split(':', 2)[-1]
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                def committed_at
         | 
| 12 | 
            +
                  raw_commit_lines.grep(/Date:/).first.split(':', 2)[-1]
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                def message
         | 
| 16 | 
            +
                  raw_commit.split("\n\n", 3)[1].to_s.strip
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                def raw_commit
         | 
| 20 | 
            +
                  @raw_commit ||= `cd #{project_path} && git show #{sha}`.chomp
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                def raw_commit_lines
         | 
| 24 | 
            +
                  @raw_commit_lines ||= raw_commit.split("\n")
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
              end
         | 
| 28 | 
            +
            end
         | 
| @@ -0,0 +1,43 @@ | |
| 1 | 
            +
            class CIQuantum
         | 
| 2 | 
            +
              class Config
         | 
| 3 | 
            +
                def self.method_missing(command, *args)
         | 
| 4 | 
            +
                  new(command, *args)
         | 
| 5 | 
            +
                end
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def initialize(command, project_path = nil, parent = nil)
         | 
| 8 | 
            +
                  @command  = command
         | 
| 9 | 
            +
                  @parent   = parent
         | 
| 10 | 
            +
                  @project_path = project_path || File.join(File.dirname(__FILE__), '../../')
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                def method_missing(command, *args)
         | 
| 14 | 
            +
                  Config.new(command, @project_path, self)
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                def to_s
         | 
| 18 | 
            +
                  git_command = "cd #{@project_path} && git config #{config_string}"
         | 
| 19 | 
            +
                  result = `#{git_command} 2>&1`.chomp
         | 
| 20 | 
            +
                  process_status = $?
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  if successful_command?(process_status) || config_command_with_empty_value?(result,process_status)
         | 
| 23 | 
            +
                    return result
         | 
| 24 | 
            +
                  else
         | 
| 25 | 
            +
                    return ""
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                def config_string
         | 
| 30 | 
            +
                  @parent ? "#{@parent.config_string}.#{@command}" : @command
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                private
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                def successful_command?(process_status)
         | 
| 36 | 
            +
                  process_status.exitstatus.to_i == 0
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                def config_command_with_empty_value?(result, process_status)
         | 
| 40 | 
            +
                  process_status.exitstatus.to_i == 1 && result.empty?
         | 
| 41 | 
            +
                end
         | 
| 42 | 
            +
              end
         | 
| 43 | 
            +
            end
         | 
| Binary file | 
| Binary file | 
| @@ -0,0 +1,213 @@ | |
| 1 | 
            +
            /*****************************************************************************/
         | 
| 2 | 
            +
            /*
         | 
| 3 | 
            +
            /* Common
         | 
| 4 | 
            +
            /*
         | 
| 5 | 
            +
            /*****************************************************************************/
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            /* Global Reset */
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            * {
         | 
| 10 | 
            +
              margin: 0;
         | 
| 11 | 
            +
              padding: 0;
         | 
| 12 | 
            +
            }
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            html, body {
         | 
| 15 | 
            +
              height: 100%;
         | 
| 16 | 
            +
            }
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            body {
         | 
| 19 | 
            +
              background-color: white;
         | 
| 20 | 
            +
              font: 13.34px helvetica, arial, clean, sans-serif;
         | 
| 21 | 
            +
              *font-size: small;
         | 
| 22 | 
            +
              text-align: center;
         | 
| 23 | 
            +
            }
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            h1, h2, h3, h4, h5, h6 {
         | 
| 26 | 
            +
              font-size: 100%;
         | 
| 27 | 
            +
            }
         | 
| 28 | 
            +
             | 
| 29 | 
            +
            h1 {
         | 
| 30 | 
            +
              margin-bottom: 1em;
         | 
| 31 | 
            +
            }
         | 
| 32 | 
            +
             | 
| 33 | 
            +
            h1 a {
         | 
| 34 | 
            +
              text-decoration: none;
         | 
| 35 | 
            +
              color: #000;
         | 
| 36 | 
            +
            }
         | 
| 37 | 
            +
             | 
| 38 | 
            +
            .failed, .color31 {
         | 
| 39 | 
            +
              color: red !important;
         | 
| 40 | 
            +
            }
         | 
| 41 | 
            +
             | 
| 42 | 
            +
            .worked, .color32 {
         | 
| 43 | 
            +
              color: green !important;
         | 
| 44 | 
            +
            }
         | 
| 45 | 
            +
             | 
| 46 | 
            +
            .errored, .color33 {
         | 
| 47 | 
            +
              color: yellow !important;
         | 
| 48 | 
            +
            }
         | 
| 49 | 
            +
             | 
| 50 | 
            +
            p {
         | 
| 51 | 
            +
              margin: 1em 0;
         | 
| 52 | 
            +
            }
         | 
| 53 | 
            +
             | 
| 54 | 
            +
            a {
         | 
| 55 | 
            +
              color: #00a;
         | 
| 56 | 
            +
            }
         | 
| 57 | 
            +
             | 
| 58 | 
            +
            a:hover {
         | 
| 59 | 
            +
              color: black;
         | 
| 60 | 
            +
            }
         | 
| 61 | 
            +
             | 
| 62 | 
            +
            a:visited {
         | 
| 63 | 
            +
              color: #a0a;
         | 
| 64 | 
            +
            }
         | 
| 65 | 
            +
             | 
| 66 | 
            +
            table {
         | 
| 67 | 
            +
              font-size: inherit;
         | 
| 68 | 
            +
              font: 100%;
         | 
| 69 | 
            +
            }
         | 
| 70 | 
            +
             | 
| 71 | 
            +
            /*****************************************************************************/
         | 
| 72 | 
            +
            /*
         | 
| 73 | 
            +
            /* Home
         | 
| 74 | 
            +
            /*
         | 
| 75 | 
            +
            /*****************************************************************************/
         | 
| 76 | 
            +
             | 
| 77 | 
            +
            ul.posts {
         | 
| 78 | 
            +
              list-style-type: none;
         | 
| 79 | 
            +
              margin-bottom: 2em;
         | 
| 80 | 
            +
            }
         | 
| 81 | 
            +
             | 
| 82 | 
            +
              ul.posts li {
         | 
| 83 | 
            +
                line-height: 1.75em;
         | 
| 84 | 
            +
              }
         | 
| 85 | 
            +
             
         | 
| 86 | 
            +
              ul.posts .date,
         | 
| 87 | 
            +
              ul.posts .duration {
         | 
| 88 | 
            +
                color: #aaa;
         | 
| 89 | 
            +
                font-family: Monaco, "Courier New", monospace;
         | 
| 90 | 
            +
                font-size: 80%;
         | 
| 91 | 
            +
              }
         | 
| 92 | 
            +
             | 
| 93 | 
            +
            /*****************************************************************************/
         | 
| 94 | 
            +
            /*
         | 
| 95 | 
            +
            /* Site
         | 
| 96 | 
            +
            /*
         | 
| 97 | 
            +
            /*****************************************************************************/
         | 
| 98 | 
            +
             | 
| 99 | 
            +
            .site {
         | 
| 100 | 
            +
              font-size: 110%;
         | 
| 101 | 
            +
              text-align: justify;
         | 
| 102 | 
            +
              width: 80%;
         | 
| 103 | 
            +
              margin: 3em auto 2em auto;
         | 
| 104 | 
            +
              line-height: 1.5em;
         | 
| 105 | 
            +
            }
         | 
| 106 | 
            +
             | 
| 107 | 
            +
            .title {
         | 
| 108 | 
            +
              color: #a00;
         | 
| 109 | 
            +
              font-weight: bold;
         | 
| 110 | 
            +
              margin-bottom: 2em;
         | 
| 111 | 
            +
            }
         | 
| 112 | 
            +
             | 
| 113 | 
            +
              .site .title a {
         | 
| 114 | 
            +
                color: #a00;
         | 
| 115 | 
            +
                text-decoration: none;
         | 
| 116 | 
            +
              }
         | 
| 117 | 
            +
             | 
| 118 | 
            +
              .site .title a:hover {
         | 
| 119 | 
            +
                color: black;
         | 
| 120 | 
            +
              }
         | 
| 121 | 
            +
             | 
| 122 | 
            +
              .site .title .extra {
         | 
| 123 | 
            +
                color: #aaa;
         | 
| 124 | 
            +
                text-decoration: none;
         | 
| 125 | 
            +
                margin-left: 1em;
         | 
| 126 | 
            +
                font-size: 0.9em;
         | 
| 127 | 
            +
              }
         | 
| 128 | 
            +
             | 
| 129 | 
            +
              .site .title a.extra:hover {
         | 
| 130 | 
            +
                color: black;
         | 
| 131 | 
            +
              }
         | 
| 132 | 
            +
             | 
| 133 | 
            +
              .site .meta {
         | 
| 134 | 
            +
                color: #aaa;
         | 
| 135 | 
            +
              }
         | 
| 136 | 
            +
             | 
| 137 | 
            +
              .site .footer {
         | 
| 138 | 
            +
                font-size: 80%;
         | 
| 139 | 
            +
                color: #666;
         | 
| 140 | 
            +
                border-top: 4px solid #eee;
         | 
| 141 | 
            +
                margin-top: 2em;
         | 
| 142 | 
            +
                overflow: hidden;
         | 
| 143 | 
            +
              }
         | 
| 144 | 
            +
             | 
| 145 | 
            +
                .site .footer .contact {
         | 
| 146 | 
            +
                  float: left;
         | 
| 147 | 
            +
                  margin-right: 3em;
         | 
| 148 | 
            +
                }
         | 
| 149 | 
            +
             | 
| 150 | 
            +
                  .site .footer .contact a {
         | 
| 151 | 
            +
                    color: #8085C1;
         | 
| 152 | 
            +
                  }
         | 
| 153 | 
            +
             | 
| 154 | 
            +
                .site .footer .rss {
         | 
| 155 | 
            +
                  margin-top: 1.1em;
         | 
| 156 | 
            +
                  margin-right: -.2em;
         | 
| 157 | 
            +
                  float: right;
         | 
| 158 | 
            +
                }
         | 
| 159 | 
            +
             | 
| 160 | 
            +
                  .site .footer .rss img {
         | 
| 161 | 
            +
                    border: 0;
         | 
| 162 | 
            +
                  }
         | 
| 163 | 
            +
             | 
| 164 | 
            +
            /*****************************************************************************/
         | 
| 165 | 
            +
            /*
         | 
| 166 | 
            +
            /* Posts
         | 
| 167 | 
            +
            /*
         | 
| 168 | 
            +
            /*****************************************************************************/
         | 
| 169 | 
            +
             | 
| 170 | 
            +
            #post {
         | 
| 171 | 
            +
             | 
| 172 | 
            +
            }
         | 
| 173 | 
            +
             | 
| 174 | 
            +
            /* standard */
         | 
| 175 | 
            +
             | 
| 176 | 
            +
            #post pre {
         | 
| 177 | 
            +
              border: 1px solid #ddd;
         | 
| 178 | 
            +
              background-color: #eef;
         | 
| 179 | 
            +
              padding: 0 .4em;
         | 
| 180 | 
            +
            }
         | 
| 181 | 
            +
             | 
| 182 | 
            +
            #post ul,
         | 
| 183 | 
            +
            #post ol {
         | 
| 184 | 
            +
              margin-left: 1.25em;
         | 
| 185 | 
            +
            }
         | 
| 186 | 
            +
             | 
| 187 | 
            +
            #post code {
         | 
| 188 | 
            +
              border: 1px solid #ddd;
         | 
| 189 | 
            +
              background-color: #eef;
         | 
| 190 | 
            +
              font-size: 95%;
         | 
| 191 | 
            +
              padding: 0 .2em;
         | 
| 192 | 
            +
            }
         | 
| 193 | 
            +
             | 
| 194 | 
            +
              #post pre code {
         | 
| 195 | 
            +
                border: none;
         | 
| 196 | 
            +
              }
         | 
| 197 | 
            +
             | 
| 198 | 
            +
            /* terminal */
         | 
| 199 | 
            +
             | 
| 200 | 
            +
            pre.terminal {
         | 
| 201 | 
            +
              border: 1px solid black;
         | 
| 202 | 
            +
              background-color: #333;
         | 
| 203 | 
            +
              color: white;
         | 
| 204 | 
            +
              padding: 5px;
         | 
| 205 | 
            +
              overflow: auto;
         | 
| 206 | 
            +
              word-wrap: break-word;
         | 
| 207 | 
            +
            }
         | 
| 208 | 
            +
             | 
| 209 | 
            +
            pre.terminal code {
         | 
| 210 | 
            +
              font-family: 'Bitstream Vera Sans Mono', 'Courier', monospace;
         | 
| 211 | 
            +
              background-color: #333;
         | 
| 212 | 
            +
            }
         | 
| 213 | 
            +
             | 
| @@ -0,0 +1,55 @@ | |
| 1 | 
            +
            class CIQuantum
         | 
| 2 | 
            +
              # An in memory queue used for maintaining an order list of requested
         | 
| 3 | 
            +
              # builds.
         | 
| 4 | 
            +
              class Queue
         | 
| 5 | 
            +
                # enabled - determines whether builds should be queued or not.
         | 
| 6 | 
            +
                def initialize(enabled, verbose=false)
         | 
| 7 | 
            +
                  @enabled = enabled
         | 
| 8 | 
            +
                  @verbose = verbose
         | 
| 9 | 
            +
                  @queue = []
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  log("Build queueing enabled") if enabled
         | 
| 12 | 
            +
                end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                # Public: Appends a branch to be built, unless it already exists
         | 
| 15 | 
            +
                # within the queue.
         | 
| 16 | 
            +
                #
         | 
| 17 | 
            +
                # branch - the name of the branch to build or nil if the default
         | 
| 18 | 
            +
                #         should be built.
         | 
| 19 | 
            +
                #
         | 
| 20 | 
            +
                # Returns nothing
         | 
| 21 | 
            +
                def append_unless_already_exists(branch)
         | 
| 22 | 
            +
                  return unless enabled?
         | 
| 23 | 
            +
                  unless @queue.include? branch
         | 
| 24 | 
            +
                    @queue << branch
         | 
| 25 | 
            +
                    log "#{Time.now.to_i}: Queueing #{branch}"
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                # Returns a String of the next branch to build
         | 
| 30 | 
            +
                def next_branch_to_build
         | 
| 31 | 
            +
                  branch = @queue.shift
         | 
| 32 | 
            +
                  log "#{Time.now.to_i}: De-queueing #{branch}"
         | 
| 33 | 
            +
                  branch
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                # Returns true if there are requested builds waiting and false
         | 
| 37 | 
            +
                # otherwise.
         | 
| 38 | 
            +
                def waiting?
         | 
| 39 | 
            +
                  if enabled?
         | 
| 40 | 
            +
                    not @queue.empty?
         | 
| 41 | 
            +
                  else
         | 
| 42 | 
            +
                    false
         | 
| 43 | 
            +
                  end
         | 
| 44 | 
            +
                end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
              protected
         | 
| 47 | 
            +
                def log(msg)
         | 
| 48 | 
            +
                  puts msg if @verbose
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                def enabled?
         | 
| 52 | 
            +
                  @enabled
         | 
| 53 | 
            +
                end
         | 
| 54 | 
            +
              end
         | 
| 55 | 
            +
            end
         | 
| @@ -0,0 +1,117 @@ | |
| 1 | 
            +
            require 'sinatra/base'
         | 
| 2 | 
            +
            require 'erb'
         | 
| 3 | 
            +
            require 'json'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            class CIQuantum
         | 
| 6 | 
            +
              class Server < Sinatra::Base
         | 
| 7 | 
            +
                attr_reader :quantum
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                dir = File.dirname(File.expand_path(__FILE__))
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                set :views,  "#{dir}/views"
         | 
| 12 | 
            +
                set :public_folder, "#{dir}/public"
         | 
| 13 | 
            +
                set :static, true
         | 
| 14 | 
            +
                set :lock, true
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                before { quantum.restore }
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                get '/ping' do
         | 
| 19 | 
            +
                  if quantum.building? || !quantum.last_build || !quantum.last_build.worked?
         | 
| 20 | 
            +
                    halt 412, (quantum.building? || quantum.last_build.nil?) ? "building" : quantum.last_build.sha
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  quantum.last_build.sha
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                get '/?' do
         | 
| 27 | 
            +
                  erb(:template, {}, :quantum => quantum)
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                post '/?' do
         | 
| 31 | 
            +
                  unless params[:rebuild]
         | 
| 32 | 
            +
                    payload = JSON.parse(params[:payload])
         | 
| 33 | 
            +
                    pushed_branch = payload["ref"].split('/').last
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
                  
         | 
| 36 | 
            +
                  # Only build if we were given an explicit branch via `?branch=blah`
         | 
| 37 | 
            +
                  # or the payload exists and the "ref" property matches our 
         | 
| 38 | 
            +
                  # specified build branch.
         | 
| 39 | 
            +
                  if params[:branch] || params[:rebuild] || pushed_branch == quantum.git_branch
         | 
| 40 | 
            +
                    quantum.build(params[:branch])
         | 
| 41 | 
            +
                  end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                  redirect request.path
         | 
| 44 | 
            +
                end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                get '/api/json' do
         | 
| 47 | 
            +
                    response  = [200, {'Content-Type' => 'application/json'}]
         | 
| 48 | 
            +
                    response_json = erb(:json, {}, :quantum => quantum)
         | 
| 49 | 
            +
                  if params[:jsonp]
         | 
| 50 | 
            +
                    response << params[:jsonp] + '(' +  response_json + ')'
         | 
| 51 | 
            +
                  else
         | 
| 52 | 
            +
                    response << response_json
         | 
| 53 | 
            +
                  end
         | 
| 54 | 
            +
                  response
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                get '/coverage' do
         | 
| 58 | 
            +
                  redirect quantum.coverage_path
         | 
| 59 | 
            +
                end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                helpers do
         | 
| 62 | 
            +
                  include Rack::Utils
         | 
| 63 | 
            +
                  alias_method :h, :escape_html
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                  # thanks integrity!
         | 
| 66 | 
            +
                  def ansi_color_codes(string)
         | 
| 67 | 
            +
                    string.gsub("\e[0m", '</span>').
         | 
| 68 | 
            +
                      gsub(/\e\[(\d+)m/, "<span class=\"color\\1\">")
         | 
| 69 | 
            +
                  end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                  def pretty_time(time)
         | 
| 72 | 
            +
                    time.strftime("%Y-%m-%d %H:%M")
         | 
| 73 | 
            +
                  end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                  def ciquantum_root
         | 
| 76 | 
            +
                    root = request.path
         | 
| 77 | 
            +
                    root = "" if root == "/"
         | 
| 78 | 
            +
                    root
         | 
| 79 | 
            +
                  end
         | 
| 80 | 
            +
                end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                def initialize(*args)
         | 
| 83 | 
            +
                  super
         | 
| 84 | 
            +
                  check_project
         | 
| 85 | 
            +
                  @quantum = CIQuantum.new(settings.project_path)
         | 
| 86 | 
            +
                end
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                def self.start(host, port, project_path)
         | 
| 89 | 
            +
                  set :project_path, project_path
         | 
| 90 | 
            +
                  CIQuantum::Server.run! :host => host, :port => port
         | 
| 91 | 
            +
                end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                def self.rack_start(project_path)
         | 
| 94 | 
            +
                  set :project_path, project_path
         | 
| 95 | 
            +
                  self.new
         | 
| 96 | 
            +
                end
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                def self.project_path=(project_path)
         | 
| 99 | 
            +
                  user, pass = Config.ciquantum(project_path).user.to_s, Config.ciquantum(project_path).pass.to_s
         | 
| 100 | 
            +
                  if user != '' && pass != ''
         | 
| 101 | 
            +
                    use Rack::Auth::Basic do |username, password|
         | 
| 102 | 
            +
                      [ username, password ] == [ user, pass ]
         | 
| 103 | 
            +
                    end
         | 
| 104 | 
            +
                    puts "Using HTTP basic auth"
         | 
| 105 | 
            +
                  end
         | 
| 106 | 
            +
                  set :project_path, Proc.new{project_path}, true
         | 
| 107 | 
            +
                end
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                def check_project
         | 
| 110 | 
            +
                  if settings.project_path.nil? || !File.exists?(File.expand_path(settings.project_path))
         | 
| 111 | 
            +
                    puts "Whoops! I need the path to a Git repo."
         | 
| 112 | 
            +
                    puts "  $ git clone git@github.com:username/project.git project"
         | 
| 113 | 
            +
                    abort "  $ ciquantum project"
         | 
| 114 | 
            +
                  end
         | 
| 115 | 
            +
                end
         | 
| 116 | 
            +
              end
         | 
| 117 | 
            +
            end
         |