Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 101 additions & 3 deletions .github/scripts/macos_smoke_suite.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,113 @@ RUN_CONNECT_SMOKE="${RUN_CONNECT_SMOKE:-true}"
ENABLE_IP_CHECK="${ENABLE_IP_CHECK:-false}"
FORCE_FULL_TUNNEL="${FORCE_FULL_TUNNEL:-true}"
EXTENSION_TIMEOUT_SECONDS="${EXTENSION_TIMEOUT_SECONDS:-120}"
APP_INSTALL_DIR="${APP_INSTALL_DIR:-${RUNNER_TEMP:-/tmp}/lantern-macos-smoke/Lantern.app}"
DMG_MOUNT_DIR=""

log_step() {
printf '[%s] %s\n' "$(date +%H:%M:%S)" "$*"
printf '[%s] %s\n' "$(date +%H:%M:%S)" "$*" >&2
}

find_first_name() {
local root="$1"
local name="$2"

if [[ -z "$root" || ! -e "$root" ]]; then
return 1
fi

find "$root" -maxdepth 4 -name "$name" -print -quit
}

copy_app_bundle() {
local source="$1"
local destination="$2"

log_step "Copying Lantern app from $source"
rm -rf "$destination"
mkdir -p "$(dirname "$destination")"
ditto "$source" "$destination"
xattr -dr com.apple.quarantine "$destination" 2>/dev/null || true
printf '%s\n' "$destination"
}

extract_app_zip() {
local zip_path="$1"
local tmp_dir
tmp_dir="$(mktemp -d)"

log_step "Extracting Lantern app from $zip_path"
ditto -x -k "$zip_path" "$tmp_dir"

local app_path
app_path="$(find_first_name "$tmp_dir" "Lantern.app" || true)"
if [[ -z "$app_path" ]]; then
printf 'Lantern.app not found inside %s\n' "$zip_path" >&2
rm -rf "$tmp_dir"
return 1
fi

copy_app_bundle "$app_path" "$APP_INSTALL_DIR"
rm -rf "$tmp_dir"
}
Comment thread
Copilot marked this conversation as resolved.

detach_dmg() {
if [[ -n "$DMG_MOUNT_DIR" && -d "$DMG_MOUNT_DIR" ]]; then
local mount_dir="$DMG_MOUNT_DIR"
hdiutil detach "$mount_dir" -quiet || true
rmdir "$mount_dir" 2>/dev/null || true
DMG_MOUNT_DIR=""
fi
}

copy_app_from_dmg() {
local dmg_path="$1"
local mount_dir
mount_dir="$(mktemp -d)"
DMG_MOUNT_DIR="$mount_dir"

log_step "Mounting Lantern DMG $dmg_path"
hdiutil attach "$dmg_path" -nobrowse -readonly -mountpoint "$mount_dir" >/dev/null

local app_path
app_path="$(find_first_name "$mount_dir" "Lantern.app" || true)"
if [[ -z "$app_path" ]]; then
printf 'Lantern.app not found inside %s\n' "$dmg_path" >&2
detach_dmg
return 1
fi

copy_app_bundle "$app_path" "$APP_INSTALL_DIR"
detach_dmg
}

