Class: Fastlane::Actions::EnsureGitStatusCleanAction

Inherits:
Fastlane::Action show all
Defined in:
fastlane/lib/fastlane/actions/ensure_git_status_clean.rb

Overview

Raises an exception and stop the lane execution if the repo is not in a clean state

Constant Summary

Constants inherited from Fastlane::Action

Fastlane::Action::AVAILABLE_CATEGORIES, Fastlane::Action::RETURN_TYPES

Class Method Summary collapse

Methods inherited from Fastlane::Action

action_name, authors, deprecated_notes, lane_context, method_missing, other_action, return_type, return_value, sample_return_value, shell_out_should_use_bundle_exec?, step_text

Class Method Details

.authorObject



90
91
92
# File 'fastlane/lib/fastlane/actions/ensure_git_status_clean.rb', line 90

def self.author
  ["lmirosevic", "antondomashnev"]
end

.available_optionsObject



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'fastlane/lib/fastlane/actions/ensure_git_status_clean.rb', line 100

def self.available_options
  [
    FastlaneCore::ConfigItem.new(key: :show_uncommitted_changes,
                                 env_name: "FL_ENSURE_GIT_STATUS_CLEAN_SHOW_UNCOMMITTED_CHANGES",
                                 description: "The flag whether to show uncommitted changes if the repo is dirty",
                                 optional: true,
                                 default_value: false,
                                 type: Boolean),
    FastlaneCore::ConfigItem.new(key: :show_diff,
                                 env_name: "FL_ENSURE_GIT_STATUS_CLEAN_SHOW_DIFF",
                                 description: "The flag whether to show the git diff if the repo is dirty",
                                 optional: true,
                                 default_value: false,
                                 type: Boolean),
    FastlaneCore::ConfigItem.new(key: :ignored,
                                 env_name: "FL_ENSURE_GIT_STATUS_CLEAN_IGNORED_FILE",
                                 description: [
                                   "The handling mode of the ignored files. The available options are: `'traditional'`, `'none'` (default) and `'matching'`.",
                                   "Specifying `'none'` to this parameter is the same as not specifying the parameter at all, which means that no ignored file will be used to check if the repo is dirty or not.",
                                   "Specifying `'traditional'` or `'matching'` causes some ignored files to be used to check if the repo is dirty or not (more info in the official docs: https://git-scm.com/docs/git-status#Documentation/git-status.txt---ignoredltmodegt)"
                                 ].join(" "),
                                 optional: true,
                                 verify_block: proc do |value|
                                   mode = value.to_s
                                   modes = %w(traditional none matching)

                                   UI.user_error!("Unsupported mode, must be: #{modes}") unless modes.include?(mode)
                                 end),
    FastlaneCore::ConfigItem.new(key: :ignore_files,
                                 env_name: "FL_ENSURE_GIT_STATUS_CLEAN_IGNORE_FILES",
                                 description: "Array of files to ignore",
                                 optional: true,
                                 type: Array)
  ]
end

.categoryObject



136
137
138
# File 'fastlane/lib/fastlane/actions/ensure_git_status_clean.rb', line 136

def self.category
  :source_control
end

.descriptionObject



72
73
74
# File 'fastlane/lib/fastlane/actions/ensure_git_status_clean.rb', line 72

def self.description
  "Raises an exception if there are uncommitted git changes"
end

.detailsObject



76
77
78
79
80
81
82
# File 'fastlane/lib/fastlane/actions/ensure_git_status_clean.rb', line 76

def self.details
  [
    "A sanity check to make sure you are working in a repo that is clean.",
    "Especially useful to put at the beginning of your Fastfile in the `before_all` block, if some of your other actions will touch your filesystem, do things to your git repo, or just as a general reminder to save your work.",
    "Also needed as a prerequisite for some other actions like `reset_git_repo`."
  ].join("\n")
end

.example_codeObject



94
95
96
97
98
# File 'fastlane/lib/fastlane/actions/ensure_git_status_clean.rb', line 94

def self.example_code
  [
    'ensure_git_status_clean'
  ]
end

.is_supported?(platform) ⇒ Boolean

Returns:



140
141
142
# File 'fastlane/lib/fastlane/actions/ensure_git_status_clean.rb', line 140

def self.is_supported?(platform)
  true
end

.outputObject



84
85
86
87
88
# File 'fastlane/lib/fastlane/actions/ensure_git_status_clean.rb', line 84

def self.output
  [
    ['GIT_REPO_WAS_CLEAN_ON_START', 'Stores the fact that the git repo was clean at some point']
  ]
end

.path_from_git_status_line(line) ⇒ Object



59
60
61
62
63
64
65
66
67
68
69
70
# File 'fastlane/lib/fastlane/actions/ensure_git_status_clean.rb', line 59

def self.path_from_git_status_line(line)
  # Extract the file path from the line based on https://git-scm.com/docs/git-status#_output.
  # The first two characters indicate the status of the file path (e.g. ' M')
  #  M App/script.sh
  #
  # If the file path is renamed, the original path is also included in the line (e.g. 'R  ORIG_PATH -> PATH')
  # R  App/script.sh -> App/script_renamed.sh
  #
  path = line.match(/^.. (.* -> )?(.*)$/)[2]
  path = path.delete_prefix('"').delete_suffix('"')
  return path
end

.run(params) ⇒ Object



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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
# File 'fastlane/lib/fastlane/actions/ensure_git_status_clean.rb', line 9

def self.run(params)
  # Build command
  if params[:ignored]
    ignored_mode = params[:ignored]
    ignored_mode = 'no' if ignored_mode == 'none'
    command = "git status --porcelain --ignored='#{ignored_mode}'"
  else
    command = "git status --porcelain"
  end

  # Don't log if manually ignoring files as it will emulate output later
  print_output = params[:ignore_files].nil?
  repo_status = Actions.sh(command, log: print_output)

  # Manual post processing trying to ignore certain file paths
  if (ignore_files = params[:ignore_files])
    repo_status = repo_status.lines.reject do |line|
      path = path_from_git_status_line(line)
      next if path.empty?

      was_found = ignore_files.include?(path)

      UI.message("Ignoring '#{path}'") if was_found

      was_found
    end.join("")

    # Emulate the output format of `git status --porcelain`
    UI.command(command)
    repo_status.lines.each do |line|
      UI.message("" + line.chomp.magenta)
    end
  end

  repo_clean = repo_status.empty?

  if repo_clean
    UI.success('Git status is clean, all good! 💪')
    Actions.lane_context[SharedValues::GIT_REPO_WAS_CLEAN_ON_START] = true
  else
    error_message = "Git repository is dirty! Please ensure the repo is in a clean state by committing/stashing/discarding all changes first."
    error_message += "\nUncommitted changes:\n#{repo_status}" if params[:show_uncommitted_changes]
    if params[:show_diff]
      repo_diff = Actions.sh("git diff")
      error_message += "\nGit diff: \n#{repo_diff}"
    end
    UI.user_error!(error_message)
  end
end