Class: Fastlane::Actions::AnalyzeCommitsAction

Inherits:
Action
  • Object
show all
Defined in:
lib/fastlane/plugin/semantic_release2/actions/analyze_commits.rb

Documentation collapse

Class Method Summary collapse

Class Method Details

.authorsObject



305
306
307
308
# File 'lib/fastlane/plugin/semantic_release2/actions/analyze_commits.rb', line 305

def self.authors
  # So no one will ever forget your contribution to fastlane :) You are awesome btw!
  ["xotahal"]
end

.available_optionsObject



225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
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
# File 'lib/fastlane/plugin/semantic_release2/actions/analyze_commits.rb', line 225

def self.available_options
  # Define all options your action supports.

  # Below a few examples
  [
    FastlaneCore::ConfigItem.new(
      key: :match,
      description: "Match parameter of git describe. See man page of git describe for more info",
      verify_block: proc do |value|
        UI.user_error!("No match for analyze_commits action given, pass using `match: 'expr'`") unless value && !value.empty?
      end
    ),
    FastlaneCore::ConfigItem.new(
      key: :start,
      description: "Use a commit hash instead of HEAD",
      type: String,
      optional: true
    ),
    FastlaneCore::ConfigItem.new(
      key: :end,
      description: "Use a commit hash instead of HEAD",
      type: String,
      optional: true
    ),
    FastlaneCore::ConfigItem.new(
      key: :releases,
      description: "Map types of commit to release (major, minor, patch)",
      default_value: { fix: "patch", feat: "minor" },
      type: Hash
    ),
    FastlaneCore::ConfigItem.new(
      key: :codepush_friendly,
      description: "These types are consider as codepush friendly automatically",
      default_value: ["chore", "test", "docs"],
      type: Array,
      optional: true
    ),
    FastlaneCore::ConfigItem.new(
      key: :tag_version_match,
      description: "To parse version number from tag name",
      default_value: '\d+\.\d+\.\d+'
    ),
    FastlaneCore::ConfigItem.new(
      key: :ignore_scopes,
      description: "To ignore certain scopes when calculating releases",
      default_value: [],
      type: Array,
      optional: true
    ),
    FastlaneCore::ConfigItem.new(
      key: :debug,
      description: "True if you want to log out a debug info",
      default_value: false,
      type: Boolean,
      optional: true
    )
  ]
end

.descriptionObject



217
218
219
# File 'lib/fastlane/plugin/semantic_release2/actions/analyze_commits.rb', line 217

def self.description
  "Finds a tag of last release and determinates version of next release"
end

.detailsObject



221
222
223
# File 'lib/fastlane/plugin/semantic_release2/actions/analyze_commits.rb', line 221

def self.details
  "This action will find a last release tag and analyze all commits since the tag. It uses conventional commits. Every time when commit is marked as fix or feat it will increase patch or minor number (you can setup this default behaviour). After all it will suggest if the version should be released or not."
end

.get_commits_from_hash(params) ⇒ Object



33
34
35
36
37
38
39
40
41
# File 'lib/fastlane/plugin/semantic_release2/actions/analyze_commits.rb', line 33

def self.get_commits_from_hash(params)
  commits = Helper::SemanticReleaseHelper.git_log(
    pretty: '%s|%b|>',
    start: params[:hash],
    end: params[:end],
    debug: params[:debug]
  )
  commits.split("|>")
end

.get_last_tag(params) ⇒ Object



19
20
21
22
23
24
25
26
# File 'lib/fastlane/plugin/semantic_release2/actions/analyze_commits.rb', line 19

def self.get_last_tag(params)
  # Try to find the tag
  command = "git describe --tags --match=#{params[:match]}"
  Actions.sh(command, log: params[:debug])
rescue
  UI.message("Tag was not found for match pattern - #{params[:match]}")
  ''
end

.get_last_tag_hash(params) ⇒ Object



28
29
30
31
# File 'lib/fastlane/plugin/semantic_release2/actions/analyze_commits.rb', line 28

def self.get_last_tag_hash(params)
  command = "git rev-list -n 1 refs/tags/#{params[:tag_name]}"
  Actions.sh(command, log: params[:debug]).chomp
