Module: GitReflow::Workflow::ClassMethods

Includes:
GitHelpers, Sandbox
Defined in:
lib/git_reflow/workflow.rb

Constant Summary

Constants included from Sandbox

Sandbox::COLOR_FOR_LABEL

Instance Method Summary collapse

Methods included from GitHelpers

#append_to_merge_commit_message, #current_branch, #default_base_branch, #default_editor, #fetch_destination, #get_first_commit_message, #git_editor_command, #git_root_dir, #merge_commit_template, #merge_message_path, #pull_request_template, #push_current_branch, #remote_repo_name, #remote_user, #update_current_branch, #update_destination, #update_feature_branch

Methods included from Sandbox

#run, #run_command_with_label, #say

Instance Method Details

#after(name) { ... } ⇒ Object

Stores a Proc to be called once the command successfully finishes

Procs declared with ‘after` are executed sequentially in the order they are defined in a custom Workflow file.

are executed in the context of ‘GitReflow::Workflows::Core`

Parameters:

  • name (Symbol)

    the name of the method to create

Yields:

  • A block to be executed after the given command. These blocks



222
223
224
225
226
227
228
229
230
231
# File 'lib/git_reflow/workflow.rb', line 222

def after(name, &block)
  name = name.to_sym
  if commands[name].nil?
    GitReflow.logger.error "Attempted to register (after) callback for non-existing command: #{name}"
  else
    GitReflow.logger.debug "(after) callback registered for: #{name}"
    callbacks[:after][name] ||= []
    callbacks[:after][name] << block
  end
end

#before(name) { ... } ⇒ Object

Stores a Proc to be called once the command successfully finishes

Procs declared with ‘before` are executed sequentially in the order they are defined in a custom Workflow file.

are executed in the context of ‘GitReflow::Workflows::Core`

Parameters:

  • name (Symbol)

    the name of the method to create

Yields:

  • A block to be executed before the given command. These blocks



202
203
204
205
206
207
208
209
210
211
# File 'lib/git_reflow/workflow.rb', line 202

def before(name, &block)
  name = name.to_sym
  if commands[name].nil?
    GitReflow.logger.error "Attempted to register (before) callback for non-existing command: #{name}"
  else
    GitReflow.logger.debug "(before) callback registered for: #{name}"
    callbacks[:before][name] ||= []
    callbacks[:before][name] << block
  end
end

#callbacksObject



60
61
62
63
64
65
# File 'lib/git_reflow/workflow.rb', line 60

def callbacks
  @callbacks ||= {
    before: {},
    after: {}
  }
end

#callbacks=(callback_hash) ⇒ Object



67
68
69
# File 'lib/git_reflow/workflow.rb', line 67

def callbacks=(callback_hash)
  @callbacks = callback_hash
end

#command(name, **params) {|a:, b:, c:, ...| ... } ⇒ Object

Creates a singleton method on the included class

This method will take any number of keyword parameters. If @defaults keyword is provided, and the given key(s) in the defaults are not provided as keyword parameters, then it will use the value given in the defaults for that parameter.

Parameters:

  • name (Symbol)

    the name of the method to create

  • defaults (Hash)

    keyword arguments to provide fallbacks for

Yields:

  • (a:, b:, c:, ...)

    Invokes the block with an arbitrary number of keyword arguments



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/git_reflow/workflow.rb', line 145

def command(name, **params, &block)
  params[:flags]     ||= {}
  params[:switches]  ||= {}
  params[:arguments] ||= {}
  defaults           ||= params[:arguments].merge(params[:flags]).merge(params[:switches])

  # Ensure flags and switches use kebab-case
  kebab_case_keys!(params[:flags])
  kebab_case_keys!(params[:switches])

  # Register the command with the workflow so that we can properly handle
  # option parsing from the command line
  self.commands[name] = params
  self.command_docs[name] = params

  self.define_singleton_method(name) do |args = {}|
    args_with_defaults = {}
    args.each do |name, value|
      if "#{value}".length <= 0 && !defaults[name].nil?
        args_with_defaults[name] = defaults[name]
      else
        args_with_defaults[name] = value
      end
    end

    defaults.each do |name, value|
      if "#{args_with_defaults[name]}".length <= 0
        args_with_defaults[name] = value
      end
    end

    GitReflow.logger.debug "callbacks: #{callbacks.inspect}"
    Array(callbacks[:before][name]).each do |block|
      GitReflow.logger.debug "(before) callback running for `#{name}` command..."
      argument_overrides = block.call(**args_with_defaults) || {}
      args_with_defaults.merge!(argument_overrides) if argument_overrides.is_a?(Hash)
    end

    GitReflow.logger.info "Running command `#{name}` with args: #{args_with_defaults.inspect}..."
    block.call(**args_with_defaults)

    Array(callbacks[:after][name]).each do |block|
      GitReflow.logger.debug "(after) callback running for `#{name}` command..."
      block.call(**args_with_defaults)
    end
  end
