mason-server 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ group :test do
6
+ gem "contest"
7
+ gem "rake"
8
+ gem "rack-test"
9
+ gem "mason-client", :path => "../client"
10
+ gem "artifice", :git => "git://github.com/wycats/artifice"
11
+ end
@@ -0,0 +1,79 @@
1
+ GIT
2
+ remote: git://github.com/wycats/artifice
3
+ revision: c493eb5c359fd018b4135c1de8876953a44b193c
4
+ specs:
5
+ artifice (0.5)
6
+ rack-test
7
+
8
+ PATH
9
+ remote: /data/homedirs/ey/sr/buildozor/client
10
+ specs:
11
+ mason-client (0)
12
+ json
13
+ rack
14
+
15
+ PATH
16
+ remote: .
17
+ specs:
18
+ mason-server (0.0.1)
19
+ dm-aggregates (~> 1.0.2)
20
+ dm-core (~> 1.0.2)
21
+ dm-migrations (~> 1.0.2)
22
+ dm-postgres-adapter (~> 1.0.2)
23
+ dm-timestamps (~> 1.0.2)
24
+ json (~> 1.4.6)
25
+ open4 (~> 1.0)
26
+ sinatra (~> 1.0)
27
+
28
+ GEM
29
+ remote: http://rubygems.org/
30
+ specs:
31
+ addressable (2.2.1)
32
+ contest (0.1.2)
33
+ data_objects (0.10.2)
34
+ addressable (~> 2.1)
35
+ dm-aggregates (1.0.2)
36
+ dm-core (~> 1.0.2)
37
+ dm-core (1.0.2)
38
+ addressable (~> 2.2)
39
+ extlib (~> 0.9.15)
40
+ dm-do-adapter (1.0.2)
41
+ data_objects (~> 0.10.2)
42
+ dm-core (~> 1.0.2)
43
+ dm-migrations (1.0.2)
44
+ dm-core (~> 1.0.2)
45
+ dm-postgres-adapter (1.0.2)
46
+ dm-do-adapter (~> 1.0.2)
47
+ do_postgres (~> 0.10.2)
48
+ dm-timestamps (1.0.2)
49
+ dm-core (~> 1.0.2)
50
+ do_postgres (0.10.2)
51
+ data_objects (= 0.10.2)
52
+ extlib (0.9.15)
53
+ json (1.4.6)
54
+ open4 (1.0.1)
55
+ rack (1.2.1)
56
+ rack-test (0.5.4)
57
+ rack (>= 1.0)
58
+ rake (0.8.7)
59
+ sinatra (1.0)
60
+ rack (>= 1.0)
61
+
62
+ PLATFORMS
63
+ ruby
64
+
65
+ DEPENDENCIES
66
+ artifice!
67
+ contest
68
+ dm-aggregates (~> 1.0.2)
69
+ dm-core (~> 1.0.2)
70
+ dm-migrations (~> 1.0.2)
71
+ dm-postgres-adapter (~> 1.0.2)
72
+ dm-timestamps (~> 1.0.2)
73
+ json (~> 1.4.6)
74
+ mason-client!
75
+ mason-server!
76
+ open4 (~> 1.0)
77
+ rack-test
78
+ rake
79
+ sinatra (~> 1.0)
@@ -0,0 +1,8 @@
1
+ require "rake/testtask"
2
+
3
+ task :default => :test
4
+
5
+ Rake::TestTask.new(:test) do |t|
6
+ t.ruby_opts << "-Ilib" << "-Itest"
7
+ t.test_files = FileList["test/*_test.rb"]
8
+ end
@@ -0,0 +1,7 @@
1
+ require "mason_server"
2
+
3
+ MasonServer.setup(ENV["RACK_ENV"], "config/database.yml", "/mnt")
4
+
5
+ map "/" do
6
+ run MasonServer.app
7
+ end
@@ -0,0 +1,8 @@
1
+ development:
2
+ usermame: ey
3
+ password: eB3JNowOI3
4
+ database: ensemble_development
5
+ test:
6
+ usermame: ey
7
+ password: eB3JNowOI3
8
+ database: ensemble_test
@@ -0,0 +1,4 @@
1
+ test:
2
+ usermame: ey
3
+ password: foobar
4
+ database: buildorgium_test
@@ -0,0 +1,55 @@
1
+ require "pathname"
2
+
3
+ require "dm-core"
4
+ require "dm-aggregates"
5
+ require "dm-timestamps"
6
+ require "dm-migrations"
7
+ require "json"
8
+ require "sinatra"
9
+ require "open4"
10
+
11
+ require "mason_server/build"
12
+ require "mason_server/app"
13
+ require "mason_server/command_runner"
14
+ require "mason_server/deployment"
15
+
16
+ module MasonServer
17
+ class Error < StandardError; end
18
+
19
+ Request = Struct.new(:repo, :ref, :command, :callback)
20
+
21
+ def self.setup(environment, url, db_config, directory)
22
+ @deployment = Deployment.new(environment, url, db_config, directory)
23
+ end
24
+
25
+ def self.app
26
+ App
27
+ end
28
+
29
+ def self.enable_mock!
30
+ end
31
+
32
+ def self.url
33
+ @deployment.url
34
+ end
35
+
36
+ def self.setup_db
37
+ @deployment.setup_db
38
+ end
39
+
40
+ def self.reset_db
41
+ @deployment.reset_db
42
+ end
43
+
44
+ def self.directory
45
+ @deployment.directory
46
+ end
47
+
48
+ def self.deployment
49
+ unless @deployment
50
+ raise Error, "call setup first"
51
+ end
52
+
53
+ @deployment
54
+ end
55
+ end
@@ -0,0 +1,24 @@
1
+ module MasonServer
2
+ class App < Sinatra::Base
3
+ # TODO
4
+ enable :raise_errors
5
+ disable :show_exceptions
6
+
7
+ def _request
8
+ Request.new(*payload.values_at("repo", "ref", "command", "callback"))
9
+ end
10
+
11
+ def payload
12
+ JSON.parse(params["payload"])
13
+ end
14
+
15
+ post "/" do
16
+ if url = Build.run(_request)
17
+ headers["Location"] = url
18
+ 301
19
+ else
20
+ 500
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,87 @@
1
+ module MasonServer
2
+ class Build
3
+ include DataMapper::Resource
4
+
5
+ property :id, Serial
6
+ property :repo, String, :required => true
7
+ property :ref, String, :required => true
8
+ property :command, String, :required => true
9
+ property :callback, String, :required => true
10
+ property :status, Integer
11
+ property :output, Text
12
+ property :started_at, DateTime
13
+ property :completed_at, DateTime
14
+
15
+ timestamps :at
16
+
17
+ def self.run(request)
18
+ build = new(
19
+ :repo => request.repo,
20
+ :ref => request.ref,
21
+ :command => request.command,
22
+ :callback => request.callback
23
+ )
24
+
25
+ unless build.save
26
+ raise Error, "failed to create build for #{request.inspect}"
27
+ end
28
+
29
+ # TODO need aync
30
+ build.run
31
+ build.url
32
+ end
33
+
34
+ def url
35
+ "#{MasonServer.url}/#{id}"
36
+ end
37
+
38
+ def run
39
+ start
40
+ setup_checkout
41
+ run_command
42
+ complete
43
+ notify
44
+ end
45
+
46
+ def start
47
+ unless update(:started_at => Time.now)
48
+ raise Error, "failed to start build"
49
+ end
50
+ end
51
+
52
+ def setup_checkout
53
+ end
54
+
55
+ def run_command
56
+ status = CommandRunner.run(MasonServer.directory, command) do |chunk|
57
+ output_handler(chunk)
58
+ end
59
+
60
+ unless update(:status => status)
61
+ raise Error, "could not set status"
62
+ end
63
+ end
64
+
65
+ def output_handler(chunk)
66
+ # TODO StringIO
67
+ @_output ||= ""
68
+ @_output << chunk
69
+ update(:output => @_output)
70
+ rescue DataObjects::ConnectionError
71
+ end
72
+
73
+ def complete
74
+ unless update(:completed_at => Time.now)
75
+ raise Error, "failed to complete build"
76
+ end
77
+ end
78
+
79
+ def notify
80
+ Net::HTTP.post_form(URI(callback.to_s), "payload" => payload.to_json)
81
+ end
82
+
83
+ def payload
84
+ { "url" => url, "callback" => callback, "status" => status }
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,44 @@
1
+ module MasonServer
2
+ class CommandRunner
3
+ def self.run(directory, command, &handler)
4
+ new(directory, command, handler).run
5
+ end
6
+
7
+ def initialize(directory, command, handler)
8
+ @directory = Pathname(directory)
9
+ @command = command
10
+ @handler = handler
11
+ end
12
+
13
+ def run
14
+ file_write
15
+
16
+ status = Open4.spawn(
17
+ file.to_s,
18
+ :dir => @directory,
19
+ :out => self,
20
+ :err => self,
21
+ :quiet => true
22
+ )
23
+
24
+ status.exitstatus
25
+ end
26
+
27
+ def <<(chunk)
28
+ @handler.call(chunk)
29
+ end
30
+
31
+ def file_write
32
+ File.open(file, "w") { |f| f.puts file_content }
33
+ file.chmod(0700)
34
+ end
35
+
36
+ def file_content
37
+ "#!/bin/sh\n#{@command}"
38
+ end
39
+
40
+ def file
41
+ @tmp_file ||= @directory.join("mason#{$$}").expand_path
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,26 @@
1
+ module MasonServer
2
+ class Deployment
3
+ def initialize(environment, url, db_config_file, directory)
4
+ @environment = environment
5
+ @url = url
6
+ @db_config_file = db_config_file
7
+ @directory = directory
8
+ end
9
+
10
+ attr_reader :url, :directory
11
+
12
+ def setup_db
13
+ DataMapper.setup(:default, ("postgres://%s:%s@%s/%s" % db_config))
14
+ end
15
+
16
+ def reset_db
17
+ DataMapper.auto_migrate!
18
+ end
19
+
20
+ def db_config
21
+ @db_config ||= YAML.load_file(@db_config_file).
22
+ fetch(@environment).
23
+ values_at("username", "password", "host", "database")
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,17 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "mason-server"
3
+ s.version = "0.0.2"
4
+ s.summary = "builds things"
5
+
6
+ s.add_dependency "dm-core", "~>1.0.2"
7
+ s.add_dependency "dm-aggregates", "~>1.0.2"
8
+ s.add_dependency "dm-migrations", "~>1.0.2"
9
+ s.add_dependency "dm-timestamps", "~>1.0.2"
10
+ s.add_dependency "dm-postgres-adapter", "~>1.0.2"
11
+
12
+ s.add_dependency "open4", "~>1.0"
13
+ s.add_dependency "sinatra", "~>1.0"
14
+ s.add_dependency "json", "~>1.4.6"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ end
@@ -0,0 +1,23 @@
1
+ require "helper"
2
+
3
+ class CommandRunnerTest < Test::Unit::TestCase
4
+ test "output" do
5
+ output = ""
6
+
7
+ MasonServer::CommandRunner.run("tmp", "echo suck") do |chunk|
8
+ output << chunk
9
+ end
10
+
11
+ assert_equal "suck\n", output
12
+ end
13
+
14
+ test "success" do
15
+ result = MasonServer::CommandRunner.run("tmp", "true")
16
+ assert_equal 0, result
17
+ end
18
+
19
+ test "fail" do
20
+ result = MasonServer::CommandRunner.run("tmp", "false")
21
+ assert_equal 1, result
22
+ end
23
+ end
@@ -0,0 +1,9 @@
1
+ require "test/unit"
2
+ require "contest"
3
+ require "rack/test"
4
+ require "artifice"
5
+
6
+ require "helper/mock_receiver"
7
+
8
+ require "mason_client"
9
+ require "mason_server"
@@ -0,0 +1,17 @@
1
+ class MockReceiver
2
+ def initialize
3
+ @builds = {}
4
+ end
5
+
6
+ def [](url)
7
+ @builds[url]
8
+ end
9
+
10
+ def size
11
+ @builds.size
12
+ end
13
+
14
+ def handle(result)
15
+ @builds[result.url] = result
16
+ end
17
+ end
@@ -0,0 +1,81 @@
1
+ require "helper"
2
+
3
+ class MasonServerTest < Test::Unit::TestCase
4
+ include Rack::Test::Methods
5
+
6
+ def app
7
+ @app ||= Rack::Builder.new {
8
+ map "/callback" do
9
+ run MasonClient.app
10
+ end
11
+
12
+ map "/" do
13
+ run MasonServer.app
14
+ end
15
+ }
16
+ end
17
+
18
+ setup do
19
+ @receiver = MockReceiver.new
20
+
21
+ MasonServer.setup("test", "http://mason.example.com", "config/database.yml", "tmp")
22
+ MasonServer.setup_db
23
+ MasonServer.reset_db
24
+ MasonServer.enable_mock!
25
+
26
+ MasonClient.setup("http://mason.example.com/") do |result|
27
+ @receiver.handle(result)
28
+ end
29
+
30
+ Artifice.activate_with(app)
31
+ end
32
+
33
+ teardown do
34
+ Artifice.deactivate
35
+ end
36
+
37
+ =begin
38
+ test "fail request" do
39
+ response = MasonClient.build(
40
+ "git@github.com:sr/mason.git",
41
+ "HEAD",
42
+ nil,
43
+ "http://example.org/callback"
44
+ )
45
+
46
+ assert ! response.success?
47
+ assert_nil response.url
48
+ end
49
+ =end
50
+
51
+ test "success build" do
52
+ response = MasonClient.build(
53
+ "git@github.com:sr/mason.git",
54
+ "HEAD",
55
+ "echo foo",
56
+ "http://example.org/callback"
57
+ )
58
+
59
+ # TODO move to client test?
60
+ assert response.success?
61
+ assert_not_nil response.url
62
+
63
+ assert_equal 1, MasonServer::Build.count
64
+
65
+ build = MasonServer::Build.first
66
+
67
+ assert_equal "git@github.com:sr/mason.git", build.repo
68
+ assert_equal "HEAD", build.ref
69
+ assert_equal "echo foo", build.command
70
+ assert_equal "http://example.org/callback", build.callback
71
+ assert_equal "foo\n", build.output
72
+
73
+ assert_equal 1, @receiver.size
74
+
75
+ result = @receiver[response.url]
76
+
77
+ assert result.success?
78
+ assert_equal "http://mason.example.com/1", result.url
79
+ assert_equal "http://example.org/callback", result.callback
80
+ end
81
+ end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 0
8
- - 1
9
- version: 0.0.1
8
+ - 2
9
+ version: 0.0.2
10
10
  platform: ruby
11
11
  authors: []
12
12
 
@@ -135,8 +135,24 @@ extensions: []
135
135
 
136
136
  extra_rdoc_files: []
137
137
 
138
- files: []
139
-
138
+ files:
139
+ - Gemfile
140
+ - Gemfile.lock
141
+ - Rakefile
142
+ - config.ru
143
+ - config/database.yml
144
+ - config/database.yml.example
145
+ - lib/mason_server.rb
146
+ - lib/mason_server/app.rb
147
+ - lib/mason_server/build.rb
148
+ - lib/mason_server/command_runner.rb
149
+ - lib/mason_server/deployment.rb
150
+ - mason-server.gemspec
151
+ - test/command_runner_test.rb
152
+ - test/helper.rb
153
+ - test/helper/mock_receiver.rb
154
+ - test/mason_server_test.rb
155
+ - tmp/.gitignore
140
156
  has_rdoc: true
141
157
  homepage:
142
158
  licenses: []