toys-release 0.1.1 → 0.2.2

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.
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "fileutils"
4
+ require "toys/utils/gems"
4
5
 
5
6
  module Toys
6
7
  module Release
@@ -9,294 +10,65 @@ module Toys
9
10
  #
10
11
  module Steps
11
12
  ##
12
- # Entrypoint for running a step.
13
+ # The interface that steps must implement.
13
14
  #
14
- # @param type [String] Name of the step class
15
- # @param name [String,nil] An optional unique name for the step
16
- # @param options [Hash{String=>String}] Options to pass to the step
17
- # @param repository [Toys::Release::Repository]
18
- # @param component [Toys::Release::Component] The component to release
19
- # @param version [Gem::Version] The version to release
20
- # @param artifact_dir [Toys::Release::ArtifactDir]
21
- # @param dry_run [boolean] Whether to do a dry run release
22
- # @param git_remote [String] The git remote to push gh-pages to
15
+ # This module is primarily for documentation. It need not actually be
16
+ # included in a step implementation.
23
17
  #
24
- # @return [:continue] if the step finished and the next step should run
25
- # @return [:abort] if the pipeline should be aborted
26
- #
27
- def self.run(type:, name:, options:,
28
- repository:, component:, version:, performer_result:,
29
- artifact_dir:, dry_run:, git_remote:)
30
- step_class = nil
31
- begin
32
- step_class = const_get(type)
33
- rescue ::NameError
34
- repository.utils.error("Unknown step type: #{type}")
35
- return
36
- end
37
- step = step_class.new(repository: repository, component: component, version: version,
38
- artifact_dir: artifact_dir, dry_run: dry_run, git_remote: git_remote,
39
- name: name, options: options, performer_result: performer_result)
40
- begin
41
- step.run
42
- :continue
43
- rescue StepExit
44
- :continue
45
- rescue AbortingExit
46
- :abort
47
- end
48
- end
49
-
50
- ##
51
- # Internal exception signaling that the step should end immediately but
52
- # the pipeline should continue.
53
- # @private
54
- #
55
- class StepExit < ::StandardError
56
- end
57
-
58
- ##
59
- # Internal exception signaling that the step should end immediately and
60
- # the pipeline should be aborted.
61
- # @private
62
- #
63
- class AbortingExit < ::StandardError
64
- end
65
-
66
- ##
67
- # Base class for steps
68
- #
69
- class Base
70
- ##
71
- # Construct a base step.
72
- # @private
73
- #
74
- def initialize(repository:, component:, version:, performer_result:,
75
- artifact_dir:, dry_run:, git_remote:, name:, options:)
76
- @repository = repository
77
- @component = component
78
- @release_version = version
79
- @performer_result = performer_result
80
- @artifact_dir = artifact_dir
81
- @dry_run = dry_run
82
- @git_remote = git_remote || "origin"
83
- @utils = repository.utils
84
- @repo_settings = repository.settings
85
- @component_settings = component.settings
86
- @name = name
87
- @options = options
88
- end
89
-
90
- ##
91
- # Get the option with the given key.
92
- #
93
- # @param key [String] Option name to fetch
94
- # @param required [boolean] Whether to exit with an error if the option
95
- # is not set. Defaults to false, which instead returns the default.
96
- # @param default [Object] Default value to return if the option is not
97
- # set and required is set to false.
98
- #
99
- # @return [Object] The option value
100
- #
101
- def option(key, required: false, default: nil)
102
- value = @options[key]
103
- if !value.nil?
104
- value
105
- elsif required
106
- exit_step("Missing option: #{key.inspect} for step #{self.class} (name = #{name.inspect})")
107
- else
108
- default
109
- end
110
- end
111
-
112
- ##
113
- # Exit the step immediately. If an error message is given, it is added
114
- # to the error stream.
115
- # Raises an error and not return.
116
- #
117
- # @param error_message [String] Optional error message
118
- # @param abort_pipeline [boolean] Whether to abort the pipeline.
119
- # Default is false.
120
- #
121
- def exit_step(error_message = nil, abort_pipeline: false)
122
- utils.error(error_message) if error_message
123
- if abort_pipeline
124
- raise AbortingExit
125
- else
126
- raise StepExit
127
- end
128
- end
129
-
130
- ##
131
- # Get the path to an artifact directory for this step.
132
- #
133
- # @param name [String] Optional name that can be used to point to the
134
- # same directory from multiple steps. If not specified, the step
135
- # name is used.
136
- #
137
- def artifact_dir(name = nil)
138
- @artifact_dir.get(name || self.name)
139
- end
140
-
141
- ##
142
- # Run any pre-tool configured using the `"pre_tool"` option.
143
- # The option value must be an array of strings representing the command.
144
- #
145
- def pre_tool
146
- cmd = option("pre_tool")
147
- return unless cmd
148
- utils.log("Running pre-build tool...")
149
- result = utils.exec_separate_tool(cmd, out: [:child, :err])
150
- unless result.success?
151
- exit_step("Pre-build tool failed: #{cmd}. Check the logs for details.")
152
- end
153
- utils.log("Completed pre-build tool.")
154
- end
155
-
18
+ module Interface
156
19
  ##