end

#command_docsObject



52
53
54
# File 'lib/git_reflow/workflow.rb', line 52

def command_docs
  @command_docs ||= {}
end

#command_docs=(command_doc_hash) ⇒ Object



56
57
58
# File 'lib/git_reflow/workflow.rb', line 56

def command_docs=(command_doc_hash)
  @command_docs = command_doc_hash
end

#command_help(name, summary:, arguments: {}, flags: {}, switches: {}, description: "") ⇒ Object

Creates a singleton method on the included class

This method updates the help text associated with the provided command.

Parameters:

  • name (Symbol)

    the name of the command to add/update help text for

  • defaults (Hash)

    keyword arguments to provide fallbacks for



239
240
241
242
243
244
245
246
247
# File 'lib/git_reflow/workflow.rb', line 239

def command_help(name, summary:, arguments: {}, flags: {}, switches: {},  description: "")
  command_docs[name] = {
    summary: summary,
    description: description,
    arguments: arguments,
    flags: kebab_case_keys!(flags),
    switches: kebab_case_keys!(switches)
  }
end

#commandsObject



44
45
46
# File 'lib/git_reflow/workflow.rb', line 44

def commands
  @commands ||= {}
end

#commands=(command_hash) ⇒ Object



48
49
50
# File 'lib/git_reflow/workflow.rb', line 48

def commands=(command_hash)
  @commands = command_hash
end

#documentation_for_command(name) ⇒ Object

Outputs documentation for the provided command

Parameters:

  • name (Symbol)

    the name of the command to output help text for



252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
# File 'lib/git_reflow/workflow.rb', line 252

