perkins 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. checksums.yaml +7 -0
  2. data/.DS_Store +0 -0
  3. data/.env.example +4 -0
  4. data/.gitignore +19 -0
  5. data/.pryrc +3 -0
  6. data/.rspec +2 -0
  7. data/Gemfile +18 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +71 -0
  10. data/Rakefile +28 -0
  11. data/TODO.md +4 -0
  12. data/bin/perkins +6 -0
  13. data/db/migrate/20150130143030_create_repo.rb +18 -0
  14. data/db/migrate/20150130143050_create_builds.rb +20 -0
  15. data/db/schema.rb +38 -0
  16. data/examples/config.rb +12 -0
  17. data/examples/database.yml +17 -0
  18. data/examples/mongo.yml +13 -0
  19. data/lib/core_ext/hash/compact.rb +8 -0
  20. data/lib/core_ext/hash/deep_merge.rb +15 -0
  21. data/lib/core_ext/hash/deep_symbolize_keys.rb +20 -0
  22. data/lib/core_ext/object/false.rb +5 -0
  23. data/lib/core_ext/string/indent.rb +5 -0
  24. data/lib/core_ext/string/unindent.rb +5 -0
  25. data/lib/perkins/.DS_Store +0 -0
  26. data/lib/perkins/application.rb +40 -0
  27. data/lib/perkins/assets/images/github.svg +4 -0
  28. data/lib/perkins/assets/images/spinner.svg +23 -0
  29. data/lib/perkins/assets/javascripts/app.js +9 -0
  30. data/lib/perkins/assets/javascripts/log_view.js.coffee +95 -0
  31. data/lib/perkins/assets/javascripts/perkings.js.coffee +40 -0
  32. data/lib/perkins/assets/javascripts/vendor/ansiparse.js +187 -0
  33. data/lib/perkins/assets/javascripts/vendor/jquery.timeago.js +189 -0
  34. data/lib/perkins/assets/javascripts/vendor/log.js +2 -0
  35. data/lib/perkins/assets/javascripts/vendor/minispade.js +55 -0
  36. data/lib/perkins/assets/stylesheets/app.css +2 -0
  37. data/lib/perkins/assets/stylesheets/log.css.scss +115 -0
  38. data/lib/perkins/assets/stylesheets/styles.css.scss +199 -0
  39. data/lib/perkins/auth/github.rb +181 -0
  40. data/lib/perkins/build/data/env.rb +84 -0
  41. data/lib/perkins/build/data/var.rb +60 -0
  42. data/lib/perkins/build/data.rb +167 -0
  43. data/lib/perkins/build/script/bundler.rb +76 -0
  44. data/lib/perkins/build/script/go.rb +100 -0
  45. data/lib/perkins/build/script/helpers.rb +39 -0
  46. data/lib/perkins/build/script/jdk.rb +43 -0
  47. data/lib/perkins/build/script/ruby.rb +31 -0
  48. data/lib/perkins/build/script/rvm.rb +73 -0
  49. data/lib/perkins/build/script/stages.rb +28 -0
  50. data/lib/perkins/build/script/templates/footer.sh +3 -0
  51. data/lib/perkins/build/script/templates/header.sh +201 -0
  52. data/lib/perkins/build/script/templates/xcode.sh +21 -0
  53. data/lib/perkins/build/script.rb +167 -0
  54. data/lib/perkins/build/shell/dsl.rb +104 -0
  55. data/lib/perkins/build/shell/node.rb +121 -0
  56. data/lib/perkins/build/shell.rb +16 -0
  57. data/lib/perkins/build.rb +27 -0
  58. data/lib/perkins/build_report.rb +11 -0
  59. data/lib/perkins/cli.rb +42 -0
  60. data/lib/perkins/commit.rb +30 -0
  61. data/lib/perkins/dsl/app_proxy.rb +23 -0
  62. data/lib/perkins/dsl.rb +12 -0
  63. data/lib/perkins/listener.rb +38 -0
  64. data/lib/perkins/logger.rb +12 -0
  65. data/lib/perkins/notifier.rb +5 -0
  66. data/lib/perkins/repo.rb +145 -0
  67. data/lib/perkins/runner.rb +124 -0
  68. data/lib/perkins/server.rb +314 -0
  69. data/lib/perkins/thor_utils.rb +79 -0
  70. data/lib/perkins/version.rb +3 -0
  71. data/lib/perkins/views/401.haml +1 -0
  72. data/lib/perkins/views/builds.haml +46 -0
  73. data/lib/perkins/views/index.haml +6 -0
  74. data/lib/perkins/views/layout.haml +53 -0
  75. data/lib/perkins/views/menu.haml +18 -0
  76. data/lib/perkins/views/orgs.haml +101 -0
  77. data/lib/perkins/views/profile.haml +31 -0
  78. data/lib/perkins/views/readme.md +20 -0
  79. data/lib/perkins/views/repos/config.haml +72 -0
  80. data/lib/perkins/views/repos/github.haml +76 -0
  81. data/lib/perkins/views/repos/menu.haml +17 -0
  82. data/lib/perkins/views/repos/repo.haml +64 -0
  83. data/lib/perkins/views/repos/spinner.haml +3 -0
  84. data/lib/perkins/webhooks/github.rb +12 -0
  85. data/lib/perkins/worker.rb +33 -0
  86. data/lib/perkins.rb +36 -0
  87. data/perkins.gemspec +52 -0
  88. data/spec/fixtures/.travis.yml +8 -0
  89. data/spec/fixtures/config.yml +6 -0
  90. data/spec/lib/build/build_spec.rb +58 -0
  91. data/spec/lib/commit_spec.rb +6 -0
  92. data/spec/lib/dsl_spec.rb +17 -0
  93. data/spec/lib/listener_spec.rb +30 -0
  94. data/spec/lib/repo_spec.rb +110 -0
  95. data/spec/lib/runner_spec.rb +76 -0
  96. data/spec/lib/server_spec.rb +108 -0
  97. data/spec/spec_helper.rb +67 -0
  98. data/spec/support/auth.rb +30 -0
  99. data/spec/support/github_api.rb +177 -0
  100. metadata +503 -0