157
- # Run any pre-command configured using the `"pre_command"` option.
158
- # The option value must be an array of strings representing the command.
20
+ # Whether this step is a primary step (i.e. always runs.)
159
21
  #
160
- def pre_command
161
- cmd = option("pre_command")
162
- return unless cmd
163
- utils.log("Running pre-build command...")
164
- result = utils.exec(cmd, out: [:child, :err])
165
- unless result.success?
166
- exit_step("Pre-build command failed: #{cmd.inspect}. Check the logs for details.")
167
- end
168
- utils.log("Completed pre-build command.")
169
- end
170
-
171
- ##
172
- # Clean any files not part of the git repository, unless the `"clean"`
173
- # option is explicitly set to false.
22
+ # @param step_context [Toys::Release::Pipeline::StepContext] Context
23
+ # provided for the step
24
+ # @return [boolean]
174
25
  #
175
- def pre_clean
176
- return if option("clean") == false
177
- count = clean_gitignored(".")
178
- utils.log("Cleaned #{count} gitignored items")
26
+ def primary?(step_context)
27
+ raise "Unimplemented #{step_context}"
179
28
  end
180
29
 
181
30
  ##
182
- # Check whether gh_pages is enabled for this component. If not enabled
183
- # and the step requires it, exit the step.
31
+ # Return the names of the standard dependencies of this step
184
32
  #
185
- # @param required [boolean] Force this step to require gh_pages. If
186
- # false, the `"require_gh_pages_enabled"` option can still specify
187
- # that the step requires gh_pages.
33
+ # @param step_context [Toys::Release::Pipeline::StepContext] Context
34
+ # provided for the step
35
+ # @return [Array<String>]
188
36
  #
189
- def check_gh_pages_enabled(required:)
190
- if (required || option("require_gh_pages_enabled")) && !component_settings.gh_pages_enabled
191
- utils.log("Skipping step #{name.inspect} because gh_pages is not enabled.")
192
- exit_step
193
- end
37
+ def dependencies(step_context)
38
+ raise "Unimplemented #{step_context}"
194
39
  end
195
40
 
196
- ##
197
- # @return [boolean] Whether this step is being run in dry run mode
198
- #
199
- def dry_run?
200
- @dry_run
201
- end
202
-
203
- ##
204
- # @return [Toys::Release::Repository]
205
- #
206
- attr_reader :repository
207
-
208
- ##
209
- # @return [Toys::Release::Component]
210
- #
211
- attr_reader :component
212
-
213
- ##
214
- # @return [Toys::Release::RepoSettings]
215
- #
216
- attr_reader :repo_settings
217
-
218
- ##
219
- # @return [Toys::Release::ComponentSettings]
220
- #
221
- attr_reader :component_settings
222
-
223
- ##
224
- # @return [Toys::Release::EnvironmentUtils]
225
- #
226
- attr_reader :utils
227
-
228
- ##
229
- # @return [Gem::Version]
230
- #
231
- attr_reader :release_version
232
-
233
- ##
234
- # @return [Toys::Release::Performer::Result]
235
- #
236
- attr_reader :performer_result
237
-
238
- ##
239
- # @return [String]
240
- #
241
- attr_reader :name
242
-
243
- ##
244
- # @return [String]
245
- #
246
- attr_reader :git_remote
247
-
248
41
  ##
