diff --git a/.buildkite/beta-builds.yml b/.buildkite/beta-builds.yml index 556fb4dfb1bc..b58ecc63269e 100644 --- a/.buildkite/beta-builds.yml +++ b/.buildkite/beta-builds.yml @@ -5,7 +5,7 @@ common_params: # Common plugin settings to use with the `plugins` key. - &common_plugins - - automattic/a8c-ci-toolkit#2.15.0 + - automattic/a8c-ci-toolkit#2.15.1 steps: ################# diff --git a/.buildkite/code-freeze.yml b/.buildkite/code-freeze.yml new file mode 100644 index 000000000000..004369d3b578 --- /dev/null +++ b/.buildkite/code-freeze.yml @@ -0,0 +1,15 @@ +# Nodes with values to reuse in the pipeline. +common_params: + # Common plugin settings to use with the `plugins` key. + - &common_plugins + - automattic/a8c-ci-toolkit#2.15.1 + +steps: + - label: "Code Freeze" + plugins: *common_plugins + command: | + .buildkite/commands/configure-git-for-release-management.sh + + install_gems + + bundle exec fastlane code_freeze skip_confirm:true diff --git a/.buildkite/commands/checkout-editorial-branch.sh b/.buildkite/commands/checkout-editorial-branch.sh new file mode 100755 index 000000000000..f495193ae32f --- /dev/null +++ b/.buildkite/commands/checkout-editorial-branch.sh @@ -0,0 +1,22 @@ +#!/bin/bash -eu + +# EDITORIAL_BRANCH is passed as an environment variable from fastlane to Buildkite +# +if [[ -z "${EDITORIAL_BRANCH}" ]]; then + echo "EDITORIAL_BRANCH is not set." + exit 1 +fi + +# RELEASE_VERSION is passed as an environment variable from fastlane to Buildkite +# Even though RELEASE_VERSION is not directly used in this script, it's necessary to update +# the app store strings. Having this check here keeps the buildkite pipeline cleaner. Later on, +# if we don't want it here, we can move it to a separate script file. +if [[ -z "${RELEASE_VERSION}" ]]; then + echo "RELEASE_VERSION is not set." + exit 1 +fi + +# Buildkite, by default, checks out a specific commit. When we update the app store strings, we open +# a PR from the current branch. So, we need to checkout the `EDITORIAL_BRANCH`. +git fetch origin "$EDITORIAL_BRANCH" +git checkout "$EDITORIAL_BRANCH" diff --git a/.buildkite/commands/checkout-release-branch.sh b/.buildkite/commands/checkout-release-branch.sh new file mode 100755 index 000000000000..2f6fb8302590 --- /dev/null +++ b/.buildkite/commands/checkout-release-branch.sh @@ -0,0 +1,14 @@ +#!/bin/bash -eu + +# RELEASE_VERSION is passed as an environment variable from fastlane to Buildkite +# +if [[ -z "${RELEASE_VERSION}" ]]; then + echo "RELEASE_VERSION is not set." + exit 1 +fi + +# Buildkite, by default, checks out a specific commit. For many release actions, we need to be +# on a release branch instead. +BRANCH_NAME="release/${RELEASE_VERSION}" +git fetch origin "$BRANCH_NAME" +git checkout "$BRANCH_NAME" diff --git a/.buildkite/commands/configure-git-for-release-management.sh b/.buildkite/commands/configure-git-for-release-management.sh new file mode 100755 index 000000000000..eb39be490f66 --- /dev/null +++ b/.buildkite/commands/configure-git-for-release-management.sh @@ -0,0 +1,10 @@ +#!/bin/bash -eu + +# Git command line client is not configured in Buildkite. Temporarily, we configure it in each step. +# Later on, we should be able to configure the agent instead. +curl -L https://api.github.com/meta | jq -r '.ssh_keys | .[]' | sed -e 's/^/github.com /' >> ~/.ssh/known_hosts +git config --global user.email "mobile+wpmobilebot@automattic.com" +git config --global user.name "Automattic Release Bot" + +# Buildkite is currently using the https url to checkout. We need to override it to be able to use the deploy key. +git remote set-url origin git@github.com:wordpress-mobile/WordPress-Android.git diff --git a/.buildkite/complete-code-freeze.yml b/.buildkite/complete-code-freeze.yml new file mode 100644 index 000000000000..6c913fd783ad --- /dev/null +++ b/.buildkite/complete-code-freeze.yml @@ -0,0 +1,16 @@ +# Nodes with values to reuse in the pipeline. +common_params: + # Common plugin settings to use with the `plugins` key. + - &common_plugins + - automattic/a8c-ci-toolkit#2.15.1 + +steps: + - label: "Complete Code Freeze" + plugins: *common_plugins + command: | + .buildkite/commands/configure-git-for-release-management.sh + .buildkite/commands/checkout-release-branch.sh + + install_gems + + bundle exec fastlane complete_code_freeze skip_confirm:true diff --git a/.buildkite/finalize-release.yml b/.buildkite/finalize-release.yml new file mode 100644 index 000000000000..049580502b3c --- /dev/null +++ b/.buildkite/finalize-release.yml @@ -0,0 +1,16 @@ +# Nodes with values to reuse in the pipeline. +common_params: + # Common plugin settings to use with the `plugins` key. + - &common_plugins + - automattic/a8c-ci-toolkit#2.15.1 + +steps: + - label: "Finalize release" + plugins: *common_plugins + command: | + .buildkite/commands/configure-git-for-release-management.sh + .buildkite/commands/checkout-release-branch.sh + + install_gems + + bundle exec fastlane finalize_release skip_confirm:true diff --git a/.buildkite/new-beta-release.yml b/.buildkite/new-beta-release.yml new file mode 100644 index 000000000000..55cd764b2c7a --- /dev/null +++ b/.buildkite/new-beta-release.yml @@ -0,0 +1,15 @@ +# Nodes with values to reuse in the pipeline. +common_params: + # Common plugin settings to use with the `plugins` key. + - &common_plugins + - automattic/a8c-ci-toolkit#2.15.1 + +steps: + - label: "New Beta Release" + plugins: *common_plugins + command: | + .buildkite/commands/configure-git-for-release-management.sh + + install_gems + + bundle exec fastlane new_beta_release skip_confirm:true diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index 1aa998530d8b..043759063514 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -2,7 +2,7 @@ common_params: # Common plugin settings to use with the `plugins` key. - &common_plugins - - automattic/a8c-ci-toolkit#2.14.0 + - automattic/a8c-ci-toolkit#2.15.1 steps: ################# diff --git a/.buildkite/release-builds.yml b/.buildkite/release-builds.yml index a19960ace524..762426dd12f1 100644 --- a/.buildkite/release-builds.yml +++ b/.buildkite/release-builds.yml @@ -5,7 +5,7 @@ common_params: # Common plugin settings to use with the `plugins` key. - &common_plugins - - automattic/a8c-ci-toolkit#2.14.0 + - automattic/a8c-ci-toolkit#2.15.1 steps: ################# diff --git a/.buildkite/update-release-notes.yml b/.buildkite/update-release-notes.yml new file mode 100644 index 000000000000..b26efbc24886 --- /dev/null +++ b/.buildkite/update-release-notes.yml @@ -0,0 +1,16 @@ +# Nodes with values to reuse in the pipeline. +common_params: + # Common plugin settings to use with the `plugins` key. + - &common_plugins + - automattic/a8c-ci-toolkit#2.15.1 + +steps: + - label: "Update release notes" + plugins: *common_plugins + command: | + .buildkite/commands/configure-git-for-release-management.sh + .buildkite/commands/checkout-editorial-branch.sh + + install_gems + + bundle exec fastlane update_appstore_strings version:${RELEASE_VERSION} diff --git a/Gemfile b/Gemfile index c9787fdbf99b..5374b220ade2 100644 --- a/Gemfile +++ b/Gemfile @@ -7,7 +7,7 @@ gem 'nokogiri' ### Fastlane Plugins -gem 'fastlane-plugin-wpmreleasetoolkit', '~> 7.0' +gem 'fastlane-plugin-wpmreleasetoolkit', '~> 8.0' # gem 'fastlane-plugin-wpmreleasetoolkit', path: '../../release-toolkit' # gem 'fastlane-plugin-wpmreleasetoolkit', git: 'https://github.com/wordpress-mobile/release-toolkit', branch: 'trunk' diff --git a/Gemfile.lock b/Gemfile.lock index 06201cb383f9..df906a58810f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -3,26 +3,26 @@ GEM specs: CFPropertyList (3.0.6) rexml - activesupport (7.0.4.2) + activesupport (7.0.4.3) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) tzinfo (~> 2.0) - addressable (2.8.1) + addressable (2.8.4) public_suffix (>= 2.0.2, < 6.0) artifactory (3.0.15) atomos (0.1.3) aws-eventstream (1.2.0) - aws-partitions (1.721.0) - aws-sdk-core (3.170.0) + aws-partitions (1.763.0) + aws-sdk-core (3.172.0) aws-eventstream (~> 1, >= 1.0.2) aws-partitions (~> 1, >= 1.651.0) aws-sigv4 (~> 1.5) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.63.0) + aws-sdk-kms (1.64.0) aws-sdk-core (~> 3, >= 3.165.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.119.1) + aws-sdk-s3 (1.122.0) aws-sdk-core (~> 3, >= 3.165.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.4) @@ -38,7 +38,7 @@ GEM colored2 (3.1.2) commander (4.6.0) highline (~> 2.0.0) - concurrent-ruby (1.2.0) + concurrent-ruby (1.2.2) declarative (0.0.20) diffy (3.4.2) digest-crc (0.6.4) @@ -77,7 +77,7 @@ GEM faraday_middleware (1.2.0) faraday (~> 1.0) fastimage (2.2.6) - fastlane (2.212.1) + fastlane (2.212.2) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.8, < 3.0.0) artifactory (~> 3.0) @@ -116,7 +116,7 @@ GEM xcodeproj (>= 1.13.0, < 2.0.0) xcpretty (~> 0.3.0) xcpretty-travis-formatter (>= 0.0.3) - fastlane-plugin-wpmreleasetoolkit (7.0.0) + fastlane-plugin-wpmreleasetoolkit (8.0.0) activesupport (>= 6.1.7.1) bigdecimal (~> 1.4) buildkit (~> 1.5) @@ -125,17 +125,17 @@ GEM git (~> 1.3) google-cloud-storage (~> 1.31) nokogiri (~> 1.11) - octokit (~> 4.18) + octokit (~> 5.6) parallel (~> 1.14) plist (~> 3.1) progress_bar (~> 1.3) rake (>= 12.3, < 14.0) rake-compiler (~> 1.0) gh_inspector (1.1.3) - git (1.13.2) + git (1.18.0) addressable (~> 2.8) rchardet (~> 1.8) - google-apis-androidpublisher_v3 (0.35.0) + google-apis-androidpublisher_v3 (0.41.0) google-apis-core (>= 0.11.0, < 2.a) google-apis-core (0.11.0) addressable (~> 2.5, >= 2.5.1) @@ -166,7 +166,7 @@ GEM google-cloud-core (~> 1.6) googleauth (>= 0.16.2, < 2.a) mini_mime (~> 1.0) - googleauth (1.3.0) + googleauth (1.5.2) faraday (>= 0.17.3, < 3.a) jwt (>= 1.4, < 3.0) memoist (~> 0.16) @@ -177,7 +177,7 @@ GEM http-cookie (1.0.5) domain_name (~> 0.5) httpclient (2.8.3) - i18n (1.12.0) + i18n (1.13.0) concurrent-ruby (~> 1.0) jmespath (1.6.2) json (2.6.3) @@ -185,22 +185,22 @@ GEM memoist (0.16.2) mini_magick (4.12.0) mini_mime (1.1.2) - mini_portile2 (2.8.1) - minitest (5.17.0) + mini_portile2 (2.8.2) + minitest (5.18.0) multi_json (1.15.0) multipart-post (2.0.0) nanaimo (0.3.0) naturally (2.2.1) - nokogiri (1.14.2) + nokogiri (1.14.4) mini_portile2 (~> 2.8.0) racc (~> 1.4) - octokit (4.25.1) + octokit (5.6.1) faraday (>= 1, < 3) sawyer (~> 0.9) options (2.3.2) optparse (0.1.1) os (1.1.4) - parallel (1.22.1) + parallel (1.23.0) plist (3.7.0) progress_bar (1.3.3) highline (>= 1.6, < 3) @@ -267,7 +267,7 @@ PLATFORMS DEPENDENCIES fastlane (~> 2) - fastlane-plugin-wpmreleasetoolkit (~> 7.0) + fastlane-plugin-wpmreleasetoolkit (~> 8.0) nokogiri rmagick (~> 4.1) diff --git a/fastlane/Fastfile b/fastlane/Fastfile index c887a763e4c4..e02d7602621f 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -48,11 +48,13 @@ REPOSITORY_NAME = 'WordPress-Android' ######################################################################## # Import domain-specific lanes ######################################################################## +import 'helpers/github.rb' import 'lanes/build.rb' import 'lanes/localization.rb' import 'lanes/release.rb' import 'lanes/screenshots.rb' import 'lanes/test.rb' +import 'lanes/release_management_in_ci.rb' default_platform(:android) diff --git a/fastlane/helpers/github.rb b/fastlane/helpers/github.rb new file mode 100644 index 000000000000..df5bc2fe175e --- /dev/null +++ b/fastlane/helpers/github.rb @@ -0,0 +1,10 @@ +def create_release_management_pull_request(base_branch, title) + create_pull_request( + api_token: ENV['GITHUB_TOKEN'], + repo: GHHELPER_REPO, + title: title, + head: Fastlane::Helper::GitHelper.current_git_branch, + base: base_branch, + labels: 'Releases' + ) +end diff --git a/fastlane/lanes/localization.rb b/fastlane/lanes/localization.rb index 71bd81738093..f2f026e8bfe4 100644 --- a/fastlane/lanes/localization.rb +++ b/fastlane/lanes/localization.rb @@ -98,12 +98,12 @@ lane :update_appstore_strings do |options| # If no `app:` is specified, call this for both WordPress and Jetpack apps = options[:app].nil? ? %i[wordpress jetpack] : Array(options[:app]&.downcase&.to_sym) + version = options.fetch(:version, android_get_app_version) apps.each do |app| app_values = APP_SPECIFIC_VALUES[app] metadata_folder = File.join(PROJECT_ROOT_FOLDER, 'WordPress', app_values[:metadata_dir]) - version = options.fetch(:version, android_get_app_version) # => files = { @@ -126,6 +126,11 @@ commit_message: "Update #{app_values[:display_name]} `PlayStoreStrings.po` for version #{version}" ) end + + push_to_git_remote(tags: false) + + release_branch = "release/#{version}" + create_release_management_pull_request(release_branch, "Merge #{version} editorialized release notes to #{release_branch}") end # Updates the metadata in the Play Store (Main store listing) from the content of `fastlane/{metadata|jetpack_metadata}/android/*/*.txt` files @@ -173,7 +178,7 @@ skip_commit = options.fetch(:skip_commit, false) skip_git_push = options.fetch(:skip_git_push, false) - + # If no `app:` is specified, call this for both WordPress and Jetpack apps = options[:app].nil? ? %i[wordpress jetpack] : Array(options[:app]&.to_s&.downcase&.to_sym) @@ -213,7 +218,7 @@ source_file = key.to_s.start_with?('release_note_') ? 'release_notes.txt' : h[:desc] FileUtils.cp(File.join(metadata_source_dir, source_file), File.join(download_path, 'en-US', h[:desc])) end - + unless skip_commit git_add(path: download_path) message = "Update #{app_values[:display_name]} metadata translations" diff --git a/fastlane/lanes/release.rb b/fastlane/lanes/release.rb index 0c436955b952..d091621f221d 100644 --- a/fastlane/lanes/release.rb +++ b/fastlane/lanes/release.rb @@ -32,12 +32,14 @@ sh('git', 'commit', '-m', "Update draft release notes for Jetpack #{new_version}.") end cleanup_release_files(files: release_notes_short_paths) - android_update_release_notes(new_version: new_version) # Adds empty section for next version - setbranchprotection(repository: GHHELPER_REPO, branch: "release/#{new_version}") - setfrozentag(repository: GHHELPER_REPO, milestone: new_version) UI.message("Jetpack release notes were based on the same ones as WordPress. Don't forget to check #{release_notes_path('jetpack')} and amend them as necessary if any item does not apply for Jetpack before sending them to Editorial.") + + push_to_git_remote(tags: false) + + setbranchprotection(repository: GHHELPER_REPO, branch: "release/#{new_version}") + setfrozentag(repository: GHHELPER_REPO, milestone: new_version) end ##################################################################################### @@ -60,10 +62,12 @@ update_frozen_strings_for_translation ensure_git_status_clean - push_to_git_remote + push_to_git_remote(tags: false) new_version = android_get_app_version() trigger_beta_build(branch_to_build: "release/#{new_version}") + + create_release_management_pull_request('trunk', "Merge #{new_version} code freeze to trunk") end ##################################################################################### @@ -88,10 +92,20 @@ update_frozen_strings_for_translation download_translations() android_bump_version_beta() - next unless UI.confirm('Ready for CI build') - new_version = android_get_app_version() - trigger_beta_build(branch_to_build: "release/#{new_version}") + app_version = android_get_app_version() + release_branch = "release/#{app_version}" + release_version = android_get_release_version()["name"] + + # Create an intermediate branch + new_beta_branch_name = "new_beta/#{release_version}" + Fastlane::Helper::GitHelper.create_branch(new_beta_branch_name) + + push_to_git_remote(tags: false) + + trigger_beta_build(branch_to_build: new_beta_branch_name) + + create_release_management_pull_request(release_branch, "Merge #{release_version} to #{release_branch}") end ##################################################################################### @@ -110,6 +124,7 @@ hotfix_version = options[:version_name] || UI.input('Version number for the new hotfix?') previous_tag = android_hotfix_prechecks(version_name: hotfix_version, skip_confirm: options[:skip_confirm]) android_bump_version_hotfix(previous_version_name: previous_tag, version_name: hotfix_version, version_code: options[:version_code]) + push_to_git_remote(tags: false) end ##################################################################################### @@ -148,6 +163,12 @@ android_finalize_prechecks(skip_confirm: options[:skip_confirm]) configure_apply(force: is_ci) + app_version = android_get_app_version() + release_branch = "release/#{app_version}" + + # Remove branch protection first, so that we can push the final commits directly to the release branch + removebranchprotection(repository: GHHELPER_REPO, branch: release_branch) + check_translations_coverage() download_translations() @@ -155,14 +176,17 @@ version = android_get_release_version() download_metadata_strings(version: version['name'], build_number: version['code']) + push_to_git_remote(tags: false) + # Wrap up - removebranchprotection(repository: GHHELPER_REPO, branch: "release/#{version['name']}") setfrozentag(repository: GHHELPER_REPO, milestone: version['name'], freeze: false) create_new_milestone(repository: GHHELPER_REPO) close_milestone(repository: GHHELPER_REPO, milestone: version['name']) # Trigger release build trigger_release_build(branch_to_build: "release/#{version['name']}") + + create_release_management_pull_request('trunk', "Merge #{app_version} final to trunk") end lane :check_translations_coverage do |options| diff --git a/fastlane/lanes/release_management_in_ci.rb b/fastlane/lanes/release_management_in_ci.rb new file mode 100644 index 000000000000..b8186c13ab2f --- /dev/null +++ b/fastlane/lanes/release_management_in_ci.rb @@ -0,0 +1,65 @@ +BUILDKITE_ORGANIZATION = 'automattic'.freeze +BUILDKITE_PIPELINE = 'wordpress-android'.freeze +platform :android do + ##################################################################################### + # Triggers for Buildkite + ##################################################################################### + lane :trigger_code_freeze_in_ci do |options| + buildkite_trigger_build( + buildkite_organization: BUILDKITE_ORGANIZATION, + buildkite_pipeline: BUILDKITE_PIPELINE, + branch: 'trunk', + pipeline_file: 'code-freeze.yml', + message: 'Code Freeze' + ) + end + + lane :trigger_complete_code_freeze_in_ci do |options| + release_version = options[:release_version] + buildkite_trigger_build( + buildkite_organization: BUILDKITE_ORGANIZATION, + buildkite_pipeline: BUILDKITE_PIPELINE, + branch: "release/#{release_version}", + pipeline_file: 'complete-code-freeze.yml', + message: 'Complete Code Freeze', + environment: { RELEASE_VERSION: release_version } + ) + end + + lane :trigger_finalize_release_in_ci do |options| + release_version = options[:release_version] + buildkite_trigger_build( + buildkite_organization: BUILDKITE_ORGANIZATION, + buildkite_pipeline: BUILDKITE_PIPELINE, + branch: "release/#{release_version}", + pipeline_file: 'finalize-release.yml', + message: 'Finalize Release', + environment: { RELEASE_VERSION: release_version } + ) + end + + lane :trigger_new_beta_release_in_ci do |options| + release_version = options[:release_version] + buildkite_trigger_build( + buildkite_organization: BUILDKITE_ORGANIZATION, + buildkite_pipeline: BUILDKITE_PIPELINE, + branch: "release/#{release_version}", + pipeline_file: 'new-beta-release.yml', + message: 'New Beta Release', + environment: { RELEASE_VERSION: release_version } + ) + end + + lane :trigger_update_appstore_strings_in_ci do |options| + release_version = options[:release_version] + editorial_branch = options[:editorial_branch] + buildkite_trigger_build( + buildkite_organization: BUILDKITE_ORGANIZATION, + buildkite_pipeline: BUILDKITE_PIPELINE, + branch: "#{editorial_branch}", + pipeline_file: 'update-release-notes.yml', + message: 'Update Release Notes', + environment: { RELEASE_VERSION: release_version, EDITORIAL_BRANCH: editorial_branch } + ) + end +end