diff --git a/.buildkite/commands/run-fastlane-tests.sh b/.buildkite/commands/run-fastlane-tests.sh new file mode 100755 index 0000000000..3bfd1eb0fa --- /dev/null +++ b/.buildkite/commands/run-fastlane-tests.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +set -euo pipefail + +if .buildkite/commands/should-skip-job.sh --job-type fastlane; then + exit 0 +fi + +echo '--- :fastlane: Run fastlane Helper Tests' +for test_file in fastlane/test/*_test.rb; do + ruby "$test_file" +done diff --git a/.buildkite/commands/should-skip-job.sh b/.buildkite/commands/should-skip-job.sh index c2fcb47a4e..9b870fcc32 100755 --- a/.buildkite/commands/should-skip-job.sh +++ b/.buildkite/commands/should-skip-job.sh @@ -14,6 +14,8 @@ set -eu # Since metrics measure app performance, test file changes don't affect them. # - build: Skip if changes are limited to documentation and config files. # Does NOT skip on localization changes since builds should include translation updates. +# - fastlane: Inverse of the others — only runs when fastlane/ or Ruby setup files change. +# Used for the standalone tests in fastlane/test/. App-only PRs skip it. # # Exit codes: # 0 - Job should be skipped @@ -76,6 +78,20 @@ TEST_PATTERNS=( "metrics/**" ) +# Fastlane / Ruby setup files - changes here affect the standalone fastlane +# helper tests. Anything that the test runner reads (Fastfile, lib/, test/), +# that defines the Ruby environment, or that implements this CI job's runner / +# skip logic belongs here. +FASTLANE_PATTERNS=( + "fastlane/**" + "Gemfile" + "Gemfile.lock" + ".ruby-version" + ".bundle/**" + ".buildkite/commands/run-fastlane-tests.sh" + ".buildkite/commands/should-skip-job.sh" +) + show_skip_message() { local job_type=$1 local job_label="${BUILDKITE_LABEL:-$job_type}" @@ -107,7 +123,7 @@ done if [[ -z "$job_type" ]]; then echo "Error: --job-type is required" - echo "Usage: should-skip-job.sh --job-type " + echo "Usage: should-skip-job.sh --job-type " exit 1 fi @@ -147,9 +163,18 @@ case "$job_type" in fi ;; + "fastlane") + # Run only if at least one changed file is fastlane/ or Ruby-setup-related. + # Other job types treat fastlane changes as non-code; this one is the inverse. + if ! pr_changed_files --any-match "${FASTLANE_PATTERNS[@]}"; then + show_skip_message "$job_type" + exit 0 + fi + ;; + *) echo "Unknown job type: $job_type" - echo "Valid types: validation, metrics, build" + echo "Valid types: validation, metrics, build, fastlane" exit 1 ;; esac diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index 4571f68666..9bdeafbaee 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -38,6 +38,16 @@ steps: - github_commit_status: context: Lint + - label: ":fastlane: fastlane Helper Tests" + agents: + queue: mac + key: fastlane_tests + command: bash .buildkite/commands/run-fastlane-tests.sh + plugins: [$CI_TOOLKIT_PLUGIN] + notify: + - github_commit_status: + context: fastlane Helper Tests + - label: Unit Tests on {{matrix}} key: unit_tests command: bash .buildkite/commands/run-unit-tests.sh "{{matrix}}" diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 443ace5e2b..1828aec571 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -7,6 +7,7 @@ fastlane_require 'json' fastlane_require 'net/http' fastlane_require 'uri' +require_relative 'lib/studio_apps_cdn_upload' require_relative 'lib/studio_release_git' require_relative 'lib/studio_release_version' @@ -712,12 +713,14 @@ def distribute_builds( } } - builds_to_upload = release_tag.nil? ? update_builds : { **update_builds, **full_install_builds } + builds_to_upload = { **update_builds, **full_install_builds } - UI.message("Uploading #{builds_to_upload.count} builds (#{release_tag.nil? ? 'dev: updates only' : 'release: all builds'})") + UI.message("Uploading #{builds_to_upload.count} builds (#{build_type})") # Upload to Apps CDN builds_to_upload.each_value do |build| + visibility = StudioAppsCdnUpload.visibility_for(build_type: build_type, install_type: build[:install_type]) + result = upload_file_to_apps_cdn( site_id: WPCOM_STUDIO_SITE_ID, product: 'WordPress.com Studio', @@ -726,7 +729,7 @@ def distribute_builds( arch: build[:arch], build_type: build_type, install_type: build[:install_type], - visibility: 'external', + visibility: visibility, version: version, build_number: build_number, release_notes: release_notes, diff --git a/fastlane/lib/studio_apps_cdn_upload.rb b/fastlane/lib/studio_apps_cdn_upload.rb new file mode 100644 index 0000000000..444c74281b --- /dev/null +++ b/fastlane/lib/studio_apps_cdn_upload.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module StudioAppsCdnUpload + # Returns the Apps CDN visibility (`'internal'` or `'external'`) for a build. + # + # Nightly full installers are restricted to logged-in Automatticians via the + # Apps CDN visibility filter, so the `/nightly` link only resolves for + # internal users. Update binaries stay external — the in-app auto-updater is + # unauthenticated and would otherwise stop seeing nightly updates. Beta and + # Production stay external across the board. + def self.visibility_for(build_type:, install_type:) + return 'internal' if build_type == 'Nightly' && install_type == 'Full Install' + + 'external' + end +end diff --git a/fastlane/test/studio_apps_cdn_upload_test.rb b/fastlane/test/studio_apps_cdn_upload_test.rb new file mode 100644 index 0000000000..f28e58a1d9 --- /dev/null +++ b/fastlane/test/studio_apps_cdn_upload_test.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +# Sanity checks for fastlane/lib/studio_apps_cdn_upload.rb. +# +# Run with: `ruby fastlane/test/studio_apps_cdn_upload_test.rb` +# (No bundle / fastlane required — minitest ships with stdlib Ruby.) + +require 'minitest/autorun' +require_relative '../lib/studio_apps_cdn_upload' + +class StudioAppsCdnUploadTest < Minitest::Test + def test_nightly_full_install_is_internal + assert_equal 'internal', StudioAppsCdnUpload.visibility_for(build_type: 'Nightly', install_type: 'Full Install') + end + + def test_nightly_update_is_external + assert_equal 'external', StudioAppsCdnUpload.visibility_for(build_type: 'Nightly', install_type: 'Update') + end + + def test_beta_full_install_is_external + assert_equal 'external', StudioAppsCdnUpload.visibility_for(build_type: 'Beta', install_type: 'Full Install') + end + + def test_beta_update_is_external + assert_equal 'external', StudioAppsCdnUpload.visibility_for(build_type: 'Beta', install_type: 'Update') + end + + def test_production_full_install_is_external + assert_equal 'external', StudioAppsCdnUpload.visibility_for(build_type: 'Production', install_type: 'Full Install') + end + + def test_production_update_is_external + assert_equal 'external', StudioAppsCdnUpload.visibility_for(build_type: 'Production', install_type: 'Update') + end +end