249
42
  # Run the step.
250
- # This method must be overridden in a subclass.
251
43
  #
252
- def run
253
- raise "Cannot run base step"
254
- end
255
-
256
- private
257
-
258
- def clean_gitignored(dir)
259
- count = 0
260
- children = dir_children(dir)
261
- result = utils.exec(["git", "check-ignore", "--stdin"], in: :controller, out: :capture) do |controller|
262
- children.each { |child| controller.in.puts(child) }
263
- end
264
- result.captured_out.split("\n").each do |path|
265
- ::FileUtils.rm_rf(path)
266
- utils.log("Cleaning: #{path}")
267
- count += 1
268
- end
269
- dir_children(dir).each do |child|
270
- count += clean_gitignored(child) if ::File.directory?(child)
271
- end
272
- count
273
- end
274
-
275
- def dir_children(dir)
276
- ::Dir.entries(dir)
277
- .grep_v(/^\.\.?$/)
278
- .sort
279
- .map { |entry| ::File.join(dir, entry) }
44
+ # @param step_context [Toys::Release::Pipeline::StepContext] Context
45
+ # provided for the step
46
+ #
47
+ def run(step_context)
48
+ raise "Unimplemented #{step_context}"
280
49
  end
281
50
  end
282
51
 
52
+ ##
53
+ # A step that does nothing.
54
+ #
55
+ NOOP = ::Object.new
56
+
283
57
  ##
284
58
  # A step that runs a toys tool.
285
59
  # The tool must be specified as a string array in the `"tool"` option.
286
60
  #
287
- class Tool < Base
288
- ##
289
- # Run this step
290
- #
291
- def run
292
- tool = Array(option("tool", required: true))
293
- utils.log("Running tool #{tool.inspect}...")
294
- result = utils.exec_separate_tool(tool, out: [:child, :err])
61
+ TOOL = ::Object.new
62
+ class << TOOL
63
+ # @private
64
+ def run(step_context)
65
+ tool = Array(step_context.option("tool", required: true))
66
+ step_context.log("Running tool #{tool.inspect}...")
67
+ result = step_context.utils.exec_separate_tool(tool, out: [:child, :err])
295
68
  unless result.success?
296
- exit_step("Tool failed: #{tool.inspect}. Check the logs for details.",
297
- abort_pipeline: option("abort_pipeline_on_error"))
69
+ step_context.abort_pipeline("Tool failed: #{tool.inspect}. Check the logs for details.")
298
70
  end
299
- utils.log("Completed tool")
71
+ step_context.log("Completed tool")
300
72
  end
301
73
  end
302
74
 
@@ -305,33 +77,32 @@ module Toys
305
77
  # The command must be specified as a string array in the `"command"`
306
78
  # option.
307
79
  #
308
- class Command < Base
309
- ##
310
- # Run this step
311
- #
312
- def run
313
- command = Array(option("command", required: true))
314
- utils.log("Running command #{command.inspect}...")
315
- result = utils.exec(command, out: [:child, :err])
80
+ COMMAND = ::Object.new
81
+ class << COMMAND
82
+ # @private
83
+ def run(step_context)
84
+ command = Array(step_context.option("command", required: true))
85
+ step_context.log("Running command #{command.inspect}...")
86
+ result = step_context.utils.exec(command, out: [:child, :err])
316
87
  unless result.success?
317
- exit_step("Command failed: #{command.inspect}. Check the logs for details.",
318
- abort_pipeline: option("abort_pipeline_on_error"))
88
+ step_context.abort_pipeline("Command failed: #{command.inspect}. Check the logs for details.")
319
89
  end
320
- utils.log("Completed command")
90
+ step_context.log("Completed command")
321
91
  end
322
92
  end
323
93
 
324
94
  ##
325
95
  # A step that runs bundler
326
96
  #
327
- class Bundle < Base
328
- ##
329
- # Run this step
330
- #
331
- def run
332
- utils.log("Running bundler for #{component.name} ...")
97
+ BUNDLE = ::Object.new
98
+ class << BUNDLE
99
+ # @private
100
+ def run(step_context)
101
+ component = step_context.component
102
+ step_context.log("Running bundler for #{component.name} ...")
333
103
  component.bundle
