cijoe-febuiles 0.8.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/LICENSE +20 -0
- data/README.md +171 -0
- data/Rakefile +31 -0
- data/bin/cijoe +51 -0
- data/lib/cijoe.rb +233 -0
- data/lib/cijoe/build.rb +67 -0
- data/lib/cijoe/campfire.rb +74 -0
- data/lib/cijoe/commit.rb +27 -0
- data/lib/cijoe/config.rb +43 -0
- data/lib/cijoe/public/favicon.ico +0 -0
- data/lib/cijoe/public/octocat.png +0 -0
- data/lib/cijoe/public/screen.css +213 -0
- data/lib/cijoe/server.rb +108 -0
- data/lib/cijoe/version.rb +3 -0
- data/lib/cijoe/views/json.erb +8 -0
- data/lib/cijoe/views/template.erb +74 -0
- data/test/helper.rb +14 -0
- data/test/test_cijoe.rb +17 -0
- data/test/test_cijoe_server.rb +93 -0
- metadata +106 -0
    
        data/lib/cijoe/build.rb
    ADDED
    
    | @@ -0,0 +1,67 @@ | |
| 1 | 
            +
            require 'yaml'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            class CIJoe
         | 
| 4 | 
            +
              class Build < Struct.new(:project_path, :user, :project, :started_at, :finished_at, :sha, :status, :output, :pid)
         | 
| 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]
         | 
| 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,74 @@ | |
| 1 | 
            +
            class CIJoe
         | 
| 2 | 
            +
              module Campfire
         | 
| 3 | 
            +
                def self.activate(project_path)
         | 
| 4 | 
            +
                  @project_path = project_path
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  if valid_config?
         | 
| 7 | 
            +
                    require 'tinder'
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                    CIJoe::Build.class_eval do
         | 
| 10 | 
            +
                      include CIJoe::Campfire
         | 
| 11 | 
            +
                    end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                    puts "Loaded Campfire notifier"
         | 
| 14 | 
            +
                  elsif ENV['RACK_ENV'] != 'test'
         | 
| 15 | 
            +
                    puts "Can't load Campfire notifier."
         | 
| 16 | 
            +
                    puts "Please add the following to your project's .git/config:"
         | 
| 17 | 
            +
                    puts "[campfire]"
         | 
| 18 | 
            +
                    puts "\ttoken = your_api_token"
         | 
| 19 | 
            +
                    puts "\tsubdomain = whatever"
         | 
| 20 | 
            +
                    puts "\troom = Awesomeness"
         | 
| 21 | 
            +
                    puts "\tssl = false"
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                def self.config
         | 
| 26 | 
            +
                  campfire_config = Config.new('campfire', @project_path)
         | 
| 27 | 
            +
                  @config ||= {
         | 
| 28 | 
            +
                    :subdomain => campfire_config.subdomain.to_s,
         | 
| 29 | 
            +
                    :token     => campfire_config.token.to_s,
         | 
| 30 | 
            +
                    :room      => campfire_config.room.to_s,
         | 
| 31 | 
            +
                    :ssl       => campfire_config.ssl.to_s.strip == 'true'
         | 
| 32 | 
            +
                  }
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                def self.valid_config?
         | 
| 36 | 
            +
                  %w( subdomain token room ).all? do |key|
         | 
| 37 | 
            +
                    !config[key.intern].empty?
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                def notify
         | 
| 42 | 
            +
                  room.speak "#{short_message}. #{commit.url}"
         | 
| 43 | 
            +
                  room.paste full_message if failed?
         | 
| 44 | 
            +
                  room.leave
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
              private
         | 
| 48 | 
            +
                def room
         | 
| 49 | 
            +
                  @room ||= begin
         | 
| 50 | 
            +
                    config = Campfire.config
         | 
| 51 | 
            +
                    campfire = Tinder::Campfire.new(config[:subdomain],
         | 
| 52 | 
            +
                        :token => config[:token],
         | 
| 53 | 
            +
                        :ssl => config[:ssl] || false)
         | 
| 54 | 
            +
                    campfire.find_room_by_name(config[:room])
         | 
| 55 | 
            +
                  end
         | 
| 56 | 
            +
                end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                def short_message
         | 
| 59 | 
            +
                  "Build #{short_sha} of #{project} " +
         | 
| 60 | 
            +
                    (worked? ? "was successful" : "failed") +
         | 
| 61 | 
            +
                    " (#{duration.to_i}s)"
         | 
| 62 | 
            +
                end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                def full_message
         | 
| 65 | 
            +
                  <<-EOM
         | 
| 66 | 
            +
            Commit Message: #{commit.message}
         | 
| 67 | 
            +
            Commit Date: #{commit.committed_at}
         | 
| 68 | 
            +
            Commit Author: #{commit.author}
         | 
| 69 | 
            +
             | 
| 70 | 
            +
            #{clean_output}
         | 
| 71 | 
            +
            EOM
         | 
| 72 | 
            +
                end
         | 
| 73 | 
            +
              end
         | 
| 74 | 
            +
            end
         | 
    
        data/lib/cijoe/commit.rb
    ADDED
    
    | @@ -0,0 +1,27 @@ | |
| 1 | 
            +
            class CIJoe
         | 
| 2 | 
            +
              class Commit < Struct.new(:sha, :user, :project, :project_path)
         | 
| 3 | 
            +
                def url
         | 
| 4 | 
            +
                  "http://github.com/#{user}/#{project}/commit/#{sha}"
         | 
| 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 | 
            +
              end
         | 
