omnibus 4.0.0 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (111) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +8 -0
  3. data/.travis.yml +3 -11
  4. data/CHANGELOG.md +50 -0
  5. data/MAINTAINERS.md +26 -0
  6. data/README.md +61 -4
  7. data/appveyor.yml +35 -0
  8. data/docs/Build Cache.md +28 -3
  9. data/docs/Building on RHEL.md +1 -1
  10. data/features/commands/publish.feature +4 -9
  11. data/features/step_definitions/generator_steps.rb +14 -1
  12. data/features/support/env.rb +5 -3
  13. data/lib/omnibus.rb +10 -0
  14. data/lib/omnibus/build_version.rb +34 -25
  15. data/lib/omnibus/build_version_dsl.rb +43 -4
  16. data/lib/omnibus/builder.rb +30 -11
  17. data/lib/omnibus/changelog.rb +52 -0
  18. data/lib/omnibus/changelog_printer.rb +77 -0
  19. data/lib/omnibus/cli.rb +37 -2
  20. data/lib/omnibus/cli/changelog.rb +149 -0
  21. data/lib/omnibus/cli/publish.rb +30 -10
  22. data/lib/omnibus/config.rb +41 -2
  23. data/lib/omnibus/digestable.rb +6 -1
  24. data/lib/omnibus/exceptions.rb +15 -1
  25. data/lib/omnibus/fetcher.rb +78 -34
  26. data/lib/omnibus/fetchers/git_fetcher.rb +84 -42
  27. data/lib/omnibus/fetchers/net_fetcher.rb +64 -13
  28. data/lib/omnibus/fetchers/null_fetcher.rb +8 -1
  29. data/lib/omnibus/fetchers/path_fetcher.rb +24 -1
  30. data/lib/omnibus/file_syncer.rb +52 -1
  31. data/lib/omnibus/generator.rb +22 -21
  32. data/lib/omnibus/generator_files/.kitchen.yml.erb +8 -12
  33. data/lib/omnibus/generator_files/Berksfile.erb +4 -4
  34. data/lib/omnibus/generator_files/Gemfile.erb +3 -3
  35. data/lib/omnibus/generator_files/README.md.erb +17 -0
  36. data/lib/omnibus/generator_files/omnibus.rb.erb +6 -0
  37. data/lib/omnibus/git_repository.rb +43 -0
  38. data/lib/omnibus/health_check.rb +5 -1
  39. data/lib/omnibus/manifest.rb +134 -0
  40. data/lib/omnibus/manifest_diff.rb +88 -0
  41. data/lib/omnibus/manifest_entry.rb +43 -0
  42. data/lib/omnibus/metadata.rb +19 -1
  43. data/lib/omnibus/package.rb +9 -0
  44. data/lib/omnibus/packagers/base.rb +1 -1
  45. data/lib/omnibus/packagers/bff.rb +5 -5
  46. data/lib/omnibus/packagers/deb.rb +11 -4
  47. data/lib/omnibus/packagers/msi.rb +243 -2
  48. data/lib/omnibus/packagers/rpm.rb +68 -14
  49. data/lib/omnibus/packagers/solaris.rb +17 -23
  50. data/lib/omnibus/project.rb +129 -16
  51. data/lib/omnibus/publisher.rb +62 -49
  52. data/lib/omnibus/publishers/artifactory_publisher.rb +96 -5
  53. data/lib/omnibus/publishers/s3_publisher.rb +20 -25
  54. data/lib/omnibus/s3_cache.rb +13 -34
  55. data/lib/omnibus/s3_helpers.rb +119 -0
  56. data/lib/omnibus/semantic_version.rb +57 -0
  57. data/lib/omnibus/software.rb +87 -28
  58. data/lib/omnibus/sugarable.rb +18 -0
  59. data/lib/omnibus/templating.rb +8 -1
  60. data/lib/omnibus/version.rb +1 -1
  61. data/omnibus.gemspec +10 -7
  62. data/resources/bff/gen.template.erb +1 -1
  63. data/resources/msi/bundle.wxs.erb +17 -0
  64. data/resources/msi/localization-en-us.wxl.erb +1 -1
  65. data/resources/rpm/spec.erb +1 -1
  66. data/spec/functional/builder_spec.rb +15 -7
  67. data/spec/functional/fetchers/git_fetcher_spec.rb +44 -12
  68. data/spec/functional/fetchers/net_fetcher_spec.rb +171 -20
  69. data/spec/functional/fetchers/path_fetcher_spec.rb +16 -1
  70. data/spec/functional/file_syncer_spec.rb +58 -5
  71. data/spec/functional/templating_spec.rb +17 -6
  72. data/spec/spec_helper.rb +17 -0
  73. data/spec/support/file_helpers.rb +12 -2
  74. data/spec/support/git_helpers.rb +23 -18
  75. data/spec/support/matchers.rb +22 -0
  76. data/spec/support/output_helpers.rb +29 -0
  77. data/spec/unit/build_version_dsl_spec.rb +31 -4
  78. data/spec/unit/build_version_spec.rb +11 -4
  79. data/spec/unit/builder_spec.rb +33 -0
  80. data/spec/unit/changelog_spec.rb +55 -0
  81. data/spec/unit/cleanroom_spec.rb +1 -1
  82. data/spec/unit/compressors/dmg_spec.rb +3 -3
  83. data/spec/unit/compressors/tgz_spec.rb +3 -3
  84. data/spec/unit/config_spec.rb +3 -1
  85. data/spec/unit/fetcher_spec.rb +35 -0
  86. data/spec/unit/fetchers/git_fetcher_spec.rb +28 -14
  87. data/spec/unit/fetchers/net_fetcher_spec.rb +178 -24
  88. data/spec/unit/fetchers/path_fetcher_spec.rb +8 -7
  89. data/spec/unit/generator_spec.rb +22 -21
  90. data/spec/unit/git_repository_spec.rb +60 -0
  91. data/spec/unit/manifest_diff_spec.rb +75 -0
  92. data/spec/unit/manifest_spec.rb +116 -0
  93. data/spec/unit/metadata_spec.rb +11 -0
  94. data/spec/unit/omnibus_spec.rb +9 -6
  95. data/spec/unit/package_spec.rb +1 -1
  96. data/spec/unit/packagers/base_spec.rb +8 -18
  97. data/spec/unit/packagers/bff_spec.rb +9 -5
  98. data/spec/unit/packagers/deb_spec.rb +44 -12
  99. data/spec/unit/packagers/makeself_spec.rb +4 -4
  100. data/spec/unit/packagers/msi_spec.rb +122 -6
  101. data/spec/unit/packagers/pkg_spec.rb +3 -3
  102. data/spec/unit/packagers/rpm_spec.rb +37 -12
  103. data/spec/unit/project_spec.rb +18 -1
  104. data/spec/unit/publisher_spec.rb +65 -0
  105. data/spec/unit/publishers/artifactory_publisher_spec.rb +26 -20
  106. data/spec/unit/publishers/s3_publisher_spec.rb +14 -30
  107. data/spec/unit/s3_cacher_spec.rb +1 -1
  108. data/spec/unit/s3_helpers_spec.rb +32 -0
  109. data/spec/unit/semantic_version_spec.rb +55 -0
  110. data/spec/unit/software_spec.rb +112 -4
  111. metadata +86 -16