334
- utils.log("Completed bundler for #{component.name}")
104
+ step_context.log("Completed bundler for #{component.name}")
105
+ step_context.copy_to_output(source_path: "Gemfile.lock")
335
106
  end
336
107
  end
337
108
 
@@ -340,22 +111,24 @@ module Toys
340
111
  # artifact directory. This step can also run a pre_command and/or a
341
112
  # pre_tool.
342
113
  #
343
- class BuildGem < Base
344
- ##
345
- # Run this step
346
- #
347
- def run
348
- pre_clean
349
- utils.log("Building gem: #{component.name} #{release_version}...")
350
- pre_command
351
- pre_tool
352
- pkg_path = ::File.join(artifact_dir, "#{component.name}-#{release_version}.gem")
353
- result = utils.exec(["gem", "build", "#{component.name}.gemspec", "-o", pkg_path], out: [:child, :err])
114
+ BUILD_GEM = ::Object.new
115
+ class << BUILD_GEM
116
+ # @private
117
+ def run(step_context)
118
+ step_context.log("Building gem: #{step_context.release_description}...")
119
+ pkg_dir = ::File.join(step_context.output_dir, "pkg")
120
+ ::FileUtils.mkdir_p(pkg_dir)
121
+ pkg_path = ::File.join(pkg_dir, step_context.gem_package_name)
122
+ result = step_context.utils.exec(
123
+ ["gem", "build", "#{step_context.component.name}.gemspec", "-o", pkg_path],
124
+ out: [:child, :err]
125
+ )
354
126
  unless result.success?
355
- exit_step("Gem build failed for #{component.name} #{release_version}. Check the logs for details.")
127
+ step_context.abort_pipeline("Gem build failed for #{step_context.release_description}." \
128
+ " Check the logs for details.")
356
129
  end
357
- utils.log("Gem built to #{pkg_path}.")
358
- utils.log("Completed gem build.")
130
+ step_context.log("Gem built to #{pkg_path}.")
131
+ step_context.log("Completed gem build.")
359
132
  end
360
133
  end
361
134
 
@@ -364,26 +137,54 @@ module Toys
364
137
  # the step's artifact directory. This step can also run a pre_command
365
138
  # and/or a pre_tool.
366
139
  #
367
- class BuildYard < Base
368
- ##
369
- # Run this step
370
- #
371
- def run
372
- check_gh_pages_enabled(required: false)
373
- pre_clean
374
- utils.log("Building yard: #{component.name} #{release_version}...")
375
- pre_command
376
- pre_tool
377
- ::FileUtils.rm_rf(".yardoc")
378
- ::FileUtils.rm_rf("doc")
379
- result = utils.exec(["bundle", "exec", "yard", "doc"], out: [:child, :err])
380
- if !result.success? || !::File.directory?("doc")
381
- exit_step("Yard build failed for #{component.name} #{release_version}. Check the logs for details.")
140
+ BUILD_YARD = ::Object.new
141
+ class << BUILD_YARD
142
+ # @private
143
+ def dependencies(step_context)
144
+ if step_context.option("uses_gems")
145
+ []
146
+ else
147
+ ["bundle"]
148
+ end
149
+ end
150
+
151
+ # @private
152
+ def run(step_context)
153
+ step_context.log("Building yard: #{step_context.release_description}...")
154
+ doc_dir = ::File.join(step_context.output_dir, "doc")
155
+ code_lines = setup_gems(step_context) + [
156
+ "require 'yard'",
157
+ "::YARD::CLI::Yardoc.run('--no-cache', '-o', '#{doc_dir}')",
158
+ ]
159
+ result = step_context.utils.ruby(code_lines.join("\n"), out: [:child, :err])
160
+ if !result.success? || !::File.directory?(doc_dir)
161
+ step_context.abort_pipeline("Yard build failed for #{step_context.release_description}." \
162
+ " Check the logs for details.")
163
+ end
164
+ step_context.log("Docs built to #{doc_dir}.")
165
+ step_context.log("Completed yard build.")
166
+ end
167
+
168
+ private
169
+
170
+ def setup_gems(step_context)
171
+ if (uses_gems = step_context.option("uses_gems"))
172
+ ::Toys::Utils::Gems.activate("yard")
173
+ Array(uses_gems).each do |gem_info|
174
+ ::Toys::Utils::Gems.activate(*Array(gem_info))
175
+ end
176
+ [
177
+ "gem 'yard'",
178
+ "puts 'Loading gems explicitly: #{uses_gems.inspect}'",
179
+ ]
180
+ else
181
+ step_context.copy_from_input("bundle")
182
+ [
183
+ "require 'bundler'",
184
+ "puts 'Running with bundler'",
185
+ "Bundler.ui.silence { Bundler.setup }",
186
+ ]
382
187
  end
