Class: Bake::Gem::Helper

Inherits:
Object
  • Object
show all
Includes:
Shell
Defined in:
lib/bake/gem/helper.rb

Overview

Helper class for performing gem-related operations like building, installing, and publishing gems.

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Shell

#execute, #readlines, #system

Constructor Details

#initialize(root = Dir.pwd, gemspec: nil) ⇒ Helper

Initialize a new helper with the specified root directory and optional gemspec.



91
92
93
94
# File 'lib/bake/gem/helper.rb', line 91

def initialize(root = Dir.pwd, gemspec: nil)
	@root = root
	@gemspec = gemspec || find_gemspec
end

Instance Attribute Details

#gemspecObject (readonly)

Returns the value of attribute gemspec.



100
101
102
# File 'lib/bake/gem/helper.rb', line 100

def gemspec
  @gemspec
end

#rootObject (readonly)

Returns the value of attribute root.



97
98
99
# File 'lib/bake/gem/helper.rb', line 97

def root
  @root
end

#The root directory of the gem project.(rootdirectoryofthegemproject.) ⇒ Object (readonly)



97
# File 'lib/bake/gem/helper.rb', line 97

attr :root

Instance Method Details

#build_gem(root: "pkg", signing_key: nil) ⇒ Object



195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/bake/gem/helper.rb', line 195

def build_gem(root: "pkg", signing_key: nil)
	# Ensure the output directory exists:
	FileUtils.mkdir_p(root)
	
	output_path = File.join(root, @gemspec.file_name)
	
	if signing_key == false
		@gemspec.signing_key = nil
	elsif signing_key.is_a?(String)
		@gemspec.signing_key = signing_key
	elsif signing_key == true and @gemspec.signing_key.nil?
		raise ArgumentError, "Signing key is required for signing the gem, but none was specified by the gemspec."
	end
	
	::Gem::Package.build(@gemspec, false, false, output_path)
end

#build_gem_in_worktree(root: "pkg", signing_key: nil) ⇒ Object

Build the gem in a clean worktree for better isolation



230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'lib/bake/gem/helper.rb', line 230

def build_gem_in_worktree(root: "pkg", signing_key: nil)
	original_pkg_path = File.join(@root, root)
	
	# Create a unique temporary path for the worktree
	timestamp = Time.now.strftime("%Y%m%d-%H%M%S-%N")
	worktree_path = File.join(Dir.tmpdir, "bake-gem-build-#{timestamp}")
	
	begin
		# Create worktree from current HEAD
		unless system("git", "worktree", "add", worktree_path, "HEAD", chdir: @root)
			raise "Failed to create git worktree. Make sure you have at least one commit in the repository."
		end
		
		# Create helper for the worktree
		worktree_helper = self.class.new(worktree_path)
		
		# Build gem directly into the target pkg directory
		output_path = worktree_helper.build_gem(root: original_pkg_path, signing_key: signing_key)
		
		output_path
	ensure
		# Clean up the worktree
		system("git", "worktree", "remove", worktree_path, "--force", chdir: @root)
	end
end

#commit_version_changes(message: "Bump version.") ⇒ Object

Commit version changes to the current branch.



272
273
274
275
# File 'lib/bake/gem/helper.rb', line 272

def commit_version_changes(message: "Bump version.")
	system("git", "add", "--all", chdir: @root)
	system("git", "commit", "-m", message, chdir: @root)
end

#create_release_branch(version_path, message: "Bump version.") ⇒ Object

Create a release branch, add the version file, and commit the changes.



260
261
262
263
264
265
266
267
268
# File 'lib/bake/gem/helper.rb', line 260

def create_release_branch(version_path, message: "Bump version.")
	branch_name = "release-v#{@gemspec.version}"
	
	system("git", "checkout", "-b", branch_name, chdir: @root)
	system("git", "add", version_path, chdir: @root)
	system("git", "commit", "-m", message, chdir: @root)
	
	return branch_name
end

#create_release_tag(tag: true, version:) ⇒ Object

Fetch remote tags and create a release tag for the specified version.



281
282
283
284
285
286
287
288
289
290
291
# File 'lib/bake/gem/helper.rb', line 281

def create_release_tag(tag: true, version:)
	tag_name = nil
	
	if tag
		tag_name = "v#{version}"
		system("git", "fetch", "--all", "--tags", chdir: @root)
		system("git", "tag", tag_name, chdir: @root)
	end
	
	return tag_name
end

#current_branchObject

Figure out if there is a current branch, if not, return ‘nil`.



312
313
314
315
316
317
318
319
# File 'lib/bake/gem/helper.rb', line 312

def current_branch
	# We originally used this but it is not supported by older versions of git.
	# readlines("git", "branch", "--show-current").first&.chomp
	
	readlines("git", "symbolic-ref", "--short", "--quiet", "HEAD", chdir: @root).first&.chomp
rescue CommandExecutionError
	nil
end

#delete_git_tag(tag_name) ⇒ Object

Delete a git tag.



295
296
297
# File 'lib/bake/gem/helper.rb', line 295

def delete_git_tag(tag_name)
	system("git", "tag", "--delete", tag_name, chdir: @root)
end

#find_gemspec(glob = "*.gemspec") ⇒ Object

Find a gemspec file in the root directory.



325
326
327
328
329
330
331
332
333
334
335
# File 'lib/bake/gem/helper.rb', line 325

def find_gemspec(glob = "*.gemspec")
	paths = Dir.glob(glob, base: @root).sort
	
	if paths.size > 1
		raise "Multiple gemspecs found: #{paths}, please specify one!"
	end
	
	if path = paths.first
		return ::Gem::Specification.load(File.expand_path(path, @root))
	end
end

#guard_cleanObject

Verify that the repository has no uncommitted changes.



153
154
155
156
157
158
159
160
161
# File 'lib/bake/gem/helper.rb', line 153

def guard_clean
	lines = uncommitted_changes
	
	if lines.any?
		raise "Repository has uncommited changes!\n#{lines.join('')}"
	end
	
	return true
end

#guard_last_commit_not_version_bumpObject

Verify that the last commit was not a version bump.



172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/bake/gem/helper.rb', line 172

def guard_last_commit_not_version_bump
	# Get the last commit message:
	begin
		last_commit_message = readlines("git", "log", "-1", "--pretty=format:%s", chdir: @root).first&.strip
	rescue CommandExecutionError => error
		# If git log fails (e.g., no commits yet), skip the check:
		if error.exit_code == 128
			return true
		else
			raise
		end
	end
	
	if last_commit_message && last_commit_message.match?(/^Bump (patch|minor|major|version)( version)?\.?$/i)
		raise "Last commit appears to be a version bump: #{last_commit_message.inspect}. Cannot bump version consecutively."
	end
	
	return true
end

#install_gem(*arguments, path: @gemspec.file_name) ⇒ Object

Install the gem using the ‘gem install` command.



215
216
217
# File 'lib/bake/gem/helper.rb', line 215

def install_gem(*arguments, path: @gemspec.file_name)
	system("gem", "install", path, *arguments)
end

#push_gem(*arguments, path: @gemspec.file_name) ⇒ Object

Push the gem to a gem repository using the ‘gem push` command.



222
223
224
# File 'lib/bake/gem/helper.rb', line 222

def push_gem(*arguments, path: @gemspec.file_name)
	system("gem", "push", path, *arguments)
end

#push_release(current_branch: nil) ⇒ Object

Push changes and tags to the remote repository.



301
302
303
304
305
306
307
308
# File 'lib/bake/gem/helper.rb', line 301

def push_release(current_branch: nil)
	# If we are on a branch, push, otherwise just push the tags (assuming shallow checkout):
	if current_branch
		system("git", "push", chdir: @root)
	end
	
	system("git", "push", "--tags", chdir: @root)
end

#The gemspec for the gem.=(gemspec) ⇒ Object



100
# File 'lib/bake/gem/helper.rb', line 100

attr :gemspec

#uncommitted_changesObject

Get the list of uncommitted changes in the repository.



165
166
167
# File 'lib/bake/gem/helper.rb', line 165

def uncommitted_changes
	readlines("git", "status", "--porcelain", chdir: @root)
end

#update_version(bump, version_path = self.version_path) ⇒ Object

Update the version number in the version file according to the bump specification.



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/bake/gem/helper.rb', line 124

def update_version(bump, version_path = self.version_path)
	return false unless version_path
	
	# Guard against consecutive version bumps
	guard_last_commit_not_version_bump
	
	lines = File.readlines(version_path)
	new_version = nil
	
	lines.each do |line|
		Version.update_version(line) do |version|
			new_version = version.increment(bump)
		end
	end
	
	if new_version
		File.write(version_path, lines.join)
		
		if block_given?
			yield new_version
		end
		
		return version_path
	end
end

#version_pathObject

Find the path to the version.rb file in the gem.



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/bake/gem/helper.rb', line 104

def version_path
	if @gemspec
		candidates = @gemspec.files.grep(/lib(.*?)\/version.rb/)
		
		# If only one version file exists, use it:
		return candidates.first if candidates.size == 1
		
		# Try to match the gem name convention (e.g., "protocol-rack" -> "lib/protocol/rack/version.rb"):
		expected_path = "lib/#{@gemspec.name.gsub('-', '/')}/version.rb"
		return expected_path if candidates.include?(expected_path)
		
		# Fall back to the shortest path (most likely to be the main gem version):
		return candidates.min_by(&:length)
	end
end