toys-release 0.1.1 → 0.3.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.
@@ -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,71 @@ 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
18
+ module Interface
70
19
  ##
71
- # Construct a base step.
72
- # @private
20
+ # Whether this step is a primary step (i.e. always runs.)
73
21
  #
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.
22
+ # @param step_context [Toys::Release::Pipeline::StepContext] Context
23
+ # provided for the step
24
+ # @return [boolean]
98
25
  #
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
26
+ def primary?(step_context)
27
+ raise "Unimplemented #{step_context}"
110
28
  end
111
29
 
112
30
  ##
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.
31
+ # Return the names of the standard dependencies of this step
116
32
  #
117
- # @param error_message [String] Optional error message
118
- # @param abort_pipeline [boolean] Whether to abort the pipeline.
119
- # Default is false.
33
+ # @param step_context [Toys::Release::Pipeline::StepContext] Context
34
+ # provided for the step
35
+ # @return [Array<String>]
120
36
  #
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
37
+ def dependencies(step_context)
38
+ raise "Unimplemented #{step_context}"
128
39
  end
129
40
 
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
-
156
- ##
157
- # Run any pre-command configured using the `"pre_command"` option.
158
- # The option value must be an array of strings representing the command.
159
- #
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.
174
- #
175
- def pre_clean
176
- return if option("clean") == false
177
- count = clean_gitignored(".")
178
- utils.log("Cleaned #{count} gitignored items")
179
- end
180
-
181
- ##
182
- # Check whether gh_pages is enabled for this component. If not enabled
183
- # and the step requires it, exit the step.
184
- #
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.
188
- #
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
194
- end
195
-
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
+ chdir = step_context.option("chdir", default: ".")
67
+ step_context.log("Running tool #{tool.inspect}...")
68
+ result = step_context.utils.exec_separate_tool(tool, chdir: chdir, out: [:child, :err])
295
69
  unless result.success?
296
- exit_step("Tool failed: #{tool.inspect}. Check the logs for details.",
297
- abort_pipeline: option("abort_pipeline_on_error"))
70
+ if step_context.option("continue_on_error")
71
+ step_context.warning("Tool failed: #{tool.inspect}.")
72
+ step_context.exit_step
73
+ else
74
+ step_context.abort_pipeline("Tool failed: #{tool.inspect}. Check the logs for details.")
75
+ end
298
76
  end
299
- utils.log("Completed tool")
77
+ step_context.log("Completed tool")
300
78
  end
301
79
  end
302
80
 
@@ -305,275 +83,351 @@ module Toys
305
83
  # The command must be specified as a string array in the `"command"`
306
84
  # option.
307
85
  #
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])
86
+ COMMAND = ::Object.new
87
+ class << COMMAND
88
+ # @private
89
+ def run(step_context)
90
+ command = Array(step_context.option("command", required: true))
91
+ chdir = step_context.option("chdir", default: ".")
92
+ step_context.log("Running command #{command.inspect}...")
93
+ result = step_context.utils.exec(command, chdir: chdir, out: [:child, :err])
316
94
  unless result.success?
317
- exit_step("Command failed: #{command.inspect}. Check the logs for details.",
318
- abort_pipeline: option("abort_pipeline_on_error"))
95
+ if step_context.option("continue_on_error")
96
+ step_context.warning("Command failed: #{command.inspect}.")
97
+ step_context.exit_step
98
+ else
99
+ step_context.abort_pipeline("Command failed: #{command.inspect}. Check the logs for details.")
100
+ end
319
101
  end
320
- utils.log("Completed command")
102
+ step_context.log("Completed command")
321
103
  end
322
104
  end
323
105
 
324
106
  ##
325
107
  # A step that runs bundler
326
108
  #
327
- class Bundle < Base
328
- ##
329
- # Run this step
330
- #
331
- def run
332
- utils.log("Running bundler for #{component.name} ...")
333
- component.bundle
334
- utils.log("Completed bundler for #{component.name}")
109
+ BUNDLE = ::Object.new
110
+ class << BUNDLE
111
+ # @private
112
+ def run(step_context)
113
+ chdir = step_context.option("chdir", default: ".")
114
+ component = step_context.component
115
+ step_context.log("Running bundler for #{component.name} ...")
116
+ ::Dir.chdir(chdir) do
117
+ result = component.bundle
118
+ unless result.success?
119
+ step_context.abort_pipeline("Bundle failed for #{component.name}. Check the logs for details.")
120
+ end
121
+ end
122
+ step_context.log("Completed bundler for #{component.name}")
123
+ step_context.copy_to_output(source_path: "Gemfile.lock")
335
124
  end
