pipely 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/pipely/bundler.rb +29 -0
- data/lib/pipely/bundler/bundle.rb +73 -0
- data/lib/pipely/bundler/gem_packager.rb +119 -0
- data/lib/pipely/bundler/project_gem.rb +47 -0
- data/lib/pipely/deploy/bootstrap.rb +13 -93
- data/lib/pipely/deploy/bootstrap_context.rb +37 -0
- data/lib/pipely/deploy/s3_uploader.rb +58 -0
- data/lib/pipely/tasks/upload_pipeline_as_gem.rb +4 -4
- data/lib/pipely/tasks/upload_steps.rb +2 -5
- data/lib/pipely/version.rb +1 -1
- data/spec/lib/pipely/bundler/bundle_spec.rb +72 -0
- data/spec/lib/pipely/bundler/gem_packager_spec.rb +101 -0
- data/spec/lib/pipely/bundler/project_gem_spec.rb +95 -0
- data/spec/lib/pipely/deploy/bootstrap_context_spec.rb +57 -0
- data/spec/lib/pipely/deploy/bootstrap_spec.rb +31 -64
- data/spec/lib/pipely/deploy/s3_uploader_spec.rb +54 -0
- data/spec/spec_helper.rb +1 -0
- metadata +32 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 84180497ce48add0459eec37acfdb95bf66ebe05
|
4
|
+
data.tar.gz: d39f9fef3430493934320d8a88b38a2ced74b42f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dae88dbeb0ed78e2197b90cba2601ed218e7bfc8787e5ca4a28618e5b167b2e9d010bfe378670b8b4a8d688f7b624f44abc123f75173046fbf25e3afa1d69555
|
7
|
+
data.tar.gz: acbb97a002b4f611be91bd2768e25ed75637e9eba962a632d04a4203bfb2db35ca4141d03fe14dfed196313c53e4f49e4945187e3b4b9d8fbc2a9f64d84a0866
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'pipely/bundler/bundle'
|
2
|
+
require 'pipely/bundler/gem_packager'
|
3
|
+
require 'pipely/bundler/project_gem'
|
4
|
+
|
5
|
+
module Pipely
|
6
|
+
|
7
|
+
#
|
8
|
+
# Module for packaging up a gem project and its dependencies, as they exist
|
9
|
+
# on your machine, for deployment.
|
10
|
+
#
|
11
|
+
# None of this code is specific to AWS Data Pipelines, and it could be used
|
12
|
+
# anywhere else you want to an in-development gem with frozen dependencies.
|
13
|
+
#
|
14
|
+
module Bundler
|
15
|
+
|
16
|
+
# List all the gems used in this project in the format:
|
17
|
+
#
|
18
|
+
# { name => path_to_cache_file }
|
19
|
+
#
|
20
|
+
# For gems that are git- or path-sourced, it will first build a fresh cache
|
21
|
+
# file for the gem.
|
22
|
+
#
|
23
|
+
def self.gem_files(vendor_dir='vendor/pipeline')
|
24
|
+
ProjectGem.load(vendor_dir).gem_files
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module Pipely
|
4
|
+
module Bundler
|
5
|
+
|
6
|
+
#
|
7
|
+
# Provides access to a bundle's list of gems
|
8
|
+
#
|
9
|
+
class Bundle
|
10
|
+
|
11
|
+
attr_reader :spec_set
|
12
|
+
|
13
|
+
SOURCE_TYPES = %w[Bundler::Source::Git Bundler::Source::Path]
|
14
|
+
|
15
|
+
def self.build(vendor_dir,
|
16
|
+
groups=[:default],
|
17
|
+
definition=::Bundler.definition)
|
18
|
+
new(
|
19
|
+
vendor_dir,
|
20
|
+
definition.specs_for(groups),
|
21
|
+
definition.instance_variable_get(:@locked_sources)
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize(vendor_dir, spec_set, locked_sources)
|
26
|
+
@spec_set = spec_set
|
27
|
+
@locked_sources = locked_sources
|
28
|
+
@vendor_dir = vendor_dir
|
29
|
+
unless Dir.exists? @vendor_dir
|
30
|
+
FileUtils.mkdir_p(@vendor_dir)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
def gem_files(gem_packager=GemPackager.new(@vendor_dir))
|
36
|
+
gem_files = {}
|
37
|
+
|
38
|
+
@spec_set.to_a.each do |spec|
|
39
|
+
gem_files.merge!(gem_file(spec, gem_packager))
|
40
|
+
end
|
41
|
+
|
42
|
+
gem_files
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def gem_file(spec, gem_packager)
|
48
|
+
if source = locked_sources_by_name[spec.name]
|
49
|
+
gem_packager.build_from_source(source.name, source.path)
|
50
|
+
else
|
51
|
+
gem_packager.package(spec)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def locked_sources_by_name
|
56
|
+
return @locked_sources_by_name if @locked_sources_by_name
|
57
|
+
|
58
|
+
@locked_sources_by_name = {}
|
59
|
+
|
60
|
+
@locked_sources.each do |source|
|
61
|
+
# Only include git or path sources.
|
62
|
+
if SOURCE_TYPES.include?(source.class.name)
|
63
|
+
@locked_sources_by_name[source.name] = source
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
locked_sources_by_name
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'excon'
|
3
|
+
|
4
|
+
module Pipely
|
5
|
+
module Bundler
|
6
|
+
|
7
|
+
#
|
8
|
+
# Builds cache files for git- or path-sourced gems.
|
9
|
+
#
|
10
|
+
class GemPackager
|
11
|
+
|
12
|
+
# Alert upon gem-building failures
|
13
|
+
class GemBuildError < RuntimeError ; end
|
14
|
+
|
15
|
+
# Alert upon gem-fetching failures
|
16
|
+
class GemFetchError < RuntimeError ; end
|
17
|
+
|
18
|
+
def initialize(vendor_dir)
|
19
|
+
@vendor_dir = vendor_dir
|
20
|
+
unless Dir.exists? @vendor_dir
|
21
|
+
FileUtils.mkdir_p(@vendor_dir)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def package(spec)
|
26
|
+
if vendored_gem = vendor_local_gem(spec)
|
27
|
+
vendored_gem
|
28
|
+
|
29
|
+
# Finally, some gems do not exist in the cache or as source. For
|
30
|
+
# instance, json is shipped with the ruby dist. Try to fetch directly
|
31
|
+
# from rubygems.
|
32
|
+
else
|
33
|
+
gem_file_name = "#{spec.name}-#{spec.version}.gem"
|
34
|
+
{ spec.name => download_from_rubygems(gem_file_name)}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def vendor_local_gem(spec)
|
39
|
+
gem_file = spec.cache_file
|
40
|
+
vendored_gem = File.join( @vendor_dir, File.basename(gem_file) )
|
41
|
+
|
42
|
+
if File.exists?(vendored_gem)
|
43
|
+
{ spec.name => vendored_gem }
|
44
|
+
|
45
|
+
# Gem exists in the local ruby gems cache
|
46
|
+
elsif File.exists? gem_file
|
47
|
+
|
48
|
+
# Copy to vendor dir
|
49
|
+
FileUtils.cp(gem_file, vendored_gem)
|
50
|
+
|
51
|
+
{ spec.name => vendored_gem }
|
52
|
+
|
53
|
+
# If source exists, build a gem from it
|
54
|
+
elsif File.directory?(spec.gem_dir)
|
55
|
+
build_from_source(spec.name, spec.gem_dir)
|
56
|
+
else
|
57
|
+
nil
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def build_from_source(spec_name, source_path)
|
62
|
+
gem_spec_path = "#{spec_name}.gemspec"
|
63
|
+
|
64
|
+
# Build the gemspec
|
65
|
+
gem_spec = Gem::Specification::load(
|
66
|
+
File.join(source_path,gem_spec_path))
|
67
|
+
|
68
|
+
gem_file = build_gem(spec_name, source_path)
|
69
|
+
|
70
|
+
# Move to vendor dir
|
71
|
+
FileUtils.mv(
|
72
|
+
File.join(source_path,gem_file),
|
73
|
+
File.join(@vendor_dir,gem_file))
|
74
|
+
|
75
|
+
{ gem_spec.name => File.join(@vendor_dir, gem_file) }
|
76
|
+
end
|
77
|
+
|
78
|
+
def build_gem(spec_name, source_path)
|
79
|
+
gem_spec_path = "#{spec_name}.gemspec"
|
80
|
+
|
81
|
+
Dir.chdir(source_path) do
|
82
|
+
result = `gem build #{gem_spec_path} 2>&1`
|
83
|
+
|
84
|
+
if result =~ /ERROR/i
|
85
|
+
raise GemBuildError.new(
|
86
|
+
"Failed to build #{gem_spec_path} \n" << result)
|
87
|
+
else
|
88
|
+
result.scan(
|
89
|
+
/File:(.+.gem)$/).flatten.first.strip
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def download_from_rubygems(gem_file_name)
|
95
|
+
vendored_gem = File.join( @vendor_dir, gem_file_name )
|
96
|
+
|
97
|
+
# XXX: add link on wiki details what is going on here
|
98
|
+
puts "Fetching gem #{gem_file_name} directly from rubygems, most " +
|
99
|
+
"likely this gem was packaged along with your ruby " +
|
100
|
+
"distrubtion, for more details see LINK"
|
101
|
+
|
102
|
+
ruby_gem_url = "https://rubygems.org/downloads/#{gem_file_name}"
|
103
|
+
response = Excon.get( ruby_gem_url, {
|
104
|
+
middlewares: Excon.defaults[:middlewares] +
|
105
|
+
[Excon::Middleware::RedirectFollower]
|
106
|
+
})
|
107
|
+
|
108
|
+
if response.status == 200
|
109
|
+
File.open(vendored_gem, 'w') { |file| file.write( response.body ) }
|
110
|
+
return vendored_gem
|
111
|
+
else
|
112
|
+
raise GemFetchError.new(
|
113
|
+
"Failed to find #{gem_file_name} at rubygems, recieved
|
114
|
+
#{response.status} with #{response.body}" )
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Pipely
|
2
|
+
module Bundler
|
3
|
+
|
4
|
+
#
|
5
|
+
# Builds the project's gem from gemspec and pulls in its dependencies via
|
6
|
+
# the gem's bundle.
|
7
|
+
#
|
8
|
+
class ProjectGem
|
9
|
+
|
10
|
+
attr_reader :project_spec
|
11
|
+
|
12
|
+
def self.load(vendor_dir)
|
13
|
+
if gem_spec = Dir.glob("*.gemspec").first
|
14
|
+
# project gem spec
|
15
|
+
new(Gem::Specification::load(gem_spec), vendor_dir)
|
16
|
+
else
|
17
|
+
raise "Failed to find gemspec"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(project_spec, vendor_dir)
|
22
|
+
@project_spec = project_spec
|
23
|
+
@vendor_dir = vendor_dir
|
24
|
+
unless Dir.exists? @vendor_dir
|
25
|
+
FileUtils.mkdir_p(@vendor_dir)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def gem_files
|
30
|
+
# Project gem should be at the bottom of the dep list
|
31
|
+
@gem_files ||= dependency_gem_files.merge(project_gem_file)
|
32
|
+
end
|
33
|
+
|
34
|
+
def dependency_gem_files(bundle=Bundle.build(@vendor_dir))
|
35
|
+
# Always exclude bundler and the project gem
|
36
|
+
gems_to_exclude = [ @project_spec.name, 'bundler' ]
|
37
|
+
|
38
|
+
bundle.gem_files.reject { |name, path| gems_to_exclude.include?(name) }
|
39
|
+
end
|
40
|
+
|
41
|
+
def project_gem_file(gem_packager=GemPackager.new(@vendor_dir))
|
42
|
+
gem_packager.build_from_source(@project_spec.name, Dir.pwd)
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -1,115 +1,35 @@
|
|
1
|
+
require 'pipely/bundler'
|
2
|
+
require 'pipely/deploy/bootstrap_context'
|
3
|
+
require 'pipely/deploy/s3_uploader'
|
4
|
+
|
1
5
|
module Pipely
|
2
6
|
module Deploy
|
3
7
|
|
4
8
|
# Helps bootstrap a pipeline
|
5
9
|
class Bootstrap
|
6
10
|
|
7
|
-
attr_reader :bucket_name
|
8
|
-
attr_reader :s3_gems_path
|
9
11
|
attr_reader :project_spec
|
10
12
|
attr_reader :gem_files
|
11
13
|
|
12
|
-
def initialize(
|
13
|
-
@
|
14
|
-
@bucket_name = s3_bucket.name
|
15
|
-
@s3_gems_path = s3_gems_path
|
14
|
+
def initialize(s3_uploader)
|
15
|
+
@s3_uploader = s3_uploader
|
16
16
|
end
|
17
17
|
|
18
18
|
# Builds the project's gem from gemspec, uploads the gem to s3, and
|
19
19
|
# uploads all the gem dependences to S3
|
20
20
|
def build_and_upload_gems
|
21
|
-
|
22
|
-
|
23
|
-
project_gem_name = nil
|
24
|
-
|
25
|
-
gem_spec = Dir.glob("*.gemspec").first
|
26
|
-
if gem_spec
|
27
|
-
|
28
|
-
# Build pipeline gem
|
29
|
-
@project_spec = Gem::Specification::load(gem_spec)
|
30
|
-
# build the gem
|
31
|
-
project_gem_file =
|
32
|
-
`gem build ./#{gem_spec}`.scan(
|
33
|
-
/File:(.+.gem)$/).flatten.first.strip
|
34
|
-
project_gem_name = @project_spec.name
|
35
|
-
upload_gem(project_gem_file)
|
36
|
-
end
|
37
|
-
|
38
|
-
@gem_files = upload_gems_from_bundler(project_gem_name)
|
39
|
-
|
40
|
-
# project gem has to be loaded last
|
41
|
-
@gem_files << project_gem_file if @project_spec
|
21
|
+
@gem_files = Pipely::Bundler.gem_files
|
22
|
+
@s3_uploader.upload(@gem_files.values)
|
42
23
|
end
|
43
24
|
|
44
|
-
def context
|
45
|
-
BootstrapContext.new
|
46
|
-
@gem_files.
|
47
|
-
|
48
|
-
} )
|
49
|
-
end
|
50
|
-
|
51
|
-
def gem_s3_path(gem_file)
|
52
|
-
filename = File.basename(gem_file)
|
53
|
-
File.join(@s3_gems_path, filename)
|
54
|
-
end
|
55
|
-
|
56
|
-
private
|
57
|
-
|
58
|
-
def s3_gem_exists?( gem_file )
|
59
|
-
!@s3_bucket.objects[gem_s3_path(gem_file)].nil?
|
60
|
-
end
|
61
|
-
|
62
|
-
def upload_gem( gem_file )
|
63
|
-
puts "uploading #{gem_file} to #{gem_s3_path(gem_file)}"
|
64
|
-
@s3_bucket.objects[gem_s3_path(gem_file)].write(File.open(gem_file))
|
65
|
-
end
|
66
|
-
|
67
|
-
def upload_gems_from_bundler(project_gem_name)
|
68
|
-
gem_files = []
|
69
|
-
Bundler.definition.specs_for([:default]).each do |spec|
|
70
|
-
# Exclude project from gem deps
|
71
|
-
unless spec.name == project_gem_name
|
72
|
-
gem_file = spec.cache_file
|
73
|
-
|
74
|
-
# XXX: Some gems do not exist in the cache, e.g. json. Looks the
|
75
|
-
# gem is already packaged with the ruby dist
|
76
|
-
if File.exists? gem_file
|
77
|
-
gem_files << gem_file
|
78
|
-
|
79
|
-
# only upload gem if it doesnt exist already
|
80
|
-
unless s3_gem_exists?( gem_file )
|
81
|
-
upload_gem(gem_file)
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
25
|
+
def context(s3_steps_path)
|
26
|
+
BootstrapContext.new.tap do |context|
|
27
|
+
context.gem_files = @s3_uploader.s3_urls(gem_files.values)
|
28
|
+
context.s3_steps_path = s3_steps_path
|
85
29
|
end
|
86
|
-
|
87
|
-
gem_files
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
# Context passed to the erb templates
|
92
|
-
class BootstrapContext
|
93
|
-
attr_reader :gem_files
|
94
|
-
|
95
|
-
def initialize(gem_files)
|
96
|
-
@gem_files = gem_files
|
97
30
|
end
|
98
31
|
|
99
|
-
def install_gems_script
|
100
|
-
script = ""
|
101
|
-
|
102
|
-
@gem_files.each do |gem_file|
|
103
|
-
filename = File.basename(gem_file)
|
104
|
-
script << %Q[
|
105
|
-
# #{filename}
|
106
|
-
hadoop fs -copyToLocal #{gem_file} /home/hadoop/#{filename}
|
107
|
-
gem install --local /home/hadoop/#{filename} --no-ri --no-rdoc
|
108
|
-
]
|
109
|
-
end
|
110
|
-
|
111
|
-
script
|
112
|
-
end
|
113
32
|
end
|
33
|
+
|
114
34
|
end
|
115
35
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
|
2
|
+
module Pipely
|
3
|
+
module Deploy
|
4
|
+
|
5
|
+
# Context passed to the erb templates
|
6
|
+
class BootstrapContext
|
7
|
+
attr_accessor :gem_files
|
8
|
+
attr_accessor :s3_steps_path
|
9
|
+
|
10
|
+
def install_gems_script(transport = :hadoop_fs, &blk)
|
11
|
+
script = ""
|
12
|
+
|
13
|
+
case transport.to_sym
|
14
|
+
when :hadoop_fs
|
15
|
+
transport_cmd = 'hadoop fs -copyToLocal'
|
16
|
+
when :awscli
|
17
|
+
transport_cmd = 'aws s3 cp'
|
18
|
+
else
|
19
|
+
raise "Unsupported transport: #{transport}" unless blk
|
20
|
+
end
|
21
|
+
|
22
|
+
@gem_files.each do |gem_file|
|
23
|
+
filename = File.basename(gem_file)
|
24
|
+
command = "#{transport_cmd} #{gem_file} #{filename}" if transport_cmd
|
25
|
+
command = yield(gem_file, filename, command) if blk
|
26
|
+
script << %Q[
|
27
|
+
# #{filename}
|
28
|
+
#{command}
|
29
|
+
gem install --force --local #{filename} --no-ri --no-rdoc
|
30
|
+
]
|
31
|
+
end
|
32
|
+
|
33
|
+
script
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'digest/md5'
|
2
|
+
|
3
|
+
module Pipely
|
4
|
+
module Deploy
|
5
|
+
|
6
|
+
#
|
7
|
+
# Manage syncing of local files to a particular S3 path
|
8
|
+
#
|
9
|
+
class S3Uploader
|
10
|
+
|
11
|
+
attr_reader :bucket_name
|
12
|
+
attr_reader :s3_path
|
13
|
+
|
14
|
+
def initialize(s3_bucket, s3_path)
|
15
|
+
@s3_bucket = s3_bucket
|
16
|
+
@bucket_name = s3_bucket.name
|
17
|
+
@s3_path = s3_path
|
18
|
+
end
|
19
|
+
|
20
|
+
def s3_file_path(file)
|
21
|
+
filename = File.basename(file)
|
22
|
+
File.join(@s3_path, filename)
|
23
|
+
end
|
24
|
+
|
25
|
+
def s3_urls(files)
|
26
|
+
files.map do |file|
|
27
|
+
File.join("s3://", @s3_bucket.name, s3_file_path(file) )
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def upload(files)
|
32
|
+
files.each do |file|
|
33
|
+
upload_file(file)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# Upload file to S3 unless ETAGs already match.
|
39
|
+
#
|
40
|
+
def upload_file(file)
|
41
|
+
target_path = s3_file_path(file)
|
42
|
+
s3_object = @s3_bucket.objects[target_path]
|
43
|
+
|
44
|
+
content = File.read(file)
|
45
|
+
digest = Digest::MD5.hexdigest(content)
|
46
|
+
|
47
|
+
if s3_object.exists? && (digest == s3_object.etag.gsub('"', ''))
|
48
|
+
puts "skipping #{file} to #{target_path} (ETAG matches)"
|
49
|
+
else
|
50
|
+
puts "uploading #{file} to #{target_path}"
|
51
|
+
s3_object.write(content)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'rake'
|
2
2
|
require 'aws'
|
3
|
+
require 'erubis'
|
3
4
|
require 'pipely/deploy/bootstrap'
|
4
5
|
|
5
6
|
module Pipely
|
@@ -59,19 +60,18 @@ module Pipely
|
|
59
60
|
end
|
60
61
|
|
61
62
|
def build_bootstrap_context
|
62
|
-
|
63
|
-
|
63
|
+
s3_uploader = Pipely::Deploy::S3Uploader.new(s3_bucket, @s3_gems_path)
|
64
|
+
bootstrap_helper = Pipely::Deploy::Bootstrap.new(s3_uploader)
|
64
65
|
bootstrap_helper.build_and_upload_gems
|
65
66
|
|
66
67
|
# erb context
|
67
68
|
{
|
68
|
-
bootstrap: bootstrap_helper.context,
|
69
|
+
bootstrap: bootstrap_helper.context(@s3_steps_path),
|
69
70
|
config: config
|
70
71
|
}
|
71
72
|
end
|
72
73
|
|
73
74
|
def upload_to_s3( upload_filename, body )
|
74
|
-
|
75
75
|
s3_dest = File.join(@s3_steps_path, upload_filename)
|
76
76
|
puts "uploading #{s3_dest}" if verbose
|
77
77
|
s3_bucket.objects[s3_dest].write(body)
|
@@ -57,11 +57,8 @@ module Pipely
|
|
57
57
|
|
58
58
|
def run_task(verbose)
|
59
59
|
with_bucket do |bucket|
|
60
|
-
|
61
|
-
|
62
|
-
puts "uploading #{dest}" if verbose
|
63
|
-
bucket.objects[dest].write(File.read(file_name))
|
64
|
-
end
|
60
|
+
s3_uploader = Pipely::Deploy::S3Uploader.new(bucket, s3_path)
|
61
|
+
s3_uploader.upload(step_files)
|
65
62
|
end
|
66
63
|
end
|
67
64
|
|
data/lib/pipely/version.rb
CHANGED
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'pipely/bundler/bundle'
|
2
|
+
|
3
|
+
describe Pipely::Bundler::Bundle do
|
4
|
+
|
5
|
+
describe ".build" do
|
6
|
+
let(:groups) { [ :group1 ] }
|
7
|
+
let(:definition) { double "Bundler::Definition" }
|
8
|
+
let(:spec_set) { double }
|
9
|
+
|
10
|
+
before do
|
11
|
+
definition.stub(:specs_for).with(groups) { spec_set }
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'builds a Bundle instance with the spec_set' do
|
15
|
+
bundle = described_class.build('vendor/test', groups, definition)
|
16
|
+
expect(bundle.spec_set).to eq(spec_set)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
let(:pipely_spec) { double("Gem::Specification", name: "pipely") }
|
21
|
+
let(:gem1_spec) { double("Gem::Specification", name: "gem1") }
|
22
|
+
let(:gem2_spec) { double("Gem::Specification", name: "gem2") }
|
23
|
+
|
24
|
+
let(:pipely_source) do
|
25
|
+
Bundler::Source::Path.new('name' => "pipely", 'path' => '.')
|
26
|
+
end
|
27
|
+
|
28
|
+
let(:spec_set) { [ pipely_spec, gem1_spec, gem2_spec ] }
|
29
|
+
let(:locked_sources) { [ pipely_source ] }
|
30
|
+
|
31
|
+
subject { described_class.new('vendor/test', spec_set, locked_sources) }
|
32
|
+
|
33
|
+
describe "#gem_files" do
|
34
|
+
let(:gem_packager) { double }
|
35
|
+
|
36
|
+
before do
|
37
|
+
gem_packager.stub(:package).and_return do |spec|
|
38
|
+
{ spec.name => '/path/to/cache/file.gem' }
|
39
|
+
end
|
40
|
+
|
41
|
+
gem_packager.stub(:build_from_source).and_return do |name, path|
|
42
|
+
{ name => "#{path}/#{name}-X.Y.Z.gem" }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
it "returns a cache file for each gem" do
|
47
|
+
gem_files = subject.gem_files(gem_packager)
|
48
|
+
expect(gem_files.keys).to match_array(%w[ gem1 gem2 pipely ])
|
49
|
+
end
|
50
|
+
|
51
|
+
context "given a packaged/non-locked gem" do
|
52
|
+
it "returns the gems and their existing cache files" do
|
53
|
+
expect(gem_packager).to receive(:package).with(gem1_spec)
|
54
|
+
expect(gem_packager).to receive(:package).with(gem2_spec)
|
55
|
+
|
56
|
+
subject.gem_files(gem_packager)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context "given a locked-source gem" do
|
61
|
+
it "should build new cache files from source" do
|
62
|
+
expect(gem_packager).to receive(:build_from_source).with(
|
63
|
+
pipely_source.name,
|
64
|
+
pipely_source.path
|
65
|
+
)
|
66
|
+
|
67
|
+
subject.gem_files(gem_packager)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'pipely/bundler/gem_packager'
|
3
|
+
|
4
|
+
describe Pipely::Bundler::GemPackager do
|
5
|
+
|
6
|
+
subject { described_class.new(vendor_path) }
|
7
|
+
let(:vendor_path) { 'vendor/test' }
|
8
|
+
|
9
|
+
before(:each) {
|
10
|
+
unless Dir.exists? vendor_path
|
11
|
+
FileUtils.mkdir_p 'vendor/test'
|
12
|
+
end
|
13
|
+
}
|
14
|
+
|
15
|
+
describe "#package" do
|
16
|
+
let(:gem_spec) do
|
17
|
+
double("spec",
|
18
|
+
name: 'test',
|
19
|
+
cache_file: 'a/cache/file',
|
20
|
+
gem_dir:'a/gem/dir',
|
21
|
+
version:'0.0.1'
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
let(:vendored_gem) { "vendor/test/file" }
|
26
|
+
|
27
|
+
context "with a cache file" do
|
28
|
+
before do
|
29
|
+
allow(File).to receive(:exists?).with(vendored_gem) { false }
|
30
|
+
allow(File).to receive(:exists?).with(gem_spec.cache_file) { true }
|
31
|
+
allow(FileUtils).to receive(:cp).with(
|
32
|
+
gem_spec.cache_file, vendored_gem)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "returns the cache file" do
|
36
|
+
expect(subject.package(gem_spec)).to eq(
|
37
|
+
{gem_spec.name => vendored_gem}
|
38
|
+
)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context "without a cache file" do
|
43
|
+
before do
|
44
|
+
allow(File).to receive(:exists?).with(gem_spec.cache_file) { false }
|
45
|
+
allow(File).to receive(:exists?).with(vendored_gem) { false }
|
46
|
+
end
|
47
|
+
|
48
|
+
context "if source is available" do
|
49
|
+
before do
|
50
|
+
allow(File).to receive(:directory?).with(gem_spec.gem_dir) { true }
|
51
|
+
end
|
52
|
+
|
53
|
+
it "builds the gem from source" do
|
54
|
+
expect(subject).to receive(:build_from_source).and_return(
|
55
|
+
{"test"=>"a/packaged/file"})
|
56
|
+
|
57
|
+
expect(subject.package(gem_spec)).to eq({"test"=>"a/packaged/file"})
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context "if source not available, e.g. json-1.8.1 built into Ruby 2.1" do
|
62
|
+
before do
|
63
|
+
allow(File).to receive(:directory?).with(gem_spec.gem_dir) { false }
|
64
|
+
end
|
65
|
+
|
66
|
+
it "downloads from rubygems" do
|
67
|
+
response = double(:response, status: 200, body: 'im a gem')
|
68
|
+
expect(Excon).to receive(:get).with(
|
69
|
+
"https://rubygems.org/downloads/test-0.0.1.gem", anything())
|
70
|
+
.and_return(response)
|
71
|
+
expect(subject.package(gem_spec)).to eq(
|
72
|
+
{"test"=>"vendor/test/test-0.0.1.gem"})
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context "if source not available and not on rubygems" do
|
77
|
+
before do
|
78
|
+
allow(File).to receive(:directory?).with(gem_spec.gem_dir) { false }
|
79
|
+
end
|
80
|
+
|
81
|
+
it "raises" do
|
82
|
+
response = double(:response, status: 400, body: 'test')
|
83
|
+
expect(Excon).to receive(:get).with(
|
84
|
+
"https://rubygems.org/downloads/test-0.0.1.gem", anything())
|
85
|
+
.and_return(response)
|
86
|
+
|
87
|
+
expect { subject.package(gem_spec) }.to raise_error
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe "#build_from_source" do
|
94
|
+
context "with bad spec" do
|
95
|
+
it "raises" do
|
96
|
+
expect { subject.build_from_source("bad-name", ".") }.to raise_error
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# Copyright Swipely, Inc. All rights reserved.
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'pipely/deploy/bootstrap'
|
5
|
+
|
6
|
+
describe Pipely::Bundler::ProjectGem do
|
7
|
+
|
8
|
+
let(:project_spec) do
|
9
|
+
double "Gem::Specification",
|
10
|
+
name: "my-project",
|
11
|
+
file_name: "/path/to/cache/my-project.gem"
|
12
|
+
end
|
13
|
+
|
14
|
+
subject { described_class.new(project_spec, 'vendor/test') }
|
15
|
+
|
16
|
+
describe ".load" do
|
17
|
+
let(:filename) { 'foo.gemspec' }
|
18
|
+
let(:gemspec) { double }
|
19
|
+
|
20
|
+
before do
|
21
|
+
allow(Dir).to receive(:glob).with("*.gemspec") { [ filename ] }
|
22
|
+
allow(Gem::Specification).to receive(:load).with(filename) { gemspec }
|
23
|
+
end
|
24
|
+
|
25
|
+
it "loads the gemspec" do
|
26
|
+
loaded = described_class.load('vendor/test')
|
27
|
+
expect(loaded.project_spec).to eq(gemspec)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "#gem_files" do
|
32
|
+
let(:dependency_gem_files) do
|
33
|
+
{
|
34
|
+
'packaged-gem1' => '/path/to/cache/packaged-gem1.gem',
|
35
|
+
'built-from-source-gem1' => '/path/to/cache/built-from-source-gem1.gem',
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
let(:project_gem_file) do
|
40
|
+
{
|
41
|
+
project_spec.name => project_spec.file_name
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
before do
|
46
|
+
allow(subject).to receive(:dependency_gem_files) { dependency_gem_files }
|
47
|
+
allow(subject).to receive(:project_gem_file) { project_gem_file }
|
48
|
+
end
|
49
|
+
|
50
|
+
it "combines the dependency_gem_files and the project_gem_file" do
|
51
|
+
expect(subject.gem_files.keys).to match_array(
|
52
|
+
dependency_gem_files.keys + project_gem_file.keys
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "lists the project_gem_file last" do
|
57
|
+
expect(subject.gem_files.keys.last).to eq(project_spec.name)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "#dependency_gem_files" do
|
62
|
+
let(:bundle_gem_files) do
|
63
|
+
{
|
64
|
+
'packaged-gem1' => '/path/to/cache/packaged-gem1.gem',
|
65
|
+
'built-from-source-gem1' => '/path/to/cache/built-from-source-gem1.gem',
|
66
|
+
'bundler' => '/path/to/cache/bundler.gem',
|
67
|
+
project_spec.name => project_spec.file_name,
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
let(:bundle) do
|
72
|
+
double "Pipely::Bundler::Bundle", gem_files: bundle_gem_files
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should filter out the bundler gem and the project gem" do
|
76
|
+
result = subject.dependency_gem_files(bundle)
|
77
|
+
expect(result.keys).to eq(%w[ packaged-gem1 built-from-source-gem1 ])
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe "#project_gem_file" do
|
82
|
+
let(:gem_packager) { double "Pipely::Bundler::GemPackager" }
|
83
|
+
let(:project_gem_file) { double }
|
84
|
+
|
85
|
+
before do
|
86
|
+
allow(gem_packager).to receive(:build_from_source) { project_gem_file }
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should return the project's own gem file" do
|
90
|
+
result = subject.project_gem_file(gem_packager)
|
91
|
+
expect(result).to eq(project_gem_file)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# Copyright Swipely, Inc. All rights reserved.
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'pipely/deploy/bootstrap_context'
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
describe Pipely::Deploy::BootstrapContext do
|
8
|
+
subject do
|
9
|
+
Pipely::Deploy::BootstrapContext.new.tap do |context|
|
10
|
+
context.gem_files = ['one.gem', 'two.gem']
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "#install_gems_script" do
|
15
|
+
it "defaults to hadoop fs" do
|
16
|
+
expect(subject.install_gems_script).to eql "
|
17
|
+
# one.gem
|
18
|
+
hadoop fs -copyToLocal one.gem one.gem
|
19
|
+
gem install --force --local one.gem --no-ri --no-rdoc
|
20
|
+
|
21
|
+
# two.gem
|
22
|
+
hadoop fs -copyToLocal two.gem two.gem
|
23
|
+
gem install --force --local two.gem --no-ri --no-rdoc
|
24
|
+
"
|
25
|
+
end
|
26
|
+
|
27
|
+
context "with aws cli" do
|
28
|
+
it "should build script for aws cli" do
|
29
|
+
expect(subject.install_gems_script(:awscli) ).to eql "
|
30
|
+
# one.gem
|
31
|
+
aws s3 cp one.gem one.gem
|
32
|
+
gem install --force --local one.gem --no-ri --no-rdoc
|
33
|
+
|
34
|
+
# two.gem
|
35
|
+
aws s3 cp two.gem two.gem
|
36
|
+
gem install --force --local two.gem --no-ri --no-rdoc
|
37
|
+
"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context "with yield" do
|
42
|
+
it "should build script for aws cli" do
|
43
|
+
expect(subject.install_gems_script(:awscli) do |file,filename,command|
|
44
|
+
"custom command - #{file} #{filename} #{command}"
|
45
|
+
end).to eql "
|
46
|
+
# one.gem
|
47
|
+
custom command - one.gem one.gem aws s3 cp one.gem one.gem
|
48
|
+
gem install --force --local one.gem --no-ri --no-rdoc
|
49
|
+
|
50
|
+
# two.gem
|
51
|
+
custom command - two.gem two.gem aws s3 cp two.gem two.gem
|
52
|
+
gem install --force --local two.gem --no-ri --no-rdoc
|
53
|
+
"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -2,85 +2,52 @@
|
|
2
2
|
|
3
3
|
require 'spec_helper'
|
4
4
|
require 'pipely/deploy/bootstrap'
|
5
|
-
require 'fog'
|
6
5
|
require 'fileutils'
|
7
6
|
|
8
|
-
|
9
|
-
describe Deploy::Bootstrap do
|
10
|
-
subject { described_class.new(s3_bucket, 'test_path/gems') }
|
11
|
-
let(:s3_bucket) do
|
12
|
-
s3 = AWS::S3.new
|
13
|
-
s3.buckets['a-test-bucket']
|
14
|
-
end
|
15
|
-
|
16
|
-
it "should have bucket name" do
|
17
|
-
expect(subject.bucket_name).to eql 'a-test-bucket'
|
18
|
-
end
|
19
|
-
|
20
|
-
it "should have a s3 gems path" do
|
21
|
-
expect(subject.s3_gems_path).to eql 'test_path/gems'
|
22
|
-
end
|
23
|
-
|
24
|
-
describe "#build_and_upload_gems" do
|
25
|
-
let(:build_and_upload_gems) do
|
26
|
-
VCR.use_cassette('build_and_upload_gems') do
|
27
|
-
subject.build_and_upload_gems
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
it "should create project gem" do
|
32
|
-
build_and_upload_gems
|
33
|
-
|
34
|
-
expect(File).to exist(subject.project_spec.file_name)
|
35
|
-
end
|
7
|
+
describe Pipely::Deploy::Bootstrap do
|
36
8
|
|
37
|
-
|
38
|
-
specs = Bundler.definition.specs_for([:default])
|
9
|
+
subject { described_class.new(s3_uploader) }
|
39
10
|
|
40
|
-
|
41
|
-
# Ignore bundler, since it could be a system installed gem (travis)
|
42
|
-
# without a cache file
|
43
|
-
g.match(/^json-\d+\.\d+\.\d+\.gem$/) or
|
44
|
-
g.match(/^bundler-\d+\.\d+\.\d+\.gem$/)
|
45
|
-
end
|
11
|
+
let(:s3_uploader) { double }
|
46
12
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
13
|
+
let(:gem_files) do
|
14
|
+
{
|
15
|
+
'packaged-gem1' => '/path/to/cache/packaged-gem1.gem',
|
16
|
+
'built-from-source-gem1' => '/path/to/cache/built-from-source-gem1.gem',
|
17
|
+
}
|
18
|
+
end
|
51
19
|
|
52
|
-
|
53
|
-
|
54
|
-
|
20
|
+
describe "#build_and_upload_gems" do
|
21
|
+
before do
|
22
|
+
allow(Pipely::Bundler).to receive(:gem_files) { gem_files }
|
23
|
+
end
|
55
24
|
|
56
|
-
|
57
|
-
|
58
|
-
at_most(gems.size + 1).times. # allow for Bundler
|
59
|
-
and_return(objects)
|
25
|
+
it 'uploads each gem' do
|
26
|
+
expect(s3_uploader).to receive(:upload).with(gem_files.values)
|
60
27
|
|
61
|
-
|
62
|
-
end
|
28
|
+
subject.build_and_upload_gems
|
63
29
|
end
|
30
|
+
end
|
64
31
|
|
65
|
-
|
66
|
-
|
32
|
+
describe "#context" do
|
33
|
+
let(:context) { subject.context(s3_steps_path) }
|
34
|
+
let(:s3_steps_path) { 'a/test/path' }
|
35
|
+
let(:s3_gem_paths) { double }
|
67
36
|
|
68
|
-
|
69
|
-
|
70
|
-
subject.build_and_upload_gems
|
71
|
-
end
|
72
|
-
end
|
37
|
+
before do
|
38
|
+
allow(subject).to receive(:gem_files) { gem_files }
|
73
39
|
|
74
|
-
|
75
|
-
|
40
|
+
allow(s3_uploader).to receive(:s3_urls).with(gem_files.values) do
|
41
|
+
s3_gem_paths
|
76
42
|
end
|
43
|
+
end
|
77
44
|
|
78
|
-
|
79
|
-
|
80
|
-
match( /^s3:\/\/#{subject.bucket_name}\/#{subject.s3_gems_path}/ )
|
81
|
-
)
|
82
|
-
end
|
45
|
+
it "should have s3 steps path" do
|
46
|
+
expect(context.s3_steps_path).to eq(s3_steps_path)
|
83
47
|
end
|
84
48
|
|
49
|
+
it "builds S3 urls to the uploaded gem files" do
|
50
|
+
expect(context.gem_files).to eq(s3_gem_paths)
|
51
|
+
end
|
85
52
|
end
|
86
53
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# Copyright Swipely, Inc. All rights reserved.
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'pipely/deploy/s3_uploader'
|
5
|
+
require 'tempfile'
|
6
|
+
|
7
|
+
describe Pipely::Deploy::S3Uploader do
|
8
|
+
|
9
|
+
subject { described_class.new(s3_bucket, 'test_path/gems') }
|
10
|
+
|
11
|
+
let(:s3_bucket) do
|
12
|
+
s3 = AWS::S3.new
|
13
|
+
s3.buckets['a-test-bucket']
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should have bucket name" do
|
17
|
+
expect(subject.bucket_name).to eq('a-test-bucket')
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should have a s3 path" do
|
21
|
+
expect(subject.s3_path).to eq('test_path/gems')
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "#upload(files)" do
|
25
|
+
let(:objects) { double(:objects) }
|
26
|
+
|
27
|
+
let(:s3_object) do
|
28
|
+
double('s3_object', write: nil, exists?: true, etag: 'mismatch')
|
29
|
+
end
|
30
|
+
|
31
|
+
let(:files) do
|
32
|
+
[
|
33
|
+
Tempfile.new('packaged-gem1.gem').path,
|
34
|
+
Tempfile.new('built-from-source-gem1.gem').path,
|
35
|
+
]
|
36
|
+
end
|
37
|
+
|
38
|
+
before do
|
39
|
+
allow(objects).to receive(:[]) { s3_object }
|
40
|
+
allow(s3_bucket).to receive(:objects) { objects }
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'uploads each file' do
|
44
|
+
files.each do |file|
|
45
|
+
expect(objects).to receive(:[]).with(subject.s3_file_path(file))
|
46
|
+
end
|
47
|
+
|
48
|
+
expect(s3_bucket).to receive(:objects).exactly(files.size).times
|
49
|
+
|
50
|
+
subject.upload(files)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pipely
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Gillooly
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-09-
|
11
|
+
date: 2014-09-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ruby-graphviz
|
@@ -206,6 +206,20 @@ dependencies:
|
|
206
206
|
- - ">="
|
207
207
|
- !ruby/object:Gem::Version
|
208
208
|
version: '0'
|
209
|
+
- !ruby/object:Gem::Dependency
|
210
|
+
name: pry
|
211
|
+
requirement: !ruby/object:Gem::Requirement
|
212
|
+
requirements:
|
213
|
+
- - ">="
|
214
|
+
- !ruby/object:Gem::Version
|
215
|
+
version: '0'
|
216
|
+
type: :development
|
217
|
+
prerelease: false
|
218
|
+
version_requirements: !ruby/object:Gem::Requirement
|
219
|
+
requirements:
|
220
|
+
- - ">="
|
221
|
+
- !ruby/object:Gem::Version
|
222
|
+
version: '0'
|
209
223
|
description:
|
210
224
|
email:
|
211
225
|
- matt@swipely.com
|
@@ -230,12 +244,18 @@ files:
|
|
230
244
|
- lib/pipely/build/s3_path_builder.rb
|
231
245
|
- lib/pipely/build/template.rb
|
232
246
|
- lib/pipely/build/template_helpers.rb
|
247
|
+
- lib/pipely/bundler.rb
|
248
|
+
- lib/pipely/bundler/bundle.rb
|
249
|
+
- lib/pipely/bundler/gem_packager.rb
|
250
|
+
- lib/pipely/bundler/project_gem.rb
|
233
251
|
- lib/pipely/component.rb
|
234
252
|
- lib/pipely/definition.rb
|
235
253
|
- lib/pipely/dependency.rb
|
236
254
|
- lib/pipely/deploy.rb
|
237
255
|
- lib/pipely/deploy/bootstrap.rb
|
256
|
+
- lib/pipely/deploy/bootstrap_context.rb
|
238
257
|
- lib/pipely/deploy/client.rb
|
258
|
+
- lib/pipely/deploy/s3_uploader.rb
|
239
259
|
- lib/pipely/fog_client.rb
|
240
260
|
- lib/pipely/graph_builder.rb
|
241
261
|
- lib/pipely/live_pipeline.rb
|
@@ -257,11 +277,16 @@ files:
|
|
257
277
|
- spec/lib/pipely/build/s3_path_builder_spec.rb
|
258
278
|
- spec/lib/pipely/build/template_spec.rb
|
259
279
|
- spec/lib/pipely/build_spec.rb
|
280
|
+
- spec/lib/pipely/bundler/bundle_spec.rb
|
281
|
+
- spec/lib/pipely/bundler/gem_packager_spec.rb
|
282
|
+
- spec/lib/pipely/bundler/project_gem_spec.rb
|
260
283
|
- spec/lib/pipely/component_spec.rb
|
261
284
|
- spec/lib/pipely/definition_spec.rb
|
262
285
|
- spec/lib/pipely/dependency_spec.rb
|
286
|
+
- spec/lib/pipely/deploy/bootstrap_context_spec.rb
|
263
287
|
- spec/lib/pipely/deploy/bootstrap_spec.rb
|
264
288
|
- spec/lib/pipely/deploy/client_spec.rb
|
289
|
+
- spec/lib/pipely/deploy/s3_uploader_spec.rb
|
265
290
|
- spec/lib/pipely/graph_builder_spec.rb
|
266
291
|
- spec/lib/pipely/reference_list_spec.rb
|
267
292
|
- spec/lib/pipely_spec.rb
|
@@ -297,11 +322,16 @@ test_files:
|
|
297
322
|
- spec/lib/pipely/build/s3_path_builder_spec.rb
|
298
323
|
- spec/lib/pipely/build/template_spec.rb
|
299
324
|
- spec/lib/pipely/build_spec.rb
|
325
|
+
- spec/lib/pipely/bundler/bundle_spec.rb
|
326
|
+
- spec/lib/pipely/bundler/gem_packager_spec.rb
|
327
|
+
- spec/lib/pipely/bundler/project_gem_spec.rb
|
300
328
|
- spec/lib/pipely/component_spec.rb
|
301
329
|
- spec/lib/pipely/definition_spec.rb
|
302
330
|
- spec/lib/pipely/dependency_spec.rb
|
331
|
+
- spec/lib/pipely/deploy/bootstrap_context_spec.rb
|
303
332
|
- spec/lib/pipely/deploy/bootstrap_spec.rb
|
304
333
|
- spec/lib/pipely/deploy/client_spec.rb
|
334
|
+
- spec/lib/pipely/deploy/s3_uploader_spec.rb
|
305
335
|
- spec/lib/pipely/graph_builder_spec.rb
|
306
336
|
- spec/lib/pipely/reference_list_spec.rb
|
307
337
|
- spec/lib/pipely_spec.rb
|