Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions .buildkite/commands/promote-to-beta.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,29 @@ set -eu

# Promotes the build chosen in the preceding block step to the beta track. No build — just gems + secrets.

# `beta_build_to_promote` must stay in sync with PROMOTION_META_DATA_KEY in fastlane/lanes/promote.rb,
# which is the key the gather lane writes the block-step select field under.
# `beta_build_to_promote` must stay in sync with BETA_META_DATA_KEY in fastlane/lanes/promote.rb,
# which is the key the gather lane writes the block-step build select field under.
VERSION_CODE="$(buildkite-agent meta-data get "beta_build_to_promote" --default "")"

# `beta_release_notes_option` must stay in sync with BETA_RELEASE_NOTES_META_DATA_KEY in the same file
# (the block step's release-notes select field).
RELEASE_NOTES_OPTION="$(buildkite-agent meta-data get "beta_release_notes_option" --default "")"

if [[ -z "${VERSION_CODE}" ]]; then
echo "+++ :x: No build was selected to promote."
exit 1
fi

if [[ -z "${RELEASE_NOTES_OPTION}" ]]; then
echo "+++ :x: No release notes option was selected."
exit 1
fi

echo "--- :rubygems: Setting up Gems"
install_gems

echo "--- :closed_lock_with_key: Installing Secrets"
bundle exec fastlane run configure_apply

echo "--- :rocket: Promoting ${VERSION_CODE} to the beta track"
bundle exec fastlane promote_to_beta version_code:"${VERSION_CODE}"
bundle exec fastlane promote_to_beta version_code:"${VERSION_CODE}" release_notes_option:"${RELEASE_NOTES_OPTION}"
3 changes: 3 additions & 0 deletions WordPress/jetpack_metadata/PlayStoreStrings.po
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,6 @@ msgid ""
"- Removed beta badge from Menus in My Site\n"
msgstr ""

msgctxt "release_note_static_serious"
msgid "We're focused on making Jetpack more stable and dependable with every release. This update continues that work with ongoing improvements and fixes. Have feedback or need help? Reach us any time from Help & Support. Thank you for using Jetpack."
msgstr ""
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
We're focused on making Jetpack more stable and dependable with every release. This update continues that work with ongoing improvements and fixes. Have feedback or need help? Reach us any time from Help & Support. Thank you for using Jetpack.
3 changes: 3 additions & 0 deletions WordPress/metadata/PlayStoreStrings.po
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,6 @@ msgid ""
"- Removed beta badge from Menus in My Site\n"
msgstr ""

msgctxt "release_note_static_serious"
msgid "We're focused on making WordPress more stable and dependable with every release. This update continues that work with ongoing improvements and fixes. Have feedback or need help? Reach us any time from Help & Support. Thank you for using WordPress."
msgstr ""
1 change: 1 addition & 0 deletions WordPress/metadata/release_notes_static/serious.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
We're focused on making WordPress more stable and dependable with every release. This update continues that work with ongoing improvements and fixes. Have feedback or need help? Reach us any time from Help & Support. Thank you for using WordPress.
8 changes: 8 additions & 0 deletions fastlane/Fastfile
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ UPLOAD_TO_PLAY_STORE_JSON_KEY = File.join(Dir.home, '.configure', 'wordpress-and
# writes a track release (AAB upload or beta promotion) keeps it in the release's version codes.
PLAY_STORE_VERSION_CODES_TO_RETAIN = [1440].freeze

# The "what's new" options shown in the beta-promotion picker. The picked one is set on the beta
# release and carried to production by the promote. `key` names the source file
# (`release_notes_static/<key>.txt`) and the GlotPress string (`release_note_static_<key>`); `label`
# is shown in the picker. Add entries to offer more options.
STATIC_RELEASE_NOTE_OPTIONS = [
{ key: 'serious', label: 'Serious' }
].freeze

PROTOTYPE_BUILD_TYPE = 'Debug'

PROJECT_ROOT_FOLDER = File.dirname(File.expand_path(__dir__))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
We're focused on making Jetpack more stable and dependable with every release. This update continues that work with ongoing improvements and fixes. Have feedback or need help? Reach us any time from Help & Support. Thank you for using Jetpack.
55 changes: 55 additions & 0 deletions fastlane/lanes/localization.rb
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,15 @@
}
end

# The beta-promotion picker's "what's new" options. Stable keys (no version suffix) so each is
# translated once, unlike the per-version `release_note_*` keys.
STATIC_RELEASE_NOTE_OPTIONS.each do |option|
files[:"release_note_static_#{option[:key]}"] = {
path: File.join(metadata_folder, 'release_notes_static', "#{option[:key]}.txt"),
comment: 'translators: Generic "what\'s new" text shown in the Play Store. Limit to 500 characters including spaces.'
}
end