def documentation_for_command(name)
  name = name.to_sym
  docs = command_docs[name]
  if !docs.nil?
    GitReflow.say "USAGE"
    GitReflow.say "    git-reflow #{name} [command options] #{docs[:arguments].keys.map {|arg| "[#{arg}]" }.join(' ')}"
    if docs[:arguments].any?
      GitReflow.say "ARGUMENTS"
      docs[:arguments].each do |arg_name, arg_desc|
        default_text = commands[name][:arguments][arg_name].nil? ? "" : "(default: #{commands[name][:arguments][arg_name]}) "
        GitReflow.say "    #{arg_name}#{default_text}#{arg_desc}"
      end
    end
    if docs[:flags].any? || docs[:switches].any?
      cmd = commands[name.to_sym]
      GitReflow.say "COMMAND OPTIONS"
      docs[:flags].each do |flag_name, flag_desc|
        flag_names = ["-#{flag_name.to_s[0]}", "--#{flag_name}"]
        flag_default = cmd[:flags][flag_name]

        GitReflow.say "    #{flag_names}#{!flag_default.nil? ? "(default: #{flag_default})  " : ""}#{flag_desc}"
      end
      docs[:switches].each do |switch_name, switch_desc|
        switch_names = [switch_name.to_s[0], "-#{switch_name}"].map {|s| "-#{s}" }.join(', ')
        switch_default = cmd[:switches][switch_name]

        GitReflow.say "    #{switch_names}#{!switch_default.nil? ? "(default: #{switch_default})  " : ""}#{switch_desc}"
      end
    end
  else
    help
  end
end

#git_configObject

Proxy our Config class so that it’s available in workflow files



72
73
74
# File 'lib/git_reflow/workflow.rb', line 72

def git_config
  GitReflow::Config
end

#git_serverObject



76
77
78
# File 'lib/git_reflow/workflow.rb', line 76

def git_server
  GitReflow.git_server
end

#helpObject

Outputs documentation for git-reflow



287
288
289
290
291
292
293
294
295
296
297
298
# File 'lib/git_reflow/workflow.rb', line 287

def help
  GitReflow.say "NAME"
  GitReflow.say "    git-reflow – Git Reflow manages your git workflow."
  GitReflow.say "VERSION"
  GitReflow.say "    #{GitReflow::VERSION}"
  GitReflow.say "USAGE"
  GitReflow.say "    git-reflow command [command options] [arguments...]"
  GitReflow.say "COMMANDS"
  command_docs.each do |command_name, command_doc|
    GitReflow.say "    #{command_name}\t– #{command_doc[:summary]}"
  end
end

#loggerObject



80
81
82
# File 'lib/git_reflow/workflow.rb', line 80

def logger
  GitReflow.logger
end

#parse_command_options!(name) ⇒ Object

Parses ARGV for the provided git-reflow command name

Parameters:

  • name (Symbol, String)

    the name of the git-reflow command to parse from ARGV



303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
# File 'lib/git_reflow/workflow.rb', line 303

def parse_command_options!(name)
  name = name.to_sym
  options = {}
  docs = command_docs[name]
  OptionParser.new do |opts|
    opts.banner = "USAGE:\n  git-reflow #{name} [command options] #{docs[:arguments].keys.map {|arg| "[#{arg}]" }.join(' ')}"
    opts.separator  ""
    opts.separator  "COMMAND OPTIONS:" if docs[:flags].any? || docs[:switches].any?

    self.commands[name][:flags].each do |flag_name, flag_default|
      opts.on("-#{flag_name[0]}", "--#{flag_name} #{flag_name.upcase}", command_docs[name][:flags][flag_name]) do |f|
        options[kebab_to_underscore(flag_name)] = f || flag_default
      end
    end

    self.commands[name][:switches].each do |switch_name, switch_default|
      opts.on("-#{switch_name[0]}", "--[no-]#{switch_name}", command_docs[name][:switches][switch_name]) do |s|
        options[kebab_to_underscore(switch_name)] = s || switch_default
      end
    end
  end.parse!

  # Add arguments to optiosn to pass to defined commands
  commands[name][:arguments].each do |arg_name, arg_default|
    options[arg_name] = ARGV.shift || arg_default
  end
  options
rescue OptionParser::InvalidOption
  documentation_for_command(name)
  exit 1
end

#use(workflow_name) ⇒ Object

Loads a pre-defined workflow (FlatMergeWorkflow) from within another Workflow file

Parameters:

  • name (String)

    the name of the Workflow file to use as a basis



114
115
116
117
118
119
120
121
# File 'lib/git_reflow/workflow.rb', line 114

def use(workflow_name)
  if workflows.key?(workflow_name)
    GitReflow.logger.debug "Using Workflow: #{workflow_name}"
    GitReflow::Workflows::Core.load_workflow(workflows[workflow_name])
  else
    GitReflow.logger.error "Tried to use non-existent Workflow: #{workflow_name}"
  end
end

#use_gem(name, *args) ⇒ Object

Checks for an installed gem, and if none is installed use bundler’s inline gemfile to install it.

Parameters:

  • name (String)

    the name of the gem to require as a dependency



88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/git_reflow/workflow.rb', line 88

def use_gem(name, *args)
  run("gem list -ie #{name}", loud: false, raise: true)
  logger.info "Using installed gem '#{name}' with options: #{args.inspect}"
rescue ::GitReflow::Sandbox::CommandError => e
  abort e.message unless e.output =~ /\Afalse/
  logger.info "Installing gem '#{name}' with options: #{args.inspect}"
  say "Installing gem '#{name}'...", :notice
  gemfile do
    source "https://rubygems.org"
    gem name, *args
  end
end

#use_gemfile { ... } ⇒ Object

Use bundler’s inline gemfile to install dependencies. See: bundler.io/v1.16/guides/bundler_in_a_single_file_ruby_script.html

Yields:

  • A block to be executed in the context of Bundler’s ‘gemfile` DSL



105
106
107
108
# File 'lib/git_reflow/workflow.rb', line 105

def use_gemfile(&block)
  logger.info "Using a custom gemfile"
  gemfile(true, &block)
end

#workflowsHash?

Keeps track of available workflows when using ‘.use(workflow_name)` Workflow file

Returns:

  • (Hash, nil)

    A hash with [workflow_name, workflow_path] as key/value pairs



127
128
129
130
131
132
133
# File 'lib/git_reflow/workflow.rb', line 127

def workflows
  return @workflows if @workflows
  workflow_paths = Dir["#{File.dirname(__FILE__)}/workflows/*Workflow"]
  @workflows = {}
  workflow_paths.each { |p| @workflows[File.basename(p)] = p }
  @workflows
end