ci(release): add TestFlight release matrix
* chore(release): bump version to 1.1.5+2 * chore(release): bump build metadata for 1.1.5+2 * ci(release): add TestFlight release matrix --------- Co-authored-by: Haitao Pan <manbuzhe2009@qq.com>
This commit is contained in:
parent
40ce8a95de
commit
a9e7a6fa9e
86
.github/workflows/build-and-release.yml
vendored
86
.github/workflows/build-and-release.yml
vendored
@ -122,6 +122,14 @@ jobs:
|
||||
artifact_name: build-macos-arm64-dmg
|
||||
artifact_paths: |
|
||||
dist/macos/*.dmg
|
||||
- platform: macos
|
||||
arch: arm64
|
||||
package: app-store-pkg
|
||||
release_only: true
|
||||
runs_on: macos-14
|
||||
artifact_name: build-macos-arm64-pkg
|
||||
artifact_paths: |
|
||||
dist/macos-app-store/*.pkg
|
||||
- platform: ios
|
||||
arch: arm64
|
||||
package: ipa
|
||||
@ -166,8 +174,12 @@ jobs:
|
||||
kv/data/github-actions/xworkmate-app APPLE_CERT_P12_BASE64 | APPLE_CERT_P12_BASE64 ;
|
||||
kv/data/github-actions/xworkmate-app APPLE_CERT_PASSWORD | APPLE_CERT_PASSWORD ;
|
||||
kv/data/github-actions/xworkmate-app APPLE_PROVISION_PROFILE_BASE64 | APPLE_PROVISION_PROFILE_BASE64 ;
|
||||
kv/data/github-actions/xworkmate-app APPLE_MAC_PROVISION_PROFILE_BASE64 | APPLE_MAC_PROVISION_PROFILE_BASE64 ;
|
||||
kv/data/github-actions/xworkmate-app APPLE_KEYCHAIN_PASSWORD | APPLE_KEYCHAIN_PASSWORD ;
|
||||
kv/data/github-actions/xworkmate-app APPLE_EXPORT_METHOD | APPLE_EXPORT_METHOD ;
|
||||
kv/data/github-actions/xworkmate-app APP_STORE_CONNECT_API_KEY_ID | APP_STORE_CONNECT_API_KEY_ID ;
|
||||
kv/data/github-actions/xworkmate-app APP_STORE_CONNECT_ISSUER_ID | APP_STORE_CONNECT_ISSUER_ID ;
|
||||
kv/data/github-actions/xworkmate-app APP_STORE_CONNECT_API_KEY_P8_BASE64 | APP_STORE_CONNECT_API_KEY_P8_BASE64 ;
|
||||
kv/data/github-actions/xworkmate-app ANDROID_KEYSTORE_BASE64 | ANDROID_KEYSTORE_BASE64 ;
|
||||
kv/data/github-actions/xworkmate-app ANDROID_KEYSTORE_PASSWORD | ANDROID_KEYSTORE_PASSWORD ;
|
||||
kv/data/github-actions/xworkmate-app ANDROID_KEY_ALIAS | ANDROID_KEY_ALIAS ;
|
||||
@ -183,8 +195,12 @@ jobs:
|
||||
echo "APPLE_CERT_P12_BASE64=${{ steps.vault.outputs.APPLE_CERT_P12_BASE64 }}"
|
||||
echo "APPLE_CERT_PASSWORD=${{ steps.vault.outputs.APPLE_CERT_PASSWORD }}"
|
||||
echo "APPLE_PROVISION_PROFILE_BASE64=${{ steps.vault.outputs.APPLE_PROVISION_PROFILE_BASE64 }}"
|
||||
echo "APPLE_MAC_PROVISION_PROFILE_BASE64=${{ steps.vault.outputs.APPLE_MAC_PROVISION_PROFILE_BASE64 }}"
|
||||
echo "APPLE_KEYCHAIN_PASSWORD=${{ steps.vault.outputs.APPLE_KEYCHAIN_PASSWORD }}"
|
||||
echo "APPLE_EXPORT_METHOD=${{ steps.vault.outputs.APPLE_EXPORT_METHOD }}"
|
||||
echo "APP_STORE_CONNECT_API_KEY_ID=${{ steps.vault.outputs.APP_STORE_CONNECT_API_KEY_ID }}"
|
||||
echo "APP_STORE_CONNECT_ISSUER_ID=${{ steps.vault.outputs.APP_STORE_CONNECT_ISSUER_ID }}"
|
||||
echo "APP_STORE_CONNECT_API_KEY_P8_BASE64=${{ steps.vault.outputs.APP_STORE_CONNECT_API_KEY_P8_BASE64 }}"
|
||||
echo "ANDROID_KEYSTORE_BASE64=${{ steps.vault.outputs.ANDROID_KEYSTORE_BASE64 }}"
|
||||
echo "ANDROID_KEYSTORE_PASSWORD=${{ steps.vault.outputs.ANDROID_KEYSTORE_PASSWORD }}"
|
||||
echo "ANDROID_KEY_ALIAS=${{ steps.vault.outputs.ANDROID_KEY_ALIAS }}"
|
||||
@ -220,12 +236,12 @@ jobs:
|
||||
go-version: "1.24.1"
|
||||
|
||||
- name: Build platform artifacts
|
||||
if: ${{ steps.preflight.outputs.should_build_platform == 'true' }}
|
||||
if: ${{ steps.preflight.outputs.should_build_platform == 'true' && (matrix.release_only != 'true' || env.SHOULD_RELEASE == 'true') }}
|
||||
shell: bash
|
||||
run: bash ./scripts/ci/build_matrix_artifacts.sh "$PLATFORM" "$ARCH" "$SHOULD_RELEASE"
|
||||
run: bash ./scripts/ci/build_matrix_artifacts.sh "$PLATFORM" "$ARCH" "${{ matrix.package }}" "$SHOULD_RELEASE"
|
||||
|
||||
- name: Upload build artifacts
|
||||
if: ${{ steps.preflight.outputs.should_build_platform == 'true' }}
|
||||
if: ${{ steps.preflight.outputs.should_build_platform == 'true' && (matrix.release_only != 'true' || env.SHOULD_RELEASE == 'true') }}
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: ${{ matrix.artifact_name }}
|
||||
@ -273,7 +289,23 @@ jobs:
|
||||
# never blocked by it being skipped (e.g. push events) or failing.
|
||||
# build/prepare must still genuinely succeed.
|
||||
if: ${{ always() && needs.prepare.outputs.should_release == 'true' && needs.prepare.result == 'success' && needs.build.result == 'success' }}
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- target: github_release
|
||||
runs_on: ubuntu-22.04
|
||||
- target: testflight_ios
|
||||
runs_on: macos-14
|
||||
artifact_name: build-ios-arm64-ipa
|
||||
artifact_path: release-artifacts/build-ios-arm64-ipa
|
||||
testflight_platform: ios
|
||||
- target: testflight_macos
|
||||
runs_on: macos-14
|
||||
artifact_name: build-macos-arm64-pkg
|
||||
artifact_path: release-artifacts/build-macos-arm64-pkg
|
||||
testflight_platform: macos
|
||||
runs-on: ${{ matrix.runs_on }}
|
||||
permissions:
|
||||
contents: write
|
||||
needs:
|
||||
@ -284,12 +316,46 @@ jobs:
|
||||
- name: Checkout source
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Load App Store Connect secrets
|
||||
id: vault
|
||||
if: ${{ matrix.target != 'github_release' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) }}
|
||||
uses: hashicorp/vault-action@v4
|
||||
with:
|
||||
url: ${{ env.VAULT_ADDR }}
|
||||
method: jwt
|
||||
role: github-actions-xworkmate-app
|
||||
jwtGithubAudience: vault
|
||||
ignoreNotFound: true
|
||||
secrets: |
|
||||
kv/data/github-actions/xworkmate-app APPLE_CERT_P12_BASE64 | APPLE_CERT_P12_BASE64 ;
|
||||
kv/data/github-actions/xworkmate-app APPLE_CERT_PASSWORD | APPLE_CERT_PASSWORD ;
|
||||
kv/data/github-actions/xworkmate-app APPLE_MAC_PROVISION_PROFILE_BASE64 | APPLE_MAC_PROVISION_PROFILE_BASE64 ;
|
||||
kv/data/github-actions/xworkmate-app APPLE_KEYCHAIN_PASSWORD | APPLE_KEYCHAIN_PASSWORD ;
|
||||
kv/data/github-actions/xworkmate-app APP_STORE_CONNECT_API_KEY_ID | APP_STORE_CONNECT_API_KEY_ID ;
|
||||
kv/data/github-actions/xworkmate-app APP_STORE_CONNECT_ISSUER_ID | APP_STORE_CONNECT_ISSUER_ID ;
|
||||
kv/data/github-actions/xworkmate-app APP_STORE_CONNECT_API_KEY_P8_BASE64 | APP_STORE_CONNECT_API_KEY_P8_BASE64
|
||||
|
||||
- name: Export App Store Connect secrets
|
||||
if: ${{ matrix.target != 'github_release' }}
|
||||
run: |
|
||||
{
|
||||
echo "APPLE_CERT_P12_BASE64=${{ steps.vault.outputs.APPLE_CERT_P12_BASE64 }}"
|
||||
echo "APPLE_CERT_PASSWORD=${{ steps.vault.outputs.APPLE_CERT_PASSWORD }}"
|
||||
echo "APPLE_MAC_PROVISION_PROFILE_BASE64=${{ steps.vault.outputs.APPLE_MAC_PROVISION_PROFILE_BASE64 }}"
|
||||
echo "APPLE_KEYCHAIN_PASSWORD=${{ steps.vault.outputs.APPLE_KEYCHAIN_PASSWORD }}"
|
||||
echo "APP_STORE_CONNECT_API_KEY_ID=${{ steps.vault.outputs.APP_STORE_CONNECT_API_KEY_ID }}"
|
||||
echo "APP_STORE_CONNECT_ISSUER_ID=${{ steps.vault.outputs.APP_STORE_CONNECT_ISSUER_ID }}"
|
||||
echo "APP_STORE_CONNECT_API_KEY_P8_BASE64=${{ steps.vault.outputs.APP_STORE_CONNECT_API_KEY_P8_BASE64 }}"
|
||||
} >> "$GITHUB_ENV"
|
||||
|
||||
- name: Download all artifacts
|
||||
if: ${{ matrix.target == 'github_release' }}
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
path: release-artifacts
|
||||
|
||||
- name: Upload assets to GitHub Release
|
||||
if: ${{ matrix.target == 'github_release' }}
|
||||
shell: bash
|
||||
run: bash ./scripts/ci/github_release_upload.sh release-artifacts
|
||||
env:
|
||||
@ -297,3 +363,15 @@ jobs:
|
||||
RELEASE_TAG: ${{ needs.prepare.outputs.release_tag }}
|
||||
RELEASE_TITLE: ${{ needs.prepare.outputs.release_title }}
|
||||
RELEASE_NOTES: ${{ needs.prepare.outputs.release_notes }}
|
||||
|
||||
- name: Download TestFlight artifact
|
||||
if: ${{ matrix.target != 'github_release' }}
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: ${{ matrix.artifact_name }}
|
||||
path: ${{ matrix.artifact_path }}
|
||||
|
||||
- name: Upload to TestFlight
|
||||
if: ${{ matrix.target != 'github_release' }}
|
||||
shell: bash
|
||||
run: bash ./scripts/ci/testflight_upload.sh "${{ matrix.testflight_platform }}" "${{ matrix.artifact_path }}"
|
||||
|
||||
@ -6,7 +6,8 @@ cd "$repo_root"
|
||||
eval "$(python3 "$repo_root/scripts/ci/build_version.py" --format shell)"
|
||||
platform="${1:?platform is required}"
|
||||
arch="${2:?arch is required}"
|
||||
should_release="${3:-false}"
|
||||
package_kind="${3:-}"
|
||||
should_release="${4:-false}"
|
||||
|
||||
flutter pub get
|
||||
|
||||
@ -15,9 +16,22 @@ case "$platform" in
|
||||
bash ./scripts/package-linux.sh
|
||||
;;
|
||||
macos)
|
||||
bash ./scripts/package-flutter-mac-app.sh
|
||||
mkdir -p dist/macos
|
||||
find dist -maxdepth 1 -name '*.dmg' -exec mv {} dist/macos/ \;
|
||||
case "$package_kind" in
|
||||
dmg)
|
||||
bash ./scripts/package-flutter-mac-app.sh
|
||||
mkdir -p dist/macos
|
||||
find dist -maxdepth 1 -name '*.dmg' -exec mv {} dist/macos/ \;
|
||||
;;
|
||||
app-store-pkg)
|
||||
bash ./scripts/package-macos-app-store-pkg.sh
|
||||
mkdir -p dist/macos-app-store
|
||||
find dist -maxdepth 1 -name '*.pkg' -exec mv {} dist/macos-app-store/ \;
|
||||
;;
|
||||
*)
|
||||
echo "Unsupported macOS package kind: $package_kind" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
windows)
|
||||
flutter build windows --release \
|
||||
|
||||
82
scripts/ci/testflight_upload.sh
Executable file
82
scripts/ci/testflight_upload.sh
Executable file
@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
platform="${1:?platform is required}"
|
||||
artifact_root="${2:?artifact root is required}"
|
||||
|
||||
required_vars=(
|
||||
APP_STORE_CONNECT_API_KEY_ID
|
||||
APP_STORE_CONNECT_ISSUER_ID
|
||||
APP_STORE_CONNECT_API_KEY_P8_BASE64
|
||||
)
|
||||
|
||||
missing=()
|
||||
for var_name in "${required_vars[@]}"; do
|
||||
if [[ -z "${!var_name:-}" ]]; then
|
||||
missing+=("$var_name")
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ "${#missing[@]}" -gt 0 ]]; then
|
||||
echo "Missing App Store Connect secrets: ${missing[*]}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v xcrun >/dev/null 2>&1; then
|
||||
echo "xcrun is required to upload TestFlight artifacts." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
apple_decode_base64() {
|
||||
if base64 --help 2>&1 | grep -q -- '--decode'; then
|
||||
base64 --decode
|
||||
else
|
||||
base64 -D
|
||||
fi
|
||||
}
|
||||
|
||||
tmp_dir="$(mktemp -d "${RUNNER_TEMP:-/tmp}/xworkmate-testflight.XXXXXX")"
|
||||
cleanup() {
|
||||
rm -rf "$tmp_dir"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
private_keys_dir="$tmp_dir/private_keys"
|
||||
mkdir -p "$private_keys_dir"
|
||||
|
||||
p8_path="$private_keys_dir/AuthKey_${APP_STORE_CONNECT_API_KEY_ID}.p8"
|
||||
printf '%s' "$APP_STORE_CONNECT_API_KEY_P8_BASE64" | apple_decode_base64 > "$p8_path"
|
||||
|
||||
case "$platform" in
|
||||
ios)
|
||||
artifact_file="$(find "$artifact_root" -type f -name '*.ipa' | head -n 1)"
|
||||
;;
|
||||
macos)
|
||||
artifact_file="$(find "$artifact_root" -type f -name '*.pkg' | head -n 1)"
|
||||
;;
|
||||
*)
|
||||
echo "Unsupported TestFlight platform: $platform" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
if [[ -z "$artifact_file" ]]; then
|
||||
echo "No ipa/pkg artifact found under $artifact_root" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
export API_PRIVATE_KEYS_DIR="$private_keys_dir"
|
||||
|
||||
if [[ "$platform" == "ios" ]]; then
|
||||
xcrun altool \
|
||||
--upload-app \
|
||||
-f "$artifact_file" \
|
||||
--api-key "$APP_STORE_CONNECT_API_KEY_ID" \
|
||||
--api-issuer "$APP_STORE_CONNECT_ISSUER_ID" \
|
||||
--show-progress
|
||||
else
|
||||
xcrun altool \
|
||||
--upload-package "$artifact_file" \
|
||||
--api-key "$APP_STORE_CONNECT_API_KEY_ID" \
|
||||
--api-issuer "$APP_STORE_CONNECT_ISSUER_ID" \
|
||||
--show-progress
|
||||
fi
|
||||
95
scripts/package-macos-app-store-pkg.sh
Executable file
95
scripts/package-macos-app-store-pkg.sh
Executable file
@ -0,0 +1,95 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
DIST_DIR="$ROOT_DIR/dist/macos-app-store"
|
||||
APP_NAME="${APP_NAME:-XWorkmate}"
|
||||
APP_STORE_DEFINE="${APP_STORE_DEFINE:---dart-define=XWORKMATE_APP_STORE=${XWORKMATE_APP_STORE:-true}}"
|
||||
source "$ROOT_DIR/scripts/ci/apple_signing.sh"
|
||||
APPLE_SIGNING_CLEANUP_COMMANDS=()
|
||||
trap apple_run_cleanup EXIT
|
||||
|
||||
required_vars=(
|
||||
APPLE_CERT_P12_BASE64
|
||||
APPLE_CERT_PASSWORD
|
||||
APPLE_MAC_PROVISION_PROFILE_BASE64
|
||||
APPLE_KEYCHAIN_PASSWORD
|
||||
)
|
||||
|
||||
missing=()
|
||||
for var_name in "${required_vars[@]}"; do
|
||||
if [[ -z "${!var_name:-}" ]]; then
|
||||
missing+=("$var_name")
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ "${#missing[@]}" -gt 0 ]]; then
|
||||
echo "Missing macOS TestFlight signing secrets: ${missing[*]}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
eval "$(python3 "$ROOT_DIR/scripts/ci/build_version.py" --format shell)"
|
||||
app_version="$DISPLAY_VERSION"
|
||||
app_build="$BUILD_NUMBER"
|
||||
BUILD_DATE_LINE="$(sed -n 's/^build-date:[[:space:]]*//p' "$ROOT_DIR/pubspec.yaml" | head -n 1)"
|
||||
BUILD_ID_LINE="$(sed -n 's/^build-id:[[:space:]]*//p' "$ROOT_DIR/pubspec.yaml" | head -n 1)"
|
||||
GIT_BUILD_DATE="$(cd "$ROOT_DIR" && git show -s --format=%cs HEAD 2>/dev/null || true)"
|
||||
GIT_BUILD_COMMIT="$(cd "$ROOT_DIR" && git rev-parse --short HEAD 2>/dev/null || true)"
|
||||
app_build_date="${GIT_BUILD_DATE:-${BUILD_DATE_LINE:-unknown}}"
|
||||
app_build_commit="${GIT_BUILD_COMMIT:-${BUILD_ID_LINE:-unknown}}"
|
||||
|
||||
tmp_dir="$(mktemp -d "${RUNNER_TEMP:-/tmp}/xworkmate-macos-app-store.XXXXXX")"
|
||||
cleanup() {
|
||||
rm -rf "$tmp_dir"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
apple_setup_signing_keychain
|
||||
|
||||
apple_decode_base64() {
|
||||
if base64 --help 2>&1 | grep -q -- '--decode'; then
|
||||
base64 --decode
|
||||
else
|
||||
base64 -D
|
||||
fi
|
||||
}
|
||||
|
||||
profile_dir="$HOME/Library/MobileDevice/Provisioning Profiles"
|
||||
profile_path="$profile_dir/xworkmate-macos.mobileprovision"
|
||||
mkdir -p "$profile_dir"
|
||||
printf '%s' "$APPLE_MAC_PROVISION_PROFILE_BASE64" | apple_decode_base64 > "$profile_path"
|
||||
apple_register_cleanup "rm -f \"$profile_path\""
|
||||
|
||||
mkdir -p "$DIST_DIR"
|
||||
archive_path="$tmp_dir/$APP_NAME.xcarchive"
|
||||
export_options_path="$tmp_dir/ExportOptions.plist"
|
||||
sed "s|\${EXPORT_METHOD}|app-store|g" "$ROOT_DIR/ios/ExportOptions.plist" > "$export_options_path"
|
||||
|
||||
flutter pub get
|
||||
flutter build macos --release \
|
||||
--build-name="$PLATFORM_RELEASE_VERSION" \
|
||||
--build-number="$app_build" \
|
||||
--dart-define="XWORKMATE_DISPLAY_VERSION=$app_version" \
|
||||
--dart-define="XWORKMATE_BUILD_NUMBER=$app_build" \
|
||||
--dart-define="XWORKMATE_BUILD_DATE=$app_build_date" \
|
||||
--dart-define="XWORKMATE_BUILD_COMMIT=$app_build_commit" \
|
||||
"$APP_STORE_DEFINE"
|
||||
|
||||
xcodebuild archive \
|
||||
-workspace "$ROOT_DIR/macos/Runner.xcworkspace" \
|
||||
-scheme Runner \
|
||||
-configuration Release \
|
||||
-archivePath "$archive_path" \
|
||||
DEVELOPMENT_TEAM="N3G9T67W78"
|
||||
|
||||
xcodebuild -exportArchive \
|
||||
-archivePath "$archive_path" \
|
||||
-exportPath "$DIST_DIR" \
|
||||
-exportOptionsPlist "$export_options_path"
|
||||
|
||||
if ! compgen -G "$DIST_DIR/*.pkg" >/dev/null; then
|
||||
echo "No macOS TestFlight pkg was produced under $DIST_DIR" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "macOS TestFlight pkg: $(find "$DIST_DIR" -maxdepth 1 -name '*.pkg' | head -n 1)"
|
||||
Loading…
Reference in New Issue
Block a user