fastlane-plugin-semantic_release2 1.10.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 1fbcc1c8051d9c7c07cc412e30eda2a008813e1851bf4f84af44382c2cbda688
4
+ data.tar.gz: 3a91be3ff0cce974a7a8ac32f3c98bb13d47af69eead0bd19675ef0c9b51af22
5
+ SHA512:
6
+ metadata.gz: 4c0f9b7286779c8617ca4c3525e4f3b151116b54f66c6eb662bb5a63f7c11d74a9d39882a503029aedebb02e6714d2d616f664e9a39121c026769f4f78a3f0bb
7
+ data.tar.gz: 2cc2c2d3e2ec45343a3621bae10939fbc5a4724fa7e5cd28d68c8e3706e6afd6da98da3eb010f3f1a184cc329f79923400063d9c03d3fea8aada3d83e460d2df
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 Jiří Otáhal <xotahal@gmail.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,92 @@
1
+ # semantic_release plugin for `fastlane`
2
+
3
+ [![CircleCI](https://circleci.com/gh/xotahal/fastlane-plugin-semantic-fork_release.svg?style=svg)](https://circleci.com/gh/xotahal/fastlane-plugin-semantic-fork_release) [![License](https://img.shields.io/github/license/SiarheiFedartsou/fastlane-plugin-versioning.svg)](https://github.com/SiarheiFedartsou/fastlane-plugin-versioning/blob/master/LICENSE) [![Gem Version](https://badge.fury.io/rb/fastlane-plugin-semantic-fork_release.svg)](https://badge.fury.io/rb/fastlane-plugin-semantic-fork_release) [![fastlane Plugin Badge](https://rawcdn.githack.com/fastlane/fastlane/master/fastlane/assets/plugin-badge.svg)](https://rubygems.org/gems/fastlane-plugin-versioning)
4
+
5
+ ## Getting Started
6
+
7
+ ```
8
+ fastlane add_plugin semantic_release
9
+ ```
10
+
11
+ ## About
12
+
13
+ Automated version managment and generator of release notes. Inspired by [semantic-release](https://github.com/semantic-release/semantic-release) for npm packages. Based on [conventional commits](https://www.conventionalcommits.org/).
14
+
15
+ ### Articles
16
+
17
+ [Semantic Release for Fastlane](https://medium.com/@xotahal/semantic-release-for-fastlane-781df4cf5888?source=friends_link&sk=5c02e32daca7a68539e27e0e1bac1092) @ Medium - By Jiri Otahal
18
+
19
+ ## Available Actions
20
+
21
+ ### `conventional_changelog`
22
+
23
+ - parses all commits since last version
24
+ - groups those commits by their type (fix, feat, docs, refactor, chore, etc)
25
+ - and creates formated release notes either in markdown or in slack format
26
+
27
+ Available parameters:
28
+
29
+ - `format: 'slack|markdown|plain'` (defaults to `markdown`). This formats the changelog for the destination you need. If you're using this for TestFlight changelogs, we suggest using the `plain` option
30
+ - `title: 'My Title'` - is appended to the release notes title, "1.1.8 My Title (YYYY-MM-DD)"
31
+ - `display_title: true|false` (defaults to true) - allows you to hide the entire first line of the changelog
32
+ - `display_links: true|false` (defaults to true) - allows you to hide links to commits from your changelog
33
+ - `commit_url: 'https://github.com/username/repository/commit'` - prepended to the commit ID to build usable links
34
+ - View other options by searching for `available_options` in `conventional_changelog.rb`
35
+
36
+ Example:
37
+
38
+ ```
39
+ notes = conventional_changelog(format: 'slack', title: 'Android Alpha')
40
+ ```
41
+
42
+ <img src="https://raw.githubusercontent.com/xotahal/fastlane-plugin-semantic-fork_release/master/docs/Changelog.png" />
43
+
44
+ ### `analyze_commits`
45
+
46
+ - analyzes your git history
47
+ - finds last tag on current branch (for example ios/beta/1.3.2)
48
+ - parses the last version from tag (1.3.2)
49
+ - gets all commits since this tag
50
+ - analyzes subject of every single commit and increases version number if there is a need (check conventional commit rules)
51
+ - if next version number is higher then last version number it will recommend you to release this version
52
+
53
+ Options:
54
+
55
+ - `ignore_scopes: ['android','windows']`: allows you to ignore any commits which include a given scope, like this one: `feat(android): add functionality not relevant to the release we are producing`
56
+
57
+ Example usage:
58
+
59
+ ```
60
+ isReleasable = analyze_commits(match: 'ios/beta*')
61
+ ```
62
+
63
+ It provides these variables in `lane_context`.
64
+
65
+ ```
66
+ ['RELEASE_ANALYZED', 'True if commits were analyzed.'],
67
+ ['RELEASE_IS_NEXT_VERSION_HIGHER', 'True if next version is higher then last version'],
68
+ ['RELEASE_LAST_TAG_HASH', 'Hash of commit that is tagged as a last version'],
69
+ ['RELEASE_LAST_VERSION', 'Last version number - parsed from last tag.'],
70
+ ['RELEASE_NEXT_MAJOR_VERSION', 'Major number of the next version'],
71
+ ['RELEASE_NEXT_MINOR_VERSION', 'Minor number of the next version'],
72
+ ['RELEASE_NEXT_PATCH_VERSION', 'Patch number of the next version'],
73
+ ['RELEASE_NEXT_VERSION', 'Next version string in format (major.minor.patch)'],
74
+ ```
75
+
76
+ And you can access these like this:
77
+
78
+ `next_version = lane_context[SharedValues::RELEASE_NEXT_VERSION]`
79
+
80
+ <img src="https://raw.githubusercontent.com/xotahal/fastlane-plugin-semantic-fork_release/master/docs/Analyze.png" />
81
+
82
+ ## Tests
83
+
84
+ To run the test suite (contained in `./spec`), call `bundle exec rake`
85
+
86
+ ## Questions
87
+
88
+ If you need anything ping us on [twitter](http://bit.ly/t-xotahal).
89
+
90
+ | Jiri Otahal |
91
+ | -------------------------------------------------------------------------------------------------------------------------------------- |
92
+ | [<img src="https://avatars3.githubusercontent.com/u/3531955?v=4" width="100px;" style="border-radius:50px"/>](http://bit.ly/t-xotahal) |
@@ -0,0 +1,16 @@
1
+ require 'fastlane/plugin/semantic_release2/version'
2
+
3
+ module Fastlane
4
+ module SemanticRelease
5
+ # Return all .rb files inside the "actions" and "helper" directory
6
+ def self.all_classes
7
+ Dir[File.expand_path('**/{actions,helper}/*.rb', File.dirname(__FILE__))]
8
+ end
9
+ end
10
+ end
11
+
12
+ # By default we want to import all available actions and helpers
13
+ # A plugin can contain any number of actions and plugins
14
+ Fastlane::SemanticRelease.all_classes.each do |current|
15
+ require current
16
+ end
@@ -0,0 +1,306 @@
1
+ require 'fastlane/action'
2
+ require_relative '../helper/semantic_release_helper'
3
+
4
+ module Fastlane
5
+ module Actions
6
+ module SharedValues
7
+ RELEASE_ANALYZED = :RELEASE_ANALYZED
8
+ RELEASE_IS_NEXT_VERSION_HIGHER = :RELEASE_IS_NEXT_VERSION_HIGHER
9
+ RELEASE_LAST_TAG_HASH = :RELEASE_LAST_TAG_HASH
10
+ RELEASE_LAST_VERSION = :RELEASE_LAST_VERSION
11
+ RELEASE_NEXT_MAJOR_VERSION = :RELEASE_NEXT_MAJOR_VERSION
12
+ RELEASE_NEXT_MINOR_VERSION = :RELEASE_NEXT_MINOR_VERSION
13
+ RELEASE_NEXT_PATCH_VERSION = :RELEASE_NEXT_PATCH_VERSION
14
+ RELEASE_NEXT_VERSION = :RELEASE_NEXT_VERSION
15
+ RELEASE_LAST_INCOMPATIBLE_CODEPUSH_VERSION = :RELEASE_LAST_INCOMPATIBLE_CODEPUSH_VERSION
16
+ end
17
+
18
+ class AnalyzeCommitsAction < Action
19
+ def self.get_last_tag(params)
20
+ # Try to find the tag
21
+ command = "git describe --tags --match=#{params[:match]}"
22
+ Actions.sh(command, log: params[:debug])
23
+ rescue
24
+ UI.message("Tag was not found for match pattern - #{params[:match]}")
25
+ ''
26
+ end
27
+
28
+ def self.get_last_tag_hash(params)
29
+ command = "git rev-list -n 1 refs/tags/#{params[:tag_name]}"
30
+ Actions.sh(command, log: params[:debug]).chomp
31
+ end
32
+
33
+ def self.get_commits_from_hash(params)
34
+ commits = Helper::SemanticReleaseHelper.git_log(
35
+ pretty: '%s|%b|>',
36
+ start: params[:hash],
37
+ end: params[:end],
38
+ debug: params[:debug]
39
+ )
40
+ commits.split("|>")
41
+ end
42
+
43
+ def self.is_releasable(params)
44
+ # Hash of the commit where is the last version
45
+ # If the tag is not found we are taking HEAD as reference
46
+ hash = 'HEAD'
47
+ # Default last version
48
+ version = '0.0.0'
49
+
50
+ tag = get_last_tag(
51
+ match: params[:match],
52
+ debug: params[:debug]
53
+ )
54
+
55
+ if tag.empty?
56
+ UI.message("First commit of the branch is taken as a begining of next release")
57
+ # If there is no tag found we taking the first commit of current branch
58
+ hash = Actions.sh('git rev-list --max-parents=0 HEAD', log: params[:debug]).chomp
59
+ else
60
+ # Tag's format is v2.3.4-5-g7685948
61
+ # See git describe man page for more info
62
+ tag_name = tag.split('-')[0].strip
63
+ parsed_version = tag_name.match(params[:tag_version_match])
64
+
65
+ if parsed_version.nil?
66
+ 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.")
67
+ end
68
+
69
+ version = parsed_version[0]
70
+ # Get a hash of last version tag
71
+ hash = get_last_tag_hash(
72
+ tag_name: tag_name,
73
+ debug: params[:debug]
74
+ )
75
+
76
+ UI.message("Found a tag #{tag_name} associated with version #{version}")
77
+ end
78
+
79
+ # converts last version string to the int numbers
80
+ next_major = (version.split('.')[0] || 0).to_i
81
+ next_minor = (version.split('.')[1] || 0).to_i
82
+ next_patch = (version.split('.')[2] || 0).to_i
83
+
84
+ # Get commits log between last version and head
85
+ splitted = get_commits_from_hash(
86
+ hash: hash,
87
+ end: params[:end],
88
+ debug: params[:debug]
89
+ )
90
+
91
+ UI.message("Found #{splitted.length} commits since last release")
92
+ releases = params[:releases]
93
+
94
+ splitted.each do |line|
95
+ # conventional commits are in format
96
+ # type: subject (fix: app crash - for example)
97
+ commit = Helper::SemanticReleaseHelper.parse_commit(
98
+ commit_subject: line.split("|")[0],
99
+ commit_body: line.split("|")[1],
100
+ releases: releases
101
+ )
102
+
103
+ unless commit[:scope].nil?
104
+ # 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
105
+ scope = commit[:scope]
106
+ scopes_to_ignore = params[:ignore_scopes]
107
+ # if it is, we'll skip this commit when bumping versions
108
+ next if scopes_to_ignore.include?(scope) #=> true
109
+ end
110
+
111
+ if commit[:release] == "major" || commit[:is_breaking_change]
112
+ next_major += 1
113
+ next_minor = 0
114
+ next_patch = 0
115
+ elsif commit[:release] == "minor"
116
+ next_minor += 1
117
+ next_patch = 0
118
+ elsif commit[:release] == "patch"
119
+ next_patch += 1
120
+ end
121
+
122
+ next_version = "#{next_major}.#{next_minor}.#{next_patch}"
123
+ UI.message("#{next_version}: #{line}")
124
+ end
125
+
126
+ next_version = "#{next_major}.#{next_minor}.#{next_patch}"
127
+
128
+ is_next_version_releasable = Helper::SemanticReleaseHelper.semver_gt(next_version, version)
129
+
130
+ Actions.lane_context[SharedValues::RELEASE_ANALYZED] = true
131
+ Actions.lane_context[SharedValues::RELEASE_IS_NEXT_VERSION_HIGHER] = is_next_version_releasable
132
+ # Last release analysis
133
+ Actions.lane_context[SharedValues::RELEASE_LAST_TAG_HASH] = hash
134
+ Actions.lane_context[SharedValues::RELEASE_LAST_VERSION] = version
135
+ # Next release analysis
136
+ Actions.lane_context[SharedValues::RELEASE_NEXT_MAJOR_VERSION] = next_major
137
+ Actions.lane_context[SharedValues::RELEASE_NEXT_MINOR_VERSION] = next_minor
138
+ Actions.lane_context[SharedValues::RELEASE_NEXT_PATCH_VERSION] = next_patch
139
+ Actions.lane_context[SharedValues::RELEASE_NEXT_VERSION] = next_version
140
+
141
+ success_message = "Next version (#{next_version}) is higher than last version (#{version}). This version should be released."
142
+ UI.success(success_message) if is_next_version_releasable
143
+
144
+ is_next_version_releasable
145
+ end
146
+
147
+ def self.is_codepush_friendly(params)
148
+ git_command = 'git rev-list --max-parents=0 HEAD'
149
+ # Begining of the branch is taken for codepush analysis
150
+ hash_lines = Actions.sh("#{git_command} | wc -l", log: params[:debug]).chomp
151
+ hash = Actions.sh(git_command, log: params[:debug]).chomp
152
+ next_major = 0
153
+ next_minor = 0
154
+ next_patch = 0
155
+ last_incompatible_codepush_version = '0.0.0'
156
+
157
+ if hash_lines.to_i > 1
158
+ UI.error("#{git_command} resulted to more than 1 hash")
159
+ 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.')
160
+ Actions.sh(git_command, log: true).chomp
161
+ return false
162
+ end
163
+
164
+ # Get commits log between last version and head
165
+ splitted = get_commits_from_hash(
166
+ hash: hash,
167
+ end: params[:end],
168
+ debug: params[:debug]
169
+ )
170
+ releases = params[:releases]
171
+ codepush_friendly = params[:codepush_friendly]
172
+
173
+ splitted.each do |line|
174
+ # conventional commits are in format
175
+ # type: subject (fix: app crash - for example)
176
+ commit = Helper::SemanticReleaseHelper.parse_commit(
177
+ commit_subject: line.split("|")[0],
178
+ commit_body: line.split("|")[1],
179
+ releases: releases,
180
+ codepush_friendly: codepush_friendly
181
+ )
182
+
183
+ if commit[:release] == "major" || commit[:is_breaking_change]
184
+ next_major += 1
185
+ next_minor = 0
186
+ next_patch = 0
187
+ elsif commit[:release] == "minor"
188
+ next_minor += 1
189
+ next_patch = 0
190
+ elsif commit[:release] == "patch"
191
+ next_patch += 1
192
+ end
193
+
194
+ unless commit[:is_codepush_friendly]
195
+ last_incompatible_codepush_version = "#{next_major}.#{next_minor}.#{next_patch}"
196
+ end
197
+ end
198
+
199
+ Actions.lane_context[SharedValues::RELEASE_LAST_INCOMPATIBLE_CODEPUSH_VERSION] = last_incompatible_codepush_version
200
+ end
201
+
202
+ def self.run(params)
203
+ is_next_version_releasable = is_releasable(params)
204
+ is_codepush_friendly(params)
205
+
206
+ is_next_version_releasable
207
+ end
208
+
209
+ #####################################################
210
+ # @!group Documentation
211
+ #####################################################
212
+
213
+ def self.description
214
+ "Finds a tag of last release and determinates version of next release"
215
+ end
216
+
217
+ def self.details
218
+ "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."
219
+ end
220
+
221
+ def self.available_options
222
+ # Define all options your action supports.
223
+
224
+ # Below a few examples
225
+ [
226
+ FastlaneCore::ConfigItem.new(
227
+ key: :match,
228
+ description: "Match parameter of git describe. See man page of git describe for more info",
229
+ verify_block: proc do |value|
230
+ UI.user_error!("No match for analyze_commits action given, pass using `match: 'expr'`") unless value && !value.empty?
231
+ end
232
+ ),
233
+ FastlaneCore::ConfigItem.new(
234
+ key: :end,
235
+ description: "Use a commit hash instead of HEAD",
236
+ type: String,
237
+ optional: true
238
+ ),
239
+ FastlaneCore::ConfigItem.new(
240
+ key: :releases,
241
+ description: "Map types of commit to release (major, minor, patch)",
242
+ default_value: { fix: "patch", feat: "minor" },
243
+ type: Hash
244
+ ),
245
+ FastlaneCore::ConfigItem.new(
246
+ key: :codepush_friendly,
247
+ description: "These types are consider as codepush friendly automatically",
248
+ default_value: ["chore", "test", "docs"],
249
+ type: Array,
250
+ optional: true
251
+ ),
252
+ FastlaneCore::ConfigItem.new(
253
+ key: :tag_version_match,
254
+ description: "To parse version number from tag name",
255
+ default_value: '\d+\.\d+\.\d+'
256
+ ),
257
+ FastlaneCore::ConfigItem.new(
258
+ key: :ignore_scopes,
259
+ description: "To ignore certain scopes when calculating releases",
260
+ default_value: [],
261
+ type: Array,
262
+ optional: true
263
+ ),
264
+ FastlaneCore::ConfigItem.new(
265
+ key: :debug,
266
+ description: "True if you want to log out a debug info",
267
+ default_value: false,
268
+ type: Boolean,
269
+ optional: true
270
+ )
271
+ ]
272
+ end
273
+
274
+ def self.output
275
+ # Define the shared values you are going to provide
276
+ # Example
277
+ [
278
+ ['RELEASE_ANALYZED', 'True if commits were analyzed.'],
279
+ ['RELEASE_IS_NEXT_VERSION_HIGHER', 'True if next version is higher then last version'],
280
+ ['RELEASE_LAST_TAG_HASH', 'Hash of commit that is tagged as a last version'],
281
+ ['RELEASE_LAST_VERSION', 'Last version number - parsed from last tag.'],
282
+ ['RELEASE_NEXT_MAJOR_VERSION', 'Major number of the next version'],
283
+ ['RELEASE_NEXT_MINOR_VERSION', 'Minor number of the next version'],
284
+ ['RELEASE_NEXT_PATCH_VERSION', 'Patch number of the next version'],
285
+ ['RELEASE_NEXT_VERSION', 'Next version string in format (major.minor.patch)'],
286
+ ['RELEASE_LAST_INCOMPATIBLE_CODEPUSH_VERSION', 'Last commit without codepush']
287
+ ]
288
+ end
289
+
290
+ def self.return_value
291
+ # If your method provides a return value, you can describe here what it does
292
+ "Returns true if the next version is higher then the last version"
293
+ end
294
+
295
+ def self.authors
296
+ # So no one will ever forget your contribution to fastlane :) You are awesome btw!
297
+ ["xotahal"]
298
+ end
299
+
300
+ def self.is_supported?(platform)
301
+ # you can do things like
302
+ true
303
+ end
304
+ end
305
+ end
306
+ end
@@ -0,0 +1,314 @@
1
+ require 'fastlane/action'
2
+ require_relative '../helper/semantic_release_helper'
3
+
4
+ module Fastlane
5
+ module Actions
6
+ module SharedValues
7
+ end
8
+
9
+ class ConventionalChangelogAction < Action
10
+ def self.get_commits_from_hash(params)
11
+ commits = Helper::SemanticReleaseHelper.git_log(
12
+ pretty: '%s|%b|%H|%h|%an|%at|>',
13
+ start: params[:hash],
14
+ end: params[:end],
15
+ debug: params[:debug]
16
+ )
17
+ commits.split("|>")
18
+ end
19
+
20
+ def self.run(params)
21
+ # Get next version number from shared values
22
+ analyzed = lane_context[SharedValues::RELEASE_ANALYZED]
23
+
24
+ # If analyze commits action was not run there will be no version in shared
25
+ # values. We need to run the action to get next version number
26
+ unless analyzed
27
+ UI.user_error!("Release hasn't been analyzed yet. Run analyze_commits action first please.")
28
+ # version = other_action.analyze_commits(match: params[:match])
29
+ end
30
+
31
+ last_tag_hash = lane_context[SharedValues::RELEASE_LAST_TAG_HASH]
32
+ version = lane_context[SharedValues::RELEASE_NEXT_VERSION]
33
+
34
+ # Get commits log between last version and head
35
+ commits = get_commits_from_hash(
36
+ hash: last_tag_hash,
37
+ end: params[:end],
38
+ debug: params[:debug]
39
+ )
40
+ parsed = parse_commits(commits)
41
+
42
+ commit_url = params[:commit_url]
43
+ format = params[:format]
44
+
45
+ result = note_builder(format, parsed, version, commit_url, params)
46
+
47
+ result
48
+ end
49
+
50
+ def self.note_builder(format, commits, version, commit_url, params)
51
+ sections = params[:sections]
52
+
53
+ result = ""
54
+
55
+ # Begining of release notes
56
+ if params[:display_title] == true
57
+ title = version
58
+ title += " #{params[:title]}" if params[:title]
59
+ title += " (#{Date.today})"
60
+
61
+ result = style_text(title, format, "title").to_s
62
+ result += "\n\n"
63
+ end
64
+
65
+ params[:order].each do |type|
66
+ # write section only if there is at least one commit
67
+ next if commits.none? { |commit| commit[:type] == type }
68
+
69
+ result += style_text(sections[type.to_sym], format, "heading").to_s
70
+ result += "\n"
71
+
72
+ commits.each do |commit|
73
+ next if commit[:type] != type || commit[:is_merge]
74
+
75
+ result += "-"
76
+
77
+ unless commit[:scope].nil?
78
+ formatted_text = style_text("#{commit[:scope]}:", format, "bold").to_s
79
+ result += " #{formatted_text}"
80
+ end
81
+
82
+ result += " #{commit[:subject]}"
83
+
84
+ if params[:display_links] == true
85
+ styled_link = build_commit_link(commit, commit_url, format).to_s
86
+ result += " (#{styled_link})"
87
+ end
88
+
89
+ if params[:display_author]
90
+ result += " - #{commit[:author_name]}"
91
+ end
92
+
93
+ result += "\n"
94
+ end
95
+ result += "\n"
96
+ end
97
+
98
+ if commits.any? { |commit| commit[:is_breaking_change] == true }
99
+ result += style_text("BREAKING CHANGES", format, "heading").to_s
100
+ result += "\n"
101
+
102
+ commits.each do |commit|
103
+ next unless commit[:is_breaking_change]
104
+ result += "- #{commit[:breaking_change]}" # This is the only unique part of this loop
105
+
106
+ if params[:display_links] == true
107
+ styled_link = build_commit_link(commit, commit_url, format).to_s
108
+ result += " (#{styled_link})"
109
+ end
110
+
111
+ if params[:display_author]
112
+ result += " - #{commit[:author_name]}"
113
+ end
114
+
115
+ result += "\n"
116
+ end
117
+
118
+ result += "\n"
119
+ end
120
+
121
+ # Trim any trailing newlines
122
+ result = result.rstrip!
123
+
124
+ result
125
+ end
126
+
127
+ def self.style_text(text, format, style)
128
+ # formats the text according to the style we're looking to use
129
+
130
+ # Skips all styling
131
+ case style
132
+ when "title"
133
+ if format == "markdown"
134
+ "# #{text}"
135
+ elsif format == "slack"
136
+ "*#{text}*"
137
+ else
138
+ text
139
+ end
140
+ when "heading"
141
+ if format == "markdown"
142
+ "### #{text}"
143
+ elsif format == "slack"
144
+ "*#{text}*"
145
+ else
146
+ "#{text}:"
147
+ end
148
+ when "bold"
149
+ if format == "markdown"
150
+ "**#{text}**"
151
+ elsif format == "slack"
152
+ "*#{text}*"
153
+ else
154
+ text
155
+ end
156
+ else
157
+ text # catchall, shouldn't be needed
158
+ end
159
+ end
160
+
161
+ def self.build_commit_link(commit, commit_url, format)
162
+ # formats the link according to the output format we need
163
+ short_hash = commit[:short_hash]
164
+ hash = commit[:hash]
165
+ url = "#{commit_url}/#{hash}"
166
+
167
+ case format
168
+ when "slack"
169
+ "<#{url}|#{short_hash}>"
170
+ when "markdown"
171
+ "[#{short_hash}](#{url})"
172
+ else
173
+ url
174
+ end
175
+ end
176
+
177
+ def self.parse_commits(commits)
178
+ parsed = []
179
+ # %s|%b|%H|%h|%an|%at
180
+ commits.each do |line|
181
+ splitted = line.split("|")
182
+
183
+ commit = Helper::SemanticReleaseHelper.parse_commit(
184
+ commit_subject: splitted[0],
185
+ commit_body: splitted[1]
186
+ )
187
+
188
+ commit[:hash] = splitted[2]
189
+ commit[:short_hash] = splitted[3]
190
+ commit[:author_name] = splitted[4]
191
+ commit[:commit_date] = splitted[5]
192
+
193
+ parsed.push(commit)
194
+ end
195
+
196
+ parsed
197
+ end
198
+
199
+ #####################################################
200
+ # @!group Documentation
201
+ #####################################################
202
+
203
+ def self.description
204
+ "Get commits since last version and generates release notes"
205
+ end
206
+
207
+ def self.details
208
+ "Uses conventional commits. It groups commits by their types and generates release notes in markdown or slack format."
209
+ end
210
+
211
+ def self.available_options
212
+ # Define all options your action supports.
213
+
214
+ # Below a few examples
215
+ [
216
+ FastlaneCore::ConfigItem.new(
217
+ key: :format,
218
+ description: "You can use either markdown, slack or plain",
219
+ default_value: "markdown",
220
+ optional: true
221
+ ),
222
+ FastlaneCore::ConfigItem.new(
223
+ key: :title,
224
+ description: "Title for release notes",
225
+ optional: true
226
+ ),
227
+ FastlaneCore::ConfigItem.new(
228
+ key: :commit_url,
229
+ description: "Uses as a link to the commit",
230
+ optional: true
231
+ ),
232
+ FastlaneCore::ConfigItem.new(
233
+ key: :order,
234
+ description: "You can change the order of groups in release notes",
235
+ default_value: ["feat", "fix", "refactor", "perf", "chore", "test", "docs", "no_type"],
236
+ type: Array,
237
+ optional: true
238
+ ),
239
+ FastlaneCore::ConfigItem.new(
240
+ key: :end,
241
+ description: "Use a commit hash instead of HEAD",
242
+ type: String,
243
+ optional: true
244
+ ),
245
+ FastlaneCore::ConfigItem.new(
246
+ key: :sections,
247
+ description: "Map type to section title",
248
+ default_value: {
249
+ feat: "Features",
250
+ fix: "Bug fixes",
251
+ refactor: "Code refactoring",
252
+ perf: "Performance improvements",
253
+ chore: "Building system",
254
+ test: "Testing",
255
+ docs: "Documentation",
256
+ no_type: "Other work"
257
+ },
258
+ type: Hash,
259
+ optional: true
260
+ ),
261
+ FastlaneCore::ConfigItem.new(
262
+ key: :display_author,
263
+ description: "Whether you want to show the author of the commit",
264
+ default_value: false,
265
+ type: Boolean,
266
+ optional: true
267
+ ),
268
+ FastlaneCore::ConfigItem.new(
269
+ key: :display_title,
270
+ description: "Whether you want to hide the title/header with the version details at the top of the changelog",
271
+ default_value: true,
272
+ type: Boolean,
273
+ optional: true
274
+ ),
275
+ FastlaneCore::ConfigItem.new(
276
+ key: :display_links,
277
+ description: "Whether you want to display the links to commit IDs",
278
+ default_value: true,
279
+ type: Boolean,
280
+ optional: true
281
+ ),
282
+ FastlaneCore::ConfigItem.new(
283
+ key: :debug,
284
+ description: "True if you want to log out a debug info",
285
+ default_value: false,
286
+ type: Boolean,
287
+ optional: true
288
+ )
289
+ ]
290
+ end
291
+
292
+ def self.output
293
+ # Define the shared values you are going to provide
294
+ # Example
295
+ []
296
+ end
297
+
298
+ def self.return_value
299
+ # If your method provides a return value, you can describe here what it does
300
+ "Returns generated release notes as a string"
301
+ end
302
+
303
+ def self.authors
304
+ # So no one will ever forget your contribution to fastlane :) You are awesome btw!
305
+ ["xotahal"]
306
+ end
307
+
308
+ def self.is_supported?(platform)
309
+ # you can do things like
310
+ true
311
+ end
312
+ end
313
+ end
314
+ end
@@ -0,0 +1,102 @@
1
+ require 'fastlane_core/ui/ui'
2
+
3
+ module Fastlane
4
+ UI = FastlaneCore::UI unless Fastlane.const_defined?("UI")
5
+
6
+ module Helper
7
+ class SemanticReleaseHelper
8
+ # class methods that you define here become available in your action
9
+ # as `Helper::SemanticReleaseHelper.your_method`
10
+ #
11
+ def self.git_log(params)
12
+ if params[:end]
13
+ command = "git log --pretty='#{params[:pretty]}' --reverse #{params[:start]}..#{params[:end]}"
14
+ Actions.sh(command, log: params[:debug]).chomp
15
+ else
16
+ command = "git log --pretty='#{params[:pretty]}' --reverse #{params[:start]}..HEAD"
17
+ Actions.sh(command, log: params[:debug]).chomp
18
+ end
19
+ end
20
+
21
+ def self.parse_commit(params)
22
+ commit_subject = params[:commit_subject].strip
23
+ commit_body = params[:commit_body]
24
+ releases = params[:releases]
25
+ codepush_friendly = params[:codepush_friendly]
26
+ pattern = /^(docs|fix|feat|chore|style|refactor|perf|test)(\((.*)\))?(!?)\: (.*)/
27
+ breaking_change_pattern = /BREAKING CHANGES?: (.*)/
28
+ codepush_pattern = /codepush?: (.*)/
29
+
30
+ matched = commit_subject.match(pattern)
31
+ result = {
32
+ is_valid: false,
33
+ subject: commit_subject,
34
+ is_merge: !(commit_subject =~ /^Merge/).nil?,
35
+ type: 'no_type'
36
+ }
37
+
38
+ unless matched.nil?
39
+ type = matched[1]
40
+ scope = matched[3]
41
+
42
+ result[:is_valid] = true
43
+ result[:type] = type
44
+ result[:scope] = scope
45
+ result[:has_exclamation_mark] = matched[4] == '!'
46
+ result[:subject] = matched[5]
47
+
48
+ unless releases.nil?
49
+ result[:release] = releases[type.to_sym]
50
+ end
51
+ unless codepush_friendly.nil?
52
+ result[:is_codepush_friendly] = codepush_friendly.include?(type)
53
+ end
54
+
55
+ unless commit_body.nil?
56
+ breaking_change_matched = commit_body.match(breaking_change_pattern)
57
+ codepush_matched = commit_body.match(codepush_pattern)
58
+
59
+ unless breaking_change_matched.nil?
60
+ result[:is_breaking_change] = true
61
+ result[:breaking_change] = breaking_change_matched[1]
62
+ end
63
+ unless codepush_matched.nil?
64
+ result[:is_codepush_friendly] = codepush_matched[1] == 'ok'
65
+ end
66
+ end
67
+ end
68
+
69
+ result
70
+ end
71
+
72
+ def self.semver_gt(first, second)
73
+ first_major = (first.split('.')[0] || 0).to_i
74
+ first_minor = (first.split('.')[1] || 0).to_i
75
+ first_patch = (first.split('.')[2] || 0).to_i
76
+
77
+ second_major = (second.split('.')[0] || 0).to_i
78
+ second_minor = (second.split('.')[1] || 0).to_i
79
+ second_patch = (second.split('.')[2] || 0).to_i
80
+
81
+ # Check if next version is higher then last version
82
+ if first_major > second_major
83
+ return true
84
+ elsif first_major == second_major
85
+ if first_minor > second_minor
86
+ return true
87
+ elsif first_minor == second_minor
88
+ if first_patch > second_patch
89
+ return true
90
+ end
91
+ end
92
+ end
93
+
94
+ return false
95
+ end
96
+
97
+ def self.semver_lt(first, second)
98
+ return !semver_gt(first, second)
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1 @@
1
+ module Fastlane module SemanticRelease VERSION = "1.10.0" end end
metadata ADDED
@@ -0,0 +1,175 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fastlane-plugin-semantic_release2
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.10.0
5
+ platform: ruby
6
+ authors:
7
+ - William Lauze
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-05-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: pry
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec_junit_formatter
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '='
88
+ - !ruby/object:Gem::Version
89
+ version: 0.49.1
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '='
95
+ - !ruby/object:Gem::Version
96
+ version: 0.49.1
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop-require_tools
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: simplecov
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: fastlane
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: 2.117.1
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: 2.117.1
139
+ description:
140
+ email: william.lauze@gmail.com
141
+ executables: []
142
+ extensions: []
143
+ extra_rdoc_files: []
144
+ files:
145
+ - LICENSE
146
+ - README.md
147
+ - lib/fastlane/plugin/semantic_release2.rb
148
+ - lib/fastlane/plugin/semantic_release2/actions/analyze_commits.rb
149
+ - lib/fastlane/plugin/semantic_release2/actions/conventional_changelog.rb
150
+ - lib/fastlane/plugin/semantic_release2/helper/semantic_release_helper.rb
151
+ - lib/fastlane/plugin/semantic_release2/version.rb
152
+ homepage: https://github.com/xotahal/fastlane-plugin-semantic_release
153
+ licenses:
154
+ - MIT
155
+ metadata: {}
156
+ post_install_message:
157
+ rdoc_options: []
158
+ require_paths:
159
+ - lib
160
+ required_ruby_version: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - ">="
163
+ - !ruby/object:Gem::Version
164
+ version: '0'
165
+ required_rubygems_version: !ruby/object:Gem::Requirement
166
+ requirements:
167
+ - - ">="
168
+ - !ruby/object:Gem::Version
169
+ version: '0'
170
+ requirements: []
171
+ rubygems_version: 3.0.3
172
+ signing_key:
173
+ specification_version: 4
174
+ summary: 'Fork for: Automated version managment and generator of release notes.'
175
+ test_files: []