end

.is_codepush_friendly(params) ⇒ Object



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
198
199
200
201
202
203
204
# File 'lib/fastlane/plugin/semantic_release2/actions/analyze_commits.rb', line 151

def self.is_codepush_friendly(params)
  git_command = 'git rev-list --max-parents=0 HEAD'
  # Begining of the branch is taken for codepush analysis
  hash_lines = Actions.sh("#{git_command} | wc -l", log: params[:debug]).chomp
  hash = params[:start] || Actions.sh(git_command, log: params[:debug]).chomp
  next_major = 0
  next_minor = 0
  next_patch = 0
  last_incompatible_codepush_version = '0.0.0'

  if hash_lines.to_i > 1
    UI.error("#{git_command} resulted to more than 1 hash")
    UI.error('This usualy happens when you pull only part of a git history. Check out how you pull the repo! "git fetch" should be enough.')
    Actions.sh(git_command, log: true).chomp
    return false
  end

  # Get commits log between last version and head
  splitted = get_commits_from_hash(
    hash: hash,
    end: params[:end],
    debug: params[:debug]
  )
  releases = params[:releases]
  codepush_friendly = params[:codepush_friendly]

  splitted.each do |line|
    # conventional commits are in format
    # type: subject (fix: app crash - for example)
    commit = Helper::SemanticReleaseHelper.parse_commit(
      commit_subject: line.split("|")[0],
      commit_body: line.split("|")[1],
      releases: releases,
      codepush_friendly: codepush_friendly
    )

    if commit[:release] == "major" || commit[:is_breaking_change]
      next_major += 1
      next_minor = 0
      next_patch = 0
    elsif commit[:release] == "minor"
      next_minor += 1
      next_patch = 0
    elsif commit[:release] == "patch"
      next_patch += 1
    end

    unless commit[:is_codepush_friendly]
      last_incompatible_codepush_version = "#{next_major}.#{next_minor}.#{next_patch}"
    end
  end

  Actions.lane_context[SharedValues::RELEASE_LAST_INCOMPATIBLE_CODEPUSH_VERSION] = last_incompatible_codepush_version
end

.is_releasable(params) ⇒ Object



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
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/fastlane/plugin/semantic_release2/actions/analyze_commits.rb', line 43

