magi 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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()