merge: repair cross-platform release CI gating
This commit is contained in:
commit
d1df6fd920
25
.github/workflows/build-and-release.yml
vendored
25
.github/workflows/build-and-release.yml
vendored
@ -164,21 +164,36 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||||
|
|
||||||
|
- name: Set up Java 17 for Android
|
||||||
|
if: ${{ matrix.platform == 'android' }}
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
distribution: temurin
|
||||||
|
java-version: "17"
|
||||||
|
|
||||||
|
- name: Preflight platform lane
|
||||||
|
id: preflight
|
||||||
|
shell: bash
|
||||||
|
run: bash ./scripts/ci/platform_preflight.sh "$PLATFORM" "$SHOULD_RELEASE"
|
||||||
|
|
||||||
|
- name: Install platform dependencies
|
||||||
|
if: ${{ steps.preflight.outputs.should_build_platform == 'true' }}
|
||||||
|
shell: bash
|
||||||
|
run: bash ./scripts/ci/setup_platform_deps.sh "$PLATFORM"
|
||||||
|
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
if: ${{ matrix.platform == 'macos' }}
|
if: ${{ matrix.platform == 'macos' && steps.preflight.outputs.should_build_platform == 'true' }}
|
||||||
uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff
|
uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff
|
||||||
with:
|
with:
|
||||||
go-version: "1.24.1"
|
go-version: "1.24.1"
|
||||||
|
|
||||||
- name: Install platform dependencies
|
|
||||||
shell: bash
|
|
||||||
run: bash ./scripts/ci/setup_platform_deps.sh "$PLATFORM"
|
|
||||||
|
|
||||||
- name: Build platform artifacts
|
- name: Build platform artifacts
|
||||||
|
if: ${{ steps.preflight.outputs.should_build_platform == 'true' }}
|
||||||
shell: bash
|
shell: bash
|
||||||
run: bash ./scripts/ci/build_matrix_artifacts.sh "$PLATFORM" "$ARCH" "$SHOULD_RELEASE"
|
run: bash ./scripts/ci/build_matrix_artifacts.sh "$PLATFORM" "$ARCH" "$SHOULD_RELEASE"
|
||||||
|
|
||||||
- name: Upload build artifacts
|
- name: Upload build artifacts
|
||||||
|
if: ${{ steps.preflight.outputs.should_build_platform == 'true' }}
|
||||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
|
||||||
with:
|
with:
|
||||||
name: ${{ matrix.artifact_name }}
|
name: ${{ matrix.artifact_name }}
|
||||||
|
|||||||
84
scripts/ci/apple_signing.sh
Normal file
84
scripts/ci/apple_signing.sh
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
apple_decode_base64() {
|
||||||
|
if base64 --help 2>&1 | grep -q -- '--decode'; then
|
||||||
|
base64 --decode
|
||||||
|
else
|
||||||
|
base64 -D
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
apple_require_signing_vars() {
|
||||||
|
local missing=()
|
||||||
|
local var_name=""
|
||||||
|
|
||||||
|
for var_name in "$@"; do
|
||||||
|
if [[ -z "${!var_name:-}" ]]; then
|
||||||
|
missing+=("$var_name")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "${#missing[@]}" -gt 0 ]]; then
|
||||||
|
echo "Missing Apple signing secrets: ${missing[*]}" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
apple_register_cleanup() {
|
||||||
|
local command="$1"
|
||||||
|
APPLE_SIGNING_CLEANUP_COMMANDS+=("$command")
|
||||||
|
}
|
||||||
|
|
||||||
|
apple_run_cleanup() {
|
||||||
|
local status=$?
|
||||||
|
local index=0
|
||||||
|
|
||||||
|
for (( index=${#APPLE_SIGNING_CLEANUP_COMMANDS[@]}-1; index>=0; index-- )); do
|
||||||
|
eval "${APPLE_SIGNING_CLEANUP_COMMANDS[index]}" >/dev/null 2>&1 || true
|
||||||
|
done
|
||||||
|
|
||||||
|
return "$status"
|
||||||
|
}
|
||||||
|
|
||||||
|
apple_setup_signing_keychain() {
|
||||||
|
apple_require_signing_vars \
|
||||||
|
APPLE_CERT_P12_BASE64 \
|
||||||
|
APPLE_CERT_PASSWORD \
|
||||||
|
APPLE_KEYCHAIN_PASSWORD
|
||||||
|
|
||||||
|
local tmp_dir
|
||||||
|
tmp_dir="$(mktemp -d "${RUNNER_TEMP:-/tmp}/xworkmate-apple.XXXXXX")"
|
||||||
|
local keychain_name="xworkmate-build.keychain-db"
|
||||||
|
local keychain_path="$HOME/Library/Keychains/$keychain_name"
|
||||||
|
local cert_path="$tmp_dir/dist-cert.p12"
|
||||||
|
|
||||||
|
printf '%s' "$APPLE_CERT_P12_BASE64" | apple_decode_base64 > "$cert_path"
|
||||||
|
|
||||||
|
security create-keychain -p "$APPLE_KEYCHAIN_PASSWORD" "$keychain_name"
|
||||||
|
security set-keychain-settings -lut 21600 "$keychain_path"
|
||||||
|
security unlock-keychain -p "$APPLE_KEYCHAIN_PASSWORD" "$keychain_path"
|
||||||
|
security import "$cert_path" -P "$APPLE_CERT_PASSWORD" -A -t cert -f pkcs12 -k "$keychain_path"
|
||||||
|
security list-keychains -d user -s "$keychain_path"
|
||||||
|
security set-key-partition-list -S apple-tool:,apple: -s -k "$APPLE_KEYCHAIN_PASSWORD" "$keychain_path"
|
||||||
|
|
||||||
|
export APPLE_SIGNING_TMP_DIR="$tmp_dir"
|
||||||
|
export APPLE_SIGNING_KEYCHAIN_PATH="$keychain_path"
|
||||||
|
|
||||||
|
apple_register_cleanup "security delete-keychain \"$keychain_path\""
|
||||||
|
apple_register_cleanup "rm -rf \"$tmp_dir\""
|
||||||
|
}
|
||||||
|
|
||||||
|
apple_install_provision_profile() {
|
||||||
|
local profile_name="${1:-xworkmate.mobileprovision}"
|
||||||
|
|
||||||
|
apple_require_signing_vars APPLE_PROVISION_PROFILE_BASE64
|
||||||
|
|
||||||
|
local profile_dir="$HOME/Library/MobileDevice/Provisioning Profiles"
|
||||||
|
local profile_path="$profile_dir/$profile_name"
|
||||||
|
|
||||||
|
mkdir -p "$profile_dir"
|
||||||
|
printf '%s' "$APPLE_PROVISION_PROFILE_BASE64" | apple_decode_base64 > "$profile_path"
|
||||||
|
|
||||||
|
export APPLE_SIGNING_PROFILE_PATH="$profile_path"
|
||||||
|
apple_register_cleanup "rm -f \"$profile_path\""
|
||||||
|
}
|
||||||
@ -1,6 +1,9 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
cd "$repo_root"
|
||||||
|
eval "$(python3 "$repo_root/scripts/ci/build_version.py" --format shell)"
|
||||||
platform="${1:?platform is required}"
|
platform="${1:?platform is required}"
|
||||||
arch="${2:?arch is required}"
|
arch="${2:?arch is required}"
|
||||||
should_release="${3:-false}"
|
should_release="${3:-false}"
|
||||||
@ -17,7 +20,9 @@ case "$platform" in
|
|||||||
find dist -maxdepth 1 -name '*.dmg' -exec mv {} dist/macos/ \;
|
find dist -maxdepth 1 -name '*.dmg' -exec mv {} dist/macos/ \;
|
||||||
;;
|
;;
|
||||||
windows)
|
windows)
|
||||||
flutter build windows --release
|
flutter build windows --release \
|
||||||
|
--build-name="$PLATFORM_RELEASE_VERSION" \
|
||||||
|
--build-number="$BUILD_NUMBER"
|
||||||
pwsh -File ./scripts/package-windows-msi.ps1 -Arch "$arch"
|
pwsh -File ./scripts/package-windows-msi.ps1 -Arch "$arch"
|
||||||
;;
|
;;
|
||||||
ios)
|
ios)
|
||||||
@ -25,7 +30,11 @@ case "$platform" in
|
|||||||
bash ./scripts/package-ios-ipa.sh
|
bash ./scripts/package-ios-ipa.sh
|
||||||
else
|
else
|
||||||
echo "Release secrets not required for non-release runs; building unsigned iOS app bundle."
|
echo "Release secrets not required for non-release runs; building unsigned iOS app bundle."
|
||||||
flutter build ios --release --no-codesign
|
flutter build ios --release --no-codesign \
|
||||||
|
--build-name="$PLATFORM_RELEASE_VERSION" \
|
||||||
|
--build-number="$BUILD_NUMBER" \
|
||||||
|
--dart-define="XWORKMATE_DISPLAY_VERSION=$DISPLAY_VERSION" \
|
||||||
|
--dart-define="XWORKMATE_BUILD_NUMBER=$BUILD_NUMBER"
|
||||||
mkdir -p dist/ios
|
mkdir -p dist/ios
|
||||||
(
|
(
|
||||||
cd build/ios/iphoneos
|
cd build/ios/iphoneos
|
||||||
|
|||||||
108
scripts/ci/build_version.py
Normal file
108
scripts/ci/build_version.py
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Resolve platform-safe build versions from pubspec.yaml.
|
||||||
|
|
||||||
|
This repo keeps prerelease semantics in the pubspec version, for example:
|
||||||
|
1.0.0-beta.2+4
|
||||||
|
|
||||||
|
Packaging metadata across Apple/Linux/Windows must use the numeric release
|
||||||
|
portion only, while display-facing surfaces can keep the prerelease label.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import shlex
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
VERSION_PATTERN = re.compile(r"^version:\s*([^\n]+)", re.MULTILINE)
|
||||||
|
RELEASE_PATTERN = re.compile(r"^\d+\.\d+\.\d+$")
|
||||||
|
|
||||||
|
|
||||||
|
def parse_pubspec_version(pubspec_path: Path) -> dict[str, str]:
|
||||||
|
text = pubspec_path.read_text(encoding="utf-8")
|
||||||
|
match = VERSION_PATTERN.search(text)
|
||||||
|
if match is None:
|
||||||
|
raise ValueError(f"Unable to find version in {pubspec_path}")
|
||||||
|
|
||||||
|
raw_version = match.group(1).strip()
|
||||||
|
if not raw_version:
|
||||||
|
raise ValueError(f"Version in {pubspec_path} is empty")
|
||||||
|
|
||||||
|
if "+" in raw_version:
|
||||||
|
display_version, build_number = raw_version.split("+", 1)
|
||||||
|
else:
|
||||||
|
display_version, build_number = raw_version, "1"
|
||||||
|
|
||||||
|
display_version = display_version.strip()
|
||||||
|
build_number = build_number.strip()
|
||||||
|
platform_release_version = display_version.split("-", 1)[0].strip()
|
||||||
|
|
||||||
|
if not RELEASE_PATTERN.fullmatch(platform_release_version):
|
||||||
|
raise ValueError(
|
||||||
|
"Expected pubspec version to expose a three-part numeric release "
|
||||||
|
f"prefix before prerelease/build metadata, got: {raw_version}"
|
||||||
|
)
|
||||||
|
|
||||||
|
if not build_number.isdigit():
|
||||||
|
raise ValueError(f"Expected numeric build number in pubspec version, got: {raw_version}")
|
||||||
|
|
||||||
|
return {
|
||||||
|
"raw_version": raw_version,
|
||||||
|
"display_version": display_version,
|
||||||
|
"platform_release_version": platform_release_version,
|
||||||
|
"build_number": build_number,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def emit_shell(values: dict[str, str]) -> None:
|
||||||
|
for key, value in values.items():
|
||||||
|
env_key = key.upper()
|
||||||
|
print(f"{env_key}={shlex.quote(value)}")
|
||||||
|
|
||||||
|
|
||||||
|
def emit_json(values: dict[str, str]) -> None:
|
||||||
|
print(json.dumps(values))
|
||||||
|
|
||||||
|
|
||||||
|
def emit_github_output(values: dict[str, str]) -> None:
|
||||||
|
for key, value in values.items():
|
||||||
|
print(f"{key}={value}")
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument(
|
||||||
|
"--pubspec",
|
||||||
|
default="pubspec.yaml",
|
||||||
|
help="Path to pubspec.yaml",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--format",
|
||||||
|
choices=("shell", "json", "github-output"),
|
||||||
|
default="json",
|
||||||
|
help="Output format",
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
values = parse_pubspec_version(Path(args.pubspec))
|
||||||
|
|
||||||
|
if args.format == "shell":
|
||||||
|
emit_shell(values)
|
||||||
|
elif args.format == "github-output":
|
||||||
|
emit_github_output(values)
|
||||||
|
else:
|
||||||
|
emit_json(values)
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
raise SystemExit(main())
|
||||||
|
except Exception as exc: # pragma: no cover - CLI failure path
|
||||||
|
print(str(exc), file=sys.stderr)
|
||||||
|
raise SystemExit(1)
|
||||||
@ -6,22 +6,25 @@ if [[ -z "${GITHUB_OUTPUT:-}" ]]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
eval "$(python3 "$repo_root/scripts/ci/build_version.py" --format shell)"
|
||||||
|
|
||||||
if [[ "${GITHUB_REF_TYPE:-}" == "tag" ]]; then
|
if [[ "${GITHUB_REF_TYPE:-}" == "tag" ]]; then
|
||||||
release_tag="${GITHUB_REF_NAME}"
|
release_tag="${GITHUB_REF_NAME}"
|
||||||
release_title="Release ${GITHUB_REF_NAME}"
|
release_title="Release ${GITHUB_REF_NAME}"
|
||||||
release_notes="Automated release for ${GITHUB_REF_NAME}"
|
release_notes="Automated release for ${DISPLAY_VERSION}"
|
||||||
elif [[ "${GITHUB_EVENT_NAME:-}" == "workflow_dispatch" ]]; then
|
elif [[ "${GITHUB_EVENT_NAME:-}" == "workflow_dispatch" ]]; then
|
||||||
release_tag="manual-${GITHUB_RUN_NUMBER:-0}"
|
release_tag="manual-${GITHUB_RUN_NUMBER:-0}"
|
||||||
release_title="Manual Build ${GITHUB_RUN_NUMBER:-0}"
|
release_title="Manual Build ${GITHUB_RUN_NUMBER:-0} (${DISPLAY_VERSION})"
|
||||||
release_notes="Automated manual build from ${GITHUB_SHA:-unknown}"
|
release_notes="Automated manual build ${DISPLAY_VERSION} from ${GITHUB_SHA:-unknown}"
|
||||||
elif [[ "${GITHUB_REF:-}" == "refs/heads/main" ]]; then
|
elif [[ "${GITHUB_REF:-}" == "refs/heads/main" ]]; then
|
||||||
release_tag="latest"
|
release_tag="latest"
|
||||||
release_title="Latest"
|
release_title="Latest"
|
||||||
release_notes="Automated latest main build from ${GITHUB_SHA:-unknown}"
|
release_notes="Automated latest main build ${DISPLAY_VERSION} from ${GITHUB_SHA:-unknown}"
|
||||||
else
|
else
|
||||||
release_tag="main-${GITHUB_RUN_NUMBER:-0}"
|
release_tag="main-${GITHUB_RUN_NUMBER:-0}"
|
||||||
release_title="Main Build ${GITHUB_RUN_NUMBER:-0}"
|
release_title="Main Build ${GITHUB_RUN_NUMBER:-0} (${DISPLAY_VERSION})"
|
||||||
release_notes="Automated build from ${GITHUB_SHA:-unknown}"
|
release_notes="Automated build ${DISPLAY_VERSION} from ${GITHUB_SHA:-unknown}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
95
scripts/ci/platform_preflight.sh
Normal file
95
scripts/ci/platform_preflight.sh
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
platform="${1:?platform is required}"
|
||||||
|
should_release="${2:-false}"
|
||||||
|
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
|
||||||
|
emit_output() {
|
||||||
|
local key="$1"
|
||||||
|
local value="$2"
|
||||||
|
|
||||||
|
if [[ -n "${GITHUB_OUTPUT:-}" ]]; then
|
||||||
|
printf '%s=%s\n' "$key" "$value" >> "$GITHUB_OUTPUT"
|
||||||
|
else
|
||||||
|
printf '%s=%s\n' "$key" "$value"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
set_build_state() {
|
||||||
|
local should_build="$1"
|
||||||
|
local reason="$2"
|
||||||
|
|
||||||
|
emit_output "should_build_platform" "$should_build"
|
||||||
|
emit_output "skip_reason" "$reason"
|
||||||
|
|
||||||
|
if [[ "$should_build" == "true" ]]; then
|
||||||
|
echo "Preflight passed for $platform."
|
||||||
|
else
|
||||||
|
echo "Skipping $platform lane: $reason"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$platform" in
|
||||||
|
linux)
|
||||||
|
set_build_state "true" ""
|
||||||
|
;;
|
||||||
|
windows)
|
||||||
|
set_build_state "true" ""
|
||||||
|
;;
|
||||||
|
macos)
|
||||||
|
required_vars=(
|
||||||
|
APPLE_CERT_P12_BASE64
|
||||||
|
APPLE_CERT_PASSWORD
|
||||||
|
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
|
||||||
|
set_build_state "false" "missing macOS signing secrets: ${missing[*]}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
set_build_state "true" ""
|
||||||
|
;;
|
||||||
|
ios)
|
||||||
|
if [[ "$should_release" != "true" ]]; then
|
||||||
|
set_build_state "true" ""
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
required_vars=(
|
||||||
|
APPLE_CERT_P12_BASE64
|
||||||
|
APPLE_CERT_PASSWORD
|
||||||
|
APPLE_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
|
||||||
|
set_build_state "false" "missing iOS signing secrets: ${missing[*]}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
set_build_state "true" ""
|
||||||
|
;;
|
||||||
|
android)
|
||||||
|
set_build_state "true" ""
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unsupported platform: $platform" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
@ -2,6 +2,8 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
platform="${1:?platform is required}"
|
platform="${1:?platform is required}"
|
||||||
|
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
cd "$repo_root"
|
||||||
|
|
||||||
case "$platform" in
|
case "$platform" in
|
||||||
linux)
|
linux)
|
||||||
@ -52,16 +54,14 @@ case "$platform" in
|
|||||||
|
|
||||||
flutter_bin="$(command -v flutter)"
|
flutter_bin="$(command -v flutter)"
|
||||||
flutter_root="$(cd "$(dirname "$flutter_bin")/.." && pwd)"
|
flutter_root="$(cd "$(dirname "$flutter_bin")/.." && pwd)"
|
||||||
app_version="$(sed -n 's/^version:[[:space:]]*//p' pubspec.yaml | head -n 1)"
|
eval "$(python3 "$repo_root/scripts/ci/build_version.py" --format shell)"
|
||||||
app_version="${app_version%%+*}"
|
|
||||||
version_code="${GITHUB_RUN_NUMBER:-1}"
|
|
||||||
|
|
||||||
cat > android/local.properties <<EOF
|
cat > android/local.properties <<EOF
|
||||||
sdk.dir=$android_sdk_root
|
sdk.dir=$android_sdk_root
|
||||||
flutter.sdk=$flutter_root
|
flutter.sdk=$flutter_root
|
||||||
flutter.buildMode=release
|
flutter.buildMode=release
|
||||||
flutter.versionName=$app_version
|
flutter.versionName=$DISPLAY_VERSION
|
||||||
flutter.versionCode=$version_code
|
flutter.versionCode=$BUILD_NUMBER
|
||||||
EOF
|
EOF
|
||||||
;;
|
;;
|
||||||
macos)
|
macos)
|
||||||
|
|||||||
@ -13,6 +13,9 @@ PRODUCTS_DIR_NAME="$(tr '[:lower:]' '[:upper:]' <<< "${BUILD_MODE:0:1}")${BUILD_
|
|||||||
FLUTTER_BUILD_STATE_DIR="${ROOT_DIR}/.dart_tool/flutter_build"
|
FLUTTER_BUILD_STATE_DIR="${ROOT_DIR}/.dart_tool/flutter_build"
|
||||||
MACOS_BUILD_DIR="${ROOT_DIR}/build/macos"
|
MACOS_BUILD_DIR="${ROOT_DIR}/build/macos"
|
||||||
NATIVE_ASSETS_DIR="${ROOT_DIR}/build/native_assets"
|
NATIVE_ASSETS_DIR="${ROOT_DIR}/build/native_assets"
|
||||||
|
source "$ROOT_DIR/scripts/ci/apple_signing.sh"
|
||||||
|
APPLE_SIGNING_CLEANUP_COMMANDS=()
|
||||||
|
trap apple_run_cleanup EXIT
|
||||||
|
|
||||||
remove_tree_with_retries() {
|
remove_tree_with_retries() {
|
||||||
local path="$1"
|
local path="$1"
|
||||||
@ -45,19 +48,13 @@ if [[ ! -f "$PUBSPEC_PATH" ]]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
VERSION_LINE="$(sed -n 's/^version:[[:space:]]*//p' "$PUBSPEC_PATH" | head -n 1)"
|
eval "$(python3 "$ROOT_DIR/scripts/ci/build_version.py" --format shell)"
|
||||||
if [[ -z "$VERSION_LINE" ]]; then
|
|
||||||
echo "Unable to read version from $PUBSPEC_PATH" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
BUILD_DATE_LINE="$(sed -n 's/^build-date:[[:space:]]*//p' "$PUBSPEC_PATH" | head -n 1)"
|
BUILD_DATE_LINE="$(sed -n 's/^build-date:[[:space:]]*//p' "$PUBSPEC_PATH" | head -n 1)"
|
||||||
BUILD_ID_LINE="$(sed -n 's/^build-id:[[:space:]]*//p' "$PUBSPEC_PATH" | head -n 1)"
|
BUILD_ID_LINE="$(sed -n 's/^build-id:[[:space:]]*//p' "$PUBSPEC_PATH" | head -n 1)"
|
||||||
|
|
||||||
APP_VERSION="${VERSION_LINE%%+*}"
|
APP_VERSION="$DISPLAY_VERSION"
|
||||||
APP_BUILD="${VERSION_LINE#*+}"
|
APP_RELEASE_VERSION="$PLATFORM_RELEASE_VERSION"
|
||||||
if [[ "$APP_BUILD" == "$VERSION_LINE" ]]; then
|
APP_BUILD="$BUILD_NUMBER"
|
||||||
APP_BUILD="1"
|
|
||||||
fi
|
|
||||||
APP_BUILD_DATE="${BUILD_DATE_LINE:-unknown}"
|
APP_BUILD_DATE="${BUILD_DATE_LINE:-unknown}"
|
||||||
APP_BUILD_COMMIT="${BUILD_ID_LINE:-unknown}"
|
APP_BUILD_COMMIT="${BUILD_ID_LINE:-unknown}"
|
||||||
|
|
||||||
@ -74,10 +71,19 @@ remove_tree_with_retries "$FLUTTER_BUILD_STATE_DIR"
|
|||||||
remove_tree_with_retries "$MACOS_BUILD_DIR"
|
remove_tree_with_retries "$MACOS_BUILD_DIR"
|
||||||
remove_tree_with_retries "$NATIVE_ASSETS_DIR"
|
remove_tree_with_retries "$NATIVE_ASSETS_DIR"
|
||||||
|
|
||||||
|
if [[ -n "${APPLE_CERT_P12_BASE64:-}" &&
|
||||||
|
-n "${APPLE_CERT_PASSWORD:-}" &&
|
||||||
|
-n "${APPLE_KEYCHAIN_PASSWORD:-}" ]]; then
|
||||||
|
echo "Provisioning Apple signing certificate for macOS build..."
|
||||||
|
apple_setup_signing_keychain
|
||||||
|
else
|
||||||
|
echo "Apple signing secrets not set; using existing local macOS signing context."
|
||||||
|
fi
|
||||||
|
|
||||||
BUILD_ARGS=(
|
BUILD_ARGS=(
|
||||||
flutter build macos
|
flutter build macos
|
||||||
"--$BUILD_MODE"
|
"--$BUILD_MODE"
|
||||||
--build-name="$APP_VERSION"
|
--build-name="$APP_RELEASE_VERSION"
|
||||||
--build-number="$APP_BUILD"
|
--build-number="$APP_BUILD"
|
||||||
--dart-define="XWORKMATE_DISPLAY_VERSION=$APP_VERSION"
|
--dart-define="XWORKMATE_DISPLAY_VERSION=$APP_VERSION"
|
||||||
--dart-define="XWORKMATE_BUILD_NUMBER=$APP_BUILD"
|
--dart-define="XWORKMATE_BUILD_NUMBER=$APP_BUILD"
|
||||||
|
|||||||
@ -4,17 +4,13 @@ set -euo pipefail
|
|||||||
root_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
root_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
dist_dir="$root_dir/dist/ios"
|
dist_dir="$root_dir/dist/ios"
|
||||||
export_method="${APPLE_EXPORT_METHOD:-ad-hoc}"
|
export_method="${APPLE_EXPORT_METHOD:-ad-hoc}"
|
||||||
|
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
|
||||||
|
|
||||||
mkdir -p "$dist_dir"
|
mkdir -p "$dist_dir"
|
||||||
|
|
||||||
decode_base64() {
|
|
||||||
if base64 --help 2>&1 | grep -q -- '--decode'; then
|
|
||||||
base64 --decode
|
|
||||||
else
|
|
||||||
base64 -D
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
required_vars=(
|
required_vars=(
|
||||||
APPLE_CERT_P12_BASE64
|
APPLE_CERT_P12_BASE64
|
||||||
APPLE_CERT_PASSWORD
|
APPLE_CERT_PASSWORD
|
||||||
@ -34,36 +30,25 @@ if [[ "${#missing[@]}" -gt 0 ]]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
tmp_dir="$(mktemp -d "${RUNNER_TEMP:-/tmp}/xworkmate-ios.XXXXXX")"
|
eval "$(python3 "$root_dir/scripts/ci/build_version.py" --format shell)"
|
||||||
keychain_name="xworkmate-build.keychain-db"
|
app_version="$DISPLAY_VERSION"
|
||||||
keychain_path="$HOME/Library/Keychains/$keychain_name"
|
app_build="$BUILD_NUMBER"
|
||||||
cert_path="$tmp_dir/dist-cert.p12"
|
apple_setup_signing_keychain
|
||||||
profile_path="$tmp_dir/profile.mobileprovision"
|
apple_install_provision_profile "xworkmate.mobileprovision"
|
||||||
|
|
||||||
|
tmp_dir="$APPLE_SIGNING_TMP_DIR"
|
||||||
export_options_path="$tmp_dir/ExportOptions.plist"
|
export_options_path="$tmp_dir/ExportOptions.plist"
|
||||||
|
|
||||||
cleanup() {
|
|
||||||
security delete-keychain "$keychain_path" >/dev/null 2>&1 || true
|
|
||||||
rm -rf "$tmp_dir"
|
|
||||||
}
|
|
||||||
trap cleanup EXIT
|
|
||||||
|
|
||||||
printf '%s' "$APPLE_CERT_P12_BASE64" | decode_base64 > "$cert_path"
|
|
||||||
printf '%s' "$APPLE_PROVISION_PROFILE_BASE64" | decode_base64 > "$profile_path"
|
|
||||||
|
|
||||||
security create-keychain -p "$APPLE_KEYCHAIN_PASSWORD" "$keychain_name"
|
|
||||||
security set-keychain-settings -lut 21600 "$keychain_path"
|
|
||||||
security unlock-keychain -p "$APPLE_KEYCHAIN_PASSWORD" "$keychain_path"
|
|
||||||
security import "$cert_path" -P "$APPLE_CERT_PASSWORD" -A -t cert -f pkcs12 -k "$keychain_path"
|
|
||||||
security list-keychains -d user -s "$keychain_path"
|
|
||||||
security set-key-partition-list -S apple-tool:,apple: -s -k "$APPLE_KEYCHAIN_PASSWORD" "$keychain_path"
|
|
||||||
|
|
||||||
mkdir -p "$HOME/Library/MobileDevice/Provisioning Profiles"
|
|
||||||
cp "$profile_path" "$HOME/Library/MobileDevice/Provisioning Profiles/xworkmate.mobileprovision"
|
|
||||||
|
|
||||||
sed "s|\${EXPORT_METHOD}|$export_method|g" "$root_dir/ios/ExportOptions.plist" > "$export_options_path"
|
sed "s|\${EXPORT_METHOD}|$export_method|g" "$root_dir/ios/ExportOptions.plist" > "$export_options_path"
|
||||||
|
|
||||||
flutter pub get
|
flutter pub get
|
||||||
flutter build ipa --release --export-options-plist="$export_options_path"
|
flutter build ipa --release \
|
||||||
|
--build-name="$PLATFORM_RELEASE_VERSION" \
|
||||||
|
--build-number="$app_build" \
|
||||||
|
--dart-define="XWORKMATE_DISPLAY_VERSION=$app_version" \
|
||||||
|
--dart-define="XWORKMATE_BUILD_NUMBER=$app_build" \
|
||||||
|
"$app_store_define" \
|
||||||
|
--export-options-plist="$export_options_path"
|
||||||
|
|
||||||
archive_path="$root_dir/build/ios/archive/Runner.xcarchive"
|
archive_path="$root_dir/build/ios/archive/Runner.xcarchive"
|
||||||
if [[ -d "$archive_path" ]]; then
|
if [[ -d "$archive_path" ]]; then
|
||||||
|
|||||||
@ -3,14 +3,9 @@ set -euo pipefail
|
|||||||
|
|
||||||
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
app_name="xworkmate"
|
app_name="xworkmate"
|
||||||
version="$(python3 - <<'PY'
|
|
||||||
from pathlib import Path
|
eval "$(python3 "$repo_root/scripts/ci/build_version.py" --format shell)"
|
||||||
import re
|
package_version="${PLATFORM_RELEASE_VERSION}-${BUILD_NUMBER}"
|
||||||
text = Path("pubspec.yaml").read_text()
|
|
||||||
match = re.search(r"^version:\s*([^\n+]+)", text, re.M)
|
|
||||||
print(match.group(1) if match else "0.0.0")
|
|
||||||
PY
|
|
||||||
)"
|
|
||||||
|
|
||||||
build_dir="$repo_root/build/linux/x64/release/bundle"
|
build_dir="$repo_root/build/linux/x64/release/bundle"
|
||||||
stage_dir="$repo_root/build/linux/deb-stage"
|
stage_dir="$repo_root/build/linux/deb-stage"
|
||||||
@ -39,7 +34,7 @@ chmod 0755 "$stage_dir/DEBIAN/postinst" "$stage_dir/DEBIAN/postrm"
|
|||||||
|
|
||||||
cat > "$stage_dir/DEBIAN/control" <<EOF
|
cat > "$stage_dir/DEBIAN/control" <<EOF
|
||||||
Package: $app_name
|
Package: $app_name
|
||||||
Version: $version
|
Version: $package_version
|
||||||
Section: utils
|
Section: utils
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Architecture: amd64
|
Architecture: amd64
|
||||||
@ -49,4 +44,4 @@ Description: XWorkmate Linux desktop shell with GNOME/KDE proxy and tunnel integ
|
|||||||
EOF
|
EOF
|
||||||
|
|
||||||
mkdir -p "$out_dir"
|
mkdir -p "$out_dir"
|
||||||
dpkg-deb --build "$stage_dir" "$out_dir/${app_name}_${version}_amd64.deb"
|
dpkg-deb --build "$stage_dir" "$out_dir/${app_name}_${package_version}_amd64.deb"
|
||||||
|
|||||||
@ -3,14 +3,8 @@ set -euo pipefail
|
|||||||
|
|
||||||
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
app_name="xworkmate"
|
app_name="xworkmate"
|
||||||
version="$(python3 - <<'PY'
|
|
||||||
from pathlib import Path
|
eval "$(python3 "$repo_root/scripts/ci/build_version.py" --format shell)"
|
||||||
import re
|
|
||||||
text = Path("pubspec.yaml").read_text()
|
|
||||||
match = re.search(r"^version:\s*([^\n+]+)", text, re.M)
|
|
||||||
print(match.group(1) if match else "0.0.0")
|
|
||||||
PY
|
|
||||||
)"
|
|
||||||
|
|
||||||
bundle_dir="$repo_root/build/linux/x64/release/bundle"
|
bundle_dir="$repo_root/build/linux/x64/release/bundle"
|
||||||
rpm_root="$repo_root/build/linux/rpm"
|
rpm_root="$repo_root/build/linux/rpm"
|
||||||
@ -34,8 +28,8 @@ cp "$repo_root/linux/packaging/icons/xworkmate.svg" \
|
|||||||
|
|
||||||
cat > "$spec_file" <<EOF
|
cat > "$spec_file" <<EOF
|
||||||
Name: $app_name
|
Name: $app_name
|
||||||
Version: $version
|
Version: $PLATFORM_RELEASE_VERSION
|
||||||
Release: 1%{?dist}
|
Release: $BUILD_NUMBER%{?dist}
|
||||||
Summary: XWorkmate Linux desktop shell
|
Summary: XWorkmate Linux desktop shell
|
||||||
License: Proprietary
|
License: Proprietary
|
||||||
BuildArch: x86_64
|
BuildArch: x86_64
|
||||||
|
|||||||
@ -3,5 +3,5 @@ set -euo pipefail
|
|||||||
|
|
||||||
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
|
||||||
"$repo_root/scripts/package-linux-deb.sh"
|
bash "$repo_root/scripts/package-linux-deb.sh"
|
||||||
"$repo_root/scripts/package-linux-rpm.sh"
|
bash "$repo_root/scripts/package-linux-rpm.sh"
|
||||||
|
|||||||
@ -8,9 +8,13 @@ $root = Resolve-Path (Join-Path $PSScriptRoot "..")
|
|||||||
$sourceDir = Join-Path $root "build\windows\x64\runner\Release"
|
$sourceDir = Join-Path $root "build\windows\x64\runner\Release"
|
||||||
$distDir = Join-Path $root "dist\windows"
|
$distDir = Join-Path $root "dist\windows"
|
||||||
$wxsPath = Join-Path $root "packaging\windows\main.wxs"
|
$wxsPath = Join-Path $root "packaging\windows\main.wxs"
|
||||||
$versionLine = (Get-Content (Join-Path $root "pubspec.yaml") | Select-String '^version:\s*').ToString()
|
$metadataScript = (Join-Path $root "scripts\ci\build_version.py").Replace('\', '/')
|
||||||
$versionValue = ($versionLine -replace '^version:\s*', '').Split('+')[0]
|
$metadata = (& python $metadataScript --format json | ConvertFrom-Json)
|
||||||
$msiPath = Join-Path $distDir "xworkmate-$versionValue-$Arch.msi"
|
$displayVersion = $metadata.display_version
|
||||||
|
$platformReleaseVersion = $metadata.platform_release_version
|
||||||
|
$buildNumber = $metadata.build_number
|
||||||
|
Write-Host "Packaging Windows MSI for $displayVersion (build $buildNumber)"
|
||||||
|
$msiPath = Join-Path $distDir "xworkmate-$displayVersion-$Arch.msi"
|
||||||
$zipPath = Join-Path $distDir "xworkmate-windows-$Arch.zip"
|
$zipPath = Join-Path $distDir "xworkmate-windows-$Arch.zip"
|
||||||
|
|
||||||
if (-not (Test-Path $sourceDir)) {
|
if (-not (Test-Path $sourceDir)) {
|
||||||
@ -24,15 +28,10 @@ if (Test-Path $zipPath) {
|
|||||||
}
|
}
|
||||||
Compress-Archive -Path (Join-Path $sourceDir '*') -DestinationPath $zipPath
|
Compress-Archive -Path (Join-Path $sourceDir '*') -DestinationPath $zipPath
|
||||||
|
|
||||||
$wixVersion = $versionValue
|
|
||||||
if ($wixVersion -notmatch '^\d+\.\d+\.\d+$') {
|
|
||||||
$wixVersion = "0.0.0"
|
|
||||||
}
|
|
||||||
|
|
||||||
& wix build $wxsPath `
|
& wix build $wxsPath `
|
||||||
-arch x64 `
|
-arch x64 `
|
||||||
-d SourceDir=$sourceDir `
|
-d SourceDir=$sourceDir `
|
||||||
-d ProductVersion=$wixVersion `
|
-d ProductVersion=$platformReleaseVersion `
|
||||||
-o $msiPath
|
-o $msiPath
|
||||||
|
|
||||||
if ($env:WINDOWS_PFX_BASE64 -and $env:WINDOWS_PFX_PASSWORD) {
|
if ($env:WINDOWS_PFX_BASE64 -and $env:WINDOWS_PFX_PASSWORD) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user