336
125
  end
337
126
 
338
127
  ##
339
- # A step that builds the gem, and leaves the built gem file in the step's
340
- # artifact directory. This step can also run a pre_command and/or a
341
- # pre_tool.
128
+ # A step that builds the gem, and leaves the built gem package file in
129
+ # the step's output directory.
342
130
  #
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])
131
+ BUILD_GEM = ::Object.new
132
+ class << BUILD_GEM
133
+ # @private
134
+ def run(step_context)
135
+ step_context.log("Building gem: #{step_context.release_description}...")
136
+ pkg_dir = ::File.join(step_context.output_dir, "pkg")
137
+ ::FileUtils.mkdir_p(pkg_dir)
138
+ pkg_path = ::File.join(pkg_dir, step_context.gem_package_name)
139
+ result = step_context.utils.exec(
140
+ ["gem", "build", "#{step_context.component.name}.gemspec", "-o", pkg_path],
141
+ out: [:child, :err]
142
+ )
354
143
  unless result.success?
355
- exit_step("Gem build failed for #{component.name} #{release_version}. Check the logs for details.")
144
+ step_context.abort_pipeline("Gem build failed for #{step_context.release_description}." \
145
+ " Check the logs for details.")
356
146
  end
357
- utils.log("Gem built to #{pkg_path}.")
358
- utils.log("Completed gem build.")
147
+ step_context.log("Gem built to #{pkg_path}.")
148
+ step_context.log("Completed gem build.")
359
149
  end
360
150
  end
361
151
 
362
152
  ##
363
- # A step that builds yardocs, and leaves the built documentation file in
364
- # the step's artifact directory. This step can also run a pre_command
365
- # and/or a pre_tool.
153
+ # A step that builds yardocs, and leaves the built documentation in the
154
+ # step's output directory.
366
155
  #
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.")
156
+ BUILD_YARD = ::Object.new
157
+ class << BUILD_YARD
158
+ # @private
159
+ def dependencies(step_context)
160
+ if step_context.option("uses_gems")
161
+ []
162
+ else
163
+ [step_context.option("bundle_step", default: "bundle")]
164
+ end
165
+ end
166
+
167
+ # @private
168
+ def run(step_context)
169
+ step_context.log("Building yard: #{step_context.release_description}...")
170
+ doc_dir = ::File.join(step_context.output_dir, "doc")
171
+ code_lines = setup_gems(step_context) + [
172
+ "require 'yard'",
173
+ "::YARD::CLI::Yardoc.run('--no-cache', '-o', '#{doc_dir}')",
174
+ ]
175
+ result = step_context.utils.ruby(code_lines.join("\n"), out: [:child, :err])
176
+ if !result.success? || !::File.directory?(doc_dir)
177
+ step_context.abort_pipeline("Yard build failed for #{step_context.release_description}." \
178
+ " Check the logs for details.")
179
+ end
180
+ step_context.log("Docs built to #{doc_dir}.")
181
+ step_context.log("Completed yard build.")
182
+ end
183
+
184
+ private
185
+
186
+ def setup_gems(step_context)
187
+ if (uses_gems = step_context.option("uses_gems"))
188
+ Array(uses_gems).each do |gem_info|
189
+ ::Toys::Utils::Gems.activate(*Array(gem_info))
190
+ end
191
+ ::Toys::Utils::Gems.activate("yard")
192
+ [
193
+ "gem 'yard'",
194
+ "puts 'Loading gems explicitly: #{uses_gems.inspect}'",
195
+ ]
196
+ else
197
+ step_context.copy_from_input("bundle")
198
+ [
199
+ "require 'bundler'",
200
+ "puts 'Running with bundler'",
201
+ "Bundler.ui.silence { Bundler.setup }",
202
+ ]
382
203
  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
204
  end
388
205
  end
389
206
 
390
207
  ##