383
- dest_path = ::File.join(artifact_dir, "doc")
384
- ::FileUtils.mv("doc", dest_path)
385
- utils.log("Docs built to #{dest_path}.")
386
- utils.log("Completed yard build.")
387
188
  end
388
189
  end
389
190
 
@@ -392,51 +193,69 @@ module Toys
392
193
  # `"input"` option provides the name of the artifact directory containing
393
194
  # the built gem.
394
195
  #
395
- class ReleaseGem < Base
396
- ##
397
- # Run this step
398
- #
399
- def run
400
- check_existence
401
- if dry_run?
402
- push_dry_run
196
+ RELEASE_GEM = ::Object.new
197
+ class << RELEASE_GEM
198
+ # @private
199
+ def primary?(_step_context)
200
+ true
201
+ end
202
+
203
+ # @private
204
+ def dependencies(step_context)
205
+ [source_step(step_context)]
206
+ end
207
+
208
+ # @private
209
+ def run(step_context)
210
+ check_existence(step_context)
211
+ pkg_path = find_package(step_context)
212
+ if step_context.dry_run?
213
+ push_dry_run(step_context)
403
214
  else
404
- push_gem
215
+ push_gem(step_context, pkg_path)
405
216
  end
406
217
  end
407
218
 
408
219
  private
409
220
 
410
- def check_existence
411
- utils.log("Checking whether #{component.name} #{release_version} already exists...")
412
- if component.version_released?(release_version)
413
- utils.warning("Gem already pushed for #{component.name} #{release_version}. Skipping.")
414
- performer_result.successes << "Gem already pushed for #{component.name} #{release_version}"
415
- exit_step
416
- end
417
- utils.log("Gem has not yet been released.")
221
+ def source_step(step_context)
222
+ step_context.option("source", default: "build_gem")
418
223
  end
419
224
 
420
- def push_dry_run
421
- unless ::File.file?(pkg_path)
422
- exit_step("DRY RUN: Package not found at #{pkg_path}")
225
+ def check_existence(step_context)
226
+ step_context.log("Checking whether #{step_context.release_description} already exists...")
227
+ if step_context.component.version_released?(step_context.release_version)
228
+ step_context.warning("Gem already pushed for #{step_context.release_description}. Skipping.")
229
+ step_context.add_success("Gem already pushed for #{step_context.release_description}")
230
+ step_context.exit_step
423
231
  end
424
- performer_result.successes << "DRY RUN Rubygems push for #{component.name} #{release_version}."
425
- utils.log("DRY RUN: Gem not actually pushed to Rubygems.")
232
+ step_context.log("Gem has not yet been released.")
426
233
  end
427
234
 
428
- def push_gem
429
- utils.log("Pushing gem: #{component.name} #{release_version}...")
430
- result = utils.exec(["gem", "push", pkg_path], out: [:child, :err])
431
- unless result.success?
432
- exit_step("Rubygems push failed for #{component.name} #{release_version}. Check the logs for details.")
235
+ def find_package(step_context)
236
+ step_name = source_step(step_context)
237
+ source_dir = step_context.output_dir(step_name)
238
+ source_path = ::File.join(source_dir, "pkg", step_context.gem_package_name)
239
+ unless ::File.file?(source_path)
240
+ step_context.abort_pipeline("The output of step #{step_name} did not include a built gem at #{source_path}")
433
241
  end
434
- performer_result.successes << "Rubygems push for #{component.name} #{release_version}."
435
- utils.log("Gem push successful.")
242
+ source_path
436
243
  end