@@ -9,6 +9,7 @@ module Omnibus
9
9
  subject(:build_version) { described_class.new }
10
10
 
11
11
  before do
12
+ Config.append_timestamp(true)
12
13
  allow_any_instance_of(described_class).to receive(:shellout)
13
14
  .and_return(double('ouput', stdout: git_describe, exitstatus: 0))
14
15
  end
@@ -88,6 +89,16 @@ module Omnibus
88
89
  its(:commits_since_tag) { should == 0 }
89
90
  its(:prerelease_version?) { should be_truthy }
90
91
  end
92
+
93
+ # v-prefixed tag
94
+ context "v1.2.3-beta2" do
95
+ let(:git_describe) { "v1.2.3-beta2" }
96
+ its(:version_tag) { should == "1.2.3" }
97
+ its(:prerelease_tag) { should == "beta2" }
98
+ its(:git_sha_tag) { should be(nil) }
99
+ its(:commits_since_tag) { should eq(0) }
100
+ its(:prerelease_version?) { should be(true) }
101
+ end
91
102
  end
92
103
 
93
104
  describe 'semver output' do
@@ -129,7 +140,6 @@ module Omnibus
129
140
 
130
141
  describe 'appending a timestamp' do
131
142
  let(:git_describe) { '11.0.0-alpha-3-207-g694b062' }
