buildpack-packager 2.3.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.rspec +2 -0
- data/Gemfile +5 -0
- data/LICENSE +176 -0
- data/README.md +180 -0
- data/Rakefile +6 -0
- data/bin/buildpack-packager +79 -0
- data/buildpack-packager.gemspec +33 -0
- data/doc/disconnected_environments.md +50 -0
- data/lib/buildpack/manifest_dependency.rb +14 -0
- data/lib/buildpack/manifest_validator.rb +88 -0
- data/lib/buildpack/packager.rb +55 -0
- data/lib/buildpack/packager/default_versions_presenter.rb +38 -0
- data/lib/buildpack/packager/dependencies_presenter.rb +41 -0
- data/lib/buildpack/packager/manifest_schema.yml +67 -0
- data/lib/buildpack/packager/package.rb +139 -0
- data/lib/buildpack/packager/table_presentation.rb +21 -0
- data/lib/buildpack/packager/version.rb +5 -0
- data/lib/buildpack/packager/zip_file_excluder.rb +31 -0
- data/lib/kwalify/parser/yaml-patcher.rb +70 -0
- data/spec/buildpack/packager_spec.rb +7 -0
- data/spec/fixtures/buildpack-with-uri-credentials/VERSION +1 -0
- data/spec/fixtures/buildpack-with-uri-credentials/manifest.yml +36 -0
- data/spec/fixtures/buildpack-without-uri-credentials/VERSION +1 -0
- data/spec/fixtures/buildpack-without-uri-credentials/manifest.yml +36 -0
- data/spec/fixtures/manifests/manifest_invalid-md6.yml +18 -0
- data/spec/fixtures/manifests/manifest_invalid-md6_and_defaults.yml +21 -0
- data/spec/fixtures/manifests/manifest_valid.yml +19 -0
- data/spec/helpers/cache_directory_helpers.rb +15 -0
- data/spec/helpers/fake_binary_hosting_helpers.rb +21 -0
- data/spec/helpers/file_system_helpers.rb +35 -0
- data/spec/integration/bin/buildpack_packager/download_caching_spec.rb +85 -0
- data/spec/integration/bin/buildpack_packager_spec.rb +378 -0
- data/spec/integration/buildpack/directory_name_spec.rb +89 -0
- data/spec/integration/buildpack/packager_spec.rb +454 -0
- data/spec/integration/default_versions_spec.rb +170 -0
- data/spec/integration/output_spec.rb +70 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/unit/buildpack/packager/zip_file_excluder_spec.rb +68 -0
- data/spec/unit/manifest_dependency_spec.rb +41 -0
- data/spec/unit/manifest_validator_spec.rb +35 -0
- data/spec/unit/packager/package_spec.rb +196 -0
- 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
|
data/spec/spec_helper.rb
ADDED
@@ -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
|