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



228
229
230
231
232
233
234
235
236
237
# File 'lib/git_reflow/workflow.rb', line 228

def after(name, &block)
  name = name.to_sym
  if commands[name].nil?
    logger.error "Attempted to register (after) callback for non-existing command: #{name}"
  else
    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



208
209
210
211
212
213
214
215
216
217
# File 'lib/git_reflow/workflow.rb', line 208

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

#callbacksObject



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

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

#callbacks=(callback_hash) ⇒ Object



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

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



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
192
193
194
195
196
197
# File 'lib/git_reflow/workflow.rb', line 150

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

  logger.debug "adding new command '#{name}' with #{defaults.inspect}"
  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

    logger.debug "callbacks: #{callbacks.inspect}"
    Array(callbacks[:before][name]).each do |block|
      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

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

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

#command_docsObject



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

def command_docs
  @command_docs ||= {}
end

#command_docs=(command_doc_hash) ⇒ Object



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

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



245
246
247
248
249
250
251
252
253
# File 'lib/git_reflow/workflow.rb', line 245

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



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

def commands
  @commands ||= {}
end

#commands=(command_hash) ⇒ Object



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

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



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
285
286
287
288
289
290
# File 'lib/git_reflow/workflow.rb', line 258

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



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

def git_config
  GitReflow::Config
end

#git_serverObject



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

def git_server
  GitReflow.git_server
end

#helpObject

Outputs documentation for git-reflow



293
294
295
296
297
298
299
300
301
302
303
304
# File 'lib/git_reflow/workflow.rb', line 293

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

#logger(*args) ⇒ Object



81
82
83
84
85
86
87
# File 'lib/git_reflow/workflow.rb', line 81

def logger(*args)
  return @logger if defined?(@logger)

  @logger = GitReflow.try(:logger, *args) || GitReflow::Logger.new(*args)
rescue NoMethodError
  @logger = GitReflow::Logger.new(*args)
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



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
334
335
336
337
338
339
340
341
342
343
344
345
# File 'lib/git_reflow/workflow.rb', line 309

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|
      # There is a bug in Ruby that will not parse the flag value if no
      # help text is provided.  Fallback to the flag name.
      flag_help = command_docs[name][:flags][flag_name] || flag_name
      opts.on("-#{flag_name[0]}", "--#{flag_name} #{flag_name.upcase}", flag_help) do |f|
        options[kebab_to_underscore(flag_name)] = f || flag_default
      end
    end

    self.commands[name][:switches].each do |switch_name, switch_default|
      # There is a bug in Ruby that will not parse the switch value if no
      # help text is provided.  Fallback to the switch name.
      switch_help = command_docs[name][:switches][switch_name] || switch_name
      opts.on("-#{switch_name[0]}", "--[no-]#{switch_name}", switch_help) 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



119
120
121
122
123
124
125
126
# File 'lib/git_reflow/workflow.rb', line 119

def use(workflow_name)
  if workflows.key?(workflow_name)
    logger.debug "Using Workflow: #{workflow_name}"
    GitReflow::Workflows::Core.load_workflow(workflows[workflow_name])
  else
    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



93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/git_reflow/workflow.rb', line 93

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



110
111
112
113
# File 'lib/git_reflow/workflow.rb', line 110

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



132
133
134
135
136
137
138
# File 'lib/git_reflow/workflow.rb', line 132

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