buildpack-packager 2.3.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +5 -0
  5. data/LICENSE +176 -0
  6. data/README.md +180 -0
  7. data/Rakefile +6 -0
  8. data/bin/buildpack-packager +79 -0
  9. data/buildpack-packager.gemspec +33 -0
  10. data/doc/disconnected_environments.md +50 -0
  11. data/lib/buildpack/manifest_dependency.rb +14 -0
  12. data/lib/buildpack/manifest_validator.rb +88 -0
  13. data/lib/buildpack/packager.rb +55 -0
  14. data/lib/buildpack/packager/default_versions_presenter.rb +38 -0
  15. data/lib/buildpack/packager/dependencies_presenter.rb +41 -0
  16. data/lib/buildpack/packager/manifest_schema.yml +67 -0
  17. data/lib/buildpack/packager/package.rb +139 -0
  18. data/lib/buildpack/packager/table_presentation.rb +21 -0
  19. data/lib/buildpack/packager/version.rb +5 -0
  20. data/lib/buildpack/packager/zip_file_excluder.rb +31 -0
  21. data/lib/kwalify/parser/yaml-patcher.rb +70 -0
  22. data/spec/buildpack/packager_spec.rb +7 -0
  23. data/spec/fixtures/buildpack-with-uri-credentials/VERSION +1 -0
  24. data/spec/fixtures/buildpack-with-uri-credentials/manifest.yml +36 -0
  25. data/spec/fixtures/buildpack-without-uri-credentials/VERSION +1 -0
  26. data/spec/fixtures/buildpack-without-uri-credentials/manifest.yml +36 -0
  27. data/spec/fixtures/manifests/manifest_invalid-md6.yml +18 -0
  28. data/spec/fixtures/manifests/manifest_invalid-md6_and_defaults.yml +21 -0
  29. data/spec/fixtures/manifests/manifest_valid.yml +19 -0
  30. data/spec/helpers/cache_directory_helpers.rb +15 -0
  31. data/spec/helpers/fake_binary_hosting_helpers.rb +21 -0
  32. data/spec/helpers/file_system_helpers.rb +35 -0
  33. data/spec/integration/bin/buildpack_packager/download_caching_spec.rb +85 -0
  34. data/spec/integration/bin/buildpack_packager_spec.rb +378 -0
  35. data/spec/integration/buildpack/directory_name_spec.rb +89 -0
  36. data/spec/integration/buildpack/packager_spec.rb +454 -0
  37. data/spec/integration/default_versions_spec.rb +170 -0
  38. data/spec/integration/output_spec.rb +70 -0
  39. data/spec/spec_helper.rb +12 -0
  40. data/spec/unit/buildpack/packager/zip_file_excluder_spec.rb +68 -0
  41. data/spec/unit/manifest_dependency_spec.rb +41 -0
  42. data/spec/unit/manifest_validator_spec.rb +35 -0
  43. data/spec/unit/packager/package_spec.rb +196 -0
  44. metadata +235 -0