update_po_file_for_metadata_localization(
po_path: File.join(metadata_folder, 'PlayStoreStrings.po'),
sources: files,
Expand Down Expand Up @@ -267,6 +276,52 @@
end
end

# Downloads the translated "what's new" options from GlotPress into the per-locale
# `release_notes_static/<option>.txt` files. Not part of the release path; run it whenever an
# option's copy changes.
#
# @param [Symbol|String] app The app to download for. If nil, does both WordPress and Jetpack.
# @param [Boolean] skip_commit If true, skips the `git add`/`git commit`. Default false.
#
lane :download_static_release_notes do |app: nil, skip_commit: false|
apps = app.nil? ? %i[wordpress jetpack] : Array(app.to_s.downcase.to_sym)

apps.each do |current_app|
app_values = APP_SPECIFIC_VALUES[current_app]
source_dir = File.join(PROJECT_ROOT_FOLDER, 'WordPress', app_values[:metadata_dir], 'release_notes_static')
download_path = File.join(FASTLANE_FOLDER, app_values[:metadata_dir], 'android')
locales = { wordpress: WP_RELEASE_NOTES_LOCALES, jetpack: JP_RELEASE_NOTES_LOCALES }[current_app]

target_files = STATIC_RELEASE_NOTE_OPTIONS.to_h do |option|
[
"release_note_static_#{option[:key]}",
{ desc: File.join('release_notes_static', "#{option[:key]}.txt"), max_size: 500 }
]
end

UI.header("Downloading static release notes for #{app_values[:display_name]}")
gp_downloadmetadata(
project_url: app_values[:glotpress_metadata_project],
target_files: target_files,
locales: locales,
download_path: download_path
)

# en-US is the source language and isn't exported from GlotPress; copy each source verbatim.
en_us_dir = File.join(download_path, 'en-US', 'release_notes_static')
FileUtils.mkdir_p(en_us_dir)
STATIC_RELEASE_NOTE_OPTIONS.each do |option|
FileUtils.cp(File.join(source_dir, "#{option[:key]}.txt"), File.join(en_us_dir, "#{option[:key]}.txt"))
end

next if skip_commit

git_add(path: download_path)
message = "Update #{app_values[:display_name]} static release notes translations"
git_commit(path: download_path, message: message, allow_nothing_to_commit: true)
end
end

########################################################################
# In-App Translations
########################################################################
Expand Down
83 changes: 68 additions & 15 deletions fastlane/lanes/promote.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
# NOTE: `.buildkite/commands/promote-to-beta.sh` reads this same key as a bare string literal
# (`meta-data get "beta_build_to_promote"`) — keep the two in sync.
BETA_META_DATA_KEY = 'beta_build_to_promote'
# The block step also writes the chosen release-note option here; the promote step reads it back.
# NOTE: `.buildkite/commands/promote-to-beta.sh` reads this same key as a bare string literal
# (`meta-data get "beta_release_notes_option"`) — keep the two in sync.
BETA_RELEASE_NOTES_META_DATA_KEY = 'beta_release_notes_option'
# Matched via the Buildkite API to find the block step's job and build its unblock URL.
BETA_BLOCK_LABEL = ':android: Promote to beta'
BETA_BLOCK_STEP_KEY = 'promote_to_beta_block'
Expand Down Expand Up @@ -95,7 +99,7 @@
# @param [String] version_code The version code to promote, e.g. `269027172`.
# @called_by CI (`.buildkite/commands/promote-to-beta.sh`)
desc 'Promote an existing build to the beta track (WordPress + Jetpack)'
lane :promote_to_beta do |version_code: nil|
lane :promote_to_beta do |version_code: nil, release_notes_option: nil|
# Set once the per-app result has been posted, so the rescue doesn't double-report it.
result_posted = false
ensure_promotion_on_trunk!
Expand All @@ -106,9 +110,11 @@
UI.user_error!('`version_code` is required, e.g. `version_code:269027172`') if version_code.empty?
UI.user_error!("`version_code` must be an integer, got #{version_code.inspect}") unless version_code.match?(/\A\d+\z/)

release_notes_option = validated_release_notes_option(release_notes_option)

UI.important("Promoting version code #{version_code} to the beta track for WordPress and Jetpack")

results = distribute_to_beta(version_code: version_code)
results = distribute_to_beta(version_code: version_code, release_notes_option: release_notes_option)

post_beta_result_to_slack(version_code: version_code, results: results)
result_posted = true
Expand Down Expand Up @@ -342,13 +348,15 @@ def available_aab_version_codes(package_name:)

# Promotes a version code to beta for each app, returning a per-app `{ ok:, error: }` result.
# A failure for one app doesn't stop the other.
def distribute_to_beta(version_code:)
def distribute_to_beta(version_code:, release_notes_option:)
%i[wordpress jetpack].to_h do |app|
result =
begin
promote_version_code_to_beta(
app: app,
package_name: APP_SPECIFIC_VALUES[app][:package_name],
version_code: version_code
version_code: version_code,
release_notes_option: release_notes_option
)
{ ok: true }
rescue StandardError => e
Expand All @@ -364,7 +372,7 @@ def distribute_to_beta(version_code:)
# `upload_to_play_store` can't do this: it only builds a track release from binaries uploaded in
# the same run, so a bare `version_code:` with `skip_upload_aab` commits an empty edit. We create
# the release directly instead, mirroring supply's own `update_track`.
def promote_version_code_to_beta(package_name:, version_code:)
def promote_version_code_to_beta(app:, package_name:, version_code:, release_notes_option:)
require 'supply'
require 'supply/options'

Expand All @@ -373,6 +381,10 @@ def promote_version_code_to_beta(package_name:, version_code:)
{ json_key: UPLOAD_TO_PLAY_STORE_JSON_KEY, package_name: package_name, track: BETA_TRACK }
)