resolve_app_path() {
if [[ -n "${APP_PATH:-}" ]]; then
if [[ -n "${APP_PATH:-}" && -x "$APP_PATH/Contents/MacOS/Lantern" ]]; then
printf '%s\n' "$APP_PATH"
return
fi

local dmg_path
dmg_path="$(find_first_name "${DMG_ARTIFACT_DIR:-}" "*.dmg" || true)"
if [[ -n "$dmg_path" ]]; then
copy_app_from_dmg "$dmg_path"
return
fi

local app_zip
app_zip="$(find_first_name "${APP_ARTIFACT_DIR:-}" "Lantern.app.zip" || true)"
if [[ -n "$app_zip" ]]; then
extract_app_zip "$app_zip"
return
fi

local app_artifact
app_artifact="$(find_first_name "${APP_ARTIFACT_DIR:-}" "Lantern.app" || true)"
if [[ -n "$app_artifact" ]]; then
copy_app_bundle "$app_artifact" "$APP_INSTALL_DIR"
return
fi

local candidates=(
"build/macos/Build/Products/Release/Lantern.app"
"build/macos/Build/Products/Debug/Lantern.app"
Expand All @@ -31,7 +127,8 @@ resolve_app_path() {
fi
done

printf '%s\n' "${candidates[0]}"
printf 'Lantern.app was not found. Set APP_PATH, or provide APP_ARTIFACT_DIR/DMG_ARTIFACT_DIR.\n' >&2
return 1
}

capture_command() {
Expand Down Expand Up @@ -177,6 +274,7 @@ on_exit() {
if [[ "$status" -ne 0 ]]; then
capture_diagnostics "failure"
fi
detach_dmg

exit "$status"
}
Expand Down
14 changes: 11 additions & 3 deletions .github/workflows/app-smoke-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ on:
type: boolean
default: true
macos_connect_smoke:
description: "Include macOS connect/disconnect smoke when platforms=all (requires approved system extension)"
description: "Include macOS connect/disconnect smoke when platforms=all (requires self-hosted runner with approved system extension)"
required: false
type: boolean
default: false
Expand Down Expand Up @@ -113,17 +113,25 @@ jobs:

macos:
needs: prepare
# macOS smoke stays opt-in for "all" until runners have system-extension approval.
if: ${{ inputs.platforms == 'macos' || (inputs.platforms == 'all' && inputs.macos_connect_smoke) }}
uses: ./.github/workflows/build-macos.yml
secrets: inherit
with:
version: ${{ needs.prepare.outputs.version }}
build_type: nightly
installer_base_name: ${{ needs.prepare.outputs.installer_base_name }}

macos-connect-smoke:
needs: [prepare, macos]
if: ${{ inputs.platforms == 'macos' || (inputs.platforms == 'all' && inputs.macos_connect_smoke) }}
uses: ./.github/workflows/macos-connect-smoke.yml
secrets:
APP_ENV: ${{ secrets.APP_ENV }}
with:
version: ${{ needs.prepare.outputs.version }}
build_type: nightly
enable_ip_check: ${{ inputs.enable_ip_check }}
force_full_tunnel_smoke: ${{ inputs.force_full_tunnel_smoke }}
run_connect_smoke: true

windows:
needs: prepare
Expand Down
45 changes: 13 additions & 32 deletions .github/workflows/build-macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,6 @@ on:
installer_base_name:
required: true
type: string
run_connect_smoke:
description: "Run macOS connect/disconnect smoke test"
required: false
type: boolean
default: false
enable_ip_check:
description: "Enable public IP change validation in macOS connect smoke"
required: false
type: boolean
default: false
force_full_tunnel_smoke:
description: "Force full tunnel routing mode in macOS connect smoke"
required: false
type: boolean
default: true

jobs:
build-macos:
permissions:
Expand Down Expand Up @@ -165,25 +149,22 @@ jobs:
BUCKET: ${{ vars.S3_RELEASES_BUCKET }}
run: bash ./.github/scripts/check_macos_sysext_build.sh

- name: macOS connect smoke suite
if: ${{ inputs.run_connect_smoke }}
- name: Package macOS app artifact
shell: bash
timeout-minutes: 20
env:
APP_PATH: build/macos/Build/Products/Release/Lantern.app
ARTIFACT_DIR: ${{ runner.temp }}/macos-smoke-artifacts
ENABLE_IP_CHECK: ${{ inputs.enable_ip_check }}
FORCE_FULL_TUNNEL: ${{ inputs.force_full_tunnel_smoke }}
RUN_CONNECT_SMOKE: true
run: ./.github/scripts/macos_smoke_suite.sh

- name: Upload macOS smoke diagnostics
if: ${{ always() && inputs.run_connect_smoke }}
run: |
APP_PATH="build/macos/Build/Products/Release/Lantern.app"
if [[ ! -d "$APP_PATH" ]]; then
printf 'Lantern.app not found at %s\n' "$APP_PATH" >&2
exit 1
fi

ditto -c -k --keepParent "$APP_PATH" "$RUNNER_TEMP/Lantern.app.zip"

- name: Upload macOS app
uses: actions/upload-artifact@v4
with:
name: macos-smoke-diagnostics
path: ${{ runner.temp }}/macos-smoke-artifacts
if-no-files-found: ignore
name: lantern-macos-app
path: ${{ runner.temp }}/Lantern.app.zip
retention-days: 2

- name: Upload macOS installer
Expand Down
109 changes: 109 additions & 0 deletions .github/workflows/macos-connect-smoke.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
name: macOS Connect Smoke

on:
workflow_call:
inputs:
version:
required: true
type: string
build_type:
required: true
type: string
app_artifact_name:
required: false
type: string
default: lantern-macos-app
dmg_artifact_name:
required: false
type: string
default: lantern-installer-dmg
pubspec_artifact_name:
required: false
type: string
default: pubspec
enable_ip_check:
description: "Validate public IP change during macOS connect smoke"
required: false
type: boolean
default: false
force_full_tunnel_smoke:
description: "Force full tunnel routing mode during macOS connect smoke"
required: false
type: boolean
default: true
extension_timeout_seconds:
description: "Seconds to wait for system extension activation"
required: false
type: number
default: 120
secrets:
APP_ENV:
required: true

jobs:
macos-connect-smoke:
permissions:
contents: read
runs-on: [self-hosted, macOS, ARM64, lantern-macos-smoke]
timeout-minutes: 40
env:
ARTIFACT_DIR: ${{ runner.temp }}/macos-smoke-artifacts
APP_ARTIFACT_DIR: ${{ runner.temp }}/lantern-macos-app
DMG_ARTIFACT_DIR: ${{ runner.temp }}/lantern-installer-dmg
BUILD_TYPE: ${{ inputs.build_type }}
VERSION: ${{ inputs.version }}
ENABLE_IP_CHECK: ${{ inputs.enable_ip_check }}
FORCE_FULL_TUNNEL: ${{ inputs.force_full_tunnel_smoke }}
EXTENSION_TIMEOUT_SECONDS: ${{ inputs.extension_timeout_seconds }}
RUN_CONNECT_SMOKE: true
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Download pubspec.yaml
uses: actions/download-artifact@v4
with:
name: ${{ inputs.pubspec_artifact_name }}

- name: Download macOS app artifact
uses: actions/download-artifact@v4
with:
name: ${{ inputs.app_artifact_name }}
path: ${{ env.APP_ARTIFACT_DIR }}

- name: Download macOS DMG artifact
uses: actions/download-artifact@v4
with:
name: ${{ inputs.dmg_artifact_name }}
path: ${{ env.DMG_ARTIFACT_DIR }}

- name: Decode APP_ENV
uses: timheuer/base64-to-file@v1.2
with:
fileName: "app.env"
fileDir: ${{ github.workspace }}
encodedString: ${{ secrets.APP_ENV }}

- name: Prepare macOS test project
shell: bash
run: |
set -euo pipefail
echo "GOMOBILECACHE=$HOME/.cache/gomobile" >> "$GITHUB_ENV"
mkdir -p "$HOME/.cache/gomobile"
flutter --version
flutter config --enable-macos-desktop
make install-gomobile
make macos pubget gen

Comment thread
atavism marked this conversation as resolved.
- name: macOS connect smoke suite
shell: bash
run: ./.github/scripts/macos_smoke_suite.sh
Comment thread
atavism marked this conversation as resolved.

- name: Upload macOS smoke diagnostics
if: ${{ always() }}
uses: actions/upload-artifact@v4
with:
name: macos-smoke-diagnostics
path: ${{ env.ARTIFACT_DIR }}
if-no-files-found: ignore
retention-days: 2