bosh_cli 1.2671.0 → 1.2682.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/cli.rb +8 -1
- data/lib/cli/commands/release/create_release.rb +279 -0
- data/lib/cli/commands/release/delete_release.rb +32 -0
- data/lib/cli/commands/release/init_release.rb +45 -0
- data/lib/cli/commands/release/list_releases.rb +123 -0
- data/lib/cli/commands/release/reset_release.rb +27 -0
- data/lib/cli/commands/release/upload_release.rb +208 -0
- data/lib/cli/commands/release/verify_release.rb +28 -0
- data/lib/cli/packaging_helper.rb +5 -5
- data/lib/cli/release_builder.rb +17 -23
- data/lib/cli/release_compiler.rb +12 -10
- data/lib/cli/source_control/git_ignore.rb +42 -0
- data/lib/cli/version.rb +1 -1
- data/lib/cli/versions/local_version_storage.rb +1 -1
- data/lib/cli/versions/multi_release_support.rb +24 -0
- data/lib/cli/versions/release_versions_index.rb +1 -1
- data/lib/cli/versions/releases_dir_migrator.rb +108 -0
- data/lib/cli/versions/version_file_resolver.rb +2 -11
- data/lib/cli/versions/versions_index.rb +61 -27
- metadata +19 -10
- data/lib/cli/commands/release.rb +0 -684
@@ -0,0 +1,27 @@
|
|
1
|
+
module Bosh::Cli::Command
|
2
|
+
module Release
|
3
|
+
class ResetRelease < Base
|
4
|
+
|
5
|
+
usage 'reset release'
|
6
|
+
desc 'Reset dev release'
|
7
|
+
def reset
|
8
|
+
check_if_release_dir
|
9
|
+
|
10
|
+
say('Your dev release environment will be completely reset'.make_red)
|
11
|
+
if confirmed?
|
12
|
+
say('Removing dev_builds index...')
|
13
|
+
FileUtils.rm_rf('.dev_builds')
|
14
|
+
say('Clearing dev name...')
|
15
|
+
release.dev_name = nil
|
16
|
+
release.save_config
|
17
|
+
say('Removing dev tarballs...')
|
18
|
+
FileUtils.rm_rf('dev_releases')
|
19
|
+
|
20
|
+
say('Release has been reset'.make_green)
|
21
|
+
else
|
22
|
+
say('Canceled')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,208 @@
|
|
1
|
+
module Bosh::Cli::Command
|
2
|
+
module Release
|
3
|
+
class UploadRelease < Base
|
4
|
+
|
5
|
+
usage 'upload release'
|
6
|
+
desc 'Upload release (release_file can be a local file or a remote URI)'
|
7
|
+
option '--rebase',
|
8
|
+
'Rebases this release onto the latest version',
|
9
|
+
'known by director (discards local job/package',
|
10
|
+
'versions in favor of versions assigned by director)'
|
11
|
+
option '--skip-if-exists', 'skips upload if release already exists'
|
12
|
+
def upload(release_file = nil)
|
13
|
+
auth_required
|
14
|
+
|
15
|
+
upload_options = {
|
16
|
+
:rebase => options[:rebase],
|
17
|
+
:repack => true,
|
18
|
+
:skip_if_exists => options[:skip_if_exists],
|
19
|
+
}
|
20
|
+
|
21
|
+
if release_file.nil?
|
22
|
+
check_if_release_dir
|
23
|
+
release_file = release.latest_release_filename
|
24
|
+
if release_file.nil?
|
25
|
+
err('The information about latest generated release is missing, please provide release filename')
|
26
|
+
end
|
27
|
+
unless confirmed?("Upload release `#{File.basename(release_file).make_green}' to `#{target_name.make_green}'")
|
28
|
+
err('Canceled upload')
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
if release_file =~ /^#{URI::regexp}$/
|
33
|
+
upload_remote_release(release_file, upload_options)
|
34
|
+
else
|
35
|
+
unless File.exist?(release_file)
|
36
|
+
err("Release file doesn't exist")
|
37
|
+
end
|
38
|
+
|
39
|
+
file_type = `file --mime-type -b '#{release_file}'`
|
40
|
+
|
41
|
+
if file_type =~ /text\/(plain|yaml)/
|
42
|
+
upload_manifest(release_file, upload_options)
|
43
|
+
else
|
44
|
+
upload_tarball(release_file, upload_options)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def upload_manifest(manifest_path, upload_options = {})
|
52
|
+
package_matches = match_remote_packages(File.read(manifest_path))
|
53
|
+
|
54
|
+
find_release_dir(manifest_path)
|
55
|
+
|
56
|
+
blobstore = release.blobstore
|
57
|
+
tmpdir = Dir.mktmpdir
|
58
|
+
|
59
|
+
compiler = Bosh::Cli::ReleaseCompiler.new(manifest_path, blobstore, package_matches)
|
60
|
+
need_repack = true
|
61
|
+
|
62
|
+
unless compiler.exists?
|
63
|
+
compiler.tarball_path = File.join(tmpdir, 'release.tgz')
|
64
|
+
compiler.compile
|
65
|
+
need_repack = false
|
66
|
+
end
|
67
|
+
|
68
|
+
upload_options[:repack] = need_repack
|
69
|
+
upload_tarball(compiler.tarball_path, upload_options)
|
70
|
+
end
|
71
|
+
|
72
|
+
def upload_tarball(tarball_path, upload_options = {})
|
73
|
+
tarball = Bosh::Cli::ReleaseTarball.new(tarball_path)
|
74
|
+
# Trying to repack release by default
|
75
|
+
repack = upload_options[:repack]
|
76
|
+
rebase = upload_options[:rebase]
|
77
|
+
|
78
|
+
say("\nVerifying release...")
|
79
|
+
tarball.validate(:allow_sparse => true)
|
80
|
+
nl
|
81
|
+
|
82
|
+
unless tarball.valid?
|
83
|
+
err('Release is invalid, please fix, verify and upload again')
|
84
|
+
end
|
85
|
+
|
86
|
+
if should_convert_to_old_format?(tarball.version)
|
87
|
+
msg = "You are using CLI > 1.2579.0 with a director that doesn't support " +
|
88
|
+
'the new version format you are using. Upgrade your ' +
|
89
|
+
'director to match the version of your CLI or downgrade your ' +
|
90
|
+
'CLI to 1.2579.0 to avoid versioning mismatch issues.'
|
91
|
+
|
92
|
+
say(msg.make_yellow)
|
93
|
+
tarball_path = tarball.convert_to_old_format
|
94
|
+
end
|
95
|
+
|
96
|
+
remote_release = get_remote_release(tarball.release_name) rescue nil
|
97
|
+
if remote_release && !rebase
|
98
|
+
version = if new_director?
|
99
|
+
Bosh::Common::Version::ReleaseVersion.parse(tarball.version)
|
100
|
+
else
|
101
|
+
tarball.version
|
102
|
+
end
|
103
|
+
if remote_release['versions'].include?(version.to_s)
|
104
|
+
if upload_options[:skip_if_exists]
|
105
|
+
say("Release `#{tarball.release_name}/#{version}' already exists. Skipping upload.")
|
106
|
+
return
|
107
|
+
else
|
108
|
+
err('This release version has already been uploaded')
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
begin
|
114
|
+
if repack
|
115
|
+
package_matches = match_remote_packages(tarball.manifest)
|
116
|
+
|
117
|
+
say('Checking if can repack release for faster upload...')
|
118
|
+
repacked_path = tarball.repack(package_matches)
|
119
|
+
|
120
|
+
if repacked_path.nil?
|
121
|
+
say('Uploading the whole release'.make_green)
|
122
|
+
else
|
123
|
+
say("Release repacked (new size is #{pretty_size(repacked_path)})".make_green)
|
124
|
+
tarball_path = repacked_path
|
125
|
+
end
|
126
|
+
end
|
127
|
+
rescue Bosh::Cli::DirectorError
|
128
|
+
# It's OK for director to choke on getting
|
129
|
+
# a release info (think new releases)
|
130
|
+
end
|
131
|
+
|
132
|
+
if rebase
|
133
|
+
say("Uploading release (#{'will be rebased'.make_yellow})")
|
134
|
+
status, task_id = director.rebase_release(tarball_path)
|
135
|
+
task_report(status, task_id, 'Release rebased')
|
136
|
+
else
|
137
|
+
say("\nUploading release\n")
|
138
|
+
status, task_id = director.upload_release(tarball_path)
|
139
|
+
task_report(status, task_id, 'Release uploaded')
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def upload_remote_release(release_location, upload_options = {})
|
144
|
+
nl
|
145
|
+
if upload_options[:rebase]
|
146
|
+
say("Using remote release `#{release_location}' (#{'will be rebased'.make_yellow})")
|
147
|
+
status, task_id = director.rebase_remote_release(release_location)
|
148
|
+
task_report(status, task_id, 'Release rebased')
|
149
|
+
else
|
150
|
+
say("Using remote release `#{release_location}'")
|
151
|
+
status, task_id = director.upload_remote_release(release_location)
|
152
|
+
task_report(status, task_id, 'Release uploaded')
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# if we aren't already in a release directory, try going up two levels
|
157
|
+
# to see if that is a release directory, and then use that as the base
|
158
|
+
def find_release_dir(manifest_path)
|
159
|
+
unless in_release_dir?
|
160
|
+
dir = File.expand_path('../..', manifest_path)
|
161
|
+
Dir.chdir(dir)
|
162
|
+
if in_release_dir?
|
163
|
+
@release = Bosh::Cli::Release.new(dir)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
def get_remote_release(name)
|
170
|
+
release = director.get_release(name)
|
171
|
+
|
172
|
+
unless release.is_a?(Hash) &&
|
173
|
+
release.has_key?('jobs') &&
|
174
|
+
release.has_key?('packages')
|
175
|
+
raise Bosh::Cli::DirectorError,
|
176
|
+
'Cannot find version, jobs and packages info in the director response, maybe old director?'
|
177
|
+
end
|
178
|
+
|
179
|
+
release
|
180
|
+
end
|
181
|
+
|
182
|
+
def match_remote_packages(manifest_yaml)
|
183
|
+
director.match_packages(manifest_yaml)
|
184
|
+
rescue Bosh::Cli::DirectorError
|
185
|
+
msg = "You are using CLI >= 0.20 with director that doesn't support " +
|
186
|
+
"package matches.\nThis will result in uploading all packages " +
|
187
|
+
"and jobs to your director.\nIt is recommended to update your " +
|
188
|
+
'director or downgrade your CLI to 0.19.6'
|
189
|
+
|
190
|
+
say(msg.make_yellow)
|
191
|
+
exit(1) unless confirmed?
|
192
|
+
end
|
193
|
+
|
194
|
+
def should_convert_to_old_format?(version)
|
195
|
+
return false if new_director?
|
196
|
+
old_format = Bosh::Common::Version::ReleaseVersion.parse(version).to_old_format
|
197
|
+
old_format && version != old_format
|
198
|
+
end
|
199
|
+
|
200
|
+
def new_director?
|
201
|
+
director_version = director.get_status['version']
|
202
|
+
new_format_director_version = '1.2580.0'
|
203
|
+
Bosh::Common::Version::BoshVersion.parse(director_version) >=
|
204
|
+
Bosh::Common::Version::BoshVersion.parse(new_format_director_version)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Bosh::Cli::Command
|
2
|
+
module Release
|
3
|
+
class VerifyRelease < Base
|
4
|
+
|
5
|
+
# bosh verify release
|
6
|
+
usage 'verify release'
|
7
|
+
desc 'Verify release'
|
8
|
+
def verify(tarball_path)
|
9
|
+
tarball = Bosh::Cli::ReleaseTarball.new(tarball_path)
|
10
|
+
|
11
|
+
nl
|
12
|
+
say('Verifying release...')
|
13
|
+
tarball.validate
|
14
|
+
nl
|
15
|
+
|
16
|
+
if tarball.valid?
|
17
|
+
say("`#{tarball_path}' is a valid release".make_green)
|
18
|
+
else
|
19
|
+
say('Validation errors:'.make_red)
|
20
|
+
tarball.errors.each do |error|
|
21
|
+
say("- #{error}")
|
22
|
+
end
|
23
|
+
err("`#{tarball_path}' is not a valid release".make_red)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/cli/packaging_helper.rb
CHANGED
@@ -7,13 +7,13 @@ module Bosh::Cli
|
|
7
7
|
attr_accessor :dry_run
|
8
8
|
|
9
9
|
def init_indices
|
10
|
-
@dev_index = VersionsIndex.new(@dev_builds_dir)
|
11
|
-
@dev_storage = LocalVersionStorage.new(@dev_builds_dir)
|
10
|
+
@dev_index = Versions::VersionsIndex.new(@dev_builds_dir)
|
11
|
+
@dev_storage = Versions::LocalVersionStorage.new(@dev_builds_dir)
|
12
12
|
|
13
|
-
@final_index = VersionsIndex.new(@final_builds_dir)
|
14
|
-
@final_storage = LocalVersionStorage.new(@final_builds_dir)
|
13
|
+
@final_index = Versions::VersionsIndex.new(@final_builds_dir)
|
14
|
+
@final_storage = Versions::LocalVersionStorage.new(@final_builds_dir)
|
15
15
|
|
16
|
-
@final_resolver = VersionFileResolver.new(@final_storage, @blobstore)
|
16
|
+
@final_resolver = Versions::VersionFileResolver.new(@final_storage, @blobstore)
|
17
17
|
end
|
18
18
|
|
19
19
|
def final?
|
data/lib/cli/release_builder.rb
CHANGED
@@ -2,29 +2,30 @@ module Bosh::Cli
|
|
2
2
|
class ReleaseBuilder
|
3
3
|
include Bosh::Cli::DependencyHelper
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
attr_reader :release, :packages, :jobs, :version, :build_dir, :commit_hash, :uncommitted_changes
|
5
|
+
attr_reader :release, :packages, :jobs, :name, :version, :build_dir, :commit_hash, :uncommitted_changes
|
8
6
|
|
9
7
|
# @param [Bosh::Cli::Release] release Current release
|
10
8
|
# @param [Array<Bosh::Cli::PackageBuilder>] packages Built packages
|
11
9
|
# @param [Array<Bosh::Cli::JobBuilder>] jobs Built jobs
|
12
10
|
# @param [Hash] options Release build options
|
13
|
-
def initialize(release, packages, jobs, options = { })
|
11
|
+
def initialize(release, packages, jobs, name, options = { })
|
14
12
|
@release = release
|
15
13
|
@final = options.has_key?(:final) ? !!options[:final] : false
|
16
14
|
@commit_hash = options.fetch(:commit_hash, '00000000')
|
17
15
|
@uncommitted_changes = options.fetch(:uncommitted_changes, true)
|
18
16
|
@packages = packages
|
19
17
|
@jobs = jobs
|
18
|
+
@name = name
|
19
|
+
raise 'Release name is blank' if name.blank?
|
20
|
+
|
20
21
|
@version = options.fetch(:version, nil)
|
21
22
|
|
22
23
|
raise ReleaseVersionError.new('Version numbers cannot be specified for dev releases') if (@version && !@final)
|
23
24
|
|
24
|
-
@final_index = VersionsIndex.new(final_releases_dir)
|
25
|
-
@dev_index = VersionsIndex.new(dev_releases_dir)
|
25
|
+
@final_index = Versions::VersionsIndex.new(final_releases_dir)
|
26
|
+
@dev_index = Versions::VersionsIndex.new(dev_releases_dir)
|
26
27
|
@index = @final ? @final_index : @dev_index
|
27
|
-
@release_storage = LocalVersionStorage.new(@index.storage_dir,
|
28
|
+
@release_storage = Versions::LocalVersionStorage.new(@index.storage_dir, @name)
|
28
29
|
|
29
30
|
if @version && @release_storage.has_file?(@version)
|
30
31
|
raise ReleaseVersionError.new('Release version already exists')
|
@@ -38,12 +39,6 @@ module Bosh::Cli
|
|
38
39
|
end
|
39
40
|
end
|
40
41
|
|
41
|
-
# @return [String] Release name
|
42
|
-
def release_name
|
43
|
-
name = @final ? @release.final_name : @release.dev_name
|
44
|
-
name.blank? ? DEFAULT_RELEASE_NAME : name
|
45
|
-
end
|
46
|
-
|
47
42
|
# @return [String] Release version
|
48
43
|
def version
|
49
44
|
@version ||= assign_version.to_s
|
@@ -136,11 +131,10 @@ module Bosh::Cli
|
|
136
131
|
manifest["commit_hash"] = commit_hash
|
137
132
|
manifest["uncommitted_changes"] = uncommitted_changes
|
138
133
|
|
139
|
-
|
140
|
-
|
141
|
-
unless manifest["name"].bosh_valid_id?
|
142
|
-
raise InvalidRelease, "Release name `#{manifest["name"]}' is not a valid BOSH identifier"
|
134
|
+
unless @name.bosh_valid_id?
|
135
|
+
raise InvalidRelease, "Release name `#{@name}' is not a valid BOSH identifier"
|
143
136
|
end
|
137
|
+
manifest["name"] = @name
|
144
138
|
|
145
139
|
# New release versions are allowed to have the same fingerprint as old versions.
|
146
140
|
# For reverse compatibility, random uuids are stored instead.
|
@@ -192,25 +186,25 @@ module Bosh::Cli
|
|
192
186
|
end
|
193
187
|
|
194
188
|
def final_releases_dir
|
195
|
-
File.join(@release.dir,
|
189
|
+
File.join(@release.dir, 'releases', @name)
|
196
190
|
end
|
197
191
|
|
198
192
|
def dev_releases_dir
|
199
|
-
File.join(@release.dir,
|
193
|
+
File.join(@release.dir, 'dev_releases', @name)
|
200
194
|
end
|
201
195
|
|
202
196
|
def tarball_path
|
203
|
-
File.join(releases_dir, "#{
|
197
|
+
File.join(releases_dir, "#{@name}-#{version}.tgz")
|
204
198
|
end
|
205
199
|
|
206
200
|
def manifest_path
|
207
|
-
File.join(releases_dir, "#{
|
201
|
+
File.join(releases_dir, "#{@name}-#{version}.yml")
|
208
202
|
end
|
209
203
|
|
210
204
|
private
|
211
205
|
|
212
206
|
def assign_version
|
213
|
-
latest_final_version = ReleaseVersionsIndex.new(@final_index).latest_version
|
207
|
+
latest_final_version = Versions::ReleaseVersionsIndex.new(@final_index).latest_version
|
214
208
|
latest_final_version ||= Bosh::Common::Version::ReleaseVersion.parse('0')
|
215
209
|
|
216
210
|
if @final
|
@@ -218,7 +212,7 @@ module Bosh::Cli
|
|
218
212
|
latest_final_version.increment_release
|
219
213
|
else
|
220
214
|
# Increment or Reset the post-release segment
|
221
|
-
dev_versions = ReleaseVersionsIndex.new(@dev_index).versions
|
215
|
+
dev_versions = Versions::ReleaseVersionsIndex.new(@dev_index).versions
|
222
216
|
latest_dev_version = dev_versions.latest_with_pre_release(latest_final_version)
|
223
217
|
|
224
218
|
if latest_dev_version
|
data/lib/cli/release_compiler.rb
CHANGED
@@ -94,18 +94,20 @@ module Bosh::Cli
|
|
94
94
|
end
|
95
95
|
|
96
96
|
def find_package(package)
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
97
|
+
name = package.name
|
98
|
+
final_package_dir = File.join(@release_dir, '.final_builds', 'packages', name)
|
99
|
+
final_index = Versions::VersionsIndex.new(final_package_dir)
|
100
|
+
dev_package_dir = File.join(@release_dir, '.dev_builds', 'packages', name)
|
101
|
+
dev_index = Versions::VersionsIndex.new(dev_package_dir)
|
101
102
|
find_in_indices(final_index, dev_index, package, 'package')
|
102
103
|
end
|
103
104
|
|
104
105
|
def find_job(job)
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
106
|
+
name = job.name
|
107
|
+
final_jobs_dir = File.join(@release_dir, '.final_builds', 'jobs', name)
|
108
|
+
final_index = Versions::VersionsIndex.new(final_jobs_dir)
|
109
|
+
dev_jobs_dir = File.join(@release_dir, '.dev_builds', 'jobs', name)
|
110
|
+
dev_index = Versions::VersionsIndex.new(dev_jobs_dir)
|
109
111
|
find_in_indices(final_index, dev_index, job, 'job')
|
110
112
|
end
|
111
113
|
|
@@ -133,9 +135,9 @@ module Bosh::Cli
|
|
133
135
|
sha1 = found_build["sha1"]
|
134
136
|
blobstore_id = found_build["blobstore_id"]
|
135
137
|
|
136
|
-
storage = LocalVersionStorage.new(index.storage_dir)
|
138
|
+
storage = Versions::LocalVersionStorage.new(index.storage_dir)
|
137
139
|
|
138
|
-
resolver = VersionFileResolver.new(storage, @blobstore)
|
140
|
+
resolver = Versions::VersionFileResolver.new(storage, @blobstore)
|
139
141
|
resolver.find_file(blobstore_id, sha1, version, "#{build_type} #{desc}")
|
140
142
|
rescue Bosh::Blobstore::BlobstoreError => e
|
141
143
|
raise BlobstoreError, "Blobstore error: #{e}"
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Bosh::Cli::SourceControl
|
2
|
+
class GitIgnore
|
3
|
+
|
4
|
+
RELEASE_IGNORE_PATTERNS = [
|
5
|
+
'config/dev.yml',
|
6
|
+
'config/private.yml',
|
7
|
+
'releases/*.tgz',
|
8
|
+
'releases/**/*.tgz',
|
9
|
+
'dev_releases',
|
10
|
+
'.blobs',
|
11
|
+
'blobs',
|
12
|
+
'.dev_builds',
|
13
|
+
'.idea',
|
14
|
+
'.DS_Store',
|
15
|
+
'.final_builds/jobs/**/*.tgz',
|
16
|
+
'.final_builds/packages/**/*.tgz',
|
17
|
+
'*.swp',
|
18
|
+
'*~',
|
19
|
+
'*#',
|
20
|
+
'#*',
|
21
|
+
]
|
22
|
+
|
23
|
+
def initialize(dir)
|
24
|
+
@dir = dir
|
25
|
+
end
|
26
|
+
|
27
|
+
def update
|
28
|
+
file_path = File.join(@dir, '.gitignore')
|
29
|
+
|
30
|
+
found_patterns = []
|
31
|
+
if File.exist?(file_path)
|
32
|
+
File.open(file_path, 'r').each_line { |line| found_patterns << line.chomp }
|
33
|
+
end
|
34
|
+
|
35
|
+
File.open(file_path, 'a') do |f|
|
36
|
+
RELEASE_IGNORE_PATTERNS.each do |pattern|
|
37
|
+
f.print(pattern + "\n") unless found_patterns.include?(pattern)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|