391
- # A step that releases a gem built by a previous run of BuildGem. The
392
- # `"input"` option provides the name of the artifact directory containing
393
- # the built gem.
208
+ # A step that releases a gem built by a previous step, normally a
209
+ # run of BUILD_GEM.
394
210
  #
395
- class ReleaseGem < Base
396
- ##
397
- # Run this step
398
- #
399
- def run
400
- check_existence
401
- if dry_run?
402
- push_dry_run
211
+ RELEASE_GEM = ::Object.new
212
+ class << RELEASE_GEM
213
+ # @private
214
+ def primary?(step_context)
215
+ ::File.file?("#{step_context.component.name}.gemspec")
216
+ end
217
+
218
+ # @private
219
+ def dependencies(step_context)
220
+ [source_step(step_context)]
221
+ end
222
+
223
+ # @private
224
+ def run(step_context)
225
+ check_existence(step_context)
226
+ pkg_path = find_package(step_context)
227
+ if step_context.dry_run?
228
+ push_dry_run(step_context)
403
229
  else
404
- push_gem
230
+ push_gem(step_context, pkg_path)
405
231
  end
406
232
  end
407
233
 
408
234
  private
409
235
 
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.")
236
+ def source_step(step_context)
237
+ step_context.option("source", default: "build_gem")
418
238
  end
419
239
 
420
- def push_dry_run
421
- unless ::File.file?(pkg_path)
422
- exit_step("DRY RUN: Package not found at #{pkg_path}")
240
+ def check_existence(step_context)
241
+ step_context.log("Checking whether #{step_context.release_description} already exists...")
242
+ gem_name = step_context.component.name
243
+ gem_version = step_context.release_version.to_s
244
+ cmd = ["gem", "search", gem_name, "--exact", "--remote", "--version", gem_version]
245
+ if step_context.utils.capture(cmd).include?("#{gem_name} (#{gem_version})")
246
+ step_context.warning("Gem already pushed for #{step_context.release_description}. Skipping.")
247
+ step_context.add_success("Gem already pushed for #{step_context.release_description}")
248
+ step_context.exit_step
423
249
  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.")
250
+ step_context.log("Gem has not yet been released.")
426
251
  end
427
252
 
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.")
253
+ def find_package(step_context)
254
+ step_name = source_step(step_context)
255
+ source_dir = step_context.output_dir(step_name)
256
+ source_path = ::File.join(source_dir, "pkg", step_context.gem_package_name)
257
+ unless ::File.file?(source_path)
258
+ step_context.abort_pipeline("The output of step #{step_name} did not include a built gem at #{source_path}")
433
259
  end
434
- performer_result.successes << "Rubygems push for #{component.name} #{release_version}."
435
- utils.log("Gem push successful.")
260
+ source_path
261
+ end
262
+
263
+ def push_dry_run(step_context)
264
+ step_context.add_success("DRY RUN Rubygems push for #{step_context.release_description}.")
265
+ step_context.log("DRY RUN: Gem not actually pushed to Rubygems.")
436
266
  end
437
267
 
438
- def pkg_path
439
- @pkg_path ||= ::File.join(artifact_dir(option("input")), "#{component.name}-#{release_version}.gem")
268
+ def push_gem(step_context, pkg_path)
269
+ step_context.log("Pushing gem: #{step_context.release_description}...")
270
+ result = step_context.utils.exec(["gem", "push", pkg_path], out: [:child, :err])
271
+ unless result.success?
272
+ step_context.abort_pipeline("Rubygems push failed for #{step_context.release_description}." \
273
+ " Check the logs for details.")
274
+ end
275
+ step_context.add_success("Rubygems push for #{step_context.release_description}.")
276
+ step_context.log("Gem push successful.")
440
277
  end
441
278
  end
442
279
 
443
280
  ##
444
- # A step that pushes to gh-pages documentation built by a previous run of
445
- # BuildYard. The `"input"` option provides the name of the artifact
446
- # directory containing the built documentation.
281
+ # A step that pushes to gh-pages documentation built by a previous step,
282
+ # normally an instance of BUILD_YARD.
447
283
  #
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
284
+ PUSH_GH_PAGES = ::Object.new
285
+ class << PUSH_GH_PAGES
286
+ # @private
287
+ def primary?(step_context)
288
+ step_context.component.settings.gh_pages_enabled
289
+ end
290
+
291
+ # @private
292
+ def dependencies(step_context)
293
+ [source_step(step_context)]
294
+ end
295
+
296
+ # @private
297
+ def run(step_context)
298
+ gh_pages_dir = setup_gh_pages_dir(step_context)
299
+ component_dir = ::File.expand_path(step_context.component.settings.gh_pages_directory, gh_pages_dir)
300
+ dest_dir = ::File.join(component_dir, "v#{step_context.release_version}")
301
+ check_existence(step_context, dest_dir)
302
+ copy_docs_dir(step_context, dest_dir)
303
+ update_docs_404_page(step_context, gh_pages_dir)
304
+ push_docs_to_git(step_context, gh_pages_dir)
459
305
  end
460
306
 
461
307
  private
462
308
 
463
- def setup_gh_pages_dir
464
- utils.log("Setting up gh-pages access ...")
309
+ def source_step(step_context)
310
+ step_context.option("source", default: "build_yard")
311
+ end
312
+
313
+ def setup_gh_pages_dir(step_context)
314
+ step_context.log("Setting up gh-pages access ...")
465
315
  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
316
+ gh_pages_dir = step_context.repository.checkout_separate_dir(
317
+ branch: "gh-pages", remote: step_context.git_remote, dir: step_context.temp_dir, gh_token: gh_token
468
318
  )
469
- exit_step("Unable to access the gh-pages branch.") unless @gh_pages_dir
470
- utils.log("Checked out gh-pages")
319
+ step_context.abort_pipeline("Unable to access the gh-pages branch.") unless gh_pages_dir
320
+ step_context.log("Checked out gh-pages")
321
+ gh_pages_dir
471
322
  end
472
323
 
473
- def check_existence
324
+ def check_existence(step_context, dest_dir)
474
325
  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
326
+ step_context.warning("Docs already published for #{step_context.release_description}. Skipping.")
327
+ step_context.add_success("Docs already published for #{step_context.release_description}")
328
+ step_context.exit_step
478
329
  end
479
- utils.log("Verified docs not yet published for #{component.name} #{release_version}")
330
+ step_context.log("Verified docs not yet published for #{step_context.release_description}")
480
331
  end
481
332
 
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)
333
+ def copy_docs_dir(step_context, dest_dir)
334
+ step_name = source_step(step_context)
335
+ source_dir = ::File.join(step_context.output_dir(step_name), "doc")
336
+ unless ::File.directory?(source_dir)
337
+ step_context.abort_pipeline("The output of step #{step_name} did not include built docs at #{source_dir}")
338
+ end
339
+ ::FileUtils.mkdir_p(::File.dirname(dest_dir))
340
+ ::FileUtils.cp_r(source_dir, dest_dir)
486
341
  end
487
342
 
488
- def update_docs_404_page
489
- path = ::File.join(@gh_pages_dir, "404.html")
343
+ def update_docs_404_page(step_context, gh_pages_dir)
344
+ path = ::File.join(gh_pages_dir, "404.html")
490
345
  content = ::File.read(path)
491
- content.sub!(/#{component.settings.gh_pages_version_var} = "[\w.]+";/,
492
- "#{component.settings.gh_pages_version_var} = \"#{release_version}\";")
346
+ version_var = step_context.component.settings.gh_pages_version_var
347
+ content.sub!(/#{version_var} = "[\w.]+";/,
348
+ "#{version_var} = \"#{step_context.release_version}\";")
493
349
  ::File.write(path, content)
494
350
  end
495
351
 
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.")
352
+ def push_docs_to_git(step_context, gh_pages_dir)
353
+ ::Dir.chdir(gh_pages_dir) do
354
+ step_context.repository.git_commit("Generated docs for #{step_context.release_description}",
355
+ signoff: step_context.repository.settings.signoff_commits?)
356
+ if step_context.dry_run?
357
+ step_context.add_success("DRY RUN documentation published for #{step_context.release_description}.")
358
+ step_context.log("DRY RUN: Documentation not actually published to gh-pages.")
503
359
  else
504
- result = utils.exec(["git", "push", git_remote, "gh-pages"], out: [:child, :err])
360
+ result = step_context.utils.exec(["git", "push", step_context.git_remote, "gh-pages"],
361
+ out: [:child, :err])
505
362
  unless result.success?
