rhomobile-cijoe 0.2.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.
@@ -0,0 +1,60 @@
1
+ require 'yaml'
2
+
3
+ class CIJoe
4
+ class Build < Struct.new(:user, :project, :started_at, :finished_at, :sha, :status, :output, :pid, :total, :passes, :fails, :faillog)
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 short_sha
24
+ if sha
25
+ sha[0,7]
26
+ else
27
+ "<unknown>"
28
+ end
29
+ end
30
+
31
+ def clean_output
32
+ output.gsub(/\e\[.+?m/, '').strip
33
+ end
34
+
35
+ def commit
36
+ return if sha.nil?
37
+ @commit ||= Commit.new(sha, user, project)
38
+ end
39
+
40
+ def dump(file)
41
+ config = [user, project, started_at, finished_at, sha, status, output, pid, total, passes, fails, faillog]
42
+ data = YAML.dump(config)
43
+ File.open(file, 'wb') { |io| io.write(data) }
44
+ end
45
+
46
+ def self.load(file)
47
+ if File.exist?(file)
48
+ config = YAML.load(File.read(file))
49
+
50
+ if config[8].to_s == "" or config[9].to_s == "" or config[10].to_s == ""
51
+ config[8] = $1 if config[6] =~ /Total: ([0-9]+)/
52
+ config[9] = $1 if config[6] =~ /Passed: ([0-9]+)/
53
+ config[10] = $1 if config[6] =~ /Failed: ([0-9]+)/
54
+ end
55
+
56
+ new *config
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,27 @@
1
+ class CIJoe
2
+ class Commit < Struct.new(:sha, :user, :project)
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 ||= `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
@@ -0,0 +1,42 @@
1
+ class CIJoe
2
+ class Config
3
+ def self.method_missing(command, *args)
4
+ new(command)
5
+ end
6
+
7
+ def initialize(command, parent = nil)
8
+ @command = command
9
+ @parent = parent
10
+ end
11
+
12
+ def method_missing(command, *args)
13
+ Config.new(command, self)
14
+ end
15
+
16
+ def to_s
17
+ git_command = "git config #{config_string}"
18
+ result = `#{git_command} 2>&1`.chomp
19
+ process_status = $?
20
+
21
+ if successful_command?(process_status) || config_command_with_empty_value?(result,process_status)
22
+ return result
23
+ else
24
+ raise "Error calling git config, is a recent version of git installed? Command: #{git_command}, Error: #{result}"
25
+ end
26
+ end
27
+
28
+ def config_string
29
+ @parent ? "#{@parent.config_string}.#{@command}" : @command
30
+ end
31
+
32
+ private
33
+
34
+ def successful_command?(process_status)
35
+ process_status.exitstatus.to_i == 0
36
+ end
37
+
38
+ def config_command_with_empty_value?(result, process_status)
39
+ process_status.exitstatus.to_i == 1 && result.empty?
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,66 @@
1
+ require File.dirname(__FILE__) + '/../mmmail'
2
+
3
+ class CIJoe
4
+ module Email
5
+ def self.activate
6
+ if valid_config?
7
+ CIJoe::Build.class_eval do
8
+ include CIJoe::Email
9
+ end
10
+
11
+ puts "Loaded Email notifier"
12
+ else
13
+ puts "Can't load Email notifier."
14
+ puts "Please add the following to your project's .git/config:"
15
+ puts "[email]"
16
+ puts "\tto = build@example.com"
17
+ puts "\tuser = horst"
18
+ puts "\tpass = passw0rd"
19
+ puts "\thost = mail.example.com"
20
+ puts "\tauthtype = plain"
21
+ puts "\tenabletls = 1"
22
+ end
23
+ end
24
+
25
+ def self.config
26
+ @config ||= {
27
+ :to => Config.email.to.to_s,
28
+ :user => Config.email.user.to_s,
29
+ :pass => Config.email.pass.to_s,
30
+ :host => Config.email.host.to_s,
31
+ :auth_type => Config.email.authtype.to_s,
32
+ :enable_tls => Config.email.enabletls.to_s
33
+ }
34
+ end
35
+
36
+ def self.valid_config?
37
+ %w( host user pass to auth_type ).all? do |key|
38
+ !config[key.intern].empty?
39
+ end
40
+ end
41
+
42
+ def notify
43
+ options = {
44
+ :to => Email.config[:to],
45
+ :from => Email.config[:to],
46
+ :subject => "(#{project}) Build failed",
47
+ :body => mail_body
48
+ }
49
+ MmMail.mail(options, mail_config) if failed?
50
+ end
51
+
52
+ def mail_config
53
+ config = MmMail::Transport::Config.new
54
+ config.auth_user = Email.config[:user]
55
+ config.auth_pass = Email.config[:pass]
56
+ config.auth_type = Email.config[:auth_type].to_sym
57
+ config.host = Email.config[:host]
58
+ config.enable_tls = Email.config[:enable_tls] == "1" ? true : false
59
+ config
60
+ end
61
+
62
+ def mail_body
63
+ "The commit '#{commit.message}' (#{commit.url}) by #{commit.author} caused the build to fail."
64
+ end
65
+ end
66
+ end
Binary file
Binary file
@@ -0,0 +1,234 @@
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
+ color: #aaa;
88
+ font-family: Monaco, "Courier New", monospace;
89
+ font-size: 80%;
90
+ }
91
+
92
+ ul.posts .total {
93
+ color: blue;
94
+ font-size: 66%;
95
+ }
96
+
97
+ ul.posts .pass {
98
+ color: green;
99
+ font-size: 66%;
100
+ }
101
+
102
+ ul.posts .fail {
103
+ color: red;
104
+ font-size: 66%;
105
+ }
106
+
107
+ ul.posts .showlog {
108
+ font-size: 66%;
109
+ }
110
+
111
+ /*****************************************************************************/
112
+ /*
113
+ /* Site
114
+ /*
115
+ /*****************************************************************************/
116
+
117
+ .site {
118
+ font-size: 110%;
119
+ text-align: justify;
120
+ width: 50em;
121
+ margin: 3em auto 2em auto;
122
+ line-height: 1.5em;
123
+ }
124
+
125
+ .title {
126
+ color: #a00;
127
+ font-weight: bold;
128
+ margin-bottom: 2em;
129
+ }
130
+
131
+ .site .title a {
132
+ color: #a00;
133
+ text-decoration: none;
134
+ }
135
+
136
+ .site .title a:hover {
137
+ color: black;
138
+ }
139
+
140
+ .site .title .extra {
141
+ color: #aaa;
142
+ text-decoration: none;
143
+ margin-left: 1em;
144
+ font-size: 0.9em;
145
+ }
146
+
147
+ .site .title a.extra:hover {
148
+ color: black;
149
+ }
150
+
151
+ .site .meta {
152
+ color: #aaa;
153
+ }
154
+
155
+ .site .footer {
156
+ font-size: 80%;
157
+ color: #666;
158
+ border-top: 4px solid #eee;
159
+ margin-top: 2em;
160
+ overflow: hidden;
161
+ }
162
+
163
+ .site .footer .contact {
164
+ float: left;
165
+ margin-right: 3em;
166
+ }
167
+
168
+ .site .footer .contact a {
169
+ color: #8085C1;
170
+ }
171
+
172
+ .site .footer .rss {
173
+ margin-top: 1.1em;
174
+ margin-right: -.2em;
175
+ float: right;
176
+ }
177
+
178
+ .site .footer .rss img {
179
+ border: 0;
180
+ }
181
+
182
+ /*****************************************************************************/
183
+ /*
184
+ /* Posts
185
+ /*
186
+ /*****************************************************************************/
187
+
188
+ #post {
189
+
190
+ }
191
+
192
+ /* standard */
193
+
194
+ #post pre {
195
+ border: 1px solid #ddd;
196
+ background-color: #eef;
197
+ padding: 0 .4em;
198
+ }
199
+
200
+ #post ul,
201
+ #post ol {
202
+ margin-left: 1.25em;
203
+ }
204
+
205
+ #post code {
206
+ border: 1px solid #ddd;
207
+ background-color: #eef;
208
+ font-size: 95%;
209
+ padding: 0 .2em;
210
+ }
211
+
212
+ #post pre code {
213
+ border: none;
214
+ }
215
+
216
+ /* terminal */
217
+
218
+ pre.terminal {
219
+ border: 1px solid black;
220
+ background-color: #333;
221
+ color: white;
222
+ padding: 5px;
223
+ overflow: scroll;
224
+ overflow-y: hidden;
225
+ word-wrap: normal;
226
+ line-height: 1em;
227
+ }
228
+
229
+ pre.terminal code {
230
+ font-family: 'Bitstream Vera Sans Mono', 'Courier', monospace;
231
+ background-color: #333;
232
+ font-size: 12px;
233
+ }
234
+
@@ -0,0 +1,90 @@
1
+ require 'sinatra/base'
2
+ require 'erb'
3
+
4
+ class CIJoe
5
+ class Server < Sinatra::Base
6
+ dir = File.dirname(File.expand_path(__FILE__))
7
+
8
+ set :views, "#{dir}/views"
9
+ set :public, "#{dir}/public"
10
+ set :static, true
11
+ set :lock, true
12
+
13
+ before { @joe.restore }
14
+
15
+ get '/?' do
16
+ erb(:template, {}, :joe => @joe)
17
+ end
18
+
19
+ get '/logfail/:log_name' do
20
+ ansi_color_codes(@joe.failure_for_time params[:log_name])
21
+
22
+ end
23
+
24
+ get '/log/:log_name' do
25
+ ansi_color_codes(@joe.log_for_time params[:log_name])
26
+
27
+ end
28
+
29
+
30
+
31
+ post '/?' do
32
+ payload = params[:payload].to_s
33
+ if payload.empty? || payload.include?(@joe.git_branch)
34
+ @joe.build
35
+ end
36
+ redirect request.path
37
+ end
38
+
39
+ user, pass = Config.cijoe.user.to_s, Config.cijoe.pass.to_s
40
+ if user != '' && pass != ''
41
+ use Rack::Auth::Basic do |username, password|
42
+ [ username, password ] == [ user, pass ]
43
+ end
44
+ puts "Using HTTP basic auth"
45
+ end
46
+
47
+ helpers do
48
+ include Rack::Utils
49
+ alias_method :h, :escape_html
50
+
51
+ # thanks integrity!
52
+ def ansi_color_codes(string)
53
+ string.gsub("\e[0m", '</span>').
54
+ gsub(/\e\[(\d+)m/, "<span class=\"color\\1\">")
55
+ end
56
+
57
+ def pretty_time(time)
58
+ return time if time.nil?
59
+ time.strftime("%Y-%m-%d %H:%M")
60
+ end
61
+
62
+ def cijoe_root
63
+ root = request.path
64
+ root = "" if root == "/"
65
+ root
66
+ end
67
+ end
68
+
69
+ def initialize(*args)
70
+ super
71
+ check_project
72
+ @joe = CIJoe.new(options.project_path)
73
+
74
+ CIJoe::Email.activate
75
+ end
76
+
77
+ def self.start(host, port, project_path)
78
+ set :project_path, project_path
79
+ CIJoe::Server.run! :host => host, :port => port
80
+ end
81
+
82
+ def check_project
83
+ if options.project_path.nil? || !File.exists?(File.expand_path(options.project_path))
84
+ puts "Whoops! I need the path to a Git repo."
85
+ puts " $ git clone git@github.com:username/project.git project"
86
+ abort " $ cijoe project"
87
+ end
88
+ end
89
+ end
90
+ end