phoebo 0.1.2 → 0.2.0
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.
- checksums.yaml +4 -4
- data/README.md +92 -8
- data/lib/phoebo.rb +12 -7
- data/lib/phoebo/application.rb +64 -76
- data/lib/phoebo/config.rb +17 -6
- data/lib/phoebo/config/image.rb +2 -1
- data/lib/phoebo/docker/image_builder.rb +2 -0
- data/lib/phoebo/docker/image_pusher.rb +2 -0
- data/lib/phoebo/git.rb +28 -0
- data/lib/phoebo/ping.rb +24 -0
- data/lib/phoebo/request.rb +132 -0
- data/lib/phoebo/version.rb +1 -1
- data/lib/phoebo/worker.rb +38 -0
- data/spec/phoebo/application_spec.rb +60 -0
- data/spec/phoebo/config/image_spec.rb +19 -0
- data/spec/phoebo/config_spec.rb +57 -0
- data/spec/phoebo/docker/image_builder_spec.rb +45 -0
- data/spec/phoebo/git_spec.rb +35 -0
- data/spec/phoebo/ping_spec.rb +37 -0
- data/spec/phoebo/request_spec.rb +141 -0
- data/spec/phoebo/worker_spec.rb +30 -0
- data/spec/spec_helper.rb +4 -1
- metadata +20 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 31be5faeea137754c6c0047c9f1bc0da857512df
|
4
|
+
data.tar.gz: db94bcf4c396136cf0838d1273b75cc8acd7beda
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 08325e381ee53679aac752bd2809b0fd2a27b81ee7a511cfdd8605d716342cc029d2241e30d4efb39cd2854eb593c035f4e32e7febfec81f0cc994c08685de6d
|
7
|
+
data.tar.gz: 71b3afc4d20ff1c830e68901a725ca09608f3e8cc091d529aef847c520e78a66b46521cb3d001daffc8fc1e4cec4f3d4b46634fa7b6eeed2eb724b02ce99750d
|
data/README.md
CHANGED
@@ -1,11 +1,95 @@
|
|
1
|
-
#
|
1
|
+
# Phoebo
|
2
2
|
|
3
|
-
|
4
|
-
$ bundle exec bin/phoebo
|
5
|
-
```
|
3
|
+
## DSL example
|
6
4
|
|
7
|
-
|
5
|
+
~~~ruby
|
6
|
+
Phoebo.configure(1) {
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
# Create image based on Apache + mod_php
|
9
|
+
image('phoebo/nette-example', 'phoebo/simple-apache-php') {
|
10
|
+
# Copy all application data
|
11
|
+
add('.', '/app/')
|
12
|
+
|
13
|
+
# Install dependencies
|
14
|
+
run('composer', 'install', '--prefer-dist')
|
15
|
+
|
16
|
+
# Set document root
|
17
|
+
run('ln', '-s', '/app/www', '/var/www')
|
18
|
+
}
|
19
|
+
|
20
|
+
# Deploy image and keep it runing (default CMD: apache)
|
21
|
+
task('deploy', 'phoebo/nette-example')
|
22
|
+
|
23
|
+
# Run tests on image
|
24
|
+
task('test', 'phoebo/nette-example', '/app/bin/tester', '/app/tests')
|
25
|
+
}
|
26
|
+
~~~
|
27
|
+
|
28
|
+
## Usage
|
29
|
+
|
30
|
+
~~~bash
|
31
|
+
sudo docker pull phoebo/phoebo:latest
|
32
|
+
sudo docker run -ti --privileged -v ~/nette-example-keys:/root/deploy-keys phoebo/phoebo:latest phoebo \
|
33
|
+
--repository ssh://gitlab.fit.cvut.cz/phoebo/nette-example.git \
|
34
|
+
--ssh-key /root/deploy-keys/key \
|
35
|
+
--ssh-public /root/deploy-keys/key.pub \
|
36
|
+
--docker-user joe \
|
37
|
+
--docker-password secret123 \
|
38
|
+
--docker-email joe@domain.tld
|
39
|
+
~~~
|
40
|
+
|
41
|
+
### Process request from URL
|
42
|
+
|
43
|
+
For better integration into your CI workflow you can specify build job by URL instead of verbose CLI arguments.
|
44
|
+
|
45
|
+
~~~bash
|
46
|
+
phoebo --from-url http://domain.tld/api/requests/2e2996fe8420
|
47
|
+
~~~
|
48
|
+
|
49
|
+
URL should return JSON formatted payload with following structure:
|
50
|
+
|
51
|
+
~~~javascript
|
52
|
+
{
|
53
|
+
"id": "2e2996fe8420",
|
54
|
+
"repo_url": "ssh://gitlab.fit.cvut.cz/phoebo/nette-example.git",
|
55
|
+
"ssh_public": "ssh-rsa AAAAB3NzaC1yc2E...",
|
56
|
+
"ssh_private": "-----BEGIN RSA PRIVATE KEY----- ...",
|
57
|
+
"docker_user": "joe",
|
58
|
+
"docker_password": "secret123",
|
59
|
+
"docker_email": "joe@domain.tld"
|
60
|
+
}
|
61
|
+
~~~
|
62
|
+
|
63
|
+
If other CLI arguments are present they will override those loaded from URL.
|
64
|
+
|
65
|
+
### Ping back
|
66
|
+
|
67
|
+
You can optionally let Phoebo notify you when the build is ready.
|
68
|
+
|
69
|
+
~~~bash
|
70
|
+
phoebo --ping-url http://domain.tld/api/notify
|
71
|
+
~~~
|
72
|
+
|
73
|
+
Notification is sent as JSON formatted HTTP POST request with structure bellow.
|
74
|
+
The payload contains the same ID you've passed to application with your JSON request.
|
75
|
+
Other than that payload contains list of tasks with arguments and image on which they should be run.
|
76
|
+
|
77
|
+
~~~javascript
|
78
|
+
{
|
79
|
+
"id": "2e2996fe8420",
|
80
|
+
"tasks":[
|
81
|
+
{
|
82
|
+
"name":"deploy",
|
83
|
+
"image":"phoebo/nette-example"
|
84
|
+
},
|
85
|
+
{
|
86
|
+
"name":"test",
|
87
|
+
"image":"phoebo/nette-example",
|
88
|
+
"cmd":[
|
89
|
+
"/app/bin/tester",
|
90
|
+
"/app/tests"
|
91
|
+
]
|
92
|
+
}
|
93
|
+
]
|
94
|
+
}
|
95
|
+
~~~
|
data/lib/phoebo.rb
CHANGED
@@ -1,12 +1,16 @@
|
|
1
1
|
require 'phoebo/version'
|
2
2
|
|
3
3
|
module Phoebo
|
4
|
-
autoload :Application,
|
5
|
-
autoload :Config,
|
6
|
-
autoload :Console,
|
7
|
-
autoload :Docker,
|
8
|
-
autoload :Environment,
|
9
|
-
autoload :
|
4
|
+
autoload :Application, 'phoebo/application'
|
5
|
+
autoload :Config, 'phoebo/config'
|
6
|
+
autoload :Console, 'phoebo/console'
|
7
|
+
autoload :Docker, 'phoebo/docker'
|
8
|
+
autoload :Environment, 'phoebo/environment'
|
9
|
+
autoload :Git, 'phoebo/git'
|
10
|
+
autoload :Ping, 'phoebo/ping'
|
11
|
+
autoload :Request, 'phoebo/request'
|
12
|
+
autoload :Util, 'phoebo/util'
|
13
|
+
autoload :Worker, 'phoebo/worker'
|
10
14
|
|
11
15
|
class PhoeboError < StandardError
|
12
16
|
def self.status_code(code)
|
@@ -15,6 +19,7 @@ module Phoebo
|
|
15
19
|
end
|
16
20
|
|
17
21
|
class InvalidArgumentError < PhoeboError; status_code(4) ; end
|
22
|
+
class InvalidRequestError < InvalidArgumentError ; end
|
18
23
|
class IOError < PhoeboError; status_code(5) ; end
|
19
24
|
class ExternalError < PhoeboError; status_code(6) ; end
|
20
25
|
|
@@ -28,7 +33,7 @@ module Phoebo
|
|
28
33
|
# Configure Phoebo environment from Phoebofile
|
29
34
|
def self.configure(version, &block)
|
30
35
|
raise ConfigError, "Configuration version #{version} not supported" if version != 1
|
31
|
-
Config.
|
36
|
+
Config.new_from_block(block)
|
32
37
|
end
|
33
38
|
|
34
39
|
end
|
data/lib/phoebo/application.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
require 'optparse'
|
2
2
|
require 'colorize'
|
3
|
-
require 'docker'
|
4
|
-
require 'rugged'
|
5
3
|
require 'pathname'
|
6
4
|
|
7
5
|
module Phoebo
|
@@ -23,6 +21,8 @@ module Phoebo
|
|
23
21
|
class Application
|
24
22
|
include Console
|
25
23
|
|
24
|
+
attr_writer :environment, :temp_file_manager
|
25
|
+
|
26
26
|
# Creates application
|
27
27
|
def initialize(args = [])
|
28
28
|
@args = args
|
@@ -55,7 +55,7 @@ module Phoebo
|
|
55
55
|
stderr.puts
|
56
56
|
end
|
57
57
|
|
58
|
-
send(('run_' + options[:mode].to_s).to_sym, options).to_i
|
58
|
+
[send(('run_' + options[:mode].to_s).to_sym, options).to_i, result].max
|
59
59
|
end
|
60
60
|
|
61
61
|
# Cleanup sequence
|
@@ -72,93 +72,69 @@ module Phoebo
|
|
72
72
|
# Run in normal mode
|
73
73
|
def run_normal(options)
|
74
74
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
# If we are building image from Git repository
|
79
|
-
if options[:repository]
|
75
|
+
request = Request.new
|
76
|
+
request.load_from_hash!(options[:request])
|
80
77
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
raise InvalidArgumentError, "SSH key not found" unless private_path.exist?
|
86
|
-
raise InvalidArgumentError, "Missing public SSH key" unless options[:ssh_public]
|
78
|
+
if options[:request_file]
|
79
|
+
stdout.puts "Loading request from file: " + options[:request_file].cyan
|
80
|
+
request.load_from_file!(options[:request_file])
|
81
|
+
end
|
87
82
|
|
88
|
-
|
89
|
-
|
83
|
+
if options[:request_url]
|
84
|
+
stdout.puts "Fetching request from URL: " + options[:request_url].cyan + "...".light_black
|
85
|
+
request.load_from_url!(options[:request_url])
|
86
|
+
end
|
90
87
|
|
91
|
-
|
92
|
-
|
93
|
-
publickey: public_path.realpath.to_s,
|
94
|
-
privatekey: private_path.realpath.to_s
|
95
|
-
)
|
96
|
-
else
|
97
|
-
cred = nil
|
98
|
-
end
|
88
|
+
# Raises InvalidRequestError if invalid
|
89
|
+
request.validate
|
99
90
|
|
100
|
-
|
91
|
+
# Prepare project directory
|
92
|
+
if request.repo_url
|
93
|
+
# Create temp dir & clone
|
101
94
|
project_path = temp_file_manager.path('project')
|
95
|
+
stdout.puts "Cloning remote repository " + "...".light_black
|
96
|
+
Git.clone(request, project_path)
|
102
97
|
|
103
|
-
# Clone remote repository
|
104
|
-
begin
|
105
|
-
stdout.puts "Cloning remote repository " + "...".light_black
|
106
|
-
Rugged::Repository.clone_at options[:repository],
|
107
|
-
project_path, credentials: cred
|
108
|
-
|
109
|
-
rescue Rugged::SshError => e
|
110
|
-
raise unless e.message.include?('authentication')
|
111
|
-
raise IOError, "Unable to clone remote repository. SSH authentication failed."
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
# Prepare Docker credentials
|
116
|
-
if options[:docker_username]
|
117
|
-
raise InvalidArgumentError, "Missing docker password." unless options[:docker_password]
|
118
|
-
raise InvalidArgumentError, "Missing docker e-mail." unless options[:docker_email]
|
119
|
-
|
120
|
-
pusher = Docker::ImagePusher.new(
|
121
|
-
options[:docker_username],
|
122
|
-
options[:docker_password],
|
123
|
-
options[:docker_email]
|
124
|
-
)
|
125
98
|
else
|
126
|
-
|
99
|
+
project_path = Dir.pwd
|
127
100
|
end
|
128
101
|
|
129
|
-
#
|
102
|
+
# Worker
|
103
|
+
worker = Worker.new(request)
|
130
104
|
result = 0
|
105
|
+
configs = []
|
131
106
|
|
132
|
-
# Process all
|
107
|
+
# Process all
|
133
108
|
options[:files] << '.' if options[:files].empty?
|
134
109
|
options[:files].each do |rel_path|
|
110
|
+
path = (Pathname.new(project_path) + rel_path)
|
135
111
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
# Check if config exists -> resumable error
|
143
|
-
unless File.exists?(config_path)
|
144
|
-
stderr.puts "No #{config_filename} found in #{path}".red
|
112
|
+
# Directory does not exist -> resume with next dir.
|
113
|
+
if path.exist?
|
114
|
+
path = path.realpath.to_s
|
115
|
+
else
|
116
|
+
stderr.puts "Directory #{path.to_s} does not exist".red
|
145
117
|
result = 1
|
146
118
|
next
|
147
119
|
end
|
148
120
|
|
149
|
-
#
|
150
|
-
config =
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
config.images.each do |image|
|
155
|
-
image_id = builder.build(image)
|
156
|
-
pusher.push(image_id) if pusher
|
121
|
+
# Directory does not have any config -> nothing to do -> resume with next dir.
|
122
|
+
unless config = worker.process(path)
|
123
|
+
stderr.puts "No config found in #{path}".red
|
124
|
+
result = 1
|
125
|
+
next
|
157
126
|
end
|
127
|
+
|
128
|
+
configs << config
|
158
129
|
end
|
159
130
|
|
160
|
-
|
131
|
+
# Ping
|
132
|
+
if request.ping_url
|
133
|
+
stdout.puts "Notifying URL: " + request.ping_url.cyan + "...".light_black
|
134
|
+
Ping.send(request, configs)
|
135
|
+
end
|
161
136
|
|
137
|
+
stdout.puts "Everything done :-)".green
|
162
138
|
result
|
163
139
|
end
|
164
140
|
|
@@ -176,7 +152,7 @@ module Phoebo
|
|
176
152
|
|
177
153
|
# Parse passed command-line options
|
178
154
|
def parse_options
|
179
|
-
options = { error: nil, mode: :normal, files: [], ssh_user: 'git' }
|
155
|
+
options = { error: nil, mode: :normal, request: {}, files: [], ssh_user: 'git' }
|
180
156
|
|
181
157
|
unless @option_parser
|
182
158
|
@option_parser = OptionParser.new
|
@@ -184,31 +160,43 @@ module Phoebo
|
|
184
160
|
"Usage: #{@option_parser.program_name} [options] file"
|
185
161
|
|
186
162
|
@option_parser.on_tail('-rURL', '--repository=URL', 'Repository URL') do |value|
|
187
|
-
options[:
|
163
|
+
options[:request][:repo_url] = value
|
188
164
|
end
|
189
165
|
|
190
166
|
@option_parser.on_tail('--ssh-user=USERNAME', 'Username for Git over SSH (Default: git)') do |value|
|
191
|
-
options[:ssh_user] = value
|
167
|
+
options[:request][:ssh_user] = value
|
192
168
|
end
|
193
169
|
|
194
170
|
@option_parser.on_tail('--ssh-public=PATH', 'Path to public SSH key for Git repository') do |value|
|
195
|
-
options[:
|
171
|
+
options[:request][:ssh_public_file] = value
|
196
172
|
end
|
197
173
|
|
198
174
|
@option_parser.on_tail('--ssh-key=PATH', 'Path to SSH key for Git repository') do |value|
|
199
|
-
options[:
|
175
|
+
options[:request][:ssh_private_file] = value
|
200
176
|
end
|
201
177
|
|
202
178
|
@option_parser.on_tail('--docker-user=USERNAME', 'Username for Docker Registry') do |value|
|
203
|
-
options[:
|
179
|
+
options[:request][:docker_user] = value
|
204
180
|
end
|
205
181
|
|
206
182
|
@option_parser.on_tail('--docker-password=PASSWORD', 'Password for Docker Registry') do |value|
|
207
|
-
options[:docker_password] = value
|
183
|
+
options[:request][:docker_password] = value
|
208
184
|
end
|
209
185
|
|
210
186
|
@option_parser.on_tail('--docker-email=EMAIL', 'E-mail for Docker Registry') do |value|
|
211
|
-
options[:docker_email] = value
|
187
|
+
options[:request][:docker_email] = value
|
188
|
+
end
|
189
|
+
|
190
|
+
@option_parser.on_tail('--ping-url=REQUEST_URL', 'URL will be notified after build') do |value|
|
191
|
+
options[:request][:ping_url] = value
|
192
|
+
end
|
193
|
+
|
194
|
+
@option_parser.on_tail('-U', '--from-url=REQUEST_URL', 'Process build request from URL') do |value|
|
195
|
+
options[:request_url] = value
|
196
|
+
end
|
197
|
+
|
198
|
+
@option_parser.on_tail('-f', '--from-file=PATH', 'Process build request from local file') do |value|
|
199
|
+
options[:request_file] = value
|
212
200
|
end
|
213
201
|
|
214
202
|
@option_parser.on_tail('--version', 'Show version info') do
|
data/lib/phoebo/config.rb
CHANGED
@@ -3,14 +3,14 @@ module Phoebo
|
|
3
3
|
autoload :Image, 'phoebo/config/image'
|
4
4
|
autoload :ImageCommands, 'phoebo/config/image_commands'
|
5
5
|
|
6
|
-
attr_accessor :images
|
6
|
+
attr_accessor :images, :tasks
|
7
7
|
|
8
8
|
# Loads config from file
|
9
9
|
# @see Phoebo.configure()
|
10
|
-
def self.
|
10
|
+
def self.new_from_file(file_path)
|
11
11
|
begin
|
12
12
|
@instance = nil
|
13
|
-
Kernel.load
|
13
|
+
Kernel.load file_path, true
|
14
14
|
@instance
|
15
15
|
|
16
16
|
rescue ::SyntaxError => e
|
@@ -18,7 +18,7 @@ module Phoebo
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
-
def self.
|
21
|
+
def self.new_from_block(block)
|
22
22
|
@instance = self.new
|
23
23
|
@instance.dsl_eval(block)
|
24
24
|
end
|
@@ -26,6 +26,7 @@ module Phoebo
|
|
26
26
|
# Instance initialization
|
27
27
|
def initialize
|
28
28
|
@images = []
|
29
|
+
@tasks = []
|
29
30
|
end
|
30
31
|
|
31
32
|
# Evaluate block within DSL context
|
@@ -42,10 +43,20 @@ module Phoebo
|
|
42
43
|
end
|
43
44
|
|
44
45
|
def image(name, from, &block)
|
45
|
-
@config.images << (img_instance = Image.new(name, from))
|
46
|
-
img_instance.dsl_eval(block)
|
46
|
+
@config.images << (img_instance = Image.new(name, from, block))
|
47
47
|
img_instance
|
48
48
|
end
|
49
|
+
|
50
|
+
def task(name, image, *args)
|
51
|
+
task = {
|
52
|
+
name: name,
|
53
|
+
image: image
|
54
|
+
}
|
55
|
+
|
56
|
+
task[:cmd] = args unless args.empty?
|
57
|
+
@config.tasks << task
|
58
|
+
task
|
59
|
+
end
|
49
60
|
end
|
50
61
|
|
51
62
|
end
|
data/lib/phoebo/config/image.rb
CHANGED
@@ -3,10 +3,11 @@ module Phoebo
|
|
3
3
|
class Image
|
4
4
|
attr_accessor :actions, :name, :from
|
5
5
|
|
6
|
-
def initialize(name, from)
|
6
|
+
def initialize(name, from, block = nil)
|
7
7
|
@actions = []
|
8
8
|
@name = name
|
9
9
|
@from = from
|
10
|
+
dsl_eval(block) if block
|
10
11
|
end
|
11
12
|
|
12
13
|
# Evaluate block within DSL context
|
data/lib/phoebo/git.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rugged'
|
2
|
+
|
3
|
+
module Phoebo
|
4
|
+
class Git
|
5
|
+
def self.clone(request, clone_path)
|
6
|
+
# Use passed SSH keys or fallback to system authentication
|
7
|
+
if request.ssh_private_file
|
8
|
+
cred = Rugged::Credentials::SshKey.new(
|
9
|
+
username: request.ssh_user,
|
10
|
+
publickey: request.ssh_public_file,
|
11
|
+
privatekey: request.ssh_private_file
|
12
|
+
)
|
13
|
+
else
|
14
|
+
cred = Rugged::Credentials::Default.new
|
15
|
+
end
|
16
|
+
|
17
|
+
# Clone remote repository
|
18
|
+
begin
|
19
|
+
Rugged::Repository.clone_at request.repo_url,
|
20
|
+
clone_path, credentials: cred
|
21
|
+
|
22
|
+
rescue Rugged::SshError => e
|
23
|
+
raise unless e.message.include?('authentication')
|
24
|
+
raise IOError, "Unable to clone remote repository. SSH authentication failed."
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/phoebo/ping.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'net/http'
|
3
|
+
|
4
|
+
module Phoebo
|
5
|
+
class Ping
|
6
|
+
def self.send(request, configs)
|
7
|
+
payload = {
|
8
|
+
id: request.id,
|
9
|
+
tasks: []
|
10
|
+
}
|
11
|
+
|
12
|
+
configs.each do |config|
|
13
|
+
payload[:tasks] += config.tasks
|
14
|
+
end
|
15
|
+
|
16
|
+
uri = URI(request.ping_url)
|
17
|
+
req = Net::HTTP::Post.new(uri, {'Content-Type' =>'application/json'})
|
18
|
+
req.body = payload.to_json
|
19
|
+
res = Net::HTTP.start(uri.hostname, uri.port) do |http|
|
20
|
+
http.request(req)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'net/http'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Phoebo
|
6
|
+
# Application request
|
7
|
+
# Class is used as a container for user input and to perform easy merging user
|
8
|
+
# input from multiple sources (HTTP request, CLI options) and provides data
|
9
|
+
# validation with human-readable error messages.
|
10
|
+
class Request
|
11
|
+
attr_accessor :repo_url, :ssh_user, :ssh_private_file, :ssh_public_file
|
12
|
+
attr_accessor :docker_user, :docker_password, :docker_email
|
13
|
+
attr_accessor :id, :ping_url
|
14
|
+
attr_writer :temp_file_manager
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
@ssh_user = 'git'
|
18
|
+
end
|
19
|
+
|
20
|
+
# Temporary file manager
|
21
|
+
def temp_file_manager
|
22
|
+
@temp_file_manager ||= Application.instance.temp_file_manager
|
23
|
+
end
|
24
|
+
|
25
|
+
# Allows to use SSH content instead of SSH key file
|
26
|
+
# (We need to place it into temporary file because Rugged needs it)
|
27
|
+
def ssh_private=(key_content)
|
28
|
+
@ssh_private_file = temp_file_manager.path('key')
|
29
|
+
IO.write(@ssh_private_file, key_content)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Allows to use SSH content instead of SSH key file
|
33
|
+
# (We need to place it into temporary file because Rugged needs it)
|
34
|
+
def ssh_public=(key_content)
|
35
|
+
@ssh_public_file = temp_file_manager.path('key.pub')
|
36
|
+
IO.write(@ssh_public_file, key_content)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Loads request with data from hash
|
40
|
+
def load_from_hash!(hash)
|
41
|
+
hash.each do |k, v|
|
42
|
+
m = (k.to_s + '=').to_sym
|
43
|
+
raise InvalidRequestError, "Invalid key #{k.inspect}" unless respond_to?(m)
|
44
|
+
send(m, v)
|
45
|
+
end
|
46
|
+
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
# Loads request from JSON string
|
51
|
+
def load_from_json!(raw_data)
|
52
|
+
begin
|
53
|
+
hash = JSON.parse(raw_data)
|
54
|
+
rescue JSON::ParserError => e
|
55
|
+
raise InvalidRequestError, "Malformed JSON data."
|
56
|
+
end
|
57
|
+
|
58
|
+
load_from_hash!(hash)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Loads request fro local file
|
62
|
+
def load_from_file!(file_path)
|
63
|
+
raise IOError, "File #{file_path} not exist" unless File.exist?(file_path)
|
64
|
+
raw_data = IO.read(file_path)
|
65
|
+
load_from_json!(raw_data)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Loads request from URL
|
69
|
+
def load_from_url!(url)
|
70
|
+
uri = URI(url)
|
71
|
+
req = Net::HTTP::Get.new(uri)
|
72
|
+
req['Accept'] = 'application/json'
|
73
|
+
|
74
|
+
res = Net::HTTP.start(uri.hostname, uri.port) do |http|
|
75
|
+
http.request(req)
|
76
|
+
end
|
77
|
+
|
78
|
+
# TODO: handle redirects
|
79
|
+
raise IOError, "Unable to load URL #{url}" unless res.code == 200
|
80
|
+
|
81
|
+
load_from_json!(res.body)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Validates request and returns array of error messages
|
85
|
+
# or empty array if there are none
|
86
|
+
def errors
|
87
|
+
errors = []
|
88
|
+
|
89
|
+
if repo_url
|
90
|
+
begin
|
91
|
+
uri = URI(repo_url)
|
92
|
+
errors << "Invalid repository URL. Only SSH supported at the moment. Expected format: ssh://host/path/to/repo.git" \
|
93
|
+
unless uri.scheme == 'ssh'
|
94
|
+
rescue URI::InvalidURIError
|
95
|
+
errors << "Invalid repository URL. Expected format: ssh://host/path/to/repo.git"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
unless [ssh_private_file, ssh_public_file].select { |v| v } .empty?
|
100
|
+
errors << "Missing SSH user." unless ssh_user
|
101
|
+
errors << "Missing private SSH key." unless ssh_private_file
|
102
|
+
errors << "Missing public SSH key." unless ssh_public_file
|
103
|
+
end
|
104
|
+
|
105
|
+
unless [docker_user, docker_password, docker_email].select { |v| v } .empty?
|
106
|
+
errors << "Missing Docker user." unless docker_user
|
107
|
+
errors << "Missing Docker password." unless docker_password
|
108
|
+
errors << "Missing Docker email." unless docker_email
|
109
|
+
end
|
110
|
+
|
111
|
+
if ping_url
|
112
|
+
begin
|
113
|
+
uri = URI(ping_url)
|
114
|
+
errors << "Invalid ping URL. Only HTTP / HTTPS supported at the moment. Expected format: https://domain.tld/api/notify" \
|
115
|
+
unless uri.scheme == 'http' || uri.scheme == 'https'
|
116
|
+
rescue URI::InvalidURIError
|
117
|
+
errors << "Invalid ping URL. Expected format: https://domain.tld/api/notify"
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
errors
|
122
|
+
end
|
123
|
+
|
124
|
+
# Validate request and raises first discovered error as an exception
|
125
|
+
def validate
|
126
|
+
unless (e = errors).empty?
|
127
|
+
raise InvalidRequestError, e.first
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
end
|
data/lib/phoebo/version.rb
CHANGED
@@ -0,0 +1,38 @@
|
|
1
|
+
module Phoebo
|
2
|
+
class Worker
|
3
|
+
def initialize(request)
|
4
|
+
@request = request
|
5
|
+
end
|
6
|
+
|
7
|
+
def pusher
|
8
|
+
@pusher ||= Docker::ImagePusher.new(
|
9
|
+
@request.docker_user,
|
10
|
+
@request.docker_password,
|
11
|
+
@request.docker_email
|
12
|
+
)
|
13
|
+
end
|
14
|
+
|
15
|
+
def process(path)
|
16
|
+
|
17
|
+
# Prepare config path
|
18
|
+
config_filename = 'Phoebofile'
|
19
|
+
config_path = "#{path}#{File::SEPARATOR}#{config_filename}"
|
20
|
+
|
21
|
+
if File.exists?(config_path)
|
22
|
+
# Load config
|
23
|
+
config = Config.new_from_file(config_path)
|
24
|
+
|
25
|
+
# Build & push image
|
26
|
+
builder = Docker::ImageBuilder.new(path)
|
27
|
+
config.images.each do |image|
|
28
|
+
image_id = builder.build(image)
|
29
|
+
pusher.push(image_id) if @request.docker_user
|
30
|
+
end
|
31
|
+
|
32
|
+
config
|
33
|
+
else
|
34
|
+
false
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -48,4 +48,64 @@ describe Phoebo::Application do
|
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
|
+
context '--help argument' do
|
52
|
+
subject(:app) { described_class.new(['--help']) }
|
53
|
+
|
54
|
+
it 'returns 0' do
|
55
|
+
expect(app.run).to eq 0
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'shows usage' do
|
59
|
+
app.run
|
60
|
+
expect(app.stdout.string).to include('Usage:')
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'normal run with all arguments' do
|
65
|
+
let(:args) {[
|
66
|
+
'--repository', 'ssh://host/path/to/repo.git',
|
67
|
+
'--ssh-user', 'git',
|
68
|
+
'--ssh-key', './key',
|
69
|
+
'--ssh-public', './key.pub',
|
70
|
+
'--docker-user', 'joe',
|
71
|
+
'--docker-password', 'secret123',
|
72
|
+
'--docker-email', 'joe@domain.tld',
|
73
|
+
'dir1', 'dir2'
|
74
|
+
]}
|
75
|
+
|
76
|
+
subject(:app) {
|
77
|
+
app = described_class.new(args)
|
78
|
+
app.temp_file_manager = instance_double(Phoebo::Util::TempFileManager)
|
79
|
+
allow(app.temp_file_manager).to receive(:path) { |*args| "/tmp/random/#{args.join('/')}" }
|
80
|
+
allow(app.temp_file_manager).to receive(:need_cleanup?).and_return(true)
|
81
|
+
app
|
82
|
+
}
|
83
|
+
|
84
|
+
before(:each) {
|
85
|
+
allow(Phoebo::Git).to receive(:clone).and_return(nil)
|
86
|
+
}
|
87
|
+
|
88
|
+
it 'returns 1 if no files were processed' do
|
89
|
+
expect(app.run).to eq 1
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'cleans temporary files' do
|
93
|
+
expect(app.temp_file_manager).to receive(:cleanup)
|
94
|
+
app.cleanup
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context 'bad arguments' do
|
99
|
+
subject(:app) { described_class.new(['--foobar']) }
|
100
|
+
|
101
|
+
it 'returns 1' do
|
102
|
+
expect(app.run).to eq 1
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'shows usage' do
|
106
|
+
app.run
|
107
|
+
expect(app.stdout.string).to include('Usage:')
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
51
111
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
|
3
|
+
describe Phoebo::Config::Image do
|
4
|
+
|
5
|
+
let(:dsl) {
|
6
|
+
Proc.new {
|
7
|
+
add('.', '/app/')
|
8
|
+
run('composer', 'install', '--prefer-dist')
|
9
|
+
}
|
10
|
+
}
|
11
|
+
|
12
|
+
subject { described_class.new('image-name', 'base-image-name') }
|
13
|
+
|
14
|
+
it 'processes commands' do
|
15
|
+
expect(Phoebo::Config::ImageCommands::Add).to receive(:action).with('.', '/app/')
|
16
|
+
expect(Phoebo::Config::ImageCommands::Run).to receive(:action).with('composer', 'install', '--prefer-dist')
|
17
|
+
subject.dsl_eval(dsl)
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
describe Phoebo::Config do
|
4
|
+
|
5
|
+
subject { described_class }
|
6
|
+
|
7
|
+
context 'loading DSL' do
|
8
|
+
subject { described_class }
|
9
|
+
|
10
|
+
let(:dsl_content) { Proc.new { } }
|
11
|
+
|
12
|
+
it 'loads block' do
|
13
|
+
allow(Kernel).to receive(:load).with('./Phoebofile', true) do
|
14
|
+
Phoebo.configure(1, &dsl_content)
|
15
|
+
end
|
16
|
+
|
17
|
+
# It goes like this:
|
18
|
+
# 1) Config.new_from_file('./Phoebofile')
|
19
|
+
# 2) It loads file using Kernel.load (immediatly interprets it's content)
|
20
|
+
# 3) Inside of this file there is call Phoebo.configure(version) { DSL BLOCK }
|
21
|
+
# which introduces our DSL.
|
22
|
+
# 4) When Phoebo.configure() is called it passes DSL BLOCK to Config.new_from_block()
|
23
|
+
# 5) Config.new_from_block() saves instance internally
|
24
|
+
# 6) Config.new_from_file() returns saved instance
|
25
|
+
|
26
|
+
expect(subject).to receive(:new_from_block).with(dsl_content).and_call_original
|
27
|
+
expect(subject.new_from_file('./Phoebofile').is_a?(described_class)).to eq true
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'raises human readable message on syntax error' do
|
31
|
+
allow(Kernel).to receive(:load).with('./Phoebofile', true) do
|
32
|
+
raise ::SyntaxError
|
33
|
+
end
|
34
|
+
|
35
|
+
expect { subject.new_from_file('./Phoebofile') }.to raise_error(Phoebo::SyntaxError)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'images' do
|
40
|
+
subject { described_class.new }
|
41
|
+
let(:image_dsl_block) { Proc.new { } }
|
42
|
+
let(:dsl) {
|
43
|
+
child_block = image_dsl_block
|
44
|
+
Proc.new {
|
45
|
+
image('image-name', 'base-image-name', &child_block)
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
it 'processes images' do
|
50
|
+
image = instance_double(Phoebo::Config::Image)
|
51
|
+
expect(Phoebo::Config::Image).to receive(:new).with('image-name', 'base-image-name', image_dsl_block).and_return(image)
|
52
|
+
|
53
|
+
subject.dsl_eval(dsl)
|
54
|
+
expect(subject.images).to include(image)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
|
3
|
+
describe Phoebo::Docker::ImageBuilder do
|
4
|
+
|
5
|
+
# Set output stream before test
|
6
|
+
before(:all) {
|
7
|
+
described_class.stdout = StringIO.new
|
8
|
+
described_class.stderr = StringIO.new
|
9
|
+
}
|
10
|
+
|
11
|
+
# Clean output streams before each run
|
12
|
+
before(:each) {
|
13
|
+
described_class.stdout.truncate(0)
|
14
|
+
described_class.stderr.truncate(0)
|
15
|
+
}
|
16
|
+
|
17
|
+
subject { described_class.new('./somedir') }
|
18
|
+
|
19
|
+
let(:image) {
|
20
|
+
actions = [
|
21
|
+
Phoebo::Config::ImageCommands::Add.action('.', '/app/'),
|
22
|
+
Phoebo::Config::ImageCommands::Run.action('composer', 'install', '--prefer-dist')
|
23
|
+
]
|
24
|
+
|
25
|
+
image = instance_double(Phoebo::Config::Image)
|
26
|
+
allow(image).to receive(:name).and_return('image-name')
|
27
|
+
allow(image).to receive(:from).and_return('debian')
|
28
|
+
allow(image).to receive(:actions).and_return(actions)
|
29
|
+
|
30
|
+
image
|
31
|
+
}
|
32
|
+
|
33
|
+
let(:docker_image) {
|
34
|
+
image = instance_double(Docker::Image)
|
35
|
+
allow(image).to receive(:id).and_return('c90d655b99b2')
|
36
|
+
allow(image).to receive(:tag).and_return(image)
|
37
|
+
allow(image).to receive(:json).and_return({'VirtualSize' => 123, 'Size' => 123})
|
38
|
+
image
|
39
|
+
}
|
40
|
+
|
41
|
+
it 'processes commands' do
|
42
|
+
expect(Docker::Image).to receive(:build_from_tar).and_return(docker_image)
|
43
|
+
subject.build(image)
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
describe Phoebo::Git do
|
4
|
+
|
5
|
+
subject { described_class }
|
6
|
+
|
7
|
+
context 'without credentials' do
|
8
|
+
let(:request) do
|
9
|
+
Phoebo::Request.new do
|
10
|
+
repo_url = 'ssh://somehost.tld/user/repo.git'
|
11
|
+
ssh_private_file = 'key'
|
12
|
+
ssh_public_file = 'key.pub'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'clones' do
|
17
|
+
expect(Rugged::Repository).to receive(:clone_at)
|
18
|
+
subject.clone(request, '/tmp/somepath')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'with SSH credentials' do
|
23
|
+
let(:request) do
|
24
|
+
Phoebo::Request.new do
|
25
|
+
repo_url = 'ssh://somehost.tld/user/repo.git'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'clones' do
|
30
|
+
expect(Rugged::Repository).to receive(:clone_at)
|
31
|
+
subject.clone(request, '/tmp/somepath')
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
describe Phoebo::Ping do
|
4
|
+
|
5
|
+
subject { described_class }
|
6
|
+
|
7
|
+
let(:request) do
|
8
|
+
request = double(Phoebo::Request)
|
9
|
+
allow(request).to receive(:id).and_return('2e2996fe8420')
|
10
|
+
allow(request).to receive(:ping_url).and_return('http://domain.tld/api/notify')
|
11
|
+
request
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:configs) do
|
15
|
+
tasks = [{
|
16
|
+
name: 'test',
|
17
|
+
image: 'someimage',
|
18
|
+
cmd: ['do_something', '--with', 'that']
|
19
|
+
}]
|
20
|
+
|
21
|
+
configs = [ config = double(Phoebo::Config) ]
|
22
|
+
allow(config).to receive(:tasks).and_return(tasks)
|
23
|
+
configs
|
24
|
+
end
|
25
|
+
|
26
|
+
let(:http_response) do
|
27
|
+
response = double
|
28
|
+
allow(response).to receive(:code).and_return(200)
|
29
|
+
response
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'pings' do
|
33
|
+
allow(Net::HTTP).to receive(:start).and_return(http_response)
|
34
|
+
subject.send(request, configs)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
describe Phoebo::Request do
|
4
|
+
|
5
|
+
subject { described_class.new }
|
6
|
+
|
7
|
+
context 'request validation' do
|
8
|
+
it 'defaults are valid' do
|
9
|
+
expect(subject.errors).to be_empty
|
10
|
+
end
|
11
|
+
|
12
|
+
# Unix like format is not accepted and should produce human-readable message
|
13
|
+
it 'returns error on invalid repo URL' do
|
14
|
+
subject.repo_url = '@host:a.git'
|
15
|
+
expect(subject.errors.select { |e| e.include?('Invalid repository URL.') }).not_to be_empty
|
16
|
+
end
|
17
|
+
|
18
|
+
# Non SSH repos not supported at the moment
|
19
|
+
it 'returns error when non SSH repo is requested' do
|
20
|
+
subject.repo_url = 'http://host/repo.git'
|
21
|
+
expect(subject.errors.select { |e| e.include?('Invalid repository URL.') }).not_to be_empty
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'returns error on invalid ping URL' do
|
25
|
+
subject.ping_url = '@host:a.git'
|
26
|
+
expect(subject.errors.select { |e| e.include?('Invalid ping URL.') }).not_to be_empty
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'valid repo url returns no errors' do
|
30
|
+
subject.repo_url = 'ssh://host/path/to/repo.git'
|
31
|
+
expect(subject.errors.select { |e| e.include?('Invalid repository URL.') }).to be_empty
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'validates private SSH key if required' do
|
35
|
+
subject.ssh_public_file = 'secret.pub'
|
36
|
+
expect(subject.errors.select { |e| e.include?('private') && e.include?('SSH') }).not_to be_empty
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'validates public SSH key if required' do
|
40
|
+
subject.ssh_private_file = 'secret'
|
41
|
+
expect(subject.errors.select { |e| e.include?('public') && e.include?('SSH') }).not_to be_empty
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'no error with both SSH keys valid' do
|
45
|
+
subject.repo_url = 'ssh://host/path/to/repo.git'
|
46
|
+
subject.ssh_private_file = 'secret'
|
47
|
+
subject.ssh_public_file = 'secret.pub'
|
48
|
+
expect(subject.errors.select { |e| e.include?('SSH') }).to be_empty
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'enforces none or all docker parameters' do
|
52
|
+
subject.docker_user = 'user'
|
53
|
+
expect(subject.errors.select { |e| e.include?('Docker') }.size).to be 2
|
54
|
+
subject.docker_password = 'password'
|
55
|
+
expect(subject.errors.select { |e| e.include?('Docker') }.size).to be 1
|
56
|
+
subject.docker_email = 'email'
|
57
|
+
expect(subject.errors.select { |e| e.include?('Docker') }).to be_empty
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'raises exception' do
|
61
|
+
subject.repo_url = '@host:a.git'
|
62
|
+
expect { subject.validate }.to raise_error(Phoebo::InvalidRequestError)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'loading from hash' do
|
67
|
+
let(:valid_hash) do
|
68
|
+
{
|
69
|
+
ssh_private_file: 'secret',
|
70
|
+
ssh_public_file: 'secret.pub'
|
71
|
+
}
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'raises error on unknown argument' do
|
75
|
+
expect { subject.load_from_hash!({ a: 13 }) }.to raise_error(Phoebo::InvalidRequestError)
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'applies arguments' do
|
79
|
+
expect(subject.load_from_hash!(valid_hash).is_a?(described_class)).to eq true
|
80
|
+
expect(subject.ssh_private_file).to be valid_hash[:ssh_private_file]
|
81
|
+
expect(subject.ssh_public_file).to be valid_hash[:ssh_public_file]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
let(:json) do
|
86
|
+
<<-EOS
|
87
|
+
{
|
88
|
+
"repo_url": "ssh://somehost.tld/user/repo.git"
|
89
|
+
}
|
90
|
+
EOS
|
91
|
+
end
|
92
|
+
|
93
|
+
let(:malformed_json) do
|
94
|
+
<<-EOS
|
95
|
+
{
|
96
|
+
"repo_url
|
97
|
+
}
|
98
|
+
EOS
|
99
|
+
end
|
100
|
+
|
101
|
+
context 'loading from JSON' do
|
102
|
+
it 'raises error on malformed data' do
|
103
|
+
expect { subject.load_from_json!(malformed_json) }.to raise_error(Phoebo::InvalidRequestError)
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'applies arguments' do
|
107
|
+
subject.load_from_json!(json)
|
108
|
+
expect(subject.repo_url).to eql('ssh://somehost.tld/user/repo.git')
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
context 'loading from file' do
|
113
|
+
it 'raises error if file does not exist' do
|
114
|
+
allow(File).to receive(:exist?).and_return(false)
|
115
|
+
expect { subject.load_from_file!('somefile.json') }.to raise_error(Phoebo::IOError)
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'loads data' do
|
119
|
+
allow(File).to receive(:exist?).and_return(true)
|
120
|
+
allow(IO).to receive(:read).and_return(json)
|
121
|
+
expect(subject).to receive(:load_from_json!).with(json)
|
122
|
+
subject.load_from_file!('somefile.json')
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# TODO: cover all possible states
|
127
|
+
context 'loading from url' do
|
128
|
+
let(:http_response) do
|
129
|
+
response = double
|
130
|
+
allow(response).to receive(:code).and_return(200)
|
131
|
+
allow(response).to receive(:body).and_return(json)
|
132
|
+
response
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'loads data' do
|
136
|
+
allow(Net::HTTP).to receive(:start).and_return(http_response)
|
137
|
+
expect(subject).to receive(:load_from_json!).with(json)
|
138
|
+
subject.load_from_url!('http://test')
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
describe Phoebo::Worker do
|
4
|
+
|
5
|
+
let(:path) { './somepath' }
|
6
|
+
let(:request) {
|
7
|
+
image = instance_double(Phoebo::Request)
|
8
|
+
}
|
9
|
+
|
10
|
+
let(:config) {
|
11
|
+
config = instance_double(Phoebo::Config)
|
12
|
+
allow(config).to receive(:images).and_return([])
|
13
|
+
config
|
14
|
+
}
|
15
|
+
|
16
|
+
subject { described_class.new(request) }
|
17
|
+
|
18
|
+
context 'Pheobofile' do
|
19
|
+
before(:each) do
|
20
|
+
allow(File).to receive(:exists?).and_return(true)
|
21
|
+
allow(Phoebo::Config).to receive(:new_from_file).and_return(config)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'processes directories' do
|
25
|
+
subject.process(path)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: phoebo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adam Staněk
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-02-
|
11
|
+
date: 2015-02-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: colorize
|
@@ -134,14 +134,25 @@ files:
|
|
134
134
|
- lib/phoebo/docker/image_builder.rb
|
135
135
|
- lib/phoebo/docker/image_pusher.rb
|
136
136
|
- lib/phoebo/environment.rb
|
137
|
+
- lib/phoebo/git.rb
|
138
|
+
- lib/phoebo/ping.rb
|
139
|
+
- lib/phoebo/request.rb
|
137
140
|
- lib/phoebo/util.rb
|
138
141
|
- lib/phoebo/util/temp_file_manager.rb
|
139
142
|
- lib/phoebo/version.rb
|
143
|
+
- lib/phoebo/worker.rb
|
140
144
|
- phoebo.gemspec
|
141
145
|
- spec/phoebo/application_spec.rb
|
146
|
+
- spec/phoebo/config/image_spec.rb
|
147
|
+
- spec/phoebo/config_spec.rb
|
142
148
|
- spec/phoebo/console_spec.rb
|
149
|
+
- spec/phoebo/docker/image_builder_spec.rb
|
143
150
|
- spec/phoebo/environment_spec.rb
|
151
|
+
- spec/phoebo/git_spec.rb
|
152
|
+
- spec/phoebo/ping_spec.rb
|
153
|
+
- spec/phoebo/request_spec.rb
|
144
154
|
- spec/phoebo/util/temp_file_manager_spec.rb
|
155
|
+
- spec/phoebo/worker_spec.rb
|
145
156
|
- spec/spec_helper.rb
|
146
157
|
homepage: https://gitlab.fit.cvut.cz/phoebo/phoebo
|
147
158
|
licenses:
|
@@ -169,7 +180,14 @@ specification_version: 4
|
|
169
180
|
summary: CI worker for creating Docker images
|
170
181
|
test_files:
|
171
182
|
- spec/phoebo/application_spec.rb
|
183
|
+
- spec/phoebo/config/image_spec.rb
|
184
|
+
- spec/phoebo/config_spec.rb
|
172
185
|
- spec/phoebo/console_spec.rb
|
186
|
+
- spec/phoebo/docker/image_builder_spec.rb
|
173
187
|
- spec/phoebo/environment_spec.rb
|
188
|
+
- spec/phoebo/git_spec.rb
|
189
|
+
- spec/phoebo/ping_spec.rb
|
190
|
+
- spec/phoebo/request_spec.rb
|
174
191
|
- spec/phoebo/util/temp_file_manager_spec.rb
|
192
|
+
- spec/phoebo/worker_spec.rb
|
175
193
|
- spec/spec_helper.rb
|