stage fingerprint ota workflows and native receipts
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
name: Native build receipt
|
||||
description: Verify the packaged Expo runtime and record an immutable native artifact receipt.
|
||||
|
||||
inputs:
|
||||
platform:
|
||||
required: true
|
||||
profile:
|
||||
required: true
|
||||
artifact-path:
|
||||
required: true
|
||||
native-build-number:
|
||||
required: true
|
||||
default-channel:
|
||||
required: true
|
||||
output-directory:
|
||||
required: false
|
||||
default: native-build-receipt
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Resolve and verify packaged runtime
|
||||
shell: bash
|
||||
env:
|
||||
PLATFORM: ${{ inputs.platform }}
|
||||
PROFILE: ${{ inputs.profile }}
|
||||
ARTIFACT_PATH: ${{ inputs.artifact-path }}
|
||||
BUILD_NUMBER: ${{ inputs.native-build-number }}
|
||||
DEFAULT_CHANNEL: ${{ inputs.default-channel }}
|
||||
OUTPUT_DIRECTORY: ${{ inputs.output-directory }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
[[ "$PLATFORM" = ios || "$PLATFORM" = android ]] || { echo "::error::platform must be ios or android"; exit 1; }
|
||||
[[ "$PROFILE" = production || "$PROFILE" = testflight ]] || { echo "::error::profile must be production or testflight"; exit 1; }
|
||||
[ "$DEFAULT_CHANNEL" = "$PROFILE" ] || { echo "::error::default channel must match the native profile"; exit 1; }
|
||||
[[ "$BUILD_NUMBER" =~ ^[0-9]+$ ]] || { echo "::error::native build number must be numeric"; exit 1; }
|
||||
[ -f "$ARTIFACT_PATH" ] || { echo "::error::native artifact does not exist"; exit 1; }
|
||||
mkdir -p "$OUTPUT_DIRECTORY"
|
||||
report="$OUTPUT_DIRECTORY/fingerprint-report.json"
|
||||
node scripts/ota/resolve-runtime.mjs \
|
||||
--platform "$PLATFORM" \
|
||||
--profile "$PROFILE" \
|
||||
--source-commit "$GITHUB_SHA" \
|
||||
--output "$report"
|
||||
|
||||
if [ "$PLATFORM" = ios ]; then
|
||||
runtime_entries=$(unzip -Z1 "$ARTIFACT_PATH" | grep -E '^Payload/[^/]+\.app/EXUpdates\.bundle/fingerprint$' || true)
|
||||
if [ "$(printf '%s\n' "$runtime_entries" | grep -c .)" -ne 1 ]; then
|
||||
echo "::error::Expected exactly one packaged iOS Expo fingerprint"
|
||||
exit 1
|
||||
fi
|
||||
runtime_entry="$runtime_entries"
|
||||
inspect_dir=$(mktemp -d)
|
||||
unzip -q "$ARTIFACT_PATH" 'Payload/*.app/Info.plist' 'Payload/*.app/Expo.plist' -d "$inspect_dir"
|
||||
info_plist=$(find "$inspect_dir" -name Info.plist -print -quit)
|
||||
expo_plist=$(find "$inspect_dir" -name Expo.plist -print -quit)
|
||||
packaged_build_number=$(/usr/libexec/PlistBuddy -c 'Print CFBundleVersion' "$info_plist")
|
||||
packaged_channel=$(/usr/libexec/PlistBuddy -c 'Print EXUpdatesRequestHeaders:expo-channel-name' "$expo_plist")
|
||||
packaged_runtime_configuration=$(/usr/libexec/PlistBuddy -c 'Print EXUpdatesRuntimeVersion' "$expo_plist")
|
||||
else
|
||||
runtime_entries=$(unzip -Z1 "$ARTIFACT_PATH" | grep -E '(^|/)assets/fingerprint$' || true)
|
||||
if [ "$(printf '%s\n' "$runtime_entries" | grep -c .)" -ne 1 ]; then
|
||||
echo "::error::Expected exactly one packaged Android Expo fingerprint"
|
||||
exit 1
|
||||
fi
|
||||
runtime_entry="$runtime_entries"
|
||||
inspect_dir=$(mktemp -d)
|
||||
: > "$inspect_dir/runtime-resource.txt"
|
||||
if [[ "$ARTIFACT_PATH" != *.aab ]]; then
|
||||
echo "::error::Android receipt requires an .aab so bundletool can bind compiled resource identities"
|
||||
exit 1
|
||||
fi
|
||||
bundletool dump manifest --bundle="$ARTIFACT_PATH" --module=base > "$inspect_dir/manifest.xml"
|
||||
bundletool dump resources --bundle="$ARTIFACT_PATH" --resource=string/expo_runtime_version --values > "$inspect_dir/runtime-resource.txt"
|
||||
packaged_build_number=$(bundletool dump manifest --bundle="$ARTIFACT_PATH" --module=base --xpath=/manifest/@android:versionCode)
|
||||
packaged_config=$(node .github/scripts/native-receipt-android.mjs "$inspect_dir/manifest.xml" "$inspect_dir/runtime-resource.txt")
|
||||
packaged_channel=$(jq -er .channel <<<"$packaged_config")
|
||||
packaged_runtime_configuration=$(jq -er .runtimeConfiguration <<<"$packaged_config")
|
||||
fi
|
||||
if [ -z "$runtime_entry" ]; then
|
||||
echo "::error::Could not find the packaged Expo fingerprint in $ARTIFACT_PATH"
|
||||
exit 1
|
||||
fi
|
||||
packaged_runtime=$(unzip -p "$ARTIFACT_PATH" "$runtime_entry" | tr -d '\r\n')
|
||||
if [ "$packaged_runtime_configuration" != 'file:fingerprint' ]; then
|
||||
echo "::error::Packaged Expo runtime configuration does not use the fingerprint resource"
|
||||
exit 1
|
||||
fi
|
||||
if [ "$packaged_build_number" != "$BUILD_NUMBER" ]; then
|
||||
echo "::error::Packaged build number does not match the workflow output"
|
||||
exit 1
|
||||
fi
|
||||
if [ "$packaged_channel" != "$DEFAULT_CHANNEL" ]; then
|
||||
echo "::error::Packaged update channel does not match the expected native channel"
|
||||
exit 1
|
||||
fi
|
||||
calculated_runtime=$(jq -r .runtimeVersion "$report")
|
||||
if [ "$packaged_runtime" != "$calculated_runtime" ]; then
|
||||
echo "::error::Packaged runtime does not match the canonical build calculation"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
artifact_digest=$(shasum -a 256 "$ARTIFACT_PATH" | cut -d ' ' -f 1)
|
||||
app_version=$(jq -r .version package.json)
|
||||
jq -n \
|
||||
--arg platform "$PLATFORM" \
|
||||
--arg profile "$PROFILE" \
|
||||
--arg channel "$DEFAULT_CHANNEL" \
|
||||
--arg appVersion "$app_version" \
|
||||
--arg nativeBuildNumber "$BUILD_NUMBER" \
|
||||
--arg runtimeVersion "$packaged_runtime" \
|
||||
--arg sourceCommit "$GITHUB_SHA" \
|
||||
--arg artifactDigest "$artifact_digest" \
|
||||
--arg buildRunUrl "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" \
|
||||
'{schemaVersion: 1, platform: $platform, nativeProfile: $profile,
|
||||
defaultChannel: $channel, appVersion: $appVersion,
|
||||
nativeBuildNumber: $nativeBuildNumber, runtimeVersion: $runtimeVersion,
|
||||
sourceCommit: $sourceCommit, fingerprintPolicyVersion: 1,
|
||||
fingerprintToolVersion: (input.fingerprintToolVersion),
|
||||
artifactDigest: $artifactDigest, buildRunUrl: $buildRunUrl,
|
||||
fingerprintReportRef: "fingerprint-report.json"}' \
|
||||
"$report" > "$OUTPUT_DIRECTORY/receipt.json"
|
||||
|
||||
jq -e --arg runtime "$packaged_runtime" \
|
||||
'.schemaVersion == 1 and .runtimeVersion == $runtime' \
|
||||
"$OUTPUT_DIRECTORY/receipt.json" >/dev/null
|
||||
Reference in New Issue
Block a user