| 27 | 
            +
            end
         | 
    
        data/lib/cijoe/config.rb
    ADDED
    
    | @@ -0,0 +1,43 @@ | |
| 1 | 
            +
            class CIJoe
         | 
| 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 | 
            +
                    raise "Error calling git config, is a recent version of git installed? Command: #{git_command.inspect}, Error: #{result.inspect}, Status: #{process_status.inspect}"
         | 
| 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 | 
            +
             | 
    
        data/lib/cijoe/server.rb
    ADDED
    
    | @@ -0,0 +1,108 @@ | |
| 1 | 
            +
            require 'sinatra/base'
         | 
| 2 | 
            +
            require 'erb'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            class CIJoe
         | 
| 5 | 
            +
              class Server < Sinatra::Base
         | 
| 6 | 
            +
                attr_reader :joe
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                dir = File.dirname(File.expand_path(__FILE__))
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                set :views,  "#{dir}/views"
         | 
| 11 | 
            +
                set :public, "#{dir}/public"
         | 
| 12 | 
            +
                set :static, true
         | 
| 13 | 
            +
                set :lock, true
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                before { joe.restore }
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                get '/ping' do
         | 
| 18 | 
            +
                  if joe.building? || !joe.last_build || !joe.last_build.worked?
         | 
| 19 | 
            +
                    halt 412, (joe.building? || joe.last_build.nil?) ? "building" : joe.last_build.sha
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  joe.last_build.sha
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                get '/?' do
         | 
| 26 | 
            +
                  erb(:template, {}, :joe => joe)
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                post '/?' do
         | 
| 30 | 
            +
                  payload = YAML.load(params[:payload].to_s) || {}
         | 
| 31 | 
            +
                  pushed_branch = payload['ref'].to_s.split('/').last
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                  # Only build if we were given an explicit branch via `?branch=blah`,
         | 
| 34 | 
            +
                  # no payload exists (we're probably testing), or the payload exists and
         | 
| 35 | 
            +
                  # the "ref" property matches our specified build branch.
         | 
| 36 | 
            +
                  if params[:branch] || payload.empty? || pushed_branch == joe.git_branch
         | 
| 37 | 
            +
                    joe.build(params[:branch])
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                  redirect request.path
         | 
| 41 | 
            +
                end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                get '/api/json' do
         | 
| 44 | 
            +
                    response  = [200, {'Content-Type' => 'application/json'}]
         | 
| 45 | 
            +
                    response_json = erb(:json, {}, :joe => joe)
         | 
| 46 | 
            +
                  if params[:jsonp]
         | 
| 47 | 
            +
                    response << params[:jsonp] + '(' +  response_json + ')'
         | 
| 48 | 
            +
                  else
         | 
| 49 | 
            +
                    response << response_json
         | 
| 50 | 
            +
                  end
         | 
| 51 | 
            +
                  response
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
             | 
| 55 | 
            +
                helpers do
         | 
| 56 | 
            +
                  include Rack::Utils
         | 
| 57 | 
            +
                  alias_method :h, :escape_html
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                  # thanks integrity!
         | 
| 60 | 
            +
                  def ansi_color_codes(string)
         | 
| 61 | 
            +
                    string.gsub("\e[0m", '</span>').
         | 
| 62 | 
            +
                      gsub(/\e\[(\d+)m/, "<span class=\"color\\1\">")
         | 
| 63 | 
            +
                  end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                  def pretty_time(time)
         | 
| 66 | 
            +
                    time.strftime("%Y-%m-%d %H:%M")
         | 
| 67 | 
            +
                  end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                  def cijoe_root
         | 
| 70 | 
            +
                    root = request.path
         | 
| 71 | 
            +
                    root = "" if root == "/"
         | 
| 72 | 
            +
                    root
         | 
| 73 | 
            +
                  end
         | 
| 74 | 
            +
                end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                def initialize(*args)
         | 
| 77 | 
            +
                  super
         | 
| 78 | 
            +
                  check_project
         | 
| 79 | 
            +
                  @joe = CIJoe.new(options.project_path)
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                  CIJoe::Campfire.activate(options.project_path)
         | 
| 82 | 
            +
                end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                def self.start(host, port, project_path)
         | 
| 85 | 
            +
                  set :project_path, project_path
         | 
| 86 | 
            +
                  CIJoe::Server.run! :host => host, :port => port
         | 
| 87 | 
            +
                end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                def self.project_path=(project_path)
         | 
| 90 | 
            +
                  user, pass = Config.cijoe(project_path).user.to_s, Config.cijoe(project_path).pass.to_s
         | 
| 91 | 
            +
                  if user != '' && pass != ''
         | 
| 92 | 
            +
                    use Rack::Auth::Basic do |username, password|
         | 
| 93 | 
            +
                      [ username, password ] == [ user, pass ]
         | 
| 94 | 
            +
                    end
         | 
| 95 | 
            +
                    puts "Using HTTP basic auth"
         | 
| 96 | 
            +
                  end
         | 
| 97 | 
            +
                  set :project_path, Proc.new{project_path}
         | 
| 98 | 
            +
                end
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                def check_project
         | 
| 101 | 
            +
                  if options.project_path.nil? || !File.exists?(File.expand_path(options.project_path))
         | 
| 102 | 
            +
                    puts "Whoops! I need the path to a Git repo."
         | 
| 103 | 
            +
                    puts "  $ git clone git@github.com:username/project.git project"
         | 
| 104 | 
            +
                    abort "  $ cijoe project"
         | 
| 105 | 
            +
                  end
         | 
| 106 | 
            +
                end
         | 
| 107 | 
            +
              end
         | 
| 108 | 
            +
            end
         |