fastlane-plugin-semantic_versioning 1.0.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 85cd9cae201bc1df61f042591928d143e3be6b16f2118af031d8277872bb2602
4
- data.tar.gz: a1bc72d14b4866bf874ff0f1ad0b47e965baa895bcfed5ded778120e17a66ec8
3
+ metadata.gz: 1205dbf528eadda53b7561ee36d93418c9f12fd7e5b8baa1f93f3f338411baa8
4
+ data.tar.gz: e083a5c015ecd318254c853589dc9233aa3e86b3da96699ebeaeecf95c2630f7
5
5
  SHA512:
6
- metadata.gz: 51ea808a889665240814d26ce939f1c006c9bd03b6544a3a289e38afb460668dc51e4f723a4507b6a8f3a3112b71bb1bd407bfd2da5df513a22b085b3053337e
7
- data.tar.gz: 0227ac5c1726f2e9954f9d272bb36e99fbf83af5280e7831ffb823ccd44ed32f1b5528c57188a4bd927956e9cdcbd7940183604369fcbcf6c95e830c1e9cd85c
6
+ metadata.gz: 03b8bd862f3f5ef41791fd214864ac6d15c3928fe6e1fbf27acdb817619c6d3acae71b98c66b342a34d978795172e429fa7b0849e22313273fdbeac27a2fc4b5
7
+ data.tar.gz: c5efd5ec4a7f1e73150f105d6f82a0c54aa52926d694224286ce64fb38f5614abb916b25fb2c2933ad099bc843bad6e01f5ead837a9cacf980a3a321c811e6c9
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "fastlane/action"
2
4
  require "fastlane_core/configuration/config_item"
3
5
  require_relative "../helper/semantic_versioning_helper"
@@ -13,11 +15,13 @@ module Fastlane
13
15
  SEMVER_BUMPABLE = :SEMVER_BUMPABLE
14
16
  end
15
17
 
18
+ # Action to retrieve semantic versioning information from commit history.
16
19
  class GetVersioningInfoAction < Action
17
20
  def self.run(params)
18
21
  params[:allowed_types].map!(&:to_sym)
19
22
  params[:bump_map].transform_keys!(&:to_sym)
20
23
  params[:bump_map].transform_values!(&:to_sym)
24
+ params[:force_type] = params[:force_type]&.to_sym
21
25
 
22
26
  UI.message("The semantic_versioning plugin is working!")
23
27
 
@@ -26,12 +30,15 @@ module Fastlane
26
30
 
27
31
  commits = Helper::SemanticVersioningHelper.git_commits(
28
32
  from: Helper::SemanticVersioningHelper.git_tag_exists?(formatted_tag) ? formatted_tag : nil,
29
- allowed_types: params[:allowed_types]
33
+ allowed_types: params[:allowed_types],
34
+ bump_map: params[:bump_map]
30
35
  )
31
36
 
32
- bump_type = Helper::SemanticVersioningHelper.bump_type(commits: commits, bump_map: params[:bump_map])
33
- new_version = Helper::SemanticVersioningHelper.increase_version(current_version: current_version, bump_type: bump_type)
34
- new_changelog = Helper::SemanticVersioningHelper.build_changelog(version: new_version, commits: commits, type_map: params[:type_map])
37
+ bump_type = Helper::SemanticVersioningHelper.bump_type(commits: commits, force_type: params[:force_type])
38
+ new_version = Helper::SemanticVersioningHelper.increase_version(current_version: current_version,
39
+ bump_type: bump_type)
40
+ new_changelog = Helper::SemanticVersioningHelper.build_changelog(version: new_version, commits: commits,
41
+ type_map: params[:type_map])
35
42
  bumpable = current_version != new_version
36
43
 
37
44
  Actions.lane_context[SharedValues::SEMVER_CURRENT_VERSION] = current_version
@@ -41,9 +48,10 @@ module Fastlane
41
48
  Actions.lane_context[SharedValues::SEMVER_NEW_CHANGELOG] = new_changelog
42
49
  Actions.lane_context[SharedValues::SEMVER_BUMPABLE] = bumpable
43
50
 
44
- return bumpable
51
+ bumpable
45
52
  end
46
53
 
54
+ # :nocov:
47
55
  def self.description
48
56
  "Retrieve semantic versioning information from commit history."
49
57
  end
@@ -73,6 +81,7 @@ module Fastlane
73
81
  # Optional:
74
82
  "Reads commits from last version and determines next version and changelog."
75
83
  end
84
+ # :nocov:
76
85
 
77
86
  def self.available_options
