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 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: []