Skip to content
Merged
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
128 changes: 128 additions & 0 deletions .github/workflow-scripts/assert-spm-deinit.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
#!/bin/bash
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.

# Checks that `npx react-native spm deinit` left an iOS app with only its

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: the script fails on any packageReferences / packageProductDependencies in the pbxproj, so it assumes the app had no Swift packages before spm add. That's true for the template, but the header says it checks "an iOS app". Can we say that in the comment, so nobody reuses the script on an app that already has Swift packages?

# CocoaPods files. It checks only what deinit removes; `pod install` restores
# the CocoaPods references, so they are not checked here.
#
# It assumes the app had no Swift packages before `spm add`: any
# packageReferences or packageProductDependencies left after deinit fail the
# check. Do not use it on an app that already has Swift packages.
#
# Usage:
# assert-spm-deinit.sh record <ios-dir>
# Run before `spm add`. Prints PODFILE_SHA256 and GITIGNORE_SHA256, and
# appends them to $GITHUB_ENV when it is set.
# assert-spm-deinit.sh check <ios-dir> <name.xcodeproj> <podfile-sha256> <gitignore-sha256|absent>
# Run after `spm deinit`, with the values that record printed.

usage() {
echo "Usage: $0 record <ios-dir>"
echo " $0 check <ios-dir> <name.xcodeproj> <podfile-sha256> <gitignore-sha256|absent>"
exit 2
}

sha256_or_absent() {
if [ -f "$1" ]; then
shasum -a 256 "$1" | cut -d ' ' -f 1
else
echo absent
fi
}

record() {
local ios_dir=$1
if [ ! -f "$ios_dir/Podfile" ]; then
echo "::error::$ios_dir/Podfile is missing. Record the checksums while the app is still on CocoaPods."
exit 1
fi

local values
values="PODFILE_SHA256=$(sha256_or_absent "$ios_dir/Podfile")
GITIGNORE_SHA256=$(sha256_or_absent "$ios_dir/.gitignore")"
echo "$values"
if [ -n "$GITHUB_ENV" ]; then
echo "$values" >> "$GITHUB_ENV"
fi
}

check() {
local ios_dir=$1
local xcodeproj="$ios_dir/$2"
local expected_podfile_sha256=$3
local expected_gitignore_sha256=$4
local pbxproj="$xcodeproj/project.pbxproj"
local failed=0

fail() {
echo "::error::$1"
failed=1
}

local actual_podfile_sha256
actual_podfile_sha256=$(sha256_or_absent "$ios_dir/Podfile")
if [ "$actual_podfile_sha256" != "$expected_podfile_sha256" ]; then
fail "Podfile changed: sha256 is '$actual_podfile_sha256', expected '$expected_podfile_sha256'."
fi

local actual_gitignore_sha256
actual_gitignore_sha256=$(sha256_or_absent "$ios_dir/.gitignore")
if [ "$actual_gitignore_sha256" != "$expected_gitignore_sha256" ]; then
fail ".gitignore changed: sha256 is '$actual_gitignore_sha256', expected '$expected_gitignore_sha256'."
fi

if [ -e "$xcodeproj/.spm-injected.json" ]; then
fail "$xcodeproj/.spm-injected.json is still there."
fi

if [ -e "$ios_dir/ReactNativeSPM" ]; then
fail "$ios_dir/ReactNativeSPM is still there."
fi

if [ ! -f "$pbxproj" ]; then
fail "$pbxproj is missing."
else
local injected
for injected in \
XCLocalSwiftPackageReference \
XCSwiftPackageProductDependency \
packageReferences \
packageProductDependencies \
ReactNativeSPM \
"Sync SPM Autolinking" \
"Embed React Native Flavored Frameworks"; do
if grep -qF "$injected" "$pbxproj"; then
fail "$pbxproj still contains '$injected'."
fi
done
fi

local scheme
for scheme in "$xcodeproj"/xcshareddata/xcschemes/*.xcscheme; do
if [ -f "$scheme" ] && grep -qF 'title = "Sync SPM Autolinking"' "$scheme"; then
fail "$scheme still has the Sync SPM Autolinking pre-action."
fi
done

if [ $failed -ne 0 ]; then
exit 1
fi
echo "spm deinit removed every SwiftPM change."
}

case "$1" in
record)
[ $# -eq 2 ] || usage
record "$2"
;;
check)
[ $# -eq 5 ] || usage
check "$2" "$3" "$4" "$5"
;;
*)
usage
;;
esac
52 changes: 52 additions & 0 deletions .github/workflows/test-ios-spm-newapp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@ jobs:
uses: ./.github/actions/setup-spm-artifacts
with:
working-directory: ${{ env.APP_IOS_DIR }}
# The `spm deinit` round trip runs in the Debug cell only: it does not
# depend on the flavor.
- name: Record HelloWorld before SwiftPM
if: matrix.flavor == 'Debug'
shell: bash
run: |
./.github/workflow-scripts/assert-spm-deinit.sh record "$APP_IOS_DIR"
cp -R "$APP_IOS_DIR" "$RUNNER_TEMP/ios-before-spm"
# HelloWorld is a CocoaPods project in git. CI converts it to SwiftPM on every run.
- name: Convert HelloWorld to SwiftPM
shell: bash
Expand Down Expand Up @@ -100,3 +108,47 @@ jobs:
echo "::error::Release build embeds a Debug React.framework ($COUNT getDebugProps symbols, expected none)"
exit 1
fi
- name: Take HelloWorld back to CocoaPods
if: matrix.flavor == 'Debug'
shell: bash
working-directory: ${{ env.APP_IOS_DIR }}
run: npx react-native spm deinit
# Informational only. Expected differences include the CocoaPods
# references and the .xcworkspace, which `pod install` restores, and the
# JavaScriptCore.framework reference and empty Pods group, which deinit
# does not restore on purpose.
- name: Show what changed since before SwiftPM
if: ${{ !cancelled() && matrix.flavor == 'Debug' }}
shell: bash
run: diff -r -x build "$RUNNER_TEMP/ios-before-spm" "$APP_IOS_DIR" || true
- name: Check that only the CocoaPods files are left
if: matrix.flavor == 'Debug'
shell: bash
run: ./.github/workflow-scripts/assert-spm-deinit.sh check "$APP_IOS_DIR" "$XCODE_PROJECT" "$PODFILE_SHA256" "$GITIGNORE_SHA256"
- name: Setup ruby
if: matrix.flavor == 'Debug'
uses: ruby/setup-ruby@v1
with:
ruby-version: 2.6.10
# The tarballs are the Debug ones that setup-spm-artifacts downloaded.
- name: Install the CocoaPods dependencies
if: matrix.flavor == 'Debug'
shell: bash
working-directory: private/helloworld
run: |
export RCT_USE_LOCAL_RN_DEP=/tmp/deps-debug/ReactNativeDependenciesDebug.xcframework.tar.gz
export RCT_TESTONLY_RNCORE_TARBALL_PATH=/tmp/rc-debug/ReactCoreDebug.xcframework.tar.gz
yarn bootstrap ios | cat
- name: Build ${{ env.XCODE_SCHEME }} with CocoaPods (Debug)
if: matrix.flavor == 'Debug'
shell: bash
working-directory: ${{ env.APP_IOS_DIR }}
run: |
xcodebuild \
-workspace "${XCODE_PROJECT%.xcodeproj}.xcworkspace" \
-scheme "$XCODE_SCHEME" \
-configuration Debug \
-sdk iphonesimulator \
-destination 'generic/platform=iOS Simulator' \
-derivedDataPath build/cocoapods-e2e-dd \
build
Loading