78
87
  [
@@ -80,7 +89,8 @@ module Fastlane
80
89
  env_name: "SEMANTIC_VERSIONING_ALLOWED_TYPES",
81
90
  description: "List of allowed commit types",
82
91
  optional: true,
83
- default_value: %w[build ci docs feat fix perf refactor style test chore revert bump init],
92
+ default_value: %w[build ci docs feat fix perf refactor style test chore revert
93
+ bump init],
84
94
  type: Array),
85
95
  FastlaneCore::ConfigItem.new(key: :bump_map,
86
96
  description: "Map of commit types to their bump level (major, minor, patch)",
@@ -88,6 +98,12 @@ module Fastlane
88
98
  default_value: { breaking: :major, feat: :minor, fix: :patch },
89
99
  is_string: false,
90
100
  verify_block: ->(value) { verify_bump_map(value) }),
101
+ FastlaneCore::ConfigItem.new(key: :force_type,
102
+ env_name: "SEMANTIC_VERSIONING_FORCE_TYPE",
103
+ description: "Force a minimum bump type",
104
+ optional: true,
105
+ default_value: nil,
106
+ type: String),
91
107
  FastlaneCore::ConfigItem.new(key: :tag_format,
92
108
  env_name: "SEMANTIC_VERSIONING_TAG_FORMAT",
93
109
  description: "The format for the git tag",
@@ -99,29 +115,30 @@ module Fastlane
99
115
  description: "Map of types to section titles for the changelog." \
100
116
  "Only the specified types will be used for the changelog",
101
117
  optional: true,
102
- default_value: { breaking: "BREAKING CHANGES", feat: "Features", fix: "Bug Fixes" },
118
+ default_value: { breaking: "BREAKING CHANGES", feat: "Features",
119
+ fix: "Bug Fixes" },
103
120
  is_string: false,
104
121
  verify_block: ->(value) { verify_type_map(value) })
105
122
  ]
106
123
  end
107
124
 
108
125
  def self.verify_type_map(value)
109
- UI.user_error!("Parameter 'type_map' must be a Hash.") unless value.kind_of?(Hash)
110
- # It could be helpful to also test for valid keys, which must all be part of ConfigItem :allowed_types plus breaking.
111
- # How to check this?
126
+ UI.user_error!("Parameter 'type_map' must be a Hash.") unless value.is_a?(Hash)
112
127
  end
113
128
 
114
129
  def self.verify_bump_map(value)
115
- UI.user_error!("Parameter 'bump_map' must be a Hash.") unless value.kind_of?(Hash)
130
+ UI.user_error!("Parameter 'bump_map' must be a Hash.") unless value.is_a?(Hash)
116
131
  end
117
132
 
118
- def self.is_supported?(platform)
133
+ # :nocov:
134
+ def self.is_supported?(_platform)
119
135
  # Adjust this if your plugin only works for a particular platform (iOS vs. Android, for example)
120
136
  # See: https://docs.fastlane.tools/advanced/#control-configuration-by-lane-and-by-platform
121
137
  #
122
138
  # [:ios, :mac, :android].include?(platform)
123
139
  true
124
140
  end
141
+ # :nocov:
125
142
  end
126
143
  end
127
144
  end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fastlane/action"
