fastlane-plugin-wpmreleasetoolkit 5.6.0 → 6.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_download_file_by_version.rb +3 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_firebase_test.rb +31 -4
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/close_milestone_action.rb +5 -2
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/comment_on_pr.rb +3 -7
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/create_new_milestone_action.rb +17 -2
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/create_release_action.rb +3 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/get_prs_list_action.rb +3 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/promo_screenshots_action.rb +14 -9
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/removebranchprotection_action.rb +12 -6
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/setbranchprotection_action.rb +12 -5
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/setfrozentag_action.rb +5 -2
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/upload_to_s3.rb +16 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_lint_localizations.rb +17 -15
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/github_helper.rb +110 -46
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/ios/ios_l10n_helper.rb +24 -4
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/ios/ios_strings_file_validation_helper.rb +14 -11
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/promo_screenshots_helper.rb +58 -10
- data/lib/fastlane/plugin/wpmreleasetoolkit/version.rb +1 -1
- metadata +2 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a5ccc5cff47b793af8f4ab760dae506824b97736743751874e6ffe05da49c6d6
|
4
|
+
data.tar.gz: b505f57985fa018c5845437e38b9d62a82db73252fca4990d30bf2e5b8b6455e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ef7e10048e6a7bd1795667f06e59ff4af01638ea2a3f47bb2742f3ea50bd1d52ae1c8b747e4fc8d2422599f216e9dc03ba334ace07f9a90dfa785630f937c9d7
|
7
|
+
data.tar.gz: 559bee78785335bcc564e04cafc52af9d5514f2d979598ce05c5837ba97b6378c70f0d919809fc6f4db5ca4e5f64db47ac90e430b058d5cd6d777ebf81cc54dc
|
data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_download_file_by_version.rb
CHANGED
@@ -9,7 +9,8 @@ module Fastlane
|
|
9
9
|
UI.user_error!("Can't find any reference for key #{params[:import_key]}") if version.nil?
|
10
10
|
UI.message "Downloading #{params[:file_path]} from #{params[:repository]} at version #{version} to #{params[:download_folder]}"
|
11
11
|
|
12
|
-
Fastlane::Helper::GithubHelper.
|
12
|
+
github_helper = Fastlane::Helper::GithubHelper.new(github_token: params[:github_token])
|
13
|
+
github_helper.download_file_from_tag(
|
13
14
|
repository: params[:repository],
|
14
15
|
tag: "#{params[:github_release_prefix]}#{version}",
|
15
16
|
file_path: params[:file_path],
|
@@ -57,6 +58,7 @@ module Fastlane
|
|
57
58
|
description: 'The prefix which is used in the GitHub release title',
|
58
59
|
type: String,
|
59
60
|
optional: true),
|
61
|
+
Fastlane::Helper::GithubHelper.github_token_config_item,
|
60
62
|
]
|
61
63
|
end
|
62
64
|
|
@@ -3,8 +3,9 @@ require 'securerandom'
|
|
3
3
|
module Fastlane
|
4
4
|
module Actions
|
5
5
|
module SharedValues
|
6
|
-
FIREBASE_TEST_RESULT = :FIREBASE_TEST_LOG_FILE
|
6
|
+
FIREBASE_TEST_RESULT = :FIREBASE_TEST_LOG_FILE # FirebaseTestLabResult object, for internal consumption
|
7
7
|
FIREBASE_TEST_LOG_FILE_PATH = :FIREBASE_TEST_LOG_FILE_PATH
|
8
|
+
FIREBASE_TEST_MORE_DETAILS_URL = :FIREBASE_TEST_MORE_DETAILS_URL
|
8
9
|
end
|
9
10
|
|
10
11
|
class AndroidFirebaseTestAction < Action
|
@@ -45,13 +46,20 @@ module Fastlane
|
|
45
46
|
key_file_path: params[:key_file]
|
46
47
|
)
|
47
48
|
|
48
|
-
|
49
|
+
Fastlane::Actions.lane_context[SharedValues::FIREBASE_TEST_MORE_DETAILS_URL] = result.more_details_url
|
49
50
|
|
50
|
-
|
51
|
+
if result.success?
|
52
|
+
UI.success 'Firebase Tests Complete'
|
53
|
+
return true
|
54
|
+
else
|
55
|
+
ui_method = params[:crash_on_test_failure] ? :test_failure! : :error
|
56
|
+
FastlaneCore::UI.send(ui_method, "Firebase Tests failed – more information can be found at #{result.more_details_url}")
|
57
|
+
return false
|
58
|
+
end
|
51
59
|
end
|
52
60
|
|
53
61
|
# Fastlane doesn't eagerly validate options for us, so we'll do it first to have control over
|
54
|
-
# when they're
|
62
|
+
# when they're evaluated.
|
55
63
|
def self.validate_options(params)
|
56
64
|
available_options
|
57
65
|
.reject { |opt| opt.optional || !opt.default_value.nil? }
|
@@ -180,9 +188,28 @@ module Fastlane
|
|
180
188
|
optional: true,
|
181
189
|
type: String
|
182
190
|
),
|
191
|
+
FastlaneCore::ConfigItem.new(
|
192
|
+
key: :crash_on_test_failure,
|
193
|
+
description: 'If set to `true` (the default), will stop fastlane with `test_failure!`. ' \
|
194
|
+
+ 'If `false`, the action will return the test status, without interrupting the rest of your Fastlane run on failure, letting the caller handle the failure on their side',
|
195
|
+
optional: true,
|
196
|
+
type: Boolean,
|
197
|
+
default_value: true
|
198
|
+
),
|
183
199
|
]
|
184
200
|
end
|
185
201
|
|
202
|
+
def self.output
|
203
|
+
[
|
204
|
+
['FIREBASE_TEST_LOG_FILE_PATH', 'Path to the `output.log` file containing the logs or invoking the tests'],
|
205
|
+
['FIREBASE_TEST_MORE_DETAILS_URL', 'URL to the Firebase Console dashboard showing the details of the test run (and failures, if any)'],
|
206
|
+
]
|
207
|
+
end
|
208
|
+
|
209
|
+
def self.return_value
|
210
|
+
'True if the test succeeded, false if they failed'
|
211
|
+
end
|
212
|
+
|
186
213
|
def self.authors
|
187
214
|
['Automattic']
|
188
215
|
end
|
@@ -10,10 +10,12 @@ module Fastlane
|
|
10
10
|
repository = params[:repository]
|
11
11
|
milestone_title = params[:milestone]
|
12
12
|
|
13
|
-
|
13
|
+
github_helper = Fastlane::Helper::GithubHelper.new(github_token: params[:github_token])
|
14
|
+
milestone = github_helper.get_milestone(repository, milestone_title)
|
15
|
+
|
14
16
|
UI.user_error!("Milestone #{milestone_title} not found.") if milestone.nil?
|
15
17
|
|
16
|
-
|
18
|
+
github_helper.update_milestone(repository: repository, number: milestone[:number], state: 'closed')
|
17
19
|
end
|
18
20
|
|
19
21
|
def self.description
|
@@ -45,6 +47,7 @@ module Fastlane
|
|
45
47
|
description: 'The GitHub milestone',
|
46
48
|
optional: false,
|
47
49
|
type: String),
|
50
|
+
Fastlane::Helper::GithubHelper.github_token_config_item,
|
48
51
|
]
|
49
52
|
end
|
50
53
|
|
@@ -10,7 +10,8 @@ module Fastlane
|
|
10
10
|
def self.run(params)
|
11
11
|
require_relative '../../helper/github_helper'
|
12
12
|
|
13
|
-
|
13
|
+
github_helper = Fastlane::Helper::GithubHelper.new(github_token: params[:github_token])
|
14
|
+
reuse_identifier = github_helper.comment_on_pr(
|
14
15
|
project_slug: params[:project],
|
15
16
|
pr_number: params[:pr_number],
|
16
17
|
body: params[:body],
|
@@ -41,12 +42,7 @@ module Fastlane
|
|
41
42
|
|
42
43
|
def self.available_options
|
43
44
|
[
|
44
|
-
|
45
|
-
key: :access_token,
|
46
|
-
env_name: 'GITHUB_TOKEN',
|
47
|
-
description: 'The GitHub token to use for posting the comment',
|
48
|
-
type: String
|
49
|
-
),
|
45
|
+
Fastlane::Helper::GithubHelper.github_token_config_item,
|
50
46
|
FastlaneCore::ConfigItem.new(
|
51
47
|
key: :reuse_identifier,
|
52
48
|
description: 'If provided, the reuse identifier can identify an existing comment to overwrite',
|
@@ -9,15 +9,29 @@ module Fastlane
|
|
9
9
|
def self.run(params)
|
10
10
|
repository = params[:repository]
|
11
11
|
|
12
|
-
|
12
|
+
github_helper = Fastlane::Helper::GithubHelper.new(github_token: params[:github_token])
|
13
|
+
last_stone = github_helper.get_last_milestone(repository)
|
14
|
+
|
13
15
|
UI.message("Last detected milestone: #{last_stone[:title]} due on #{last_stone[:due_on]}.")
|
16
|
+
|
14
17
|
milestone_duedate = last_stone[:due_on]
|
15
18
|
milestone_duration = params[:milestone_duration]
|
16
19
|
newmilestone_duedate = (milestone_duedate.to_datetime.next_day(milestone_duration).to_time).utc
|
17
20
|
newmilestone_number = Fastlane::Helper::Ios::VersionHelper.calc_next_release_version(last_stone[:title])
|
18
21
|
number_of_days_from_code_freeze_to_release = params[:number_of_days_from_code_freeze_to_release]
|
22
|
+
# Because of the app stores review process, we submit the binary 3 days before the intended release date.
|
23
|
+
# Using 3 days is mostly for historical reasons, for a long time, we've been submitting apps on Friday and releasing them on Monday.
|
24
|
+
days_until_submission = params[:need_appstore_submission] ? (number_of_days_from_code_freeze_to_release - 3) : milestone_duration
|
25
|
+
|
19
26
|
UI.message("Next milestone: #{newmilestone_number} due on #{newmilestone_duedate}.")
|
20
|
-
|
27
|
+
|
28
|
+
github_helper.create_milestone(
|
29
|
+
repository: repository,
|
30
|
+
title: newmilestone_number,
|
31
|
+
due_date: newmilestone_duedate,
|
32
|
+
days_until_submission: days_until_submission,
|
33
|
+
days_until_release: number_of_days_from_code_freeze_to_release
|
34
|
+
)
|
21
35
|
end
|
22
36
|
|
23
37
|
def self.description
|
@@ -62,6 +76,7 @@ module Fastlane
|
|
62
76
|
optional: true,
|
63
77
|
is_string: false,
|
64
78
|
default_value: 14),
|
79
|
+
Fastlane::Helper::GithubHelper.github_token_config_item,
|
65
80
|
]
|
66
81
|
end
|
67
82
|
|
@@ -21,7 +21,8 @@ module Fastlane
|
|
21
21
|
UI.user_error!("Can't find file #{file_path}!") unless File.exist?(file_path)
|
22
22
|
end
|
23
23
|
|
24
|
-
Fastlane::Helper::GithubHelper.
|
24
|
+
github_helper = Fastlane::Helper::GithubHelper.new(github_token: params[:github_token])
|
25
|
+
github_helper.create_release(
|
25
26
|
repository: repository,
|
26
27
|
version: version,
|
27
28
|
target: params[:target],
|
@@ -82,6 +83,7 @@ module Fastlane
|
|
82
83
|
optional: true,
|
83
84
|
default_value: false,
|
84
85
|
is_string: false),
|
86
|
+
Fastlane::Helper::GithubHelper.github_token_config_item,
|
85
87
|
]
|
86
88
|
end
|
87
89
|
|
@@ -10,7 +10,8 @@ module Fastlane
|
|
10
10
|
milestone = params[:milestone]
|
11
11
|
|
12
12
|
# Get commit list
|
13
|
-
|
13
|
+
github_helper = Fastlane::Helper::GithubHelper.new(github_token: params[:github_token])
|
14
|
+
pr_list = github_helper.get_prs_for_milestone(repository, milestone)
|
14
15
|
|
15
16
|
File.open(report_path, 'w') do |file|
|
16
17
|
pr_list.each do |data|
|
@@ -53,6 +54,7 @@ module Fastlane
|
|
53
54
|
description: 'The name of the milestone we want to fetch the list of PRs for (e.g.: `16.9`)',
|
54
55
|
optional: false,
|
55
56
|
is_string: true),
|
57
|
+
Fastlane::Helper::GithubHelper.github_token_config_item,
|
56
58
|
]
|
57
59
|
end
|
58
60
|
|
@@ -11,7 +11,9 @@ module Fastlane
|
|
11
11
|
UI.message "#{self.check_path(params[:orig_folder])} Original Screenshot Source: #{params[:orig_folder]}"
|
12
12
|
UI.message "#{self.check_path(params[:metadata_folder])} Translation source: #{params[:metadata_folder]}"
|
13
13
|
|
14
|
-
config = helper.
|
14
|
+
config = helper.read_config(params[:config_file])
|
15
|
+
|
16
|
+
helper.check_fonts_installed!(config: config) unless params[:skip_font_check]
|
15
17
|
|
16
18
|
translationDirectories = subdirectories_for_path(params[:metadata_folder])
|
17
19
|
imageDirectories = subdirectories_for_path(params[:orig_folder])
|
@@ -141,31 +143,34 @@ module Fastlane
|
|
141
143
|
env_name: 'PROMOSS_ORIG',
|
142
144
|
description: 'The directory containing the original screenshots',
|
143
145
|
optional: false,
|
144
|
-
|
146
|
+
type: String),
|
145
147
|
FastlaneCore::ConfigItem.new(key: :output_folder,
|
146
148
|
env_name: 'PROMOSS_OUTPUT',
|
147
149
|
description: 'The path of the folder to save the promo screenshots',
|
148
150
|
optional: false,
|
149
|
-
|
150
|
-
|
151
|
+
type: String),
|
151
152
|
FastlaneCore::ConfigItem.new(key: :metadata_folder,
|
152
153
|
env_name: 'PROMOSS_METADATA_FOLDER',
|
153
154
|
description: 'The directory containing the translation data',
|
154
155
|
optional: false,
|
155
|
-
|
156
|
-
|
156
|
+
type: String),
|
157
157
|
FastlaneCore::ConfigItem.new(key: :config_file,
|
158
158
|
env_name: 'PROMOSS_CONFIG_FILE',
|
159
159
|
description: 'The path to the file containing the promo screenshot configuration',
|
160
160
|
optional: true,
|
161
|
-
|
161
|
+
type: String,
|
162
162
|
default_value: 'screenshots.json'),
|
163
|
-
|
164
163
|
FastlaneCore::ConfigItem.new(key: :force,
|
165
164
|
env_name: 'PROMOSS_FORCE_CREATION',
|
166
165
|
description: 'Overwrite existing promo screenshots without asking first?',
|
167
166
|
optional: true,
|
168
|
-
|
167
|
+
type: Boolean,
|
168
|
+
default_value: false),
|
169
|
+
FastlaneCore::ConfigItem.new(key: :skip_font_check,
|
170
|
+
env_name: 'PROMOSS_SKIP_FONT_CHECK',
|
171
|
+
description: 'Skip the check verifying that needed fonts are installed and active',
|
172
|
+
optional: true,
|
173
|
+
type: Boolean,
|
169
174
|
default_value: false),
|
170
175
|
]
|
171
176
|
end
|
@@ -7,14 +7,19 @@ module Fastlane
|
|
7
7
|
def self.run(params)
|
8
8
|
repository = params[:repository]
|
9
9
|
branch_name = params[:branch]
|
10
|
-
branch_prot = {}
|
11
10
|
|
12
11
|
branch_url = "https://api.github.com/repos/#{repository}/branches/#{branch_name}"
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
12
|
+
restrictions = { url: "#{branch_url}/protection/restrictions", users_url: "#{branch_url}/protection/restrictions/users", teams_url: "#{branch_url}/protection/restrictions/teams", users: [], teams: [] }
|
13
|
+
required_pull_request_reviews = { url: "#{branch_url}/protection/required_pull_request_reviews", dismiss_stale_reviews: false, require_code_owner_reviews: false }
|
14
|
+
|
15
|
+
github_helper = Fastlane::Helper::GithubHelper.new(github_token: params[:github_token])
|
16
|
+
github_helper.remove_branch_protection(
|
17
|
+
repository: repository,
|
18
|
+
branch: branch_name,
|
19
|
+
restrictions: restrictions,
|
20
|
+
enforce_admins: nil,
|
21
|
+
required_pull_request_reviews: required_pull_request_reviews
|
22
|
+
)
|
18
23
|
end
|
19
24
|
|
20
25
|
def self.description
|
@@ -46,6 +51,7 @@ module Fastlane
|
|
46
51
|
description: 'The branch to unprotect',
|
47
52
|
optional: false,
|
48
53
|
type: String),
|
54
|
+
Fastlane::Helper::GithubHelper.github_token_config_item,
|
49
55
|
]
|
50
56
|
end
|
51
57
|
|
@@ -7,13 +7,19 @@ module Fastlane
|
|
7
7
|
def self.run(params)
|
8
8
|
repository = params[:repository]
|
9
9
|
branch_name = params[:branch]
|
10
|
-
branch_prot = {}
|
11
10
|
|
12
11
|
branch_url = "https://api.github.com/repos/#{repository}/branches/#{branch_name}"
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
Fastlane::Helper::GithubHelper.
|
12
|
+
restrictions = { url: "#{branch_url}/protection/restrictions", users_url: "#{branch_url}/protection/restrictions/users", teams_url: "#{branch_url}/protection/restrictions/teams", users: [], teams: [] }
|
13
|
+
required_pull_request_reviews = { url: "#{branch_url}/protection/required_pull_request_reviews", dismiss_stale_reviews: false, require_code_owner_reviews: false }
|
14
|
+
|
15
|
+
github_helper = Fastlane::Helper::GithubHelper.new(github_token: params[:github_token])
|
16
|
+
github_helper.set_branch_protection(
|
17
|
+
repository: repository,
|
18
|
+
branch: branch_name,
|
19
|
+
restrictions: restrictions,
|
20
|
+
enforce_admins: nil,
|
21
|
+
required_pull_request_reviews: required_pull_request_reviews
|
22
|
+
)
|
17
23
|
end
|
18
24
|
|
19
25
|
def self.description
|
@@ -45,6 +51,7 @@ module Fastlane
|
|
45
51
|
description: 'The branch to protect',
|
46
52
|
optional: false,
|
47
53
|
type: String),
|
54
|
+
Fastlane::Helper::GithubHelper.github_token_config_item,
|
48
55
|
]
|
49
56
|
end
|
50
57
|
|
@@ -9,7 +9,9 @@ module Fastlane
|
|
9
9
|
milestone_title = params[:milestone]
|
10
10
|
freeze = params[:freeze]
|
11
11
|
|
12
|
-
|
12
|
+
github_helper = Fastlane::Helper::GithubHelper.new(github_token: params[:github_token])
|
13
|
+
milestone = github_helper.get_milestone(repository, milestone_title)
|
14
|
+
|
13
15
|
UI.user_error!("Milestone #{milestone_title} not found.") if milestone.nil?
|
14
16
|
|
15
17
|
mile_title = milestone[:title]
|
@@ -27,7 +29,7 @@ module Fastlane
|
|
27
29
|
end
|
28
30
|
|
29
31
|
UI.message("New milestone: #{mile_title}")
|
30
|
-
|
32
|
+
github_helper.update_milestone(repository: repository, number: milestone[:number], title: mile_title)
|
31
33
|
end
|
32
34
|
|
33
35
|
def self.is_frozen(milestone)
|
@@ -70,6 +72,7 @@ module Fastlane
|
|
70
72
|
optional: false,
|
71
73
|
default_value: true,
|
72
74
|
is_string: false),
|
75
|
+
Fastlane::Helper::GithubHelper.github_token_config_item,
|
73
76
|
]
|
74
77
|
end
|
75
78
|
|
@@ -20,7 +20,15 @@ module Fastlane
|
|
20
20
|
key = [file_name_hash, key].join('/')
|
21
21
|
end
|
22
22
|
|
23
|
-
|
23
|
+
if file_is_already_uploaded?(bucket, key)
|
24
|
+
message = "File already exists in S3 bucket #{bucket} at #{key}"
|
25
|
+
if params[:skip_if_exists]
|
26
|
+
UI.important("#{message}. Skipping upload.")
|
27
|
+
return key
|
28
|
+
else
|
29
|
+
UI.user_error!(message)
|
30
|
+
end
|
31
|
+
end
|
24
32
|
|
25
33
|
UI.message("Uploading #{file_path} to: #{key}")
|
26
34
|
|
@@ -101,6 +109,13 @@ module Fastlane
|
|
101
109
|
default_value: true,
|
102
110
|
type: Boolean
|
103
111
|
),
|
112
|
+
FastlaneCore::ConfigItem.new(
|
113
|
+
key: :skip_if_exists,
|
114
|
+
description: 'If the file already exists in the S3 bucket, skip the upload (and report it in the logs), instead of failing with `user_error!`',
|
115
|
+
optional: true,
|
116
|
+
default_value: false,
|
117
|
+
type: Boolean
|
118
|
+
),
|
104
119
|
]
|
105
120
|
end
|
106
121
|
|
@@ -2,12 +2,11 @@ module Fastlane
|
|
2
2
|
module Actions
|
3
3
|
class IosLintLocalizationsAction < Action
|
4
4
|
def self.run(params)
|
5
|
-
violations =
|
5
|
+
violations = nil
|
6
6
|
|
7
7
|
loop do
|
8
|
-
|
9
|
-
#
|
10
|
-
violations = violations.merge(self.run_linter(params))
|
8
|
+
violations = self.run_linter(params)
|
9
|
+
violations.default = [] # Set the default value for when querying a missing key
|
11
10
|
|
12
11
|
if params[:check_duplicate_keys]
|
13
12
|
find_duplicated_keys(params).each do |language, duplicates|
|
@@ -49,18 +48,21 @@ module Fastlane
|
|
49
48
|
def self.find_duplicated_keys(params)
|
50
49
|
duplicate_keys = {}
|
51
50
|
|
52
|
-
files_to_lint = Dir.
|
53
|
-
Dir.glob('*.lproj/Localizable.strings').map do |file|
|
54
|
-
{
|
55
|
-
language: File.basename(File.dirname(file), '.lproj'),
|
56
|
-
path: File.join(params[:input_dir], file)
|
57
|
-
}
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
51
|
+
files_to_lint = Dir.glob('*.lproj/Localizable.strings', base: params[:input_dir])
|
61
52
|
files_to_lint.each do |file|
|
62
|
-
|
63
|
-
|
53
|
+
language = File.basename(File.dirname(file), '.lproj')
|
54
|
+
path = File.join(params[:input_dir], file)
|
55
|
+
|
56
|
+
file_type = Fastlane::Helper::Ios::L10nHelper.strings_file_type(path: path)
|
57
|
+
if file_type == :text
|
58
|
+
duplicates = Fastlane::Helper::Ios::StringsFileValidationHelper.find_duplicated_keys(file: path)
|
59
|
+
duplicate_keys[language] = duplicates.map { |key, value| "`#{key}` was found at multiple lines: #{value.join(', ')}" } unless duplicates.empty?
|
60
|
+
else
|
61
|
+
UI.important <<~WRONG_FORMAT
|
62
|
+
File `#{path}` is in #{file_type} format, while finding duplicate keys only make sense on files that are in ASCII-plist format.
|
63
|
+
Since your files are in #{file_type} format, you should probably disable the `check_duplicate_keys` option from this `#{self.action_name}` call.
|
64
|
+
WRONG_FORMAT
|
65
|
+
end
|
64
66
|
end
|
65
67
|
|
66
68
|
duplicate_keys
|
@@ -8,34 +8,25 @@ module Fastlane
|
|
8
8
|
|
9
9
|
module Helper
|
10
10
|
class GithubHelper
|
11
|
-
|
12
|
-
token = [
|
13
|
-
'GHHELPER_ACCESS', # For historical reasons / backward compatibility
|
14
|
-
'GITHUB_TOKEN', # Used by the `gh` CLI tool
|
15
|
-
].map { |key| ENV[key] }
|
16
|
-
.compact
|
17
|
-
.first
|
18
|
-
|
19
|
-
token || UI.user_error!('Please provide a GitHub authentication token via the `GITHUB_TOKEN` environment variable')
|
20
|
-
end
|
21
|
-
|
22
|
-
def self.github_client
|
23
|
-
@@client ||= begin
|
24
|
-
client = Octokit::Client.new(access_token: github_token!)
|
11
|
+
attr_reader :client
|
25
12
|
|
26
|
-
|
27
|
-
|
28
|
-
|
13
|
+
# Helper for GitHub Actions
|
14
|
+
#
|
15
|
+
# @param [String?] github_token GitHub OAuth access token
|
16
|
+
#
|
17
|
+
def initialize(github_token:)
|
18
|
+
@client = Octokit::Client.new(access_token: github_token)
|
29
19
|
|
30
|
-
|
31
|
-
|
20
|
+
# Fetch the current user
|
21
|
+
user = @client.user
|
22
|
+
UI.message("Logged in as: #{user.name}")
|
32
23
|
|
33
|
-
|
34
|
-
|
24
|
+
# Auto-paginate to ensure we're not missing data
|
25
|
+
@client.auto_paginate = true
|
35
26
|
end
|
36
27
|
|
37
|
-
def
|
38
|
-
miles =
|
28
|
+
def get_milestone(repository, release)
|
29
|
+
miles = client.list_milestones(repository)
|
39
30
|
mile = nil
|
40
31
|
|
41
32
|
miles&.each do |mm|
|
@@ -51,15 +42,15 @@ module Fastlane
|
|
51
42
|
# @param [String] milestone The name of the milestone we want to fetch the list of PRs for (e.g.: `16.9`)
|
52
43
|
# @return [<Sawyer::Resource>] A list of the PRs for the given milestone, sorted by number
|
53
44
|
#
|
54
|
-
def
|
55
|
-
|
45
|
+
def get_prs_for_milestone(repository, milestone)
|
46
|
+
client.search_issues(%(type:pr milestone:"#{milestone}" repo:#{repository}))[:items].sort_by(&:number)
|
56
47
|
end
|
57
48
|
|
58
|
-
def
|
49
|
+
def get_last_milestone(repository)
|
59
50
|
options = {}
|
60
51
|
options[:state] = 'open'
|
61
52
|
|
62
|
-
milestones =
|
53
|
+
milestones = client.list_milestones(repository, options)
|
63
54
|
return nil if milestones.nil?
|
64
55
|
|
65
56
|
last_stone = nil
|
@@ -80,19 +71,42 @@ module Fastlane
|
|
80
71
|
last_stone
|
81
72
|
end
|
82
73
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
74
|
+
# Creates a new milestone
|
75
|
+
#
|
76
|
+
# @param [String] repository The repository name, including the organization (e.g. `wordpress-mobile/wordpress-ios`)
|
77
|
+
# @param [String] title The name of the milestone we want to create (e.g.: `16.9`)
|
78
|
+
# @param [Time] due_date Milestone due date—which will also correspond to the code freeze date
|
79
|
+
# @param [Integer] days_until_submission Number of days from code freeze to submission to the App Store / Play Store
|
80
|
+
# @param [Integer] days_until_release Number of days from code freeze to release
|
81
|
+
#
|
82
|
+
def create_milestone(repository:, title:, due_date:, days_until_submission:, days_until_release:)
|
83
|
+
UI.user_error!('days_until_release must be greater than zero.') unless days_until_release.positive?
|
84
|
+
UI.user_error!('days_until_submission must be greater than zero.') unless days_until_submission.positive?
|
85
|
+
UI.user_error!('days_until_release must be greater or equal to days_until_submission.') unless days_until_release >= days_until_submission
|
86
|
+
|
87
|
+
submission_date = due_date.to_datetime.next_day(days_until_submission)
|
88
|
+
release_date = due_date.to_datetime.next_day(days_until_release)
|
89
|
+
comment = <<~MILESTONE_DESCRIPTION
|
90
|
+
Code freeze: #{due_date.to_datetime.strftime('%B %d, %Y')}
|
91
|
+
App Store submission: #{submission_date.strftime('%B %d, %Y')}
|
92
|
+
Release: #{release_date.strftime('%B %d, %Y')}
|
93
|
+
MILESTONE_DESCRIPTION
|
91
94
|
|
92
95
|
options = {}
|
93
|
-
|
96
|
+
# == Workaround for GitHub API bug ==
|
97
|
+
#
|
98
|
+
# It seems that whatever date we send to the API, GitHub will 'floor' it to the date that seems to be at
|
99
|
+
# 00:00 PST/PDT and then discard the time component of the date we sent.
|
100
|
+
# This means that, when we cross the November DST change date, where the due date of the previous milestone
|
101
|
+
# was e.g. `2022-10-31T07:00:00Z` and `.next_day(14)` returns `2022-11-14T07:00:00Z` and we send that value
|
102
|
+
# for the `due_on` field via the API, GitHub ends up creating a milestone with a due of `2022-11-13T08:00:00Z`
|
103
|
+
# instead, introducing an off-by-one error on that due date.
|
104
|
+
#
|
105
|
+
# This is a bug in the GitHub API, not in our date computation logic.
|
106
|
+
# To solve this, we trick it by forcing the time component of the ISO date we send to be `12:00:00Z`.
|
107
|
+
options[:due_on] = due_date.strftime('%Y-%m-%dT12:00:00Z')
|
94
108
|
options[:description] = comment
|
95
|
-
|
109
|
+
client.create_milestone(repository, title, options)
|
96
110
|
end
|
97
111
|
|
98
112
|
# Creates a Release on GitHub as a Draft
|
@@ -106,8 +120,8 @@ module Fastlane
|
|
106
120
|
# @param [Array<String>] assets List of file paths to attach as assets to the release
|
107
121
|
# @param [TrueClass|FalseClass] prerelease Indicates if this should be created as a pre-release (i.e. for alpha/beta)
|
108
122
|
#
|
109
|
-
def
|
110
|
-
release =
|
123
|
+
def create_release(repository:, version:, target: nil, description:, assets:, prerelease:)
|
124
|
+
release = client.create_release(
|
111
125
|
repository,
|
112
126
|
version, # tag name
|
113
127
|
name: version, # release name
|
@@ -117,7 +131,7 @@ module Fastlane
|
|
117
131
|
body: description
|
118
132
|
)
|
119
133
|
assets.each do |file_path|
|
120
|
-
|
134
|
+
client.upload_asset(release[:url], file_path, content_type: 'application/octet-stream')
|
121
135
|
end
|
122
136
|
end
|
123
137
|
|
@@ -129,15 +143,14 @@ module Fastlane
|
|
129
143
|
# @param [String] download_folder The folder which the file should be downloaded into
|
130
144
|
# @return [String] The path of the downloaded file, or nil if something went wrong
|
131
145
|
#
|
132
|
-
def
|
146
|
+
def download_file_from_tag(repository:, tag:, file_path:, download_folder:)
|
133
147
|
repository = repository.delete_prefix('/').chomp('/')
|
134
148
|
file_path = file_path.delete_prefix('/').chomp('/')
|
135
149
|
file_name = File.basename(file_path)
|
136
150
|
download_path = File.join(download_folder, file_name)
|
137
151
|
|
138
|
-
download_url =
|
139
|
-
|
140
|
-
ref: tag).download_url
|
152
|
+
download_url = client.contents(repository, path: file_path, ref: tag).download_url
|
153
|
+
|
141
154
|
begin
|
142
155
|
uri = URI.parse(download_url)
|
143
156
|
uri.open do |remote_file|
|
@@ -151,8 +164,7 @@ module Fastlane
|
|
151
164
|
end
|
152
165
|
|
153
166
|
# Creates (or updates an existing) GitHub PR Comment
|
154
|
-
def
|
155
|
-
client = github_client
|
167
|
+
def comment_on_pr(project_slug:, pr_number:, body:, reuse_identifier: SecureRandom.uuid)
|
156
168
|
comments = client.issue_comments(project_slug, pr_number)
|
157
169
|
|
158
170
|
reuse_marker = "<!-- REUSE_ID: #{reuse_identifier} -->"
|
@@ -172,6 +184,58 @@ module Fastlane
|
|
172
184
|
|
173
185
|
reuse_identifier
|
174
186
|
end
|
187
|
+
|
188
|
+
# Update a milestone for a repository
|
189
|
+
#
|
190
|
+
# @param [String] repository The repository name (including the organization)
|
191
|
+
# @param [String] number The number of the milestone we want to fetch
|
192
|
+
# @param options [Hash] A customizable set of options.
|
193
|
+
# @option options [String] :title A unique title.
|
194
|
+
# @option options [String] :state
|
195
|
+
# @option options [String] :description A meaningful description
|
196
|
+
# @option options [Time] :due_on Set if the milestone has a due date
|
197
|
+
# @return [Milestone] A single milestone object
|
198
|
+
# @see http://developer.github.com/v3/issues/milestones/#update-a-milestone
|
199
|
+
#
|
200
|
+
def update_milestone(repository:, number:, **options)
|
201
|
+
client.update_milestone(repository, number, options)
|
202
|
+
end
|
203
|
+
|
204
|
+
# Remove the protection of a single branch from a repository
|
205
|
+
#
|
206
|
+
# @param [String] repository The repository name (including the organization)
|
207
|
+
# @param [String] branch The branch name
|
208
|
+
# @param [Hash] options A customizable set of options.
|
209
|
+
# @see https://docs.github.com/en/rest/branches/branch-protection#update-branch-protection
|
210
|
+
#
|
211
|
+
def remove_branch_protection(repository:, branch:, **options)
|
212
|
+
client.unprotect_branch(repository, branch, options)
|
213
|
+
end
|
214
|
+
|
215
|
+
# Protects a single branch from a repository
|
216
|
+
#
|
217
|
+
# @param [String] repository The repository name (including the organization)
|
218
|
+
# @param [String] branch The branch name
|
219
|
+
# @param options [Hash] A customizable set of options.
|
220
|
+
# @see https://docs.github.com/en/rest/branches/branch-protection#update-branch-protection
|
221
|
+
#
|
222
|
+
def set_branch_protection(repository:, branch:, **options)
|
223
|
+
client.protect_branch(repository, branch, options)
|
224
|
+
end
|
225
|
+
|
226
|
+
# Creates a GithubToken Fastlane ConfigItem
|
227
|
+
#
|
228
|
+
# @return [FastlaneCore::ConfigItem] The Fastlane ConfigItem for GitHub OAuth access token
|
229
|
+
#
|
230
|
+
def self.github_token_config_item
|
231
|
+
FastlaneCore::ConfigItem.new(
|
232
|
+
key: :github_token,
|
233
|
+
env_name: 'GITHUB_TOKEN',
|
234
|
+
description: 'The GitHub OAuth access token',
|
235
|
+
optional: false,
|
236
|
+
type: String
|
237
|
+
)
|
238
|
+
end
|
175
239
|
end
|
176
240
|
end
|
177
241
|
end
|
@@ -36,6 +36,28 @@ module Fastlane
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
+
# Read a file line by line and iterate over it (just like `File.readlines` does),
|
40
|
+
# except that it also detects the encoding used by the file (using the BOM if present) when reading it,
|
41
|
+
# and then convert each line to UTF-8 before yielding it
|
42
|
+
#
|
43
|
+
# This is particularly useful if you need to then use a `RegExp` to match part of the lines you're iterating over,
|
44
|
+
# as the `RegExp` (which will typically be UTF-8) and the string you're matching with it have to use the same encoding
|
45
|
+
# (otherwise we would get a `Encoding::CompatibilityError`)
|
46
|
+
#
|
47
|
+
# @important If you are then using a `RegExp` to match the UTF-8 lines you iterate on,
|
48
|
+
# remember to use the `u` flag on it (`/…/u`) to make it UTF-8-aware too.
|
49
|
+
#
|
50
|
+
# @param [String] file The path to the file to read
|
51
|
+
# @yield each line read from the file, after converting it to the UTF-8 encoding
|
52
|
+
#
|
53
|
+
def self.read_utf8_lines(file)
|
54
|
+
# Be sure to guess file encoding using the Byte-Order-Mark, and fallback to UTF-8 if there's no BOM.
|
55
|
+
File.readlines(file, mode: 'rb:BOM|UTF-8').map do |line|
|
56
|
+
# Ensure the line is re-encoded to UTF-8 regardless of the encoding that was used in the input file
|
57
|
+
line.encode(Encoding::UTF_8)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
39
61
|
# Merge the content of multiple `.strings` files into a new `.strings` text file.
|
40
62
|
#
|
41
63
|
# @param [Hash<String, String>] paths The paths of the `.strings` files to merge together, associated with the prefix to prepend to each of their respective keys
|
@@ -68,11 +90,9 @@ module Fastlane
|
|
68
90
|
all_keys_found += string_keys
|
69
91
|
|
70
92
|
tmp_file.write("/* MARK: - #{File.basename(input_file)} */\n\n")
|
71
|
-
# Read line-by-line to reduce memory footprint during content copy
|
72
|
-
|
93
|
+
# Read line-by-line to reduce memory footprint during content copy
|
94
|
+
read_utf8_lines(input_file).each do |line|
|
73
95
|
unless prefix.nil? || prefix.empty?
|
74
|
-
# We need to ensure the line and RegExp are using the same encoding, so we transcode everything to UTF-8.
|
75
|
-
line.encode!(Encoding::UTF_8)
|
76
96
|
# The `/u` modifier on the RegExps is to make them UTF-8
|
77
97
|
line.gsub!(/^(\s*")/u, "\\1#{prefix}") # Lines starting with a quote are considered to be start of a key; add prefix right after the quote
|
78
98
|
line.gsub!(/^(\s*)([A-Z0-9_]+)(\s*=\s*")/ui, "\\1\"#{prefix}\\2\"\\3") # Lines starting with an identifier followed by a '=' are considered to be an unquoted key (typical in InfoPlist.strings files for example)
|
@@ -11,25 +11,25 @@ module Fastlane
|
|
11
11
|
|
12
12
|
TRANSITIONS = {
|
13
13
|
root: {
|
14
|
-
/\s/ => :root,
|
14
|
+
/\s/u => :root,
|
15
15
|
'/' => :maybe_comment_start,
|
16
16
|
'"' => :in_quoted_key
|
17
17
|
},
|
18
18
|
maybe_comment_start: {
|
19
19
|
'/' => :in_line_comment,
|
20
|
-
/\*/ => :in_block_comment
|
20
|
+
/\*/u => :in_block_comment
|
21
21
|
},
|
22
22
|
in_line_comment: {
|
23
23
|
"\n" => :root,
|
24
|
-
/./ => :in_line_comment
|
24
|
+
/./u => :in_line_comment
|
25
25
|
},
|
26
26
|
in_block_comment: {
|
27
27
|
/\*/ => :maybe_block_comment_end,
|
28
|
-
/./
|
28
|
+
/./mu => :in_block_comment
|
29
29
|
},
|
30
30
|
maybe_block_comment_end: {
|
31
31
|
'/' => :root,
|
32
|
-
/./
|
32
|
+
/./mu => :in_block_comment
|
33
33
|
},
|
34
34
|
in_quoted_key: {
|
35
35
|
'"' => lambda do |state, _|
|
@@ -37,25 +37,25 @@ module Fastlane
|
|
37
37
|
state.buffer.string = ''
|
38
38
|
:after_quoted_key_before_eq
|
39
39
|
end,
|
40
|
-
/./ => lambda do |state, c|
|
40
|
+
/./u => lambda do |state, c|
|
41
41
|
state.buffer.write(c)
|
42
42
|
:in_quoted_key
|
43
43
|
end
|
44
44
|
},
|
45
45
|
after_quoted_key_before_eq: {
|
46
|
-
/\s/ => :after_quoted_key_before_eq,
|
46
|
+
/\s/u => :after_quoted_key_before_eq,
|
47
47
|
'=' => :after_quoted_key_and_eq
|
48
48
|
},
|
49
49
|
after_quoted_key_and_eq: {
|
50
|
-
/\s/ => :after_quoted_key_and_eq,
|
50
|
+
/\s/u => :after_quoted_key_and_eq,
|
51
51
|
'"' => :in_quoted_value
|
52
52
|
},
|
53
53
|
in_quoted_value: {
|
54
54
|
'"' => :after_quoted_value,
|
55
|
-
/./
|
55
|
+
/./mu => :in_quoted_value
|
56
56
|
},
|
57
57
|
after_quoted_value: {
|
58
|
-
/\s/ => :after_quoted_value,
|
58
|
+
/\s/u => :after_quoted_value,
|
59
59
|
';' => :root
|
60
60
|
}
|
61
61
|
}.freeze
|
@@ -70,7 +70,10 @@ module Fastlane
|
|
70
70
|
|
71
71
|
state = State.new(context: :root, buffer: StringIO.new, in_escaped_ctx: false, found_key: nil)
|
72
72
|
|
73
|
-
File.readlines
|
73
|
+
# Using our `each_utf8_line` helper instead of `File.readlines` ensures we can also read files that are
|
74
|
+
# encoded in UTF-16, yet process each of their lines as a UTF-8 string, so that `RegExp#match?` don't throw
|
75
|
+
# an `Encoding::CompatibilityError` exception. (Note how all our `RegExp`s in `TRANSITIONS` have the `u` flag)
|
76
|
+
Fastlane::Helper::Ios::L10nHelper.read_utf8_lines(file).each_with_index do |line, line_no|
|
74
77
|
line.chars.each_with_index do |c, col_no|
|
75
78
|
# Handle escaped characters at a global level.
|
76
79
|
# This is more straightforward than having to account for it in the `TRANSITIONS` table.
|
@@ -7,11 +7,11 @@ rescue LoadError
|
|
7
7
|
end
|
8
8
|
require 'json'
|
9
9
|
require 'tempfile'
|
10
|
+
require 'open3'
|
10
11
|
require 'optparse'
|
11
12
|
require 'pathname'
|
12
13
|
require 'progress_bar'
|
13
14
|
require 'parallel'
|
14
|
-
require 'jsonlint'
|
15
15
|
require 'chroma'
|
16
16
|
require 'securerandom'
|
17
17
|
|
@@ -32,17 +32,65 @@ module Fastlane
|
|
32
32
|
UI.user_error!('`drawText` not found – install it using `brew install automattic/build-tools/drawText`.') unless system('command -v drawText')
|
33
33
|
end
|
34
34
|
|
35
|
-
def
|
35
|
+
def read_config(configFilePath)
|
36
36
|
configFilePath = resolve_path(configFilePath)
|
37
37
|
|
38
38
|
begin
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
39
|
+
# NOTE: While JSON is a subset of YAML and thus YAML.load_file would technically cover both cases at once, in practice
|
40
|
+
# `JSON.parse` is more lenient with JSON files than `YAML.load_file` is — especially, it accepts `// comments` in the
|
41
|
+
# JSON file, despite this not being allowed in the spec — hence why we still try with `JSON.parse` for `.json` files.
|
42
|
+
return File.extname(configFilePath) == '.json' ? JSON.parse(File.read(configFilePath)) : YAML.load_file(configFilePath)
|
43
|
+
rescue StandardError => e
|
44
|
+
UI.error(e)
|
45
|
+
UI.user_error!('Invalid JSON/YAML configuration. Please lint your config file to check for syntax errors.')
|
46
|
+
end
|
47
|
+
end
|
44
48
|
|
45
|
-
|
49
|
+
# Checks that all required fonts are installed
|
50
|
+
# - Visits the JSON config to find all the stylesheets referenced in it
|
51
|
+
# - For each stylesheet, extract the first font of each `font-family` attribute found
|
52
|
+
# - Finally, for each of those fonts, check that they exist and are activated.
|
53
|
+
#
|
54
|
+
# @param [Hash] config The promo screenshots configuration, as returned by #read_config
|
55
|
+
# @raise [UserError] Raises if at least one font is missing.
|
56
|
+
#
|
57
|
+
def check_fonts_installed!(config:)
|
58
|
+
# Find all stylesheets in the config
|
59
|
+
all_stylesheets = ([config['stylesheet']] + config['entries'].flat_map do |entry|
|
60
|
+
entry['attachments']&.map { |att| att['stylesheet'] }
|
61
|
+
end).compact.uniq
|
62
|
+
|
63
|
+
# Parse the first font in each `font-family` attribute found in all found CSS files.
|
64
|
+
# Only the first in each `font-family` font list matters, as others are fallbacks we don't want to use anyway.
|
65
|
+
font_families = all_stylesheets.flat_map do |s|
|
66
|
+
File.readlines(s).flat_map do |line|
|
67
|
+
attr = line.match(/font-family: (.*);/)&.captures&.first
|
68
|
+
attr.split(',').first.strip.gsub(/'(.*)'/, '\1').gsub(/"(.*)"/, '\1') unless attr.nil?
|
69
|
+
end
|
70
|
+
end.compact.uniq
|
71
|
+
|
72
|
+
# Verify that all fonts exists and are active—using a small swift script as there's no nice CLI for that
|
73
|
+
swift_script = <<~SWIFT
|
74
|
+
import AppKit
|
75
|
+
|
76
|
+
var exitCode: Int32 = 0
|
77
|
+
for fontName in CommandLine.arguments.dropFirst() {
|
78
|
+
if NSFont(name: fontName, size: NSFont.systemFontSize) != nil {
|
79
|
+
print(" ✅ Font \\"\\(fontName)\\" found and active")
|
80
|
+
} else {
|
81
|
+
print(" ❌ Font \\"\\(fontName)\\" not found, it is either not installed or disabled. Please install it using FontBook first.")
|
82
|
+
exitCode = 1
|
83
|
+
}
|
84
|
+
}
|
85
|
+
exit(exitCode)
|
86
|
+
SWIFT
|
87
|
+
|
88
|
+
Tempfile.create(['fonts-check-', '.swift']) do |f|
|
89
|
+
f.write(swift_script)
|
90
|
+
f.close
|
91
|
+
oe, s = Open3.capture2e('/usr/bin/env', 'xcrun', 'swift', f.path, *font_families)
|
92
|
+
UI.command_output(oe)
|
93
|
+
UI.user_error!('Some fonts required by your stylesheets are missing. Please install them and try again.') unless s.success?
|
46
94
|
end
|
47
95
|
end
|
48
96
|
|
@@ -343,8 +391,8 @@ module Fastlane
|
|
343
391
|
def open_image(path)
|
344
392
|
path = resolve_path(path)
|
345
393
|
|
346
|
-
Magick::Image.read(path)
|
347
|
-
|
394
|
+
Magick::Image.read(path) do |image|
|
395
|
+
image.background_color = 'transparent'
|
348
396
|
end.first
|
349
397
|
end
|
350
398
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fastlane-plugin-wpmreleasetoolkit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 6.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Automattic
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-12-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -80,20 +80,6 @@ dependencies:
|
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '1.3'
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: jsonlint
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
86
|
-
requirements:
|
87
|
-
- - "~>"
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
version: '0.3'
|
90
|
-
type: :runtime
|
91
|
-
prerelease: false
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
93
|
-
requirements:
|
94
|
-
- - "~>"
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
version: '0.3'
|
97
83
|
- !ruby/object:Gem::Dependency
|
98
84
|
name: nokogiri
|
99
85
|
requirement: !ruby/object:Gem::Requirement
|