giddyup-deploy 0.1.0.1.g3db36a2

Sign up to get free protection for your applications and to get access to all the features.
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: []