506
- exit_step("Docs publication failed for #{component.name} #{release_version}." \
507
- " Check the logs for details.")
363
+ step_context.abort_pipeline("Docs publication failed for #{step_context.release_description}." \
364
+ " Check the logs for details.")
508
365
  end
509
- performer_result.successes << "Published documentation for #{component.name} #{release_version}."
510
- utils.log("Documentation publish successful.")
366
+ step_context.add_success("Published documentation for #{step_context.release_description}.")
367
+ step_context.log("Documentation publish successful.")
511
368
  end
512
369
  end
513
370
  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
371
  end
523
372
 
524
373
  ##
525
374
  # A step that creates a GitHub tag and release.
526
375
  #
527
- class GitHubRelease < Base
528
- ##
529
- # Run this step
530
- #
531
- def run
532
- check_existence
533
- push_tag
376
+ RELEASE_GITHUB = ::Object.new
377
+ class << RELEASE_GITHUB
378
+ # @private
379
+ def primary?(_step_context)
380
+ true
381
+ end
382
+
383
+ # @private
384
+ def run(step_context)
385
+ check_existence(step_context)
386
+ push_tag(step_context)
534
387
  end
535
388
 
536
389
  private
537
390
 
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}",
391
+ def check_existence(step_context)
392
+ tag_name = step_context.tag_name
393
+ repo_path = step_context.repository.settings.repo_path
394
+ step_context.log("Checking whether #{tag_name} already exists...")
395
+ cmd = ["gh", "api", "repos/#{repo_path}/releases/tags/#{tag_name}",
541
396
  "-H", "Accept: application/vnd.github.v3+json"]
542
- result = utils.exec(cmd, out: :null, err: :null)
397
+ result = step_context.utils.exec(cmd, out: :null, err: :null)
543
398
  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
399
+ step_context.warning("GitHub tag #{tag_name} already exists. Skipping.")
400
+ step_context.add_success("GitHub tag #{tag_name} already exists.")
401
+ step_context.exit_step
547
402
  end
548
- utils.log("GitHub tag #{tag_name} has not yet been created.")
403
+ step_context.log("GitHub tag #{tag_name} has not yet been created.")
549
404
  end
550
405
 
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
406
+ def push_tag(step_context)
407
+ tag_name = step_context.tag_name
408
+ repo_path = step_context.repository.settings.repo_path
409
+ step_context.log("Creating GitHub release #{tag_name}...")
410
+ changelog_file = step_context.component.changelog_file
411
+ changelog_content = changelog_file.read_and_verify_latest_entry(step_context.release_version)
412
+ release_sha = step_context.repository.current_sha
555
413
  body = ::JSON.dump(tag_name: tag_name,
556
414
  target_commitish: release_sha,
557
- name: "#{component.name} #{release_version}",
415
+ name: step_context.release_description,
558
416
  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.")
417
+ if step_context.dry_run?
418
+ step_context.add_success("DRY RUN GitHub tag #{tag_name}.")
419
+ step_context.log("DRY RUN: GitHub tag #{tag_name} not actually created.")
562
420
  else
563
- cmd = ["gh", "api", "repos/#{repo_settings.repo_path}/releases", "--input", "-",
421
+ cmd = ["gh", "api", "repos/#{repo_path}/releases", "--input", "-",
564
422
  "-H", "Accept: application/vnd.github.v3+json"]
565
- result = utils.exec(cmd, in: [:string, body], out: :null)
423
+ result = step_context.utils.exec(cmd, in: [:string, body], out: :null)
566
424
  unless result.success?
567
- exit_step("Unable to create release #{tag_name}. Check the logs for details.")
425
+ step_context.abort_pipeline("Unable to create release #{tag_name}. Check the logs for details.")
568
426
  end
569
- performer_result.successes << "Created release with tag #{tag_name} on GitHub."
570
- utils.log("GitHub release successful.")
427
+ step_context.add_success("Created release with tag #{tag_name} on GitHub.")
428
+ step_context.log("GitHub release successful.")
571
429
  end
572
430
  end
573
-
574
- def tag_name
575
- "#{component.name}/v#{release_version}"
576
- end
577
431
  end
578
432
  end
579
433
  end