From b71df93460f6278de718f1fd330be356f050bc36 Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Tue, 24 Mar 2026 18:11:17 +1100 Subject: [PATCH 1/5] Add XCFramework build script and Make target MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Archives `GutenbergKitResources` for device and simulator, assembles an XCFramework with resource bundles and dSYMs, then outputs a zip with SPM checksum. The `.o` → dylib linking step is necessary because SPM produces a static object file; without it, `BundleFinder` gets statically linked into the consuming app and `Bundle(for:)` resolves to the wrong bundle at runtime. --- Generated with the help of Claude Code, https://claude.ai/code Co-Authored-By: Claude Code Opus 4.6 --- Makefile | 5 ++ build_xcframework.sh | 207 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 212 insertions(+) create mode 100755 build_xcframework.sh diff --git a/Makefile b/Makefile index ab4f6560c..3773d744d 100644 --- a/Makefile +++ b/Makefile @@ -122,6 +122,11 @@ copy-dist-android: build-swift-package: build ## Build the Swift package for iOS $(call XCODEBUILD_CMD, build, GutenbergKit) +.PHONY: build-resources-xcframework +build-resources-xcframework: build ## Build GutenbergKitResources XCFramework + @echo "--- :swift: Building GutenbergKitResources XCFramework" + ./build_xcframework.sh + .PHONY: local-android-library local-android-library: build ## Build the Android library to local Maven @echo "--- :android: Building Library" diff --git a/build_xcframework.sh b/build_xcframework.sh new file mode 100755 index 000000000..d12aadfca --- /dev/null +++ b/build_xcframework.sh @@ -0,0 +1,207 @@ +#!/usr/bin/env bash + +# Builds a GutenbergKitResources XCFramework from the local source target. +# +# Prerequisites: +# - Built web assets in ios/Sources/GutenbergKitResources/Gutenberg/ +# - Package.swift must have `resourcesMode` set to `.local` +# +# Output: +# - GutenbergKitResources-.xcframework.zip +# - GutenbergKitResources-.xcframework.zip.checksum.txt +# +# Adapted from: +# https://github.com/OpenSwiftUIProject/ProtobufKit/blob/937eae542/Scripts/build_xcframework.sh +# https://docs.emergetools.com/docs/analyzing-a-spm-framework-ios + +set -euo pipefail + +SCHEME="GutenbergKitResources" +MINIMUM_IOS_VERSION="17.0" +BUILD_DIR="$(pwd)/build" +OUTPUT_DIR="${1:-$(pwd)}" +DERIVED_DATA_PATH="${BUILD_DIR}/DerivedData" + +GIT_SHA="$(git rev-parse HEAD)" +XCFRAMEWORK_NAME="${SCHEME}-${GIT_SHA}.xcframework" +ZIP_NAME="${XCFRAMEWORK_NAME}.zip" + +link_dylib() { + local object_file="$1" + local output="$2" + local sdk="$3" + + local sdk_path + sdk_path="$(xcrun --sdk "${sdk}" --show-sdk-path)" + local install_name="@rpath/${SCHEME}.framework/${SCHEME}" + + local archs + archs=$(xcrun lipo -archs "${object_file}") + + local arch_count + arch_count=$(echo "${archs}" | wc -w | tr -d ' ') + + local dylibs=() + for arch in ${archs}; do + local thin_o="${BUILD_DIR}/${SCHEME}-${sdk}-${arch}.o" + local thin_dylib="${BUILD_DIR}/${SCHEME}-${sdk}-${arch}.dylib" + + if [[ "${arch_count}" -eq 1 ]]; then + thin_o="${object_file}" + else + xcrun lipo "${object_file}" -thin "${arch}" -output "${thin_o}" + fi + + local target="${arch}-apple-ios${MINIMUM_IOS_VERSION}" + if [[ "${sdk}" == "iphonesimulator" ]]; then + target="${target}-simulator" + fi + + xcrun clang -target "${target}" -dynamiclib \ + -install_name "${install_name}" \ + -o "${thin_dylib}" \ + "${thin_o}" \ + -isysroot "${sdk_path}" \ + -L "${sdk_path}/usr/lib/swift" \ + -L "${sdk_path}/usr/lib" + + dylibs+=("${thin_dylib}") + done + + if [[ ${#dylibs[@]} -eq 1 ]]; then + cp "${dylibs[0]}" "${output}" + else + xcrun lipo -create "${dylibs[@]}" -output "${output}" + fi +} + +build_framework() { + local sdk="$1" + local destination="$2" + local archive_path="${BUILD_DIR}/${SCHEME}-${sdk}.xcarchive" + + echo "--- Building ${SCHEME} for ${sdk}" + + rm -rf "${archive_path}" + + xcodebuild archive \ + -scheme "${SCHEME}" \ + -archivePath "${archive_path}" \ + -derivedDataPath "${DERIVED_DATA_PATH}" \ + -sdk "${sdk}" \ + -destination "${destination}" \ + BUILD_LIBRARY_FOR_DISTRIBUTION=YES \ + INSTALL_PATH='Library/Frameworks' \ + OTHER_SWIFT_FLAGS=-no-verify-emitted-module-interface \ + CODE_SIGN_IDENTITY="-" \ + CODE_SIGNING_REQUIRED=NO \ + CODE_SIGNING_ALLOWED=NO \ + | xcbeautify + + # SPM archives don't produce a .framework — assemble it from DerivedData + local intermediates="${DERIVED_DATA_PATH}/Build/Intermediates.noindex/ArchiveIntermediates/${SCHEME}" + local build_products="${intermediates}/BuildProductsPath/Release-${sdk}" + local generated_maps="${intermediates}/IntermediateBuildFilesPath/GeneratedModuleMaps-${sdk}" + + local framework_path="${BUILD_DIR}/frameworks/${sdk}/${SCHEME}.framework" + rm -rf "${framework_path}" + mkdir -p "${framework_path}/Modules" + mkdir -p "${framework_path}/Headers" + + # Binary: SPM produces a .o object file — link it into a dylib so that + # the resource_bundle_accessor's BundleFinder class stays inside the + # framework at runtime (otherwise it gets statically linked into the + # consuming app binary and Bundle(for:) resolves to the wrong bundle). + link_dylib "${build_products}/${SCHEME}.o" "${framework_path}/${SCHEME}" "${sdk}" + + # Swift module + cp -r "${build_products}/${SCHEME}.swiftmodule" "${framework_path}/Modules/${SCHEME}.swiftmodule" + rm -f "${framework_path}/Modules/${SCHEME}.swiftmodule"/*.package.swiftinterface + rm -f "${framework_path}/Modules/${SCHEME}.swiftmodule"/*.private.swiftinterface + + # Module map and generated header + cp "${generated_maps}/${SCHEME}.modulemap" "${framework_path}/Modules/module.modulemap" + cp "${generated_maps}/${SCHEME}-Swift.h" "${framework_path}/Headers/${SCHEME}-Swift.h" + + # Minimal Info.plist + cat > "${framework_path}/Info.plist" < + + + + CFBundleExecutable + ${SCHEME} + CFBundleIdentifier + org.wordpress.GutenbergKitResources + CFBundleName + ${SCHEME} + CFBundlePackageType + FMWK + + +PLIST +} + +copy_resource_bundles() { + local sdk="$1" + local framework_path="${BUILD_DIR}/frameworks/${sdk}/${SCHEME}.framework" + local bundle_path="${DERIVED_DATA_PATH}/Build/Intermediates.noindex/ArchiveIntermediates/${SCHEME}/IntermediateBuildFilesPath/UninstalledProducts/${sdk}" + + echo "--- Copying resource bundles for ${sdk}" + + if [ -d "${bundle_path}" ]; then + find "${bundle_path}" -name "*.bundle" -maxdepth 1 -type d -print0 | while IFS= read -r -d '' bundle; do + bundle_name=$(basename "${bundle}") + echo " ${bundle_name} -> ${framework_path}/" + rm -rf "${framework_path:?}/${bundle_name}" + cp -R "${bundle}" "${framework_path}/" + done + else + echo " Warning: bundle path not found: ${bundle_path}" + fi +} + +# Build for both platforms +build_framework "iphoneos" "generic/platform=iOS" +copy_resource_bundles "iphoneos" + +build_framework "iphonesimulator" "generic/platform=iOS Simulator" +copy_resource_bundles "iphonesimulator" + +# Create XCFramework +echo "--- Creating XCFramework" + +DEVICE_FRAMEWORK="${BUILD_DIR}/frameworks/iphoneos/${SCHEME}.framework" +SIM_FRAMEWORK="${BUILD_DIR}/frameworks/iphonesimulator/${SCHEME}.framework" +XCFRAMEWORK_PATH="${BUILD_DIR}/${XCFRAMEWORK_NAME}" + +rm -rf "${XCFRAMEWORK_PATH}" +xcodebuild -create-xcframework \ + -framework "${DEVICE_FRAMEWORK}" \ + -framework "${SIM_FRAMEWORK}" \ + -output "${XCFRAMEWORK_PATH}" + +# Copy dSYMs into xcframework slices +DEVICE_DSYMS="${BUILD_DIR}/${SCHEME}-iphoneos.xcarchive/dSYMs" +SIM_DSYMS="${BUILD_DIR}/${SCHEME}-iphonesimulator.xcarchive/dSYMs" + +if [ -d "${DEVICE_DSYMS}" ]; then + cp -r "${DEVICE_DSYMS}" "${XCFRAMEWORK_PATH}/ios-arm64/" +fi +if [ -d "${SIM_DSYMS}" ]; then + cp -r "${SIM_DSYMS}" "${XCFRAMEWORK_PATH}/ios-arm64_x86_64-simulator/" +fi + +# Create ZIP archive +echo "--- Creating ZIP archive" +(cd "${BUILD_DIR}" && zip -r "${ZIP_NAME}" "$(basename "${XCFRAMEWORK_PATH}")" > /dev/null) +cp "${BUILD_DIR}/${ZIP_NAME}" "${OUTPUT_DIR}/${ZIP_NAME}" + +# Compute checksum +echo "--- Computing checksum" +CHECKSUM=$(swift package compute-checksum "${OUTPUT_DIR}/${ZIP_NAME}") +echo "${CHECKSUM}" > "${OUTPUT_DIR}/${ZIP_NAME}.checksum.txt" + +echo "" +echo "XCFramework: ${OUTPUT_DIR}/${ZIP_NAME}" +echo "Checksum: ${CHECKSUM}" From 3f351549b185912afb7a32f1c4c7438e870f171d Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Wed, 25 Mar 2026 10:59:34 +1100 Subject: [PATCH 2/5] Add Fastlane for S3 publish and signing Scoped to `publish_to_s3` and `xcframework_sign` lanes. Release orchestration lanes (`release`, `validate`, `update_swift_package`, `publish_release_to_github`) are deferred to a follow-up once the full CI release flow is designed. --- Generated with the help of Claude Code, https://claude.ai/code Co-Authored-By: Claude Code Opus 4.6 --- .gitignore | 3 + Gemfile | 6 + Gemfile.lock | 434 ++++++++++++++++++++++++++++++++++++++++++++++ fastlane/Fastfile | 100 +++++++++++ 4 files changed, 543 insertions(+) create mode 100644 Gemfile create mode 100644 Gemfile.lock create mode 100644 fastlane/Fastfile diff --git a/.gitignore b/.gitignore index cd573e6b2..f1f86eba6 100644 --- a/.gitignore +++ b/.gitignore @@ -44,6 +44,9 @@ playground.xcworkspace # Ruby tooling vendor/bundle +# Fastlane +fastlane/report.xml + # Logs logs *.log diff --git a/Gemfile b/Gemfile new file mode 100644 index 000000000..e0688b0f4 --- /dev/null +++ b/Gemfile @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +source 'https://rubygems.org' + +gem 'fastlane', '~> 2.230' +gem 'fastlane-plugin-wpmreleasetoolkit', '~> 13.8' diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 000000000..c36d4f570 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,434 @@ +GEM + remote: https://rubygems.org/ + specs: + CFPropertyList (3.0.8) + abbrev (0.1.2) + activesupport (8.1.2) + base64 + bigdecimal + concurrent-ruby (~> 1.0, >= 1.3.1) + connection_pool (>= 2.2.5) + drb + i18n (>= 1.6, < 2) + json + logger (>= 1.4.2) + minitest (>= 5.1) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) + uri (>= 0.13.1) + addressable (2.8.9) + public_suffix (>= 2.0.2, < 8.0) + artifactory (3.0.17) + atomos (0.1.3) + aws-eventstream (1.4.0) + aws-partitions (1.1220.0) + aws-sdk-core (3.242.0) + aws-eventstream (~> 1, >= 1.3.0) + aws-partitions (~> 1, >= 1.992.0) + aws-sigv4 (~> 1.9) + base64 + bigdecimal + jmespath (~> 1, >= 1.6.1) + logger + aws-sdk-kms (1.122.0) + aws-sdk-core (~> 3, >= 3.241.4) + aws-sigv4 (~> 1.5) + aws-sdk-s3 (1.213.0) + aws-sdk-core (~> 3, >= 3.241.4) + aws-sdk-kms (~> 1) + aws-sigv4 (~> 1.5) + aws-sigv4 (1.12.1) + aws-eventstream (~> 1, >= 1.0.2) + babosa (1.0.4) + base64 (0.2.0) + benchmark (0.5.0) + bigdecimal (4.0.1) + buildkit (1.6.1) + sawyer (>= 0.6) + chroma (0.2.0) + claide (1.1.0) + colored (1.2) + colored2 (3.1.2) + commander (4.6.0) + highline (~> 2.0.0) + concurrent-ruby (1.3.6) + connection_pool (3.0.2) + csv (3.3.5) + declarative (0.0.20) + diffy (3.4.4) + digest-crc (0.7.0) + rake (>= 12.0.0, < 14.0.0) + domain_name (0.6.20240107) + dotenv (2.8.1) + drb (2.2.3) + emoji_regex (3.2.3) + excon (0.112.0) + faraday (1.10.5) + faraday-em_http (~> 1.0) + faraday-em_synchrony (~> 1.0) + faraday-excon (~> 1.1) + faraday-httpclient (~> 1.0) + faraday-multipart (~> 1.0) + faraday-net_http (~> 1.0) + faraday-net_http_persistent (~> 1.0) + faraday-patron (~> 1.0) + faraday-rack (~> 1.0) + faraday-retry (~> 1.0) + ruby2_keywords (>= 0.0.4) + faraday-cookie_jar (0.0.8) + faraday (>= 0.8.0) + http-cookie (>= 1.0.0) + faraday-em_http (1.0.0) + faraday-em_synchrony (1.0.1) + faraday-excon (1.1.0) + faraday-httpclient (1.0.1) + faraday-multipart (1.2.0) + multipart-post (~> 2.0) + faraday-net_http (1.0.2) + faraday-net_http_persistent (1.2.0) + faraday-patron (1.0.0) + faraday-rack (1.0.0) + faraday-retry (1.0.3) + faraday_middleware (1.2.1) + faraday (~> 1.0) + fastimage (2.4.0) + fastlane (2.232.2) + CFPropertyList (>= 2.3, < 4.0.0) + abbrev (~> 0.1.2) + addressable (>= 2.8, < 3.0.0) + artifactory (~> 3.0) + aws-sdk-s3 (~> 1.197) + babosa (>= 1.0.3, < 2.0.0) + base64 (~> 0.2.0) + benchmark (>= 0.1.0) + bundler (>= 1.17.3, < 5.0.0) + colored (~> 1.2) + commander (~> 4.6) + csv (~> 3.3) + dotenv (>= 2.1.1, < 3.0.0) + emoji_regex (>= 0.1, < 4.0) + excon (>= 0.71.0, < 1.0.0) + faraday (~> 1.0) + faraday-cookie_jar (~> 0.0.6) + faraday_middleware (~> 1.0) + fastimage (>= 2.1.0, < 3.0.0) + fastlane-sirp (>= 1.0.0) + gh_inspector (>= 1.1.2, < 2.0.0) + google-apis-androidpublisher_v3 (~> 0.3) + google-apis-playcustomapp_v1 (~> 0.1) + google-cloud-env (>= 1.6.0, <= 2.1.1) + google-cloud-storage (~> 1.31) + highline (~> 2.0) + http-cookie (~> 1.0.5) + json (< 3.0.0) + jwt (>= 2.1.0, < 3) + logger (>= 1.6, < 2.0) + mini_magick (>= 4.9.4, < 5.0.0) + multipart-post (>= 2.0.0, < 3.0.0) + mutex_m (~> 0.3.0) + naturally (~> 2.2) + nkf (~> 0.2.0) + optparse (>= 0.1.1, < 1.0.0) + ostruct (>= 0.1.0) + plist (>= 3.1.0, < 4.0.0) + rubyzip (>= 2.0.0, < 3.0.0) + security (= 0.1.5) + simctl (~> 1.6.3) + terminal-notifier (>= 2.0.0, < 3.0.0) + terminal-table (~> 3) + tty-screen (>= 0.6.3, < 1.0.0) + tty-spinner (>= 0.8.0, < 1.0.0) + word_wrap (~> 1.0.0) + xcodeproj (>= 1.13.0, < 2.0.0) + xcpretty (~> 0.4.1) + xcpretty-travis-formatter (>= 0.0.3, < 2.0.0) + fastlane-plugin-wpmreleasetoolkit (13.8.1) + activesupport (>= 6.1.7.1) + buildkit (~> 1.5) + chroma (= 0.2.0) + diffy (~> 3.3) + fastlane (~> 2.213) + git (~> 1.3) + google-cloud-storage (~> 1.31) + java-properties (~> 0.3.0) + nokogiri (~> 1.11) + octokit (~> 6.1) + parallel (~> 1.14) + plist (~> 3.1) + progress_bar (~> 1.3) + rake (>= 12.3, < 14.0) + rake-compiler (~> 1.0) + xcodeproj (~> 1.22) + fastlane-sirp (1.0.0) + sysrandom (~> 1.0) + gh_inspector (1.1.3) + git (1.19.1) + addressable (~> 2.8) + rchardet (~> 1.8) + google-apis-androidpublisher_v3 (0.96.0) + google-apis-core (>= 0.15.0, < 2.a) + google-apis-core (0.18.0) + addressable (~> 2.5, >= 2.5.1) + googleauth (~> 1.9) + httpclient (>= 2.8.3, < 3.a) + mini_mime (~> 1.0) + mutex_m + representable (~> 3.0) + retriable (>= 2.0, < 4.a) + google-apis-iamcredentials_v1 (0.26.0) + google-apis-core (>= 0.15.0, < 2.a) + google-apis-playcustomapp_v1 (0.17.0) + google-apis-core (>= 0.15.0, < 2.a) + google-apis-storage_v1 (0.61.0) + google-apis-core (>= 0.15.0, < 2.a) + google-cloud-core (1.8.0) + google-cloud-env (>= 1.0, < 3.a) + google-cloud-errors (~> 1.0) + google-cloud-env (2.1.1) + faraday (>= 1.0, < 3.a) + google-cloud-errors (1.5.0) + google-cloud-storage (1.58.0) + addressable (~> 2.8) + digest-crc (~> 0.4) + google-apis-core (>= 0.18, < 2) + google-apis-iamcredentials_v1 (~> 0.18) + google-apis-storage_v1 (>= 0.42) + google-cloud-core (~> 1.6) + googleauth (~> 1.9) + mini_mime (~> 1.0) + googleauth (1.11.2) + faraday (>= 1.0, < 3.a) + google-cloud-env (~> 2.1) + jwt (>= 1.4, < 3.0) + multi_json (~> 1.11) + os (>= 0.9, < 2.0) + signet (>= 0.16, < 2.a) + highline (2.0.3) + http-cookie (1.0.8) + domain_name (~> 0.5) + httpclient (2.9.0) + mutex_m + i18n (1.14.8) + concurrent-ruby (~> 1.0) + java-properties (0.3.0) + jmespath (1.6.2) + json (2.18.1) + jwt (2.10.2) + base64 + logger (1.7.0) + mini_magick (4.13.2) + mini_mime (1.1.5) + mini_portile2 (2.8.9) + minitest (6.0.2) + drb (~> 2.0) + prism (~> 1.5) + multi_json (1.19.1) + multipart-post (2.4.1) + mutex_m (0.3.0) + nanaimo (0.4.0) + naturally (2.3.0) + nkf (0.2.0) + nokogiri (1.19.1) + mini_portile2 (~> 2.8.2) + racc (~> 1.4) + octokit (6.1.1) + faraday (>= 1, < 3) + sawyer (~> 0.9) + options (2.3.2) + optparse (0.8.1) + os (1.1.4) + ostruct (0.6.3) + parallel (1.27.0) + plist (3.7.2) + prism (1.9.0) + progress_bar (1.3.4) + highline (>= 1.6) + options (~> 2.3.0) + public_suffix (7.0.5) + racc (1.8.1) + rake (13.3.1) + rake-compiler (1.3.1) + rake + rchardet (1.10.0) + representable (3.2.0) + declarative (< 0.1.0) + trailblazer-option (>= 0.1.1, < 0.2.0) + uber (< 0.2.0) + retriable (3.2.1) + rexml (3.4.4) + rouge (3.28.0) + ruby2_keywords (0.0.5) + rubyzip (2.4.1) + sawyer (0.9.3) + addressable (>= 2.3.5) + faraday (>= 0.17.3, < 3) + securerandom (0.4.1) + security (0.1.5) + signet (0.21.0) + addressable (~> 2.8) + faraday (>= 0.17.5, < 3.a) + jwt (>= 1.5, < 4.0) + multi_json (~> 1.10) + simctl (1.6.10) + CFPropertyList + naturally + sysrandom (1.0.5) + terminal-notifier (2.0.0) + terminal-table (3.0.2) + unicode-display_width (>= 1.1.1, < 3) + trailblazer-option (0.1.2) + tty-cursor (0.7.1) + tty-screen (0.8.2) + tty-spinner (0.9.3) + tty-cursor (~> 0.7) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + uber (0.1.0) + unicode-display_width (2.6.0) + uri (1.1.1) + word_wrap (1.0.0) + xcodeproj (1.27.0) + CFPropertyList (>= 2.3.3, < 4.0) + atomos (~> 0.1.3) + claide (>= 1.0.2, < 2.0) + colored2 (~> 3.1) + nanaimo (~> 0.4.0) + rexml (>= 3.3.6, < 4.0) + xcpretty (0.4.1) + rouge (~> 3.28.0) + xcpretty-travis-formatter (1.0.1) + xcpretty (~> 0.2, >= 0.0.7) + +PLATFORMS + ruby + +DEPENDENCIES + fastlane (~> 2.230) + fastlane-plugin-wpmreleasetoolkit (~> 13.8) + +CHECKSUMS + CFPropertyList (3.0.8) sha256=2c99d0d980536d3d7ab252f7bd59ac8be50fbdd1ff487c98c949bb66bb114261 + abbrev (0.1.2) sha256=ad1b4eaaaed4cb722d5684d63949e4bde1d34f2a95e20db93aecfe7cbac74242 + activesupport (8.1.2) sha256=88842578ccd0d40f658289b0e8c842acfe9af751afee2e0744a7873f50b6fdae + addressable (2.8.9) sha256=cc154fcbe689711808a43601dee7b980238ce54368d23e127421753e46895485 + artifactory (3.0.17) sha256=3023d5c964c31674090d655a516f38ca75665c15084140c08b7f2841131af263 + atomos (0.1.3) sha256=7d43b22f2454a36bace5532d30785b06de3711399cb1c6bf932573eda536789f + aws-eventstream (1.4.0) sha256=116bf85c436200d1060811e6f5d2d40c88f65448f2125bc77ffce5121e6e183b + aws-partitions (1.1220.0) sha256=1567da9ae45cba28e1d31f5e996928b2eb92ad01700000846d6d90043be8670f + aws-sdk-core (3.242.0) sha256=c17b3003acc78d80c1a8437b285a1cfc5e4d7749ce7821cf3071e847535a29a0 + aws-sdk-kms (1.122.0) sha256=47ce3f51b26bd7d76f1270cfdfca17b40073ecd3219c8c9400788712abfb4eb8 + aws-sdk-s3 (1.213.0) sha256=af596ccf544582406db610e95cc9099276eaf03142f57a2f30f76940e598e50d + aws-sigv4 (1.12.1) sha256=6973ff95cb0fd0dc58ba26e90e9510a2219525d07620c8babeb70ef831826c00 + babosa (1.0.4) sha256=18dea450f595462ed7cb80595abd76b2e535db8c91b350f6c4b3d73986c5bc99 + base64 (0.2.0) sha256=0f25e9b21a02a0cc0cea8ef92b2041035d39350946e8789c562b2d1a3da01507 + benchmark (0.5.0) sha256=465df122341aedcb81a2a24b4d3bd19b6c67c1530713fd533f3ff034e419236c + bigdecimal (4.0.1) sha256=8b07d3d065a9f921c80ceaea7c9d4ae596697295b584c296fe599dd0ad01c4a7 + buildkit (1.6.1) sha256=e0e51c0ce3334654c356bdef3410cba5c9b36b336d0d6542f3ecfb477c5fa97c + chroma (0.2.0) sha256=64bdcd36a4765fbcd45adc64960cc153101300b4918f90ffdd89f4e2eb954b54 + claide (1.1.0) sha256=6d3c5c089dde904d96aa30e73306d0d4bd444b1accb9b3125ce14a3c0183f82e + colored (1.2) sha256=9d82b47ac589ce7f6cab64b1f194a2009e9fd00c326a5357321f44afab2c1d2c + colored2 (3.1.2) sha256=b13c2bd7eeae2cf7356a62501d398e72fde78780bd26aec6a979578293c28b4a + commander (4.6.0) sha256=7d1ddc3fccae60cc906b4131b916107e2ef0108858f485fdda30610c0f2913d9 + concurrent-ruby (1.3.6) sha256=6b56837e1e7e5292f9864f34b69c5a2cbc75c0cf5338f1ce9903d10fa762d5ab + connection_pool (3.0.2) sha256=33fff5ba71a12d2aa26cb72b1db8bba2a1a01823559fb01d29eb74c286e62e0a + csv (3.3.5) sha256=6e5134ac3383ef728b7f02725d9872934f523cb40b961479f69cf3afa6c8e73f + declarative (0.0.20) sha256=8021dd6cb17ab2b61233c56903d3f5a259c5cf43c80ff332d447d395b17d9ff9 + diffy (3.4.4) sha256=79384ab5ca82d0e115b2771f0961e27c164c456074bd2ec46b637ebf7b6e47e3 + digest-crc (0.7.0) sha256=64adc23a26a241044cbe6732477ca1b3c281d79e2240bcff275a37a5a0d78c07 + domain_name (0.6.20240107) sha256=5f693b2215708476517479bf2b3802e49068ad82167bcd2286f899536a17d933 + dotenv (2.8.1) sha256=c5944793349ae03c432e1780a2ca929d60b88c7d14d52d630db0508c3a8a17d8 + drb (2.2.3) sha256=0b00d6fdb50995fe4a45dea13663493c841112e4068656854646f418fda13373 + emoji_regex (3.2.3) sha256=ecd8be856b7691406c6bf3bb3a5e55d6ed683ffab98b4aa531bb90e1ddcc564b + excon (0.112.0) sha256=daf9ac3a4c2fc9aa48383a33da77ecb44fa395111e973084d5c52f6f214ae0f0 + faraday (1.10.5) sha256=b144f1d2b045652fa820b5f532723e1643cc28b93dae911d784e5c5f88e8f6ed + faraday-cookie_jar (0.0.8) sha256=0140605823f8cc63c7028fccee486aaed8e54835c360cffc1f7c8c07c4299dbb + faraday-em_http (1.0.0) sha256=7a3d4c7079789121054f57e08cd4ef7e40ad1549b63101f38c7093a9d6c59689 + faraday-em_synchrony (1.0.1) sha256=bf3ce45dcf543088d319ab051f80985ea6d294930635b7a0b966563179f81750 + faraday-excon (1.1.0) sha256=b055c842376734d7f74350fe8611542ae2000c5387348d9ba9708109d6e40940 + faraday-httpclient (1.0.1) sha256=4c8ff1f0973ff835be8d043ef16aaf54f47f25b7578f6d916deee8399a04d33b + faraday-multipart (1.2.0) sha256=7d89a949693714176f612323ca13746a2ded204031a6ba528adee788694ef757 + faraday-net_http (1.0.2) sha256=63992efea42c925a20818cf3c0830947948541fdcf345842755510d266e4c682 + faraday-net_http_persistent (1.2.0) sha256=0b0cbc8f03dab943c3e1cc58d8b7beb142d9df068b39c718cd83e39260348335 + faraday-patron (1.0.0) sha256=dc2cd7b340bb3cc8e36bcb9e6e7eff43d134b6d526d5f3429c7a7680ddd38fa7 + faraday-rack (1.0.0) sha256=ef60ec969a2bb95b8dbf24400155aee64a00fc8ba6c6a4d3968562bcc92328c0 + faraday-retry (1.0.3) sha256=add154f4f399243cbe070806ed41b96906942e7f5259bb1fe6daf2ec8f497194 + faraday_middleware (1.2.1) sha256=d45b78c8ee864c4783fbc276f845243d4a7918a67301c052647bacabec0529e9 + fastimage (2.4.0) sha256=5fce375e27d3bdbb46c18dbca6ba9af29d3304801ae1eb995771c4796c5ac7e8 + fastlane (2.232.2) sha256=978689f60f0fc3d54699de86ef12be4eda9f5b52217c1798965257c390d2b112 + fastlane-plugin-wpmreleasetoolkit (13.8.1) sha256=88a0639a05bd02c915eb2ad7a29a0baf566b8900011a6a6524cb944933323619 + fastlane-sirp (1.0.0) sha256=66478f25bcd039ec02ccf65625373fca29646fa73d655eb533c915f106c5e641 + gh_inspector (1.1.3) sha256=04cca7171b87164e053aa43147971d3b7f500fcb58177698886b48a9fc4a1939 + git (1.19.1) sha256=b0a422d9f6517353c48a330d6114de4db9e0c82dbe7202964a1d9f1fbc827d70 + google-apis-androidpublisher_v3 (0.96.0) sha256=9e27b03295fdd2c4a67b5e4d11f891492c89f73beff4a3f9323419165a56d01c + google-apis-core (0.18.0) sha256=96b057816feeeab448139ed5b5c78eab7fc2a9d8958f0fbc8217dedffad054ee + google-apis-iamcredentials_v1 (0.26.0) sha256=3ff70a10a1d6cddf2554e95b7c5df2c26afdeaeb64100048a355194da19e48a3 + google-apis-playcustomapp_v1 (0.17.0) sha256=d5bc90b705f3f862bab4998086449b0abe704ee1685a84821daa90ca7fa95a78 + google-apis-storage_v1 (0.61.0) sha256=b330e599b58e6a01533c189525398d6dbdbaf101ffb0c60145940b57e1c982e8 + google-cloud-core (1.8.0) sha256=e572edcbf189cfcab16590628a516cec3f4f63454b730e59f0b36575120281cf + google-cloud-env (2.1.1) sha256=cf4bb8c7d517ee1ea692baedf06e0b56ce68007549d8d5a66481aa9f97f46999 + google-cloud-errors (1.5.0) sha256=b56be28b8c10628125214dde571b925cfcebdbc58619e598250c37a2114f7b4b + google-cloud-storage (1.58.0) sha256=1bedc07a9c75af169e1ede1dd306b9f941f9ffa9e7095d0364c0803c468fdffd + googleauth (1.11.2) sha256=7e6bacaeed7aea3dd66dcea985266839816af6633e9f5983c3c2e0e40a44731e + highline (2.0.3) sha256=2ddd5c127d4692721486f91737307236fe005352d12a4202e26c48614f719479 + http-cookie (1.0.8) sha256=b14fe0445cf24bf9ae098633e9b8d42e4c07c3c1f700672b09fbfe32ffd41aa6 + httpclient (2.9.0) sha256=4b645958e494b2f86c2f8a2f304c959baa273a310e77a2931ddb986d83e498c8 + i18n (1.14.8) sha256=285778639134865c5e0f6269e0b818256017e8cde89993fdfcbfb64d088824a5 + java-properties (0.3.0) sha256=0a9fdda90c25ba9ba4de0e242d954a5688629652b592aab66ed54e2b16b93093 + jmespath (1.6.2) sha256=238d774a58723d6c090494c8879b5e9918c19485f7e840f2c1c7532cf84ebcb1 + json (2.18.1) sha256=fe112755501b8d0466b5ada6cf50c8c3f41e897fa128ac5d263ec09eedc9f986 + jwt (2.10.2) sha256=31e1ee46f7359883d5e622446969fe9c118c3da87a0b1dca765ce269c3a0c4f4 + logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203 + mini_magick (4.13.2) sha256=71d6258e0e8a3d04a9a0a09784d5d857b403a198a51dd4f882510435eb95ddd9 + mini_mime (1.1.5) sha256=8681b7e2e4215f2a159f9400b5816d85e9d8c6c6b491e96a12797e798f8bccef + mini_portile2 (2.8.9) sha256=0cd7c7f824e010c072e33f68bc02d85a00aeb6fce05bb4819c03dfd3c140c289 + minitest (6.0.2) sha256=db6e57956f6ecc6134683b4c87467d6dd792323c7f0eea7b93f66bd284adbc3d + multi_json (1.19.1) sha256=7aefeff8f2c854bf739931a238e4aea64592845e0c0395c8a7d2eea7fdd631b7 + multipart-post (2.4.1) sha256=9872d03a8e552020ca096adadbf5e3cb1cd1cdd6acd3c161136b8a5737cdb4a8 + mutex_m (0.3.0) sha256=cfcb04ac16b69c4813777022fdceda24e9f798e48092a2b817eb4c0a782b0751 + nanaimo (0.4.0) sha256=faf069551bab17f15169c1f74a1c73c220657e71b6e900919897a10d991d0723 + naturally (2.3.0) sha256=459923cf76c2e6613048301742363200c3c7e4904c324097d54a67401e179e01 + nkf (0.2.0) sha256=fbc151bda025451f627fafdfcb3f4f13d0b22ae11f58c6d3a2939c76c5f5f126 + nokogiri (1.19.1) sha256=598b327f36df0b172abd57b68b18979a6e14219353bca87180c31a51a00d5ad3 + octokit (6.1.1) sha256=920e4a9d820205f70738f58de6a7e6ef0e2f25b27db954b5806a63105207b0bf + options (2.3.2) sha256=32413a4b9e363234eed2eecfb2a1a9deb32810f72c54820a37a62f65b905c5e8 + optparse (0.8.1) sha256=42bea10d53907ccff4f080a69991441d611fbf8733b60ed1ce9ee365ce03bd1a + os (1.1.4) sha256=57816d6a334e7bd6aed048f4b0308226c5fb027433b67d90a9ab435f35108d3f + ostruct (0.6.3) sha256=95a2ed4a4bd1d190784e666b47b2d3f078e4a9efda2fccf18f84ddc6538ed912 + parallel (1.27.0) sha256=4ac151e1806b755fb4e2dc2332cbf0e54f2e24ba821ff2d3dcf86bf6dc4ae130 + plist (3.7.2) sha256=d37a4527cc1116064393df4b40e1dbbc94c65fa9ca2eec52edf9a13616718a42 + prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85 + progress_bar (1.3.4) sha256=adb10e040275e08eadfbe405749584e4b01fd15e8e692fdcb4b1969e9c071c8c + public_suffix (7.0.5) sha256=1a8bb08f1bbea19228d3bed6e5ed908d1cb4f7c2726d18bd9cadf60bc676f623 + racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f + rake (13.3.1) sha256=8c9e89d09f66a26a01264e7e3480ec0607f0c497a861ef16063604b1b08eb19c + rake-compiler (1.3.1) sha256=6b351612b6e2d73ddd5563ee799bb58685176e05363db6758504bd11573d670a + rchardet (1.10.0) sha256=d5ea2ed61a720a220f1914778208e718a0c7ed2a484b6d357ba695aa7001390f + representable (3.2.0) sha256=cc29bf7eebc31653586849371a43ffe36c60b54b0a6365b5f7d95ec34d1ebace + retriable (3.2.1) sha256=26e87a33391fae4c382d4750f1e135e4dda7e5aa32b6b71f1992265981f9b991 + rexml (3.4.4) sha256=19e0a2c3425dfbf2d4fc1189747bdb2f849b6c5e74180401b15734bc97b5d142 + rouge (3.28.0) sha256=0d6de482c7624000d92697772ab14e48dca35629f8ddf3f4b21c99183fd70e20 + ruby2_keywords (0.0.5) sha256=ffd13740c573b7301cf7a2e61fc857b2a8e3d3aff32545d6f8300d8bae10e3ef + rubyzip (2.4.1) sha256=8577c88edc1fde8935eb91064c5cb1aef9ad5494b940cf19c775ee833e075615 + sawyer (0.9.3) sha256=0d0f19298408047037638639fe62f4794483fb04320269169bd41af2bdcf5e41 + securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1 + security (0.1.5) sha256=3a977a0eca7706e804c96db0dd9619e0a94969fe3aac9680fcfc2bf9b8a833b7 + signet (0.21.0) sha256=d617e9fbf24928280d39dcfefba9a0372d1c38187ffffd0a9283957a10a8cd5b + simctl (1.6.10) sha256=b99077f4d13ad81eace9f86bf5ba4df1b0b893a4d1b368bd3ed59b5b27f9236b + sysrandom (1.0.5) sha256=5ac1ac3c2ec64ef76ac91018059f541b7e8f437fbda1ccddb4f2c56a9ccf1e75 + terminal-notifier (2.0.0) sha256=7a0d2b2212ab9835c07f4b2e22a94cff64149dba1eed203c04835f7991078cea + terminal-table (3.0.2) sha256=f951b6af5f3e00203fb290a669e0a85c5dd5b051b3b023392ccfd67ba5abae91 + trailblazer-option (0.1.2) sha256=20e4f12ea4e1f718c8007e7944ca21a329eee4eed9e0fa5dde6e8ad8ac4344a3 + tty-cursor (0.7.1) sha256=79534185e6a777888d88628b14b6a1fdf5154a603f285f80b1753e1908e0bf48 + tty-screen (0.8.2) sha256=c090652115beae764336c28802d633f204fb84da93c6a968aa5d8e319e819b50 + tty-spinner (0.9.3) sha256=0e036f047b4ffb61f2aa45f5a770ec00b4d04130531558a94bfc5b192b570542 + tzinfo (2.0.6) sha256=8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b + uber (0.1.0) sha256=5beeb407ff807b5db994f82fa9ee07cfceaa561dad8af20be880bc67eba935dc + unicode-display_width (2.6.0) sha256=12279874bba6d5e4d2728cef814b19197dbb10d7a7837a869bab65da943b7f5a + uri (1.1.1) sha256=379fa58d27ffb1387eaada68c749d1426738bd0f654d812fcc07e7568f5c57c6 + word_wrap (1.0.0) sha256=f556d4224c812e371000f12a6ee8102e0daa724a314c3f246afaad76d82accc7 + xcodeproj (1.27.0) sha256=8cc7a73b4505c227deab044dce118ede787041c702bc47636856a2e566f854d3 + xcpretty (0.4.1) sha256=b14c50e721f6589ee3d6f5353e2c2cfcd8541fa1ea16d6c602807dd7327f3892 + xcpretty-travis-formatter (1.0.1) sha256=aacc332f17cb7b2cba222994e2adc74223db88724fe76341483ad3098e232f93 + +BUNDLED WITH + 4.0.6 diff --git a/fastlane/Fastfile b/fastlane/Fastfile new file mode 100644 index 000000000..57bc6147a --- /dev/null +++ b/fastlane/Fastfile @@ -0,0 +1,100 @@ +# frozen_string_literal: true + +PROJECT_ROOT = File.expand_path('..', __dir__) + +APPLE_TEAM_ID = 'PZYM8XX95Q' + +ASC_API_KEY_ENV_VARS = %w[ + APP_STORE_CONNECT_API_KEY_KEY_ID + APP_STORE_CONNECT_API_KEY_ISSUER_ID + APP_STORE_CONNECT_API_KEY_KEY +].freeze + +CODE_SIGNING_STORAGE_ENV_VARS = %w[ + MATCH_S3_ACCESS_KEY + MATCH_S3_SECRET_ACCESS_KEY +].freeze + +before_all do + setup_ci +end + +lane :publish_to_s3 do |options| + version = required_version!(options) + + upload_to_s3( + bucket: 'a8c-apps-public-artifacts', + key: File.join('gutenbergkit', version, 'GutenbergKitResources.xcframework.zip'), + file: xcframework_file_path, + auto_prefix: false, + if_exists: :fail + ) + + upload_to_s3( + bucket: 'a8c-apps-public-artifacts', + key: File.join('gutenbergkit', version, 'GutenbergKitResources.xcframework.zip.checksum.txt'), + file: xcframework_checksum_file_path, + auto_prefix: false, + if_exists: :fail + ) +end + +lane :xcframework_sign do + require_env_vars!(*ASC_API_KEY_ENV_VARS, *CODE_SIGNING_STORAGE_ENV_VARS) + + set_up_signing_release + + sh( + 'codesign', + '--timestamp', + '-s', 'Apple Distribution', + xcframework_file_path.sub('.zip', '') + ) +end + +desc 'Download the release (distribution) signing certificates' +lane :set_up_signing_release do |readonly: true| + require_env_vars!(*ASC_API_KEY_ENV_VARS, *CODE_SIGNING_STORAGE_ENV_VARS) + + sync_code_signing( + team_id: APPLE_TEAM_ID, + api_key: app_store_connect_api_key, + type: 'appstore', + storage_mode: 's3', + s3_region: 'us-east-2', + s3_bucket: 'a8c-fastlane-match', + readonly: readonly, + app_identifier: 'com.automattic.hostmgr', + platform: 'macos' + ) +end + +def xcframework_checksum + File.read(xcframework_checksum_file_path).strip +end + +def xcframework_checksum_file_path + Dir.glob(File.join(PROJECT_ROOT, 'GutenbergKitResources-*.xcframework.zip.checksum.txt')).first \ + || UI.user_error!('XCFramework checksum file not found. Run build_xcframework.sh first.') +end + +def xcframework_file_path + Dir.glob(File.join(PROJECT_ROOT, 'GutenbergKitResources-*.xcframework.zip')).first \ + || UI.user_error!('XCFramework zip not found. Run build_xcframework.sh first.') +end + +def required_version!(options) + version = options[:version].to_s + UI.user_error!('version is required') if version.empty? + version +end + +def require_env_vars!(*keys) + keys.each { |key| get_required_env!(key) } +end + +def get_required_env!(key) + return ENV.fetch(key) if ENV.key?(key) + + UI.user_error!("Environment variable `#{key}` is not set.") +end From f27b6bbdab7c901701e5b595c67731446521ff5c Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Wed, 25 Mar 2026 11:11:27 +1100 Subject: [PATCH 3/5] Add CI steps for XCFramework build and S3 The build step runs on every push, uploading the zip and checksum as Buildkite artifacts. The S3 publish step is gated on `NEW_VERSION` env var, matching the pattern Android already uses for on-demand release builds. --- Generated with the help of Claude Code, https://claude.ai/code Co-Authored-By: Claude Code Opus 4.6 --- .buildkite/pipeline.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index 8729445ce..a0fd49e16 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -67,6 +67,24 @@ steps: make test-swift-package plugins: *plugins + - label: ':xcode: Build XCFramework' + key: build-xcframework + depends_on: build-react + command: | + buildkite-agent artifact download dist.tar.gz . + tar -xzf dist.tar.gz + make build-resources-xcframework + artifact_paths: + - '*.xcframework.zip' + - '*.xcframework.zip.checksum.txt' + plugins: *plugins + + - label: ':s3: Publish XCFramework to S3' + if: build.env("NEW_VERSION") != null + depends_on: build-xcframework + command: bundle exec fastlane publish_to_s3 version:$NEW_VERSION + plugins: *plugins + - label: ':ios: Test iOS E2E' depends_on: build-react command: | From 919f5fd740ee079e34bdeb05e158c1fc2061a687 Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Wed, 25 Mar 2026 15:48:22 +1100 Subject: [PATCH 4/5] Wire codesigning into XCFramework build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split zip+checksum out of `build_xcframework.sh` into `package_xcframework.sh` so codesigning can happen on the unzipped `.xcframework` directory between build and package. The CI build step now runs: 1. `fastlane set_up_signing_release` — installs certs 2. `make build-resources-xcframework` — builds xcframework 3. `fastlane xcframework_sign` — codesigns xcframework 4. `./package_xcframework.sh` — zips + checksums --- Generated with the help of Claude Code, https://claude.ai/code Co-Authored-By: Claude Code Opus 4.6 --- .buildkite/pipeline.yml | 4 ++++ build_xcframework.sh | 18 ++---------------- fastlane/Fastfile | 15 ++++++++------- package_xcframework.sh | 39 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 53 insertions(+), 23 deletions(-) create mode 100755 package_xcframework.sh diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index a0fd49e16..4cce6c68c 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -73,7 +73,11 @@ steps: command: | buildkite-agent artifact download dist.tar.gz . tar -xzf dist.tar.gz + install_gems + bundle exec fastlane set_up_signing_release make build-resources-xcframework + bundle exec fastlane xcframework_sign + ./package_xcframework.sh artifact_paths: - '*.xcframework.zip' - '*.xcframework.zip.checksum.txt' diff --git a/build_xcframework.sh b/build_xcframework.sh index d12aadfca..211206a38 100755 --- a/build_xcframework.sh +++ b/build_xcframework.sh @@ -7,8 +7,7 @@ # - Package.swift must have `resourcesMode` set to `.local` # # Output: -# - GutenbergKitResources-.xcframework.zip -# - GutenbergKitResources-.xcframework.zip.checksum.txt +# - build/GutenbergKitResources-.xcframework # # Adapted from: # https://github.com/OpenSwiftUIProject/ProtobufKit/blob/937eae542/Scripts/build_xcframework.sh @@ -19,12 +18,10 @@ set -euo pipefail SCHEME="GutenbergKitResources" MINIMUM_IOS_VERSION="17.0" BUILD_DIR="$(pwd)/build" -OUTPUT_DIR="${1:-$(pwd)}" DERIVED_DATA_PATH="${BUILD_DIR}/DerivedData" GIT_SHA="$(git rev-parse HEAD)" XCFRAMEWORK_NAME="${SCHEME}-${GIT_SHA}.xcframework" -ZIP_NAME="${XCFRAMEWORK_NAME}.zip" link_dylib() { local object_file="$1" @@ -192,16 +189,5 @@ if [ -d "${SIM_DSYMS}" ]; then cp -r "${SIM_DSYMS}" "${XCFRAMEWORK_PATH}/ios-arm64_x86_64-simulator/" fi -# Create ZIP archive -echo "--- Creating ZIP archive" -(cd "${BUILD_DIR}" && zip -r "${ZIP_NAME}" "$(basename "${XCFRAMEWORK_PATH}")" > /dev/null) -cp "${BUILD_DIR}/${ZIP_NAME}" "${OUTPUT_DIR}/${ZIP_NAME}" - -# Compute checksum -echo "--- Computing checksum" -CHECKSUM=$(swift package compute-checksum "${OUTPUT_DIR}/${ZIP_NAME}") -echo "${CHECKSUM}" > "${OUTPUT_DIR}/${ZIP_NAME}.checksum.txt" - echo "" -echo "XCFramework: ${OUTPUT_DIR}/${ZIP_NAME}" -echo "Checksum: ${CHECKSUM}" +echo "XCFramework: ${XCFRAMEWORK_PATH}" diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 57bc6147a..7b8dfc672 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -40,15 +40,11 @@ lane :publish_to_s3 do |options| end lane :xcframework_sign do - require_env_vars!(*ASC_API_KEY_ENV_VARS, *CODE_SIGNING_STORAGE_ENV_VARS) - - set_up_signing_release - sh( 'codesign', '--timestamp', '-s', 'Apple Distribution', - xcframework_file_path.sub('.zip', '') + xcframework_dir_path ) end @@ -75,12 +71,17 @@ end def xcframework_checksum_file_path Dir.glob(File.join(PROJECT_ROOT, 'GutenbergKitResources-*.xcframework.zip.checksum.txt')).first \ - || UI.user_error!('XCFramework checksum file not found. Run build_xcframework.sh first.') + || UI.user_error!('XCFramework checksum file not found. Run package_xcframework.sh first.') +end + +def xcframework_dir_path + Dir.glob(File.join(PROJECT_ROOT, 'build', 'GutenbergKitResources-*.xcframework')).first \ + || UI.user_error!('XCFramework not found. Run build_xcframework.sh first.') end def xcframework_file_path Dir.glob(File.join(PROJECT_ROOT, 'GutenbergKitResources-*.xcframework.zip')).first \ - || UI.user_error!('XCFramework zip not found. Run build_xcframework.sh first.') + || UI.user_error!('XCFramework zip not found. Run package_xcframework.sh first.') end def required_version!(options) diff --git a/package_xcframework.sh b/package_xcframework.sh new file mode 100755 index 000000000..795ae1c7b --- /dev/null +++ b/package_xcframework.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash + +# Packages an XCFramework into a ZIP archive with a checksum file. +# +# Prerequisites: +# - A built .xcframework under build/ +# +# Output: +# - GutenbergKitResources-.xcframework.zip +# - GutenbergKitResources-.xcframework.zip.checksum.txt + +set -euo pipefail + +BUILD_DIR="$(pwd)/build" +OUTPUT_DIR="${1:-$(pwd)}" + +XCFRAMEWORK_PATH=$(find "${BUILD_DIR}" -maxdepth 1 -name "*.xcframework" -type d | head -1) + +if [ -z "${XCFRAMEWORK_PATH}" ]; then + echo "Error: No .xcframework found in ${BUILD_DIR}. Run build_xcframework.sh first." >&2 + exit 1 +fi + +XCFRAMEWORK_NAME=$(basename "${XCFRAMEWORK_PATH}") +ZIP_NAME="${XCFRAMEWORK_NAME}.zip" + +# Create ZIP archive +echo "--- Creating ZIP archive" +(cd "${BUILD_DIR}" && zip -r "${ZIP_NAME}" "${XCFRAMEWORK_NAME}" > /dev/null) +cp "${BUILD_DIR}/${ZIP_NAME}" "${OUTPUT_DIR}/${ZIP_NAME}" + +# Compute checksum +echo "--- Computing checksum" +CHECKSUM=$(swift package compute-checksum "${OUTPUT_DIR}/${ZIP_NAME}") +echo "${CHECKSUM}" > "${OUTPUT_DIR}/${ZIP_NAME}.checksum.txt" + +echo "" +echo "XCFramework: ${OUTPUT_DIR}/${ZIP_NAME}" +echo "Checksum: ${CHECKSUM}" From dae10dfdf30d1f4f5bfe05ae51aa2030042227a3 Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Wed, 25 Mar 2026 20:31:25 +1100 Subject: [PATCH 5/5] Download built XCFramework in S3 publishing step --- .buildkite/pipeline.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index 4cce6c68c..280d03b12 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -84,9 +84,16 @@ steps: plugins: *plugins - label: ':s3: Publish XCFramework to S3' - if: build.env("NEW_VERSION") != null + # Eventually, we might want to gate this based on an env var + # to be set by the process that starts the pipeline + # + # if: build.env("NEW_VERSION") != null depends_on: build-xcframework - command: bundle exec fastlane publish_to_s3 version:$NEW_VERSION + command: | + buildkite-agent artifact download '*.xcframework.zip' . + buildkite-agent artifact download '*.xcframework.zip.checksum.txt' . + install_gems + bundle exec fastlane publish_to_s3 version:${NEW_VERSION:-$BUILDKITE_COMMIT} plugins: *plugins - label: ':ios: Test iOS E2E'