132
-
133
143
  context 'by default' do
134
144
  it 'appends a timestamp' do
135
145
  expect(build_version.semver).to match(/11.0.0-alpha.3\+#{today_string}[0-9]+.git.207.694b062/)
@@ -137,8 +147,6 @@ module Omnibus
137
147
  end
138
148
 
139
149
  context 'when Config.append_timestamp is true' do
140
- before { Config.append_timestamp(true) }
141
-
142
150
  it 'appends a timestamp' do
143
151
  expect(build_version.semver).to match(/11.0.0-alpha.3\+#{today_string}[0-9]+.git.207.694b062/)
144
152
  end
@@ -146,7 +154,6 @@ module Omnibus
146
154
 
147
155
  context 'when Config.append_timestamp is false' do
148
156
  before { Config.append_timestamp(false) }
149
-
150
157
  it 'does not append a timestamp' do
151
158
  expect(build_version.semver).to match(/11.0.0-alpha.3\+git.207.694b062/)
152
159
  end
@@ -180,5 +180,38 @@ module Omnibus
180
180
  subject.make(timeout: 3600)
181
181
  end
182
182
  end
183
+
184
+ describe "#shasum" do
185
+ let(:build_step) do
186
+ Proc.new {
187
+ block do
188
+ command("true")
189
+ end
190
+ }
191
+ end
192
+
193
+ let(:tmp_dir) { Dir.mktmpdir }
194
+ after { FileUtils.rmdir(tmp_dir) }
195
+
196
+ let(:software) do
197
+ double(Software,
198
+ name: 'chefdk',
199
+ install_dir: tmp_dir,
200
+ project_dir: tmp_dir,
201
+ overridden?: false)
202
+ end
203
+
204
+ let(:before_build_shasum) do
205
+ b = described_class.new(software)
206
+ b.evaluate(&build_step)
207
+ b.shasum
208
+ end
209
+
210
+ it "returns the same value when called before or after the build" do
211
+ subject.evaluate(&build_step)
212
+ subject.build
213
+ expect(subject.shasum).to eq(before_build_shasum)
214
+ end
215
+ end
183
216
  end
184
217
  end
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+
3
+ module Omnibus
4
+ describe ChangeLog do
5
+ describe "#new" do
6
+ it "sets the start_ref to the latest tag if none is set" do
7
+ repo = double(GitRepository, :latest_tag => "1.0")
8
+ expect(ChangeLog.new(nil, "2.0", repo).start_ref).to eq("1.0")
9
+ end
10
+
11
+ it "sets the end_ref to HEAD if none is set" do
12
+ expect(ChangeLog.new.end_ref).to eq("HEAD")
13
+ end
14
+ end
15
+
16
+ describe "#changelog_entries" do
17
+ it "returns any git log lines with the ChangeLog: tag, removing the tag" do
18
+ repo = double(GitRepository, :commit_messages => ["ChangeLog-Entry: foobar\n",
19
+ "ChangeLog-Entry: wombat\n"])
20
+ changelog = ChangeLog.new("0.0.1", "0.0.2", repo)
21
+ expect(changelog.changelog_entries).to eq(["foobar\n", "wombat\n"])
22
+ end
23
+
24
+ it "returns an empty array if there were no changelog entries" do
25
+ repo = double(GitRepository, :commit_messages => [])
26
+ changelog = ChangeLog.new("0.0.1", "0.0.2", repo)
27
+ expect(changelog.changelog_entries).to eq([])
28
+ end
29
+
30
+ it "does not return git messages without a ChangeLog: tag" do
31
+ repo = double(GitRepository, :commit_messages => ["foobar\n", "wombat\n"])
32
+ changelog = ChangeLog.new("0.0.1", "0.0.2", repo)
33
+ expect(changelog.changelog_entries).to eq([])
34
+ end
35
+
36
+ it "does not return blank lines" do
37
+ repo = double(GitRepository, :commit_messages => ["\n", "\n"])
38
+ changelog = ChangeLog.new("0.0.1", "0.0.2", repo)
39
+ expect(changelog.changelog_entries).to eq([])
40
+ end
41
+
42
+ it "can handle multi-line ChangeLog entries" do
43
+ repo = double(GitRepository, :commit_messages => ["ChangeLog-Entry: foobar\n", "foobaz\n"])
44
+ changelog = ChangeLog.new("0.0.1", "0.0.2", repo)
45
+ expect(changelog.changelog_entries).to eq(["foobar\nfoobaz\n"])
46
+ end
47
+
48
+ it "end a ChangeLog entry at the first blank line" do
49
+ repo = double(GitRepository, :commit_messages => ["ChangeLog-Entry: foobar\n", "\n", "foobaz\n"])
50
+ changelog = ChangeLog.new("0.0.1", "0.0.2", repo)
51
+ expect(changelog.changelog_entries).to eq(["foobar\n"])
52
+ end
53
+ end
54
+ end
55
+ end
@@ -43,7 +43,7 @@ module Omnibus
43
43
  EOH
44
44
  end
45
45
 
46
- let(:filepath) { '/file/path' }
46
+ let(:filepath) { File.join(tmp_path, '/file/path') }
47
47
 
48
48
  before do
49
49
  allow(IO).to receive(:read).and_call_original
@@ -15,9 +15,9 @@ module Omnibus
15
15
 
16
16
  subject { described_class.new(project) }
17
17
 
18
- let(:project_root) { "#{tmp_path}/project/root" }
19
- let(:package_dir) { "#{tmp_path}/package/dir" }
20
- let(:staging_dir) { "#{tmp_path}/staging/dir" }
18
+ let(:project_root) { File.join(tmp_path, 'project/root') }
19
+ let(:package_dir) { File.join(tmp_path, 'package/dir') }
20
+ let(:staging_dir) { File.join(tmp_path, 'staging/dir') }
21
21
 
22
22
  before do
23
23
  allow(project).to receive(:packager)
@@ -15,9 +15,9 @@ module Omnibus
15
15
 
16
16
  subject { described_class.new(project) }
17
17
 
18
- let(:project_root) { "#{tmp_path}/project/root" }
19
- let(:package_dir) { "#{tmp_path}/package/dir" }
20
- let(:staging_dir) { "#{tmp_path}/staging/dir" }
18
+ let(:project_root) { File.join(tmp_path, 'project/root') }
19
+ let(:package_dir) { File.join(tmp_path, 'package/dir') }
20
+ let(:staging_dir) { File.join(tmp_path, 'staging/dir') }
21
21
 
22
22
  before do
23
23
  create_directory(project_root)
@@ -9,6 +9,7 @@ module Omnibus
9
9
  end
10
10
 
11
11
  before do
12
+ Config.reset!
12
13
  # Don't expand paths on the build system. Otherwise, you will end up with
13
14
  # paths like +\\Users\\you\\Development\\omnibus-ruby\\C:/omnibus-ruby+
14
15
  # when testing on "other" operating systems
@@ -37,11 +38,12 @@ module Omnibus
37
38
  include_examples 'a configurable', :project_root, Dir.pwd
38
39
  include_examples 'a configurable', :local_software_dirs, []
39
40
  include_examples 'a configurable', :software_gems, ['omnibus-software']
40
- include_examples 'a configurable', :solaris_compiler, nil
41
+ include_examples 'a configurable', :solaris_linker_mapfile, 'files/mapfiles/solaris'
41
42
  include_examples 'a configurable', :append_timestamp, true
42
43
  include_examples 'a configurable', :build_retries, 0
43
44
  include_examples 'a configurable', :use_git_caching, true
44
45
  include_examples 'a configurable', :fetcher_read_timeout, 60
46
+ include_examples 'a configurable', :fetcher_retries, 5
45
47
 
46
48
  describe '#workers' do
47
49
  context 'when the Ohai data is not present' do
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+ require 'omnibus/manifest_entry'
3
+
4
+ module Omnibus
5
+ describe Fetcher do
6
+ let(:source_path) { '/local/path' }
7
+ let(:project_dir) { '/project/dir' }
8
+ let(:build_dir) { '/build/dir' }
9
+
10
+ let(:manifest_entry) do
11
+ double(Software,
12
+ name: 'software',
13
+ locked_version: '31aedfs',
14
+ described_version: 'mrfancypants',
15
+ locked_source: { path: source_path })
16
+ end
17
+
18
+ subject { described_class.new(manifest_entry, project_dir, build_dir) }
19
+
20
+
21
+ describe "#initialize" do
22
+ it "sets the resovled_version to the locked_version" do
23
+ expect(subject.resolved_version).to eq("31aedfs")
24
+ end
25
+
26
+ it "sets the source to the locked_source" do
27
+ expect(subject.source).to eq({ path: source_path})
28
+ end
29
+
30
+ it 'sets the described_version to the described version' do
31
+ expect(subject.described_version).to eq('mrfancypants')
32
+ end
33
+ end
34
+ end
35
+ end
@@ -4,18 +4,20 @@ module Omnibus
4
4
  describe GitFetcher do
5
5
  let(:source_path) { '/local/path' }
6
6
  let(:project_dir) { '/project/dir' }
7
+ let(:build_dir) { '/build/dir' }
7
8
 
8
- let(:software) do
9
- double(Software,
9
+ let(:manifest_entry) do
10
+ double(ManifestEntry,
10
11
  name: 'software',
11
- source: { path: source_path },
12
- project_dir: project_dir,
13
- )
12
+ locked_version: '123abcd1234',
13
+ described_version: 'some-git-ref',
14
+ locked_source: { path: source_path })
14
15
  end
15
16
 
16
- subject { described_class.new(software) }
17
+ subject { described_class.new(manifest_entry, project_dir, build_dir) }
17
18
 
18
19
  describe '#fetch_required?' do
20
+
19
21
  context 'when the repository is not cloned' do
20
22
  before { allow(subject).to receive(:cloned?).and_return(false) }
21
23
 
@@ -26,7 +28,7 @@ module Omnibus
26
28
 
27
29
  context 'when the repository is cloned' do
28
30
  before { allow(subject).to receive(:cloned?).and_return(true) }
29
-
31
+ before { allow(subject).to receive(:resolved_version).and_return('12341235')}
30
32
  context 'when the revision is difference' do
31
33
  before { allow(subject).to receive(:same_revision?).and_return(false) }
32
34
 
@@ -65,7 +67,7 @@ module Omnibus
65
67
  allow(subject).to receive(:cloned?).and_return(true)
66
68
  end
67
69
 
68
- it 'gleans the directory' do
70
+ it 'cleans the directory' do
69
71
  expect(subject).to receive(:git).with('clean -fdx')
70
72
  subject.clean
71
73
  end
@@ -94,9 +96,6 @@ module Omnibus
94
96
  describe '#fetch' do
95
97
  before do
96
98
  allow(subject).to receive(:create_required_directories)
97
- allow(subject).to receive(:git_fetch)
98
- allow(subject).to receive(:git_clone)
99
- allow(subject).to receive(:git_checkout)
100
99
  end
101
100
 
102
101
  context 'when the repository is cloned' do
@@ -105,7 +104,7 @@ module Omnibus
105
104
  context 'when the revision is different' do
106
105
  before { allow(subject).to receive(:same_revision?).and_return(false) }
107
106
 
108
- it 'fetches and resets' do
107
+ it 'fetches and resets to the resolved_version' do
109
108
  expect(subject).to receive(:git_fetch)
110
109
  subject.fetch
111
110
  end
@@ -122,9 +121,24 @@ module Omnibus
122
121
  end
123
122
 
124
123
  context 'when the repository is not cloned' do
125
- before { allow(subject).to receive(:cloned?).and_return(false) }
124
+ before do
125
+ allow(subject).to receive(:cloned?).and_return(false)
126
+ allow(subject).to receive(:dir_empty?).and_return(true)
127
+ allow(subject).to receive(:git_clone)
128
+ allow(subject).to receive(:git_checkout)
129
+ end
130
+
131
+ context 'but a directory does exist' do
132
+ before { expect(subject).to receive(:dir_empty?).with(project_dir).and_return(false)}
133
+
134
+ it 'forcefully removes and recreateds the directory' do
135
+ expect(FileUtils).to receive(:rm_rf).with(project_dir).and_return(project_dir)
136
+ expect(Dir).to receive(:mkdir).with(project_dir).and_return(0)
137
+ subject.fetch
138
+ end
139
+ end
126
140
 
127
- it 'clones the repository' do
141
+ it 'clones the repository and checks out the correct revision' do
128
142
  expect(subject).to receive(:git_clone).once
129
143
  subject.fetch
130
144
  end
@@ -3,16 +3,17 @@ require 'spec_helper'
3
3
  module Omnibus
4
4
  describe NetFetcher do
5
5
  let(:project_dir) { '/tmp/project' }
6
+ let(:build_dir) { '/tmp/build' }
7
+ let(:source) do
8
+ { url: 'https://get.example.com/file.tar.gz', md5: 'abcd1234' }
9
+ end
6
10
 
7
- let(:software) do
8
- double(Software,
9
- downloaded_file: 'file.tar.gz',
10
- name: 'file',
11
- source: { url: 'https://get.example.com/file.tar.gz', md5: 'abcd1234' },
12
- checksum: 'abc123',
13
- source_uri: 'http://example.com/file.tar.gz',
14
- project_dir: project_dir,
15
- )
11
+ let(:manifest_entry) do
12
+ double(Omnibus::ManifestEntry,
13
+ name: 'file',
14
+ locked_version: "1.2.3",
15
+ described_version: '1.2.3',
16
+ locked_source: source)
16
17
  end
17
18
 
18
19
  let(:cache_dir) { '/cache' }
@@ -21,7 +22,7 @@ module Omnibus
21
22
  Config.cache_dir(cache_dir)
22
23
  end
23
24
 
24
- subject { described_class.new(software) }
25
+ subject { described_class.new(manifest_entry, project_dir, build_dir) }
25
26
 
26
27
  describe '#fetch_required?' do
27
28
  context 'when file is not downloaded' do
@@ -60,8 +61,40 @@ module Omnibus
60
61
  end
61
62
 
62
63
  describe '#version_guid' do
63
- it 'returns the shasum' do
64
- expect(subject.version_guid).to eq('md5:abcd1234')
64
+ context 'source with md5' do
65
+ it 'returns the shasum' do
66
+ expect(subject.version_guid).to eq('md5:abcd1234')
67
+ end
68
+ end
69
+
70
+ context 'source with sha1' do
71
+ let(:source) do
72
+ { url: 'https://get.example.com/file.tar.gz', sha1: 'abcd1234' }
73
+ end
74
+
75
+ it 'returns the shasum' do
76
+ expect(subject.version_guid).to eq('sha1:abcd1234')
77
+ end
78
+ end
79
+
80
+ context 'source with sha256' do
81
+ let(:source) do
82
+ { url: 'https://get.example.com/file.tar.gz', sha256: 'abcd1234' }
83
+ end
84
+
85
+ it 'returns the shasum' do
86
+ expect(subject.version_guid).to eq('sha256:abcd1234')
87
+ end
88
+ end
89
+
90
+ context 'source with sha512' do
91
+ let(:source) do
92
+ { url: 'https://get.example.com/file.tar.gz', sha512: 'abcd1234' }
93
+ end
94
+
95
+ it 'returns the shasum' do
96
+ expect(subject.version_guid).to eq('sha512:abcd1234')
97
+ end
65
98
  end
66
99
  end
67
100
 
@@ -104,24 +137,127 @@ module Omnibus
104
137
  end
105
138
 
106
139
  describe '#version_for_cache' do
140
+ context 'source with md5' do
141
+ it 'returns the download URL and md5' do
142
+ expect(subject.version_for_cache).to eq('download_url:https://get.example.com/file.tar.gz|md5:abcd1234')
143
+ end
144
+ end
145
+
146
+ context 'source with sha1' do
147
+ let(:source) do
148
+ { url: 'https://get.example.com/file.tar.gz', sha1: 'abcd1234' }
149
+ end
150
+
151
+ it 'returns the download URL and sha1' do
152
+ expect(subject.version_for_cache).to eq('download_url:https://get.example.com/file.tar.gz|sha1:abcd1234')
153
+ end
154
+ end
155
+
156
+ context 'source with sha256' do
157
+ let(:source) do
158
+ { url: 'https://get.example.com/file.tar.gz', sha256: 'abcd1234' }
159
+ end
160
+
161
+ it 'returns the download URL and sha256' do
162
+ expect(subject.version_for_cache).to eq('download_url:https://get.example.com/file.tar.gz|sha256:abcd1234')
163
+ end
164
+ end
165
+
166
+ context 'source with sha512' do
167
+ let(:source) do
168
+ { url: 'https://get.example.com/file.tar.gz', sha512: 'abcd1234' }
169
+ end
170
+
171
+ it 'returns the download URL and sha1' do
172
+ expect(subject.version_for_cache).to eq('download_url:https://get.example.com/file.tar.gz|sha512:abcd1234')
173
+ end
174
+ end
175
+ end
176
+
177
+ describe "downloading the file" do
178
+
179
+ let(:expected_open_opts) do
180
+ {
181
+ "Accept-Encoding" => "identity",
182
+ :read_timeout => 60,
183
+ :content_length_proc => an_instance_of(Proc),
184
+ :progress_proc => an_instance_of(Proc)
185
+ }
186
+ end
187
+
188
+ let(:tempfile_path) { "/tmp/intermediate_path/tempfile_path.random_garbage.tmp" }
189
+
190
+ let(:fetched_file) { instance_double("File", path: tempfile_path) }
191
+
192
+ let(:destination_path) { "/cache/file.tar.gz" }
193
+
194
+ let(:progress_bar_output) { StringIO.new }
195
+
196
+ let(:reported_content_length) { 100 }
197
+
198
+ let(:cumulative_downloaded_length) { 100 }
199
+
200
+ def capturing_stdout
201
+ old_stdout, $stdout = $stdout, progress_bar_output
202
+ yield
203
+ ensure
204
+ $stdout = old_stdout
205
+ end
206
+
207
+
107
208
  before do
108
- allow(software).to receive(:source).and_return({
109
- url: 'https://url',
110
- md5: 'abcd1234',
111
- })
209
+ expect(subject).to receive(:open).with(source[:url], expected_open_opts) do |_url, open_uri_opts|
210
+ open_uri_opts[:content_length_proc].call(reported_content_length)
211
+ open_uri_opts[:progress_proc].call(cumulative_downloaded_length)
212
+
213
+ fetched_file
214
+ end
215
+ expect(FileUtils).to receive(:cp).with(tempfile_path, destination_path)
216
+ expect(fetched_file).to receive(:close)
112
217
  end
113
218
 
114
- it 'returns the download URL and md5' do
115
- expect(subject.version_for_cache).to eq('download_url:https://url|md5:abcd1234')
219
+ it "downloads the thing" do
220
+ capturing_stdout do
221
+ expect { subject.send(:download) }.to_not raise_error
222
+ end
223
+ end
224
+
225
+ # In Ci we somewhat frequently see:
226
+ # ProgressBar::InvalidProgressError: You can't set the item's current value to be greater than the total.
227
+ #
228
+ # My hunch is that this is caused by some floating point shenanigans
229
+ # where we sum a bunch of floating point numbers and they add up to some
230
+ # small fraction greater than the actual total. Since we're gonna verify
231
+ # the checksum of what we downloaded later, we don't want to hear about
232
+ # this error.
233
+ context "when cumulative downloaded amount exceeds reported content length" do
234
+
235
+ let(:reported_content_length) { 100 }
236
+
237
+ let(:cumulative_downloaded_length) { 100.1 }
238
+
239
+ it "downloads the thing" do
240
+ capturing_stdout do
241
+ expect { subject.send(:download) }.to_not raise_error
242
+ end
243
+ end
244
+
116
245
  end
246
+
117
247
  end
118
248
 
119
249
  shared_examples 'an extractor' do |extension, command|
120
250
  context "when the file is a .#{extension}" do
121
- before do
122
- software.source[:url] = "https://example.com/file.#{extension}"
251
+ let(:manifest_entry) do
252
+ double(Omnibus::ManifestEntry,
253
+ name: 'file',
254
+ locked_version: "1.2.3",
255
+ described_version: '1.2.3',
256
+ locked_source: { url: "https://get.example.com/file.#{extension}", md5: 'abcd1234' })
123
257
  end
124
258
 
259
+ subject { described_class.new(manifest_entry, project_dir, build_dir) }
260
+
125
261
  it 'is the right command' do
126
262
  expect(subject.send(:extract_command)).to eq(command)
127
263
  end
@@ -134,29 +270,47 @@ module Omnibus
134
270
  end
135
271
 
136
272
  context 'when the downloaded file is a folder' do
273
+ let(:manifest_entry) do
274
+ double(Omnibus::ManifestEntry,
275
+ name: 'file',
276
+ locked_version: "1.2.3",
277
+ described_version: '1.2.3',
278
+ locked_source: { url: "https://get.example.com/folder", md5: 'abcd1234' })
279
+ end
280
+
281
+ subject { described_class.new(manifest_entry, project_dir, build_dir) }
282
+
137
283
  before do
138
284
  allow(FileUtils).to receive(:cp_r)
139
285
  allow(File).to receive(:directory?).and_return(true)
140
- allow(subject).to receive(:extract_command)
141
- software.source[:url] = 'https://example.com/folder'
142
286
  end
143
287
 
144
288
  it 'copies the entire directory to project_dir' do
289
+ allow(subject).to receive(:extract_command)
145
290
  expect(FileUtils).to receive(:cp_r).with("#{cache_dir}/folder", project_dir)
146
291
  subject.extract
147
292
  end
148
293
  end
149
294
 
150
295
  context 'when the downloaded file is a regular file' do
296
+ let(:manifest_entry) do
297
+ double(Omnibus::ManifestEntry,
298
+ name: 'file',
299
+ locked_version: "1.2.3",
300
+ described_version: '1.2.3',
301
+ locked_source: { url: "https://get.example.com/file", md5: 'abcd1234' })
302
+ end
303
+
304
+ subject { described_class.new(manifest_entry, project_dir, build_dir) }
305
+
151
306
  before do
152
307
  allow(FileUtils).to receive(:mkdir_p)
153
308
  allow(FileUtils).to receive(:cp)
154
309
  allow(File).to receive(:directory?).and_return(false)
155
- allow(subject).to receive(:extract_command)
156
- software.source[:url] = 'https://example.com/file'
157
310
  end
158
311
 
159
312
  it 'copies the file into the project_dir' do
313
+ allow(subject).to receive(:extract_command)
160
314
  expect(FileUtils).to receive(:cp).with("#{cache_dir}/file", "#{project_dir}/")
161
315
  subject.extract
162
316
  end