dockistrano 0.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 031a5bef368643972d84782c3fe8aec9f9a83cdc
4
+ data.tar.gz: d2f202905234e891b5a2ad5c07b453365d9aec1c
5
+ SHA512:
6
+ metadata.gz: d60c0c8983b6c70f78dca0a130f7db92fd0613117b11e3bb2859eb8b00dd9bfa1df6a81d8f9660978fdeeee7748974adffc60c8e45a2fd098b28d4b3f5501c8a
7
+ data.tar.gz: a341da5a23a8f8cadaed99e1d1febf57f69a5922c497b8732a1d4eb21651c7c425446a129245a77197428a9ade403f49b97dc5b7d5b77dad15b03ee346416b84
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in dockistrano.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,9 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard :rspec do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+ end
9
+
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Edwin Vlieg
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
@@ -0,0 +1,101 @@
1
+ # Dockistrano
2
+
3
+ Dockistrano uses docker to create a development and testing environment for applications. It is inspired by Capistrano, because booting a container should be as easy as doing a `cap deploy`. The approach has a lot of conventions and as little as configuration as possible.
4
+
5
+ ## Current status
6
+
7
+ Dockistrano is actively used for software development at [MoneyBird](http://www.moneybird.com), but is still very immature. Feel free to play around and provide us feedback. A pull request with tested code is the best way to help us improve the software!
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'dockistrano'
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install dockistrano
22
+
23
+ ## Preparing your application
24
+
25
+ To use Dockistrano for your application, two files should be created:
26
+
27
+ ### `Dockerfile`
28
+
29
+ The `Dockerfile` describes the container that is created for the application and should contain all dependencies. Example:
30
+
31
+ ```
32
+ FROM ubuntu
33
+ RUN apt-get update
34
+ RUN apt-get install mydependency
35
+ ADD ./ ./
36
+ EXPOSE 8000
37
+ CMD ["webserver", "start"]
38
+ ```
39
+
40
+ For more information about writing Dockerfiles, see [the documentation on docker.io](http://docs.docker.io/en/latest/use/builder/).
41
+
42
+ ### `config/dockistrano.yml`
43
+
44
+ The configuration file contains information about the registry, dependencies, ports and environment variables.
45
+
46
+ ```
47
+ ---
48
+ registry: my.privateregistry.com:5000
49
+ dependencies:
50
+ postgresql:
51
+ database: "my_databasename"
52
+ redis:
53
+ environment:
54
+ rails_env: development
55
+ ```
56
+
57
+ ## Usage
58
+
59
+ Dockistrano provides a range of commands to create containers and run commands in containers:
60
+
61
+ ### `doc build`
62
+
63
+ Builds the current container by using the `Dockerfile`. The resulting container will be tagged with the registry name, name of the image and tag of the current git branch.
64
+
65
+ When building is successfull, the container will be tested by running the `test_command` provided in the config. When tests are successfull, the container is pushes to the registry.
66
+
67
+ ### `doc start` and `doc start-services`
68
+
69
+ Starts the container by running the default command. Environment variables from dependencies are automatically added to the run command. Optionally the backing services can be started seperatly with `doc start-services`
70
+
71
+ ### `doc stop` and `doc stop-all`
72
+
73
+ Stops the current container or stops all containers, including the backing services.
74
+
75
+ ### `doc logs [NAME]`
76
+
77
+ Prints logs for the container or for a backing service when a name of the service is provided. When the container is running a `docker attach` is used, otherwise a `docker logs`.
78
+
79
+ ### `doc exec COMMAND`
80
+
81
+ Executes the command in the container, printing the result in the console.
82
+
83
+ ### `doc console [CONSOLE]`
84
+
85
+ Starts a console in the container, by default /bin/bash is started. Optionally a console can be provided, for example `doc console rails console` to start a Rails console.
86
+
87
+ ### `doc ps`
88
+
89
+ Prints all running processes on Docker
90
+
91
+ ### `doc clean`
92
+
93
+ Cleans unused containers and images from Docker
94
+
95
+ ### `doc status`
96
+
97
+ Prints information about the current container and lists environment variables that are provided to the container when started.
98
+
99
+ ### `doc pull`
100
+
101
+ Pulls new versions of containers from the registry
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/doc ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $: << File.expand_path("../../lib/", __FILE__)
4
+
5
+ require "bundler"
6
+ Bundler.setup(:default)
7
+
8
+ require "dockistrano"
9
+ begin
10
+ Dockistrano::Cli.start
11
+ rescue Thor::InvocationError => e
12
+ puts e
13
+ end
data/bin/docker ADDED
Binary file
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'dockistrano/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "dockistrano"
8
+ spec.version = Dockistrano::VERSION
9
+ spec.authors = ["Edwin Vlieg"]
10
+ spec.email = ["edwin@moneybird.com"]
11
+ spec.summary = %q{Manage Docker containers for a development workflow}
12
+ spec.homepage = "http://github.com/moneybird/dockistrano"
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files`.split($/)
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_dependency "thor"
21
+ spec.add_dependency "cocaine"
22
+ spec.add_dependency "multi_json"
23
+ spec.add_dependency "redis"
24
+ spec.add_dependency "dotenv"
25
+
26
+ spec.add_development_dependency "bundler"
27
+ spec.add_development_dependency "rake"
28
+ spec.add_development_dependency "guard"
29
+ spec.add_development_dependency "guard-rspec"
30
+ spec.add_development_dependency "terminal-notifier-guard"
31
+ spec.add_development_dependency "webmock"
32
+ end
@@ -0,0 +1,220 @@
1
+ require 'thor'
2
+ require "dotenv"
3
+ Dotenv.load(".dockistrano")
4
+ ENV["DOCKISTRANO_ENVIRONMENT"] ||= "default"
5
+ ENV["DOCKER_HOST_IP"] ||= "127.0.0.1"
6
+ ENV["DOCKER_BINARY"] ||= begin
7
+ if RUBY_PLATFORM =~ /darwin|mac os/
8
+ bin_dir = File.expand_path(File.dirname(__FILE__) + "/../../bin")
9
+ "#{bin_dir}/docker"
10
+ else
11
+ "docker"
12
+ end
13
+ end
14
+
15
+ module Dockistrano
16
+
17
+ class Cli < ::Thor
18
+
19
+ desc "ps", "List all running containers in docker"
20
+ def ps
21
+ puts Docker.ps
22
+ end
23
+
24
+ desc "setup", "Sets up a host for starting the application"
25
+ method_option "environment", aliases: "-e", default: ENV["DOCKISTRANO_ENVIRONMENT"], type: :string, desc: "Environment to start the container in"
26
+ def setup
27
+ say "Please execute the following command on the host to setup", :green
28
+ say "\tmkdir -p #{current_service.directories_required_on_host.join(" ")}"
29
+ end
30
+
31
+ desc "status", "Status of the application"
32
+ method_option "environment", aliases: "-e", default: ENV["DOCKISTRANO_ENVIRONMENT"], type: :string, desc: "Environment to start the container in"
33
+ def status
34
+ say "DOCKISTRANO_ENVIRONMENT: #{options["environment"]}", :green
35
+ say "DOCKER_HOST_IP: #{ENV['DOCKER_HOST_IP']}", :green
36
+ say "DOCKER_BINARY: #{ENV['DOCKER_BINARY']}", :green
37
+ say ""
38
+ say "Current application", :blue
39
+ say " registry: #{current_service.registry}"
40
+ say " image name: #{current_service.image_name}"
41
+ say " tag: #{current_service.tag}"
42
+ say " volumes:"
43
+ current_service.volumes.each do |volume|
44
+ say " #{volume}"
45
+ end
46
+ say ""
47
+ say "Dependencies", :blue
48
+ current_service.backing_services.each do |name, service|
49
+ say " #{service.full_image_name}"
50
+ end
51
+ say ""
52
+ say "Environment", :blue
53
+ current_service.environment_variables.each do |key, value|
54
+ say " #{key}=#{value}"
55
+ end
56
+ say ""
57
+ say "Hipache", :blue
58
+ Hipache.new(ENV["DOCKER_HOST_IP"]).status.each do |host, ips|
59
+ say " #{host}: #{ips.join(", ")}"
60
+ end
61
+ say ""
62
+ end
63
+
64
+ desc "build", "Build and test a new application container"
65
+ def build
66
+ if current_service.build
67
+ say_status "built", current_service.image_name
68
+ if current_service.test
69
+ say_status "tests passed", current_service.image_name
70
+ current_service.push
71
+ say_status "pushed", current_service.image_name
72
+ else
73
+ say_status "tests failed", current_service.image_name
74
+ exit 1
75
+ end
76
+ else
77
+ say_status "failed", current_service.image_name, :red
78
+ exit 1
79
+ end
80
+ end
81
+
82
+ desc "pull", "Pull new versions of dependencies"
83
+ def pull
84
+ current_service.backing_services.each do |name, service|
85
+ if service.newer_version_available?
86
+ service.pull
87
+ say_status "Pulled", name
88
+ else
89
+ say_status "Uptodate", name, :white
90
+ end
91
+ end
92
+
93
+ if current_service.newer_version_available?
94
+ current_service.pull
95
+ say_status "Pulled", current_service.image_name
96
+ else
97
+ say_status "Uptodate", current_service.image_name, :white
98
+ end
99
+ end
100
+
101
+ desc "push", "Pushes a new version of this container"
102
+ def push
103
+ current_service.push
104
+ end
105
+
106
+ desc "start-services", "Starts the backing services"
107
+ method_option "environment", aliases: "-e", default: ENV["DOCKISTRANO_ENVIRONMENT"], type: :string, desc: "Environment to start the container in"
108
+ def start_services
109
+ current_service.backing_services.each do |name, service|
110
+ if service.running?
111
+ say_status("Running", name, :white)
112
+ else
113
+ service.start
114
+ say_status("Started", name)
115
+ end
116
+ end
117
+ end
118
+
119
+ desc "stop-all", "Stops the backing services"
120
+ def stop_all
121
+ current_service.stop
122
+ say_status("Stopped", current_service.image_name)
123
+ current_service.backing_services.each do |name, service|
124
+ if service.running?
125
+ service.stop
126
+ say_status("Stopped", name)
127
+ end
128
+ end
129
+ end
130
+
131
+ desc "start", "Starts the application"
132
+ method_option "environment", aliases: "-e", default: ENV["DOCKISTRANO_ENVIRONMENT"], type: :string, desc: "Environment to start the container in"
133
+ def start
134
+ if current_service.running?
135
+ say_status("Running", current_service.image_name, :white)
136
+ else
137
+ current_service.start(options)
138
+ say_status("Started", current_service.image_name)
139
+ end
140
+ rescue Dockistrano::Service::EnvironmentVariablesMissing => e
141
+ say e.message, :red
142
+ end
143
+
144
+ desc "stop [ID]", "Stops the application or container with specified ID"
145
+ def stop(id=nil)
146
+ if id
147
+ Docker.stop(id)
148
+ say_status("Stopped", id, :green)
149
+ else
150
+ current_service.stop
151
+ say_status("Stopped", current_service.image_name, :green)
152
+ end
153
+ end
154
+
155
+ desc "restart", "Restarts the application"
156
+ method_option "environment", aliases: "-e", default: ENV["DOCKISTRANO_ENVIRONMENT"], type: :string, desc: "Environment to start the container in"
157
+ def restart
158
+ current_service.stop
159
+ say_status("Stopped", current_service.image_name)
160
+ current_service.start(options)
161
+ say_status("Started", current_service.image_name)
162
+ end
163
+
164
+ desc "exec COMMAND", "Executes a command in the application and returns"
165
+ method_option "environment", aliases: "-e", default: ENV["DOCKISTRANO_ENVIRONMENT"], type: :string, desc: "Environment to start the container in"
166
+ def exec(*command)
167
+ current_service.exec(command.join(" "), options)
168
+ rescue Dockistrano::Service::EnvironmentVariablesMissing => e
169
+ say e.message, :red
170
+ end
171
+
172
+ desc "console [COMMAND]", "Starts an interactive shell in the application"
173
+ method_option "environment", aliases: "-e", default: ENV["DOCKISTRANO_ENVIRONMENT"], type: :string, desc: "Environment to start the container in"
174
+ def console(*command)
175
+ command = ["/bin/bash"] if command.empty?
176
+ current_service.console(command.join(" "), options)
177
+ rescue Dockistrano::Service::EnvironmentVariablesMissing => e
178
+ say e.message, :red
179
+ end
180
+
181
+ desc "clean", "Cleans images and containers from docker"
182
+ def clean
183
+ Docker.clean
184
+ Dockistrano::ServiceDependency.clear_cache
185
+ end
186
+
187
+ desc "logs [NAME]", "Prints the logs for the service"
188
+ def logs(name=nil)
189
+ service = name ? current_service.backing_services[name] : current_service
190
+ if service.running?
191
+ say "Container #{service.image_name} running, attaching to output", :blue
192
+ service.attach
193
+ else
194
+ say "Container #{service.image_name} stopped, printing logs of last run", :blue
195
+ service.logs
196
+ end
197
+ end
198
+
199
+ def method_missing(*args)
200
+ command = args[0]
201
+ if command and current_service.config["aliases"] and current_service.config["aliases"][command.to_s]
202
+ args.shift
203
+ Kernel.exec("doc #{current_service.config["aliases"][command.to_s]} #{args.join(" ")}")
204
+ else
205
+ super
206
+ end
207
+ end
208
+
209
+ private
210
+
211
+ # Returns the current service
212
+ def current_service
213
+ @service ||= Service.factory(Dir.pwd, options["environment"])
214
+ rescue Dockistrano::Service::ConfigurationFileMissing
215
+ say "No configuration file found in current directory. config/dockistrano.yml missing", :red
216
+ exit 1
217
+ end
218
+
219
+ end
220
+ end
@@ -0,0 +1,28 @@
1
+ module Dockistrano
2
+
3
+ class CommandLine
4
+
5
+ def self.command_with_result(command)
6
+ debug(command)
7
+ `#{command}`
8
+ end
9
+
10
+ def self.command_with_stream(command)
11
+ debug(command)
12
+ begin
13
+ Kernel.system(command)
14
+ rescue Interrupt
15
+ end
16
+ end
17
+
18
+ def self.command_with_interaction(command)
19
+ debug(command)
20
+ Kernel.exec(command)
21
+ end
22
+
23
+ def self.debug(command)
24
+ puts "$ #{command}" if false
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,188 @@
1
+ require 'cocaine'
2
+ require 'multi_json'
3
+
4
+ module Dockistrano
5
+
6
+ # Class for communication with Docker. Uses two means of communication:
7
+ #
8
+ # - Actions on containers are executed by calling the docker binary with an
9
+ # ip address of the Docker location. The docker command line client is best
10
+ # capable of executing the actions for building images, running containers and
11
+ # managing containers.
12
+ # - Queries are executed using the HTTP API of docker. This API is exposed via HTTP
13
+ # and returns JSON. This allows us to easily parse and return the information.
14
+ #
15
+ # This class uses two environment variables:
16
+ #
17
+ # DOCKER_BINARY - Location of the docker binary on your system
18
+ # DOCKER_HOST_IP - IP address or host of the docker server
19
+ #
20
+ class Docker
21
+
22
+ class EnvironmentVariableMissing < StandardError
23
+ end
24
+
25
+ # Returns the docker command as a string: 'docker -H 127.0.0.1'
26
+ def self.docker_command
27
+ raise EnvironmentVariableMissing.new("Missing DOCKER_BINARY in environment, please provide the location of the docker binary") unless ENV["DOCKER_BINARY"]
28
+ raise EnvironmentVariableMissing.new("Missing DOCKER_HOST_IP in environment, please provide the host or ip address of the docker server") unless ENV["DOCKER_HOST_IP"]
29
+ "#{ENV['DOCKER_BINARY']} -H #{ENV['DOCKER_HOST_IP']}"
30
+ end
31
+
32
+ # Executes the given command on the command line
33
+ def self.execute(command, mode=:string_result)
34
+ case mode
35
+ when :string_result
36
+ Dockistrano::CommandLine.command_with_result("#{docker_command} #{command.collect { |c| c.kind_of?(String) ? c : arguments(c) }.join(" ")}".strip)
37
+ when :stream
38
+ Dockistrano::CommandLine.command_with_stream("#{docker_command} #{command.collect { |c| c.kind_of?(String) ? c : arguments(c) }.join(" ")}".strip)
39
+ else
40
+ Dockistrano::CommandLine.command_with_interaction("#{docker_command} #{command.collect { |c| c.kind_of?(String) ? c : arguments(c) }.join(" ")}".strip)
41
+ end
42
+ end
43
+
44
+ def self.ps(options={})
45
+ execute(["ps", options])
46
+ end
47
+
48
+ def self.stop(id)
49
+ execute(["stop", id])
50
+ end
51
+
52
+ def self.run(full_image_name, options={})
53
+ if (command = options.delete(:command))
54
+ execute(["run", options, full_image_name, command])
55
+ else
56
+ execute(["run", options, full_image_name])
57
+ end
58
+ end
59
+
60
+ def self.exec(full_image_name, options={})
61
+ if (command = options.delete(:command))
62
+ execute(["run", options, full_image_name, command], :stream)
63
+ else
64
+ execute(["run", options, full_image_name], :stream)
65
+ end
66
+ end
67
+
68
+ def self.console(full_image_name, options={})
69
+ options["t"] = true
70
+ options["i"] = true
71
+ if (command = options.delete(:command))
72
+ execute(["run", options, full_image_name, command], :interaction)
73
+ else
74
+ execute(["run", options, full_image_name], :interaction)
75
+ end
76
+ end
77
+
78
+ def self.build(full_image_name)
79
+ execute(["build", { t: full_image_name }, "."], :stream)
80
+ end
81
+
82
+ def self.pull(full_image_name, tag)
83
+ execute(["pull", { t: tag }, full_image_name])
84
+ end
85
+
86
+ def self.push(image_name, tag)
87
+ execute(["push", image_name, tag], :stream)
88
+ end
89
+
90
+ def self.logs(id)
91
+ execute(["logs", id], :stream)
92
+ end
93
+
94
+ def self.attach(id)
95
+ execute(["attach", id], :stream)
96
+ end
97
+
98
+ def self.clean
99
+ Dockistrano::CommandLine.command_with_stream("#{docker_command} rmi $(#{docker_command} images -a | grep \"^<none>\" | awk '{print $3}')")
100
+ Dockistrano::CommandLine.command_with_stream("#{docker_command} rm $(#{docker_command} ps -a -q)")
101
+ end
102
+
103
+ def self.running_container_id(full_image_name)
104
+ request(["containers", "json"]).each do |container|
105
+ return container["Id"] if container["Image"] == full_image_name
106
+ end
107
+ nil
108
+ end
109
+
110
+ # Returns the id of the last container with an error
111
+ def self.last_run_container_id(full_image_name)
112
+ request(["containers", "json?all=1"]).each do |container|
113
+ return container["Id"] if container["Image"] == full_image_name and container["Command"] != "cat /dockistrano.yml"
114
+ end
115
+ nil
116
+ end
117
+
118
+ def self.image_id(full_image_name)
119
+ inspect_image(full_image_name)["id"]
120
+ end
121
+
122
+ class ImageNotFound < StandardError
123
+ end
124
+
125
+ def self.inspect_image(full_image_name)
126
+ response = request(["images", full_image_name, "json"])
127
+ rescue ResourceNotFound => e
128
+ raise ImageNotFound.new(e.message)
129
+ end
130
+
131
+ def self.inspect_container(id)
132
+ request(["containers", id, "json"])
133
+ end
134
+
135
+ def self.stop_all_containers_from_image(full_image_name)
136
+ containers = request(["containers", "json"])
137
+ containers.each do |container|
138
+ execute(["stop", container["Id"]]) if container["Image"] == full_image_name
139
+ end
140
+ end
141
+
142
+ def self.tags_for_image(image_name)
143
+ images = request(["images", "json"])
144
+ [].tap do |tags|
145
+ images.each do |image|
146
+ tags << image["Tag"] if image["Repository"] == image_name
147
+ end
148
+ end
149
+ end
150
+
151
+ private
152
+
153
+ def self.request(path)
154
+ uri = URI.parse("http://#{ENV['DOCKER_HOST_IP']}:4243/#{path.join("/")}")
155
+ response = Net::HTTP.get_response(uri)
156
+ if response.kind_of?(Net::HTTPNotFound)
157
+ raise ResourceNotFound.new("Could not find #{path.join("/")}: #{response.body}")
158
+ end
159
+
160
+ MultiJson.load(response.body)
161
+ end
162
+
163
+ class ResourceNotFound < StandardError
164
+ end
165
+
166
+ def self.arguments(options)
167
+ options.collect do |k,v|
168
+ case v
169
+ when TrueClass
170
+ "-#{k}"
171
+ when Array
172
+ v.collect { |av| "-#{k} #{av}" }.join(" ").strip
173
+ when Hash
174
+ v.collect { |ak, av|
175
+ if av
176
+ "-#{k} #{ak}='#{av}'"
177
+ else
178
+ ""
179
+ end
180
+ }.join(" ").strip
181
+ else
182
+ "-#{k} #{v}"
183
+ end
184
+ end.join(" ").strip
185
+ end
186
+
187
+ end
188
+ end
@@ -0,0 +1,27 @@
1
+ module Dockistrano
2
+
3
+ class Git
4
+
5
+ def self.repository_name
6
+ git_url = Cocaine::CommandLine.new("git config --get remote.origin.url").run.strip
7
+
8
+ if git_url =~ /^[A-z0-9]+@[A-z0-9.:\-]+\/([A-z0-9\-_\.]+)(\.git)?$/
9
+ $1.gsub(/\.git$/, "")
10
+ elsif git_url =~ /^https?:\/\/[a-z\-\.]+\/[a-z\-\.]+\/([A-z0-9.\-\_]+)$/
11
+ $1
12
+ else
13
+ raise "Unknown git url '#{git_url}'"
14
+ end
15
+ end
16
+
17
+ def self.branch
18
+ if ENV['JANKY_BRANCH']
19
+ ENV['JANKY_BRANCH'].gsub("/", "-")
20
+ else
21
+ branch = Cocaine::CommandLine.new("git rev-parse --abbrev-ref HEAD").run.strip
22
+ branch.gsub("/", "-")
23
+ end
24
+ end
25
+
26
+ end
27
+ end