@@ -0,0 +1,170 @@
1
+ require 'spec_helper'
2
+ require 'fileutils'
3
+
4
+ describe 'Buildpack packager default_versions validation' do
5
+ let(:flags) { '--uncached' }
6
+ let(:buildpack_dir) { Dir.mktmpdir }
7
+ let(:cache_dir) { Dir.mktmpdir }
8
+ let(:base_manifest_contents) { <<-BASE
9
+ language: python
10
+ url_to_dependency_map: []
11
+ exclude_files: []
12
+ BASE
13
+ }
14
+
15
+ before do
16
+ Dir.chdir(buildpack_dir) do
17
+ File.write('manifest.yml', manifest)
18
+ File.write('VERSION', '1.7.8')
19
+ end
20
+ end
21
+
22
+ after do
23
+ FileUtils.rm_rf(buildpack_dir)
24
+ FileUtils.rm_rf(cache_dir)
25
+ end
26
+
27
+ shared_examples_for "general output that helps with the error is produced" do
28
+ it "outputs a link to the Cloud Foundry custom buildpacks page" do
29
+ output, status = run_packager_binary(buildpack_dir, flags)
30
+ expect(output).to include("For more information, see https://docs.cloudfoundry.org/buildpacks/custom.html#specifying-default-versions")
31
+ end
32
+
33
+ it "states the buildpack manifest is malformed" do
34
+ output, status = run_packager_binary(buildpack_dir, flags)
35
+ expect(output).to include("The buildpack manifest is malformed:")
36
+ end
37
+ end
38
+
39
+ context 'defaults and dependencies are in agreement' do
40
+ let(:manifest) {<<-MANIFEST
41
+ #{base_manifest_contents}
42
+ default_versions:
43
+ - name: python
44
+ version: 3.3.5
45
+ dependencies:
46
+ - name: python
47
+ version: 3.3.5
48
+ uri: http://example.com/
49
+ md5: 68901bbf8a04e71e0b30aa19c3946b21
50
+ cf_stacks:
51
+ - cflinuxfs2
52
+ MANIFEST
53
+ }
54
+
55
+ it 'emits no errors' do
56
+ _, status = run_packager_binary(buildpack_dir, flags)
57
+
58
+ expect(status).to be_success
59
+ end
60
+ end
61
+
62
+ context 'multiple default versions for a dependency' do
63
+ let(:manifest) {<<-MANIFEST
64
+ #{base_manifest_contents}
65
+ default_versions:
66
+ - name: python
67
+ version: 3.3.5
68
+ - name: python
69
+ version: 3.3.2
70
+ - name: ruby
71
+ version: 4.3.5
72
+ - name: ruby
73
+ version: 4.3.2
74
+ dependencies:
75
+ - name: python
76
+ uri: https://a.org
77
+ version: 3.3.5
78
+ md5: 3a2
79
+ cf_stacks: [cflinuxfs2]
80
+ - name: python
81
+ uri: https://a.org
82
+ version: 3.3.2
83
+ md5: 3a2
84
+ cf_stacks: [cflinuxfs2]
85
+ - name: ruby
86
+ uri: https://a.org
87
+ version: 4.3.5
88
+ md5: 3a2
89
+ cf_stacks: [cflinuxfs2]
90
+ - name: ruby
91
+ uri: https://a.org
92
+ version: 4.3.2
93
+ md5: 3a2
94
+ cf_stacks: [cflinuxfs2]
95
+ MANIFEST
96
+ }
97
+
98
+ it_behaves_like "general output that helps with the error is produced"
99
+
100
+ it 'fails and errors stating the context' do
101
+ output, status = run_packager_binary(buildpack_dir, flags)
102
+
103
+ expect(output).to include("python had more " +
104
+ "than one 'default_versions' entry in the buildpack manifest.")
105
+ expect(output).to include("ruby had more " +
106
+ "than one 'default_versions' entry in the buildpack manifest.")
107
+ expect(status).to_not be_success
108
+ end
109
+ end
110
+
111
+ context 'no dependency with name found for default in manifest' do
112
+ let(:manifest) {<<-MANIFEST
113
+ #{base_manifest_contents}
114
+ default_versions:
115
+ - name: python
116
+ version: 3.3.5
117
+ - name: ruby
118
+ version: 4.3.5
119
+ dependencies: []
120
+ MANIFEST
121
+ }
122
+
123
+ it_behaves_like "general output that helps with the error is produced"
124
+
125
+ it 'fails and errors stating the context' do
126
+ output, status = run_packager_binary(buildpack_dir, flags)
127
+
128
+ expect(output).to include("a 'default_versions' entry for python 3.3.5 was specified by the buildpack manifest, " +
129
+ "but no 'dependencies' entry for python 3.3.5 was found in the buildpack manifest.")
130
+ expect(output).to include("a 'default_versions' entry for ruby 4.3.5 was specified by the buildpack manifest, " +
131
+ "but no 'dependencies' entry for ruby 4.3.5 was found in the buildpack manifest.")
132
+ expect(status).to_not be_success
133
+ end
134
+ end
135
+
136
+ context 'no dependency with version found for default in manifest' do
137
+ let(:manifest) {<<-MANIFEST
138
+ #{base_manifest_contents}
139
+ default_versions:
140
+ - name: ruby
141
+ version: 1.1.1
142
+ - name: python
143
+ version: 3.3.5
144
+ dependencies:
145
+ - name: ruby
146
+ uri: https://a.org
147
+ version: 9.9.9
148
+ md5: 3a2
149
+ cf_stacks: [cflinuxfs2]
150
+ - name: python
151
+ uri: https://a.org
152
+ version: 9.9.9
153
+ md5: 3a2
154
+ cf_stacks: [cflinuxfs2]
155
+ MANIFEST
156
+ }
157
+
158
+ it_behaves_like "general output that helps with the error is produced"
159
+
160
+ it 'fails and errors stating the context' do
161
+ output, status = run_packager_binary(buildpack_dir, flags)
162
+
163
+ expect(output).to include("a 'default_versions' entry for python 3.3.5 was specified by the buildpack manifest, " +
164
+ "but no 'dependencies' entry for python 3.3.5 was found in the buildpack manifest.")
165
+ expect(output).to include("a 'default_versions' entry for ruby 1.1.1 was specified by the buildpack manifest, " +
166
+ "but no 'dependencies' entry for ruby 1.1.1 was found in the buildpack manifest.")
167
+ expect(status).to_not be_success
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,70 @@
1
+ require 'spec_helper'
2
+ require 'fileutils'
3
+
4
+ describe 'Buildpack packager output' do
5
+ let(:buildpack_type) { '--uncached' }
6
+ let(:fixture_name) { 'buildpack-without-uri-credentials' }
7
+ let(:buildpack_fixture) { File.join(File.dirname(__FILE__), '..', 'fixtures', fixture_name) }
8
+ let(:tmpdir) { Dir.mktmpdir }
9
+
10
+ before do
11
+ FileUtils.rm_rf(tmpdir)
12
+ end
13
+
14
+ def buildpack_packager_execute(buildpack_dir, home_dir)
15
+ Dir.chdir(buildpack_dir) do
16
+ `HOME=#{home_dir} buildpack-packager #{buildpack_type}`
17
+ end
18
+ end
19
+
20
+ subject { buildpack_packager_execute(buildpack_fixture, tmpdir) }
21
+
22
+ context 'building the uncached buildpack' do
23
+ it 'outputs the type of buildpack created, where and its human readable size' do
24
+ expect(subject).to include("Uncached buildpack created and saved as")
25
+ expect(subject).to include("spec/fixtures/#{fixture_name}/go_buildpack-v1.7.8.zip")
26
+ expect(subject).to match(/with a size of 4\.0K$/)
27
+ end
28
+ end
29
+
30
+ context 'building the cached buildpack' do
31
+ let(:buildpack_type) { '--cached' }
32
+
33
+ it 'outputs the dependencies downloaded, their versions, and download source url' do
34
+ expect(subject).to include("Downloading go version 1.6.3 from: https://buildpacks.cloudfoundry.org/concourse-binaries/go/go1.6.3.linux-amd64.tar.gz")
35
+ expect(subject).to include("Using go version 1.6.3 with size")
36
+ expect(subject).to include("go version 1.6.3 matches the manifest provided md5 checksum of 5f7bf9d61d2b0dd75c9e2cd7a87272cc")
37
+
38
+ expect(subject).to include("Downloading godep version v74 from: https://pivotal-buildpacks.s3.amazonaws.com/concourse-binaries/godep/godep-v74-linux-x64.tgz")
39
+ expect(subject).to include("Using godep version v74 with size 2.8M")
40
+ expect(subject).to include("godep version v74 matches the manifest provided md5 checksum of 70220eee9f9e654e0b85887f696b6add")
41
+ end
42
+
43
+ it 'outputs the type of buildpack created, where and its human readable size' do
44
+ expect(subject).to include("Cached buildpack created and saved as")
45
+ expect(subject).to include("spec/fixtures/#{fixture_name}/go_buildpack-cached-v1.7.8.zip")
46
+ expect(subject).to match(/with a size of 68M$/)
47
+ end
48
+
49
+ context 'with a buildpack packager dependency cache intact' do
50
+ before { buildpack_packager_execute(buildpack_fixture, tmpdir) }
51
+
52
+ it 'outputs the dependencies downloaded, their versions, and cache location' do
53
+ expect(subject).to include("Using go version 1.6.3 from local cache at: #{tmpdir}/.buildpack-packager/cache/https___buildpacks.cloudfoundry.org_concourse-binaries_go_go1.6.3.linux-amd64.tar.gz with size")
54
+ expect(subject).to include("go version 1.6.3 matches the manifest provided md5 checksum of 5f7bf9d61d2b0dd75c9e2cd7a87272cc")
55
+
56
+ expect(subject).to include("Using godep version v74 from local cache at: #{tmpdir}/.buildpack-packager/cache/https___pivotal-buildpacks.s3.amazonaws.com_concourse-binaries_godep_godep-v74-linux-x64.tgz with size")
57
+ expect(subject).to include("godep version v74 matches the manifest provided md5 checksum of 70220eee9f9e654e0b85887f696b6add")
58
+ end
59
+ end
60
+
61
+ context 'with auth credentials in the dependency uri' do
62
+ let(:fixture_name) { 'buildpack-with-uri-credentials' }
63
+
64
+ it 'outputs the dependencies download source url without the credentials' do
65
+ expect(subject).to include('Downloading go version 1.6.3 from: https://-redacted-:-redacted-@buildpacks.cloudfoundry.org/concourse-binaries/go/go1.6.3.linux-amd64.tar.gz')
66
+ expect(subject).to include('Downloading godep version v74 from: https://-redacted-:-redacted-@buildpacks.cloudfoundry.org/concourse-binaries/godep/godep-v74-linux-x64.tgz')
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,12 @@
1
+ $LOAD_PATH << File.expand_path('../../lib', __FILE__)
2
+ $LOAD_PATH << File.expand_path('../../spec/helpers', __FILE__)
3
+ require 'buildpack/packager'
4
+ require 'file_system_helpers'
5
+ require 'cache_directory_helpers'
6
+ require 'fake_binary_hosting_helpers'
7
+
8
+ RSpec.configure do |config|
9
+ config.include FileSystemHelpers
10
+ config.include CacheDirectoryHelpers
11
+ config.include FakeBinaryHostingHelpers
12
+ end
@@ -0,0 +1,68 @@
1
+ require 'buildpack/packager/zip_file_excluder'
2
+ require 'spec_helper'
3
+ require 'tmpdir'
4
+ require 'securerandom'
5
+
6
+ module Buildpack
7
+ module Packager
8
+ describe ZipFileExcluder do
9
+ describe '#generate_exclusions_from_manifest' do
10
+ let(:excluder) { ZipFileExcluder.new }
11
+ let(:file1) { SecureRandom.uuid }
12
+ let(:file2) { SecureRandom.uuid }
13
+ let(:dir) { "#{SecureRandom.uuid}/" }
14
+ let(:excluded_files) { [file1, file2] }
15
+ subject do
16
+ excluder.generate_manifest_exclusions excluded_files
17
+ end
18
+
19
+ context 'does not include directories' do
20
+ it do
21
+ is_expected.to eq "-x #{file1} -x \\*/#{file1} -x #{file2} -x \\*/#{file2}"
22
+ end
23
+ end
24
+
25
+ context 'includes directories' do
26
+ let(:excluded_files) { [file1, dir] }
27
+ it do
28
+ is_expected.to eq "-x #{file1} -x \\*/#{file1} -x #{dir}\\* -x \\*/#{dir}\\*"
29
+ end
30
+ end
31
+
32
+ it 'uses short flags to support zip 2.3.*, see [#107948062]' do
33
+ is_expected.to include('-x')
34
+ is_expected.to_not include('--exclude')
35
+ end
36
+ end
37
+ end
38
+
39
+ describe '#generate_exclusions_from_git_files' do
40
+ let(:excluder) { ZipFileExcluder.new }
41
+
42
+ context 'git files exist' do
43
+ let (:files) { ['.gitignore', '.git/', '.gitmodules', 'lib/.git'] }
44
+
45
+ it 'returns an exclusion string with all the git files' do
46
+ Dir.mktmpdir do |dir|
47
+ Dir.mkdir "#{dir}/lib"
48
+
49
+ files.each do |gitfilename|
50
+ if gitfilename =~ /.*\/$/
51
+ Dir.mkdir "#{dir}/#{gitfilename}"
52
+ else
53
+ File.new "#{dir}/#{gitfilename}", 'w'
54
+ end
55
+ end
56
+
57
+ git_exclusions = excluder.generate_exclusions_from_git_files dir
58
+
59
+ expect(git_exclusions).to include '-x .gitignore -x \\*/.gitignore'
60
+ expect(git_exclusions).to include '-x lib/.git -x \\*/lib/.git'
61
+ expect(git_exclusions).to include '-x .gitmodules -x \\*/.gitmodules'
62
+ expect(git_exclusions).to include '-x .git/\\* -x \\*/.git/\\*'
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+ require 'buildpack/manifest_dependency'
3
+
4
+ describe Buildpack::ManifestDependency do
5
+ describe '#==' do
6
+ context 'two objects with the same name and same version' do
7
+ let(:first_dependency) { described_class.new('name', 'version') }
8
+ let(:second_dependency) { described_class.new('name', 'version') }
9
+
10
+ it 'returns true' do
11
+ expect(first_dependency == second_dependency).to be true
12
+ end
13
+ end
14
+ context 'two objects with the same name only' do
15
+ let(:first_dependency) { described_class.new('name', 'version 11111111') }
16
+ let(:second_dependency) { described_class.new('name', 'version 22222222') }
17
+
18
+ it 'returns false' do
19
+ expect(first_dependency == second_dependency).to be false
20
+ end
21
+ end
22
+
23
+ context 'two objects with the same version only' do
24
+ let(:first_dependency) { described_class.new('name 111', 'version') }
25
+ let(:second_dependency) { described_class.new('name 222', 'version') }
26
+
27
+ it 'returns false' do
28
+ expect(first_dependency == second_dependency).to be false
29
+ end
30
+ end
31
+
32
+ context 'two objects with different names and different versions' do
33
+ let(:first_dependency) { described_class.new('name 111111', 'version 11111111') }
34
+ let(:second_dependency) { described_class.new('name 222222', 'version 22222222') }
35
+
36
+ it 'returns false' do
37
+ expect(first_dependency == second_dependency).to be false
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+ require 'buildpack/manifest_validator'
3
+
4
+ describe Buildpack::ManifestValidator do
5
+ let(:manifest_path) { "#{File.dirname(__FILE__)}/../fixtures/manifests/#{manifest_file_name}" }
6
+ let(:validator) { Buildpack::ManifestValidator.new(manifest_path) }
7
+
8
+ context 'with a valid manifest' do
9
+ let(:manifest_file_name) { 'manifest_valid.yml' }
10
+
11
+ it 'reports valid manifests correctly' do
12
+ expect(validator.valid?).to be(true)
13
+ expect(validator.errors).to be_empty
14
+ end
15
+ end
16
+
17
+ context 'with a manifest with an invalid md5 key' do
18
+ let(:manifest_file_name) { 'manifest_invalid-md6.yml' }
19
+
20
+ it 'reports invalid manifests correctly' do
21
+ expect(validator.valid?).to be(false)
22
+ expect(validator.errors[:manifest_parser_errors]).not_to be_empty
23
+ end
24
+
25
+ context 'and incorrect defaults' do
26
+ let(:manifest_file_name) { 'manifest_invalid-md6_and_defaults.yml' }
27
+
28
+ it 'reports manifest parser errors only' do
29
+ expect(validator).to_not receive(:validate_default_versions)
30
+ expect(validator.valid?).to be(false)
31
+ expect(validator.errors[:manifest_parser_errors]).not_to be_empty
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,196 @@
1
+ require 'spec_helper'
2
+ require 'tmpdir'
3
+
4
+ module Buildpack
5
+ module Packager
6
+ describe Package do
7
+ let(:packager) { Buildpack::Packager::Package.new(options) }
8
+ let(:manifest_path) { 'manifest.yml' }
9
+ let(:dependency) { double(:dependency) }
10
+ let(:mode) { :uncached }
11
+ let(:local_cache_dir) { nil }
12
+ let(:force_download) { false }
13
+ let(:options) do
14
+ {
15
+ root_dir: 'root_dir',
16
+ mode: mode,
17
+ cache_dir: local_cache_dir,
18
+ manifest_path: manifest_path,
19
+ force_download: force_download
20
+ }
21
+ end
22
+
23
+ let(:manifest) do
24
+ {
25
+ language: 'fake_language',
26
+ dependencies: [dependency],
27
+ exclude_files: ['.DS_Store', '.gitignore']
28
+ }
29
+ end
30
+
31
+ before do
32
+ allow(YAML).to receive(:load_file).and_return(manifest)
33
+ end
34
+
35
+ describe '#copy_buildpack_to_temp_dir' do
36
+ context 'with full manifest specified' do
37
+ let(:manifest_path) { 'manifest-including-unsupported.yml' }
38
+
39
+ before do
40
+ allow(FileUtils).to receive(:mv)
41
+ allow(FileUtils).to receive(:cp_r)
42
+ allow(FileUtils).to receive(:cp)
43
+ allow(FileUtils).to receive(:rm)
44
+ end
45
+
46
+ it 'replaces the default manifest with the full manifest' do
47
+ expect(FileUtils).to receive(:cp).with('manifest-including-unsupported.yml', File.join('hello_dir', 'manifest.yml'))
48
+ packager.copy_buildpack_to_temp_dir('hello_dir')
49
+ end
50
+ end
51
+ end
52
+
53
+ describe '#build_dependencies' do
54
+ let(:mode) { :cached }
55
+
56
+ before do
57
+ allow(FileUtils).to receive(:mkdir_p)
58
+ allow(packager).to receive(:download_dependencies)
59
+ end
60
+
61
+ context 'when cache_dir is provided' do
62
+ let(:local_cache_dir) { 'local_cache_dir' }
63
+
64
+ it 'creates the provided cache dir' do
65
+ expect(FileUtils).to receive(:mkdir_p).with(local_cache_dir)
66
+ packager.build_dependencies('hello_dir')
67
+ end
68
+
69
+ it 'creates the dependency dir' do
70
+ expect(FileUtils).to receive(:mkdir_p).with(File.join('hello_dir', 'dependencies'))
71
+ packager.build_dependencies('hello_dir')
72
+ end
73
+
74
+ it 'calls download_dependencies with right arguments' do
75
+ expect(packager).to receive(:download_dependencies).with([dependency], local_cache_dir, File.join('hello_dir', 'dependencies'))
76
+ packager.build_dependencies('hello_dir')
77
+ end
78
+ end
79
+
80
+ context 'when cache_dir is NOT provided' do
81
+ it 'creates the default cache dir' do
82
+ expect(FileUtils).to receive(:mkdir_p).with(File.join(ENV['HOME'], '.buildpack-packager', 'cache'))
83
+ packager.build_dependencies('hello_dir')
84
+ end
85
+ end
86
+ end
87
+
88
+ describe '#download_dependencies' do
89
+ let(:local_cache_dir) { 'local_cache_dir' }
90
+ let(:dependency_dir) { File.join('hello_dir', 'dependencies') }
91
+ let(:url_with_parameters) { 'http://some.cdn/with?parameters=true&secondParameter=present' }
92
+
93
+ before do
94
+ allow(dependency).to receive(:[])
95
+ allow(dependency).to receive(:[]).with('uri').and_return('file:///fake_uri.tgz')
96
+ allow(packager).to receive(:ensure_correct_dependency_checksum)
97
+ allow(FileUtils).to receive(:cp)
98
+ allow(packager).to receive(:download_file)
99
+ end
100
+
101
+ context 'before dependency has been cached locally' do
102
+ before do
103
+ allow(File).to receive(:exist?).and_return(false)
104
+ end
105
+
106
+ it 'downloads the dependency to the local cache' do
107
+ expanded_local_file_location = File.expand_path(File.join('local_cache_dir', 'file____fake_uri.tgz'))
108
+ expect(packager).to receive(:download_file).with('file:///fake_uri.tgz', expanded_local_file_location)
109
+ packager.download_dependencies([dependency], local_cache_dir, dependency_dir)
110
+ end
111
+
112
+ it 'copies the dependency from the local cache to the dependency_dir' do
113
+ expanded_local_file_location = File.expand_path(File.join('local_cache_dir', 'file____fake_uri.tgz'))
114
+ expect(FileUtils).to receive(:cp).with(expanded_local_file_location, dependency_dir)
115
+ packager.download_dependencies([dependency], local_cache_dir, dependency_dir)
116
+ end
117
+ end
118
+
119
+ context 'after dependency has been cached locally' do
120
+ before do
121
+ allow(File).to receive(:exist?).and_return(true)
122
+ end
123
+
124
+ it 'does not re-download the dependency' do
125
+ expect(packager).not_to receive(:download_file)
126
+ packager.download_dependencies([dependency], local_cache_dir, dependency_dir)
127
+ end
128
+
129
+ it 'copies the dependency from the local cache to the dependency_dir' do
130
+ expanded_local_file_location = File.expand_path(File.join('local_cache_dir', 'file____fake_uri.tgz'))
131
+ expect(FileUtils).to receive(:cp).with(expanded_local_file_location, dependency_dir)
132
+ packager.download_dependencies([dependency], local_cache_dir, dependency_dir)
133
+ end
134
+ end
135
+
136
+ context 'with :force_download option active and a locally cached dependency' do
137
+ let(:force_download) { true }
138
+
139
+ before do
140
+ allow(File).to receive(:exist?).and_return(true)
141
+ end
142
+
143
+ it 're-downloads the dependency anyway' do
144
+ expect(packager).to receive(:download_file)
145
+ packager.download_dependencies([dependency], local_cache_dir, dependency_dir)
146
+ end
147
+ end
148
+
149
+ it 'translates ? and & characters in the url to underscores' do
150
+ package = Package.new
151
+ expect(package.send(:uri_cache_path, url_with_parameters)).to eq("http___some.cdn_with_parameters=true_secondParameter=present")
152
+ end
153
+
154
+ context 'url has login and password authentication credentials' do
155
+ let(:url_with_credentials) { 'http://log!i213:pas!9sword@some.cdn/with' }
156
+
157
+ it 'redacts the credentials in the resulting file path' do
158
+ package = Package.new
159
+ expect(package.send(:uri_without_credentials, url_with_credentials)).to eq("http://-redacted-:-redacted-@some.cdn/with")
160
+ end
161
+ end
162
+
163
+ context 'url has a login authentication credential' do
164
+ let(:url_with_credentials) { 'http://log!i213@some.cdn/with' }
165
+
166
+ it 'redacts the credential in the resulting file path' do
167
+ package = Package.new
168
+ expect(package.send(:uri_without_credentials, url_with_credentials)).to eq("http://-redacted-@some.cdn/with")
169
+ end
170
+ end
171
+ end
172
+
173
+ describe '#build_zip_file' do
174
+ before do
175
+ allow(packager).to receive(:buildpack_version).and_return('1.0.0')
176
+ allow(FileUtils).to receive(:rm_rf)
177
+ allow(packager).to receive(:zip_files)
178
+ allow(packager).to receive(:zip_file_path)
179
+ .and_return(File.join('root_dir', 'fake_language_buildpack-v1.0.0.zip'))
180
+ end
181
+
182
+ it 'removes the file at the zip file path' do
183
+ zip_file_path = File.join('root_dir', 'fake_language_buildpack-v1.0.0.zip')
184
+ expect(FileUtils).to receive(:rm_rf).with(zip_file_path)
185
+ packager.build_zip_file('hello_dir')
186
+ end
187
+
188
+ it 'zips up the temp directory to the zip file path without the excluded files' do
189
+ zip_file_path = File.join('root_dir', 'fake_language_buildpack-v1.0.0.zip')
190
+ expect(packager).to receive(:zip_files).with('hello_dir', zip_file_path, ['.DS_Store', '.gitignore'])
191
+ packager.build_zip_file('hello_dir')
192
+ end
193
+ end
194
+ end
195
+ end
196
+ end