437
244
 
438
- def pkg_path
439
- @pkg_path ||= ::File.join(artifact_dir(option("input")), "#{component.name}-#{release_version}.gem")
245
+ def push_dry_run(step_context)
246
+ step_context.add_success("DRY RUN Rubygems push for #{step_context.release_description}.")
247
+ step_context.log("DRY RUN: Gem not actually pushed to Rubygems.")
248
+ end
249
+
250
+ def push_gem(step_context, pkg_path)
251
+ step_context.log("Pushing gem: #{step_context.release_description}...")
252
+ result = step_context.utils.exec(["gem", "push", pkg_path], out: [:child, :err])
253
+ unless result.success?
254
+ step_context.abort_pipeline("Rubygems push failed for #{step_context.release_description}." \
255
+ " Check the logs for details.")
256
+ end
257
+ step_context.add_success("Rubygems push for #{step_context.release_description}.")
258
+ step_context.log("Gem push successful.")
440
259
  end
441
260
  end
442
261
 
@@ -445,135 +264,153 @@ module Toys
445
264
  # BuildYard. The `"input"` option provides the name of the artifact
446
265
  # directory containing the built documentation.
447
266
  #
448
- class PushGhPages < Base
449
- ##
450
- # Run this step
451
- #
452
- def run
453
- check_gh_pages_enabled(required: true)
454
- setup_gh_pages_dir
455
- check_existence
456
- copy_docs_dir
457
- update_docs_404_page
458
- push_docs_to_git
267
+ PUSH_GH_PAGES = ::Object.new
268
+ class << PUSH_GH_PAGES
269
+ # @private
270
+ def primary?(step_context)
271
+ step_context.component.settings.gh_pages_enabled
272
+ end
273
+
274
+ # @private
275
+ def dependencies(step_context)
276
+ [source_step(step_context)]
277
+ end
278
+
279
+ # @private
280
+ def run(step_context)
281
+ gh_pages_dir = setup_gh_pages_dir(step_context)
282
+ component_dir = ::File.expand_path(step_context.component.settings.gh_pages_directory, gh_pages_dir)
283
+ dest_dir = ::File.join(component_dir, "v#{step_context.release_version}")
284
+ check_existence(step_context, dest_dir)
285
+ copy_docs_dir(step_context, dest_dir)
286
+ update_docs_404_page(step_context, gh_pages_dir)
287
+ push_docs_to_git(step_context, gh_pages_dir)
459
288
  end
460
289
 
461
290
  private
462
291
 
463
- def setup_gh_pages_dir
464
- utils.log("Setting up gh-pages access ...")
292
+ def source_step(step_context)
293
+ step_context.option("source", default: "build_yard")
294
+ end
295
+
296
+ def setup_gh_pages_dir(step_context)
297
+ step_context.log("Setting up gh-pages access ...")
465
298
  gh_token = ::ENV["GITHUB_TOKEN"]
