egads 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.
@@ -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: