mason-server 0.0.1 → 0.0.2
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.
- 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: []
|