ci: add signed iOS release pipeline
This commit is contained in:
58
Jenkinsfile.release
Normal file
58
Jenkinsfile.release
Normal file
@@ -0,0 +1,58 @@
|
||||
pipeline {
|
||||
agent { label 'um-trace' }
|
||||
|
||||
parameters {
|
||||
string(
|
||||
name: 'IOS_BUILD_NUMBER',
|
||||
defaultValue: '2',
|
||||
description: 'Positive, monotonically increasing App Store build number.'
|
||||
)
|
||||
}
|
||||
|
||||
options {
|
||||
buildDiscarder(logRotator(numToKeepStr: '10'))
|
||||
disableConcurrentBuilds()
|
||||
timeout(time: 60, unit: 'MINUTES')
|
||||
}
|
||||
|
||||
environment {
|
||||
DEVELOPER_DIR = '/Applications/Xcode.app/Contents/Developer'
|
||||
DOTNET_ROOT = '/Users/brent/.dotnet'
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT = '1'
|
||||
DOTNET_NOLOGO = '1'
|
||||
NUGET_XMLDOC_MODE = 'skip'
|
||||
PATH = "/Users/brent/.dotnet:/opt/homebrew/bin:${env.PATH}"
|
||||
UM_TRACE_CODESIGN_KEY = 'Apple Distribution: Underground Magnetics, Inc. (W2N8APPQ2C)'
|
||||
UM_TRACE_CODESIGN_PROVISION = 'UM Trace App Store'
|
||||
}
|
||||
|
||||
stages {
|
||||
stage('Toolchain and Signing') {
|
||||
steps {
|
||||
sh './scripts/ci/verify-macos-agent.sh'
|
||||
}
|
||||
}
|
||||
|
||||
stage('Tests') {
|
||||
steps {
|
||||
sh './scripts/ci/test.sh'
|
||||
}
|
||||
}
|
||||
|
||||
stage('Signed iOS IPA') {
|
||||
steps {
|
||||
sh './scripts/ci/build-ios-signed.sh "$IOS_BUILD_NUMBER"'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
post {
|
||||
always {
|
||||
archiveArtifacts(
|
||||
artifacts: 'artifacts/ios-signed/**/*',
|
||||
allowEmptyArchive: true,
|
||||
fingerprint: true
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,16 @@ The script deliberately supplies Xcode through `DEVELOPER_DIR`; it does not chan
|
||||
global `xcode-select` setting. It restores only the iOS target, builds with the iOS 26 SDK, selects
|
||||
the installed `UM Trace App Store` profile, and prints the generated `.ipa` path.
|
||||
|
||||
## Create a signed IPA in Jenkins
|
||||
|
||||
The manually run `um-trace-ios-release` pipeline uses `Jenkinsfile.release`. Supply an
|
||||
`IOS_BUILD_NUMBER` that is higher than every build previously uploaded to App Store Connect. The
|
||||
pipeline runs the tests, signs with the distribution identity and `UM Trace App Store` profile,
|
||||
verifies the resulting bundle signature and identity, and archives the IPA plus its SHA-256 file.
|
||||
|
||||
This pipeline only creates a signed artifact. It does not upload or submit anything to App Store
|
||||
Connect, so TestFlight release remains a separate, deliberate step.
|
||||
|
||||
Before uploading, verify that this command lists a valid distribution identity:
|
||||
|
||||
```sh
|
||||
|
||||
75
scripts/ci/build-ios-signed.sh
Executable file
75
scripts/ci/build-ios-signed.sh
Executable file
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
build_number="${1:-}"
|
||||
codesign_key="${UM_TRACE_CODESIGN_KEY:-Apple Distribution: Underground Magnetics, Inc. (W2N8APPQ2C)}"
|
||||
codesign_profile="${UM_TRACE_CODESIGN_PROVISION:-UM Trace App Store}"
|
||||
profile_dir="${HOME}/Library/MobileDevice/Provisioning Profiles"
|
||||
artifacts_dir="${repo_dir}/artifacts/ios-signed"
|
||||
artifact_name="UMTrace-ios-build-${build_number}.ipa"
|
||||
|
||||
if [[ ! "${build_number}" =~ ^[1-9][0-9]*$ ]]; then
|
||||
echo "A positive numeric iOS build number is required." >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if ! security find-identity -v -p codesigning | grep -Fq "${codesign_key}"; then
|
||||
echo "Signing identity is unavailable: ${codesign_key}" >&2
|
||||
exit 3
|
||||
fi
|
||||
|
||||
profile_found=false
|
||||
if [[ -d "${profile_dir}" ]]; then
|
||||
for profile in "${profile_dir}"/*.mobileprovision; do
|
||||
[[ -f "${profile}" ]] || continue
|
||||
installed_name="$(openssl smime -inform der -verify -noverify -in "${profile}" 2>/dev/null | plutil -extract Name raw -o - - 2>/dev/null || true)"
|
||||
if [[ "${installed_name}" == "${codesign_profile}" ]]; then
|
||||
profile_found=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if [[ "${profile_found}" != true ]]; then
|
||||
echo "Provisioning profile is unavailable: ${codesign_profile}" >&2
|
||||
exit 3
|
||||
fi
|
||||
|
||||
UM_TRACE_CODESIGN_KEY="${codesign_key}" \
|
||||
UM_TRACE_CODESIGN_PROVISION="${codesign_profile}" \
|
||||
"${repo_dir}/scripts/publish-testflight.sh" "${build_number}"
|
||||
|
||||
source_ipa="$(find "${repo_dir}/FieldLogger/bin/Release/net9.0-ios/ios-arm64" -type f -name '*.ipa' -print -quit)"
|
||||
if [[ -z "${source_ipa}" || ! -f "${source_ipa}" ]]; then
|
||||
echo "The signed IPA was not created." >&2
|
||||
exit 4
|
||||
fi
|
||||
|
||||
mkdir -p "${artifacts_dir}"
|
||||
ditto "${source_ipa}" "${artifacts_dir}/${artifact_name}"
|
||||
|
||||
verification_dir="$(mktemp -d)"
|
||||
ditto -x -k "${artifacts_dir}/${artifact_name}" "${verification_dir}"
|
||||
app_bundle="$(find "${verification_dir}/Payload" -maxdepth 1 -type d -name '*.app' -print -quit)"
|
||||
|
||||
if [[ -z "${app_bundle}" ]]; then
|
||||
echo "The IPA does not contain an app bundle." >&2
|
||||
exit 4
|
||||
fi
|
||||
|
||||
codesign --verify --deep --strict --verbose=2 "${app_bundle}"
|
||||
|
||||
bundle_id="$(plutil -extract CFBundleIdentifier raw -o - "${app_bundle}/Info.plist")"
|
||||
signed_build_number="$(plutil -extract CFBundleVersion raw -o - "${app_bundle}/Info.plist")"
|
||||
if [[ "${bundle_id}" != "com.umagul.trace" || "${signed_build_number}" != "${build_number}" ]]; then
|
||||
echo "Signed bundle metadata is incorrect: ${bundle_id} (${signed_build_number})." >&2
|
||||
exit 5
|
||||
fi
|
||||
|
||||
(
|
||||
cd "${artifacts_dir}"
|
||||
shasum -a 256 "${artifact_name}" > "${artifact_name}.sha256"
|
||||
)
|
||||
|
||||
echo "Verified signed iOS IPA: ${artifacts_dir}/${artifact_name}"
|
||||
@@ -33,6 +33,12 @@ export DEVELOPER_DIR="${developer_dir}"
|
||||
# referenced headless library's assets file. Restore that library explicitly before publish.
|
||||
"${dotnet_bin}" restore "${repo_dir}/src/FieldLogger.Sync/FieldLogger.Sync.csproj"
|
||||
|
||||
"${dotnet_bin}" clean "${repo_dir}/FieldLogger/FieldLogger.csproj" \
|
||||
-f net9.0-ios \
|
||||
-c Release \
|
||||
-p:TargetFrameworks=net9.0-ios \
|
||||
-p:RuntimeIdentifier=ios-arm64
|
||||
|
||||
"${dotnet_bin}" publish "${repo_dir}/FieldLogger/FieldLogger.csproj" \
|
||||
-p:TargetFrameworks=net9.0-ios \
|
||||
-f net9.0-ios \
|
||||
|
||||
Reference in New Issue
Block a user