@@ -0,0 +1,145 @@
1
+ require "git"
2
+ module Perkins
3
+ class Repo < ActiveRecord::Base
4
+ attr_accessor :git
5
+ attr_accessor :new_commit, :runner
6
+
7
+ has_many :build_reports, class_name: 'Perkins::BuildReport'
8
+ serialize :github_data, ActiveSupport::HashWithIndifferentAccess
9
+
10
+ scope :from_github, ->{where(cached: true)}
11
+ scope :added, ->{where(cached: false)}
12
+
13
+ DEFAULT_DIR = "/tmp/"
14
+
15
+ def self.add_from_github(id)
16
+ github_repo = synced_records.find_by(gb_id: id.to_i)
17
+ store_from_github(github_repo)
18
+ end
19
+
20
+ def self.sync_github_repos(user)
21
+ user.api.repos.each do |repo|
22
+ self.sync_github_repo(repo)
23
+ end
24
+ end
25
+
26
+ def self.sync_github_repo(r)
27
+ return if Repo.where(gb_id: r[:id]).any?
28
+ repo = Repo.new
29
+ repo.github_data = r.to_attrs.with_indifferent_access
30
+ repo.cached = true
31
+ repo.url = r[:clone_url]
32
+ repo.name = r[:full_name]
33
+ repo.gb_id = r[:id]
34
+ repo.working_dir = DEFAULT_DIR
35
+ repo.save
36
+ end
37
+
38
+ def self.synced_records
39
+ self.from_github
40
+ end
41
+
42
+ def self.store_from_github(repo)
43
+ repo
44
+ repo.working_dir = DEFAULT_DIR #this should be configurable from app
45
+ repo.cached = false
46
+ repo.save
47
+ end
48
+
49
+ def self.initialize_from_store(opts)
50
+ repo = Repo.new
51
+ repo.url = opts["github_data"]["ssh_url"]
52
+ repo.name = opts["name"]
53
+ repo.github_data = opts["github_data"]
54
+ repo.gb_id = opts["github_data"]["id"]
55
+ repo.working_dir = DEFAULT_DIR #this should be configurable from app
56
+ repo
57
+ end
58
+
59
+ def load_git
60
+ clone_or_load
61
+ end
62
+
63
+ def clone_or_load
64
+ if exists?
65
+ open
66
+ else
67
+ ssh_url = self.github_data["ssh_url"]
68
+ g = Git.clone(ssh_url, name, :path => working_dir)
69
+ open
70
+ end
71
+ end
72
+
73
+ def open
74
+ self.git = Git.open(local_path) # :log => Logger.new(STDOUT)
75
+ build_runner_config(self.check_config_existence) if self.check_config_existence
76
+ end
77
+
78
+ def check_config_existence
79
+ config = self.git.chdir{
80
+ if File.exists?(".travis.yml")
81
+ config = Travis::Yaml.parse( File.open(".travis.yml").read )
82
+ else
83
+ config = Travis::Yaml.new
84
+ end
85
+ config
86
+ }
87
+ config
88
+ end
89
+
90
+ def exists?
91
+ File.exists?(local_path)
92
+ end
93
+
94
+ def local_path
95
+ self.working_dir + self.name.to_s
96
+ end
97
+
98
+ def branches
99
+ self.git.branches.map(&:name)
100
+ end
101
+
102
+ #http://docs.travis-ci.com/user/build-configuration/#The-Build-Matrix
103
+ def build_runner_config(config)
104
+ runner = Runner.new()
105
+ runner.config = config
106
+ runner.repo = self
107
+ self.branch = runner_branch
108
+ self.runner = runner
109
+ end
110
+
111
+ def runner_branch
112
+ case self.branch
113
+ when :all
114
+ self.branches
115
+ when nil
116
+ ["master"]
117
+ else
118
+ self.branches.include?(self.branch) ? [self.branch] : ["master"]
119
+ end
120
+ end
121
+
122
+ def add_commit(sha, branch)
123
+ if runner_branch.include?(branch)
124
+ @new_commit = Perkins::Commit.new(sha, self)
125
+ @new_commit.branch = branch
126
+ enqueue_commit(@new_commit)
127
+ else
128
+ puts "skipping commit from branch #{branch}"
129
+ end
130
+ end
131
+
132
+ def enqueue_commit(commit)
133
+ $redis.publish("commits", {id: self.id.to_s, name: self.name , sha: commit.sha, branch: commit.branch}.to_json)
134
+ end
135
+
136
+ def http_url
137
+ new_url = self.url.include?("http") ? self.url : self.url.gsub(":", "/")
138
+ new_url.gsub!("git@", "https://")
139
+ new_url.gsub!(".git", "")
140
+ new_url
141
+ end
142
+
143
+ end
144
+
145
+ end
@@ -0,0 +1,124 @@
1
+
2
+ module Perkins
3
+ class Runner
4
+
5
+ attr_accessor :repo, :command, :config, :sha, :branch
6
+ attr_reader :build_time, :duration, :response, :status, :current_build
7
+
8
+ def exec(cmd)
9
+ result = run_script(cmd)
10
+ puts result
11
+ #result = `bash #{cmd} 2>&1`.chomp
12
+ #result = `go run #{ROOT_PATH + '/lib/perkins/build/runner.go'} -cmd="#{cmd}"`.chomp
13
+ process_status = $?
14
+
15
+ if successful_command?(process_status) || config_command_with_empty_value?(result,process_status)
16
+ @response = result
17
+ @status = true
18
+ return result
19
+ else
20
+ @response = result
21
+ @status = false
22
+ #raise "command failed"
23
+ end
24
+ end
25
+
26
+
27
+ def run_script(source)
28
+ script = File.expand_path(
29
+ "~/.perkins/.build/#{repo.name}/travis-build-#{sha}" #<< stages.join('-')
30
+ )
31
+ FileUtils.mkdir_p(File.dirname(script))
32
+ File.open(script, 'w') { |f| f.write(source) }
33
+ FileUtils.chmod(0755, script)
34
+ Bundler.with_clean_env{
35
+ `bash #{script} 2>&1`.chomp
36
+ }
37
+ end
38
+
39
+ def run(sha)
40
+
41
+ self.sha = sha
42
+ start_build
43
+ script = Perkins::Build::script(config, repo)
44
+ sh = script.compile
45
+ repo.git.chdir do
46
+ git_update(sha)
47
+ set_build_stats do
48
+ puts "perform build"
49
+ self.exec(sh)
50
+ end
51
+ end
52
+ store_report
53
+ stop_build
54
+ end
55
+
56
+ def start_build
57
+ #self.update_attributes(status: true)
58
+ @running = true #self.status
59
+ #$redis.set("#{repo.name}:running", true)
60
+ end
61
+
62
+ def stop_build
63
+ #self.update_attributes(status: false)
64
+ @running = false #self.status
65
+ #redis.set("#{repo.name}:running", false)
66
+ end
67
+
68
+ def set_build_stats(&block)
69
+ up = Time.now
70
+ #call the command itself
71
+ block.call
72
+ down = Time.now
73
+ @build_time = down
74
+ @duration = down - up
75
+ end
76
+
77
+ def running?
78
+ #repo.running?
79
+ @running
80
+ end
81
+
82
+ def successful_command?(process_status)
83
+ process_status.exitstatus.to_i == 0
84
+ end
85
+
86
+ def config_command_with_empty_value?(result, process_status)
87
+ process_status.exitstatus.to_i == 1 && result.empty?
88
+ end
89
+
90
+ def working_dir
91
+ repo.git.dir.path
92
+ end
93
+
94
+ def git_update(branch)
95
+ #`git fetch origin && git reset --hard #{sha}`
96
+ puts "git fetch & reset to #{sha}"
97
+ repo.git.fetch()
98
+ repo.git.reset_hard(sha)
99
+ end
100
+
101
+ def to_report
102
+ {id: SecureRandom.hex ,
103
+ build_time: self.build_time ,
104
+ duration: self.duration,
105
+ response: self.response,
106
+ status: self.status,
107
+ sha: self.sha,
108
+ branch: self.branch}
109
+ end
110
+
111
+ def store_report
112
+ r = Perkins::BuildReport.new(self.to_report)
113
+ #return if report.keys.detect{|o| report[o].to_s == "" }
114
+ #builds_to_save = get_builds << self.to_report
115
+ #$redis.set("#{repo.name}:builds", builds_to_save.to_json )
116
+ repo.build_reports << r
117
+ end
118
+
119
+ def get_builds
120
+ repo.build_reports
121
+ end
122
+
123
+ end
124
+ end
@@ -0,0 +1,314 @@
1
+ require 'sinatra/base'
2
+ require 'sinatra/activerecord'
3
+ #require 'sinatra/reloader' #if ENV['RACK_ENV'] == "development"
4
+ require 'pry'
5
+ require 'json'
6
+ require 'haml'
7
+ require 'sprockets'
8
+ require 'coffee_script'
9
+ require "sass"
10
+ require "rdiscount"
11
+ require "perkins/auth/github"
12
+
13
+ module Perkins
14
+
15
+ class Assets < Sinatra::Base
16
+
17
+ configure do
18
+ set :assets, (Sprockets::Environment.new { |env|
19
+ env.append_path(settings.root + "/assets/images")
20
+ env.append_path(settings.root + "/assets/javascripts")
21
+ env.append_path(settings.root + "/assets/stylesheets")
22
+ # compress everything in production
23
+ if ENV["RACK_ENV"] == "production"
24
+ env.js_compressor = YUI::JavaScriptCompressor.new
25
+ env.css_compressor = YUI::CssCompressor.new
26
+ end
27
+ })
28
+ end
29
+
30
+ get "/assets/app.js" do
31
+ content_type("application/javascript")
32
+ settings.assets["app.js"]
33
+ end
34
+
35
+ get "/assets/app.css" do
36
+ content_type("text/css")
37
+ settings.assets["app.css"]
38
+ end
39
+
40
+ %w{jpg png}.each do |format|
41
+ get "/assets/:image.#{format}" do |image|
42
+ content_type("image/#{format}")
43
+ settings.assets["#{image}.#{format}"]
44
+ end
45
+ end
46
+
47
+ get "/assets/:image.svg" do |image|
48
+ content_type("image/svg+xml")
49
+ settings.assets["#{image}.svg"]
50
+ end
51
+
52
+ end
53
+
54
+ class Server < Sinatra::Application
55
+
56
+ attr_reader :app
57
+ set server: 'thin', connections: []
58
+ set :sessions, true
59
+ set :foo, 'bar'
60
+ enable :sessions
61
+ #set :environment, :production
62
+ set :session_secret, '*&(^B234'
63
+
64
+ use Perkins::Assets
65
+
66
+ register Perkins::Auth::Github
67
+
68
+ get '/' do
69
+ authenticate!
70
+ haml :index , locals: default_data
71
+ end
72
+
73
+ get '/config' do
74
+ settings.perkins_application.to_json
75
+ end
76
+
77
+ get '/repos/sync' do
78
+ @github_repos = build_data
79
+ end
80
+
81
+ post '/repos/receiver' do
82
+ #NEEDS VALIDATIONS
83
+ payload = JSON.parse request.body.read
84
+ repo = Perkins::Repo.added.find_by(gb_id: payload["repository"]["id"])
85
+ repo.load_git
86
+
87
+ pushed_branch = payload["ref"].split('/').last
88
+ repo.add_commit(payload["after"], pushed_branch)
89
+ #binding.pry
90
+ "received post #{payload}"
91
+ end
92
+
93
+ get '/repos/add/:id' do
94
+ #ADD REPO HERE
95
+ repo = github_user.api.repo(params[:id].to_i)
96
+ Repo.sync_github_repo(repo)
97
+ r = Repo.add_from_github(params[:id])
98
+ r.to_json
99
+ redirect "/me"
100
+ end
101
+
102
+ get '/repos/:login/:name' do
103
+ repo = find_repo(params)
104
+ record = repo.build_reports.last
105
+ haml :"repos/repo" , locals: {repo: repo, build: record }
106
+ end
107
+
108
+ get '/repos/:login/:name/builds' do
109
+ repo = find_repo(params)
110
+ haml :builds , locals: {repo: repo, builds: repo.build_reports }
111
+ end
112
+
113
+ get '/repos/:login/:name/builds/:build_id' do
114
+ repo = find_repo(params)
115
+ record = repo.build_reports.find(params[:build_id])
116
+
117
+ haml :"repos/repo" , locals: {repo: repo, build: record }
118
+ end
119
+
120
+ get '/repos/:login/:name/run_commit' do
121
+ repo = find_repo(params)
122
+ sha = repo.git.log.map(&:sha).first
123
+ repo.add_commit(sha, "master")
124
+ redirect "/repos/#{repo.name}"
125
+ end
126
+
127
+ get '/repos/:login/:name/config' do
128
+ repo = find_repo(params)
129
+ #find by id
130
+ hook = github_user.api.hooks("#{repo.name}").detect{|o| o[:name] == "web"}
131
+
132
+ haml :"repos/config" , locals: {repo: repo, hook: hook}
133
+ end
134
+
135
+ post '/repos/:login/:name/add_hook' do
136
+ repo = find_repo(params)
137
+ hook = github_user.api.hooks("#{repo.name}").detect{|o| o[:name] == "web"}
138
+ if hook.present?
139
+ res = github_user.api.edit_hook(
140
+ repo.name,
141
+ hook["id"],
142
+ 'web',
143
+ {:url => params[:webhook_url], :content_type => 'json'},
144
+ {:active => true}
145
+ )
146
+ else
147
+ res = github_user.api.create_hook(
148
+ repo.name,
149
+ 'web',
150
+ { :url => params[:webhook_url], :content_type => 'json'},
151
+ { :events => ['push'], :active => true}
152
+ )
153
+ end
154
+ redirect "/repos/#{repo.name}/config"
155
+ end
156
+
157
+ get '/repos/:login/:name/builds/:build_id/restart' do
158
+ repo = find_repo(params)
159
+ record = repo.build_reports.find(params[:build_id])
160
+ repo.add_commit(record['sha'], record['branch'])
161
+ redirect "/repos/#{repo.name}"
162
+ end
163
+
164
+ get '/stream', provides: 'text/event-stream' do
165
+ stream :keep_open do |out|
166
+ settings.connections << out
167
+ out.callback { settings.connections.delete(out) }
168
+ end
169
+ end
170
+
171
+ post '/sse' do
172
+ settings.connections.each { |out| out << "data: #{params[:msg]}\n\n" }
173
+ 204 # response without entity body
174
+ end
175
+
176
+ get '/me' do
177
+ authenticate!
178
+ #binding.pry
179
+ #repos = github_repos
180
+ haml :profile, locals: { user: github_user.api }
181
+ #haml :"repos/github", locals: {repos: repos}
182
+ #"Hello There, #{github_user.name}! You have access to the #{params['id']} organization."
183
+ end
184
+
185
+ get '/myrepos' do
186
+ authenticate!
187
+ repos = github_user.api.repos
188
+ haml :"repos/github", locals: { user: github_user.api, repos: repos }
189
+ end
190
+
191
+ get '/orgs/:id' do
192
+ github_organization_authenticate!(params['id'])
193
+ #"Hello There, #{github_user.name}! You have access to the #{params['id']} organization."
194
+ org = github_user.api.organization(params['id'])
195
+ orgs = github_user.api.orgs
196
+ repos = github_user.api.org_repos(params["id"])
197
+ haml :orgs, locals: { org: org, orgs: orgs, repos: repos }
198
+ end
199
+
200
+ get '/publicized_orgs/:id' do
201
+ github_publicized_organization_authenticate!(params['id'])
202
+ "Hello There, #{github_user.name}! You are publicly a member of the #{params['id']} organization."
203
+ end
204
+
205
+ get '/teams/:id' do
206
+ github_team_authenticate!(params['id'])
207
+ "Hello There, #{github_user.name}! You have access to the #{params['id']} team."
208
+ end
209
+
210
+ get '/logout' do
211
+ logout!
212
+ redirect 'https://github.com'
213
+ end
214
+
215
+ def initialize(*args)
216
+ super
217
+ puts "Start Perkins server"
218
+ #check_project
219
+ #@app = Perkin.new(options.project_path)
220
+ end
221
+
222
+ def github_repos
223
+ @github_repos ||= persisted_repos.any? ? persisted_repos : build_data
224
+ end
225
+
226
+ def build_data
227
+ Repo.sync_github_repos(github_user)
228
+ redirect "/me"
229
+ end
230
+
231
+ def persisted_repos
232
+ Repo.synced_records
233
+ end
234
+
235
+ def find_repo(params)
236
+ id = "#{params[:login]}/#{params[:name]}"
237
+ repo = Repo.find_by(name: id)
238
+ repo.load_git
239
+ repo
240
+ end
241
+
242
+ def self.start(config_file, options)
243
+
244
+ options = options.dup
245
+ ENV['RACK_ENV'] = options.delete("e")
246
+
247
+ app = eval(File.open(config_file).read)
248
+ self.start_listener(app)
249
+
250
+ set :perkins_application, app
251
+ set :github_options, {
252
+ :scopes => "admin:repo_hook,repo,user:email",
253
+ :secret => app.github_client_secret,
254
+ :client_id => app.github_client_id,
255
+ }
256
+
257
+ register Sinatra::ActiveRecordExtension
258
+
259
+ configure :development do
260
+ #register Sinatra::Reloader
261
+ #set :database, 'sqlite:///db/development.sqlite3'
262
+ end
263
+
264
+ configure :production do
265
+ #set :database, 'sqlite:///db/productions.sqlite3'
266
+ end
267
+
268
+ configure :test do
269
+ #set :database, 'sqlite:///db/test.sqlite3'
270
+ end
271
+
272
+ Perkins::Server.run! options
273
+ end
274
+
275
+ def self.start_listener(app_config)
276
+ fork do
277
+ listener = ::Perkins::Listener.new
278
+ listener.app = app_config
279
+ listener.run!
280
+ end
281
+ end
282
+
283
+ ##use in rack
284
+ ##And a corresponding config.ru:
285
+ #require './app'
286
+ #run Sinatra::Application
287
+ def default_data
288
+ {author: "miguel michelson", year: Time.now.year, app: @app}
289
+ end
290
+
291
+ helpers do
292
+ def status_label(status)
293
+ label = status == true ? "success" : "default"
294
+ msg = status ? "passed" : "error"
295
+ "<span class='label label-#{label}'>build | #{msg}</span>"
296
+ end
297
+
298
+ def status_class(status)
299
+ status ? "glyphicon glyphicon-ok" : "glyphicon glyphicon-remove"
300
+ end
301
+
302
+
303
+ def avatar_url(email, size)
304
+ gravatar_id = Digest::MD5.hexdigest(email.downcase)
305
+ "http://gravatar.com/avatar/#{gravatar_id}.png?s=#{size}"
306
+ end
307
+
308
+ def commit_url(repo, sha)
309
+ "#{repo.http_url}/commit/#{sha}"
310
+ end
311
+ end
312
+ end
313
+
314
+ end
@@ -0,0 +1,79 @@
1
+ module Perkins
2
+ module ThorUtils
3
+ include Thor::Actions
4
+
5
+ def self.source_root
6
+ File.dirname(__FILE__)
7
+ end
8
+
9
+ def create_new_file(name, file=nil)
10
+ log "Creating #{name}"
11
+ contents = file.nil? ? '' : File.read(file)
12
+ unless File.file?(location.join(name))
13
+ File.open(location.join(name), 'w') { |f| f.write(contents) }
14
+ end
15
+ end
16
+
17
+ def remove_files(*files)
18
+ files.each do |file|
19
+ log "Removing #{file} file."
20
+ FileUtils.rm(location.join(file))
21
+ end
22
+ end
23
+
24
+ def touch(*filenames)
25
+ filenames.each do |filename|
26
+ log "Creating #{filename} file."
27
+ FileUtils.touch(location.join(filename))
28
+ end
29
+ end
30
+
31
+ def create_directories(*dirs)
32
+ dirs.each do |dir|
33
+ log "Creating the #{dir} directory."
34
+ FileUtils.mkdir_p(location.join(dir))
35
+ end
36
+ end
37
+
38
+ def remove_directories(*names)
39
+ names.each do |name|
40
+ log "Removing #{name} directory."
41
+ FileUtils.rm_rf(location.join(name))
42
+ end
43
+ end
44
+
45
+ def create_with_template(name, template_location, contents={})
46
+ template = templates("#{template_location}.erb")
47
+ eruby = Erubis::Eruby.new(File.read(template))
48
+ File.open(location.join(name.gsub(/^\//, '')), 'w') { |f| f.write(eruby.result(contents))}
49
+ end
50
+
51
+ def templates(path)
52
+ ::Perkins.root.join('pullentity-client/templates').join(path)
53
+ end
54
+
55
+ def log(msg)
56
+ ::Perkins::Logger.report(msg)
57
+ end
58
+
59
+ def error(msg)
60
+ ::Perkins::Logger.error(msg)
61
+ end
62
+
63
+ def base_location
64
+ @location ||= Pathname.new(Dir.pwd)
65
+ end
66
+
67
+ alias_method :location, :base_location
68
+
69
+ def underscore(string)
70
+ string.gsub(/::/, '/').
71
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
72
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
73
+ tr("-", "_").
74
+ downcase
75
+ end
76
+
77
+ end
78
+
79
+ end
@@ -0,0 +1,3 @@
1
+ module Perkins
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1 @@
1
+ 401
@@ -0,0 +1,46 @@
1
+
2
+
3
+ = haml :"repos/menu" , locals:{ repo: repo, active: "builds", build: nil}
4
+
5
+ .title
6
+ %h2#repo-name= repo.name
7
+ %h4= repo.url
8
+
9
+ .panel.panel-default
10
+ .panel-heading Builds
11
+ .panel-body
12
+
13
+ %table.table
14
+ %thead
15
+ %tr
16
+ %th Build
17
+ %th Message
18
+ %th Commit
19
+ %th Duration
20
+ %th Finished
21
+ %tbody
22
+
23
+ - builds.each do |build|
24
+
25
+ %tr.build-row
26
+ %td
27
+ %a{href: "/repos/#{repo.name}/builds/#{build.id}"}
28
+ %span{class: status_class(build['status']) }
29
+ = build['_id'].to_s[0..7]
30
+
31
+ %td
32
+ = build.commit.message.split("\n")[0]
33
+ %td
34
+ %a{href: commit_url(repo, build.commit.sha), target: :blank}
35
+ #{build.commit.sha[0..7]}
36
+ - if build.branch.present?
37
+ %strong
38
+ = "(#{build.branch})"
39
+
40
+ %td
41
+ = "#{build.duration.to_i.round(2)} secs"
42
+ %td
43
+ %abbr.timeago{title:build.build_time}
44
+
45
+
46
+