pipely 0.7.0 → 0.8.0
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.
- 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
|