Module: Lhj::Actions

Defined in:
lib/lhj/action/sh_helper.rb,
lib/lhj/helper/git_helper.rb

Defined Under Namespace

Modules: SharedValues

Constant Summary collapse

GIT_MERGE_COMMIT_FILTERING_OPTIONS =
[:include_merges, :exclude_merges, :only_include_merges].freeze

Class Method Summary collapse

Class Method Details

.git_authorObject

Deprecated.

Use git_author_email instead

Get the author email of the last git commit DEPRECATED: Use git_author_email instead.



85
86
87
88
# File 'lib/lhj/helper/git_helper.rb', line 85

def self.git_author
  UI.deprecated('`git_author` is deprecated. Please use `git_author_email` instead.')
  git_author_email
end

.git_author_emailObject

Get the author email of the last git commit



91
92
93
94
95
# File 'lib/lhj/helper/git_helper.rb', line 91

def self.git_author_email
  s = last_git_commit_formatted_with('%ae')
  return s if s.to_s.length > 0
  return nil
end

.git_branchObject

Returns the current git branch, or “HEAD” if it’s not checked out to any branch Can be replaced using the environment variable ‘GIT_BRANCH`



120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/lhj/helper/git_helper.rb', line 120

def self.git_branch
  env_name = SharedValues::GIT_BRANCH_ENV_VARS.find { |env_var| Lhj::Env.truthy?(env_var) }
  ENV.fetch(env_name.to_s) do
    # Rescues if not a git repo or no commits in a git repo
    begin
      Actions.sh("git rev-parse --abbrev-ref HEAD", log: false).chomp
    rescue => err
      UI.verbose("Error getting git branch: #{err.message}")
      nil
    end
  end
end

.git_log_between(pretty_format, from, to, merge_commit_filtering, date_format = nil, ancestry_path) ⇒ Object



9
10
11
12
13
14
15
16
17
18
19
20
21
# File 'lib/lhj/helper/git_helper.rb', line 9

def self.git_log_between(pretty_format, from, to, merge_commit_filtering, date_format = nil, ancestry_path)
  command = %w(git log)
  command << "--pretty=#{pretty_format}"
  command << "--date=#{date_format}" if date_format
  command << '--ancestry-path' if ancestry_path
  command << "#{from}...#{to}"
  command << git_log_merge_commit_filtering_option(merge_commit_filtering)
  # "*command" syntax expands "command" array into variable arguments, which
  # will then be individually shell-escaped by Actions.sh.
  Actions.sh(*command.compact, log: false).chomp
rescue
  nil
end

.git_log_last_commits(pretty_format, commit_count, merge_commit_filtering, date_format = nil, ancestry_path) ⇒ Object



23
24
25
26
27
28
29
30
31
32
33
# File 'lib/lhj/helper/git_helper.rb', line 23

def self.git_log_last_commits(pretty_format, commit_count, merge_commit_filtering, date_format = nil, ancestry_path)
  command = %w(git log)
  command << "--pretty=#{pretty_format}"
  command << "--date=#{date_format}" if date_format
  command << '--ancestry-path' if ancestry_path
  command << '-n' << commit_count.to_s
  command << git_log_merge_commit_filtering_option(merge_commit_filtering)
  Actions.sh(*command.compact, log: false).chomp
rescue
  nil
end

.git_log_merge_commit_filtering_option(merge_commit_filtering) ⇒ Object



134
135
136
137
138
139
140
141
142
143
# File 'lib/lhj/helper/git_helper.rb', line 134

def self.git_log_merge_commit_filtering_option(merge_commit_filtering)
  case merge_commit_filtering
  when :exclude_merges
    "--no-merges"
  when :only_include_merges
    "--merges"
  when :include_merges
    nil
  end
end

.last_git_commitObject

Returns the unwrapped subject and body of the last commit DEPRECATED: Use last_git_commit_message instead.



99
100
101
# File 'lib/lhj/helper/git_helper.rb', line 99

def self.last_git_commit
  last_git_commit_message
end

.last_git_commit_dictObject



59
60
61
62
63
64
65
66
67
68
69
# File 'lib/lhj/helper/git_helper.rb', line 59

def self.last_git_commit_dict
  return nil if last_git_commit_formatted_with('%an').nil?

  {
    author: last_git_commit_formatted_with('%an'),
    author_email: last_git_commit_formatted_with('%ae'),
    message: last_git_commit_formatted_with('%B'),
    commit_hash: last_git_commit_formatted_with('%H'),
    abbreviated_commit_hash: last_git_commit_formatted_with('%h')
  }
end

.last_git_commit_formatted_with(pretty_format, date_format = nil) ⇒ Object

Gets the last git commit information formatted into a String by the provided pretty format String. See the git-log documentation for valid format placeholders



73
74
75
76
77
78
79
80
# File 'lib/lhj/helper/git_helper.rb', line 73

def self.last_git_commit_formatted_with(pretty_format, date_format = nil)
  command = %w(git log -1)
  command << "--pretty=#{pretty_format}"
  command << "--date=#{date_format}" if date_format
  Actions.sh(*command.compact, log: false).chomp
rescue
  nil
end

.last_git_commit_hash(short) ⇒ Object

Get the hash of the last commit



111
112
113
114
115
116
# File 'lib/lhj/helper/git_helper.rb', line 111

def self.last_git_commit_hash(short)
  format_specifier = short ? '%h' : '%H'
  string = last_git_commit_formatted_with(format_specifier).to_s
  return string unless string.empty?
  return nil
end

.last_git_commit_messageObject

Returns the unwrapped subject and body of the last commit



104
105
106
107
108
# File 'lib/lhj/helper/git_helper.rb', line 104

def self.last_git_commit_message
  s = (last_git_commit_formatted_with('%B') || "").strip
  return s if s.to_s.length > 0
  nil
end

.last_git_tag_hash(tag_match_pattern = nil) ⇒ Object



35
36
37
38
39
40
# File 'lib/lhj/helper/git_helper.rb', line 35

def self.last_git_tag_hash(tag_match_pattern = nil)
  tag_pattern_param = tag_match_pattern ? "=#{tag_match_pattern}" : ''
  Actions.sh('git', 'rev-list', "--tags#{tag_pattern_param}", '--max-count=1').chomp
rescue
  nil
end

.last_git_tag_name(match_lightweight = true, tag_match_pattern = nil) ⇒ Object



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/lhj/helper/git_helper.rb', line 42

def self.last_git_tag_name(match_lightweight = true, tag_match_pattern = nil)
  hash = last_git_tag_hash(tag_match_pattern)
  # If hash is nil (command fails), "git describe" command below will still
  # run and provide some output, although it's definitely not going to be
  # anything reasonably expected. Bail out early.
  return unless hash

  command = %w(git describe)
  command << '--tags' if match_lightweight
  command << hash
  command << '--match' if tag_match_pattern
  command << tag_match_pattern if tag_match_pattern
  Actions.sh(*command.compact, log: false).chomp
rescue
  nil
end

.sh(*command, log: true, error_callback: nil, &b) ⇒ Object

Execute a shell command This method will output the string and execute it Just an alias for sh_no_action When running this in tests, it will return the actual command instead of executing it

Parameters:

  • log (Boolean) (defaults to: true)

    should fastlane print out the executed command

  • error_callback (Block) (defaults to: nil)

    a callback invoked with the command output if there is a non-zero exit status



11
12
13
# File 'lib/lhj/action/sh_helper.rb', line 11

def self.sh(*command, log: true, error_callback: nil, &b)
  sh_control_output(*command, print_command: log, print_command_output: log, error_callback: error_callback, &b)
end

.sh_control_output(*command, print_command: true, print_command_output: true, error_callback: nil) {|status, result, cmd| ... } ⇒ Object

rubocop: disable Metrics/PerceivedComplexity

Parameters:

  • command

    The command to be executed (variadic)

  • print_command (Boolean) (defaults to: true)

    Should we print the command that’s being executed

  • print_command_output (Boolean) (defaults to: true)

    Should we print the command output during execution

  • error_callback (Block) (defaults to: nil)

    A block that’s called if the command exits with a non-zero status

Yields:

  • (status, result, cmd)

    The return status of the command, all output from the command and an equivalent shell command

Yield Parameters:

  • status (Process::Status)

    A Process::Status indicating the status of the completed command

  • result (String)

    The complete output to stdout and stderr of the completed command

  • cmd (String)

    A shell command equivalent to the arguments passed



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/lhj/action/sh_helper.rb', line 32

def self.sh_control_output(*command, print_command: true, print_command_output: true, error_callback: nil)
  print_command = print_command_output = true if $troubleshoot
  # Set the encoding first, the user might have set it wrong
  previous_encoding = [Encoding.default_external, Encoding.default_internal]
  Encoding.default_external = Encoding::UTF_8
  Encoding.default_internal = Encoding::UTF_8

  # Workaround to support previous Fastlane syntax.
  # This has some limitations. For example, it requires the caller to shell escape
  # everything because of usages like ["ls -la", "/tmp"] instead of ["ls", "-la", "/tmp"].
  command = [command.first.join(" ")] if command.length == 1 && command.first.kind_of?(Array)

  shell_command = shell_command_from_args(*command)
  UI.command(shell_command) if print_command

  result = ''
  exit_status = nil
  if sh_enabled?
    # The argument list is passed directly to Open3.popen2e, which
    # handles the variadic argument list in the same way as Kernel#spawn.
    # (http://ruby-doc.org/core-2.4.2/Kernel.html#method-i-spawn) or
    # Process.spawn (http://ruby-doc.org/core-2.4.2/Process.html#method-c-spawn).
    #
    # sh "ls -la /Applications/Xcode\ 7.3.1.app"
    # sh "ls", "-la", "/Applications/Xcode 7.3.1.app"
    # sh({ "FOO" => "Hello" }, "echo $FOO")
    Open3.popen2e(*command) do |stdin, io, thread|
      io.sync = true
      io.each do |line|
        UI.command_output(line.strip) if print_command_output
        result << line
      end
      exit_status = thread.value
    end

    # Checking Process::Status#exitstatus instead of #success? makes for more
    # testable code. (Tests mock exitstatus only.) This is also consistent
    # with previous implementations of sh and... probably portable to all
    # relevant platforms.
    if exit_status.exitstatus != 0
      message = if print_command
                  "Exit status of command '#{shell_command}' was #{exit_status.exitstatus} instead of 0."
                else
                  "Shell command exited with exit status #{exit_status.exitstatus} instead of 0."
                end
      message += "\n#{result}" if print_command_output

      if error_callback || block_given?
        UI.error(message)
        # block notified below, on success or failure
        error_callback && error_callback.call(result)
      else
        UI.shell_error!(message)
      end
    end
  else
    result << shell_command # only for the tests
  end

  if block_given?
    # Avoid yielding nil in tests. $? will be meaningless, but calls to
    # it will not crash. There is no Process::Status.new. The alternative
    # is to move this inside the sh_enabled? check and not yield in tests.
    return yield(exit_status || $?, result, shell_command)
  end
  result
rescue => ex
  raise ex
ensure
  Encoding.default_external = previous_encoding.first
  Encoding.default_internal = previous_encoding.last
end

.sh_enabled?Boolean

Returns:

  • (Boolean)


19
20
21
# File 'lib/lhj/action/sh_helper.rb', line 19

def self.sh_enabled?
  true
end

.sh_no_action(*command, log: true, error_callback: nil, &b) ⇒ Object



15
16
17
# File 'lib/lhj/action/sh_helper.rb', line 15

def self.sh_no_action(*command, log: true, error_callback: nil, &b)
  sh_control_output(*command, print_command: log, print_command_output: log, error_callback: error_callback, &b)
end

.shell_command_from_args(*args) ⇒ String

Used to produce a shell command string from a list of arguments that may be passed to methods such as Kernel#system, Kernel#spawn and Open3.popen2e in order to print the command to the terminal. The same *args are passed directly to a system call (Open3.popen2e). This interpretation is not used when executing a command.

Parameters:

  • args

    Any number of arguments used to construct a command

Returns:

  • (String)

    A shell command representing the arguments passed in

Raises:

  • (ArgumentError)

    If no arguments passed



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/lhj/action/sh_helper.rb', line 115

def self.shell_command_from_args(*args)
  raise ArgumentError, "sh requires at least one argument" unless args.count > 0

  command = ""

  # Optional initial environment Hash
  if args.first.kind_of?(Hash)
    command = args.shift.map { |k, v| "#{k}=#{v.to_s}" }.join(" ") + " "
  end

  # Support [ "/usr/local/bin/foo", "foo" ], "-x", ...
  if args.first.kind_of?(Array)
    command += args.shift.first.to_s + " " + args.join(' ')
    command.chomp!(" ")
  elsif args.count == 1 && args.first.kind_of?(String)
    command += args.first
  else
    command += args.join(' ')
  end

  command
end