Some checks failed
Cache: Publish Nix / Publish Nix Cache (push) Waiting to run
Build Rust / Cargo Test (push) Successful in 4m14s
Build Site / Next.js Build (push) Failing after 6s
Infra: OpenTofu / OpenTofu (grafana) (push) Successful in 5s
Lint Governance / BEP Metadata (push) Successful in 0s
Build: Android / Android Rust Core Stub (push) Failing after 23s
287 lines
8.8 KiB
Bash
Executable file
287 lines
8.8 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
repo_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
cd "$repo_root"
|
|
|
|
platform="${1:-all}"
|
|
build_number="${BUILD_NUMBER:-${RELEASE_BUILD_NUMBER:-${RELEASE_REF:-manual}}}"
|
|
out_root="${BURROW_RELEASE_OUT:-${repo_root}/dist/builds/${build_number}}"
|
|
apple_root="${out_root}/apple"
|
|
archives_root="${apple_root}/archives"
|
|
derived_data="${BURROW_DERIVED_DATA_PATH:-${repo_root}/.derived-data/apple}"
|
|
source_packages="${BURROW_SWIFTPM_CACHE_PATH:-${XDG_CACHE_HOME:-${HOME}/.cache}/burrow/swiftpm}"
|
|
project="Apple/Burrow.xcodeproj"
|
|
scheme="${BURROW_APPLE_SCHEME:-App}"
|
|
bundle_id="${BURROW_APPLE_BUNDLE_ID:-com.hackclub.burrow}"
|
|
network_extension_bundle_id="${BURROW_APPLE_NETWORK_EXTENSION_BUNDLE_ID:-${bundle_id}.network}"
|
|
signing_ready="${BURROW_APPLE_SIGNING_READY:-0}"
|
|
require_signing="${BURROW_APPLE_REQUIRE_SIGNING:-false}"
|
|
sparkle_feed_url="${BURROW_SPARKLE_FEED_URL:-https://releases.burrow.net/sparkle/appcast.xml}"
|
|
sparkle_public_ed_key="${BURROW_SPARKLE_PUBLIC_ED_KEY:-Myv9ZNZT6YGKMtMezh52ra4WqaeEKc4VlvVU0evhJeI=}"
|
|
sparkle_sign_with_kms="${BURROW_SPARKLE_SIGN_WITH_KMS:-false}"
|
|
|
|
mkdir -p "$apple_root" "$archives_root" "$derived_data" "$source_packages"
|
|
|
|
truthy() {
|
|
case "${1:-}" in
|
|
1|true|TRUE|True|yes|YES|Yes|y|Y|on|ON|On) return 0 ;;
|
|
*) return 1 ;;
|
|
esac
|
|
}
|
|
|
|
sha256_file() {
|
|
local file="$1"
|
|
if command -v shasum >/dev/null 2>&1; then
|
|
shasum -a 256 "$file" > "${file}.sha256"
|
|
else
|
|
sha256sum "$file" > "${file}.sha256"
|
|
fi
|
|
}
|
|
|
|
write_version() {
|
|
mkdir -p Apple/Configuration
|
|
printf 'CURRENT_PROJECT_VERSION = %s\n' "$build_number" > Apple/Configuration/Version.xcconfig
|
|
}
|
|
|
|
xcode_common_args=(
|
|
-project "$project"
|
|
-scheme "$scheme"
|
|
-configuration Release
|
|
-derivedDataPath "$derived_data"
|
|
-clonedSourcePackagesDirPath "$source_packages"
|
|
CURRENT_PROJECT_VERSION="$build_number"
|
|
APP_BUNDLE_IDENTIFIER="$bundle_id"
|
|
NETWORK_EXTENSION_BUNDLE_IDENTIFIER="$network_extension_bundle_id"
|
|
BURROW_SPARKLE_FEED_URL="$sparkle_feed_url"
|
|
BURROW_SPARKLE_PUBLIC_ED_KEY="$sparkle_public_ed_key"
|
|
)
|
|
|
|
xcode_auth_args=()
|
|
if [[ -n "${ASC_API_KEY_ID:-}" && -n "${ASC_API_ISSUER_ID:-}" && -n "${ASC_API_KEY_PATH:-}" && -f "${ASC_API_KEY_PATH:-}" ]]; then
|
|
xcode_auth_args+=(
|
|
-authenticationKeyID "$ASC_API_KEY_ID"
|
|
-authenticationKeyIssuerID "$ASC_API_ISSUER_ID"
|
|
-authenticationKeyPath "$ASC_API_KEY_PATH"
|
|
)
|
|
fi
|
|
|
|
if [[ -n "${BURROW_APPLE_KEYCHAIN_PATH:-}" ]]; then
|
|
xcode_common_args+=(OTHER_CODE_SIGN_FLAGS="--keychain ${BURROW_APPLE_KEYCHAIN_PATH}")
|
|
fi
|
|
|
|
require_xcodebuild() {
|
|
if ! command -v xcodebuild >/dev/null 2>&1; then
|
|
echo "xcodebuild is required for Apple release artifacts" >&2
|
|
exit 1
|
|
fi
|
|
xcodebuild -version
|
|
xcrun --find swiftc >/dev/null
|
|
}
|
|
|
|
unsigned_note() {
|
|
local platform_name="$1"
|
|
local artifact_name="$2"
|
|
{
|
|
echo "Burrow ${platform_name} signing assets were not available."
|
|
echo
|
|
echo "The lane built an unsigned validation artifact instead of an uploadable release artifact."
|
|
echo "Seal the Apple release credentials with agenix and let CI sync provisioning profiles from App Store Connect to produce ${artifact_name}."
|
|
} > "${apple_root}/README-${platform_name}-unsigned.txt"
|
|
}
|
|
|
|
ensure_signing_or_note() {
|
|
local platform_name="$1"
|
|
local artifact_name="$2"
|
|
if [[ "$signing_ready" == "1" ]]; then
|
|
return 0
|
|
fi
|
|
if truthy "$require_signing"; then
|
|
echo "Apple signing is required for ${platform_name}, but signing assets are not configured." >&2
|
|
exit 1
|
|
fi
|
|
unsigned_note "$platform_name" "$artifact_name"
|
|
return 1
|
|
}
|
|
|
|
write_export_options() {
|
|
local method="$1"
|
|
local path="$2"
|
|
cat > "$path" <<EOF
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
<plist version="1.0">
|
|
<dict>
|
|
<key>method</key>
|
|
<string>${method}</string>
|
|
<key>destination</key>
|
|
<string>export</string>
|
|
<key>signingStyle</key>
|
|
<string>automatic</string>
|
|
<key>stripSwiftSymbols</key>
|
|
<true/>
|
|
<key>teamID</key>
|
|
<string>${DEVELOPMENT_TEAM:-P6PV2R9443}</string>
|
|
</dict>
|
|
</plist>
|
|
EOF
|
|
}
|
|
|
|
build_ios_unsigned() {
|
|
local product_dir zip_path
|
|
xcodebuild build \
|
|
"${xcode_common_args[@]}" \
|
|
-destination "generic/platform=iOS Simulator" \
|
|
ARCHS=arm64 \
|
|
ONLY_ACTIVE_ARCH=YES \
|
|
CODE_SIGNING_ALLOWED=NO \
|
|
CODE_SIGNING_REQUIRED=NO
|
|
|
|
product_dir="${derived_data}/Build/Products/Release-iphonesimulator"
|
|
zip_path="${apple_root}/Burrow-iOS-simulator-${build_number}.unsigned.zip"
|
|
if [[ -d "${product_dir}/Burrow.app" ]]; then
|
|
(cd "$product_dir" && zip -qr "$zip_path" Burrow.app)
|
|
sha256_file "$zip_path"
|
|
fi
|
|
}
|
|
|
|
build_ios_release() {
|
|
local archive_path export_dir export_options ipa
|
|
if ! ensure_signing_or_note "iOS" "Burrow-iOS-${build_number}.ipa"; then
|
|
build_ios_unsigned
|
|
return 0
|
|
fi
|
|
|
|
archive_path="${archives_root}/Burrow-iOS-${build_number}.xcarchive"
|
|
export_dir="${apple_root}/ios-export"
|
|
export_options="${apple_root}/ExportOptions-iOS.plist"
|
|
write_export_options "app-store-connect" "$export_options"
|
|
|
|
xcodebuild archive \
|
|
"${xcode_common_args[@]}" \
|
|
-destination "generic/platform=iOS" \
|
|
-archivePath "$archive_path" \
|
|
ARCHS=arm64 \
|
|
-allowProvisioningUpdates \
|
|
"${xcode_auth_args[@]}"
|
|
|
|
xcodebuild -exportArchive \
|
|
-archivePath "$archive_path" \
|
|
-exportPath "$export_dir" \
|
|
-exportOptionsPlist "$export_options" \
|
|
-allowProvisioningUpdates \
|
|
"${xcode_auth_args[@]}"
|
|
|
|
ipa="$(find "$export_dir" -maxdepth 1 -type f -name '*.ipa' | sort | head -n 1 || true)"
|
|
if [[ -z "$ipa" ]]; then
|
|
echo "iOS export completed but did not produce an IPA" >&2
|
|
exit 1
|
|
fi
|
|
cp "$ipa" "${apple_root}/Burrow-iOS-${build_number}.ipa"
|
|
sha256_file "${apple_root}/Burrow-iOS-${build_number}.ipa"
|
|
}
|
|
|
|
build_macos_unsigned() {
|
|
local product_dir zip_path
|
|
xcodebuild build \
|
|
"${xcode_common_args[@]}" \
|
|
-destination "platform=macOS" \
|
|
CODE_SIGNING_ALLOWED=NO \
|
|
CODE_SIGNING_REQUIRED=NO
|
|
|
|
product_dir="${derived_data}/Build/Products/Release"
|
|
zip_path="${apple_root}/Burrow-macOS-${build_number}.unsigned.zip"
|
|
if [[ -d "${product_dir}/Burrow.app" ]]; then
|
|
ditto -c -k --keepParent "${product_dir}/Burrow.app" "$zip_path"
|
|
sha256_file "$zip_path"
|
|
fi
|
|
}
|
|
|
|
build_macos_release() {
|
|
local archive_path export_dir export_options app dmg channel sparkle_dir length pubdate url
|
|
if ! ensure_signing_or_note "macOS" "Burrow-macOS-${build_number}.dmg"; then
|
|
build_macos_unsigned
|
|
return 0
|
|
fi
|
|
|
|
archive_path="${archives_root}/Burrow-macOS-${build_number}.xcarchive"
|
|
export_dir="${apple_root}/macos-export"
|
|
export_options="${apple_root}/ExportOptions-macOS.plist"
|
|
write_export_options "${BURROW_MACOS_EXPORT_METHOD:-developer-id}" "$export_options"
|
|
|
|
xcodebuild archive \
|
|
"${xcode_common_args[@]}" \
|
|
-destination "generic/platform=macOS" \
|
|
-archivePath "$archive_path" \
|
|
-allowProvisioningUpdates \
|
|
"${xcode_auth_args[@]}"
|
|
|
|
xcodebuild -exportArchive \
|
|
-archivePath "$archive_path" \
|
|
-exportPath "$export_dir" \
|
|
-exportOptionsPlist "$export_options" \
|
|
-allowProvisioningUpdates \
|
|
"${xcode_auth_args[@]}"
|
|
|
|
app="$(find "$export_dir" -maxdepth 2 -type d -name 'Burrow.app' | sort | head -n 1 || true)"
|
|
if [[ -z "$app" ]]; then
|
|
echo "macOS export completed but did not produce Burrow.app" >&2
|
|
exit 1
|
|
fi
|
|
|
|
dmg="${apple_root}/Burrow-macOS-${build_number}.dmg"
|
|
rm -f "$dmg"
|
|
hdiutil create -volname "Burrow ${build_number}" -srcfolder "$app" -ov -format UDZO "$dmg"
|
|
sha256_file "$dmg"
|
|
|
|
channel="${SPARKLE_CHANNEL:-build-${build_number}}"
|
|
sparkle_dir="${out_root}/sparkle/${channel}"
|
|
mkdir -p "$sparkle_dir"
|
|
cp "$dmg" "$sparkle_dir/"
|
|
length="$(wc -c < "$dmg" | tr -d ' ')"
|
|
pubdate="$(LC_ALL=C date -u '+%a, %d %b %Y %H:%M:%S +0000')"
|
|
url="${SPARKLE_DOWNLOAD_PREFIX:-https://releases.burrow.net/sparkle}/${channel}/$(basename "$dmg")"
|
|
cat > "${sparkle_dir}/appcast.xml" <<EOF
|
|
<?xml version="1.0" encoding="utf-8"?>
|
|
<rss version="2.0" xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle">
|
|
<channel>
|
|
<title>Burrow ${channel}</title>
|
|
<item>
|
|
<title>Burrow ${build_number}</title>
|
|
<pubDate>${pubdate}</pubDate>
|
|
<sparkle:version>${build_number}</sparkle:version>
|
|
<sparkle:shortVersionString>0.1</sparkle:shortVersionString>
|
|
<enclosure url="${url}" sparkle:version="${build_number}" length="${length}" type="application/octet-stream" />
|
|
</item>
|
|
</channel>
|
|
</rss>
|
|
EOF
|
|
|
|
if truthy "$sparkle_sign_with_kms"; then
|
|
Scripts/sparkle/sign-appcast-kms.py \
|
|
--appcast "${sparkle_dir}/appcast.xml" \
|
|
--artifact-dir "$sparkle_dir"
|
|
fi
|
|
}
|
|
|
|
require_xcodebuild
|
|
write_version
|
|
|
|
case "$platform" in
|
|
all)
|
|
build_ios_release
|
|
build_macos_release
|
|
;;
|
|
ios)
|
|
build_ios_release
|
|
;;
|
|
macos)
|
|
build_macos_release
|
|
;;
|
|
*)
|
|
echo "unknown Apple release platform: $platform" >&2
|
|
exit 64
|
|
;;
|
|
esac
|
|
|
|
find "$out_root" -type f -print | sort
|