Class: Fastlane::Actions::AnalyzeCommitsAction
- Inherits:
-
Action
- Object
- Action
- Fastlane::Actions::AnalyzeCommitsAction
- Defined in:
- lib/fastlane/plugin/better_semantic_release/actions/analyze_commits.rb
Documentation collapse
- .authors ⇒ Object
- .available_options ⇒ Object
- .description ⇒ Object
- .details ⇒ Object
- .is_supported?(platform) ⇒ Boolean
- .output ⇒ Object
- .return_value ⇒ Object
Class Method Summary collapse
- .get_beginning_of_next_sprint(params) ⇒ Object
- .get_commits_from_hash(params) ⇒ Object
- .get_last_tag(params) ⇒ Object
- .get_last_tag_hash(params) ⇒ Object
- .is_codepush_friendly(params) ⇒ Object
- .is_releasable(params) ⇒ Object
- .run(params) ⇒ Object
Class Method Details
.authors ⇒ Object
400 401 402 403 |
# File 'lib/fastlane/plugin/better_semantic_release/actions/analyze_commits.rb', line 400 def self. # So no one will ever forget your contribution to fastlane :) You are awesome btw! ["xotahal"] end |
.available_options ⇒ Object
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 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 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 |
# File 'lib/fastlane/plugin/better_semantic_release/actions/analyze_commits.rb', line 288 def self. # 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: :commit_format, description: "The commit format to apply. Presets are 'default' or 'angular', or you can provide your own Regexp. Note: the supplied regex _must_ have 4 capture groups, in order: type, scope, has_exclamation_mark, and subject", default_value: "default", is_string: false, verify_block: proc do |value| case value when String unless Helper::BetterSemanticReleaseHelper.format_patterns.key?(value) UI.user_error!("Invalid format preset: #{value}") end pattern = Helper::BetterSemanticReleaseHelper.format_patterns[value] when Regexp pattern = value else UI.user_error!("Invalid option type: #{value.inspect}") end Actions.lane_context[SharedValues::CONVENTIONAL_CHANGELOG_ACTION_FORMAT_PATTERN] = pattern end ), FastlaneCore::ConfigItem.new( key: :releases, description: "Map types of commit to release (major, minor, patch)", default_value: { build: "patch", chore: "patch", ci: "patch", fix: "patch", feat: "minor", perf: "patch", refactor: "patch", style: "patch" }, type: Hash ), FastlaneCore::ConfigItem.new( key: :codepush_friendly, description: "These types are consider as codepush friendly automatically", default_value: ["build", "chore", "ci", "docs", "fix", "feat", "perf", "refactor", "style", "test"], 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: :version_start, description: "Number to start versioning from, useful if TestFlight has a bad version", default_value: '0.0.0', type: String, optional: true ), FastlaneCore::ConfigItem.new( key: :ignore_scopes, description: "To ignore certain scopes when calculating releases", default_value: [], type: Array, optional: true ), FastlaneCore::ConfigItem.new( key: :show_version_path, description: "True if you want to print out the version calculated for each commit", default_value: true, type: Boolean, 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 ), FastlaneCore::ConfigItem.new( key: :start_hash, description: "Hash to start from when calculating versions", default_value: "HEAD", type: String, optional: true ) ] end |
.description ⇒ Object
280 281 282 |
# File 'lib/fastlane/plugin/better_semantic_release/actions/analyze_commits.rb', line 280 def self.description "Finds a tag of last release and determinates version of next release" end |
.details ⇒ Object
284 285 286 |
# File 'lib/fastlane/plugin/better_semantic_release/actions/analyze_commits.rb', line 284 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_beginning_of_next_sprint(params) ⇒ Object
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 |
# File 'lib/fastlane/plugin/better_semantic_release/actions/analyze_commits.rb', line 52 def self.get_beginning_of_next_sprint(params) tag = get_last_tag(match: params[:match], debug: params[:debug]) # if tag doesn't exist it get's first commit or fallback tag (v*.*.*) if tag.empty? UI.("It couldn't match tag for #{params[:match]}. Check if first commit can be taken as a beginning of next release") # If there is no tag found we taking the first commit of current branch # command to get first commit git_command = 'git rev-list --max-parents=0 HEAD' hash_lines = Actions.sh("#{git_command} | wc -l", log: params[:debug]).chomp if hash_lines.to_i == 1 UI.("First commit of the branch is taken as a begining of next release") return { # here we know this command will return 1 line hash: Actions.sh(git_command, log: params[:debug]).chomp } end # neighter matched tag and first hash could be used - as fallback we try vX.Y.Z UI.("It couldn't match tag for #{params[:match]} and couldn't use first commit. Check if tag vX.Y.Z can be taken as a begining of next release") tag = get_last_tag(match: "v*", debug: params[:debug]) # even fallback tag doesn't work if tag.empty? return false end end # Tag's format is v2.3.4-5-g7685948 # See git describe man page for more info slach_occurrences = tag.count("/") version = tag.split('/')[slach_occurrences] if version.nil? UI.user_error!("Error while parsing version from tag #{tag}") end # Get a hash of last version tag hash = get_last_tag_hash( tag_name: tag, debug: params[:debug] ) UI.("Found a tag #{tag} associated with version #{version}") return { hash: hash, version: version } end |
.get_commits_from_hash(params) ⇒ Object
43 44 45 46 47 48 49 50 |
# File 'lib/fastlane/plugin/better_semantic_release/actions/analyze_commits.rb', line 43 def self.get_commits_from_hash(params) commits = Helper::BetterSemanticReleaseHelper.git_log( pretty: '%s|%b|>', start: params[:hash], debug: params[:debug] ) commits.split("|>") end |
.get_last_tag(params) ⇒ Object
21 22 23 24 25 26 27 28 |
# File 'lib/fastlane/plugin/better_semantic_release/actions/analyze_commits.rb', line 21 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.("Tag was not found for match pattern - #{params[:match]}") '' end |
.get_last_tag_hash(params) ⇒ Object
30 31 32 33 34 35 36 37 38 39 40 41 |
# File 'lib/fastlane/plugin/better_semantic_release/actions/analyze_commits.rb', line 30 def self.get_last_tag_hash(params) # Convert ios/production/1.15.3-2-ga8b1c030 to ios/production/1.15.3 so we can get hash of it UI. "get_last_tag_hash #{params[:tag_name]}" dashIndex = params[:tag_name].index('-') if dashIndex formatted_tag = params[:tag_name].slice(0..(params[:tag_name].index('-') - 1)) elsif formatted_tag = params[:tag_name] end command = "git rev-list -n 1 refs/tags/#{formatted_tag}" Actions.sh(command, log: params[:debug]).chomp end |
.is_codepush_friendly(params) ⇒ Object
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 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 |
# File 'lib/fastlane/plugin/better_semantic_release/actions/analyze_commits.rb', line 207 def self.is_codepush_friendly(params) git_command = "git rev-list --max-parents=0 #{params[:start_hash]} HEAD" UI.("git rev-list --max-parents=0 #{params[:start_hash]} HEAD | wc -l") # Begining of the branch is taken for codepush analysis hash_lines = Actions.sh("#{git_command} | wc -l", log: params[:debug]).chomp hash = Actions.sh(git_command, log: params[:debug]).chomp next_major = 0 next_minor = 0 next_patch = 0 last_incompatible_codepush_version = params[:version_start] 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, debug: params[:debug] ) releases = params[:releases] codepush_friendly = params[:codepush_friendly] format_pattern = lane_context[SharedValues::CONVENTIONAL_CHANGELOG_ACTION_FORMAT_PATTERN] splitted.each do |line| parts = line.split(':') if parts.length > 1 # conventional commits are in format # type: subject (fix: app crash - for example) commit = Helper::BetterSemanticReleaseHelper.parse_commit( commit_subject: line, commit_body: parts[1].strip, releases: releases, pattern: format_pattern, codepush_friendly: codepush_friendly ) #UI.message commit 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 end Actions.lane_context[SharedValues::RELEASE_LAST_INCOMPATIBLE_CODEPUSH_VERSION] = last_incompatible_codepush_version end |
.is_releasable(params) ⇒ Object
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 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 198 199 200 201 202 203 204 205 |
# File 'lib/fastlane/plugin/better_semantic_release/actions/analyze_commits.rb', line 104 def self.is_releasable(params) # Hash of the commit where is the last version beginning = get_beginning_of_next_sprint(params) unless beginning UI.error('It could not find a begining of this sprint. How to fix this:') UI.error('-- ensure there is only one commit with --max-parents=0 (this command should return one line: "git rev-list --max-parents=0 HEAD")') UI.error('-- tell us explicitely where the release starts by adding tag like this: vX.Y.Z (where X.Y.Z is version from which it starts computing next version number)') return false end # Default last version version = params[:version_start] || beginning[:version] # If the tag is not found we are taking HEAD as reference hash = beginning[:hash] || 'HEAD' # 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 is_next_version_compatible_with_codepush = true # Get commits log between last version and head splitted = get_commits_from_hash( hash: hash, debug: params[:debug] ) UI. hash UI.("Found #{splitted.length} commits since last release") releases = params[:releases] format_pattern = lane_context[SharedValues::CONVENTIONAL_CHANGELOG_ACTION_FORMAT_PATTERN] splitted.each do |squashed_commit| commits = squashed_commit.split("*") # This works for Bitbucket #commits.drop(1).each do |line| # This works for Gitlab commits.each do |line| parts = line.split(":") if parts.length > 1 # conventional commits are in format # type: subject (fix: app crash - for example) commit = Helper::BetterSemanticReleaseHelper.parse_commit( commit_subject: line.strip, commit_body: parts[1].strip, releases: releases, pattern: format_pattern ) 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 unless commit[:is_codepush_friendly] is_next_version_compatible_with_codepush = false end next_version = "#{next_major}.#{next_minor}.#{next_patch}" UI.("#{next_version}") if params[:show_version_path] end end end next_version = "#{next_major}.#{next_minor}.#{next_patch}" is_next_version_releasable = Helper::BetterSemanticReleaseHelper.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 Actions.lane_context[SharedValues::RELEASE_IS_NEXT_VERSION_COMPATIBLE_WITH_CODEPUSH] = is_next_version_compatible_with_codepush # 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 = "Next version (#{next_version}) is higher than last version (#{version}). This version should be released." UI.success() if is_next_version_releasable is_next_version_releasable end |
.is_supported?(platform) ⇒ Boolean
405 406 407 408 |
# File 'lib/fastlane/plugin/better_semantic_release/actions/analyze_commits.rb', line 405 def self.is_supported?(platform) # you can do things like true end |
.output ⇒ Object
377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 |
# File 'lib/fastlane/plugin/better_semantic_release/actions/analyze_commits.rb', line 377 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_IS_NEXT_VERSION_COMPATIBLE_WITH_CODEPUSH', 'True if next version is compatible with codepush'], ['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'], ['CONVENTIONAL_CHANGELOG_ACTION_FORMAT_PATTERN', 'The format pattern Regexp used to match commits (mainly for internal use)'] ] end |
.return_value ⇒ Object
395 396 397 398 |
# File 'lib/fastlane/plugin/better_semantic_release/actions/analyze_commits.rb', line 395 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
269 270 271 272 273 274 |
# File 'lib/fastlane/plugin/better_semantic_release/actions/analyze_commits.rb', line 269 def self.run(params) is_next_version_releasable = is_releasable(params) is_codepush_friendly(params) is_next_version_releasable end |