egads 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YTY5Zjg1MjIxZjgxYmMxN2QwZTBmYjdkYjUwMjBlMjAwNzRlN2EzMg==
5
+ data.tar.gz: !binary |-
6
+ MDYyMmIzODJjMzNjY2FiMWRjNzA2OTk0MjA2ZWVmYmJlY2Q5ZjQxMw==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ NjZjYWJlN2I2M2QyYzIyYzc5MjI4OTY2MWNjZjBkOTJlMDJmODAxYmQ5NTAw
10
+ YmYxYmE0OTFjZDcwZDgxYTY2YmM3ZjRkZDk4ZWJjZjBhNTM2ODY1MDhkOTcx
11
+ MTNmNTdhYmE3ZDExNGQ3MDdhYTk1MGYxODZkMWE3OTcwYTliZjk=
12
+ data.tar.gz: !binary |-
13
+ Mzg5ZDM3MjUxMzk5ZGZhYzc5ZGVjOWM1OWIxNWFkZGUzZTdkZmYxZDZjYmQz
14
+ NDU2YjhlMDkwYmEyODdhN2EzMTNmYmVhZTRmMjI5YWYxOGU3OWFiM2VkZjEz
15
+ Mjg5ZTlmZjVhZDdkZjU1MjlhODFjMzI3ODE0YzI5Mjc0YzE1ZWI=
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ coverage
6
+ InstalledFiles
7
+ lib/bundler/man
8
+ pkg
9
+ rdoc
10
+ spec/reports
11
+ test/tmp
12
+ test/version_tmp
13
+ tmp
14
+
15
+ # YARD artifacts
16
+ .yardoc
17
+ _yardoc
18
+ doc/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
3
+
4
+ gem 'debugger', require: nil
@@ -0,0 +1,49 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ egads (0.0.1)
5
+ fog
6
+ thor
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ builder (3.2.2)
12
+ columnize (0.3.6)
13
+ debugger (1.5.0)
14
+ columnize (>= 0.3.1)
15
+ debugger-linecache (~> 1.2.0)
16
+ debugger-ruby_core_source (~> 1.2.0)
17
+ debugger-linecache (1.2.0)
18
+ debugger-ruby_core_source (1.2.0)
19
+ excon (0.22.1)
20
+ fog (1.11.1)
21
+ builder
22
+ excon (~> 0.20)
23
+ formatador (~> 0.2.0)
24
+ json (~> 1.7)
25
+ mime-types
26
+ net-scp (~> 1.1)
27
+ net-ssh (>= 2.1.3)
28
+ nokogiri (~> 1.5.0)
29
+ ruby-hmac
30
+ formatador (0.2.4)
31
+ json (1.8.0)
32
+ mime-types (1.23)
33
+ minitest (5.0.3)
34
+ net-scp (1.1.1)
35
+ net-ssh (>= 2.6.5)
36
+ net-ssh (2.6.7)
37
+ nokogiri (1.5.9)
38
+ rake (10.0.4)
39
+ ruby-hmac (0.4.0)
40
+ thor (0.18.1)
41
+
42
+ PLATFORMS
43
+ ruby
44
+
45
+ DEPENDENCIES
46
+ debugger
47
+ egads!
48
+ minitest
49
+ rake
@@ -0,0 +1,13 @@
1
+ # egads: Extensible Git-Archive Deploy Strategy
2
+
3
+ ## Installation
4
+
5
+ For local work (building and uploading tarballs), put `egads` in your Gemfile:
6
+
7
+ # In Gemfile
8
+ gem 'egads'
9
+
10
+ On remote machines (to which you deploy), `egads` must be in your PATH.
11
+ So install `egads` as a system gem:
12
+
13
+ sudo gem install egads
@@ -0,0 +1,9 @@
1
+ require "rubygems"
2
+ require "bundler/setup"
3
+ require 'rake/testtask'
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.pattern = "spec/*_spec.rb"
7
+ end
8
+
9
+ task :default => :test
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ # TODO: remove this
3
+ $: << './lib'
4
+ require 'egads'
5
+ Egads::CLI.start
@@ -0,0 +1,28 @@
1
+ $:.unshift 'lib'
2
+ require 'egads/version'
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "egads"
6
+ s.version = Egads::VERSION
7
+ s.summary = "Extensible Git Archive Deploy Strategy"
8
+ s.homepage = "https://github.com/kickstarter/egads"
9
+ s.email = ["aaron@ktheory.com"]
10
+ s.authors = ["Aaron Suggs"]
11
+
12
+ s.files = `git ls-files`.split($/)
13
+ s.executables = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
14
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
15
+ s.require_paths = ["lib"]
16
+
17
+ s.extra_rdoc_files = [ "README.md" ]
18
+ s.rdoc_options = ["--charset=UTF-8"]
19
+
20
+ s.add_dependency "fog"
21
+ s.add_dependency "thor"
22
+ s.add_development_dependency "rake"
23
+ s.add_development_dependency "minitest"
24
+
25
+ s.description = %s{
26
+ A collection of scripts for making a deployable tarball of a git commit,
27
+ uploading it to Amazon S3, and downloading it to your servers.}
28
+ end
@@ -0,0 +1,32 @@
1
+ s3:
2
+ bucket: my-bucket
3
+ access_key: mykey
4
+ secret_key: mysecret
5
+ prefix: my_project # Optional prefix for S3 paths
6
+
7
+ build:
8
+ # Additional paths to include in the built tarball
9
+ extra_paths:
10
+ - public/assets
11
+ - public/stylesheets
12
+
13
+ before:
14
+ - script/egads/before-build
15
+ after:
16
+ - script/egads/after-build
17
+
18
+ upload:
19
+ before:
20
+ - script/egads/before-upload
21
+ after:
22
+ - script/egads/after-upload
23
+
24
+ stage:
25
+ # No before-stage hook
26
+ after:
27
+ - script/egads/after-stage
28
+ release:
29
+ before:
30
+ - script/egads/before-release
31
+ after:
32
+ - script/egads/after-release
@@ -0,0 +1,18 @@
1
+ s3:
2
+ bucket: my-bucket
3
+ access_key: mykey
4
+ secret_key: mysecret
5
+ prefix: my_project # Optional prefix for S3 paths
6
+
7
+ # Path where tarballs are extracted
8
+ extract_to: /var/apps/my_project/releases
9
+ release_to: /var/apps/my_project/current
10
+
11
+ restart_command: /etc/init.d/rails_services restart
12
+
13
+ # environment variables to set before executing commands
14
+ env:
15
+ RAILS_ENV: production
16
+ SHARED_PATH: /var/apps/my_project/shared
17
+ BUNDLE_PATH: /var/apps/my_project/shared/bundle
18
+ BUNDLE_WITHOUT: development:test
@@ -0,0 +1,7 @@
1
+ require 'yaml'
2
+ require 'fog'
3
+ require 'thor'
4
+ require 'egads/config'
5
+ require 'egads/s3_tarball'
6
+ require 'egads/cli'
7
+ require 'egads/ext/thor_actions'
@@ -0,0 +1,28 @@
1
+ # Capistrano configuration.
2
+ # Use `load 'egads/capistrano'` instead of `load 'deploy'` in your Capfile
3
+ Capistrano::Configuration.instance.load do
4
+ namespace :deploy do
5
+ desc "Deploy"
6
+ task :default do
7
+ deploy.upload
8
+ deploy.stage
9
+ deploy.release
10
+ end
11
+
12
+ desc "Prepares for release by bundling gems, symlinking shared files, etc"
13
+ task :stage do
14
+ run "egads stage #{sha}"
15
+ end
16
+
17
+ desc "Runs the release script to symlink a staged deploy and restarts services"
18
+ task :release do
19
+ run "egads release #{sha}"
20
+ end
21
+
22
+ desc "Checks that a deployable tarball is on S3; creates it if missing"
23
+ task :upload do
24
+ `bundle exec egads build`
25
+ abort "Failed to upload build" if $?.exitstatus != 0
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,190 @@
1
+ module Egads
2
+ class CLI < Thor
3
+ include Thor::Actions
4
+ ##
5
+ # Local commands
6
+
7
+ desc "build", "[local] Compiles a deployable tarball of the current commit and uploads it to S3"
8
+ method_option :force, type: :boolean, aliases: '-f', default: false, banner: "Build and overwrite existing tarball on S3"
9
+ method_option 'no-upload', type: :boolean, default: false, banner: "Don't upload the tarball to S3"
10
+ def build(rev='HEAD')
11
+ sha = run_or_die("git rev-parse --verify #{rev}", capture: true).strip
12
+ tarball = S3Tarball.new(sha)
13
+ if !options[:force] && tarball.exists?
14
+ say "Tarball for #{sha} already exists. Pass --force to rebuild."
15
+ return
16
+ end
17
+
18
+ say "Building tarball for #{sha}..."
19
+ # Check if we're on sha, if not, ask to check it out
20
+ head = run_or_die("git rev-parse --verify HEAD", capture: true).strip
21
+ unless head == sha
22
+ say "** Error **"
23
+ say "Trying to build #{sha[0,7]}, but #{head[0,7]} is checked out."
24
+ say "Run `git checkout #{head[0,7]}` and try again."
25
+ exit 1
26
+ end
27
+
28
+ # Ensure clean working directory
29
+ unless run("git status -s", capture: true).empty?
30
+ say "** Error **"
31
+ say "Working directory is not clean."
32
+ say "Stash your changes with `git add . && git stash` and try again."
33
+ exit 1
34
+ end
35
+
36
+ # Make git archive
37
+ FileUtils.mkdir_p(File.dirname(tarball.local_tar_path))
38
+ run_or_die "git archive #{sha} --format=tar > #{tarball.local_tar_path}"
39
+
40
+ # Write REVISION and add to tarball
41
+ File.open('REVISION', 'w') {|f| f << sha + "\n" }
42
+ run_or_die "tar -uf #{tarball.local_tar_path} REVISION"
43
+
44
+ run_hooks_for(:build, :post)
45
+
46
+ extra_paths = Config.build_extra_paths
47
+ if extra_paths.any?
48
+ run_or_die "tar -uf #{tarball.local_tar_path} #{extra_paths * " "}"
49
+ end
50
+
51
+ run_or_die "gzip -9f #{tarball.local_tar_path}"
52
+
53
+ invoke(:upload, [sha], force: options[:force]) unless options['no-upload']
54
+ end
55
+
56
+ method_option :force, type: :boolean, aliases: '-f', default: false, banner: "Overwrite existing tarball on S3"
57
+ desc "upload SHA", "[local, plumbing] Uploads a tarball for SHA to S3"
58
+ def upload(sha)
59
+ tarball = S3Tarball.new(sha)
60
+ if !options[:force] && tarball.exists?
61
+ say "Tarball for #{sha} already exists. Pass --force to upload again."
62
+ end
63
+
64
+ path = tarball.local_gzipped_path
65
+ size = File.size(path)
66
+
67
+ say "Uploading tarball (%.1f MB)" % (size.to_f / 2**20)
68
+ duration = Benchmark.realtime do
69
+ tarball.upload(path)
70
+ end
71
+ say "Uploaded in %.1f seconds (%.1f KB/s)" % [duration, (size.to_f / 2**10) / duration]
72
+
73
+ File.delete(path)
74
+ end
75
+
76
+ ##
77
+ # Remote commands
78
+
79
+ desc "extract SHA", "[remote, plumbing] Downloads tarball for SHA from S3 and extracts it to the filesystem"
80
+ method_option :force, type: :boolean, default: false, banner: "Overwrite existing files"
81
+ def extract(sha)
82
+ dir = RemoteConfig.release_dir
83
+ path = File.join(dir, "#{sha}.tar.gz")
84
+ tarball = S3Tarball.new(sha, true)
85
+
86
+ inside dir do
87
+ if options[:force] || !File.exists?(path)
88
+ say "Downloading tarball"
89
+ duration = Benchmark.realtime do
90
+ File.open(path, 'w') {|f| f << tarball.body }
91
+ end
92
+ size = File.size(path)
93
+ say "Downloaded in %.1f seconds (%.1f KB/s)" % [duration, (size.to_f / 2**10) / duration]
94
+ else
95
+ say "Tarball already downloads. Use --force to overwrite"
96
+ end
97
+
98
+ # Check revision file to see if tarball is already extracted
99
+ extract_flag_path File.join(dir, '.egads-extract-success')
100
+ if options[:force] || !File.exists?(extract_flag_path)
101
+ say "Extracting tarball"
102
+ run_or_die "tar -zxf #{path}"
103
+ else
104
+ say "Tarball already extracted. Use --force to overwrite"
105
+ end
106
+ FileUtils.touch(extract_flag_path)
107
+ end
108
+ end
109
+
110
+ desc "stage SHA", "[remote] Readies SHA for release. If needed, generates URL for SHA and extracts"
111
+ method_option :force, type: :boolean, default: false, banner: "Overwrite existing files"
112
+ def stage(sha)
113
+ invoke(:extract, [sha], options)
114
+ dir = RemoteConfig.release_dir
115
+ stage_flag_path = File.join(dir, '.egads-stage-success')
116
+ if options[:force] || !File.exists?(stage_flag_path)
117
+ inside dir do
118
+ run_hooks_for(:stage, :before)
119
+
120
+ run_or_die("bundle install --deployment --quiet") if File.readable?("GEMFILE")
121
+ if ENV['SHARED_PATH']
122
+ symlinked_dirs = %w(public/system tmp/pids log)
123
+ symlinked_dirs.each do |d|
124
+ source = File.join(ENV['SHARED_PATH'], d)
125
+ destination = File.join(dir, d)
126
+ symlink_directory(source, destination)
127
+ end
128
+ end
129
+
130
+ run_hooks_for(:stage, :after)
131
+ end
132
+ else
133
+ say "Already staged. Use --force to overwrite"
134
+ end
135
+ FileUtils.touch(stage_flag_path)
136
+ end
137
+
138
+ desc "release SHA", "[remote] Symlinks SHA to current and restarts services. If needed, stages SHA"
139
+ method_option :force, type: :boolean, default: false, banner: "Overwrite existing files while staging"
140
+ def release(sha)
141
+ invoke(:stage, [sha], options)
142
+ dir = RemoteConfig.release_dir
143
+ inside dir do
144
+ run_hooks_for(:release, :before)
145
+ end
146
+
147
+ symlink_directory(dir, RemoteConfig.release_to) unless RemoteConfig.release_to == File.readlink(dir)
148
+ inside RemoteConfig.release_to do
149
+ # Restart services
150
+ run_or_die(RemoteConfig.restart_command)
151
+ run_hooks_for(:release, :after)
152
+ end
153
+ invoke(:clean)
154
+
155
+ end
156
+
157
+ desc "clean N", "[remote, plumbing] Deletes old releases, keeping the N most recent (by mtime)"
158
+ def clean(n=4)
159
+ inside RemoteConfig.extract_to do
160
+ dirs = Dir.glob('*').sort_by{|path| File.mtime(path) }
161
+ dirs.slice!(n..-1)
162
+ dirs.each do |dir|
163
+ say_status :clean, "Deleting #{dir}"
164
+ FileUtils.rm_rf(dir)
165
+ end
166
+ end
167
+ end
168
+
169
+ private
170
+ # Run command hooks from config file
171
+ # E.g. run_hooks_for(:build, :post)
172
+ def run_hooks_for(cmd, hook)
173
+ say_status :hooks, "Running #{cmd} #{hook} hooks"
174
+ Config.hooks_for(cmd, hook).each do |command|
175
+ say "Running `#{command}`"
176
+ run_or_die command
177
+ end
178
+ end
179
+
180
+ # Symlinks a directory
181
+ # NB that `ln -f` doesn't work with directories.
182
+ # This is not atomic.
183
+ def symlink_directory(src, dest)
184
+ raise ArgumentError.new("#{src} is not a directory") unless File.directory?(src)
185
+ say_status :symlink, "from #{src} to #{dest}"
186
+ FileUtils.rm_rf(dest)
187
+ FileUtils.ln_s(src, dest)
188
+ end
189
+ end
190
+ end
@@ -0,0 +1,79 @@
1
+ module Egads
2
+
3
+ module CommonConfig
4
+ def config
5
+ @config ||= YAML.load_file(config_path)
6
+ end
7
+
8
+ def s3_bucket
9
+ return @bucket if @bucket
10
+ fog = Fog::Storage::AWS.new(aws_access_key_id: config['s3']['access_key'], aws_secret_access_key: config['s3']['secret_key'])
11
+ @bucket ||= fog.directories.new(key: config['s3']['bucket'])
12
+ end
13
+
14
+ def s3_prefix
15
+ config['s3']['prefix']
16
+ end
17
+
18
+ end
19
+
20
+ # Local config in the tarball or current working directory
21
+ module Config
22
+ extend CommonConfig
23
+
24
+ def self.config_path
25
+ path = ENV['EGADS_CONFIG'] || File.join(ENV['PWD'], 'egads.yml')
26
+ unless path && File.readable?(path)
27
+ raise ArgumentError.new("Could not read config file. Set either EGADS_CONFIG, or create egads.yml in the current directory")
28
+ end
29
+ path
30
+ end
31
+
32
+ # Returns the hooks in the config for cmd and hook.
33
+ # E.g. hooks_for(:build, :post)
34
+ def self.hooks_for(cmd, hook)
35
+ if Hash === config[cmd.to_s]
36
+ Array(config[cmd.to_s][hook.to_s])
37
+ else
38
+ []
39
+ end
40
+ end
41
+
42
+ def self.build_extra_paths
43
+ config['build'] && Array(config['build']['extra_paths'])
44
+ end
45
+ end
46
+
47
+ # Remote config for the extract command (before data in tarball is available)
48
+ module RemoteConfig
49
+ extend CommonConfig
50
+
51
+ def self.config_path
52
+ path = ENV['EGADS_REMOTE_CONFIG'] || "/etc/egads.yml"
53
+ unless path && File.readable?(path)
54
+ raise ArgumentError.new("Could not read remote config file. Set either EGADS_REMOTE_CONFIG, or create /etc/egads.yml")
55
+ end
56
+ end
57
+
58
+ def self.release_dir(sha)
59
+ File.join(config['extract_to'], sha)
60
+ end
61
+
62
+ def self.release_to
63
+ config['release_to']
64
+ end
65
+
66
+ def self.extract_to
67
+ config['extract_to']
68
+ end
69
+
70
+ # Set environment variables from the config
71
+ def self.setup_environment
72
+ config['env'].each{|k,v| ENV[k] = v } if config['env']
73
+ end
74
+
75
+ def self.restart_command
76
+ config['restart_command']
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,25 @@
1
+ require 'thor'
2
+ require 'benchmark'
3
+ class Thor
4
+ class CommandFailedError < Error; end
5
+
6
+ module Actions
7
+ # runs command, raises CommandFailedError unless exit status is 0.
8
+ # Also logs duration
9
+ def run_or_die(command, config={})
10
+ result = nil
11
+ duration = Benchmark.realtime do
12
+ result = run(command, config)
13
+ end
14
+ if behavior == :invoke && $?.exitstatus != 0
15
+ message = "#{command} failed with %s" % ($?.exitstatus ? "exit status #{$?.exitstatus}" : "no exit status (likely force killed)")
16
+ raise Thor::CommandFailedError.new(message)
17
+ end
18
+
19
+ say_status :done, "in %.1f seconds" % duration, config.fetch(:verbose, true)
20
+
21
+ result
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,41 @@
1
+ module Egads
2
+ class S3Tarball
3
+ attr_reader :sha, :remote
4
+ def initialize(sha, remote = false)
5
+ @sha = sha
6
+ @remote = remote
7
+ end
8
+
9
+ def key
10
+ [Config.s3_prefix, "#{sha}.tar.gz"].compact * '/'
11
+ end
12
+
13
+ def exists?
14
+ bucket.files.head(key)
15
+ end
16
+
17
+ def local_tar_path
18
+ "tmp/#{sha}.tar"
19
+ end
20
+
21
+ def local_gzipped_path
22
+ "#{local_tar_path}.gz"
23
+ end
24
+
25
+ def upload(path=local_gzipped_path)
26
+ File.open(path) {|f|
27
+ bucket.files.create(key: key, body: f)
28
+ }
29
+ end
30
+
31
+ # Load the file contents from S3
32
+ def contents
33
+ bucket.files.get(key: key).body
34
+ end
35
+
36
+ def bucket
37
+ remote ? RemoteConfig.s3_bucket : Config.s3_bucket
38
+ end
39
+ end
40
+ end
41
+
@@ -0,0 +1,3 @@
1
+ module Egads
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,23 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe Egads::Config do
4
+
5
+ subject { Egads::Config }
6
+ it "raises ArgumentError for missing config" do
7
+ -> { subject.config_path }.must_raise(ArgumentError)
8
+ end
9
+
10
+ describe "with an config file" do
11
+ before { ENV['EGADS_CONFIG'] = "example/egads.yml" }
12
+ after { ENV.delete('EGAGS_CONFIG') }
13
+
14
+ it "has an S3 bucket" do
15
+ subject.s3_bucket.key.must_equal 'my-bucket'
16
+ end
17
+
18
+ it "has an S3 prefix" do
19
+ subject.s3_prefix.must_equal 'my_project'
20
+ end
21
+ end
22
+
23
+ end
@@ -0,0 +1,30 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe Egads::S3Tarball do
4
+
5
+ before { ENV['EGADS_CONFIG'] = "example/egads.yml" }
6
+ after { ENV.delete('EGAGS_CONFIG') }
7
+
8
+ subject { Egads::S3Tarball.new('sha') }
9
+
10
+ it('has a sha') { subject.sha.must_equal 'sha' }
11
+ it('has a key') { subject.key.must_equal 'my_project/sha.tar.gz' }
12
+
13
+ it "has an S3 bucket" do
14
+ subject.bucket.must_equal Egads::Config.s3_bucket
15
+ end
16
+
17
+ it('should not exist') { subject.exists?.must_be_nil }
18
+
19
+ describe 'when uploaded' do
20
+ before do
21
+ Egads::Config.s3_bucket.save # Ensure bucket exists
22
+ subject.upload(ENV['EGADS_CONFIG'])
23
+ end
24
+
25
+ it('should exist') { subject.exists?.wont_be_nil }
26
+ end
27
+
28
+
29
+ end
30
+
@@ -0,0 +1,17 @@
1
+ require "rubygems"
2
+ require "bundler/setup"
3
+
4
+ require "minitest/autorun"
5
+ require "minitest/pride"
6
+ require "egads"
7
+
8
+ Fog.mock!
9
+
10
+ begin
11
+ require 'debugger'
12
+ rescue LoadError
13
+ puts "Skipping debugger"
14
+ end
15
+
16
+ class Minitest::Spec
17
+ end
metadata ADDED
@@ -0,0 +1,126 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: egads
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Aaron Suggs
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-06-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: fog
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: thor
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: ! "\n A collection of scripts for making a deployable tarball of a
70
+ git commit,\n uploading it to Amazon S3, and downloading it to your servers."
71
+ email:
72
+ - aaron@ktheory.com
73
+ executables:
74
+ - egads
75
+ extensions: []
76
+ extra_rdoc_files:
77
+ - README.md
78
+ files:
79
+ - .gitignore
80
+ - Gemfile
81
+ - Gemfile.lock
82
+ - README.md
83
+ - Rakefile
84
+ - bin/egads
85
+ - egads.gemspec
86
+ - example/egads.yml
87
+ - example/egads_remote.yml
88
+ - lib/egads.rb
89
+ - lib/egads/capistrano.rb
90
+ - lib/egads/cli.rb
91
+ - lib/egads/config.rb
92
+ - lib/egads/ext/thor_actions.rb
93
+ - lib/egads/s3_tarball.rb
94
+ - lib/egads/version.rb
95
+ - spec/egads_config_spec.rb
96
+ - spec/egads_s3_tarball_spec.rb
97
+ - spec/spec_helper.rb
98
+ homepage: https://github.com/kickstarter/egads
99
+ licenses: []
100
+ metadata: {}
101
+ post_install_message:
102
+ rdoc_options:
103
+ - --charset=UTF-8
104
+ require_paths:
105
+ - lib
106
+ required_ruby_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ! '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ required_rubygems_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ! '>='
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ requirements: []
117
+ rubyforge_project:
118
+ rubygems_version: 2.0.3
119
+ signing_key:
120
+ specification_version: 4
121
+ summary: Extensible Git Archive Deploy Strategy
122
+ test_files:
123
+ - spec/egads_config_spec.rb
124
+ - spec/egads_s3_tarball_spec.rb
125
+ - spec/spec_helper.rb
126
+ has_rdoc: