giddyup-deploy 0.1.0.1.g3db36a2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md ADDED
@@ -0,0 +1,319 @@
1
+ # GIDDYUP!
2
+
3
+ Does deploying your application seem like a bit too much of a chore? Do you
4
+ wish it wasn't so hard? Well, with giddyup it can become just a tiny bit
5
+ easier.
6
+
7
+ If you've ever used, or seen, Heroku's git-based deployment model, you know
8
+ how simple app deployment *can* be. While we don't try to emulate all of
9
+ Heroku's excellent infrastructure, giddyup does handle a small corner of
10
+ that -- the ability to deploy your app with a simple 'git push'.
11
+
12
+
13
+ # Installation
14
+
15
+ At this stage, giddyup is only available directly as a git repo. There are
16
+ no versioned releases, nor are there distribution packages available. My
17
+ recommendation is to create a clone of the [main giddyup
18
+ repo](https://github.com/mpalmer/giddyup) somewhere (I suggest
19
+ `/usr/local/lib/giddyup` if you've got root, and probably `~/giddyup`
20
+ otherwise) and then to update to newer giddyup functionality, just `git
21
+ pull` in that clone. You then have a fixed location to point your hook
22
+ symlinks and inclusions of `functions.sh`.
23
+
24
+ Eventually, when giddyup stabilises there will be distribution packages;
25
+ they'll likely put everything into `/usr/lib/giddyup`, at which time you can
26
+ just delete your git repo at `/usr/local/lib/giddyup` and symlink that
27
+ location `/usr/lib/giddyup`, and nothing else will need to change.
28
+
29
+ Symlink the `giddyup` script to somewhere in your `PATH`; this will allow
30
+ you to trivially setup new deployment destinations. Do **not** *copy* the
31
+ `giddyup` script; it relies on being in the same directory as `update-hook`
32
+ in order to be able to find the hook script.
33
+
34
+
35
+ # Setting up new deployments
36
+
37
+ 1. Run the `giddyup` script, passing it the location of the "root" of your
38
+ new deployment. The script will create the basic directory structure and
39
+ symlink the update hook into the right place.
40
+
41
+ 2. Set the git config variables in the config for the repo you
42
+ created in step 1 (see the section "Configuration" for the
43
+ available variables)
44
+
45
+ > e.g. `git config -f /home/appuser/appname/repo/config giddyup.environment
46
+ > production`
47
+
48
+ 3. Add the necessary hooks to your application's local git repo to effect
49
+ proper deployment.
50
+
51
+ 4. Add the newly created git repo as a remote in your local working copy,
52
+ then push to that remote to make your initial deploy:
53
+
54
+ > e.g. `git remote add deploy appuser@example.com:appname/repo;
55
+ > git push deploy master:master`
56
+
57
+ 5. Configure your webserver to pass requests to the appserver, and test that
58
+ everything is working properly.
59
+
60
+
61
+ # Deployment tree structure
62
+
63
+ Giddyup creates a tree of directories underneath the specified "root"
64
+ directory, that contains (hopefully) everything related to your application.
65
+ The structure is as follows:
66
+
67
+ * `repo`: This is the repository to which git pushes are made, and within
68
+ which the Giddyup hook script is placed. It must be a "bare" git repo
69
+ (because otherwise Git gets ridiculously confused and annoyed) and it is
70
+ the parent of this directory which is considered to be the "root" of the
71
+ entire application. (Note: this doesn't *actually* have to be named
72
+ `repo`; but it's best to keep a consistent convention, for the sake of
73
+ sanity).
74
+
75
+ * `shared`: All of your application's data which should persist between
76
+ releases should be kept in here -- log files (if you want to keep the same
77
+ log files between releases), **definitely** your customers' uploaded assets
78
+ -- all that sort of thing goes in here, and should be symlinked from your
79
+ release. See the section "Shared data", below.
80
+
81
+ * `releases`: This is where each individual release goes. Each deployment
82
+ of your application gets a directory of it's own in here, named after the
83
+ current date/time at the time of deployment, in the format
84
+ `YYYYMMDD-HHMMSS`.
85
+
86
+ * `current`: A symlink to the currently running version of your application.
87
+
88
+
89
+ ## Shared data
90
+
91
+ Anything you need to be shared across deployments should live under the
92
+ `shared` directory, and symlinks placed in your releases to point to the
93
+ relevant data in `shared`. You share things in your deployments by calling
94
+ the `share` helper function within your hook scripts. This function will
95
+ take a relative path (relative to the root of your repo) and create a
96
+ symlink into the same path within `shared`.
97
+
98
+ For example, you might have a hook script that uses Bundler to setup your
99
+ local gems. Since you don't want to have to dick around reinstalling all
100
+ your gems with every release, you want to share that bundle between
101
+ releases. Your hook script might look something like this:
102
+
103
+ . /path/to/giddyup/functions.sh
104
+ share vendor/bundle
105
+ cd "${RELEASE}"
106
+ bundle install --deployment
107
+
108
+ This will create a symlink from `$ROOT/releases/<timestamp>/vendor/bundle`
109
+ to `$ROOT/shared/vendor/bundle`, then run bundle in the new release to
110
+ ensure that all your required gems are available.
111
+
112
+ If a file or directory already exists in `releases/<timestamp>` that is
113
+ supposed to be symlinked, we'll remove it before creating the symlink. The
114
+ directory structure in `shared` will always mirror that of your releases
115
+ when you use `share`; this improves simplicity and comprehensibility.
116
+
117
+ If a symlink destination doesn't exist already within `shared`, then we'll
118
+ copy the source content in your release (if it exists) into `shared`.
119
+ Leading directory components will be created in `shared` automatically, as
120
+ well.
121
+
122
+
123
+ # Hooks
124
+
125
+ Because everyone's application is slightly different, Giddyup itself doesn't
126
+ actually do very much itself -- instead, it delegates a lot of things back
127
+ to code that you write, as hooks. To understand hooks, it's best to
128
+ understand how Giddyup works:
129
+
130
+ 1. You push your updated code to the repo controlled by giddyup
131
+ 2. The `update` hook, provided by Giddyup, starts to run
132
+ 3. Giddyup makes a copy of the code in your deployment repo
133
+ 4. Giddyup runs the `stop` hook
134
+ 5. Giddyup changes the "current" symlink for the deployment to point at the
135
+ newly configured code
136
+ 6. Giddyup runs the `start` hook
137
+ 7. Old releases are tidied up, if required (see the `giddyup.keepreleases`
138
+ config variable)
139
+
140
+ To put it another way, the following hooks are available:
141
+
142
+ * **stop**: Run after the new release is in place, but while the system's
143
+ idea of the "current" release still points to the currently running code.
144
+ You'll probably want to do whatever's required to stop your appserver from
145
+ running in this hook, run bundler, and perhaps put up a maintenance page.
146
+
147
+ * **start**: Run after the "current" symlink has been changed to point to
148
+ the new code. In here you'd probably want to do database migrations,
149
+ start your appserver, and take down your maintenance page.
150
+
151
+
152
+ ## Running hooks
153
+
154
+ Giddyup always runs hooks from the **newly deployed** version of your
155
+ codebase (so that if you happened to push a deploy with a dodgy `stop` hook,
156
+ you can recover from it by pushing a fixed version). It looks for hooks in
157
+ the location specified by the `giddyup.hookdir` git config variable (see
158
+ "Configuration", below).
159
+
160
+ It is looking for files or directories that match the name of the hook
161
+ (`start`, `stop`, etc). If there is a file named after the hook, and it is
162
+ executable, then that file is executed. If, on the other hand, there is a
163
+ directory named after the hook, then all executable files in that directory
164
+ are executed, in the lexical order of the names of the files.
165
+
166
+ If you really, *really* need to be able to run hook files that aren't
167
+ already executable (I don't know why; git stores executable bits just fine)
168
+ then you can set the `autochmodhooks` config variable; this will have the
169
+ effect of making *every* file in your hook directories (if you're using
170
+ them) a hook script. So don't combine this config option with a `README`...
171
+
172
+ Each hook script is run as a separate process, and as such cannot effect the
173
+ environment or working directory of giddyup itself or any other hook script.
174
+
175
+
176
+ ## Hook environment
177
+
178
+ The environment of the hook is very minimal; only the following environment
179
+ variables will be set:
180
+
181
+ * `PATH` -- the same as the path of the Giddyup script itself.
182
+ * `APP_ENV` -- the environment specified by `giddyup.environment` (see
183
+ "Configuration", below).
184
+ * `ROOT` -- the directory that is the "root" of the entire deployment; the
185
+ directory which the deployment git repository (and everything else) lives
186
+ * `RELEASE` -- the canonical directory that contains the data in this
187
+ release of the application.
188
+ * `NEWREV` -- The SHA of the commit being deployed.
189
+ * `OLDREV` -- The SHA of the commit being replaced.
190
+
191
+ The working directory of all hooks is the root of the deployment tree.
192
+ During the 'stop' hook, the `current` symlink will point to the previous
193
+ running release of the application, while during the `start` hook the
194
+ `current` symlink will point to the new release you're currently deploying.
195
+
196
+
197
+ ## Hook script helpers
198
+
199
+ To help you make your hook scripts easier to write, there are some shell
200
+ functions available to help you on your way. To use them, merely add:
201
+
202
+ . /path/to/giddyup/functions.sh
203
+
204
+ at the top of your hook script, and then call away to your heart's content.
205
+
206
+
207
+
208
+ # Error handling
209
+
210
+ At present, the error handling in giddyup is a bit primitive. In general,
211
+ if any part of the giddyup script fails, the whole process will abort. This
212
+ *mostly* won't hurt your running app, because giddyup does as much as it can
213
+ before starting or stopping anything. However, if your hook scripts bomb
214
+ out, or one of the things that runs between stopping and starting the app
215
+ fails, then things could be left in a bit of a limbo state.
216
+
217
+ Improvements to this part of the system, particularly around intelligent
218
+ rollback strategies, is planned. Patches welcome.
219
+
220
+
221
+ # Configuration
222
+
223
+ Gidduyp is controlled by git repository configuration variables; these can
224
+ either be set using `git config` in the deployment repository, or by
225
+ directly editing the `config` file in the deployment repository.
226
+
227
+ Available configuration variables are given below.
228
+
229
+
230
+ ## `giddyup.environment`
231
+
232
+ (**OPTIONAL**; default: none)
233
+
234
+ Many web application frameworks have a concept of "environments"; that is,
235
+ different sets of configuration parameters (like database credentials) that
236
+ vary between instances of the application running with the same source code.
237
+
238
+ Giddyup supports this paradigm with the use of the `giddyup.environment`
239
+ variable. If set, the value will be passed through to the `APP_ENV`
240
+ environment variable in your hook scripts; this allows you to do slightly
241
+ different things (like reload a different appserver, or assist in running
242
+ your database migrations against a different database). Since this
243
+ environment is specified per-repository, you can run several environments
244
+ (say, uat, staging, and production) with the same in-repo deployment hooks,
245
+ just by pushing to different remote repositories (which have different
246
+ settings for `giddyup.environment`, of course).
247
+
248
+
249
+ ## `giddyup.hookdir`
250
+
251
+ (**OPTIONAL**; default `config/hooks`)
252
+
253
+ The directory within which Giddyup will look to find hook scripts, relative
254
+ to the root of your working copy. The intent is that the hooks relating to
255
+ the deployment of your application are best kept *with* your application.
256
+
257
+
258
+ ## `giddyup.keepreleases`
259
+
260
+ (**OPTIONAL**; default: 5)
261
+
262
+ Giddyup will keep a few older deployed releases available, in case you need
263
+ to make an emergency rollback, or examine exactly what was recently
264
+ deployed. This configuration parameter determines how many releases to
265
+ keep. Set this to 1 to not keep any previous releases, and set it to 0 if
266
+ you want to keep every release ever (not a good idea unless you've got one
267
+ of those fancy new infinite-capacity hard drives).
268
+
269
+
270
+ ## `giddyup.debug`
271
+
272
+ (**OPTIONAL**; default: false)
273
+
274
+ If you want to have far, far too much gory detail about what giddyup is
275
+ doing, you can set this to true.
276
+
277
+
278
+ ## `giddyup.autochmodhooks`
279
+
280
+ (**OPTIONAL**; default: false)
281
+
282
+ Sometimes the hook files found in `giddyup.hookdir` (e.g. in
283
+ `config/hooks/`) are not marked as executable in the repository. Setting
284
+ `giddyup.autochmodhooks` to `true` will cause giddyup to set the executable
285
+ bit itself before running the hook script.
286
+
287
+
288
+ # Frequently Answered Questions
289
+
290
+ ## What? Is that all?
291
+
292
+ Yes, the main giddyup update hook script is quite minimal. The aim here is
293
+ to provide the necessary scaffolding upon which customised deployment
294
+ processes can be implemented.
295
+
296
+ To put it another way: yes, Giddyup is small and dumb. You're supposed to
297
+ provide the smarts in hooks. If you have a general-purpose hook you'd like
298
+ to share with others, please feel free to [send it to
299
+ me](mailto:theshed+giddyup@hezmatt.org) and I'll include it in the examples.
300
+
301
+
302
+ ## I need moah hooks!
303
+
304
+ Weeeeell... maybe you do, maybe you don't. Remember that you can execute as
305
+ many different hook scripts as you like, and in the order you specify. For
306
+ example, you might think you need a "pre-stop" hook, to do things that might
307
+ take a while, before the application is stopped (to minimise downtime). You
308
+ don't actually need a separate hook in giddyup; all you need to do is move
309
+ the hook script that actually stops your appservers to after the hook script
310
+ that does whatever time-consuming task you have in mind.
311
+
312
+
313
+ ## What's with the name?!?
314
+
315
+ Take the phrase "git deployment", chop off most of "deployment" to produce
316
+ something like "gitde", say it as a single word a few times until the 't'
317
+ kinda disappears and the 'e' lengthens so it sounds like "gidee", then shout
318
+ "yeehaw!" like an old time cowboy. Waving of Stetsons is optional, but
319
+ strongly encouraged.
data/bin/git-deploy ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'giddyup/git_deploy'
4
+
5
+ Giddyup::GitDeploy.new(ARGV).run
@@ -0,0 +1,104 @@
1
+ # Wrap all calls to external commands in appropriate class methods.
2
+ #
3
+ # Honestly, this is primarily for ease of testing, but I suppose if someone
4
+ # wanted to go hog-wild and replace this for some crazy use of GitDeploy, it
5
+ # might come in handy.
6
+ #
7
+ require 'open3'
8
+
9
+ module Giddyup; end
10
+
11
+ class Giddyup::CommandWrapper
12
+ class CommandFailed < StandardError
13
+ attr_reader :status, :output, :command
14
+
15
+ def initialize(status, output, command)
16
+ @status = status
17
+ @output = output
18
+ @command = command
19
+ super("Command execution failed")
20
+ end
21
+ end
22
+
23
+ # Run a command and return the output of the command as an array
24
+ # containing elements of the following form:
25
+ #
26
+ # [<stream>, <line>]
27
+ #
28
+ # Where <stream> is either :stdout or :stderr (to indicate which output
29
+ # stream the line came from), and <line> is the line of text (sans
30
+ # trailing newline) that was output.
31
+ #
32
+ # These lines of text are interspersed in the order they were received,
33
+ # to ensure the best possible match-up of errors to regular output.
34
+ #
35
+ # If the command returns a non-zero exit code, a
36
+ # Giddyup::CommandWrapper::CommandFailed exception is raised containing
37
+ # the status of the failed command and all output.
38
+ #
39
+ def self.run_command(cmd)
40
+ output = []
41
+ rv = nil
42
+
43
+ Open3.popen3(cmd) do |stdin, stdout, stderr, thr|
44
+ # We don't want to talk *to* you
45
+ stdin.close
46
+
47
+ fds = [stdout, stderr]
48
+
49
+ until fds.empty?
50
+ a = IO.select(fds, [], [], 0.1)
51
+
52
+ if a.nil?
53
+ if fds == [stderr]
54
+ # SSH is an annoying sack of crap. When using ControlMaster
55
+ # (multiplexing), the background mux process keeps stderr open,
56
+ # which means that this loop never ends (because stderr.eof?
57
+ # is never true). So, we're using the heuristic that if stdout
58
+ # is closed (ie it hsa been removed from `fds`) and we've timed
59
+ # out rather than seeing any further activity on stderr) then
60
+ # the command execution is done.
61
+ break
62
+ else
63
+ # We had a timeout, but we're still running!
64
+ next
65
+ end
66
+ end
67
+
68
+ if a[0].include? stdout
69
+ if stdout.eof?
70
+ stdout.close
71
+ fds.delete(stdout)
72
+ else
73
+ output << [:stdout, stdout.readline.chomp]
74
+ end
75
+ end
76
+
77
+ if a[0].include? stderr
78
+ if stderr.eof?
79
+ stderr.close
80
+ fds.delete(stderr)
81
+ else
82
+ output << [:stderr, stderr.readline.chomp]
83
+ end
84
+ end
85
+ end
86
+
87
+ rv = thr.value
88
+ end
89
+
90
+ if rv.exitstatus != 0
91
+ raise CommandFailed.new(rv, output, cmd)
92
+ end
93
+
94
+ output
95
+ end
96
+
97
+ # Run a command, but only return stdout as a string
98
+ def self.run_command_stdout(cmd)
99
+ run_command(cmd).
100
+ select { |l| l[0] == :stdout }.
101
+ map { |l| l[1] }.
102
+ join("\n")
103
+ end
104
+ end
@@ -0,0 +1,202 @@
1
+ # Class implementing the git-deploy command line.
2
+ #
3
+ # Create an instance with an array of command line parameters, and call `#run`.
4
+ #
5
+
6
+ require 'open3'
7
+ require 'terminal-display-colors'
8
+ require 'giddyup/command_wrapper'
9
+
10
+ module Giddyup; end
11
+
12
+ class Giddyup::GitDeploy
13
+ attr_reader :env, :verbose
14
+
15
+ def initialize(argv, opts = {})
16
+ argv = argv.dup
17
+ @verbose = false
18
+
19
+ if argv.index('-v')
20
+ @verbose = true
21
+ argv.delete('-v')
22
+ end
23
+
24
+ @stdout = opts[:stdout] || $stdout
25
+ @stderr = opts[:stderr] || $stderr
26
+ @command = opts[:command_wrapper] || Giddyup::CommandWrapper
27
+
28
+ begin
29
+ @base_dir = @command.run_command_stdout("git rev-parse --show-toplevel")
30
+ rescue Giddyup::CommandWrapper::CommandFailed => e
31
+ raise RuntimeError,
32
+ "ERROR: Not in a git tree"
33
+ end
34
+
35
+ @config_file = File.join(@base_dir, '.git-deploy')
36
+
37
+ @env = argv.shift
38
+
39
+ unless argv.empty?
40
+ raise RuntimeError,
41
+ "Unknown config parameter(s): #{argv.inspect}"
42
+ end
43
+
44
+ end
45
+
46
+ def run
47
+ start_time = Time.now
48
+
49
+ if @env.nil? or @env.empty?
50
+ raise RuntimeError,
51
+ "No environment given to deploy to"
52
+ end
53
+
54
+ @stdout.puts "Deploying to environment '#{@env}'"
55
+
56
+ do_push
57
+ do_trigger
58
+
59
+ @stdout.printf "Completed deployment in %.2f seconds.\n", (Time.now - start_time).to_f
60
+ end
61
+
62
+ private
63
+ def do_push
64
+ config_item("pushTo", true).each do |t|
65
+ run_command "Pushing HEAD to remote '#{t}'",
66
+ "git push '#{t}' HEAD:master"
67
+ end
68
+ end
69
+
70
+ def do_trigger
71
+ targets.each do |t|
72
+ ssh, dir = t.split(':', 2)
73
+ run_command "Triggering deployment for '#{t}'",
74
+ "ssh #{ssh} /usr/local/lib/giddyup/trigger-deploy " +
75
+ "#{@verbose ? '-v ' : ''}#{dir}"
76
+ end
77
+ end
78
+
79
+ # From a list of `target` / `target-enumerator` configuration parameters
80
+ # (in the form returned from `config_list`), return a one-dimensional
81
+ # array of strings containing the names of all machines to deploy to.
82
+ #
83
+ def targets
84
+ @targets ||= enumerate_targets
85
+ end
86
+
87
+ def enumerate_targets
88
+ config_list('target(Enumerator)?$').map do |li|
89
+ if li[0].downcase == 'target'
90
+ li[1]
91
+ elsif li[0].downcase == 'targetenumerator'
92
+ rv = run_command("Enumerating targets using '#{li[1]}'", li[1]).split(/\s+/)
93
+ if $?.exitstatus != 0
94
+ raise RuntimeError,
95
+ "Target enumeration failed. Aborting."
96
+ end
97
+ rv
98
+ else
99
+ raise RuntimeError,
100
+ "Unknown target expansion option: #{li[0]}"
101
+ end
102
+ end.flatten
103
+ end
104
+
105
+ # Get the value(s) of a configuration item from the config file.
106
+ #
107
+ # Reads the `.git-deploy` config file, and retrieves the value of the
108
+ # config `item`. If you pass `true` to `multi`, then the configuration
109
+ # parameter is read as a "multi-value" parameter, and the list of
110
+ # configuration parameters is returned as an array; otherwise, we will
111
+ # get a single value, returned as a string, and if multiple values are
112
+ # returned, a RuntimeError will be raised.
113
+ #
114
+ def config_item(item, multi = false)
115
+ cmd = "git config -f #{@config_file} " +
116
+ "#{multi ? '--get-all' : '--get'} " +
117
+ "'environment.#{@env}.#{item}'"
118
+
119
+ begin
120
+ s = @command.run_command_stdout(cmd)
121
+ rescue Giddyup::CommandWrapper::CommandFailed => e
122
+ if e.status.exitstatus == 2 and !multi
123
+ raise RuntimeError,
124
+ "Multiple values found for a single-value parameter environment.#{@env}.#{item}"
125
+ elsif e.status.exitstatus == 1
126
+ # This means "nothing found", which we're cool with
127
+ return multi ? [] : ""
128
+ else
129
+ raise RuntimeError,
130
+ "Failed to get config parameter environment.#{@env}.#{item}"
131
+ end
132
+ end
133
+
134
+ if multi
135
+ s.split(/\s+/)
136
+ else
137
+ s
138
+ end
139
+ end
140
+
141
+ # Retrieve the in-order list of configuration parameters whose
142
+ # item name matches the anchored `regex`.
143
+ #
144
+ # This function returns an array of the form `[[name, value], [name,
145
+ # value], ...]`.
146
+ #
147
+ def config_list(regex)
148
+ cmd = "git config -f #{@config_file} " +
149
+ "--get-regexp 'environment.#{@env}.#{regex}'"
150
+
151
+ begin
152
+ @command.
153
+ run_command_stdout(cmd).
154
+ split("\n").
155
+ map { |l| l.split(/\s+/, 2) }.
156
+ map { |item| [item[0].gsub("environment.#{@env}.", ''), item[1]] }
157
+ rescue Giddyup::CommandWrapper::CommandFailed => e
158
+ if e.status.exitstatus == 1
159
+ # "Nothing found", OK then
160
+ return []
161
+ else
162
+ raise RuntimeError,
163
+ "Failed to get config list environment.#{@env}.#{regex}"
164
+ end
165
+ end
166
+ end
167
+
168
+ # Run a command, with all sorts of prettyness.
169
+ #
170
+ # This function will run the specified command, telling the user what is
171
+ # happening. If the command succeeds (that is, exits with a status of
172
+ # 0), then a green "OK" will be printed, otherwise a red "FAILED" will be
173
+ # printed. If the command fails, or @verbose is set, then the output of
174
+ # the command will be displayed after the status line, with `stdout: ` or
175
+ # `stderr: ` prefixed to each line, as appropriate.
176
+ #
177
+ # Regardless of what happens, the stdout of the command will be returned
178
+ # as a string.
179
+ #
180
+ def run_command(desc, cmdline)
181
+ @stdout.print "[....] #{desc}"
182
+
183
+ output = nil
184
+ failed = false
185
+ begin
186
+ output = @command.run_command(cmdline)
187
+ rescue Giddyup::CommandWrapper::CommandFailed => e
188
+ @stdout.puts "\x0d["+"FAIL".red+"] #{desc}"
189
+ output = e.output
190
+ failed = true
191
+ else
192
+ @stdout.puts "\x0d["+" OK ".green+"] #{desc}"
193
+ end
194
+
195
+ if @verbose or failed
196
+ @stdout.puts cmdline
197
+ @stdout.puts output.map { |l| "#{l[0].to_s.send(l[0] == :stderr ? :cyan : :purple)}: #{l[1]}" }.join("\n")
198
+ end
199
+
200
+ return output.select { |l| l[0] == :stdout }.map { |l| l[1] }.join("\n")
201
+ end
202
+ end
metadata ADDED
@@ -0,0 +1,197 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: giddyup-deploy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0.1.g3db36a2
5
+ prerelease: 8
6
+ platform: ruby
7
+ authors:
8
+ - Matt Palmer
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-09-12 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: git-version-bump
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: terminal-display-colors
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: bundler
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: plymouth
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: pry
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: rake
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: rdoc
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: rspec
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ - !ruby/object:Gem::Dependency
143
+ name: spork
144
+ requirement: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ! '>='
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ type: :development
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ description:
159
+ email:
160
+ executables:
161
+ - git-deploy
162
+ extensions: []
163
+ extra_rdoc_files:
164
+ - README.md
165
+ files:
166
+ - bin/git-deploy
167
+ - lib/giddyup/git_deploy.rb
168
+ - lib/giddyup/command_wrapper.rb
169
+ - README.md
170
+ homepage: http://theshed.hezmatt.org/giddyup
171
+ licenses: []
172
+ post_install_message:
173
+ rdoc_options: []
174
+ require_paths:
175
+ - lib
176
+ required_ruby_version: !ruby/object:Gem::Requirement
177
+ none: false
178
+ requirements:
179
+ - - ! '>='
180
+ - !ruby/object:Gem::Version
181
+ version: '0'
182
+ segments:
183
+ - 0
184
+ hash: -3314317262415006446
185
+ required_rubygems_version: !ruby/object:Gem::Requirement
186
+ none: false
187
+ requirements:
188
+ - - ! '>'
189
+ - !ruby/object:Gem::Version
190
+ version: 1.3.1
191
+ requirements: []
192
+ rubyforge_project:
193
+ rubygems_version: 1.8.23
194
+ signing_key:
195
+ specification_version: 3
196
+ summary: ! '''git-deploy'' command to interact with giddyup-managed deployments'
197
+ test_files: []