4
+ require_relative "../helper/semantic_versioning_helper"
5
+
6
+ module Fastlane
7
+ module Actions
8
+ # Action to prepare the Xcode project for automatic versioning
9
+ class PrepareVersioningAction < Action
10
+ def self.run(params)
11
+ xcodeproj_path = params[:xcodeproj] || Dir.glob("*.xcodeproj").first
12
+
13
+ Fastlane::UI.user_error!("Unable to determine *.xcodeproj and no :xcodeproj specified") if xcodeproj_path.nil?
14
+
15
+ project = Helper::SemanticVersioningHelper.project(xcodeproj_path)
16
+ target = Helper::SemanticVersioningHelper.main_target
17
+ main_group = Helper::SemanticVersioningHelper.main_group
18
+
19
+ info_plist_file = File.join(main_group.path, "Info.plist")
20
+ info_plist_path = File.join(project.path.dirname, info_plist_file)
21
+
22
+ target.build_configurations.each do |config|
23
+ config.build_settings["VERSIONING_SYSTEM"] = "apple-generic"
24
+ config.build_settings.delete("MARKETING_VERSION")
25
+ config.build_settings.delete("GENERATE_INFOPLIST_FILE")
26
+
27
+ config.build_settings["INFOPLIST_FILE"] = info_plist_file unless config.build_settings["INFOPLIST_FILE"]
28
+
29
+ Helper::SemanticVersioningHelper.ensure_info_plist(info_plist_path)
30
+ end
31
+ project.save
32
+ true
33
+ end
34
+
35
+ # :nocov:
36
+ def self.description
37
+ "Prepares the Xcodeproject to be used with automatic versioning by tools."
38
+ end
39
+
40
+ def self.authors
41
+ ["kassi"]
42
+ end
43
+
44
+ def self.output
45
+ # Define the shared values you are going to provide
46
+ end
47
+
48
+ def self.return_value
49
+ # If your method provides a return value, you can describe here what it does
50
+ "Truthy value when everything worked out well."
51
+ end
52
+
53
+ def self.details
54
+ # Optional:
55
+ "Changes the versioning style and makes sure that version information is extracted into Info.plist " \
56
+ "to be used with agvtool"
57
+ end
58
+ # :nocov:
59
+
60
+ def self.available_options
61
+ [
62
+ FastlaneCore::ConfigItem.new(key: :main_group,
63
+ env_name: "SEMANTIC_VERSIONING_MAIN_GROUP",
64
+ description: "The name of the main group of the xcode project",
65
+ optional: true,
66
+ default_value: nil,
67
+ type: String),
68
+ FastlaneCore::ConfigItem.new(key: :xcodeproj,
69
+ env_name: "SEMANTIC_VERSIONING_PROJECT",
70
+ description: "The path to your project file (Not the workspace). Optional if you have only one",
71
+ optional: true,
72
+ default_value: nil,
73
+ verify_block: proc do |value|
74
+ if value.end_with?(".xcworkspace")
75
+ UI.user_error!("Please pass the path to the project, not the workspace")
76
+ end
77
+ UI.user_error!("Could not find Xcode project") unless File.exist?(value)
78
+ end)
79
+ ]
80
+ end
81
+
82
+ # :nocov:
83
+ def self.is_supported?(platform)
84
+ # Adjust this if your plugin only works for a particular platform (iOS vs. Android, for example)
85
+ # See: https://docs.fastlane.tools/advanced/#control-configuration-by-lane-and-by-platform
86
+ #
87
+ %i[ios mac].include?(platform)
88
+ end
89
+ # :nocov:
90
+ end
91
+ end
92
+ end
@@ -1,8 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "fastlane/action"
2
4
  require_relative "../helper/semantic_versioning_helper"
3
5
 
4
6
  module Fastlane
5
7
  module Actions
8
+ # Action to bumps the version according to semantic versioning and writes a changelog.
6
9
  class SemanticBumpAction < Action
7
10
  def self.run(params)
8
11
  unless Actions.lane_context.key?(SharedValues::SEMVER_BUMPABLE)
@@ -32,9 +35,10 @@ module Fastlane
32
35
  include: [params[:changelog_file]].compact
33
36
  )
34
37
 
35
- return true
38
+ true
36
39
  end
37
40
 
41
+ # :nocov:
38
42
  def self.description
39
43
  "Bumps the version according to semantic versioning and writes a changelog."
40
44
  end
@@ -56,6 +60,7 @@ module Fastlane
56
60
  # Optional:
57
61
  "Reads commits from last version and determines next version and changelog."
58
62
  end
63
+ # :nocov:
59
64
 
60
65
  def self.available_options
61
66
  [
@@ -74,13 +79,15 @@ module Fastlane
74
79
  ]
75
80
  end
76
81
 
77
- def self.is_supported?(platform)
82
+ # :nocov:
83
+ def self.is_supported?(_platform)
78
84
  # Adjust this if your plugin only works for a particular platform (iOS vs. Android, for example)
79
85
  # See: https://docs.fastlane.tools/advanced/#control-configuration-by-lane-and-by-platform
80
86
  #
81
87
  # [:ios, :mac, :android].include?(platform)
82
88
  true
83
89
  end
90
+ # :nocov:
84
91
  end
85
92
  end
86
93
  end
@@ -1,6 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "fastlane_core/ui/ui"
2
4
  require "fastlane/actions/get_version_number"
3
5
  require "git"
6
+ require "xcodeproj"
4
7
 
5
8
  module Fastlane
6
9
  UI = FastlaneCore::UI unless Fastlane.const_defined?(:UI)
@@ -22,41 +25,54 @@ module Fastlane
22
25
  format.sub("$version", tag)
23
26
  end
24
27
 
25
- def self.git_tag_exists?(tag)
26
- git.tags.include?(tag)
27
- end
28
-
29
28
  # Retrieves git commits and returns them grouped by type
30
- def self.git_commits(from:, allowed_types:)
29
+ def self.git_commits(from:, allowed_types:, bump_map:)
31
30
  logs = from ? git.log(-1).between(from) : git.log(-1)
