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 +11 -0
- data/Gemfile.lock +79 -0
- data/Rakefile +8 -0
- data/config.ru +7 -0
- data/config/database.yml +8 -0
- data/config/database.yml.example +4 -0
- data/lib/mason_server.rb +55 -0
- data/lib/mason_server/app.rb +24 -0
- data/lib/mason_server/build.rb +87 -0
- data/lib/mason_server/command_runner.rb +44 -0
- data/lib/mason_server/deployment.rb +26 -0
- data/mason-server.gemspec +17 -0
- data/test/command_runner_test.rb +23 -0
- data/test/helper.rb +9 -0
- data/test/helper/mock_receiver.rb +17 -0
- data/test/mason_server_test.rb +81 -0
- metadata +20 -4
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -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)
|
data/Rakefile
ADDED
data/config.ru
ADDED
data/config/database.yml
ADDED
data/lib/mason_server.rb
ADDED
@@ -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
|
data/test/helper.rb
ADDED
@@ -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
|
-
-
|
9
|
-
version: 0.0.
|
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: []
|