magi 0.1.0 → 0.1.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.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/README.md +4 -2
- data/app/assets/javascripts/application.js +3 -0
- data/app/assets/javascripts/events.js.coffee +9 -5
- data/app/assets/javascripts/magi/build_view.js.coffee +13 -0
- data/app/assets/javascripts/magi/job_view.js.coffee +8 -7
- data/app/assets/javascripts/magi/server_event.js.coffee +4 -7
- data/app/controllers/events_controller.rb +0 -7
- data/app/models/build.rb +7 -3
- data/app/models/job.rb +1 -1
- data/app/views/jobs/_form.html.slim +9 -3
- data/app/views/jobs/show.html.slim +2 -2
- data/config/environments/development.rb +1 -1
- data/config/routes.rb +1 -1
- data/lib/magi/executer.rb +5 -1
- data/lib/magi/proprietary.rb +29 -3
- data/lib/magi/version.rb +1 -1
- data/magi.gemspec +1 -0
- data/plugins/git/git.rb +44 -36
- data/plugins/simplecov/simplecov.rb +85 -0
- metadata +18 -3
- data/app/assets/javascripts/magi/job_model.js.coffee +0 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 00e582eed9e7365b07a029623587010791632dae
|
4
|
+
data.tar.gz: d25ee1877b2ddcdfbb3ed4a8b678f3f837b8c50c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 501d6fb97fae40d4b13bf4add9e97dc645e7d7b87845cb76b56260cf9625bc840f7494038c0c74f6fa53ed8782fae12a8db5a98d402be99a13602b35df0da040
|
7
|
+
data.tar.gz: 5e06f62c92daffb43bbe257f74cb250cd43d4fd0dda0c45cbae1eefaa9f76133c6df10d5ceea54e47975b3ee2345bf9dcfabdc9d4daf953597a712103e57e754
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# Magi
|
2
|
-
|
2
|
+
Casual CI server system (magi is a temporal name).
|
3
3
|
|
4
4
|
## Usage
|
5
5
|
```
|
@@ -17,10 +17,12 @@ Magi is just a rails application with some middlewares.
|
|
17
17
|
* foreman: process manager
|
18
18
|
* mysql: store jobs & builds
|
19
19
|
* resque: background worker using redis
|
20
|
-
* autodoc: generate [RESTful API documents](https://github.com/r7kamura/magi/blob/master/doc) from request-specs
|
21
20
|
* jquery: ajax updated view
|
21
|
+
* redis: build started/finished notification by pubsub system
|
22
|
+
* autodoc: generate [RESTful API documents](https://github.com/r7kamura/magi/blob/master/doc) from request-specs
|
22
23
|
|
23
24
|
### TODO
|
25
|
+
* authentication (with read, write)
|
24
26
|
* build stop button
|
25
27
|
* self-repairing system
|
26
28
|
* plugin updater
|
@@ -1,9 +1,13 @@
|
|
1
1
|
$ ->
|
2
2
|
$('.jobs_controller.index_action').each ->
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
new Magi.ServerEvent().on 'build.started build.finished', (attributes) ->
|
4
|
+
view = new Magi.JobView
|
5
|
+
el: "#job#{attributes.job_id}"
|
6
|
+
model: new Backbone.Model(attributes)
|
7
|
+
view.render()
|
6
8
|
|
9
|
+
$('.jobs_controller.show_action').each ->
|
10
|
+
view = new Magi.BuildView()
|
7
11
|
event = new Magi.ServerEvent()
|
8
|
-
event.
|
9
|
-
|
12
|
+
event.on 'build.finished', (attributes) ->
|
13
|
+
view.model.set(attributes)
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class window.Magi.BuildView
|
2
|
+
constructor: () ->
|
3
|
+
@model = new Backbone.Model()
|
4
|
+
@model.on 'change', => @render()
|
5
|
+
|
6
|
+
element: ->
|
7
|
+
$("#build#{@model.get('id')}")
|
8
|
+
|
9
|
+
render: ->
|
10
|
+
@element()
|
11
|
+
.removeClass()
|
12
|
+
.addClass(@model.get('status'))
|
13
|
+
.find('.time').text(@model.get('finished_at'))
|
@@ -1,9 +1,10 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
element: ->
|
5
|
-
$("#job#{@job.attributes.job_id}")
|
1
|
+
window.Magi.JobView = Backbone.View.extend
|
2
|
+
initialize: () ->
|
6
3
|
|
7
4
|
render: ->
|
8
|
-
|
9
|
-
|
5
|
+
console.log(@el)
|
6
|
+
console.log(@$el)
|
7
|
+
@$el.removeClass().addClass(@status())
|
8
|
+
|
9
|
+
status: ->
|
10
|
+
@model.get('status')
|
@@ -2,10 +2,7 @@ class window.Magi.ServerEvent
|
|
2
2
|
constructor: ->
|
3
3
|
@source = new EventSource('/events')
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
onBuildFinish: (callback) ->
|
10
|
-
@source.addEventListener 'build.finish', (event) ->
|
11
|
-
callback($.parseJSON(event.data))
|
5
|
+
on: (string, callback) ->
|
6
|
+
for eventName in string.split(' ')
|
7
|
+
@source.addEventListener eventName, (event) ->
|
8
|
+
callback($.parseJSON(event.data))
|
@@ -21,13 +21,6 @@ class EventsController < ApplicationController
|
|
21
21
|
|
22
22
|
private
|
23
23
|
|
24
|
-
def index_with_redis_exception
|
25
|
-
index_without_redis_exception
|
26
|
-
rescue Redis::CannotConnectError
|
27
|
-
head 503
|
28
|
-
end
|
29
|
-
alias_method_chain :index, :redis_exception
|
30
|
-
|
31
24
|
def require_event_stream
|
32
25
|
response.headers["Content-Type"] = "text/event-stream"
|
33
26
|
end
|
data/app/models/build.rb
CHANGED
@@ -53,16 +53,20 @@ class Build < ActiveRecord::Base
|
|
53
53
|
private
|
54
54
|
|
55
55
|
def notify(type)
|
56
|
-
$redis.publish("build.#{type}",
|
56
|
+
$redis.publish("build.#{type}", notified_data)
|
57
|
+
end
|
58
|
+
|
59
|
+
def notified_data
|
60
|
+
{ id: id, job_id: job.id, status: status_name, finished_at: finished_at }.to_json
|
57
61
|
end
|
58
62
|
|
59
63
|
def start
|
60
64
|
update_attributes!(started_at: Time.now)
|
61
|
-
notify(:
|
65
|
+
notify(:started)
|
62
66
|
end
|
63
67
|
|
64
68
|
def finish(result)
|
65
69
|
reload.update_attributes!(finished_at: Time.now, output: result[:output], status: result[:status])
|
66
|
-
notify(:
|
70
|
+
notify(:finished)
|
67
71
|
end
|
68
72
|
end
|
data/app/models/job.rb
CHANGED
@@ -100,7 +100,7 @@ class Job < ActiveRecord::Base
|
|
100
100
|
end
|
101
101
|
|
102
102
|
def update_attributes_with_properties(params)
|
103
|
-
params.slice(:name, *self.class.
|
103
|
+
params.slice(:name, *self.class.property_names).each do |key, value|
|
104
104
|
send("#{key}=", value)
|
105
105
|
end
|
106
106
|
tap(&:save)
|
@@ -18,10 +18,16 @@ section.job_settings
|
|
18
18
|
p= f.label :schedule
|
19
19
|
p= f.text_field :schedule, placeholder: "* * * * *"
|
20
20
|
|
21
|
-
-
|
21
|
+
- resource.class.properties.each do |property|
|
22
|
+
- next if property.name.in?([:description, :schedule, :script])
|
22
23
|
.field
|
23
|
-
p= f.label name
|
24
|
-
p
|
24
|
+
p= f.label property.name
|
25
|
+
p
|
26
|
+
- case property.type
|
27
|
+
- when :boolean
|
28
|
+
= f.check_box property.name, {}, "true", "false"
|
29
|
+
- else
|
30
|
+
= f.text_field property.name
|
25
31
|
|
26
32
|
.field
|
27
33
|
p= f.submit class: "button"
|
@@ -18,9 +18,9 @@ section.builds
|
|
18
18
|
|
19
19
|
ul
|
20
20
|
- @resource.builds.page(params[:page]).recent.limit(10).each do |build|
|
21
|
-
li class =
|
21
|
+
li class=build.status_name id="build#{build.id}"
|
22
22
|
= link_to [@resource, build] do
|
23
23
|
span.id ##{build.id}
|
24
|
-
span= build.finished_at
|
24
|
+
span.time= build.finished_at
|
25
25
|
|
26
26
|
= paginate @resource.builds.page(params[:page])
|
@@ -4,7 +4,7 @@ Magi::Application.configure do
|
|
4
4
|
# In the development environment your application's code is reloaded on
|
5
5
|
# every request. This slows down response time but is perfect for development
|
6
6
|
# since you don't have to restart the web server when you make code changes.
|
7
|
-
config.cache_classes =
|
7
|
+
config.cache_classes = ENV["DISABLE_CACHE_CLASSES"] != "1"
|
8
8
|
|
9
9
|
# Show full error reports and disable caching
|
10
10
|
config.consider_all_requests_local = true
|
data/config/routes.rb
CHANGED
data/lib/magi/executer.rb
CHANGED
data/lib/magi/proprietary.rb
CHANGED
@@ -10,21 +10,47 @@ module Magi
|
|
10
10
|
end
|
11
11
|
|
12
12
|
module ClassMethods
|
13
|
-
def property(name)
|
14
|
-
|
13
|
+
def property(name, options = {})
|
14
|
+
property = Property.new(name, options)
|
15
|
+
properties << property
|
15
16
|
|
16
17
|
define_method(name) do
|
17
18
|
properties[name.to_s]
|
18
19
|
end
|
19
20
|
|
20
21
|
define_method("#{name}=") do |value|
|
21
|
-
properties[name.to_s] =
|
22
|
+
properties[name.to_s] = begin
|
23
|
+
case
|
24
|
+
when property.type != :boolean
|
25
|
+
value
|
26
|
+
when value.to_s == "false"
|
27
|
+
false
|
28
|
+
else
|
29
|
+
true
|
30
|
+
end
|
31
|
+
end
|
22
32
|
end
|
23
33
|
end
|
24
34
|
|
25
35
|
def properties
|
26
36
|
@properties ||= []
|
27
37
|
end
|
38
|
+
|
39
|
+
def property_names
|
40
|
+
properties.map(&:name)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class Property
|
45
|
+
attr_reader :name, :options
|
46
|
+
|
47
|
+
def initialize(name, options = {})
|
48
|
+
@name, @options = name, options
|
49
|
+
end
|
50
|
+
|
51
|
+
def type
|
52
|
+
options[:type] || :string
|
53
|
+
end
|
28
54
|
end
|
29
55
|
end
|
30
56
|
end
|
data/lib/magi/version.rb
CHANGED
data/magi.gemspec
CHANGED
@@ -36,6 +36,7 @@ Gem::Specification.new do |spec|
|
|
36
36
|
spec.add_dependency "puma"
|
37
37
|
spec.add_dependency "quiet_assets"
|
38
38
|
spec.add_dependency "rails", ">= 4.0.0.rc2"
|
39
|
+
spec.add_dependency "rails-backbone"
|
39
40
|
spec.add_dependency "rake"
|
40
41
|
spec.add_dependency "redis"
|
41
42
|
spec.add_dependency "resque"
|
data/plugins/git/git.rb
CHANGED
@@ -8,17 +8,37 @@ module Magi
|
|
8
8
|
@job = job
|
9
9
|
end
|
10
10
|
|
11
|
+
def before_enqueue
|
12
|
+
if has_repository_url?
|
13
|
+
clone unless cloned?
|
14
|
+
update
|
15
|
+
updated_since_last_finished_build?
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def before_execute
|
20
|
+
if has_repository_url?
|
21
|
+
clone
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def after_execute
|
26
|
+
if has_repository_url?
|
27
|
+
job.current_build.update_properties(revision: revision)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
11
31
|
def clone
|
12
|
-
|
13
|
-
command("git clone #{job.
|
32
|
+
validate_existence_of_repository_url
|
33
|
+
command("git clone #{job.repository_url} #{path}")
|
14
34
|
end
|
15
35
|
|
16
36
|
def update
|
17
|
-
command("cd #{
|
37
|
+
command("cd #{path} && git pull")
|
18
38
|
end
|
19
39
|
|
20
40
|
def revision
|
21
|
-
command("cd #{
|
41
|
+
command("cd #{path} && git rev-parse HEAD").rstrip
|
22
42
|
end
|
23
43
|
|
24
44
|
def updated_since_last_finished_build?
|
@@ -26,59 +46,47 @@ module Magi
|
|
26
46
|
end
|
27
47
|
|
28
48
|
def cloned?
|
29
|
-
|
49
|
+
path.join(".git").exist?
|
30
50
|
end
|
31
51
|
|
32
|
-
private
|
33
|
-
|
34
52
|
def command(script)
|
35
53
|
Open3.capture3(script)[0]
|
36
54
|
end
|
37
55
|
|
38
|
-
def
|
39
|
-
|
56
|
+
def path
|
57
|
+
job.workspace.path + "repository"
|
58
|
+
end
|
59
|
+
|
60
|
+
def has_repository_url?
|
61
|
+
job.repository_url.present?
|
40
62
|
end
|
41
63
|
|
42
|
-
|
64
|
+
private
|
65
|
+
|
66
|
+
def validate_existence_of_repository_url
|
67
|
+
raise RepositoryUrlNotFound unless has_repository_url?
|
68
|
+
end
|
69
|
+
|
70
|
+
class RepositoryUrlNotFound < StandardError
|
43
71
|
def message
|
44
|
-
"You must set `
|
72
|
+
"You must set `repository_url`"
|
45
73
|
end
|
46
74
|
end
|
47
75
|
end
|
48
76
|
end
|
49
77
|
|
50
78
|
Job.class_eval do
|
51
|
-
property(:
|
79
|
+
property(:repository_url)
|
52
80
|
|
53
|
-
before_enqueue
|
54
|
-
if git_url.present?
|
55
|
-
repository.clone unless repository.cloned?
|
56
|
-
repository.update
|
57
|
-
repository.updated_since_last_finished_build?
|
58
|
-
end
|
59
|
-
end
|
81
|
+
before_enqueue { repository.before_enqueue }
|
60
82
|
|
61
|
-
before_execute
|
62
|
-
if git_url.present?
|
63
|
-
repository.clone unless repository.cloned?
|
64
|
-
end
|
65
|
-
end
|
83
|
+
before_execute { repository.before_execute }
|
66
84
|
|
67
|
-
after_execute
|
68
|
-
if git_url.present?
|
69
|
-
current_build.update_revision
|
70
|
-
end
|
71
|
-
end
|
85
|
+
after_execute { repository.after_execute }
|
72
86
|
|
73
87
|
def repository
|
74
88
|
@repository ||= Magi::Repository.new(self)
|
75
89
|
end
|
76
90
|
end
|
77
91
|
|
78
|
-
Build.
|
79
|
-
property(:revision)
|
80
|
-
|
81
|
-
def update_revision
|
82
|
-
update_properties(revision: job.repository.revision)
|
83
|
-
end
|
84
|
-
end
|
92
|
+
Build.property(:revision)
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require "json"
|
2
|
+
require "fileutils"
|
3
|
+
|
4
|
+
module Magi
|
5
|
+
class SimpleCov
|
6
|
+
attr_reader :job
|
7
|
+
|
8
|
+
def initialize(job)
|
9
|
+
@job = job
|
10
|
+
end
|
11
|
+
|
12
|
+
def after_execute
|
13
|
+
save if enabled? && has_coverage?
|
14
|
+
end
|
15
|
+
|
16
|
+
def enabled?
|
17
|
+
job.enable_simplecov == "1"
|
18
|
+
end
|
19
|
+
|
20
|
+
def workspace_path
|
21
|
+
job.workspace.path + "coverage"
|
22
|
+
end
|
23
|
+
|
24
|
+
def last_coverage
|
25
|
+
JSON.parse(last_run_json)["result"]["covered_percent"]
|
26
|
+
rescue JSON::ParserError, NoMethodError
|
27
|
+
end
|
28
|
+
|
29
|
+
def last_run_json
|
30
|
+
last_run_json_path.read
|
31
|
+
end
|
32
|
+
|
33
|
+
def has_last_run_json?
|
34
|
+
last_run_json_path.exist?
|
35
|
+
end
|
36
|
+
|
37
|
+
def has_coverage?
|
38
|
+
has_last_run_json?
|
39
|
+
end
|
40
|
+
|
41
|
+
def last_run_json_path
|
42
|
+
job.repository.path + "coverage/.last_run.json"
|
43
|
+
end
|
44
|
+
|
45
|
+
def assets_path
|
46
|
+
job.repository.path + "coverage/assets"
|
47
|
+
end
|
48
|
+
|
49
|
+
def assets_relative_path
|
50
|
+
assets_path.relative_path_from(current_build_path)
|
51
|
+
end
|
52
|
+
|
53
|
+
def result_html_path
|
54
|
+
job.repository.path + "coverage/index.html"
|
55
|
+
end
|
56
|
+
|
57
|
+
def current_build_path
|
58
|
+
workspace_path + job.current_build.id.to_s
|
59
|
+
end
|
60
|
+
|
61
|
+
def symlink_assets
|
62
|
+
File.symlink(assets_relative_path, current_build_path + "assets")
|
63
|
+
end
|
64
|
+
|
65
|
+
def copy_result_html
|
66
|
+
FileUtils.cp(result_html_path, current_build_path)
|
67
|
+
end
|
68
|
+
|
69
|
+
def save
|
70
|
+
current_build_path.mkpath
|
71
|
+
symlink_assets
|
72
|
+
copy_result_html
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
Job.class_eval do
|
78
|
+
property(:enable_simplecov, type: :boolean)
|
79
|
+
|
80
|
+
after_execute { coverage.after_execute }
|
81
|
+
|
82
|
+
def coverage
|
83
|
+
@coverage ||= Magi::SimpleCov.new(self)
|
84
|
+
end
|
85
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: magi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryo Nakamura
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-06-
|
11
|
+
date: 2013-06-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: clockwork
|
@@ -150,6 +150,20 @@ dependencies:
|
|
150
150
|
- - '>='
|
151
151
|
- !ruby/object:Gem::Version
|
152
152
|
version: 4.0.0.rc2
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: rails-backbone
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - '>='
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :runtime
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - '>='
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
153
167
|
- !ruby/object:Gem::Dependency
|
154
168
|
name: rake
|
155
169
|
requirement: !ruby/object:Gem::Requirement
|
@@ -287,7 +301,7 @@ files:
|
|
287
301
|
- app/assets/images/rails.png
|
288
302
|
- app/assets/javascripts/application.js
|
289
303
|
- app/assets/javascripts/events.js.coffee
|
290
|
-
- app/assets/javascripts/magi/
|
304
|
+
- app/assets/javascripts/magi/build_view.js.coffee
|
291
305
|
- app/assets/javascripts/magi/job_view.js.coffee
|
292
306
|
- app/assets/javascripts/magi/server_event.js.coffee
|
293
307
|
- app/assets/javascripts/magi.js.coffee
|
@@ -349,6 +363,7 @@ files:
|
|
349
363
|
- lib/magi.rb
|
350
364
|
- lib/tasks/resque.rake
|
351
365
|
- plugins/git/git.rb
|
366
|
+
- plugins/simplecov/simplecov.rb
|
352
367
|
- public/404.html
|
353
368
|
- public/422.html
|
354
369
|
- public/500.html
|