diff --git a/.github/actions/prepare-android-integration/action.yml b/.github/actions/prepare-android-integration/action.yml new file mode 100644 index 00000000..7ab869e7 --- /dev/null +++ b/.github/actions/prepare-android-integration/action.yml @@ -0,0 +1,34 @@ +name: Prepare isolated Android integration +description: Keep SDK, Flutter settings and bounded Gradle processes local to this job. +runs: + using: composite + steps: + - name: Prepare private Android build state and select the job JDK + shell: bash + run: | + set -euo pipefail + build_directory="$(mktemp -d "$RUNNER_TEMP/zstandard-android-build.XXXXXX")" + sdk_root="$build_directory/sdk" + gradle_root="$build_directory/gradle" + flutter_profile="$build_directory/flutter-profile" + mkdir -p "$sdk_root" "$gradle_root" "$flutter_profile" "$build_directory/avd" "$build_directory/android-user" + cat > "$gradle_root/gradle.properties" <<'PROPERTIES' + org.gradle.daemon=false + org.gradle.parallel=false + org.gradle.workers.max=2 + org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=1g -Dfile.encoding=UTF-8 + kotlin.compiler.execution.strategy=in-process + PROPERTIES + { + echo "ANDROID_HOME=$sdk_root" + echo "ANDROID_SDK_ROOT=$sdk_root" + echo "ANDROID_AVD_HOME=$build_directory/avd" + echo "ANDROID_USER_HOME=$build_directory/android-user" + echo "ANDROID_EMULATOR_HOME=$build_directory/android-user" + echo "GRADLE_USER_HOME=$gradle_root" + echo "ZSTANDARD_FLUTTER_PROFILE=$flutter_profile" + } >> "$GITHUB_ENV" + # Flutter otherwise prefers the host Android Studio JBR over JAVA_HOME. + # Configure a private child profile; never rewrite the operator's config. + env HOME="$flutter_profile" XDG_CONFIG_HOME="$flutter_profile/config" \ + flutter config --jdk-dir="$JAVA_HOME" diff --git a/.github/actions/prepare-linux-display/action.yml b/.github/actions/prepare-linux-display/action.yml new file mode 100644 index 00000000..27b4c42e --- /dev/null +++ b/.github/actions/prepare-linux-display/action.yml @@ -0,0 +1,39 @@ +name: Prepare Linux virtual display +description: Prepare Ubuntu Xvfb tools in the job directory without requiring sudo. +runs: + using: composite + steps: + - name: Prepare job-local Xvfb + shell: bash + run: | + set -euo pipefail + if command -v xvfb-run >/dev/null && command -v Xvfb >/dev/null && command -v xauth >/dev/null; then + exit 0 + fi + # Ubuntu's X server invokes this compiler at an absolute system path. + # It is part of the runner's base X11/GTK desktop toolchain. + if [ ! -x /usr/bin/xkbcomp ] || [ ! -f /usr/share/X11/xkb/rules/evdev ]; then + echo 'The Ubuntu runner needs x11-xkb-utils and xkb-data in its base desktop toolchain.' >&2 + exit 1 + fi + display_root="$(mktemp -d "$RUNNER_TEMP/zstandard-xvfb.XXXXXX")" + mkdir -p "$display_root/apt/lists/partial" "$display_root/packages/partial" + cp /var/lib/dpkg/status "$display_root/apt/status" + # Resolve every missing runtime dependency using a private package state + # and cache. Download-only never installs packages or runs maintainer scripts. + apt_options=(-o "Dir::State::status=$display_root/apt/status" -o "Dir::State::Lists=$display_root/apt/lists" -o "Dir::Cache::archives=$display_root/packages") + apt-get "${apt_options[@]}" update + apt-get "${apt_options[@]}" --download-only --assume-yes --no-install-recommends install xvfb xauth + ( + cd "$display_root/packages" + for package in ./*.deb; do + dpkg-deb --extract "$package" "$display_root" + done + ) + echo "$display_root/usr/bin" >> "$GITHUB_PATH" + if [ -d "$display_root/usr/lib" ]; then + library_directories="$(find "$display_root/usr/lib" -mindepth 1 -maxdepth 1 -type d -name '*-linux-gnu' | paste -sd : -)" + if [ -n "$library_directories" ]; then + echo "LD_LIBRARY_PATH=$library_directories${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" >> "$GITHUB_ENV" + fi + fi diff --git a/.github/actions/prepare-macos-coverage/action.yml b/.github/actions/prepare-macos-coverage/action.yml new file mode 100644 index 00000000..029a3f4d --- /dev/null +++ b/.github/actions/prepare-macos-coverage/action.yml @@ -0,0 +1,10 @@ +name: Prepare macOS coverage verification +description: Provide GPG for Codecov signature verification on the self-hosted macOS runner. +runs: + using: composite + steps: + - shell: bash + run: | + if ! command -v gpg >/dev/null 2>&1; then + brew install gnupg + fi diff --git a/.github/actions/setup-flutter-windows/action.yml b/.github/actions/setup-flutter-windows/action.yml new file mode 100644 index 00000000..fd464415 --- /dev/null +++ b/.github/actions/setup-flutter-windows/action.yml @@ -0,0 +1,55 @@ +name: Prepare native Windows Flutter +description: Select Git Bash instead of the WSL launcher, then install the reviewed Flutter SDK. +inputs: + flutter-version: + description: Exact Flutter SDK version used by repository validation. + required: true +runs: + using: composite + steps: + - name: Select Git Bash for composite actions + shell: cmd + run: | + @echo off + if not exist "%ProgramFiles%\Git\bin\bash.exe" ( + echo Expected the runner's Git for Windows Bash executable. 1>&2 + exit /b 1 + ) + echo %ProgramFiles%\Git\bin>>"%GITHUB_PATH%" + - name: Prepare verified portable jq + shell: powershell + run: | + $ErrorActionPreference = 'Stop' + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + $toolDirectory = Join-Path $env:RUNNER_TEMP 'zstandard-jq' + New-Item -ItemType Directory -Force -Path $toolDirectory | Out-Null + $executable = Join-Path $toolDirectory 'jq.exe' + Invoke-WebRequest -UseBasicParsing -Uri 'https://github.com/jqlang/jq/releases/download/jq-1.8.1/jq-windows-amd64.exe' -OutFile $executable + $expected = '23cb60a1354eed6bcc8d9b9735e8c7b388cd1fdcb75726b93bc299ef22dd9334' + if ((Get-FileHash -Algorithm SHA256 $executable).Hash.ToLowerInvariant() -ne $expected) { + throw 'The downloaded jq executable does not match the reviewed checksum.' + } + Add-Content -Path $env:GITHUB_PATH -Value $toolDirectory + - name: Recover interrupted repository SDK extraction + shell: powershell + env: + SDK_TOOL_CACHE: ${{ runner.tool_cache }} + SDK_VERSION: ${{ inputs.flutter-version }} + run: | + $ErrorActionPreference = 'Stop' + if ($env:SDK_VERSION -notmatch '^\d+\.\d+\.\d+$') { + throw 'Expected an exact stable Flutter release version.' + } + $sdkDirectory = Join-Path (Join-Path $env:SDK_TOOL_CACHE 'zstandard-flutter-v2') "stable-$env:SDK_VERSION-x64" + $sdkRoot = Join-Path $sdkDirectory 'flutter' + $requiredFiles = @('bin\flutter.bat', 'bin\cache\dart-sdk\bin\dart.exe', 'packages\flutter_tools\bin\flutter_tools.dart', 'packages\flutter\lib\widgets.dart') + if ((Test-Path -LiteralPath $sdkDirectory) -and ($requiredFiles | Where-Object { -not (Test-Path -LiteralPath (Join-Path $sdkRoot $_)) })) { + Write-Host 'Removing the incomplete repository-specific Flutter installation before preparing the SDK.' + Remove-Item -LiteralPath $sdkDirectory -Recurse -Force + } + - name: Prepare Flutter SDK + uses: subosito/flutter-action@v2 + with: + channel: stable + flutter-version: ${{ inputs.flutter-version }} + cache-path: '${{ runner.tool_cache }}/zstandard-flutter-v2/:channel:-:version:-:arch:' diff --git a/.github/scripts/.gitignore b/.github/scripts/.gitignore new file mode 100644 index 00000000..c18dd8d8 --- /dev/null +++ b/.github/scripts/.gitignore @@ -0,0 +1 @@ +__pycache__/ diff --git a/.github/scripts/run-web-integration.sh b/.github/scripts/run-web-integration.sh new file mode 100644 index 00000000..cd02eb9f --- /dev/null +++ b/.github/scripts/run-web-integration.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +set -euo pipefail + +driver_pid='' +browser_directory='' +cleanup() { + if [ -n "$driver_pid" ]; then + kill "$driver_pid" 2>/dev/null || true + wait "$driver_pid" 2>/dev/null || true + fi + if [ -n "$browser_directory" ]; then + rm -rf -- "$browser_directory" + fi +} +trap cleanup EXIT + +# setup-chrome strips the .app suffix from its macOS cache directory. Chrome +# prints its version there but cannot create the instrumented WebDriver session. +# Keep the shared cache untouched and restore the bundle name in a private copy. +if [ "$(uname -s)" = Darwin ]; then + chrome_bundle="${CHROME_EXECUTABLE%/Contents/MacOS/*}" + if [ ! -d "$chrome_bundle/Contents" ]; then + echo 'Expected a macOS Chrome app bundle.' >&2 + exit 1 + fi + case "$chrome_bundle" in + *.app) ;; + *) + browser_directory="$(mktemp -d "$RUNNER_TEMP/zstandard-chrome.XXXXXX")" + /bin/cp -R "$chrome_bundle" "$browser_directory/Chrome for Testing.app" + CHROME_EXECUTABLE="$browser_directory/Chrome for Testing.app/Contents/MacOS/${CHROME_EXECUTABLE##*/}" + ;; + esac +fi + +driver_log="$(mktemp "$RUNNER_TEMP/zstandard-chromedriver.XXXXXX")" +"$CHROMEDRIVER_EXECUTABLE" --port=0 > "$driver_log" 2>&1 & +driver_pid=$! + +driver_port='' +for attempt in {1..100}; do + driver_port="$(sed -nE 's/.*ChromeDriver was started successfully on port ([0-9]+).*/\1/p' "$driver_log" | tail -n 1)" + if [ -n "$driver_port" ]; then + break + fi + if ! kill -0 "$driver_pid" 2>/dev/null; then + cat "$driver_log" >&2 + exit 1 + fi + sleep 0.1 +done +if [ -z "$driver_port" ]; then + cat "$driver_log" >&2 + echo 'ChromeDriver did not become ready within 10 seconds.' >&2 + exit 1 +fi + +result=0 +flutter drive \ + --driver=test_driver/integration_test.dart \ + --target=integration_test/zstandard_web_integration_test.dart \ + --driver-port="$driver_port" \ + --chrome-binary="$CHROME_EXECUTABLE" \ + -d web-server || result=$? +if [ "$result" -ne 0 ]; then + tail -n 50 "$driver_log" >&2 +fi +exit "$result" diff --git a/.github/scripts/start_ios_simulator.py b/.github/scripts/start_ios_simulator.py new file mode 100644 index 00000000..d6dbef3e --- /dev/null +++ b/.github/scripts/start_ios_simulator.py @@ -0,0 +1,43 @@ +"""Create a fresh CI simulator without reusing or stopping personal devices.""" + +import json +import os +import subprocess +import uuid + + +def start(): + result = subprocess.run( + ["xcrun", "simctl", "list", "devices", "available", "--json"], + check=True, capture_output=True, text=True, timeout=30, + ) + candidates = [] + for runtime, devices in json.loads(result.stdout).get("devices", {}).items(): + if not runtime.startswith("com.apple.CoreSimulator.SimRuntime.iOS-"): + continue + for device in devices: + kind = device.get("deviceTypeIdentifier", "") + if device.get("isAvailable") and kind.startswith("com.apple.CoreSimulator.SimDeviceType.iPhone-"): + candidates.append((runtime, kind, device.get("name"))) + if not candidates: + raise RuntimeError("Xcode needs an available iPhone simulator and its installed iOS runtime.") + preferred = os.environ.get("ZSTANDARD_IOS_DEVICE", "iPhone 16") + runtime, kind, _ = next((item for item in candidates if item[2] == preferred), candidates[0]) + name = "zstandard-{}-{}-{}-{}".format( + os.environ["GITHUB_RUN_ID"], os.environ["GITHUB_RUN_ATTEMPT"], + os.environ["GITHUB_JOB"], uuid.uuid4().hex[:8], + ) + created = subprocess.run( + ["xcrun", "simctl", "create", name, kind, runtime], + check=True, capture_output=True, text=True, timeout=30, + ) + device_id = str(uuid.UUID(created.stdout.strip())).upper() + # Record ownership before boot so an interrupted boot can still be cleaned up. + with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as output: + output.write(f"device-id={device_id}\n") + subprocess.run(["xcrun", "simctl", "boot", device_id], check=True, timeout=30) + subprocess.run(["xcrun", "simctl", "bootstatus", device_id, "-b"], check=True, timeout=120) + + +if __name__ == "__main__": + start() diff --git a/.github/scripts/tests/test_ios_simulator.py b/.github/scripts/tests/test_ios_simulator.py new file mode 100644 index 00000000..78dde505 --- /dev/null +++ b/.github/scripts/tests/test_ios_simulator.py @@ -0,0 +1,53 @@ +import importlib.util +import json +import os +import pathlib +import subprocess +import tempfile +import unittest +from unittest.mock import Mock, patch + + +path = pathlib.Path(__file__).parents[1] / "start_ios_simulator.py" +spec = importlib.util.spec_from_file_location("start_ios_simulator", path) +simulator = importlib.util.module_from_spec(spec) +spec.loader.exec_module(simulator) +RUNTIME = "com.apple.CoreSimulator.SimRuntime.iOS-27-0" +KIND = "com.apple.CoreSimulator.SimDeviceType.iPhone-16" +EXISTING = "AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA" +CREATED = "BBBBBBBB-BBBB-BBBB-BBBB-BBBBBBBBBBBB" + + +class SimulatorTests(unittest.TestCase): + def prepare(self, folder): + return patch.dict(os.environ, {"GITHUB_RUN_ID": "12", "GITHUB_RUN_ATTEMPT": "1", "GITHUB_JOB": "ios", "GITHUB_OUTPUT": str(pathlib.Path(folder) / "output")}) + + @patch.object(simulator.subprocess, "run") + def test_booted_personal_device_is_only_used_as_type_metadata(self, command): + devices = {"devices": {RUNTIME: [{"name": "iPhone 16", "deviceTypeIdentifier": KIND, "isAvailable": True, "state": "Booted", "udid": EXISTING}]}} + command.side_effect = [Mock(stdout=json.dumps(devices)), Mock(stdout=CREATED), Mock(), Mock()] + with tempfile.TemporaryDirectory() as folder, self.prepare(folder): + simulator.start() + self.assertEqual((pathlib.Path(folder) / "output").read_text(), f"device-id={CREATED}\n") + self.assertEqual(command.call_args_list[1].args[0][-2:], [KIND, RUNTIME]) + self.assertEqual(command.call_args_list[2].args[0], ["xcrun", "simctl", "boot", CREATED]) + self.assertNotIn(EXISTING, str(command.call_args_list)) + + @patch.object(simulator.subprocess, "run", return_value=Mock(stdout='{"devices": {}}')) + def test_missing_ios_runtime_fails_before_creating_a_device(self, command): + with self.assertRaisesRegex(RuntimeError, "installed iOS runtime"): + simulator.start() + self.assertEqual(command.call_count, 1) + + @patch.object(simulator.subprocess, "run") + def test_failed_boot_preserves_created_device_id_for_cleanup(self, command): + devices = {"devices": {RUNTIME: [{"name": "iPhone 16", "deviceTypeIdentifier": KIND, "isAvailable": True}]}} + command.side_effect = [Mock(stdout=json.dumps(devices)), Mock(stdout=CREATED), subprocess.CalledProcessError(1, "boot")] + with tempfile.TemporaryDirectory() as folder, self.prepare(folder): + with self.assertRaises(subprocess.CalledProcessError): + simulator.start() + self.assertEqual((pathlib.Path(folder) / "output").read_text(), f"device-id={CREATED}\n") + + +if __name__ == "__main__": + unittest.main() diff --git a/.github/workflows/copilot_credential_health.yml b/.github/workflows/copilot_credential_health.yml index e87c82a3..58e8f6f9 100644 --- a/.github/workflows/copilot_credential_health.yml +++ b/.github/workflows/copilot_credential_health.yml @@ -51,7 +51,9 @@ jobs: verify-pat: if: ${{ inputs.check_pat }} name: Verify PAT - runs-on: ubuntu-latest + runs-on: + group: vypdev + labels: [self-hosted, codex, Linux] timeout-minutes: 10 steps: - name: Validate PAT without exposing its value @@ -67,7 +69,9 @@ jobs: verify-openai: if: ${{ inputs.check_openai }} name: Verify OPENAI_API_KEY - runs-on: ubuntu-latest + runs-on: + group: vypdev + labels: [self-hosted, codex, Linux] timeout-minutes: 10 steps: - name: Validate OpenAI key without exposing its value @@ -82,7 +86,9 @@ jobs: verify-anthropic: if: ${{ inputs.check_anthropic }} name: Verify ANTHROPIC_API_KEY - runs-on: ubuntu-latest + runs-on: + group: vypdev + labels: [self-hosted, codex, Linux] timeout-minutes: 10 steps: - name: Validate Anthropic key without exposing its value @@ -97,7 +103,9 @@ jobs: verify-google: if: ${{ inputs.check_google }} name: Verify GOOGLE_API_KEY - runs-on: ubuntu-latest + runs-on: + group: vypdev + labels: [self-hosted, codex, Linux] timeout-minutes: 10 steps: - name: Validate Google key without exposing its value @@ -112,7 +120,9 @@ jobs: verify-openrouter: if: ${{ inputs.check_openrouter }} name: Verify OPENROUTER_API_KEY - runs-on: ubuntu-latest + runs-on: + group: vypdev + labels: [self-hosted, codex, Linux] timeout-minutes: 10 steps: - name: Validate OpenRouter key without exposing its value @@ -127,7 +137,9 @@ jobs: verify-cursor: if: ${{ inputs.check_cursor }} name: Verify CURSOR_API_KEY - runs-on: ubuntu-latest + runs-on: + group: vypdev + labels: [self-hosted, codex, Linux] timeout-minutes: 10 steps: - name: Validate Cursor key without exposing its value @@ -142,7 +154,9 @@ jobs: verify-opencode: if: ${{ inputs.check_opencode }} name: Verify OPENCODE_API_KEY - runs-on: ubuntu-latest + runs-on: + group: vypdev + labels: [self-hosted, codex, Linux] timeout-minutes: 10 steps: - name: Validate OpenCode key without exposing its value @@ -157,7 +171,9 @@ jobs: verify-codex-api-key: if: ${{ inputs.check_codex_api_key }} name: Verify CODEX_API_KEY - runs-on: ubuntu-latest + runs-on: + group: vypdev + labels: [self-hosted, codex, Linux] timeout-minutes: 10 steps: - name: Validate Codex API key without exposing its value diff --git a/.github/workflows/copilot_pull_request_approval.yml b/.github/workflows/copilot_pull_request_approval.yml index 3f926e15..672bead8 100644 --- a/.github/workflows/copilot_pull_request_approval.yml +++ b/.github/workflows/copilot_pull_request_approval.yml @@ -15,7 +15,9 @@ on: jobs: observe-approval: name: Copilot - Pull Request Approval - runs-on: ubuntu-latest + runs-on: + group: vypdev + labels: [self-hosted, codex, Linux] timeout-minutes: 15 concurrency: # Some workflow_run payloads omit the PR association. A repository-wide diff --git a/.github/workflows/copilot_pull_request_merge_queue.yml b/.github/workflows/copilot_pull_request_merge_queue.yml index 0be12abd..1ee8335e 100644 --- a/.github/workflows/copilot_pull_request_merge_queue.yml +++ b/.github/workflows/copilot_pull_request_merge_queue.yml @@ -10,7 +10,9 @@ jobs: # GitHub required checks match the job name. Keep this identical to normal # PR analysis while isolating merge-group runs in their own workflow. name: Copilot - Pull Request - runs-on: ubuntu-latest + runs-on: + group: vypdev + labels: [self-hosted, codex, Linux] timeout-minutes: 10 permissions: checks: write diff --git a/.github/workflows/hotfix_workflow.yml b/.github/workflows/hotfix_workflow.yml index aa00184e..844c340d 100644 --- a/.github/workflows/hotfix_workflow.yml +++ b/.github/workflows/hotfix_workflow.yml @@ -159,7 +159,9 @@ jobs: publish: name: Publish accepted production commit if: ${{ inputs.mode == 'publish' }} - runs-on: ubuntu-latest + runs-on: + group: vypdev + labels: [self-hosted, codex, Linux] needs: validate timeout-minutes: 30 permissions: diff --git a/.github/workflows/pr_check_android.yml b/.github/workflows/pr_check_android.yml index 2d991428..77a1a1e8 100644 --- a/.github/workflows/pr_check_android.yml +++ b/.github/workflows/pr_check_android.yml @@ -4,7 +4,6 @@ on: pull_request: branches: - '**' - - '!master' push: branches: - develop @@ -19,14 +18,19 @@ env: jobs: check_android_analyze: name: Analyze Android - runs-on: [self-hosted, macOS] + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: + group: vypdev + labels: [self-hosted, macOS, ARM64] steps: - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v1 + - uses: subosito/flutter-action@v2 with: channel: 'stable' flutter-version: ${{ env.FLUTTER_VERSION }} + architecture: ${{ runner.arch == 'ARM64' && 'arm64' || 'x64' }} + cache-path: '${{ runner.tool_cache }}/zstandard-flutter-v2/:channel:-:version:-:arch:' - name: Get dependencies working-directory: zstandard_android @@ -38,14 +42,19 @@ jobs: check_android_test: name: Test Android - runs-on: [self-hosted, macOS] + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: + group: vypdev + labels: [self-hosted, macOS, ARM64] steps: - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v1 + - uses: subosito/flutter-action@v2 with: channel: 'stable' flutter-version: ${{ env.FLUTTER_VERSION }} + architecture: ${{ runner.arch == 'ARM64' && 'arm64' || 'x64' }} + cache-path: '${{ runner.tool_cache }}/zstandard-flutter-v2/:channel:-:version:-:arch:' - name: Get dependencies working-directory: zstandard_android/example @@ -57,28 +66,42 @@ jobs: distribution: 'temurin' java-version: '17' + - name: Prepare isolated Android build tools + uses: ./.github/actions/prepare-android-integration + + - name: Prepare Android command-line tools + uses: android-actions/setup-android@v3 + with: + packages: platform-tools + - name: Run integration tests on emulator uses: reactivecircus/android-emulator-runner@v2 with: api-level: 30 target: google_apis profile: pixel_4 - arch: x86_64 + arch: arm64-v8a + ndk: 28.2.13676358 avd-name: zstandard_ci_test emulator-port: 5554 script: | - cd zstandard_android/example && flutter test integration_test/ -d emulator-${EMULATOR_PORT} + cd zstandard_android/example && env HOME="$ZSTANDARD_FLUTTER_PROFILE" XDG_CONFIG_HOME="$ZSTANDARD_FLUTTER_PROFILE/config" flutter test integration_test/ -d emulator-${EMULATOR_PORT} check_android_test_from_pub_cache: name: Test Android (from pub cache) - runs-on: [self-hosted, macOS] + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: + group: vypdev + labels: [self-hosted, macOS, ARM64] steps: - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v1 + - uses: subosito/flutter-action@v2 with: channel: 'stable' flutter-version: ${{ env.FLUTTER_VERSION }} + architecture: ${{ runner.arch == 'ARM64' && 'arm64' || 'x64' }} + cache-path: '${{ runner.tool_cache }}/zstandard-flutter-v2/:channel:-:version:-:arch:' - name: Remove zstandard_native to force resolution from pub cache run: rm -rf zstandard_native @@ -101,28 +124,42 @@ jobs: distribution: 'temurin' java-version: '17' + - name: Prepare isolated Android build tools + uses: ./.github/actions/prepare-android-integration + + - name: Prepare Android command-line tools + uses: android-actions/setup-android@v3 + with: + packages: platform-tools + - name: Run integration tests on emulator (from pub cache) uses: reactivecircus/android-emulator-runner@v2 with: api-level: 30 target: google_apis profile: pixel_4 - arch: x86_64 + arch: arm64-v8a + ndk: 28.2.13676358 avd-name: zstandard_ci_test_pubcache emulator-port: 5556 script: | - cd zstandard_android/example && flutter test integration_test/ -d emulator-${EMULATOR_PORT} + cd zstandard_android/example && env HOME="$ZSTANDARD_FLUTTER_PROFILE" XDG_CONFIG_HOME="$ZSTANDARD_FLUTTER_PROFILE/config" flutter test integration_test/ -d emulator-${EMULATOR_PORT} check_android_publish_dry_run: name: Dry Run Publish Android - runs-on: [self-hosted, macOS] + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: + group: vypdev + labels: [self-hosted, macOS, ARM64] steps: - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v1 + - uses: subosito/flutter-action@v2 with: channel: 'stable' flutter-version: ${{ env.FLUTTER_VERSION }} + architecture: ${{ runner.arch == 'ARM64' && 'arm64' || 'x64' }} + cache-path: '${{ runner.tool_cache }}/zstandard-flutter-v2/:channel:-:version:-:arch:' - name: Get dependencies working-directory: zstandard_android diff --git a/.github/workflows/pr_check_cli.yml b/.github/workflows/pr_check_cli.yml index a5d865f4..a8d605a7 100644 --- a/.github/workflows/pr_check_cli.yml +++ b/.github/workflows/pr_check_cli.yml @@ -4,7 +4,6 @@ on: pull_request: branches: - '**' - - '!master' push: branches: - develop @@ -14,19 +13,24 @@ concurrency: cancel-in-progress: true env: - FLUTTER_VERSION: ${{ vars.FLUTTER_VERSION || '3.41.4' }} + FLUTTER_VERSION: ${{ vars.FLUTTER_VERSION || '3.47.2' }} jobs: check_cli_analyze: name: Analyze CLI - runs-on: [self-hosted, macOS] + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: + group: vypdev + labels: [self-hosted, macOS, ARM64] steps: - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v1 + - uses: subosito/flutter-action@v2 with: channel: 'stable' flutter-version: ${{ env.FLUTTER_VERSION }} + architecture: ${{ runner.arch == 'ARM64' && 'arm64' || 'x64' }} + cache-path: '${{ runner.tool_cache }}/zstandard-flutter-v2/:channel:-:version:-:arch:' - name: Get dependencies working-directory: zstandard_cli @@ -38,15 +42,20 @@ jobs: check_cli_test: name: Test CLI - runs-on: [self-hosted, macOS] + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: + group: vypdev + labels: [self-hosted, macOS, ARM64] timeout-minutes: 30 steps: - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v1 + - uses: subosito/flutter-action@v2 with: channel: 'stable' flutter-version: ${{ env.FLUTTER_VERSION }} + architecture: ${{ runner.arch == 'ARM64' && 'arm64' || 'x64' }} + cache-path: '${{ runner.tool_cache }}/zstandard-flutter-v2/:channel:-:version:-:arch:' - name: Get dependencies working-directory: zstandard_cli @@ -67,13 +76,17 @@ jobs: - name: Check coverage threshold (79% of lib/) working-directory: zstandard_cli run: | - lcov --extract coverage/lcov.info '*zstandard_cli/lib/*' -o coverage/lcov_lib.info - lcov --remove coverage/lcov_lib.info '*bindings_generated*' -o coverage/lcov_lib.info --ignore-errors unused - COVERAGE=$(lcov --summary coverage/lcov_lib.info 2>&1 | grep "lines" | awk '{print $2}' | sed 's/%//') + # Dart emits line coverage; keep the existing line coverage threshold. + lcov --rc function_coverage=0 --extract coverage/lcov.info '*zstandard_cli/lib/*' -o coverage/lcov_lib.info + lcov --rc function_coverage=0 --remove coverage/lcov_lib.info '*bindings_generated*' -o coverage/lcov_lib.info --ignore-errors unused + COVERAGE=$(lcov --rc function_coverage=0 --summary coverage/lcov_lib.info 2>&1 | grep "lines" | awk '{print $2}' | sed 's/%//') echo "Coverage (lib/ excluding generated): ${COVERAGE}%" if [ -z "$COVERAGE" ]; then echo "Could not parse coverage"; exit 1; fi if (( $(echo "$COVERAGE < 79" | bc -l) )); then echo "ERROR: Coverage ${COVERAGE}% is below 79%"; exit 1; fi + - name: Prepare coverage signature verification + uses: ./.github/actions/prepare-macos-coverage + - name: Upload coverage to Codecov uses: codecov/codecov-action@v4 with: @@ -98,14 +111,19 @@ jobs: check_cli_publish_dry_run: name: Dry Run Publish CLI - runs-on: [self-hosted, macOS] + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: + group: vypdev + labels: [self-hosted, macOS, ARM64] steps: - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v1 + - uses: subosito/flutter-action@v2 with: channel: 'stable' flutter-version: ${{ env.FLUTTER_VERSION }} + architecture: ${{ runner.arch == 'ARM64' && 'arm64' || 'x64' }} + cache-path: '${{ runner.tool_cache }}/zstandard-flutter-v2/:channel:-:version:-:arch:' - name: Get dependencies working-directory: zstandard_cli diff --git a/.github/workflows/pr_check_ios.yml b/.github/workflows/pr_check_ios.yml index 3a052ffd..e47b3e56 100644 --- a/.github/workflows/pr_check_ios.yml +++ b/.github/workflows/pr_check_ios.yml @@ -4,7 +4,6 @@ on: pull_request: branches: - '**' - - '!master' push: branches: - develop @@ -14,19 +13,24 @@ concurrency: cancel-in-progress: true env: - FLUTTER_VERSION: ${{ vars.FLUTTER_VERSION || '3.41.4' }} + FLUTTER_VERSION: ${{ vars.FLUTTER_VERSION || '3.47.2' }} jobs: check_ios_analyze: name: Analyze iOS - runs-on: [self-hosted, macOS, X64] + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: + group: vypdev + labels: [self-hosted, macOS, ARM64] steps: - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v1 + - uses: subosito/flutter-action@v2 with: channel: 'stable' flutter-version: ${{ env.FLUTTER_VERSION }} + architecture: ${{ runner.arch == 'ARM64' && 'arm64' || 'x64' }} + cache-path: '${{ runner.tool_cache }}/zstandard-flutter-v2/:channel:-:version:-:arch:' - name: Get dependencies working-directory: zstandard_ios @@ -38,39 +42,58 @@ jobs: check_ios_test: name: Test iOS - runs-on: [self-hosted, macOS, X64] + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: + group: vypdev + labels: [self-hosted, macOS, ARM64] steps: - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v1 + - uses: subosito/flutter-action@v2 with: channel: 'stable' flutter-version: ${{ env.FLUTTER_VERSION }} + architecture: ${{ runner.arch == 'ARM64' && 'arm64' || 'x64' }} + cache-path: '${{ runner.tool_cache }}/zstandard-flutter-v2/:channel:-:version:-:arch:' - name: Get dependencies working-directory: zstandard_ios/example run: flutter pub get - - name: Boot iOS simulator - run: | - chmod +x scripts/manage_ios_simulator.sh - ./scripts/manage_ios_simulator.sh start + - name: Boot an isolated iOS simulator + id: ios-simulator + timeout-minutes: 4 + run: python3 .github/scripts/start_ios_simulator.py - name: Run integration tests + working-directory: zstandard_ios/example + env: + DEVICE_ID: ${{ steps.ios-simulator.outputs.device-id }} + run: flutter test integration_test/ -d "$DEVICE_ID" + + - name: Remove this job's iOS simulator + if: ${{ always() && steps.ios-simulator.outputs.device-id != '' }} + env: + DEVICE_ID: ${{ steps.ios-simulator.outputs.device-id }} run: | - DEVICE_ID=$(./scripts/manage_ios_simulator.sh device-id) - cd zstandard_ios/example && flutter test integration_test/ -d "$DEVICE_ID" + xcrun simctl shutdown "$DEVICE_ID" 2>/dev/null || true + xcrun simctl delete "$DEVICE_ID" check_ios_test_from_pub_cache: name: Test iOS (from pub cache) - runs-on: [self-hosted, macOS, X64] + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: + group: vypdev + labels: [self-hosted, macOS, ARM64] steps: - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v1 + - uses: subosito/flutter-action@v2 with: channel: 'stable' flutter-version: ${{ env.FLUTTER_VERSION }} + architecture: ${{ runner.arch == 'ARM64' && 'arm64' || 'x64' }} + cache-path: '${{ runner.tool_cache }}/zstandard-flutter-v2/:channel:-:version:-:arch:' - name: Remove zstandard_native to force resolution from pub cache run: rm -rf zstandard_native @@ -87,26 +110,40 @@ jobs: flutter clean flutter pub get - - name: Boot iOS simulator - run: | - chmod +x scripts/manage_ios_simulator.sh - ./scripts/manage_ios_simulator.sh start + - name: Boot an isolated iOS simulator + id: ios-simulator + timeout-minutes: 4 + run: python3 .github/scripts/start_ios_simulator.py - name: Run integration tests (C sources from pub cache) + working-directory: zstandard_ios/example + env: + DEVICE_ID: ${{ steps.ios-simulator.outputs.device-id }} + run: flutter test integration_test/ -d "$DEVICE_ID" + + - name: Remove this job's iOS simulator + if: ${{ always() && steps.ios-simulator.outputs.device-id != '' }} + env: + DEVICE_ID: ${{ steps.ios-simulator.outputs.device-id }} run: | - DEVICE_ID=$(./scripts/manage_ios_simulator.sh device-id) - cd zstandard_ios/example && flutter test integration_test/ -d "$DEVICE_ID" + xcrun simctl shutdown "$DEVICE_ID" 2>/dev/null || true + xcrun simctl delete "$DEVICE_ID" check_ios_publish_dry_run: name: Dry Run Publish iOS - runs-on: [self-hosted, macOS, X64] + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: + group: vypdev + labels: [self-hosted, macOS, ARM64] steps: - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v1 + - uses: subosito/flutter-action@v2 with: channel: 'stable' flutter-version: ${{ env.FLUTTER_VERSION }} + architecture: ${{ runner.arch == 'ARM64' && 'arm64' || 'x64' }} + cache-path: '${{ runner.tool_cache }}/zstandard-flutter-v2/:channel:-:version:-:arch:' - name: Get dependencies working-directory: zstandard_ios diff --git a/.github/workflows/pr_check_linux.yml b/.github/workflows/pr_check_linux.yml index f18dfe2c..6ddf63ef 100644 --- a/.github/workflows/pr_check_linux.yml +++ b/.github/workflows/pr_check_linux.yml @@ -4,7 +4,6 @@ on: pull_request: branches: - '**' - - '!master' push: branches: - develop @@ -14,19 +13,24 @@ concurrency: cancel-in-progress: true env: - FLUTTER_VERSION: ${{ vars.FLUTTER_VERSION || '3.41.4' }} + FLUTTER_VERSION: ${{ vars.FLUTTER_VERSION || '3.47.2' }} jobs: check_linux_analyze: name: Analyze Linux - runs-on: [self-hosted, Linux] + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: + group: vypdev + labels: [self-hosted, codex, Linux] steps: - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v1 + - uses: subosito/flutter-action@v2 with: channel: 'stable' flutter-version: ${{ env.FLUTTER_VERSION }} + architecture: ${{ runner.arch == 'ARM64' && 'arm64' || 'x64' }} + cache-path: '${{ runner.tool_cache }}/zstandard-flutter-v2/:channel:-:version:-:arch:' - name: Get dependencies working-directory: zstandard_linux @@ -37,15 +41,23 @@ jobs: run: flutter analyze check_linux_test: + env: + GDK_BACKEND: x11 + LIBGL_ALWAYS_SOFTWARE: '1' name: Test Linux - runs-on: [self-hosted, Linux] + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: + group: vypdev + labels: [self-hosted, codex, Linux] steps: - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v1 + - uses: subosito/flutter-action@v2 with: channel: 'stable' flutter-version: ${{ env.FLUTTER_VERSION }} + architecture: ${{ runner.arch == 'ARM64' && 'arm64' || 'x64' }} + cache-path: '${{ runner.tool_cache }}/zstandard-flutter-v2/:channel:-:version:-:arch:' - name: Get dependencies working-directory: zstandard_linux @@ -59,20 +71,31 @@ jobs: working-directory: zstandard_linux/example run: flutter pub get - - name: Run integration tests + - name: Prepare virtual display + uses: ./.github/actions/prepare-linux-display + + - name: Run integration tests with a virtual display working-directory: zstandard_linux/example - run: flutter test integration_test/ -d linux + run: xvfb-run -a flutter test integration_test/ -d linux check_linux_test_from_pub_cache: + env: + GDK_BACKEND: x11 + LIBGL_ALWAYS_SOFTWARE: '1' name: Test Linux (from pub cache) - runs-on: [self-hosted, Linux] + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: + group: vypdev + labels: [self-hosted, codex, Linux] steps: - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v1 + - uses: subosito/flutter-action@v2 with: channel: 'stable' flutter-version: ${{ env.FLUTTER_VERSION }} + architecture: ${{ runner.arch == 'ARM64' && 'arm64' || 'x64' }} + cache-path: '${{ runner.tool_cache }}/zstandard-flutter-v2/:channel:-:version:-:arch:' - name: Remove zstandard_native to force resolution from pub cache run: rm -rf zstandard_native @@ -89,20 +112,28 @@ jobs: flutter clean flutter pub get - - name: Run integration tests (C sources from pub cache) + - name: Prepare virtual display + uses: ./.github/actions/prepare-linux-display + + - name: Run integration tests with a virtual display (C sources from pub cache) working-directory: zstandard_linux/example - run: flutter test integration_test/ -d linux + run: xvfb-run -a flutter test integration_test/ -d linux check_linux_publish_dry_run: name: Dry Run Publish Linux - runs-on: [self-hosted, Linux] + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: + group: vypdev + labels: [self-hosted, codex, Linux] steps: - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v1 + - uses: subosito/flutter-action@v2 with: channel: 'stable' flutter-version: ${{ env.FLUTTER_VERSION }} + architecture: ${{ runner.arch == 'ARM64' && 'arm64' || 'x64' }} + cache-path: '${{ runner.tool_cache }}/zstandard-flutter-v2/:channel:-:version:-:arch:' - name: Get dependencies working-directory: zstandard_linux diff --git a/.github/workflows/pr_check_macos.yml b/.github/workflows/pr_check_macos.yml index 336e6149..f4f6a4a4 100644 --- a/.github/workflows/pr_check_macos.yml +++ b/.github/workflows/pr_check_macos.yml @@ -4,7 +4,6 @@ on: pull_request: branches: - '**' - - '!master' push: branches: - develop @@ -14,19 +13,24 @@ concurrency: cancel-in-progress: true env: - FLUTTER_VERSION: ${{ vars.FLUTTER_VERSION || '3.41.4' }} + FLUTTER_VERSION: ${{ vars.FLUTTER_VERSION || '3.47.2' }} jobs: check_macos_analyze: name: Analyze macOS - runs-on: [self-hosted, macOS, X64] + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: + group: vypdev + labels: [self-hosted, macOS, ARM64] steps: - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v1 + - uses: subosito/flutter-action@v2 with: channel: 'stable' flutter-version: ${{ env.FLUTTER_VERSION }} + architecture: ${{ runner.arch == 'ARM64' && 'arm64' || 'x64' }} + cache-path: '${{ runner.tool_cache }}/zstandard-flutter-v2/:channel:-:version:-:arch:' - name: Get dependencies working-directory: zstandard_macos @@ -38,14 +42,19 @@ jobs: check_macos_test: name: Test macOS - runs-on: [self-hosted, macOS, X64] + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: + group: vypdev + labels: [self-hosted, macOS, ARM64] steps: - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v1 + - uses: subosito/flutter-action@v2 with: channel: 'stable' flutter-version: ${{ env.FLUTTER_VERSION }} + architecture: ${{ runner.arch == 'ARM64' && 'arm64' || 'x64' }} + cache-path: '${{ runner.tool_cache }}/zstandard-flutter-v2/:channel:-:version:-:arch:' - name: Run integration tests working-directory: zstandard_macos/example @@ -53,14 +62,19 @@ jobs: check_macos_test_from_pub_cache: name: Test macOS (from pub cache) - runs-on: [self-hosted, macOS, X64] + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: + group: vypdev + labels: [self-hosted, macOS, ARM64] steps: - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v1 + - uses: subosito/flutter-action@v2 with: channel: 'stable' flutter-version: ${{ env.FLUTTER_VERSION }} + architecture: ${{ runner.arch == 'ARM64' && 'arm64' || 'x64' }} + cache-path: '${{ runner.tool_cache }}/zstandard-flutter-v2/:channel:-:version:-:arch:' - name: Remove zstandard_native to force resolution from pub cache run: rm -rf zstandard_native @@ -83,14 +97,19 @@ jobs: check_macos_publish_dry_run: name: Dry Run Publish macOS - runs-on: [self-hosted, macOS, X64] + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: + group: vypdev + labels: [self-hosted, macOS, ARM64] steps: - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v1 + - uses: subosito/flutter-action@v2 with: channel: 'stable' flutter-version: ${{ env.FLUTTER_VERSION }} + architecture: ${{ runner.arch == 'ARM64' && 'arm64' || 'x64' }} + cache-path: '${{ runner.tool_cache }}/zstandard-flutter-v2/:channel:-:version:-:arch:' - name: Get dependencies working-directory: zstandard_macos diff --git a/.github/workflows/pr_check_platform_interface.yml b/.github/workflows/pr_check_platform_interface.yml index 44a36364..56b02220 100644 --- a/.github/workflows/pr_check_platform_interface.yml +++ b/.github/workflows/pr_check_platform_interface.yml @@ -4,7 +4,6 @@ on: pull_request: branches: - '**' - - '!master' push: branches: - develop @@ -14,19 +13,24 @@ concurrency: cancel-in-progress: true env: - FLUTTER_VERSION: ${{ vars.FLUTTER_VERSION || '3.41.4' }} + FLUTTER_VERSION: ${{ vars.FLUTTER_VERSION || '3.47.2' }} jobs: check_platform_interface_analyze: name: Analyze Platform Interface - runs-on: [self-hosted, macOS] + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: + group: vypdev + labels: [self-hosted, macOS, ARM64] steps: - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v1 + - uses: subosito/flutter-action@v2 with: channel: 'stable' flutter-version: ${{ env.FLUTTER_VERSION }} + architecture: ${{ runner.arch == 'ARM64' && 'arm64' || 'x64' }} + cache-path: '${{ runner.tool_cache }}/zstandard-flutter-v2/:channel:-:version:-:arch:' - name: Get dependencies working-directory: zstandard_platform_interface @@ -38,14 +42,19 @@ jobs: check_platform_interface_test: name: Test Platform Interface - runs-on: [self-hosted, macOS] + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: + group: vypdev + labels: [self-hosted, macOS, ARM64] steps: - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v1 + - uses: subosito/flutter-action@v2 with: channel: 'stable' flutter-version: ${{ env.FLUTTER_VERSION }} + architecture: ${{ runner.arch == 'ARM64' && 'arm64' || 'x64' }} + cache-path: '${{ runner.tool_cache }}/zstandard-flutter-v2/:channel:-:version:-:arch:' - name: Get dependencies working-directory: zstandard_platform_interface @@ -62,12 +71,16 @@ jobs: - name: Check coverage threshold (85% of lib/) working-directory: zstandard_platform_interface run: | - lcov --extract coverage/lcov.info 'lib/*' -o coverage/lcov_lib.info - COVERAGE=$(lcov --summary coverage/lcov_lib.info 2>&1 | grep "lines" | awk '{print $2}' | sed 's/%//') + # Flutter emits line coverage; keep the existing line coverage threshold. + lcov --rc function_coverage=0 --extract coverage/lcov.info 'lib/*' -o coverage/lcov_lib.info + COVERAGE=$(lcov --rc function_coverage=0 --summary coverage/lcov_lib.info 2>&1 | grep "lines" | awk '{print $2}' | sed 's/%//') echo "Coverage (lib/ only): ${COVERAGE}%" if [ -z "$COVERAGE" ]; then echo "Could not parse coverage"; exit 1; fi if (( $(echo "$COVERAGE < 85" | bc -l) )); then echo "ERROR: Coverage ${COVERAGE}% is below 85%"; exit 1; fi + - name: Prepare coverage signature verification + uses: ./.github/actions/prepare-macos-coverage + - name: Upload coverage to Codecov uses: codecov/codecov-action@v4 with: @@ -78,14 +91,19 @@ jobs: check_platform_interface_publish_dry_run: name: Dry Run Publish Platform Interface - runs-on: [self-hosted, macOS] + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: + group: vypdev + labels: [self-hosted, macOS, ARM64] steps: - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v1 + - uses: subosito/flutter-action@v2 with: channel: 'stable' flutter-version: ${{ env.FLUTTER_VERSION }} + architecture: ${{ runner.arch == 'ARM64' && 'arm64' || 'x64' }} + cache-path: '${{ runner.tool_cache }}/zstandard-flutter-v2/:channel:-:version:-:arch:' - name: Get dependencies working-directory: zstandard_platform_interface diff --git a/.github/workflows/pr_check_web.yml b/.github/workflows/pr_check_web.yml index a67055e3..c8b9a291 100644 --- a/.github/workflows/pr_check_web.yml +++ b/.github/workflows/pr_check_web.yml @@ -4,7 +4,6 @@ on: pull_request: branches: - '**' - - '!master' push: branches: - develop @@ -14,19 +13,24 @@ concurrency: cancel-in-progress: true env: - FLUTTER_VERSION: ${{ vars.FLUTTER_VERSION || '3.41.4' }} + FLUTTER_VERSION: ${{ vars.FLUTTER_VERSION || '3.47.2' }} jobs: check_web_analyze: name: Analyze Web - runs-on: [self-hosted, macOS] + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: + group: vypdev + labels: [self-hosted, macOS, ARM64] steps: - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v1 + - uses: subosito/flutter-action@v2 with: channel: 'stable' flutter-version: ${{ env.FLUTTER_VERSION }} + architecture: ${{ runner.arch == 'ARM64' && 'arm64' || 'x64' }} + cache-path: '${{ runner.tool_cache }}/zstandard-flutter-v2/:channel:-:version:-:arch:' - name: Get dependencies working-directory: zstandard_web @@ -38,14 +42,19 @@ jobs: check_web_test: name: Test Web - runs-on: [self-hosted, macOS] + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: + group: vypdev + labels: [self-hosted, macOS, ARM64] steps: - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v1 + - uses: subosito/flutter-action@v2 with: channel: 'stable' flutter-version: ${{ env.FLUTTER_VERSION }} + architecture: ${{ runner.arch == 'ARM64' && 'arm64' || 'x64' }} + cache-path: '${{ runner.tool_cache }}/zstandard-flutter-v2/:channel:-:version:-:arch:' - name: Get dependencies working-directory: zstandard_web @@ -55,54 +64,35 @@ jobs: working-directory: zstandard_web/example run: flutter pub get - - name: Update Google Chrome (match ChromeDriver) - run: | - if brew list --cask google-chrome &>/dev/null; then - brew upgrade --cask google-chrome - else - echo "Chrome not installed via Homebrew, skipping upgrade" - fi - - - name: Install ChromeDriver - run: brew install chromedriver - - - name: Remove ChromeDriver quarantine (macOS) - run: | - CHROMEDRIVER_BIN=$(command -v chromedriver) - while [[ -L "$CHROMEDRIVER_BIN" ]]; do - NEXT=$(readlink "$CHROMEDRIVER_BIN") - [[ "$NEXT" != /* ]] && NEXT="$(dirname "$CHROMEDRIVER_BIN")/$NEXT" - CHROMEDRIVER_BIN=$NEXT - done - if [[ -f "$CHROMEDRIVER_BIN" ]] && xattr "$CHROMEDRIVER_BIN" 2>/dev/null | grep -q com.apple.quarantine; then - xattr -d com.apple.quarantine "$CHROMEDRIVER_BIN" 2>/dev/null || true - fi - - - name: Start ChromeDriver - run: | - chromedriver --port=4444 & - sleep 3 - lsof -i:4444 || (echo "ChromeDriver failed to start"; exit 1) + - name: Prepare matching Chrome for Testing and ChromeDriver + id: browser + uses: browser-actions/setup-chrome@v2 + with: + chrome-version: stable + install-chromedriver: true - name: Run integration tests (flutter drive + ChromeDriver) working-directory: zstandard_web/example - run: | - flutter drive \ - --driver=test_driver/integration_test.dart \ - --target=integration_test/zstandard_web_integration_test.dart \ - -d web-server \ - --web-port=8080 + env: + CHROME_EXECUTABLE: ${{ steps.browser.outputs.chrome-path }} + CHROMEDRIVER_EXECUTABLE: ${{ steps.browser.outputs.chromedriver-path }} + run: bash "$GITHUB_WORKSPACE/.github/scripts/run-web-integration.sh" check_web_publish_dry_run: name: Dry Run Publish Web - runs-on: [self-hosted, macOS] + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: + group: vypdev + labels: [self-hosted, macOS, ARM64] steps: - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v1 + - uses: subosito/flutter-action@v2 with: channel: 'stable' flutter-version: ${{ env.FLUTTER_VERSION }} + architecture: ${{ runner.arch == 'ARM64' && 'arm64' || 'x64' }} + cache-path: '${{ runner.tool_cache }}/zstandard-flutter-v2/:channel:-:version:-:arch:' - name: Get dependencies working-directory: zstandard_web diff --git a/.github/workflows/pr_check_windows.yml b/.github/workflows/pr_check_windows.yml index ad49b64d..926de78e 100644 --- a/.github/workflows/pr_check_windows.yml +++ b/.github/workflows/pr_check_windows.yml @@ -4,7 +4,6 @@ on: pull_request: branches: - '**' - - '!master' push: branches: - develop @@ -13,14 +12,24 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +env: + FLUTTER_VERSION: ${{ vars.FLUTTER_VERSION || '3.47.2' }} jobs: check_windows_analyze: name: Analyze Windows - runs-on: [self-hosted, Windows] + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: + group: vypdev + labels: [self-hosted, Windows, X64] steps: - uses: actions/checkout@v4 + - name: Prepare Flutter SDK + uses: ./.github/actions/setup-flutter-windows + with: + flutter-version: ${{ env.FLUTTER_VERSION }} + - name: Get dependencies working-directory: zstandard_windows shell: cmd @@ -33,10 +42,18 @@ jobs: check_windows_test: name: Test Windows - runs-on: [self-hosted, Windows] + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: + group: vypdev + labels: [self-hosted, Windows, X64] steps: - uses: actions/checkout@v4 + - name: Prepare Flutter SDK + uses: ./.github/actions/setup-flutter-windows + with: + flutter-version: ${{ env.FLUTTER_VERSION }} + - name: Get dependencies working-directory: zstandard_windows shell: cmd @@ -59,10 +76,18 @@ jobs: check_windows_test_from_pub_cache: name: Test Windows (from pub cache) - runs-on: [self-hosted, Windows] + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: + group: vypdev + labels: [self-hosted, Windows, X64] steps: - uses: actions/checkout@v4 + - name: Prepare Flutter SDK + uses: ./.github/actions/setup-flutter-windows + with: + flutter-version: ${{ env.FLUTTER_VERSION }} + - name: Remove zstandard_native to force resolution from pub cache shell: cmd run: rmdir /s /q zstandard_native @@ -84,10 +109,18 @@ jobs: check_windows_publish_dry_run: name: Dry Run Publish Windows - runs-on: [self-hosted, Windows] + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: + group: vypdev + labels: [self-hosted, Windows, X64] steps: - uses: actions/checkout@v4 + - name: Prepare Flutter SDK + uses: ./.github/actions/setup-flutter-windows + with: + flutter-version: ${{ env.FLUTTER_VERSION }} + - name: Get dependencies working-directory: zstandard_windows shell: cmd diff --git a/.github/workflows/pr_check_zstandard.yml b/.github/workflows/pr_check_zstandard.yml index 0b7a1408..aa629ae6 100644 --- a/.github/workflows/pr_check_zstandard.yml +++ b/.github/workflows/pr_check_zstandard.yml @@ -4,7 +4,6 @@ on: pull_request: branches: - '**' - - '!master' push: branches: - develop @@ -14,19 +13,24 @@ concurrency: cancel-in-progress: true env: - FLUTTER_VERSION: ${{ vars.FLUTTER_VERSION || '3.41.4' }} + FLUTTER_VERSION: ${{ vars.FLUTTER_VERSION || '3.47.2' }} jobs: check_zstandard_analyze: name: Analyze Zstandard - runs-on: [self-hosted, macOS] + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: + group: vypdev + labels: [self-hosted, macOS, ARM64] steps: - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v1 + - uses: subosito/flutter-action@v2 with: channel: 'stable' flutter-version: ${{ env.FLUTTER_VERSION }} + architecture: ${{ runner.arch == 'ARM64' && 'arm64' || 'x64' }} + cache-path: '${{ runner.tool_cache }}/zstandard-flutter-v2/:channel:-:version:-:arch:' - name: Get dependencies working-directory: zstandard @@ -38,15 +42,20 @@ jobs: check_zstandard_test: name: Test Zstandard - runs-on: [self-hosted, macOS] + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: + group: vypdev + labels: [self-hosted, macOS, ARM64] timeout-minutes: 30 steps: - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v1 + - uses: subosito/flutter-action@v2 with: channel: 'stable' flutter-version: ${{ env.FLUTTER_VERSION }} + architecture: ${{ runner.arch == 'ARM64' && 'arm64' || 'x64' }} + cache-path: '${{ runner.tool_cache }}/zstandard-flutter-v2/:channel:-:version:-:arch:' - name: Get dependencies working-directory: zstandard @@ -63,8 +72,9 @@ jobs: - name: Check coverage threshold (76% of lib/) working-directory: zstandard run: | - lcov --extract coverage/lcov.info 'lib/*' -o coverage/lcov_lib.info - COVERAGE=$(lcov --summary coverage/lcov_lib.info 2>&1 | grep "lines" | awk '{print $2}' | sed 's/%//') + # Flutter emits line coverage; keep the existing line coverage threshold. + lcov --rc function_coverage=0 --extract coverage/lcov.info 'lib/*' -o coverage/lcov_lib.info + COVERAGE=$(lcov --rc function_coverage=0 --summary coverage/lcov_lib.info 2>&1 | grep "lines" | awk '{print $2}' | sed 's/%//') echo "Coverage (lib/ only): ${COVERAGE}%" if [ -z "$COVERAGE" ]; then echo "Could not parse coverage" @@ -75,6 +85,9 @@ jobs: exit 1 fi + - name: Prepare coverage signature verification + uses: ./.github/actions/prepare-macos-coverage + - name: Upload coverage to Codecov uses: codecov/codecov-action@v4 with: @@ -85,14 +98,19 @@ jobs: check_zstandard_publish_dry_run: name: Dry Run Publish Zstandard - runs-on: [self-hosted, macOS] + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: + group: vypdev + labels: [self-hosted, macOS, ARM64] steps: - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v1 + - uses: subosito/flutter-action@v2 with: channel: 'stable' flutter-version: ${{ env.FLUTTER_VERSION }} + architecture: ${{ runner.arch == 'ARM64' && 'arm64' || 'x64' }} + cache-path: '${{ runner.tool_cache }}/zstandard-flutter-v2/:channel:-:version:-:arch:' - name: Get dependencies working-directory: zstandard diff --git a/.github/workflows/release_workflow.yml b/.github/workflows/release_workflow.yml index ff13d748..fcdc2d65 100644 --- a/.github/workflows/release_workflow.yml +++ b/.github/workflows/release_workflow.yml @@ -159,7 +159,9 @@ jobs: publish: name: Publish accepted production commit if: ${{ inputs.mode == 'publish' }} - runs-on: ubuntu-latest + runs-on: + group: vypdev + labels: [self-hosted, codex, Linux] needs: validate timeout-minutes: 30 permissions: diff --git a/.github/workflows/simulator_contract.yml b/.github/workflows/simulator_contract.yml new file mode 100644 index 00000000..8591dcb7 --- /dev/null +++ b/.github/workflows/simulator_contract.yml @@ -0,0 +1,24 @@ +name: iOS simulator contract +on: + pull_request: + paths: + - '.github/scripts/start_ios_simulator.py' + - '.github/scripts/tests/test_ios_simulator.py' + - '.github/workflows/pr_check_ios.yml' + - '.github/workflows/simulator_contract.yml' +jobs: + simulator-contract: + if: github.event.pull_request.head.repo.full_name == github.repository + runs-on: + group: vypdev + labels: [self-hosted, codex, Linux] + permissions: + contents: read + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + - uses: actions/setup-python@v6 + with: + python-version: '3.11' + - run: python -m unittest discover -s .github/scripts/tests -p test_ios_simulator.py -v diff --git a/.github/workflows/stale.yaml b/.github/workflows/stale.yaml index a21e3fb1..9a2e8e74 100644 --- a/.github/workflows/stale.yaml +++ b/.github/workflows/stale.yaml @@ -5,12 +5,14 @@ on: jobs: close-issues: - runs-on: ubuntu-latest + runs-on: + group: vypdev + labels: [self-hosted, codex, Linux] permissions: issues: write pull-requests: write steps: - - uses: actions/stale@v5 + - uses: actions/stale@v10 with: operations-per-run: 100 days-before-issue-stale: 30 diff --git a/zstandard/analysis_options.yaml b/zstandard/analysis_options.yaml index a5744c1c..e46654b9 100644 --- a/zstandard/analysis_options.yaml +++ b/zstandard/analysis_options.yaml @@ -1,3 +1,6 @@ +analyzer: + exclude: + - build/** include: package:flutter_lints/flutter.yaml # Additional information about this file can be found at diff --git a/zstandard/example/analysis_options.yaml b/zstandard/example/analysis_options.yaml index 0d290213..bf8d4218 100644 --- a/zstandard/example/analysis_options.yaml +++ b/zstandard/example/analysis_options.yaml @@ -7,6 +7,15 @@ # The following line activates a set of recommended lints for Flutter apps, # packages, and plugins designed to encourage good coding practices. +analyzer: + exclude: + - build/** + - android/** + - ios/** + - web/** + - windows/** + - macos/** + - linux/** include: package:flutter_lints/flutter.yaml linter: diff --git a/zstandard_android/analysis_options.yaml b/zstandard_android/analysis_options.yaml index a5744c1c..756c0f49 100644 --- a/zstandard_android/analysis_options.yaml +++ b/zstandard_android/analysis_options.yaml @@ -1,3 +1,7 @@ +analyzer: + exclude: + - build/** + - android/** include: package:flutter_lints/flutter.yaml # Additional information about this file can be found at diff --git a/zstandard_android/example/analysis_options.yaml b/zstandard_android/example/analysis_options.yaml index 0d290213..5671d4c5 100644 --- a/zstandard_android/example/analysis_options.yaml +++ b/zstandard_android/example/analysis_options.yaml @@ -7,6 +7,10 @@ # The following line activates a set of recommended lints for Flutter apps, # packages, and plugins designed to encourage good coding practices. +analyzer: + exclude: + - build/** + - android/** include: package:flutter_lints/flutter.yaml linter: diff --git a/zstandard_ios/analysis_options.yaml b/zstandard_ios/analysis_options.yaml index a5744c1c..1186c2bb 100644 --- a/zstandard_ios/analysis_options.yaml +++ b/zstandard_ios/analysis_options.yaml @@ -1,3 +1,7 @@ +analyzer: + exclude: + - build/** + - ios/** include: package:flutter_lints/flutter.yaml # Additional information about this file can be found at diff --git a/zstandard_ios/example/analysis_options.yaml b/zstandard_ios/example/analysis_options.yaml index 0d290213..a5a2c9f5 100644 --- a/zstandard_ios/example/analysis_options.yaml +++ b/zstandard_ios/example/analysis_options.yaml @@ -7,6 +7,10 @@ # The following line activates a set of recommended lints for Flutter apps, # packages, and plugins designed to encourage good coding practices. +analyzer: + exclude: + - build/** + - ios/** include: package:flutter_lints/flutter.yaml linter: diff --git a/zstandard_linux/analysis_options.yaml b/zstandard_linux/analysis_options.yaml index a5744c1c..612ee4bf 100644 --- a/zstandard_linux/analysis_options.yaml +++ b/zstandard_linux/analysis_options.yaml @@ -1,3 +1,7 @@ +analyzer: + exclude: + - build/** + - linux/** include: package:flutter_lints/flutter.yaml # Additional information about this file can be found at diff --git a/zstandard_linux/example/analysis_options.yaml b/zstandard_linux/example/analysis_options.yaml index 0d290213..cbe5dc5b 100644 --- a/zstandard_linux/example/analysis_options.yaml +++ b/zstandard_linux/example/analysis_options.yaml @@ -7,6 +7,10 @@ # The following line activates a set of recommended lints for Flutter apps, # packages, and plugins designed to encourage good coding practices. +analyzer: + exclude: + - build/** + - linux/** include: package:flutter_lints/flutter.yaml linter: diff --git a/zstandard_macos/analysis_options.yaml b/zstandard_macos/analysis_options.yaml index a5744c1c..5e15f457 100644 --- a/zstandard_macos/analysis_options.yaml +++ b/zstandard_macos/analysis_options.yaml @@ -1,3 +1,7 @@ +analyzer: + exclude: + - build/** + - macos/** include: package:flutter_lints/flutter.yaml # Additional information about this file can be found at diff --git a/zstandard_macos/example/analysis_options.yaml b/zstandard_macos/example/analysis_options.yaml index 0d290213..5d3e697c 100644 --- a/zstandard_macos/example/analysis_options.yaml +++ b/zstandard_macos/example/analysis_options.yaml @@ -7,6 +7,10 @@ # The following line activates a set of recommended lints for Flutter apps, # packages, and plugins designed to encourage good coding practices. +analyzer: + exclude: + - build/** + - macos/** include: package:flutter_lints/flutter.yaml linter: diff --git a/zstandard_web/analysis_options.yaml b/zstandard_web/analysis_options.yaml index a5744c1c..e46654b9 100644 --- a/zstandard_web/analysis_options.yaml +++ b/zstandard_web/analysis_options.yaml @@ -1,3 +1,6 @@ +analyzer: + exclude: + - build/** include: package:flutter_lints/flutter.yaml # Additional information about this file can be found at diff --git a/zstandard_web/example/analysis_options.yaml b/zstandard_web/example/analysis_options.yaml index 0d290213..2a2b57e8 100644 --- a/zstandard_web/example/analysis_options.yaml +++ b/zstandard_web/example/analysis_options.yaml @@ -7,6 +7,10 @@ # The following line activates a set of recommended lints for Flutter apps, # packages, and plugins designed to encourage good coding practices. +analyzer: + exclude: + - build/** + - web/** include: package:flutter_lints/flutter.yaml linter: diff --git a/zstandard_windows/analysis_options.yaml b/zstandard_windows/analysis_options.yaml index a5744c1c..ba427235 100644 --- a/zstandard_windows/analysis_options.yaml +++ b/zstandard_windows/analysis_options.yaml @@ -1,3 +1,7 @@ +analyzer: + exclude: + - build/** + - windows/** include: package:flutter_lints/flutter.yaml # Additional information about this file can be found at diff --git a/zstandard_windows/example/analysis_options.yaml b/zstandard_windows/example/analysis_options.yaml index 0d290213..6bd848b7 100644 --- a/zstandard_windows/example/analysis_options.yaml +++ b/zstandard_windows/example/analysis_options.yaml @@ -7,6 +7,10 @@ # The following line activates a set of recommended lints for Flutter apps, # packages, and plugins designed to encourage good coding practices. +analyzer: + exclude: + - build/** + - windows/** include: package:flutter_lints/flutter.yaml linter: