buildpack-packager 2.3.4

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.
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