standard_automation_library 0.2.1.pre.temp
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/standard_automation_library/api_clients/app_center.rb +104 -0
- data/lib/standard_automation_library/api_clients/bugsnag.rb +94 -0
- data/lib/standard_automation_library/api_clients/github.rb +371 -0
- data/lib/standard_automation_library/api_clients/jira.rb +326 -0
- data/lib/standard_automation_library/api_clients/pagerduty.rb +101 -0
- data/lib/standard_automation_library/api_clients/slack.rb +499 -0
- data/lib/standard_automation_library/danger/danger_jira.rb +169 -0
- data/lib/standard_automation_library/errors/slack_api_error.rb +6 -0
- data/lib/standard_automation_library/personnel/release_management_team.rb +85 -0
- data/lib/standard_automation_library/personnel/team.rb +41 -0
- data/lib/standard_automation_library/personnel/user.rb +68 -0
- data/lib/standard_automation_library/services/bugsnag_service.rb +251 -0
- data/lib/standard_automation_library/services/jira_service.rb +64 -0
- data/lib/standard_automation_library/services/merge_driver_service.rb +48 -0
- data/lib/standard_automation_library/services/mobile_tech_debt_logging_service.rb +176 -0
- data/lib/standard_automation_library/services/monorepo_platform_service.rb +18 -0
- data/lib/standard_automation_library/services/perf_tracker_logging_service.rb +87 -0
- data/lib/standard_automation_library/services/platform_service.rb +34 -0
- data/lib/standard_automation_library/services/repo_service.rb +17 -0
- data/lib/standard_automation_library/services/slack_service.rb +383 -0
- data/lib/standard_automation_library/util/automerge_configuration.rb +134 -0
- data/lib/standard_automation_library/util/bundler.rb +18 -0
- data/lib/standard_automation_library/util/datetime_helper.rb +23 -0
- data/lib/standard_automation_library/util/file_content.rb +15 -0
- data/lib/standard_automation_library/util/git.rb +235 -0
- data/lib/standard_automation_library/util/git_merge_error_message_cleaner.rb +27 -0
- data/lib/standard_automation_library/util/network.rb +39 -0
- data/lib/standard_automation_library/util/path_container.rb +17 -0
- data/lib/standard_automation_library/util/platform_picker.rb +150 -0
- data/lib/standard_automation_library/util/shared_constants.rb +27 -0
- data/lib/standard_automation_library/util/shell_helper.rb +54 -0
- data/lib/standard_automation_library/util/slack_constants.rb +40 -0
- data/lib/standard_automation_library/util/version.rb +31 -0
- data/lib/standard_automation_library/version.rb +5 -0
- data/lib/standard_automation_library.rb +8 -0
- metadata +296 -0
@@ -0,0 +1,235 @@
|
|
1
|
+
require_relative 'shell_helper'
|
2
|
+
|
3
|
+
# A collection of git commands
|
4
|
+
# Keep the method list alphabetized
|
5
|
+
module Git
|
6
|
+
@repo_root_path = nil
|
7
|
+
|
8
|
+
def self.ancestor?(ancestor_git_ref, child_git_ref)
|
9
|
+
git("merge-base --is-ancestor #{ancestor_git_ref} #{child_git_ref}")
|
10
|
+
true # exit status is 0 if ancestor_git_ref is an ancestor of child_git_ref
|
11
|
+
rescue StandardError
|
12
|
+
false
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.branch_exists?(branch_name)
|
16
|
+
rev_parse("--quiet --verify #{branch_name}")
|
17
|
+
true # exit status of zero means branch exists
|
18
|
+
rescue StandardError
|
19
|
+
false
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.checkout(arg_string)
|
23
|
+
git("checkout #{arg_string}")
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.checkout_new_branch(branch_name)
|
27
|
+
checkout("-b #{branch_name}")
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.clean(arg_string = '')
|
31
|
+
git("clean #{arg_string}")
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.clean_without_vendor_bundle(arg_string = '')
|
35
|
+
clean("-ffdx -e vendor -e .bundle #{arg_string}")
|
36
|
+
end
|
37
|
+
|
38
|
+
# TODO: Calling this method `config` results in a name conflict with the Git module in ruby-git.
|
39
|
+
def self.config_command(arg_string)
|
40
|
+
git("config #{arg_string}")
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.commit(arg_string)
|
44
|
+
git("commit #{arg_string}")
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.commit_author_email(git_ref = 'HEAD')
|
48
|
+
log("-1 --format=\"%ae\" #{git_ref}")
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.commit_author_name(git_ref = 'HEAD')
|
52
|
+
log("-1 --format=\"%an\" #{git_ref}")
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.commit_message(git_ref = 'HEAD')
|
56
|
+
log("-1 --format=\"%B\" #{git_ref}")
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.commit_title(git_ref = 'HEAD')
|
60
|
+
log("-1 --format=\"%s\" #{git_ref}")
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.commit_sha?(git_ref)
|
64
|
+
!git_ref.match(/^[a-f0-9]{9,40}$/).nil?
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.commits_between(exclude_commit, include_commit)
|
68
|
+
rev_list("#{include_commit} ^#{exclude_commit}").split("\n").map(&:strip)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Create a slack message payload containing all git related information
|
72
|
+
def self.create_message_payload(git_ref, remote_name = 'origin')
|
73
|
+
commit_or_branch_label = commit_sha?(git_ref) ? 'Commit' : 'Branch'
|
74
|
+
message_payload = { commit_or_branch_label => git_ref }
|
75
|
+
commit_hash = first_non_jenkins_commit(git_ref, remote_name)
|
76
|
+
message_payload['Git Author'] = commit_author_name(commit_hash)
|
77
|
+
message_payload['Commit Message'] = commit_message(commit_hash)
|
78
|
+
message_payload
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.current_branch_name
|
82
|
+
rev_parse('--abbrev-ref HEAD')
|
83
|
+
end
|
84
|
+
|
85
|
+
# Returns the name of the current branch if there is one,
|
86
|
+
# otherwise return the current commit sha.
|
87
|
+
def self.current_branch_or_commit
|
88
|
+
git('symbolic-ref --short -q HEAD')
|
89
|
+
rescue StandardError
|
90
|
+
rev_parse('HEAD')
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.current_commit_sha
|
94
|
+
log('-1 --format="%H"')
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.current_commit_sha_short
|
98
|
+
log('-1 --format="%h"')
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.delete_branch(branch_name)
|
102
|
+
git("branch -D #{branch_name}")
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.diff?(arg_string = '')
|
106
|
+
git("diff --quiet #{arg_string}")
|
107
|
+
false # exit status 0 means that git diff --quiet reports no diff
|
108
|
+
rescue StandardError
|
109
|
+
true
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.fetch(arg_string = '')
|
113
|
+
git("fetch #{arg_string}")
|
114
|
+
end
|
115
|
+
|
116
|
+
# Get the contents of a file from an arbitrary git ref.
|
117
|
+
# file_path must be specified from the top of the repository
|
118
|
+
# If no git ref is specified then just read the contents of
|
119
|
+
# the file on disk
|
120
|
+
def self.file_content(file_path, git_ref = nil)
|
121
|
+
if git_ref.nil?
|
122
|
+
File.read(File.join(repo_root_path, file_path))
|
123
|
+
else
|
124
|
+
git("show #{git_ref}:#{file_path}")
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# When jenkins does a PR build it merges the target branch INTO the source branch so
|
129
|
+
# using git_ref~# will eventually get us the commit of the source branch of the PR.
|
130
|
+
def self.first_non_jenkins_commit(git_ref, remote_name = 'origin')
|
131
|
+
begin
|
132
|
+
commit_hash = rev_parse(git_ref)
|
133
|
+
rescue StandardError => e
|
134
|
+
puts e.message
|
135
|
+
commit_hash = rev_parse("#{remote_name}/#{git_ref}")
|
136
|
+
end
|
137
|
+
|
138
|
+
parent_count = 0
|
139
|
+
loop do
|
140
|
+
git_author = commit_author_name("#{commit_hash}~#{parent_count}")
|
141
|
+
break if git_author != 'Jenkins'
|
142
|
+
|
143
|
+
parent_count += 1
|
144
|
+
end
|
145
|
+
rev_parse("#{commit_hash}~#{parent_count}")
|
146
|
+
end
|
147
|
+
|
148
|
+
def self.git(arg_string)
|
149
|
+
ShellHelper.run("git #{arg_string}")
|
150
|
+
end
|
151
|
+
|
152
|
+
def self.local_branch_is_behind_remote?(local_branch_name, remote_name = 'origin')
|
153
|
+
rev_parse(local_branch_name) != rev_parse("#{remote_name}/#{local_branch_name}")
|
154
|
+
end
|
155
|
+
|
156
|
+
def self.log(arg_string)
|
157
|
+
git("log #{arg_string}")
|
158
|
+
end
|
159
|
+
|
160
|
+
def self.merge(arg_string)
|
161
|
+
git("merge #{arg_string}")
|
162
|
+
end
|
163
|
+
|
164
|
+
def self.merge_abort(fail_silently: true)
|
165
|
+
merge('--abort')
|
166
|
+
rescue StandardError => e
|
167
|
+
raise e unless fail_silently
|
168
|
+
end
|
169
|
+
|
170
|
+
def self.merge_driver_installed?(key)
|
171
|
+
config_command("--get-regexp \"^merge.#{key}\"")
|
172
|
+
true # exit status 0 means merge driver is installed
|
173
|
+
rescue StandardError
|
174
|
+
false
|
175
|
+
end
|
176
|
+
|
177
|
+
# We prefer our own implementation rather than the fastlane one so that we can dry run.
|
178
|
+
def self.push_to_remote(local_branch, is_dry_run, remote_branch: nil, force: false, tags: false, remote: 'origin')
|
179
|
+
remote_branch ||= local_branch
|
180
|
+
|
181
|
+
command = [
|
182
|
+
'push',
|
183
|
+
remote,
|
184
|
+
"#{local_branch.shellescape}:#{remote_branch.shellescape}"
|
185
|
+
]
|
186
|
+
|
187
|
+
# optionally add the tags component
|
188
|
+
command << '--tags' if tags
|
189
|
+
|
190
|
+
# optionally add the force component
|
191
|
+
command << '--force' if force
|
192
|
+
|
193
|
+
if is_dry_run
|
194
|
+
puts "Would have pushed branch: #{local_branch} to remote branch: #{remote}/#{remote_branch} " \
|
195
|
+
"with force: #{force} and tags: #{tags}"
|
196
|
+
else
|
197
|
+
git(command.join(' '))
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def self.return_to_current_ref(git_clean_args = '', &_blk)
|
202
|
+
current_ref = current_branch_or_commit
|
203
|
+
begin
|
204
|
+
yield
|
205
|
+
ensure
|
206
|
+
clean_without_vendor_bundle(git_clean_args)
|
207
|
+
checkout("#{current_ref} -f")
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def self.reset(arg_string)
|
212
|
+
git("reset #{arg_string}")
|
213
|
+
end
|
214
|
+
|
215
|
+
def self.reset_hard(arg_string = '')
|
216
|
+
reset("--hard #{arg_string}")
|
217
|
+
end
|
218
|
+
|
219
|
+
def self.reset_hard_if_refs_not_equal(current_ref, reset_ref)
|
220
|
+
reset_hard(reset_ref) if rev_parse(current_ref) != rev_parse(reset_ref)
|
221
|
+
end
|
222
|
+
|
223
|
+
def self.repo_root_path
|
224
|
+
@repo_root_path = rev_parse('--show-toplevel') if @repo_root_path.nil?
|
225
|
+
@repo_root_path
|
226
|
+
end
|
227
|
+
|
228
|
+
def self.rev_list(arg_string)
|
229
|
+
git("rev-list #{arg_string}")
|
230
|
+
end
|
231
|
+
|
232
|
+
def self.rev_parse(arg_string)
|
233
|
+
git("rev-parse #{arg_string}")
|
234
|
+
end
|
235
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# Class for cleaning up git merge error messages and turning them into
|
2
|
+
# a message suitable for sending via slack. If different message processing
|
3
|
+
# is desired then subclass this and override the clean_message method.
|
4
|
+
class GitMergeErrorMessageCleaner
|
5
|
+
MERGE_CONFLICT_PREFIX = 'CONFLICT (content): Merge conflict in'.freeze
|
6
|
+
|
7
|
+
def clean_message(error_message)
|
8
|
+
merge_conflicts = []
|
9
|
+
other_messages = []
|
10
|
+
error_message.split("\n").each do |line|
|
11
|
+
merge_conflicts << line.gsub(MERGE_CONFLICT_PREFIX, '').strip if line.include?(MERGE_CONFLICT_PREFIX)
|
12
|
+
end
|
13
|
+
|
14
|
+
return error_message if merge_conflicts.empty? && other_messages.empty?
|
15
|
+
|
16
|
+
new_message = ''
|
17
|
+
unless merge_conflicts.empty?
|
18
|
+
new_message = "*Merge Conflicts*\n#{merge_conflicts.sort.map { |conflict_path| "• #{conflict_path}\n" }.join}"
|
19
|
+
end
|
20
|
+
|
21
|
+
unless other_messages.empty?
|
22
|
+
new_message += "\n*Other Messages*\n#{other_messages.map { |other_message| "• #{other_message}\n" }.join}"
|
23
|
+
end
|
24
|
+
|
25
|
+
new_message.strip
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'json'
|
3
|
+
require 'open-uri'
|
4
|
+
|
5
|
+
# Functions for performing network requests
|
6
|
+
module Network
|
7
|
+
def self.get(url, headers)
|
8
|
+
uri = URI(url)
|
9
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
10
|
+
http.use_ssl = true
|
11
|
+
request = Net::HTTP::Get.new(uri.path, headers)
|
12
|
+
http.request(request)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.post(url, headers, request_body)
|
16
|
+
uri = URI(url)
|
17
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
18
|
+
http.use_ssl = true
|
19
|
+
request = Net::HTTP::Post.new(
|
20
|
+
uri.path,
|
21
|
+
headers
|
22
|
+
)
|
23
|
+
|
24
|
+
request_body = request_body.to_json if request_body.is_a?(Hash)
|
25
|
+
|
26
|
+
request.body = request_body
|
27
|
+
http.request(request)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.json_response(url, headers = {})
|
31
|
+
uri = URI.parse(url)
|
32
|
+
JSON.parse(uri.open(headers).read)
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.download_file(url, output_path, headers = {})
|
36
|
+
uri = URI.parse(url)
|
37
|
+
File.write(output_path, uri.open(headers).read)
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# An object to contain a collections of file paths.
|
2
|
+
# This could be useful to specify paths to a file
|
3
|
+
# that is known to switch places based on checking
|
4
|
+
# out different git branches. Calling the path function
|
5
|
+
# will return the first path that exists on the file system.
|
6
|
+
class PathContainer
|
7
|
+
def initialize(paths)
|
8
|
+
@paths = paths
|
9
|
+
end
|
10
|
+
|
11
|
+
def path
|
12
|
+
@paths.each do |path|
|
13
|
+
return path if File.exist?(path)
|
14
|
+
end
|
15
|
+
nil
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
require_relative '../api_clients/github'
|
2
|
+
require_relative 'shared_constants'
|
3
|
+
|
4
|
+
# Functionality for filtering a list of path prefixes based on which
|
5
|
+
# paths have been modified between two git commits. Allows us to choose
|
6
|
+
# which platforms to build
|
7
|
+
module PlatformPicker
|
8
|
+
COMMON_BUILD_FILES = %w[
|
9
|
+
AndroidCommon.gradle
|
10
|
+
build.gradle.kts
|
11
|
+
Gemfile
|
12
|
+
Gemfile.lock
|
13
|
+
gradle.properties
|
14
|
+
gradlew
|
15
|
+
gradlew.bat
|
16
|
+
Jenkinsfile.forge
|
17
|
+
settings.gradle.kts
|
18
|
+
].freeze
|
19
|
+
|
20
|
+
COMMON_BUILD_DIRECTORIES = %w[
|
21
|
+
buildTools/
|
22
|
+
common/
|
23
|
+
deps/
|
24
|
+
gradle/
|
25
|
+
].freeze
|
26
|
+
|
27
|
+
# Determines the list of platforms that require building.
|
28
|
+
#
|
29
|
+
class PlatformDetector
|
30
|
+
# @param [String] recent_commit - A required commit hash for comparing the base branch against to.
|
31
|
+
# @param [String] older_commit - An optional commit hash of an older commit.
|
32
|
+
# @param [String] pr_number - An optional PR# for label checking.
|
33
|
+
# @param [String] repo_name - A required git repo name path.
|
34
|
+
# @param [Array] platform - A required platforms array.
|
35
|
+
# @param [TrueClass] dry_run - A boolean indicating if a dry_run should be performed.
|
36
|
+
# @param [TrueClass] verbose - A boolean indicating if verbose logging should be enabled. Helps to determine if
|
37
|
+
# certain labels were matched.
|
38
|
+
def initialize(recent_commit:, older_commit:, pr_number:, repo_name:, platforms:, dry_run:, verbose:)
|
39
|
+
@recent_commit = recent_commit
|
40
|
+
@older_commit = older_commit
|
41
|
+
@pr_number = pr_number
|
42
|
+
@platforms = platforms
|
43
|
+
@verbose = verbose || false
|
44
|
+
@github_client = GitHub.new(
|
45
|
+
repo_name,
|
46
|
+
ENV.fetch('IOS_GITHUB_API_TOKEN', nil),
|
47
|
+
api_server_base_url: SharedConstants::GITHUB_API_BASE_URL,
|
48
|
+
is_dry_run: dry_run || false
|
49
|
+
)
|
50
|
+
end
|
51
|
+
|
52
|
+
def detect
|
53
|
+
log 'Determining platforms to build...'
|
54
|
+
if @pr_number
|
55
|
+
labels_to_skip = [SharedConstants::VERSION_BUMP_LABEL, SharedConstants::AUTOGENERATED_LABEL]
|
56
|
+
log "Fetching git labels for for PR ##{@pr_number}..."
|
57
|
+
has_valid_labels = @github_client.pull_request_has_labels?(pr_number: @pr_number, labels: labels_to_skip)
|
58
|
+
if has_valid_labels
|
59
|
+
log "Detected #{labels_to_skip.join(', ')} labels.
|
60
|
+
\Skipping git revision checking as only version file(s) were changed."
|
61
|
+
log 'Platforms requiring build: []'
|
62
|
+
return []
|
63
|
+
else
|
64
|
+
log 'No labels matched'
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
platforms = self.class.platforms_to_build(@older_commit, @recent_commit, @platforms)
|
69
|
+
log "Platforms requiring build: #{platforms}"
|
70
|
+
platforms
|
71
|
+
end
|
72
|
+
|
73
|
+
def log(string)
|
74
|
+
puts string if @verbose
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.check_gradle_build_files(earlier_commit, later_commit, platforms)
|
78
|
+
file_paths = `git diff --name-only #{earlier_commit} #{later_commit}`.strip.split("\n")
|
79
|
+
does_modify_common_build_files = false
|
80
|
+
common_platforms_to_build = []
|
81
|
+
file_paths.each do |file_path|
|
82
|
+
if COMMON_BUILD_FILES.include? file_path
|
83
|
+
does_modify_common_build_files = true
|
84
|
+
break
|
85
|
+
end
|
86
|
+
|
87
|
+
next unless COMMON_BUILD_DIRECTORIES.any? { |dir| file_path.start_with?(dir) }
|
88
|
+
|
89
|
+
common_platforms_to_build = if deps_contains_one_analytics_lib_change(earlier_commit, later_commit)
|
90
|
+
%w[android]
|
91
|
+
else
|
92
|
+
platforms
|
93
|
+
end
|
94
|
+
break
|
95
|
+
end
|
96
|
+
does_modify_common_build_files ? platforms : common_platforms_to_build
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.platforms_to_build(earlier_commit, later_commit, platforms)
|
100
|
+
platform_path_prefixes = platforms.map { |platform| "#{platform}/" }
|
101
|
+
prefixes_to_build = filter_path_prefixes(platform_path_prefixes, earlier_commit, later_commit)
|
102
|
+
platforms_to_build_by_prefix = prefixes_to_build.map { |prefix| prefix.delete_suffix('/') }
|
103
|
+
platforms_to_build_by_gradle_build_files = check_gradle_build_files(earlier_commit, later_commit, platforms)
|
104
|
+
(platforms_to_build_by_prefix + platforms_to_build_by_gradle_build_files).uniq
|
105
|
+
end
|
106
|
+
|
107
|
+
def self.filter_path_prefixes(path_prefixes, earlier_commit, later_commit)
|
108
|
+
file_paths = `git diff --name-only #{earlier_commit} #{later_commit}`.strip.split("\n")
|
109
|
+
path_prefixes.select do |prefix|
|
110
|
+
should_select = false
|
111
|
+
file_paths.each do |file_path|
|
112
|
+
if file_path.start_with?(prefix)
|
113
|
+
should_select = true
|
114
|
+
break
|
115
|
+
end
|
116
|
+
end
|
117
|
+
should_select
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Ensure that the only change is in the file V.kt and specifically related to analytics, the following conditions
|
122
|
+
# must be met:
|
123
|
+
|
124
|
+
# First Condition: Verify that the total count of modified files within the /deps directory is exactly one.
|
125
|
+
# Second Condition: Ensure that V.kt is the only file that has been altered in the /deps directory.
|
126
|
+
# Third Condition: Confirm that there is precisely one modification in the V.kt file, and this modification
|
127
|
+
# includes the term analyticsLib.
|
128
|
+
|
129
|
+
# This method will help to avoid running the build for all platforms when only the analytics lib is changed.
|
130
|
+
def self.deps_contains_one_analytics_lib_change(earlier_commit, later_commit)
|
131
|
+
files_in_deps = `git diff --name-only #{earlier_commit} #{later_commit} | \
|
132
|
+
grep '^deps/'`.strip.split("\n")
|
133
|
+
if files_in_deps.length == 1
|
134
|
+
v_is_changed = files_in_deps[0].include?('/V.kt')
|
135
|
+
if v_is_changed
|
136
|
+
changes_in_v_file = `
|
137
|
+
git diff --unified=0 #{earlier_commit} #{later_commit} deps/src/main/kotlin/com/plangrid/deps/V.kt | \
|
138
|
+
grep '^[+]' | \
|
139
|
+
grep -Ev '^(--- a/|\\+\\+\\+ b/)'`.strip.split("\n")
|
140
|
+
if changes_in_v_file.length == 1
|
141
|
+
changes_in_v_file[0].include?('analyticsLib')
|
142
|
+
else
|
143
|
+
false end
|
144
|
+
else
|
145
|
+
false end
|
146
|
+
else
|
147
|
+
false end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module SharedConstants
|
2
|
+
AUTOMERGE_BRANCH_PREFIX = 'automerge/'.freeze
|
3
|
+
AUTOMERGE_LABEL = 'Automerge'.freeze
|
4
|
+
AUTOGENERATED_LABEL = 'Autogenerated'.freeze
|
5
|
+
VERSION_BUMP_LABEL = 'Version bump'.freeze
|
6
|
+
|
7
|
+
DEV = 'dev'.freeze
|
8
|
+
RC = 'rc'.freeze
|
9
|
+
HOTFIX = 'hotfix'.freeze
|
10
|
+
|
11
|
+
GITHUB_API_BASE_URL = 'https://api.github.com'.freeze
|
12
|
+
|
13
|
+
SLACK_USERNAME_MAPPING_FILE_PATH = 'common/fastlane/slack_username_mapping.yml'.freeze
|
14
|
+
|
15
|
+
INITIATION_TYPE_AUTOMATIC = 'automatic'.freeze
|
16
|
+
INITIATION_TYPE_MANUAL = 'manual'.freeze
|
17
|
+
INITIATION_TYPES = [INITIATION_TYPE_AUTOMATIC, INITIATION_TYPE_MANUAL].freeze
|
18
|
+
|
19
|
+
MERGE_METHOD_SQUASH = 'squash'.freeze
|
20
|
+
MERGE_METHOD_MERGE = 'merge'.freeze
|
21
|
+
MERGE_METHOD_REBASE = 'rebase'.freeze
|
22
|
+
VALID_MERGE_METHODS = [MERGE_METHOD_MERGE, MERGE_METHOD_SQUASH, MERGE_METHOD_REBASE].freeze
|
23
|
+
|
24
|
+
FAILURE_REPORTING_INSTRUCTIONS = 'Be a good citizen! Investigate and report this build failure using ' \
|
25
|
+
'<https://wiki.autodesk.com/display/ACSMOB/Reporting+Build+Flakes|' \
|
26
|
+
'these reporting instructions>.'.freeze
|
27
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'open3'
|
2
|
+
|
3
|
+
# TODO: consider refactor now that Sorbet has been removed from the codebase
|
4
|
+
# The shell module handles running shell commands in both a Fastlane and non Fastlane context
|
5
|
+
# The sh method only works when run in a Fastlane context but some of the library methods
|
6
|
+
# need to be run outside of a Fastlane context and we want to use the same interface
|
7
|
+
# for everything. This implementation removes the ability to use an error callback but
|
8
|
+
# that could be added in the future if necessary.
|
9
|
+
module ShellHelper
|
10
|
+
# Attempt to run the command with Fastlane's sh action. If we're not in a Fastlane context
|
11
|
+
# a NameError will be thrown in which case we'll use our own implementation.
|
12
|
+
# This implementation is a slightly simplified version of fastlane's sh command
|
13
|
+
# https://github.com/fastlane/fastlane/blob/master/fastlane/lib/fastlane/helper/sh_helper.rb
|
14
|
+
# Because we're not using Fastlane::Action.sh (Action without an S) sorbet will complain
|
15
|
+
# if this file is in strict mode without the use of T.unsafe
|
16
|
+
def self.run(command, log: true)
|
17
|
+
Fastlane::Actions.sh(command.strip, log: log).strip
|
18
|
+
rescue NameError => e
|
19
|
+
raise e unless e.message.include?('uninitialized constant') && e.message.include?('Fastlane')
|
20
|
+
|
21
|
+
previous_encoding = [Encoding.default_external, Encoding.default_internal]
|
22
|
+
Encoding.default_external = Encoding::UTF_8
|
23
|
+
Encoding.default_internal = Encoding::UTF_8
|
24
|
+
|
25
|
+
begin
|
26
|
+
puts "$ #{command.strip}" if log
|
27
|
+
result = ''
|
28
|
+
exit_status = 0
|
29
|
+
Open3.popen2e(command.strip) do |_stdin, io, thread|
|
30
|
+
io.sync = true
|
31
|
+
io.each do |line|
|
32
|
+
puts "▸ #{line.strip}" if log
|
33
|
+
result << line
|
34
|
+
end
|
35
|
+
process_status = thread.value
|
36
|
+
exit_status = process_status.exitstatus # Should not be nil because io.sync = true
|
37
|
+
end
|
38
|
+
|
39
|
+
if exit_status != 0
|
40
|
+
message = if log
|
41
|
+
"Exit status of command '#{command.strip}' was #{exit_status} instead of 0."
|
42
|
+
else
|
43
|
+
"Shell command exited with exit status #{exit_status} instead of 0."
|
44
|
+
end
|
45
|
+
message += "\n#{result}" if log
|
46
|
+
raise message
|
47
|
+
end
|
48
|
+
result.strip
|
49
|
+
ensure
|
50
|
+
Encoding.default_external = previous_encoding.first
|
51
|
+
Encoding.default_internal = previous_encoding.last
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module SlackConstants
|
2
|
+
IOS_SLACK_EMOJI = ':apple-co:'.freeze
|
3
|
+
ANDROID_SLACK_EMOJI = ':android-7248:'.freeze
|
4
|
+
WINDOWS_SLACK_EMOJI = ':windows:'.freeze
|
5
|
+
|
6
|
+
HEADSUP = 'Headsup'.freeze
|
7
|
+
RELEASE = 'Release'.freeze
|
8
|
+
|
9
|
+
IOS = 'iOS'.freeze
|
10
|
+
ANDROID = 'Android'.freeze
|
11
|
+
WINDOWS = 'Windows'.freeze
|
12
|
+
|
13
|
+
ATTACHMENT_COLOR_GOOD = 'good'.freeze
|
14
|
+
ATTACHMENT_COLOR_YELLOW = '#ffe7c6'.freeze
|
15
|
+
ATTACHMENT_COLOR_GREEN = '#08a115'.freeze
|
16
|
+
ATTACHMENT_COLOR_RED = '#de0910'.freeze
|
17
|
+
ATTACHMENT_COLOR_DANGER = 'danger'.freeze
|
18
|
+
|
19
|
+
VALID_EMAIL_SUFFIXES = %w[autodesk.com].freeze
|
20
|
+
|
21
|
+
WARNING = 'warning'.freeze
|
22
|
+
|
23
|
+
# channels-ids
|
24
|
+
ACS_MOBILE_COMMON_CI = 'C01EZ7Q7ZV1'.freeze
|
25
|
+
ACS_MOBILE_RELEASE_ANNOUNCEMENT = 'CSM2T5RQQ'.freeze
|
26
|
+
ACS_MOBILE_CODE_HEALTH = 'C04LVDLDE8M'.freeze
|
27
|
+
|
28
|
+
ACS_IOS = 'CU8CK7MN2'.freeze
|
29
|
+
ACS_IOS_CI = 'CSLJSAZQC'.freeze
|
30
|
+
ACS_IOS_CODE = 'CU820SFMM'.freeze
|
31
|
+
ACS_IOS_TEST_FLAKE = 'C021WSYAMM3'.freeze
|
32
|
+
ACS_IOS_RELEASE_COMMUNICATION = 'C01RJBTG76U'.freeze
|
33
|
+
ACS_IOS_AUTOMATION_TESTING_AB = 'C026XEUCZD0'.freeze
|
34
|
+
|
35
|
+
ACS_ANDROID = 'CU66Z5X6H'.freeze
|
36
|
+
ACS_ANDROID_CI = 'C016HBNFD6G'.freeze
|
37
|
+
ACS_ANDROID_CODE = 'CU58QK1C0'.freeze
|
38
|
+
ACS_ANDROID_RELEASE_COMMUNICATION = 'C012JKW6EUR'.freeze
|
39
|
+
ACS_AN_AUTOMATION_TESTING_AB = 'C033N3891T2'.freeze
|
40
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require_relative 'file_content'
|
2
|
+
|
3
|
+
# Generic functions for dealing with versions.
|
4
|
+
module Version
|
5
|
+
def self.app_version(version_file_path, regex, git_ref = nil)
|
6
|
+
match_list = FileContent.find_content_in_file(version_file_path, regex, git_ref)
|
7
|
+
raise "Cannot find version in #{version_file_path} at ref #{git_ref} with regex #{regex}" if match_list.empty?
|
8
|
+
|
9
|
+
match_list[0]
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.update_app_version(version_file_path, regex, replacement_string)
|
13
|
+
FileContent.find_and_replace_content_in_file(version_file_path, regex, replacement_string)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.increment_minor_version(version, increment_value)
|
17
|
+
match = version.match(/^\d+\.\d+\.\d+$/)
|
18
|
+
raise "Version #{version} is not in the correct format." unless match
|
19
|
+
|
20
|
+
version_pieces = version.split('.').map(&:to_i)
|
21
|
+
[version_pieces[0], version_pieces[1] + increment_value, 0].map(&:to_s).join('.')
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.increment_patch_version(version, increment_value)
|
25
|
+
match = version.match(/^\d+\.\d+\.\d+$/)
|
26
|
+
raise "Version #{version} is not in the correct format." unless match
|
27
|
+
|
28
|
+
version_pieces = version.split('.').map(&:to_i)
|
29
|
+
[version_pieces[0], version_pieces[1], version_pieces[2] + increment_value].map(&:to_s).join('.')
|
30
|
+
end
|
31
|
+
end
|