32
- logs.reverse_each.map do |commit|
33
- parse_conventional_commit(commit: commit, allowed_types: allowed_types)
34
- end.compact
31
+ logs.reverse_each.filter_map { |commit|
32
+ parse_conventional_commit(commit: commit, allowed_types: allowed_types, bump_map: bump_map)
33
+ }
35
34
  end
36
35
 
37
- def self.group_commits(commits:, allowed_types:)
38
- result = allowed_types.to_h { |type| [type, []] }
36
+ def self.parse_conventional_commit(commit:, allowed_types:, bump_map:)
37
+ types = allowed_types.join("|")
38
+ commit.message.match(/^(?<type>#{types})(\((?<scope>\S+)\))?(?<major>!)?:\s+(?<subject>[^\n\r]+)(\z|\n\n(?<body>.*\z))/m) do |match|
39
+ cc = {
40
+ type: match[:type].to_sym,
41
+ major: !match[:major].nil?,
42
+ scope: match[:scope],
43
+ subject: match[:subject],
44
+ body: match[:body],
45
+ breaking: nil,
46
+ original_message: commit.message
47
+ }
39
48
 
40
- commits.each do |commit|
41
- if commit[:breaking] && allowed_types.include?(:breaking)
42
- result[:breaking] << commit
49
+ match[:body]&.match(/^BREAKING CHANGE?: (.+)\z/) do |breaking|
50
+ cc[:breaking] = breaking[1]
43
51
  end
44
- next unless allowed_types.include?(commit[:type])
45
52
 
46
- # If the breaking change is made from the actual feature subject, don't repeat it.
47
- next if commit[:breaking] == commit[:subject]
53
+ cc[:bump] = commit_bump_type(commit: cc, bump_map: bump_map)
48
54
 
49
- result[commit[:type]] << commit
55
+ return cc
50
56
  end
57
+ end
58
+
59
+ def self.commit_bump_type(commit:, bump_map:)
60
+ return :major if commit[:major]
61
+
62
+ return bump_map[:breaking] if commit[:breaking]
51
63
 
52
- return result
64
+ bump_map[commit[:type]]
53
65
  end
54
66
 
55
- def self.bump_type(commits:, bump_map:)
56
- result = nil
67
+ def self.bump_type(commits:, force_type: nil)
68
+ return force_type if force_type == :major # can't go any higher
69
+
70
+ result = force_type
57
71
 
58
72
  commits.each do |commit|
59
- bump_type = commit[:breaking] ? bump_map[:breaking] : bump_map[commit[:type]]
73
+ return :major if commit[:major]
74
+
75
+ bump_type = commit[:bump]
60
76
  if bump_type == :major
61
77
  return :major
62
78
  elsif bump_type == :minor
@@ -66,32 +82,27 @@ module Fastlane
66
82
  end
67
83
  end
68
84
 
69
- return result
85
+ result
70
86
  end
71
87
 
72
- def self.parse_conventional_commit(commit:, allowed_types:)
73
- types = allowed_types.join("|")
74
- commit.message.match(/^(?<type>#{types})(\((?<scope>\S+)\))?(?<breaking>!)?:\s+(?<subject>[^\n\r]+)(\z|\n\n(?<body>.*\z))/m) do |match|
75
- unless allowed_types.include?(match[:type].to_sym)
76
- UI.important("Commit #{commit.sha} has invalid type: #{match[:type]}. Ignoring")
77
- break
78
- end
88
+ def self.group_commits(commits:, allowed_types:)
89
+ result = allowed_types.to_h { |type| [type, []] }
90
+ result[:none] = []
79
91
 
80
- cc = {
81
- type: match[:type].to_sym,
82
- scope: match[:scope],
83
- subject: match[:subject],
84
- body: match[:body],
85
- breaking: match[:breaking] ? match[:subject] : nil,
86
- original_message: commit.message
87
- }
92
+ commits.each do |commit|
93
+ result[:breaking] << commit if commit[:breaking] && allowed_types.include?(:breaking)
88
94
 
89
- match[:body]&.match(/^BREAKING CHANGE?: (.+)\z/) do |breaking|
90
- cc[:breaking] = breaking[1]
95
+ if commit[:major] && !allowed_types.include?(commit[:type])
96
+ result[:none] << commit
97
+ next
91
98
  end
92
99
 
93
- return cc
100
+ next unless allowed_types.include?(commit[:type])
101
+
102
+ result[commit[:type]] << commit
94
103
  end
104
+
105
+ result
95
106
  end
96
107
 
97
108
  def self.increase_version(current_version:, bump_type:)
@@ -108,7 +119,7 @@ module Fastlane
108
119
  version_array[2] += 1
109
120
  end
110
121
 
111
- return version_array.join(".")
122
+ version_array.join(".")
112
123
  end
113
124
 
114
125
  # Builds and returns th changelog for the upcoming release.
@@ -123,7 +134,7 @@ module Fastlane
123
134
  grouped_commits.each do |key, section_commits|
124
135
  next unless section_commits.any?
125
136
 
126
- lines << "### #{type_map[key]}:"
137
+ lines << "### #{type_map[key]}:" if key != :none
127
138
  lines << ""
128
139
 
129
140
  section_commits.each do |commit|
@@ -133,7 +144,7 @@ module Fastlane
133
144
  lines << ""
134
145
  end
135
146
 
136
- return "#{lines.join("\n")}\n"
147
+ "#{lines.join("\n")}\n"
137
148
  end
138
149
 
139
150
  def self.write_changelog(path:, changelog:)
@@ -154,15 +165,54 @@ module Fastlane
154
165
  .sub("$new_version", Actions.lane_context[Actions::SharedValues::SEMVER_NEW_VERSION]))
155
166
  end
156
167
 
157
- def self.git_command(args)
158
- Actions.sh("git #{args.join(' ')}").chomp
159
- end
160
-
161
168
  def self.git
162
169
  # rubocop:disable Style/ClassVars
163
170
  @@git ||= Git.open(".")
164
171
  # rubocop:enable Style/ClassVars
165
172
  end
173
+
174
+ def self.git_tag_exists?(tag)
175
+ git.tags.include?(tag)
176
+ end
177
+
178
+ def self.project(path = nil)
179
+ # rubocop:disable Style/ClassVars
180
+ @@project ||= Xcodeproj::Project.open(path)
181
+ # rubocop:enable Style/ClassVars
182
+ end
183
+
184
+ def self.main_target
185
+ project.targets.first
186
+ end
187
+
188
+ def self.current_version
189
+ @current_version ||= main_target.build_configurations.first.build_settings["MARKETING_VERSION"] || "1.0"
190
+ end
191
+
192
+ def self.main_group(name = nil)
193
+ project.main_group[name || main_target.name]
194
+ end
195
+
196
+ def self.ensure_info_plist(path)
197
+ return if File.exist?(path)
198
+
199
+ File.write(path, <<-"PLIST")
200
+ <?xml version="1.0" encoding="UTF-8"?>
201
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
202
+ <plist version="1.0">
203
+ <dict>
204
+ <key>CFBundleVersion</key>
205
+ <string>#{current_version}</string>
206
+ <key>CFBundleShortVersionString</key>
207
+ <string>#{current_version}</string>
208
+ </dict>
209
+ </plist>
210
+ PLIST
211
+
212
+ info_plist = main_group.new_file("Info.plist")
213
+ main_target.add_file_references([info_plist])
214
+ Fastlane::UI.success("Successfully created the file '#{path}' for agvtool")
215
+ end
166
216
  end
167
217
  end
168
218
  end
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Fastlane
2
4
  module SemanticVersioning
3
- VERSION = "1.0.0"
5
+ VERSION = "2.1.0"
4
6
  end
5
7
  end
@@ -1,6 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "fastlane/plugin/semantic_versioning/version"
2
4
 
3
5
  module Fastlane
6
+ # Provides actions for dealing with semantic versions and conventional commits
4
7
  module SemanticVersioning
5
8
  # Return all .rb files inside the "actions" and "helper" directory
6
9
  def self.all_classes
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fastlane-plugin-semantic_versioning
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Karsten Silkenbäumer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-05-17 00:00:00.000000000 Z
11
+ date: 2024-05-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: git
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: xcodeproj
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.24'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.24'
27
41
  description:
28
42
  email: 993392+kassi@users.noreply.github.com
29
43
  executables: []
@@ -34,6 +48,7 @@ files:
34
48
  - README.md
35
49
  - lib/fastlane/plugin/semantic_versioning.rb
36
50
  - lib/fastlane/plugin/semantic_versioning/actions/get_versioning_info_action.rb
51
+ - lib/fastlane/plugin/semantic_versioning/actions/prepare_versioning_action.rb
37
52
  - lib/fastlane/plugin/semantic_versioning/actions/semantic_bump_action.rb
38
53
  - lib/fastlane/plugin/semantic_versioning/helper/semantic_versioning_helper.rb
39
54
  - lib/fastlane/plugin/semantic_versioning/version.rb