phoebo 0.1.1
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 +7 -0
- data/.gitignore +14 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +11 -0
- data/Rakefile +2 -0
- data/bin/phoebo +20 -0
- data/lib/phoebo.rb +34 -0
- data/lib/phoebo/application.rb +238 -0
- data/lib/phoebo/config.rb +52 -0
- data/lib/phoebo/config/image.rb +35 -0
- data/lib/phoebo/config/image_commands.rb +20 -0
- data/lib/phoebo/config/image_commands/add.rb +17 -0
- data/lib/phoebo/config/image_commands/run.rb +14 -0
- data/lib/phoebo/console.rb +42 -0
- data/lib/phoebo/docker.rb +6 -0
- data/lib/phoebo/docker/image_builder.rb +85 -0
- data/lib/phoebo/docker/image_pusher.rb +30 -0
- data/lib/phoebo/environment.rb +63 -0
- data/lib/phoebo/util.rb +5 -0
- data/lib/phoebo/util/temp_file_manager.rb +35 -0
- data/lib/phoebo/version.rb +3 -0
- data/phoebo.gemspec +29 -0
- data/spec/phoebo/application_spec.rb +51 -0
- data/spec/phoebo/console_spec.rb +80 -0
- data/spec/phoebo/environment_spec.rb +31 -0
- data/spec/phoebo/util/temp_file_manager_spec.rb +23 -0
- data/spec/spec_helper.rb +99 -0
- metadata +162 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c2f04f78db0118a1c401d6e8fb38767540a0770c
|
4
|
+
data.tar.gz: 6b74da1cd4f1d534057c5e67c001a315978087ee
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 354236b15bef8f208762c2bbe4459a60066d7c323a3aacac03625cd56fa3417b89d573d4c8ca1c7746def9ee4889d4dfa9505962ac12b3ab5fc6c4a71c004cba
|
7
|
+
data.tar.gz: f5d1ef725707f8427491bf10fb525ad8a30c77503160b6a2f3de7ce44ea08c02bbc60a1d235c9e7849dc67100133e264b880b5d37088e2cf692690c38bbefff5
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Adam Staněk
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
data/Rakefile
ADDED
data/bin/phoebo
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Exit cleanly from an early interrupt
|
4
|
+
Signal.trap("INT") { exit 130 }
|
5
|
+
|
6
|
+
require 'phoebo'
|
7
|
+
|
8
|
+
begin
|
9
|
+
app = Phoebo::Application.new(ARGV)
|
10
|
+
code = app.run
|
11
|
+
app.cleanup
|
12
|
+
exit code
|
13
|
+
|
14
|
+
rescue Phoebo::PhoeboError => err
|
15
|
+
$stderr.puts "Error: ".red + err.message
|
16
|
+
$stdout.puts
|
17
|
+
exit err.status_code
|
18
|
+
ensure
|
19
|
+
app.cleanup
|
20
|
+
end
|
data/lib/phoebo.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'phoebo/version'
|
2
|
+
|
3
|
+
module Phoebo
|
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 :Util, 'phoebo/util'
|
10
|
+
|
11
|
+
class PhoeboError < StandardError
|
12
|
+
def self.status_code(code)
|
13
|
+
define_method(:status_code) { code }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class InvalidArgumentError < PhoeboError; status_code(4) ; end
|
18
|
+
class IOError < PhoeboError; status_code(5) ; end
|
19
|
+
class ExternalError < PhoeboError; status_code(6) ; end
|
20
|
+
|
21
|
+
# Phoebofile errors
|
22
|
+
class ConfigError < PhoeboError; status_code(7) ; end
|
23
|
+
class SyntaxError < ConfigError; end
|
24
|
+
|
25
|
+
# Error while generating Docker image
|
26
|
+
class DockerError < PhoeboError; status_code(8) ; end
|
27
|
+
|
28
|
+
# Configure Phoebo environment from Phoebofile
|
29
|
+
def self.configure(version, &block)
|
30
|
+
raise ConfigError, "Configuration version #{version} not supported" if version != 1
|
31
|
+
Config.load_from_block(block)
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,238 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'colorize'
|
3
|
+
require 'docker'
|
4
|
+
require 'rugged'
|
5
|
+
require 'pathname'
|
6
|
+
|
7
|
+
module Phoebo
|
8
|
+
#
|
9
|
+
# Main Application
|
10
|
+
#
|
11
|
+
# Performs run sequence and holds instances of necessary services to ease
|
12
|
+
# access without need of dependency injection. You should still allow to
|
13
|
+
# pass dependency if necessary (for testing).
|
14
|
+
# Use following snippet in your class:
|
15
|
+
#
|
16
|
+
# class MyClass
|
17
|
+
# attr_writer :environment
|
18
|
+
# def environment
|
19
|
+
# @environment ||= Application.instance.environment
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
class Application
|
24
|
+
include Console
|
25
|
+
|
26
|
+
# Creates application
|
27
|
+
def initialize(args = [])
|
28
|
+
@args = args
|
29
|
+
@@instance = self
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns last application instance
|
33
|
+
def self.instance
|
34
|
+
@@instance
|
35
|
+
end
|
36
|
+
|
37
|
+
# Environment service instance
|
38
|
+
def environment
|
39
|
+
@environment ||= Environment.new
|
40
|
+
end
|
41
|
+
|
42
|
+
# Temporary File Manager
|
43
|
+
def temp_file_manager
|
44
|
+
@temp_file_manager ||= Util::TempFileManager.new(environment.temp_path)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Run sequence
|
48
|
+
def run()
|
49
|
+
result = 0
|
50
|
+
options = parse_options
|
51
|
+
|
52
|
+
if options[:error]
|
53
|
+
result = 1
|
54
|
+
stderr.puts 'Error: '.red + options[:error]
|
55
|
+
stderr.puts
|
56
|
+
end
|
57
|
+
|
58
|
+
send(('run_' + options[:mode].to_s).to_sym, options).to_i || result
|
59
|
+
end
|
60
|
+
|
61
|
+
# Cleanup sequence
|
62
|
+
def cleanup
|
63
|
+
if @temp_file_manager && @temp_file_manager.need_cleanup?
|
64
|
+
stdout.puts "Cleaning up"
|
65
|
+
stdout.puts
|
66
|
+
@temp_file_manager.cleanup
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
# Run in normal mode
|
73
|
+
def run_normal(options)
|
74
|
+
|
75
|
+
# Default project path, is PWD
|
76
|
+
project_path = Dir.pwd
|
77
|
+
|
78
|
+
# If we are building image from Git repository
|
79
|
+
if options[:repository]
|
80
|
+
|
81
|
+
# Prepare SSH credentials if necessary
|
82
|
+
if options[:ssh_key]
|
83
|
+
private_path = Pathname.new(options[:ssh_key])
|
84
|
+
|
85
|
+
raise InvalidArgumentError, "SSH key not found" unless private_path.exist?
|
86
|
+
raise InvalidArgumentError, "Missing public SSH key" unless options[:ssh_public]
|
87
|
+
|
88
|
+
public_path = Pathname.new(options[:ssh_public])
|
89
|
+
raise InvalidArgumentError, "Public SSH key not found" unless public_path.exist?
|
90
|
+
|
91
|
+
cred = Rugged::Credentials::SshKey.new(
|
92
|
+
username: options[:ssh_user],
|
93
|
+
publickey: public_path.realpath.to_s,
|
94
|
+
privatekey: private_path.realpath.to_s
|
95
|
+
)
|
96
|
+
else
|
97
|
+
cred = nil
|
98
|
+
end
|
99
|
+
|
100
|
+
# Create temp dir.
|
101
|
+
project_path = temp_file_manager.path('project')
|
102
|
+
|
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
|
+
else
|
126
|
+
pusher = nil
|
127
|
+
end
|
128
|
+
|
129
|
+
# Exit code
|
130
|
+
result = 0
|
131
|
+
|
132
|
+
# Process all the files
|
133
|
+
options[:files] << '.' if options[:files].empty?
|
134
|
+
options[:files].each do |rel_path|
|
135
|
+
|
136
|
+
path = (Pathname.new(project_path) + rel_path).realpath.to_s
|
137
|
+
|
138
|
+
# Prepare config path
|
139
|
+
config_filename = 'Phoebofile'
|
140
|
+
config_path = "#{path}#{File::SEPARATOR}#{config_filename}"
|
141
|
+
|
142
|
+
# Check if config exists -> resumable error
|
143
|
+
unless File.exists?(config_path)
|
144
|
+
stderr.puts "No #{config_filename} found in #{path}".red
|
145
|
+
result = 1
|
146
|
+
next
|
147
|
+
end
|
148
|
+
|
149
|
+
# Load config
|
150
|
+
config = Config.load_from_file(config_path)
|
151
|
+
|
152
|
+
# Build & push image
|
153
|
+
builder = Docker::ImageBuilder.new(path)
|
154
|
+
config.images.each do |image|
|
155
|
+
image_id = builder.build(image)
|
156
|
+
pusher.push(image_id) if pusher
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
puts "Everything done :-)".green
|
161
|
+
|
162
|
+
result
|
163
|
+
end
|
164
|
+
|
165
|
+
# Show help
|
166
|
+
def run_help(_options)
|
167
|
+
stdout.puts @option_parser
|
168
|
+
stdout.puts
|
169
|
+
end
|
170
|
+
|
171
|
+
# Show version info
|
172
|
+
def run_version(_options)
|
173
|
+
stdout.puts "Version: #{Phoebo::VERSION}"
|
174
|
+
stdout.puts
|
175
|
+
end
|
176
|
+
|
177
|
+
# Parse passed command-line options
|
178
|
+
def parse_options
|
179
|
+
options = { error: nil, mode: :normal, files: [], ssh_user: 'git' }
|
180
|
+
|
181
|
+
unless @option_parser
|
182
|
+
@option_parser = OptionParser.new
|
183
|
+
@option_parser.banner = \
|
184
|
+
"Usage: #{@option_parser.program_name} [options] file"
|
185
|
+
|
186
|
+
@option_parser.on_tail('-rURL', '--repository=URL', 'Repository URL') do |value|
|
187
|
+
options[:repository] = value
|
188
|
+
end
|
189
|
+
|
190
|
+
@option_parser.on_tail('--ssh-user=USERNAME', 'Username for Git over SSH (Default: git)') do |value|
|
191
|
+
options[:ssh_user] = value
|
192
|
+
end
|
193
|
+
|
194
|
+
@option_parser.on_tail('--ssh-public=PATH', 'Path to public SSH key for Git repository') do |value|
|
195
|
+
options[:ssh_public] = value
|
196
|
+
end
|
197
|
+
|
198
|
+
@option_parser.on_tail('--ssh-key=PATH', 'Path to SSH key for Git repository') do |value|
|
199
|
+
options[:ssh_key] = value
|
200
|
+
end
|
201
|
+
|
202
|
+
@option_parser.on_tail('--docker-user=USERNAME', 'Username for Docker Registry') do |value|
|
203
|
+
options[:docker_username] = value
|
204
|
+
end
|
205
|
+
|
206
|
+
@option_parser.on_tail('--docker-password=PASSWORD', 'Password for Docker Registry') do |value|
|
207
|
+
options[:docker_password] = value
|
208
|
+
end
|
209
|
+
|
210
|
+
@option_parser.on_tail('--docker-email=EMAIL', 'E-mail for Docker Registry') do |value|
|
211
|
+
options[:docker_email] = value
|
212
|
+
end
|
213
|
+
|
214
|
+
@option_parser.on_tail('--version', 'Show version info') do
|
215
|
+
options[:mode] = :version
|
216
|
+
end
|
217
|
+
|
218
|
+
@option_parser.on_tail('-h', '--help', 'Show this message') do
|
219
|
+
options[:mode] = :help
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
begin
|
224
|
+
# Parse and go through all non-options
|
225
|
+
@option_parser.parse(@args).each do |arg|
|
226
|
+
options[:files] << arg
|
227
|
+
end
|
228
|
+
|
229
|
+
rescue OptionParser::ParseError => err
|
230
|
+
options[:mode] = :help
|
231
|
+
options[:error] = err.message
|
232
|
+
end
|
233
|
+
|
234
|
+
options
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Phoebo
|
2
|
+
class Config
|
3
|
+
autoload :Image, 'phoebo/config/image'
|
4
|
+
autoload :ImageCommands, 'phoebo/config/image_commands'
|
5
|
+
|
6
|
+
attr_accessor :images
|
7
|
+
|
8
|
+
# Loads config from file
|
9
|
+
# @see Phoebo.configure()
|
10
|
+
def self.load_from_file(file_path)
|
11
|
+
begin
|
12
|
+
@instance = nil
|
13
|
+
Kernel.load File.expand_path(file_path), true
|
14
|
+
@instance
|
15
|
+
|
16
|
+
rescue ::SyntaxError => e
|
17
|
+
raise Phoebo::SyntaxError, e.message
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.load_from_block(block)
|
22
|
+
@instance = self.new
|
23
|
+
@instance.dsl_eval(block)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Instance initialization
|
27
|
+
def initialize
|
28
|
+
@images = []
|
29
|
+
end
|
30
|
+
|
31
|
+
# Evaluate block within DSL context
|
32
|
+
def dsl_eval(block)
|
33
|
+
@dsl ||= DSL.new(self)
|
34
|
+
@dsl.instance_eval(&block)
|
35
|
+
self
|
36
|
+
end
|
37
|
+
|
38
|
+
# Private DSL
|
39
|
+
class DSL
|
40
|
+
def initialize(config)
|
41
|
+
@config = config
|
42
|
+
end
|
43
|
+
|
44
|
+
def image(name, from, &block)
|
45
|
+
@config.images << (img_instance = Image.new(name, from))
|
46
|
+
img_instance.dsl_eval(block)
|
47
|
+
img_instance
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Phoebo
|
2
|
+
class Config
|
3
|
+
class Image
|
4
|
+
attr_accessor :actions, :name, :from
|
5
|
+
|
6
|
+
def initialize(name, from)
|
7
|
+
@actions = []
|
8
|
+
@name = name
|
9
|
+
@from = from
|
10
|
+
end
|
11
|
+
|
12
|
+
# Evaluate block within DSL context
|
13
|
+
def dsl_eval(block)
|
14
|
+
@dsl ||= DSL.new(self)
|
15
|
+
@dsl.instance_eval(&block)
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
# Private DSL
|
20
|
+
class DSL
|
21
|
+
def initialize(image)
|
22
|
+
@image = image
|
23
|
+
end
|
24
|
+
|
25
|
+
# Define action methods for all commands
|
26
|
+
ImageCommands.commands.each do |id, command_class|
|
27
|
+
define_method(id) do |*args|
|
28
|
+
@image.actions << command_class.send(:action, *args)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# Auto-require for all Image commands
|
2
|
+
Dir[File.dirname(__FILE__) + '/image_commands/*.rb'].each { |file| require file }
|
3
|
+
|
4
|
+
# Module containing all Image commands
|
5
|
+
module Phoebo::Config::ImageCommands
|
6
|
+
|
7
|
+
# Returns all discovered commands
|
8
|
+
def self.commands
|
9
|
+
unless @commands
|
10
|
+
@commands = {}
|
11
|
+
self.constants.each do |c|
|
12
|
+
c = self.const_get(c)
|
13
|
+
next unless Class === c
|
14
|
+
@commands[c.id] = c
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
@commands
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Phoebo::Config::ImageCommands
|
2
|
+
class Add
|
3
|
+
def self.id
|
4
|
+
:add
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.action(src, dest)
|
8
|
+
return Proc.new do |dockerfile, files|
|
9
|
+
|
10
|
+
virtual = 'project/' + src
|
11
|
+
files[virtual] = src
|
12
|
+
|
13
|
+
dockerfile << 'ADD ' + virtual + ' ' + dest
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Phoebo::Config::ImageCommands
|
2
|
+
class Run
|
3
|
+
def self.id
|
4
|
+
:run
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.action(cmd, *args)
|
8
|
+
return Proc.new do |dockerfile, files|
|
9
|
+
# TODO: decide which exec method we will use and enforce escaping
|
10
|
+
dockerfile << "RUN #{cmd} #{args.join(' ')}"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
#
|
2
|
+
# Helper mixin which allows to pass different IO objects as STDOUT/STDERR.
|
3
|
+
# It creates consistent output layer for our classes.
|
4
|
+
#
|
5
|
+
# Use `include Phoebo::Console` in classes which require to write console output.
|
6
|
+
# You can override output stream for each object on class and instance level.
|
7
|
+
#
|
8
|
+
# MyClass.stdout = StringIO.new sets stream for all future instances
|
9
|
+
# MyClass.new.stdout = StringIO.new sets stream for a particular instance
|
10
|
+
#
|
11
|
+
# Default output streams are $stdout / $stderr
|
12
|
+
#
|
13
|
+
module Phoebo::Console
|
14
|
+
|
15
|
+
attr_writer :stdout, :stderr
|
16
|
+
|
17
|
+
def stdout
|
18
|
+
@stdout ||= self.class.stdout
|
19
|
+
end
|
20
|
+
|
21
|
+
def stderr
|
22
|
+
@stderr ||= self.class.stderr
|
23
|
+
end
|
24
|
+
|
25
|
+
# We want to also allow Class defaults
|
26
|
+
module ClassMethods
|
27
|
+
attr_writer :stdout, :stderr
|
28
|
+
|
29
|
+
def stdout
|
30
|
+
@stdout ||= $stdout
|
31
|
+
end
|
32
|
+
|
33
|
+
def stderr
|
34
|
+
@stderr ||= $stderr
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# On include: also extend by ClassMethods
|
39
|
+
def self.included(host_class)
|
40
|
+
host_class.extend(ClassMethods)
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'rubygems/package'
|
2
|
+
|
3
|
+
module Phoebo
|
4
|
+
module Docker
|
5
|
+
class ImageBuilder
|
6
|
+
include Console
|
7
|
+
|
8
|
+
def initialize(base_path)
|
9
|
+
@base_path = Pathname.new(base_path)
|
10
|
+
end
|
11
|
+
|
12
|
+
def build(image)
|
13
|
+
project_files = { }
|
14
|
+
dockerfile = []
|
15
|
+
dockerfile << "FROM #{image.from}"
|
16
|
+
|
17
|
+
# Apply all defined actions
|
18
|
+
image.actions.each do |action|
|
19
|
+
action.call(dockerfile, project_files)
|
20
|
+
end
|
21
|
+
|
22
|
+
# TODO: Honor file mode
|
23
|
+
tar_stream = StringIO.new
|
24
|
+
Gem::Package::TarWriter.new(tar_stream) do |tar|
|
25
|
+
tar.add_file('Dockerfile', 0640) { |out_stream| out_stream.write(dockerfile.join("\n")) }
|
26
|
+
tar.mkdir('project', 0750)
|
27
|
+
|
28
|
+
# Copy project file into virtual destination
|
29
|
+
while !project_files.empty? do
|
30
|
+
virtual, relative = project_files.shift
|
31
|
+
real = @base_path + relative
|
32
|
+
|
33
|
+
if real.directory?
|
34
|
+
real.each_entry do |child|
|
35
|
+
next if child.fnmatch?('..') or child.fnmatch?('.')
|
36
|
+
if child.directory?
|
37
|
+
tar.mkdir("#{virtual}/#{child}", 0750)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Add to stack
|
41
|
+
project_files["#{virtual}/#{child}"] = "#{relative}/#{child}"
|
42
|
+
end
|
43
|
+
|
44
|
+
elsif real.file?
|
45
|
+
|
46
|
+
# 'file.txt' -> 'dest/'
|
47
|
+
if virtual.end_with?('/')
|
48
|
+
dest += real.basename.to_s
|
49
|
+
end
|
50
|
+
|
51
|
+
# Read file to our TAR output stream
|
52
|
+
tar.add_file(virtual, 0640) do |out_stream|
|
53
|
+
in_stream = real.open('r')
|
54
|
+
IO.copy_stream(in_stream, out_stream)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
begin
|
61
|
+
stdout.puts "Building image " + "...".light_black
|
62
|
+
built_image = ::Docker::Image.build_from_tar(tar_stream.tap(&:rewind))
|
63
|
+
|
64
|
+
|
65
|
+
# TODO: Perhaps we should calculate size from virtual size of the base,
|
66
|
+
# this is not working very well while merging layers.
|
67
|
+
stdout.puts "Image build successful".green
|
68
|
+
stdout.print "ID: " + built_image.id.to_s.cyan
|
69
|
+
stdout.print " size: " + ('%.2f MB' % (built_image.json['Size'].to_f / 1000 / 1000)).cyan if built_image.json['Size'] > 100
|
70
|
+
stdout.print " virtual size: " + ('%.2f MB' % (built_image.json['VirtualSize'].to_f / 1000 / 1000)).cyan
|
71
|
+
stdout.puts
|
72
|
+
|
73
|
+
stdout.puts "Tagging image #{built_image.id.to_s.cyan} -> #{image.name.cyan}"
|
74
|
+
built_image.tag('repo' => image.name, 'force' => true)
|
75
|
+
|
76
|
+
# Return image ID
|
77
|
+
built_image.id
|
78
|
+
|
79
|
+
rescue ::Docker::Error::UnexpectedResponseError => e
|
80
|
+
raise DockerError, e.message
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Phoebo
|
2
|
+
module Docker
|
3
|
+
class ImagePusher
|
4
|
+
include Console
|
5
|
+
|
6
|
+
def initialize(username, password, email)
|
7
|
+
@username = username
|
8
|
+
@password = password
|
9
|
+
@email = email
|
10
|
+
end
|
11
|
+
|
12
|
+
def push(image_id)
|
13
|
+
unless @authenticated
|
14
|
+
@authenticated = true
|
15
|
+
stdout.puts "Authenticating " + "...".light_black
|
16
|
+
begin
|
17
|
+
::Docker.authenticate!({'username' => @username, 'password' => @password, 'email' => @email}, ::Docker.connection)
|
18
|
+
rescue ::Docker::Error::AuthenticationError => e
|
19
|
+
raise DockerError, 'Authentication to docker registry failed.'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
stdout.puts "Pushing image " + image_id.cyan + " ...".light_black
|
24
|
+
|
25
|
+
built_image = ::Docker::Image.get(image_id)
|
26
|
+
built_image.push
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# Provides layer for accessing external environment
|
2
|
+
class Phoebo::Environment
|
3
|
+
|
4
|
+
# Reader definition macro
|
5
|
+
def self.exe_path_reader(name, bin = nil)
|
6
|
+
define_method("#{name}_path".to_sym) do
|
7
|
+
if instance_variable_defined? "@#{name}"
|
8
|
+
instance_variable_get "@#{name}"
|
9
|
+
else
|
10
|
+
instance_variable_set "@#{name}", path = exe_lookup(bin || name)
|
11
|
+
path
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Path to executables
|
17
|
+
# Example: exe_path_reader :our_bin, 'binary-name-to-lookup'
|
18
|
+
# -> Environment.our_bin_path
|
19
|
+
exe_path_reader :git
|
20
|
+
exe_path_reader :bash
|
21
|
+
exe_path_reader :ssh_agent, 'ssh-agent'
|
22
|
+
exe_path_reader :ssh_add, 'ssh-add'
|
23
|
+
|
24
|
+
# Constructor
|
25
|
+
def initialize(env = ENV)
|
26
|
+
@env = env
|
27
|
+
end
|
28
|
+
|
29
|
+
# Temporary files
|
30
|
+
def temp_path
|
31
|
+
"#{File::SEPARATOR}tmp"
|
32
|
+
end
|
33
|
+
|
34
|
+
# Check availability
|
35
|
+
def check_availability(*executables)
|
36
|
+
executables.each do |exe|
|
37
|
+
fail Phoebo::ExternalError, "Program #{exe} not found" \
|
38
|
+
unless send("#{exe}_path".to_sym)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
# Look up executable in system path ($PATH)
|
45
|
+
def exe_lookup(bin)
|
46
|
+
if @system_path || @env['PATH']
|
47
|
+
@system_path ||= @env['PATH'].encode(
|
48
|
+
'UTF-8', 'binary',
|
49
|
+
invalid: :replace,
|
50
|
+
undef: :replace,
|
51
|
+
replace: ''
|
52
|
+
).split(File::PATH_SEPARATOR)
|
53
|
+
|
54
|
+
@system_path.each do |path|
|
55
|
+
file_path = "#{path}#{File::SEPARATOR}#{bin}"
|
56
|
+
return file_path if File.executable? file_path
|
57
|
+
end
|
58
|
+
|
59
|
+
nil
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
data/lib/phoebo/util.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
# Temporary File Manager
|
4
|
+
#
|
5
|
+
# - Creates temporary directory exclusive for our process
|
6
|
+
# - Cleans up
|
7
|
+
#
|
8
|
+
class Phoebo::Util::TempFileManager
|
9
|
+
|
10
|
+
def initialize(temp_path)
|
11
|
+
@base_path = temp_path
|
12
|
+
end
|
13
|
+
|
14
|
+
def path(*components)
|
15
|
+
unless @process_path
|
16
|
+
@process_path = "#{@base_path}#{File::SEPARATOR}phoebo-#{Process.pid}"
|
17
|
+
FileUtils::mkdir_p @process_path
|
18
|
+
FileUtils::chmod 0600, @process_path
|
19
|
+
end
|
20
|
+
|
21
|
+
File.join(@process_path, components)
|
22
|
+
end
|
23
|
+
|
24
|
+
def need_cleanup?
|
25
|
+
@process_path != nil && File.exist?(@process_path)
|
26
|
+
end
|
27
|
+
|
28
|
+
def cleanup
|
29
|
+
if @process_path
|
30
|
+
FileUtils.remove_dir @process_path
|
31
|
+
@process_path = nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
data/phoebo.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'phoebo/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "phoebo"
|
8
|
+
spec.version = Phoebo::VERSION
|
9
|
+
spec.authors = ["Adam Staněk"]
|
10
|
+
spec.email = ["adam.stanek@v3net.cz"]
|
11
|
+
spec.summary = %q{CI worker for creating Docker images}
|
12
|
+
spec.description = %q{Phoebo creates ready-to-deploy Docker images from project Git repository}
|
13
|
+
spec.homepage = "https://gitlab.fit.cvut.cz/phoebo/phoebo"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.required_ruby_version = '~> 2.1'
|
22
|
+
|
23
|
+
spec.add_dependency "colorize", '~> 0'
|
24
|
+
spec.add_dependency "docker-api", '~> 1.17'
|
25
|
+
spec.add_dependency "rugged", '~> 0.21'
|
26
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
27
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
28
|
+
spec.add_development_dependency "rspec", '~> 3.0'
|
29
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
describe Phoebo::Application 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
|
+
context 'general' do
|
18
|
+
subject(:app) { described_class.new }
|
19
|
+
|
20
|
+
it 'is runnable' do
|
21
|
+
expect(app.respond_to?(:run)).to eq true
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'holds instance' do
|
25
|
+
app
|
26
|
+
expect(described_class.instance.is_a?(described_class)).to be true
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'creates Environment' do
|
30
|
+
expect(app.environment.is_a?(Phoebo::Environment)).to be true
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'creates TemporaryFileManager' do
|
34
|
+
expect(app.temp_file_manager.is_a?(Phoebo::Util::TempFileManager)).to be true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context '--version argument' do
|
39
|
+
subject(:app) { described_class.new(['--version']) }
|
40
|
+
|
41
|
+
it 'returns 0' do
|
42
|
+
expect(app.run).to eq 0
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'shows version' do
|
46
|
+
app.run
|
47
|
+
expect(app.stdout.string).to include(Phoebo::VERSION)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
describe Phoebo::Console do
|
4
|
+
before {
|
5
|
+
@sample_class = Class.new do
|
6
|
+
include Phoebo::Console
|
7
|
+
end
|
8
|
+
}
|
9
|
+
|
10
|
+
context 'default streams' do
|
11
|
+
subject {
|
12
|
+
@sample_class.new
|
13
|
+
}
|
14
|
+
|
15
|
+
it 'stdout returns io' do
|
16
|
+
expect(subject.stdout.is_a?(IO)).to eq true
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'stderr returns io' do
|
20
|
+
expect(subject.stderr.is_a?(IO)).to eq true
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'streams set for class' do
|
25
|
+
subject {
|
26
|
+
sample_class = @sample_class.dup
|
27
|
+
sample_class.stdout = StringIO.new
|
28
|
+
sample_class.stderr = StringIO.new
|
29
|
+
sample_class.new
|
30
|
+
}
|
31
|
+
|
32
|
+
it 'does not effect default streams' do
|
33
|
+
expect(subject.stdout).not_to be @sample_class.stdout
|
34
|
+
expect(subject.stderr).not_to be @sample_class.stderr
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'stdout returns class default' do
|
38
|
+
expect(subject.stdout.is_a?(StringIO)).to eq true
|
39
|
+
expect(subject.stdout).to be subject.class.stdout
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'stderr returns class default' do
|
43
|
+
expect(subject.stderr.is_a?(StringIO)).to eq true
|
44
|
+
expect(subject.stderr).to be subject.class.stderr
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'stdout != stderr' do
|
48
|
+
expect(subject.stdout).not_to be subject.stderr
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'streams set for instance' do
|
53
|
+
subject {
|
54
|
+
sample_class = @sample_class.dup
|
55
|
+
sample_class.stdout = StringIO.new
|
56
|
+
sample_class.stderr = StringIO.new
|
57
|
+
|
58
|
+
subject = sample_class.new
|
59
|
+
subject.stdout = StringIO.new
|
60
|
+
subject.stderr = StringIO.new
|
61
|
+
|
62
|
+
subject
|
63
|
+
}
|
64
|
+
|
65
|
+
it 'stdout returns set io' do
|
66
|
+
expect(subject.stdout.is_a?(StringIO)).to eq true
|
67
|
+
expect(subject.stdout).not_to be subject.class.stdout
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'stderr returns set io' do
|
71
|
+
expect(subject.stderr.is_a?(StringIO)).to eq true
|
72
|
+
expect(subject.stderr).not_to be subject.class.stderr
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'stdout != stderr' do
|
76
|
+
expect(subject.stdout).not_to be subject.stderr
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
describe Phoebo::Environment do
|
4
|
+
before(:all) {
|
5
|
+
@path = [
|
6
|
+
"#{File::SEPARATOR}a",
|
7
|
+
"#{File::SEPARATOR}b",
|
8
|
+
"#{File::SEPARATOR}c"
|
9
|
+
]
|
10
|
+
}
|
11
|
+
|
12
|
+
subject(:env) {
|
13
|
+
described_class.new({'PATH' => @path.join(File::PATH_SEPARATOR) })
|
14
|
+
}
|
15
|
+
|
16
|
+
context 'path lookup' do
|
17
|
+
it 'looks up path' do
|
18
|
+
allow(File).to receive(:executable?).with("#{@path[0]}#{File::SEPARATOR}bash").and_return(false)
|
19
|
+
allow(File).to receive(:executable?).with("#{@path[1]}#{File::SEPARATOR}bash").and_return(true)
|
20
|
+
allow(File).to receive(:executable?).with("#{@path[2]}#{File::SEPARATOR}bash").and_return(true)
|
21
|
+
expect(env.bash_path).to eq "#{@path[1]}#{File::SEPARATOR}bash"
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'nil if not found' do
|
25
|
+
@path.each { |path|
|
26
|
+
allow(File).to receive(:executable?).with("#{path}#{File::SEPARATOR}git").and_return(false)
|
27
|
+
}
|
28
|
+
expect(env.git_path).to eq nil
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
|
3
|
+
describe Phoebo::Util::TempFileManager, :order => :defined do
|
4
|
+
before (:all) {
|
5
|
+
@manager = described_class.new("#{File::SEPARATOR}tmp")
|
6
|
+
}
|
7
|
+
|
8
|
+
it 'does not need clean-up at init' do
|
9
|
+
expect(@manager.need_cleanup?).to eq false
|
10
|
+
end
|
11
|
+
# ->
|
12
|
+
it 'creates path' do
|
13
|
+
@manager.path 'a', 'b'
|
14
|
+
end
|
15
|
+
# ->
|
16
|
+
it 'does need to clean-up' do
|
17
|
+
expect(@manager.need_cleanup?).to eq true
|
18
|
+
end
|
19
|
+
# ->
|
20
|
+
it 'cleans up' do
|
21
|
+
@manager.cleanup
|
22
|
+
end
|
23
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
$:.unshift File.expand_path('../../lib', __FILE__)
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
Bundler.setup
|
5
|
+
|
6
|
+
require 'phoebo'
|
7
|
+
|
8
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
9
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
10
|
+
# The generated `.rspec` file contains `--require spec_helper` which will cause this
|
11
|
+
# file to always be loaded, without a need to explicitly require it in any files.
|
12
|
+
#
|
13
|
+
# Given that it is always loaded, you are encouraged to keep this file as
|
14
|
+
# light-weight as possible. Requiring heavyweight dependencies from this file
|
15
|
+
# will add to the boot time of your test suite on EVERY test run, even for an
|
16
|
+
# individual file that may not need all of that loaded. Instead, consider making
|
17
|
+
# a separate helper file that requires the additional dependencies and performs
|
18
|
+
# the additional setup, and require it from the spec files that actually need it.
|
19
|
+
#
|
20
|
+
# The `.rspec` file also contains a few flags that are not defaults but that
|
21
|
+
# users commonly want.
|
22
|
+
#
|
23
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
24
|
+
RSpec.configure do |config|
|
25
|
+
|
26
|
+
config.formatter = :documentation
|
27
|
+
|
28
|
+
# rspec-expectations config goes here. You can use an alternate
|
29
|
+
# assertion/expectation library such as wrong or the stdlib/minitest
|
30
|
+
# assertions if you prefer.
|
31
|
+
config.expect_with :rspec do |expectations|
|
32
|
+
# This option will default to `true` in RSpec 4. It makes the `description`
|
33
|
+
# and `failure_message` of custom matchers include text for helper methods
|
34
|
+
# defined using `chain`, e.g.:
|
35
|
+
# be_bigger_than(2).and_smaller_than(4).description
|
36
|
+
# # => "be bigger than 2 and smaller than 4"
|
37
|
+
# ...rather than:
|
38
|
+
# # => "be bigger than 2"
|
39
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
40
|
+
end
|
41
|
+
|
42
|
+
# rspec-mocks config goes here. You can use an alternate test double
|
43
|
+
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
44
|
+
config.mock_with :rspec do |mocks|
|
45
|
+
# Prevents you from mocking or stubbing a method that does not exist on
|
46
|
+
# a real object. This is generally recommended, and will default to
|
47
|
+
# `true` in RSpec 4.
|
48
|
+
mocks.verify_partial_doubles = true
|
49
|
+
end
|
50
|
+
|
51
|
+
# The settings below are suggested to provide a good initial experience
|
52
|
+
# with RSpec, but feel free to customize to your heart's content.
|
53
|
+
=begin
|
54
|
+
# These two settings work together to allow you to limit a spec run
|
55
|
+
# to individual examples or groups you care about by tagging them with
|
56
|
+
# `:focus` metadata. When nothing is tagged with `:focus`, all examples
|
57
|
+
# get run.
|
58
|
+
config.filter_run :focus
|
59
|
+
config.run_all_when_everything_filtered = true
|
60
|
+
|
61
|
+
# Limits the available syntax to the non-monkey patched syntax that is recommended.
|
62
|
+
# For more details, see:
|
63
|
+
# - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
|
64
|
+
# - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
65
|
+
# - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
|
66
|
+
config.disable_monkey_patching!
|
67
|
+
|
68
|
+
# This setting enables warnings. It's recommended, but in some cases may
|
69
|
+
# be too noisy due to issues in dependencies.
|
70
|
+
config.warnings = true
|
71
|
+
|
72
|
+
# Many RSpec users commonly either run the entire suite or an individual
|
73
|
+
# file, and it's useful to allow more verbose output when running an
|
74
|
+
# individual spec file.
|
75
|
+
if config.files_to_run.one?
|
76
|
+
# Use the documentation formatter for detailed output,
|
77
|
+
# unless a formatter has already been configured
|
78
|
+
# (e.g. via a command-line flag).
|
79
|
+
config.default_formatter = 'doc'
|
80
|
+
end
|
81
|
+
|
82
|
+
# Print the 10 slowest examples and example groups at the
|
83
|
+
# end of the spec run, to help surface which specs are running
|
84
|
+
# particularly slow.
|
85
|
+
config.profile_examples = 10
|
86
|
+
|
87
|
+
# Run specs in random order to surface order dependencies. If you find an
|
88
|
+
# order dependency and want to debug it, you can fix the order by providing
|
89
|
+
# the seed, which is printed after each run.
|
90
|
+
# --seed 1234
|
91
|
+
config.order = :random
|
92
|
+
|
93
|
+
# Seed global randomization in this process using the `--seed` CLI option.
|
94
|
+
# Setting this allows you to use `--seed` to deterministically reproduce
|
95
|
+
# test failures related to randomization by passing the same `--seed` value
|
96
|
+
# as the one that triggered the failure.
|
97
|
+
Kernel.srand config.seed
|
98
|
+
=end
|
99
|
+
end
|
metadata
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: phoebo
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Adam Staněk
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-02-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: colorize
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: docker-api
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.17'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.17'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rugged
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.21'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.21'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bundler
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.7'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.7'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '10.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '10.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '3.0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '3.0'
|
97
|
+
description: Phoebo creates ready-to-deploy Docker images from project Git repository
|
98
|
+
email:
|
99
|
+
- adam.stanek@v3net.cz
|
100
|
+
executables:
|
101
|
+
- phoebo
|
102
|
+
extensions: []
|
103
|
+
extra_rdoc_files: []
|
104
|
+
files:
|
105
|
+
- ".gitignore"
|
106
|
+
- ".rspec"
|
107
|
+
- Gemfile
|
108
|
+
- LICENSE.txt
|
109
|
+
- README.md
|
110
|
+
- Rakefile
|
111
|
+
- bin/phoebo
|
112
|
+
- lib/phoebo.rb
|
113
|
+
- lib/phoebo/application.rb
|
114
|
+
- lib/phoebo/config.rb
|
115
|
+
- lib/phoebo/config/image.rb
|
116
|
+
- lib/phoebo/config/image_commands.rb
|
117
|
+
- lib/phoebo/config/image_commands/add.rb
|
118
|
+
- lib/phoebo/config/image_commands/run.rb
|
119
|
+
- lib/phoebo/console.rb
|
120
|
+
- lib/phoebo/docker.rb
|
121
|
+
- lib/phoebo/docker/image_builder.rb
|
122
|
+
- lib/phoebo/docker/image_pusher.rb
|
123
|
+
- lib/phoebo/environment.rb
|
124
|
+
- lib/phoebo/util.rb
|
125
|
+
- lib/phoebo/util/temp_file_manager.rb
|
126
|
+
- lib/phoebo/version.rb
|
127
|
+
- phoebo.gemspec
|
128
|
+
- spec/phoebo/application_spec.rb
|
129
|
+
- spec/phoebo/console_spec.rb
|
130
|
+
- spec/phoebo/environment_spec.rb
|
131
|
+
- spec/phoebo/util/temp_file_manager_spec.rb
|
132
|
+
- spec/spec_helper.rb
|
133
|
+
homepage: https://gitlab.fit.cvut.cz/phoebo/phoebo
|
134
|
+
licenses:
|
135
|
+
- MIT
|
136
|
+
metadata: {}
|
137
|
+
post_install_message:
|
138
|
+
rdoc_options: []
|
139
|
+
require_paths:
|
140
|
+
- lib
|
141
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '2.1'
|
146
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
147
|
+
requirements:
|
148
|
+
- - ">="
|
149
|
+
- !ruby/object:Gem::Version
|
150
|
+
version: '0'
|
151
|
+
requirements: []
|
152
|
+
rubyforge_project:
|
153
|
+
rubygems_version: 2.4.5
|
154
|
+
signing_key:
|
155
|
+
specification_version: 4
|
156
|
+
summary: CI worker for creating Docker images
|
157
|
+
test_files:
|
158
|
+
- spec/phoebo/application_spec.rb
|
159
|
+
- spec/phoebo/console_spec.rb
|
160
|
+
- spec/phoebo/environment_spec.rb
|
161
|
+
- spec/phoebo/util/temp_file_manager_spec.rb
|
162
|
+
- spec/spec_helper.rb
|