def self.is_releasable(params)
  # Hash of the commit where is the last version
  # If the tag is not found we are taking HEAD as reference
  hash = 'HEAD'
  # Default last version
  version = '0.0.0'

  if params[:start]
    hash = params[:start]
  else
    tag = get_last_tag(
      match: params[:match],
      debug: params[:debug]
    )

    if tag.empty?
      UI.message("First commit of the branch is taken as a begining of next release")
      # If there is no tag found we taking the first commit of current branch
      hash = Actions.sh('git rev-list --max-parents=0 HEAD', log: params[:debug]).chomp
    else
      # Tag's format is v2.3.4-5-g7685948
      # See git describe man page for more info
      tag_name = tag.split('-')[0].strip
      parsed_version = tag_name.match(params[:tag_version_match])

      if parsed_version.nil?
        UI.user_error!("Error while parsing version from tag #{tag_name} by using tag_version_match - #{params[:tag_version_match]}. Please check if the tag contains version as you expect and if you are using single brackets for tag_version_match parameter.")
      end

      version = parsed_version[0]
      # Get a hash of last version tag
      hash = get_last_tag_hash(
        tag_name: tag_name,
        debug: params[:debug]
      )

      UI.message("Found a tag #{tag_name} associated with version #{version}")
    end
  end

  # converts last version string to the int numbers
  next_major = (version.split('.')[0] || 0).to_i
  next_minor = (version.split('.')[1] || 0).to_i
  next_patch = (version.split('.')[2] || 0).to_i

  # Get commits log between last version and head
  splitted = get_commits_from_hash(
    hash: hash,
    end: params[:end],
    debug: params[:debug]
  )

  UI.message("Found #{splitted.length} commits since last release")
  releases = params[:releases]

  splitted.each do |line|
    # conventional commits are in format
    # type: subject (fix: app crash - for example)
    commit = Helper::SemanticReleaseHelper.parse_commit(
      commit_subject: line.split("|")[0],
      commit_body: line.split("|")[1],
      releases: releases
    )

    unless commit[:scope].nil?
      # if this commit has a scope, then we need to inspect to see if that is one of the scopes we're trying to exclude
      scope = commit[:scope]
      scopes_to_ignore = params[:ignore_scopes]
      # if it is, we'll skip this commit when bumping versions
      next if scopes_to_ignore.include?(scope) #=> true
    end

    if commit[:release] == "major" || commit[:is_breaking_change]
      next_major += 1
      next_minor = 0
      next_patch = 0
    elsif commit[:release] == "minor"
      next_minor += 1
      next_patch = 0
    elsif commit[:release] == "patch"
      next_patch += 1
    end

    next_version = "#{next_major}.#{next_minor}.#{next_patch}"
    UI.message("#{next_version}: #{line}")
  end

  next_version = "#{next_major}.#{next_minor}.#{next_patch}"

  is_next_version_releasable = Helper::SemanticReleaseHelper.semver_gt(next_version, version)

  Actions.lane_context[SharedValues::RELEASE_ANALYZED] = true
  Actions.lane_context[SharedValues::RELEASE_IS_NEXT_VERSION_HIGHER] = is_next_version_releasable
  # Last release analysis
  Actions.lane_context[SharedValues::RELEASE_LAST_TAG_HASH] = hash
  Actions.lane_context[SharedValues::RELEASE_LAST_VERSION] = version
  # Next release analysis
  Actions.lane_context[SharedValues::RELEASE_NEXT_MAJOR_VERSION] = next_major
  Actions.lane_context[SharedValues::RELEASE_NEXT_MINOR_VERSION] = next_minor
  Actions.lane_context[SharedValues::RELEASE_NEXT_PATCH_VERSION] = next_patch
  Actions.lane_context[SharedValues::RELEASE_NEXT_VERSION] = next_version

  success_message = "Next version (#{next_version}) is higher than last version (#{version}). This version should be released."
  UI.success(success_message) if is_next_version_releasable

  is_next_version_releasable
end

.is_supported?(platform) ⇒ Boolean

Returns:

  • (Boolean)


310
311
312
313
# File 'lib/fastlane/plugin/semantic_release2/actions/analyze_commits.rb', line 310

def self.is_supported?(platform)
  # you can do things like
  true
end

.outputObject



284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
# File 'lib/fastlane/plugin/semantic_release2/actions/analyze_commits.rb', line 284

def self.output
  # Define the shared values you are going to provide
  # Example
  [
    ['RELEASE_ANALYZED', 'True if commits were analyzed.'],
    ['RELEASE_IS_NEXT_VERSION_HIGHER', 'True if next version is higher then last version'],
    ['RELEASE_LAST_TAG_HASH', 'Hash of commit that is tagged as a last version'],
    ['RELEASE_LAST_VERSION', 'Last version number - parsed from last tag.'],
    ['RELEASE_NEXT_MAJOR_VERSION', 'Major number of the next version'],
    ['RELEASE_NEXT_MINOR_VERSION', 'Minor number of the next version'],
    ['RELEASE_NEXT_PATCH_VERSION', 'Patch number of the next version'],
    ['RELEASE_NEXT_VERSION', 'Next version string in format (major.minor.patch)'],
    ['RELEASE_LAST_INCOMPATIBLE_CODEPUSH_VERSION', 'Last commit without codepush']
  ]
end

.return_valueObject



300
301
302
303
# File 'lib/fastlane/plugin/semantic_release2/actions/analyze_commits.rb', line 300

def self.return_value
  # If your method provides a return value, you can describe here what it does
  "Returns true if the next version is higher then the last version"
end

.run(params) ⇒ Object



206
207
208
209
210
211
# File 'lib/fastlane/plugin/semantic_release2/actions/analyze_commits.rb', line 206

def self.run(params)
  is_next_version_releasable = is_releasable(params)
  is_codepush_friendly(params)

  is_next_version_releasable
end