# The picked release notes (needs supply loaded, above). nil when the option has no files.
release_notes = static_release_notes(app: app, option: release_notes_option)
release_notes = nil if release_notes.empty?

with_play_edit_retries("Promoting #{version_code} to beta for #{package_name}") do
client = Supply::Client.make_from_config
client.begin_edit(package_name: package_name)
Expand All @@ -383,7 +395,9 @@ def promote_version_code_to_beta(package_name:, version_code:)
# TODO: switch to 'completed' once the feature is ready to distribute to beta testers.
status: 'draft',
# Keep the pinned legacy code(s) on the track, same as the AAB-upload path.
version_codes: [Integer(version_code), *PLAY_STORE_VERSION_CODES_TO_RETAIN]
version_codes: [Integer(version_code), *PLAY_STORE_VERSION_CODES_TO_RETAIN],
# Carried to production automatically by the promote.
release_notes: release_notes
)
track = client.tracks(BETA_TRACK).first || AndroidPublisher::Track.new(track: BETA_TRACK)
track.releases = [release]
Expand All @@ -398,6 +412,32 @@ def promote_version_code_to_beta(package_name:, version_code:)
end
end

# The picked option's notes as Play `LocalizedText`, one per locale that has a
# `release_notes_static/<option>.txt` file. Missing locales fall back to Play's default language.
def static_release_notes(app:, option:)
metadata_dir = File.join(FASTLANE_FOLDER, APP_SPECIFIC_VALUES[app][:metadata_dir], 'android')
notes = Dir.glob(File.join(metadata_dir, '*', 'release_notes_static', "#{option}.txt")).filter_map do |path|
text = File.read(path).strip
next if text.empty?

# Path is <metadata_dir>/<locale>/release_notes_static/<option>.txt; the locale is two dirs up.
locale = File.basename(File.dirname(path, 2))
AndroidPublisher::LocalizedText.new(language: locale, text: text)
end
UI.important("No static release notes found for option #{option.inspect} (#{app}).") if notes.empty?
notes
end

# Validates the picked option against the registry, raising on anything unknown.
def validated_release_notes_option(option)
option = option.to_s.strip
valid = STATIC_RELEASE_NOTE_OPTIONS.map { |o| o[:key] }
UI.user_error!("`release_notes_option` is required, one of: #{valid.join(', ')}") if option.empty?
return option if valid.include?(option)

UI.user_error!("Unknown `release_notes_option` #{option.inspect}; expected one of: #{valid.join(', ')}")
end

# Promotes a version code to production for each app, returning a per-app `{ ok:, error: }` result.
# A failure for one app doesn't stop the other.
def distribute_to_production(version_code:)
Expand Down Expand Up @@ -600,15 +640,7 @@ def write_beta_promotion_steps_file(candidates:)
'prompt' => 'Choose the build to release to beta testers. This promotes the matching WordPress and Jetpack builds.',
# Keep the build "running" (not green) while it waits for a human.
'blocked_state' => 'running',
'fields' => [
{
'select' => 'Build to promote',
'key' => BETA_META_DATA_KEY,
# Required, no default: an un-actioned unblock can't silently promote a build.
'required' => true,
'options' => options
}
]
'fields' => beta_promotion_block_fields(options: options)
},
{
'label' => ':rocket: Promote selected build to beta',
Expand All @@ -625,6 +657,27 @@ def write_beta_promotion_steps_file(candidates:)
UI.message("Wrote promotion steps for #{candidates.count} build(s) to #{PROMOTION_STEPS_FILE}")
end

# The block step's input fields: pick a build, and pick the release note to publish with it.
def beta_promotion_block_fields(options:)
[
{
'select' => 'Build to promote',
'key' => BETA_META_DATA_KEY,
# Required, no default: an un-actioned unblock can't silently promote a build.
'required' => true,
'options' => options
},
{
'select' => 'Release notes',
'key' => BETA_RELEASE_NOTES_META_DATA_KEY,
'hint' => 'Which "what\'s new" text to publish. Carries through to production.',
# Required, no default: the notes are a deliberate choice, not a silent fallback.
'required' => true,
'options' => STATIC_RELEASE_NOTE_OPTIONS.map { |o| { 'label' => o[:label], 'value' => o[:key] } }
}
]
end

# Writes the confirm → promote → finalize steps. There's no picker — the candidate is baked into the
# commands; the block step only gates on a Yes/No confirmation.
def write_production_release_steps_file(version_code:)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
We're focused on making WordPress more stable and dependable with every release. This update continues that work with ongoing improvements and fixes. Have feedback or need help? Reach us any time from Help & Support. Thank you for using WordPress.
Loading