fastlane-plugin-wpmreleasetoolkit 1.0.0
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/LICENSE +339 -0
- data/README.md +38 -0
- data/bin/drawText +19 -0
- data/ext/drawText/extconf.rb +36 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit.rb +16 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/README.md +20 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/an_localize_libs_action.rb +53 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/an_update_metadata_source_action.rb +171 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/an_validate_lib_strings_action.rb +63 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_betabuild_prechecks.rb +103 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_build_prechecks.rb +83 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_build_preflight.rb +54 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_bump_version_beta.rb +69 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_bump_version_final_release.rb +58 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_bump_version_hotfix.rb +77 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_bump_version_release.rb +89 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_codefreeze_prechecks.rb +79 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_completecodefreeze_prechecks.rb +68 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_create_xml_release_notes.rb +63 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_current_branch_is_hotfix.rb +44 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_download_file_by_version.rb +79 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_download_translations_action.rb +115 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_finalize_prechecks.rb +71 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_get_alpha_version.rb +44 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_get_app_version.rb +44 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_get_release_version.rb +44 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_hotifx_prechecks.rb +78 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_merge_translators_strings.rb +106 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_tag_build.rb +51 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_update_metadata.rb +52 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_update_release_notes.rb +56 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/circleci_trigger_job_action.rb +63 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/close_milestone_action.rb +56 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/create_new_milestone_action.rb +59 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/create_release_action.rb +91 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/extract_release_notes_for_version_action.rb +89 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/get_prs_list_action.rb +64 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/gp_downloadmetadata_action.rb +90 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/gp_update_metadata_source.rb +170 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/promo_screenshots_action.rb +247 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/removebranchprotection_action.rb +57 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/setbranchprotection_action.rb +56 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/setfrozentag_action.rb +81 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/configure/configure_add_files_to_copy_action.rb +96 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/configure/configure_apply_action.rb +139 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/configure/configure_download_action.rb +57 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/configure/configure_setup_action.rb +86 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/configure/configure_update_action.rb +139 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/configure/configure_validate_action.rb +134 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/add_development_certificates_to_provisioning_profiles.rb +77 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/add_devices_to_provisioning_profiles.rb +79 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_betabuild_prechecks.rb +92 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_build_prechecks.rb +74 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_build_preflight.rb +78 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_bump_version_beta.rb +68 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_bump_version_hotfix.rb +87 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_bump_version_release.rb +114 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_check_beta_deps.rb +62 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_clear_intermediate_tags.rb +60 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_codefreeze_prechecks.rb +70 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_completecodefreeze_prechecks.rb +63 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_current_branch_is_hotfix.rb +40 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_final_tag.rb +52 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_finalize_prechecks.rb +64 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_get_app_version.rb +47 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_get_build_version.rb +60 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_get_store_app_sizes.rb +121 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_hotifx_prechecks.rb +78 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_lint_localizations.rb +167 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_localize_project.rb +44 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_merge_translators_strings.rb +93 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_tag_build.rb +44 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_update_metadata.rb +40 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_update_metadata_source.rb +81 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_update_release_notes.rb +56 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_validate_ci_build.rb +46 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/an_metadata_update_helper.rb +152 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/android/android_git_helper.rb +44 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/android/android_localize_helper.rb +359 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/android/android_version_helper.rb +475 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/ci_helper.rb +91 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/configure_helper.rb +282 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/encryption_helper.rb +51 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/filesystem_helper.rb +93 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/git_helper.rb +224 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/github_helper.rb +135 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/ios/ios_adc_app_sizes_helper.rb +74 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/ios/ios_git_helper.rb +80 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/ios/ios_l10n_helper.rb +208 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/ios/ios_version_helper.rb +348 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/metadata_download_helper.rb +107 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/metadata_update_helper.rb +182 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/promo_screenshots_helper.rb +399 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/release_notes_helper.rb +21 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/models/configuration.rb +40 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/models/file_reference.rb +86 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/version.rb +5 -0
- metadata +449 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
require 'git'
|
|
2
|
+
|
|
3
|
+
module Fastlane
|
|
4
|
+
module Helper
|
|
5
|
+
# Helper methods to execute git-related operations
|
|
6
|
+
#
|
|
7
|
+
module GitHelper
|
|
8
|
+
# Checks if the given path, or current directory if no path is given, is
|
|
9
|
+
# inside a Git repository
|
|
10
|
+
#
|
|
11
|
+
# @param [String] path An optional path where to check if a Git repo
|
|
12
|
+
# exists.
|
|
13
|
+
#
|
|
14
|
+
# @return [Bool] True if the current directory is the root of a git repo
|
|
15
|
+
# (i.e. a local working copy) or a subdirectory of one.
|
|
16
|
+
#
|
|
17
|
+
def self.is_git_repo?(path: Dir.pwd)
|
|
18
|
+
# If the path doesn't exist, find its first ancestor.
|
|
19
|
+
path = first_existing_ancestor_of(path: path)
|
|
20
|
+
# Get the path's directory, so we can look in it for the Git folder
|
|
21
|
+
dir = path.directory? ? path : path.dirname
|
|
22
|
+
|
|
23
|
+
# Recursively look for the Git folder until it's found or we read the
|
|
24
|
+
# the file system root
|
|
25
|
+
dir = dir.parent until Dir.entries(dir).include?('.git') || dir.root?
|
|
26
|
+
|
|
27
|
+
# If we reached the root, we haven't found a repo. (Technically, there
|
|
28
|
+
# could be a repo in the root of the system, but that's a usecase that
|
|
29
|
+
# we don't need to support at this time)
|
|
30
|
+
return dir.root? == false
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Travels back the hierarchy of the given path until it finds an existing
|
|
34
|
+
# ancestor, or it reaches the root of the file system.
|
|
35
|
+
#
|
|
36
|
+
# @param [String] path The path to inspect
|
|
37
|
+
#
|
|
38
|
+
# @return [Pathname] The first existing ancestor, or `path` itself if it
|
|
39
|
+
# exists
|
|
40
|
+
#
|
|
41
|
+
def self.first_existing_ancestor_of(path:)
|
|
42
|
+
p = Pathname(path).expand_path
|
|
43
|
+
p = p.parent until p.exist? || p.root?
|
|
44
|
+
return p
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Check if the current directory has git-lfs enabled
|
|
48
|
+
#
|
|
49
|
+
# @return [Bool] True if the current directory is a git working copy and has git-lfs enabled.
|
|
50
|
+
#
|
|
51
|
+
def self.has_git_lfs?
|
|
52
|
+
return false unless is_git_repo?
|
|
53
|
+
|
|
54
|
+
`git config --get-regex lfs`.length > 0
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Switch to the given branch and pull its latest commits.
|
|
58
|
+
#
|
|
59
|
+
# @param [String,Hash] branch Name of the branch to pull.
|
|
60
|
+
# If you provide a Hash with a single key=>value pair, it will build the branch name as `"#{key}/#{value}"`,
|
|
61
|
+
# i.e. `checkout_and_pull(release: version)` is equivalent to `checkout_and_pull("release/#{version}")`.
|
|
62
|
+
#
|
|
63
|
+
# @return [Bool] True if it succeeded switching and pulling, false if there was an error during the switch or pull.
|
|
64
|
+
#
|
|
65
|
+
def self.checkout_and_pull(branch)
|
|
66
|
+
branch = branch.first.join('/') if branch.is_a?(Hash)
|
|
67
|
+
Action.sh('git', 'checkout', branch)
|
|
68
|
+
Action.sh('git', 'pull')
|
|
69
|
+
return true
|
|
70
|
+
rescue
|
|
71
|
+
return false
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Update every submodule in the current git repository
|
|
75
|
+
#
|
|
76
|
+
def self.update_submodules
|
|
77
|
+
Action.sh('git', 'submodule', 'update', '--init', '--recursive')
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Create a new branch named `branch_name`, cutting it from branch/commit/tag `from`, and push it
|
|
81
|
+
#
|
|
82
|
+
# If the branch with that name already exists, it will instead switch to it and pull new commits.
|
|
83
|
+
#
|
|
84
|
+
# @param [String] branch_name The full name of the new branch to create, e.g "release/1.2"
|
|
85
|
+
# @param [String?] from The branch or tag from which to cut the branch from.
|
|
86
|
+
# If `nil`, will cut the new branch from the current commit. Otherwise, will checkout that commit/branch/tag before cutting the branch.
|
|
87
|
+
# @param [Bool] push If true, will also push the branch to `origin`, tracking the upstream branch with the local one.
|
|
88
|
+
#
|
|
89
|
+
def self.create_branch(branch_name, from: nil, push: true)
|
|
90
|
+
if branch_exists?(branch_name)
|
|
91
|
+
UI.message("Branch #{branch_name} already exists. Skipping creation.")
|
|
92
|
+
Action.sh('git', 'checkout', branch_name)
|
|
93
|
+
Action.sh('git', 'pull', 'origin', branch_name)
|
|
94
|
+
else
|
|
95
|
+
Action.sh('git', 'checkout', from) unless from.nil?
|
|
96
|
+
Action.sh('git', 'checkout', '-b', branch_name)
|
|
97
|
+
Action.sh('git', 'push', '-u', 'origin', branch_name) if push
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# `git add` the specified files (if any provided) then commit them using the provided message.
|
|
102
|
+
# Optionally, push the commit to the remote too.
|
|
103
|
+
#
|
|
104
|
+
# @param [String] message The commit message to use
|
|
105
|
+
# @param [String|Array<String>] files A file or array of files to git-add before creating the commit.
|
|
106
|
+
# use `nil` or `[]` if you already added the files in a separate step and don't wan't this method to add any new file before commit.
|
|
107
|
+
# Also accepts the special symbol `:all` to add all the files (`git commit -a -m …`).
|
|
108
|
+
# @param [Bool] push If true, will `git push` to `origin` after the commit has been created. Defaults to `false`.
|
|
109
|
+
#
|
|
110
|
+
# @return [Bool] True if commit and push were successful, false if there was an issue during commit & push (most likely being "nothing to commit").
|
|
111
|
+
#
|
|
112
|
+
def self.commit(message:, files: nil, push: false)
|
|
113
|
+
files = [files] if files.is_a?(String)
|
|
114
|
+
args = []
|
|
115
|
+
if files == :all
|
|
116
|
+
args = ['-a']
|
|
117
|
+
elsif !files.nil? && !files.empty?
|
|
118
|
+
Action.sh('git', 'add', *files)
|
|
119
|
+
end
|
|
120
|
+
begin
|
|
121
|
+
Action.sh('git', 'commit', *args, '-m', message)
|
|
122
|
+
Action.sh('git', 'push', 'origin', 'HEAD') if push
|
|
123
|
+
return true
|
|
124
|
+
rescue
|
|
125
|
+
return false
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Get the SHA of a given git ref. Typically useful to get the SHA of the current HEAD commit.
|
|
130
|
+
#
|
|
131
|
+
# @param [String] ref The git ref (commit, branch name, 'HEAD', …) to resolve as a SHA
|
|
132
|
+
# @return [String] The commit SHA of the ref
|
|
133
|
+
#
|
|
134
|
+
def self.get_commit_sha(ref: 'HEAD')
|
|
135
|
+
Git.open(Dir.pwd).revparse(ref)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Creates a tag for the given version, and optionally push it to the remote.
|
|
139
|
+
#
|
|
140
|
+
# @param [String] version The name of the tag to push, e.g. "1.2"
|
|
141
|
+
# @param [Bool] push If true (the default), the tag will also be pushed to `origin`
|
|
142
|
+
#
|
|
143
|
+
def self.create_tag(version, push: true)
|
|
144
|
+
Action.sh('git', 'tag', version)
|
|
145
|
+
Action.sh('git', 'push', 'origin', version) if push
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Returns the list of tags that are pointing to the current commit (HEAD)
|
|
149
|
+
#
|
|
150
|
+
# @return [Array<String>] List of tags associated with the HEAD commit
|
|
151
|
+
#
|
|
152
|
+
def self.list_tags_on_current_commit
|
|
153
|
+
Action.sh('git', 'tag', '--points-at', 'HEAD').split("\n")
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# List all the tags in the local working copy, optionally filtering the list using a pattern
|
|
157
|
+
#
|
|
158
|
+
# @param [String] matching The pattern of the tag(s) to match and filter on; use "*" for wildcards.
|
|
159
|
+
# For example, `"1.2.*"` will match every tag starting with `"1.2."`. Defaults to '*' which lists all tags.
|
|
160
|
+
#
|
|
161
|
+
# @return [Array<String>] The list of local tags matching the pattern
|
|
162
|
+
#
|
|
163
|
+
def self.list_local_tags(matching: '*')
|
|
164
|
+
Action.sh('git', 'tag', '--list', matching).split("\n")
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Delete the mentioned local tags in the local working copy, and optionally delete them on the remote too.
|
|
168
|
+
#
|
|
169
|
+
# @param [Array<String>] tags_to_delete The list of tags to delete
|
|
170
|
+
# @param [Bool] delete_on_remote If true, will also delete the tag from the remote. Otherwise, it will only be deleted locally.
|
|
171
|
+
#
|
|
172
|
+
def self.delete_tags(tags_to_delete, delete_on_remote: false)
|
|
173
|
+
g = Git.open(Dir.pwd)
|
|
174
|
+
local_tag_names = g.tags.map(&:name)
|
|
175
|
+
|
|
176
|
+
Array(tags_to_delete).each do |tag|
|
|
177
|
+
g.delete_tag(tag) if local_tag_names.include?(tag)
|
|
178
|
+
g.push('origin', ":refs/tags/#{tag}") if delete_on_remote
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Fetch all the tags from the remote.
|
|
183
|
+
#
|
|
184
|
+
def self.fetch_all_tags
|
|
185
|
+
Action.sh('git', 'fetch', '--tags')
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Checks if a branch exists locally.
|
|
189
|
+
#
|
|
190
|
+
# @param [String] branch_name The name of the branch to check for
|
|
191
|
+
#
|
|
192
|
+
# @return [Bool] True if the branch exists in the local working copy, false otherwise.
|
|
193
|
+
#
|
|
194
|
+
def self.branch_exists?(branch_name)
|
|
195
|
+
!Action.sh('git', 'branch', '--list', branch_name).empty?
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Ensure that we are on the expected branch, and abort if not.
|
|
199
|
+
#
|
|
200
|
+
# @param [String] branch_name The name of the branch we expect to be on
|
|
201
|
+
#
|
|
202
|
+
# @raise [UserError] Raises a user_error! and interrupts the lane if we are not on the expected branch.
|
|
203
|
+
#
|
|
204
|
+
def self.ensure_on_branch!(branch_name)
|
|
205
|
+
current_branch_name = Action.sh('git', 'symbolic-ref', '-q', 'HEAD')
|
|
206
|
+
UI.user_error!("This command works only on #{branch_name} branch") unless current_branch_name.include?(branch_name)
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# Checks whether a given path is ignored by Git, relying on Git's
|
|
210
|
+
# `check-ignore` under the hood.
|
|
211
|
+
#
|
|
212
|
+
# @param [String] path The path to check against `.gitignore`
|
|
213
|
+
#
|
|
214
|
+
# @return [Bool] True if the given path is ignored or outside a Git repository, false otherwise.
|
|
215
|
+
def self.is_ignored?(path:)
|
|
216
|
+
return true unless is_git_repo?(path: path)
|
|
217
|
+
|
|
218
|
+
Actions.sh('git', 'check-ignore', path) do |status, _, _|
|
|
219
|
+
status.success?
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
end
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
require 'fastlane_core/ui/ui'
|
|
2
|
+
require 'octokit'
|
|
3
|
+
require 'open-uri'
|
|
4
|
+
|
|
5
|
+
module Fastlane
|
|
6
|
+
UI = FastlaneCore::UI unless Fastlane.const_defined?('UI')
|
|
7
|
+
|
|
8
|
+
module Helper
|
|
9
|
+
class GithubHelper
|
|
10
|
+
def self.github_client
|
|
11
|
+
client = Octokit::Client.new(access_token: ENV['GHHELPER_ACCESS'])
|
|
12
|
+
|
|
13
|
+
# Fetch the current user
|
|
14
|
+
user = client.user
|
|
15
|
+
UI.message("Logged in as: #{user.name}")
|
|
16
|
+
|
|
17
|
+
# Auto-paginate to ensure we're not missing data
|
|
18
|
+
client.auto_paginate = true
|
|
19
|
+
|
|
20
|
+
client
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.get_milestone(repository, release)
|
|
24
|
+
miles = github_client().list_milestones(repository)
|
|
25
|
+
mile = nil
|
|
26
|
+
|
|
27
|
+
miles&.each do |mm|
|
|
28
|
+
mile = mm if mm[:title].start_with?(release)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
return mile
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Fetch all the PRs for a given milestone
|
|
35
|
+
#
|
|
36
|
+
# @param [String] repository The repository name, including the organization (e.g. `wordpress-mobile/wordpress-ios`)
|
|
37
|
+
# @param [String] milestone The name of the milestone we want to fetch the list of PRs for (e.g.: `16.9`)
|
|
38
|
+
# @return [<Sawyer::Resource>] A list of the PRs for the given milestone, sorted by number
|
|
39
|
+
#
|
|
40
|
+
def self.get_prs_for_milestone(repository, milestone)
|
|
41
|
+
github_client.search_issues(%(type:pr milestone:"#{milestone}" repo:#{repository}))[:items].sort_by(&:number)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def self.get_last_milestone(repository)
|
|
45
|
+
options = {}
|
|
46
|
+
options[:state] = 'open'
|
|
47
|
+
|
|
48
|
+
milestones = github_client().list_milestones(repository, options)
|
|
49
|
+
return nil if milestones.nil?
|
|
50
|
+
|
|
51
|
+
last_stone = nil
|
|
52
|
+
milestones.each do |mile|
|
|
53
|
+
if last_stone.nil?
|
|
54
|
+
last_stone = mile unless mile[:title].split(' ')[0].split('.').length < 2
|
|
55
|
+
else
|
|
56
|
+
begin
|
|
57
|
+
if mile[:title].split(' ')[0].split('.')[0] > last_stone[:title].split(' ')[0].split('.')[0]
|
|
58
|
+
last_stone = mile
|
|
59
|
+
elsif mile[:title].split(' ')[0].split('.')[1] > last_stone[:title].split(' ')[0].split('.')[1]
|
|
60
|
+
last_stone = mile
|
|
61
|
+
end
|
|
62
|
+
rescue StandardError
|
|
63
|
+
puts 'Found invalid milestone'
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
last_stone
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def self.create_milestone(repository, newmilestone_number, newmilestone_duedate, need_submission)
|
|
72
|
+
submission_date = need_submission ? newmilestone_duedate.to_datetime.next_day(11) : newmilestone_duedate.to_datetime.next_day(14)
|
|
73
|
+
release_date = newmilestone_duedate.to_datetime.next_day(14)
|
|
74
|
+
comment = "Code freeze: #{newmilestone_duedate.to_datetime.strftime('%B %d, %Y')} App Store submission: #{submission_date.strftime('%B %d, %Y')} Release: #{release_date.strftime('%B %d, %Y')}"
|
|
75
|
+
|
|
76
|
+
options = {}
|
|
77
|
+
options[:due_on] = newmilestone_duedate
|
|
78
|
+
options[:description] = comment
|
|
79
|
+
github_client().create_milestone(repository, newmilestone_number, options)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Creates a Release on GitHub as a Draft
|
|
83
|
+
#
|
|
84
|
+
# @param [String] repository The repository to create the GitHub release on. Typically a repo slug (<org>/<repo>).
|
|
85
|
+
# @param [String] version The version for which to create this release. Will be used both as the name of the tag and the name of the release.
|
|
86
|
+
# @param [String?] target The commit SHA or branch name that this release will point to when it's published and creates the tag.
|
|
87
|
+
# If nil (the default), will use the repo's current HEAD commit at the time this method is called.
|
|
88
|
+
# Unused if the tag already exists.
|
|
89
|
+
# @param [String] description The text to use as the release's body / description (typically the release notes)
|
|
90
|
+
# @param [Array<String>] assets List of file paths to attach as assets to the release
|
|
91
|
+
# @param [TrueClass|FalseClass] prerelease Indicates if this should be created as a pre-release (i.e. for alpha/beta)
|
|
92
|
+
#
|
|
93
|
+
def self.create_release(repository:, version:, target: nil, description:, assets:, prerelease:)
|
|
94
|
+
release = github_client().create_release(
|
|
95
|
+
repository,
|
|
96
|
+
version, # tag name
|
|
97
|
+
name: version, # release name
|
|
98
|
+
target_commitish: target || Git.open(Dir.pwd).log.first.sha,
|
|
99
|
+
draft: true,
|
|
100
|
+
prerelease: prerelease,
|
|
101
|
+
body: description
|
|
102
|
+
)
|
|
103
|
+
assets.each do |file_path|
|
|
104
|
+
github_client().upload_asset(release[:url], file_path, content_type: 'application/octet-stream')
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Downloads a file from the given GitHub tag
|
|
109
|
+
#
|
|
110
|
+
# @param [String] repository The repository name (including the organization)
|
|
111
|
+
# @param [String] tag The name of the tag we're downloading from
|
|
112
|
+
# @param [String] file_path The path, inside the project folder, of the file to download
|
|
113
|
+
# @param [String] download_folder The folder which the file should be downloaded into
|
|
114
|
+
# @return [String] The path of the downloaded file, or nil if something went wrong
|
|
115
|
+
#
|
|
116
|
+
def self.download_file_from_tag(repository:, tag:, file_path:, download_folder:)
|
|
117
|
+
repository = repository.delete_prefix('/').chomp('/')
|
|
118
|
+
file_path = file_path.delete_prefix('/').chomp('/')
|
|
119
|
+
file_name = File.basename(file_path)
|
|
120
|
+
download_path = File.join(download_folder, file_name)
|
|
121
|
+
|
|
122
|
+
begin
|
|
123
|
+
uri = URI.parse("https://raw.githubusercontent.com/#{repository}/#{tag}/#{file_path}")
|
|
124
|
+
uri.open do |remote_file|
|
|
125
|
+
File.write(download_path, remote_file.read)
|
|
126
|
+
end
|
|
127
|
+
rescue OpenURI::HTTPError
|
|
128
|
+
return nil
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
download_path
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
require 'spaceship'
|
|
2
|
+
|
|
3
|
+
module Fastlane
|
|
4
|
+
module Helper
|
|
5
|
+
module Ios
|
|
6
|
+
module ADCAppSizesHelper
|
|
7
|
+
DEFAULT_DEVICES = ['Universal', 'iPhone 8', 'iPhone X']
|
|
8
|
+
|
|
9
|
+
# Fetch the App Sizes stats from ADC
|
|
10
|
+
#
|
|
11
|
+
# @return [Array<Hash>] app build details, one entry per app version found
|
|
12
|
+
# Each entry is a hash with keys `cfBundleVersion` and `sizesInBytes`.
|
|
13
|
+
# Value for key `sizeInBytes` is itself a Hash with one entry per device name (including special name "Universal")
|
|
14
|
+
# whose value is a Hash with keys `compressed` and `uncompressed`
|
|
15
|
+
#
|
|
16
|
+
def self.get_adc_sizes(adc_user:, adc_team: 'Automattic, Inc.', bundle_id:, only_version: nil, limit: 10)
|
|
17
|
+
UI.message 'Connecting to ADC...'
|
|
18
|
+
Spaceship::ConnectAPI.login(adc_user, team_name: adc_team)
|
|
19
|
+
app = Spaceship::ConnectAPI::App.find(bundle_id)
|
|
20
|
+
|
|
21
|
+
UI.message 'Fetching the list of versions...'
|
|
22
|
+
versions = app.app_store_versions.select { |v| v.version_string == only_version && !v.build.nil? }
|
|
23
|
+
versions = app.get_app_store_versions.reject { |v| v.build.nil? } if versions.empty?
|
|
24
|
+
UI.message "Found #{versions.count} versions." + (limit == 0 ? '' : " Limiting to last #{limit}")
|
|
25
|
+
versions = versions.first(limit) unless limit == 0
|
|
26
|
+
|
|
27
|
+
UI.message 'Fetching App Sizes...'
|
|
28
|
+
|
|
29
|
+
builds_details = versions.each_with_index.map do |v, idx|
|
|
30
|
+
print "Fetching info for: #{v.version_string.rjust(8)} (#{v.build.version.rjust(11)}) [#{idx.to_s.rjust(3)}/#{versions.count}]\r"
|
|
31
|
+
Spaceship::Tunes.client.build_details(app_id: app.id, train: v.version_string, build_number: v.build.version, platform: 'ios') rescue nil
|
|
32
|
+
end.compact.reverse
|
|
33
|
+
print(' ' * 55 + "\n")
|
|
34
|
+
|
|
35
|
+
builds_details
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.sz(bytes)
|
|
39
|
+
(bytes.to_f / (1024 * 1024)).round(1)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def self.sz_mb(bytes)
|
|
43
|
+
sz(bytes).to_s.rjust(5) + ' MB'
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def self.format_csv(app_sizes, devices: nil)
|
|
47
|
+
devices = DEFAULT_DEVICES if devices.nil? || devices.empty?
|
|
48
|
+
csv = "Version\t" + devices.join("\t") + "\n"
|
|
49
|
+
app_sizes.each do |details|
|
|
50
|
+
build_number = details['cfBundleVersion']
|
|
51
|
+
sizes = details['sizesInBytes'].select { |name, _| devices.include?(name) }
|
|
52
|
+
csv += "#{build_number}\t" + devices.map { |d| sz(sizes[d]['compressed']) }.join("\t") + "\n"
|
|
53
|
+
end
|
|
54
|
+
csv
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def self.format_markdown(app_sizes, devices: nil)
|
|
58
|
+
devices = DEFAULT_DEVICES if devices.nil? || devices.empty?
|
|
59
|
+
app_sizes.map do |details|
|
|
60
|
+
build_number = details['cfBundleVersion']
|
|
61
|
+
sizes = details['sizesInBytes'].select { |name, _| devices.include?(name) }
|
|
62
|
+
col_size = devices.map(&:length).max
|
|
63
|
+
table = "| #{build_number.ljust(col_size)} | Download | Install |\n"
|
|
64
|
+
table += "|:#{'-' * col_size}-|---------:|---------:|\n"
|
|
65
|
+
sizes.each do |(device_name, size_info)|
|
|
66
|
+
table += "| #{device_name.ljust(col_size)} | #{sz_mb(size_info['compressed'])} | #{sz_mb(size_info['uncompressed'])} |\n"
|
|
67
|
+
end
|
|
68
|
+
table
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
module Fastlane
|
|
2
|
+
module Helper
|
|
3
|
+
module Ios
|
|
4
|
+
# Helper methods to execute git-related operations that are specific to iOS projects
|
|
5
|
+
#
|
|
6
|
+
module GitHelper
|
|
7
|
+
# Commit and push the files that are modified when we bump version numbers on an iOS project
|
|
8
|
+
#
|
|
9
|
+
# This typically commits and pushes:
|
|
10
|
+
# - The files in `./config/*` – especially `Version.*.xcconfig` files
|
|
11
|
+
# - The `fastlane/Deliverfile` file (which contains the `app_version` line)
|
|
12
|
+
# - The `<ProjectRoot>/<ProjectName>/Resources/AppStoreStrings.pot` file, containing a key for that version's release notes
|
|
13
|
+
#
|
|
14
|
+
# @env PROJECT_ROOT_FOLDER The path to the git root of the project
|
|
15
|
+
# @env PROJECT_NAME The name of the directory containing the project code (especially containing the Resources/ subfolder)
|
|
16
|
+
#
|
|
17
|
+
# @param [Bool] include_deliverfile If true (the default), includes the `fastlane/Deliverfile` in files being commited
|
|
18
|
+
# @param [Bool] include_metadata If true (the default), includes the `fastlane/download_metadata.swift` file and the `.pot` file (which typically contains an entry or release notes for the new version)
|
|
19
|
+
#
|
|
20
|
+
def self.commit_version_bump(include_deliverfile: true, include_metadata: true)
|
|
21
|
+
files_list = [File.join(ENV['PROJECT_ROOT_FOLDER'], 'config', '.')]
|
|
22
|
+
files_list.append File.join('fastlane', 'Deliverfile') if include_deliverfile
|
|
23
|
+
if include_metadata
|
|
24
|
+
files_list.append File.join('fastlane', 'download_metadata.swift')
|
|
25
|
+
files_list.append File.join(ENV['PROJECT_ROOT_FOLDER'], ENV['PROJECT_NAME'], 'Resources', ENV['APP_STORE_STRINGS_FILE_NAME'])
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
Fastlane::Helper::GitHelper.commit(message: 'Bump version number', files: files_list, push: true)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Calls the `Scripts/localize.py` script in the project root folder and push the `*.strings` files
|
|
32
|
+
#
|
|
33
|
+
# That script updates the `.strings` files with translations from GlotPress.
|
|
34
|
+
#
|
|
35
|
+
# @env PROJECT_ROOT_FOLDER The path to the git root of the project
|
|
36
|
+
# @env PROJECT_NAME The name of the directory containing the project code (especially containing the `build.gradle` file)
|
|
37
|
+
#
|
|
38
|
+
# @todo Migrate the scripts, currently in each host repo and called by this method, to be helpers and actions
|
|
39
|
+
# in the release-toolkit instead, and move this code away from `ios_git_helper`.
|
|
40
|
+
#
|
|
41
|
+
def self.localize_project
|
|
42
|
+
Action.sh("cd #{get_from_env!(key: 'PROJECT_ROOT_FOLDER')} && ./Scripts/localize.py")
|
|
43
|
+
|
|
44
|
+
Fastlane::Helper::GitHelper.commit(message: 'Update strings for localization', files: strings_files, push: true) || UI.message('No new strings, skipping commit')
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Call the `Scripts/update-translations.rb` then the `fastlane/download_metadata` Scripts from the host project folder
|
|
48
|
+
#
|
|
49
|
+
# @env PROJECT_ROOT_FOLDER The path to the git root of the project
|
|
50
|
+
# @env PROJECT_NAME The name of the directory containing the project code (especially containing the `build.gradle` file)
|
|
51
|
+
#
|
|
52
|
+
# @todo Migrate the scripts, currently in each host repo and called by this method, to be helpers and actions
|
|
53
|
+
# in the release-toolkit instead, and move this code away from `ios_git_helper`.
|
|
54
|
+
#
|
|
55
|
+
def self.update_metadata
|
|
56
|
+
Action.sh("cd #{get_from_env!(key: 'PROJECT_ROOT_FOLDER')} && ./Scripts/update-translations.rb")
|
|
57
|
+
|
|
58
|
+
Fastlane::Helper::GitHelper.commit(message: 'Update translations', files: strings_files, push: false)
|
|
59
|
+
|
|
60
|
+
Action.sh('cd fastlane && ./download_metadata.swift')
|
|
61
|
+
|
|
62
|
+
Fastlane::Helper::GitHelper.commit(message: 'Update metadata translations', files: './fastlane/metadata/', push: true)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def self.strings_files
|
|
66
|
+
project_root = get_from_env!(key: 'PROJECT_ROOT_FOLDER')
|
|
67
|
+
project_name = get_from_env!(key: 'PROJECT_NAME')
|
|
68
|
+
|
|
69
|
+
Dir.glob(File.join(project_root, project_name, '**', '*.strings'))
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def self.get_from_env!(key:)
|
|
73
|
+
ENV.fetch(key) do
|
|
74
|
+
UI.user_error! "Could not find value for \"#{key}\" in environment."
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|