466
- @gh_pages_dir = repository.checkout_separate_dir(
467
- branch: "gh-pages", remote: git_remote, dir: artifact_dir("gh-pages"), gh_token: gh_token
299
+ gh_pages_dir = step_context.repository.checkout_separate_dir(
300
+ branch: "gh-pages", remote: step_context.git_remote, dir: step_context.temp_dir, gh_token: gh_token
468
301
  )
469
- exit_step("Unable to access the gh-pages branch.") unless @gh_pages_dir
470
- utils.log("Checked out gh-pages")
302
+ step_context.abort_pipeline("Unable to access the gh-pages branch.") unless gh_pages_dir
303
+ step_context.log("Checked out gh-pages")
304
+ gh_pages_dir
471
305
  end
472
306
 
473
- def check_existence
307
+ def check_existence(step_context, dest_dir)
474
308
  if ::File.directory?(dest_dir)
475
- utils.warning("Docs already published for #{component.name} #{release_version}. Skipping.")
476
- performer_result.successes << "Docs already published for #{component.name} #{release_version}"
477
- exit_step
309
+ step_context.warning("Docs already published for #{step_context.release_description}. Skipping.")
310
+ step_context.add_success("Docs already published for #{step_context.release_description}")
311
+ step_context.exit_step
478
312
  end
479
- utils.log("Verified docs not yet published for #{component.name} #{release_version}")
313
+ step_context.log("Verified docs not yet published for #{step_context.release_description}")
480
314
  end
481
315
 
482
- def copy_docs_dir
483
- from_dir = ::File.join(artifact_dir(option("input")), "doc")
484
- ::FileUtils.mkdir_p(component_dir)
485
- ::FileUtils.cp_r(from_dir, dest_dir)
316
+ def copy_docs_dir(step_context, dest_dir)
317
+ step_name = source_step(step_context)
318
+ source_dir = ::File.join(step_context.output_dir(step_name), "doc")
319
+ unless ::File.directory?(source_dir)
320
+ step_context.abort_pipeline("The output of step #{step_name} did not include built docs at #{source_dir}")
321
+ end
322
+ ::FileUtils.mkdir_p(::File.dirname(dest_dir))
323
+ ::FileUtils.cp_r(source_dir, dest_dir)
486
324
  end
487
325
 
488
- def update_docs_404_page
489
- path = ::File.join(@gh_pages_dir, "404.html")
326
+ def update_docs_404_page(step_context, gh_pages_dir)
327
+ path = ::File.join(gh_pages_dir, "404.html")
490
328
  content = ::File.read(path)
491
- content.sub!(/#{component.settings.gh_pages_version_var} = "[\w.]+";/,
492
- "#{component.settings.gh_pages_version_var} = \"#{release_version}\";")
329
+ version_var = step_context.component.settings.gh_pages_version_var
330
+ content.sub!(/#{version_var} = "[\w.]+";/,
331
+ "#{version_var} = \"#{step_context.release_version}\";")
493
332
  ::File.write(path, content)
494
333
  end
495
334
 
496
- def push_docs_to_git # rubocop:disable Metrics/AbcSize
497
- ::Dir.chdir(@gh_pages_dir) do
498
- repository.git_commit("Generated docs for #{component.name} #{release_version}",
499
- signoff: repository.settings.signoff_commits?)
500
- if dry_run?
501
- performer_result.successes << "DRY RUN documentation published for #{component.name} #{release_version}."
502
- utils.log("DRY RUN: Documentation not actually published to gh-pages.")
335
+ def push_docs_to_git(step_context, gh_pages_dir)
336
+ ::Dir.chdir(gh_pages_dir) do
337
+ step_context.repository.git_commit("Generated docs for #{step_context.release_description}",
338
+ signoff: step_context.repository.settings.signoff_commits?)
339
+ if step_context.dry_run?
340
+ step_context.add_success("DRY RUN documentation published for #{step_context.release_description}.")
341
+ step_context.log("DRY RUN: Documentation not actually published to gh-pages.")
503
342
  else
504
- result = utils.exec(["git", "push", git_remote, "gh-pages"], out: [:child, :err])
343
+ result = step_context.utils.exec(["git", "push", step_context.git_remote, "gh-pages"],
344
+ out: [:child, :err])
505
345
  unless result.success?
506
- exit_step("Docs publication failed for #{component.name} #{release_version}." \
507
- " Check the logs for details.")
346
+ step_context.abort_pipeline("Docs publication failed for #{step_context.release_description}." \
347
+ " Check the logs for details.")
508
348
  end
509
- performer_result.successes << "Published documentation for #{component.name} #{release_version}."
510
- utils.log("Documentation publish successful.")
349
+ step_context.add_success("Published documentation for #{step_context.release_description}.")
350
+ step_context.log("Documentation publish successful.")
511
351
  end
512
352
  end
513
353
  end
514
-
515
- def component_dir
516
- @component_dir ||= ::File.expand_path(component.settings.gh_pages_directory, @gh_pages_dir)
517
- end
518
-
519
- def dest_dir
520
- @dest_dir ||= ::File.join(component_dir, "v#{release_version}")
521
- end
522
354
  end
523
355
 
524
356
  ##
525
357
  # A step that creates a GitHub tag and release.
526
358
  #
527
- class GitHubRelease < Base
528
- ##
529
- # Run this step
530
- #
531
- def run
532
- check_existence
533
- push_tag
359
+ RELEASE_GITHUB = ::Object.new
360
+ class << RELEASE_GITHUB
361
+ # @private
362
+ def primary?(_step_context)
363
+ true
364
+ end
365
+
366
+ # @private
367
+ def run(step_context)
368
+ check_existence(step_context)
369
+ push_tag(step_context)
534
370
  end
535
371
 
536
372
  private
537
373
 
538
- def check_existence
539
- utils.log("Checking whether #{tag_name} already exists...")
540
- cmd = ["gh", "api", "repos/#{repo_settings.repo_path}/releases/tags/#{tag_name}",
374
+ def check_existence(step_context)
375
+ tag_name = step_context.tag_name
376
+ repo_path = step_context.repository.settings.repo_path
377
+ step_context.log("Checking whether #{tag_name} already exists...")
378
+ cmd = ["gh", "api", "repos/#{repo_path}/releases/tags/#{tag_name}",
541
379
  "-H", "Accept: application/vnd.github.v3+json"]
542
- result = utils.exec(cmd, out: :null, err: :null)
380
+ result = step_context.utils.exec(cmd, out: :null, err: :null)
543
381
  if result.success?
544
- utils.warning("GitHub tag #{tag_name} already exists. Skipping.")
545
- performer_result.successes << "GitHub tag #{tag_name} already exists."
546
- exit_step
382
+ step_context.warning("GitHub tag #{tag_name} already exists. Skipping.")
383
+ step_context.add_success("GitHub tag #{tag_name} already exists.")
384
+ step_context.exit_step
547
385
  end
548
- utils.log("GitHub tag #{tag_name} has not yet been created.")
386
+ step_context.log("GitHub tag #{tag_name} has not yet been created.")
549
387
  end
550
388
 
551
- def push_tag # rubocop:disable Metrics/AbcSize
552
- utils.log("Creating GitHub release #{tag_name}...")
553
- changelog_content = component.changelog_file.read_and_verify_latest_entry(release_version)
554
- release_sha = repository.current_sha
389
+ def push_tag(step_context)
390
+ tag_name = step_context.tag_name
391
+ repo_path = step_context.repository.settings.repo_path
392
+ step_context.log("Creating GitHub release #{tag_name}...")
393
+ changelog_file = step_context.component.changelog_file
394
+ changelog_content = changelog_file.read_and_verify_latest_entry(step_context.release_version)
395
+ release_sha = step_context.repository.current_sha
555
396
  body = ::JSON.dump(tag_name: tag_name,
556
397
  target_commitish: release_sha,
557
- name: "#{component.name} #{release_version}",
398
+ name: step_context.release_description,
558
399
  body: changelog_content)
559
- if dry_run?
560
- performer_result.successes << "DRY RUN GitHub tag #{tag_name}."
561
- utils.log("DRY RUN: GitHub tag #{tag_name} not actually created.")
400
+ if step_context.dry_run?
401
+ step_context.add_success("DRY RUN GitHub tag #{tag_name}.")
402
+ step_context.log("DRY RUN: GitHub tag #{tag_name} not actually created.")
562
403
  else
563
- cmd = ["gh", "api", "repos/#{repo_settings.repo_path}/releases", "--input", "-",
404
+ cmd = ["gh", "api", "repos/#{repo_path}/releases", "--input", "-",
564
405
  "-H", "Accept: application/vnd.github.v3+json"]
565
- result = utils.exec(cmd, in: [:string, body], out: :null)
406
+ result = step_context.utils.exec(cmd, in: [:string, body], out: :null)
566
407
  unless result.success?
567
- exit_step("Unable to create release #{tag_name}. Check the logs for details.")
408
+ step_context.abort_pipeline("Unable to create release #{tag_name}. Check the logs for details.")
568
409
  end
569
- performer_result.successes << "Created release with tag #{tag_name} on GitHub."
570
- utils.log("GitHub release successful.")
410
+ step_context.add_success("Created release with tag #{tag_name} on GitHub.")
411
+ step_context.log("GitHub release successful.")
571
412
  end
572
413
  end
573
-
574
- def tag_name
575
- "#{component.name}/v#{release_version}"
576
- end
577
414
  end
578
415
  end
579
416
  end