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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f2f3aa7e90d1b48ae6d264fe18d45927e151fe74
4
- data.tar.gz: e08af400fadaf5e4ba012227a1a5ca827e988147
3
+ metadata.gz: 00e582eed9e7365b07a029623587010791632dae
4
+ data.tar.gz: d25ee1877b2ddcdfbb3ed4a8b678f3f837b8c50c
5
5
  SHA512:
6
- metadata.gz: 8aaacf0941adfe403a1949c99ae5b742a6dae0fbcfc983de921f7037cec182b719a5234bdbc7922761d4e74d414b2f64cc85522cf448dd881d76a6d2e5a011e6
7
- data.tar.gz: 25ce48204fb196d6133df7f04e004c1020f446dd79f33e582087a86fd62e5a10d6e10f57a4fa72635e988923b29693b7ff4cf528b13f6a2787d39598326f0585
6
+ metadata.gz: 501d6fb97fae40d4b13bf4add9e97dc645e7d7b87845cb76b56260cf9625bc840f7494038c0c74f6fa53ed8782fae12a8db5a98d402be99a13602b35df0da040
7
+ data.tar.gz: 5e06f62c92daffb43bbe257f74cb250cd43d4fd0dda0c45cbae1eefaa9f76133c6df10d5ceea54e47975b3ee2345bf9dcfabdc9d4daf953597a712103e57e754
data/Gemfile CHANGED
@@ -8,6 +8,7 @@ gem "font-awesome-rails"
8
8
  gem "jquery-rails"
9
9
  gem "kaminari"
10
10
  gem "quiet_assets"
11
+ gem "rails-backbone"
11
12
  gem "resque"
12
13
  gem "slim"
13
14
  gem "weak_parameters"
data/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  # Magi
2
- A continuous integration server implementation.
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
@@ -12,6 +12,9 @@
12
12
  //
13
13
  //= require jquery
14
14
  //= require jquery_ujs
15
+ //= require underscore
16
+ //= require backbone
17
+ //
15
18
  //= require magi
16
19
  //= require_tree ./magi
17
20
  //= require_tree .
@@ -1,9 +1,13 @@
1
1
  $ ->
2
2
  $('.jobs_controller.index_action').each ->
3
- job = new Magi.JobModel()
4
- view = new Magi.JobView(job)
5
- job.onChange -> view.render()
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.onBuildStart (attributes) -> job.set(attributes)
9
- event.onBuildFinish (attributes) -> job.set(attributes)
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
- class window.Magi.JobView
2
- constructor: (@job) ->
3
-
4
- element: ->
5
- $("#job#{@job.attributes.job_id}")
1
+ window.Magi.JobView = Backbone.View.extend
2
+ initialize: () ->
6
3
 
7
4
  render: ->
8
- @element().removeClass()
9
- @element().addClass(@job.attributes.status)
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
- onBuildStart: (callback) ->
6
- @source.addEventListener 'build.start', (event) ->
7
- callback($.parseJSON(event.data))
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}", { id: id, job_id: job.id, status: status_name }.to_json)
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(:start)
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(:finish)
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.properties).each do |key, value|
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
- - (resource.class.properties - [:description, :schedule, :script]).each do |name|
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= f.text_field name
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 = build.status_name
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 = true
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
@@ -5,5 +5,5 @@ Magi::Application.routes.draw do
5
5
  resources :builds, only: [:index, :show, :create, :update, :destroy]
6
6
  end
7
7
 
8
- resources :events, only: :index
8
+ resources :events, only: :index if Rails.configuration.cache_classes
9
9
  end
data/lib/magi/executer.rb CHANGED
@@ -27,7 +27,11 @@ module Magi
27
27
  end
28
28
 
29
29
  def result
30
- @result ||= Open3.capture2e(script)
30
+ @result ||= Open3.capture2e(lines)
31
+ end
32
+
33
+ def lines
34
+ script.gsub("\n", ";")
31
35
  end
32
36
  end
33
37
  end
@@ -10,21 +10,47 @@ module Magi
10
10
  end
11
11
 
12
12
  module ClassMethods
13
- def property(name)
14
- properties << name
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] = value
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
@@ -1,3 +1,3 @@
1
1
  module Magi
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
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
- validate_existence_of_git_url
13
- command("git clone #{job.git_url} #{job.workspace.path}")
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 #{job.workspace.path} && git pull")
37
+ command("cd #{path} && git pull")
18
38
  end
19
39
 
20
40
  def revision
21
- command("cd #{job.workspace.path} && git rev-parse HEAD").rstrip
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
- job.workspace.path.join(".git").exist?
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 validate_existence_of_git_url
39
- raise GitUrlNotFound unless job.git_url
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
- class GitUrlNotFound < StandardError
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 `git_url`"
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(:git_url)
79
+ property(:repository_url)
52
80
 
53
- before_enqueue do
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 do
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 do
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.class_eval do
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.0
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-18 00:00:00.000000000 Z
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/job_model.js.coffee
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
@@ -1,12 +0,0 @@
1
- class window.Magi.JobModel
2
- constructor: (@attributes = {}) ->
3
-
4
- set: (attributes) ->
5
- @attributes[key] = value for key, value of attributes
6
- @change()
7
-
8
- onChange: (callback) ->
9
- @callback = callback
10
-
11
- change: ->
12
- @callback()