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
82 changes: 82 additions & 0 deletions .github/workflow-scripts/assert-spm-deinit.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#!/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.
#
# Usage: assert-spm-deinit.sh <ios-dir> <name.xcodeproj> <podfile-sha256> <gitignore-sha256|absent>

if [ $# -ne 4 ]; then
echo "Usage: $0 <ios-dir> <name.xcodeproj> <podfile-sha256> <gitignore-sha256|absent>"
exit 2
fi

IOS_DIR=$1
XCODEPROJ="$IOS_DIR/$2"
EXPECTED_PODFILE_SHA256=$3
EXPECTED_GITIGNORE_SHA256=$4
PBXPROJ="$XCODEPROJ/project.pbxproj"
FAILED=0

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

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

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

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
for INJECTED in \

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.

I understand why this isn't a byte-for-byte comparison of the pbxproj: deinit deliberately doesn't restore some of what add removes, like the dangling JavaScriptCore.framework reference and the empty Pods group (see spm-pbxproj.js:736). But a list of strings only catches leftovers someone thought to add to it. Could we copy ios/ before spm add and print a diff -r against it after deinit? Just for information, not as a check. When this fails, the diff would show what was left behind.

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

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."
14 changes: 14 additions & 0 deletions .github/workflows/e2e-ios-templateapp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,15 @@ jobs:
uses: ./.github/actions/setup-spm-artifacts
with:
working-directory: /tmp/RNTestProject/ios
- name: Record the Podfile and .gitignore checksums
working-directory: /tmp/RNTestProject/ios
run: |
echo "PODFILE_SHA256=$(shasum -a 256 Podfile | cut -d ' ' -f 1)" >> "$GITHUB_ENV"

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: this step computes the sha256 and the absent value inline, while assert-spm-deinit.sh has sha256_or_absent for the same thing. They can drift apart. Also, if Podfile were missing, $(shasum … Podfile) would write an empty PODFILE_SHA256 without failing the step, and the script would then fail later with an unclear "Podfile changed". What about a record mode in the script, so both steps use one implementation?

if [ -f .gitignore ]; then
echo "GITIGNORE_SHA256=$(shasum -a 256 .gitignore | cut -d ' ' -f 1)" >> "$GITHUB_ENV"
else
echo "GITIGNORE_SHA256=absent" >> "$GITHUB_ENV"
fi
- name: Convert the app to SwiftPM
working-directory: /tmp/RNTestProject/ios
run: RN_STRICT_API=1 npx react-native spm add --deintegrate --artifacts /tmp/spm-artifacts --download skip
Expand All @@ -80,6 +89,11 @@ jobs:
name: RNTestProject-${{ matrix.flavor }}
overwrite: true
path: /tmp/RNTestProject/Build/Products/${{ matrix.flavor }}-iphonesimulator/RNTestProject.app
- name: Take the app back to CocoaPods

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.

I'd move this step and the check after it out of this workflow. If either one fails, the test job (needs: build) gets skipped, so we lose the Maestro results for both flavors. test-all.yml then reruns the whole workflow twice, rebuilding the app each time, for a failure that won't change. test-ios-spm-newapp.yml or the SwiftPM path of the test-ios-helloworld action would be a better place: both already run spm add --deintegrate and don't retry.

working-directory: /tmp/RNTestProject/ios
run: npx react-native spm deinit
- name: Check that only the CocoaPods files are left
run: ./.github/workflow-scripts/assert-spm-deinit.sh /tmp/RNTestProject/ios RNTestProject.xcodeproj "$PODFILE_SHA256" "$GITIGNORE_SHA256"
- name: Report status
id: report-status
if: always()
Expand Down
Loading