feat(desktop): Omi Beta as a separate app — run beta and stable side-by-side #3021
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Repo Checks | |
| on: | |
| push: | |
| branches: main | |
| pull_request: | |
| branches: main | |
| types: [opened, synchronize, reopened, edited, labeled, unlabeled] | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| concurrency: | |
| group: repo-checks-${{ github.event.pull_request.number || github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| metadata-preflight: | |
| name: PR Metadata Preflight | |
| if: github.event_name == 'pull_request' && contains(fromJSON('["edited", "labeled", "unlabeled"]'), github.event.action) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| - name: Set up Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version-file: backend/.python-version | |
| # The preflight can select uv-backed checks (e.g. backend-route-policy-baseline | |
| # runs the OpenAPI runner) whenever a PR touches backend routes, so this job | |
| # needs uv just like the sibling Hygiene job. | |
| - name: Set up uv | |
| uses: astral-sh/setup-uv@ecd24dd710f2fb0dca1693a67af11fc4a5c5ec84 | |
| with: | |
| enable-cache: true | |
| cache-dependency-glob: backend/openapi-requirements.txt | |
| - name: Run current PR metadata preflight | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: | | |
| scripts/pr-preflight \ | |
| --base "${{ github.event.pull_request.base.sha }}" \ | |
| --repository "${{ github.repository }}" \ | |
| --pr-number "${{ github.event.pull_request.number }}" | |
| changes: | |
| name: Detect Changes | |
| if: github.event_name != 'pull_request' || !contains(fromJSON('["edited", "labeled", "unlabeled"]'), github.event.action) | |
| runs-on: ubuntu-latest | |
| outputs: | |
| diff_base: ${{ steps.changes.outputs.diff_base }} | |
| has_arb: ${{ steps.changes.outputs.has_arb }} | |
| has_dart: ${{ steps.changes.outputs.has_dart }} | |
| has_firmware: ${{ steps.changes.outputs.has_firmware }} | |
| has_format: ${{ steps.changes.outputs.has_format }} | |
| has_python: ${{ steps.changes.outputs.has_python }} | |
| has_workflows: ${{ steps.changes.outputs.has_workflows }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| - name: Detect changed paths | |
| id: changes | |
| uses: ./.github/actions/detect-changes | |
| hygiene: | |
| name: Hygiene | |
| runs-on: ubuntu-latest | |
| needs: changes | |
| if: github.event_name != 'pull_request' || !contains(fromJSON('["edited", "labeled", "unlabeled"]'), github.event.action) | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| - name: Set up Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version-file: backend/.python-version | |
| - name: Set up uv | |
| uses: astral-sh/setup-uv@ecd24dd710f2fb0dca1693a67af11fc4a5c5ec84 | |
| with: | |
| enable-cache: true | |
| cache-dependency-glob: backend/openapi-requirements.txt | |
| - name: Save changed files | |
| run: | | |
| scripts/changed-files "${{ needs.changes.outputs.diff_base }}"...HEAD > /tmp/changed-files.txt | |
| cat /tmp/changed-files.txt | |
| - name: Test package architecture map guardrail | |
| run: python3 .github/scripts/test_check_package_architecture_maps.py | |
| - name: Test product invariant PR-body checker | |
| run: python3 .github/scripts/test_check_product_invariants.py | |
| - name: Test PR preflight contracts | |
| run: python3 .github/scripts/test_pr_preflight.py | |
| - name: Run shared PR contract preflight | |
| if: github.event_name == 'pull_request' | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: | | |
| scripts/pr-preflight \ | |
| --base "${{ needs.changes.outputs.diff_base }}" \ | |
| --repository "${{ github.repository }}" \ | |
| --pr-number "${{ github.event.pull_request.number }}" \ | |
| --event-payload-file "$GITHUB_EVENT_PATH" | |
| - name: Run deterministic check manifest on main pushes | |
| if: github.event_name != 'pull_request' | |
| run: python3 .github/scripts/run_checks.py --lane ci --base "${{ needs.changes.outputs.diff_base }}" --skip-pr-body-checks | |
| - name: Test brand UI ratchet helper | |
| run: python3 .github/scripts/test_check_brand_ui.py | |
| - name: Check typed error flow-control ratchet | |
| run: python3 .github/scripts/check_isinstance_return_ratchet.py | |
| - name: Test lifecycle header checker | |
| run: python3 .github/scripts/test_check_lifecycle_headers.py | |
| - name: Test version-prefixed filename lint | |
| run: python3 .github/scripts/test_check_version_prefixed_filenames.py | |
| - name: Check desktop prod promotion policy | |
| run: python3 .github/scripts/check-desktop-prod-promotion-policy.py | |
| - name: Test deployment concurrency policy | |
| run: python3 .github/scripts/check-deployment-concurrency.py --self-test | |
| - name: Check deployment concurrency policy | |
| run: python3 .github/scripts/check-deployment-concurrency.py | |
| - name: Check desktop release terminology | |
| run: python3 .github/scripts/check-desktop-release-terminology.py | |
| - name: Test desktop qualification metadata writer | |
| run: python3 desktop/macos/scripts/release-keyvalue.py self-test | |
| - name: Test desktop release promotion gate | |
| run: python3 .github/scripts/test_check_desktop_release_promotion.py | |
| - name: Test automatic desktop beta candidate gate | |
| run: python3 .github/scripts/test_check_desktop_auto_beta_candidate.py | |
| - name: Check release process guards | |
| run: python3 .github/scripts/check-release-process-guards.py | |
| - name: Check GitHub Actions workflows | |
| if: needs.changes.outputs.has_workflows == 'true' | |
| run: | | |
| GOBIN=/tmp/actionlint-bin go install github.com/rhysd/actionlint/cmd/actionlint@v1.7.12 | |
| mapfile -t files < <(grep -E '^\.github/workflows/.*\.ya?ml$' /tmp/changed-files.txt | while IFS= read -r f; do [ -f "$f" ] && echo "$f"; done || true) | |
| if [ "${#files[@]}" -gt 0 ]; then | |
| /tmp/actionlint-bin/actionlint -shellcheck "" "${files[@]}" | |
| fi | |
| formatting: | |
| name: Formatting | |
| runs-on: ubuntu-latest | |
| needs: changes | |
| if: needs.changes.outputs.has_format == 'true' | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| - name: Save changed files | |
| run: | | |
| scripts/changed-files "${{ needs.changes.outputs.diff_base }}"...HEAD > /tmp/changed-files.txt | |
| cat /tmp/changed-files.txt | |
| - name: Setup Flutter | |
| if: needs.changes.outputs.has_dart == 'true' | |
| uses: subosito/flutter-action@v2 | |
| with: | |
| channel: stable | |
| cache: true | |
| - name: Resolve Flutter dependencies for formatting | |
| if: needs.changes.outputs.has_dart == 'true' | |
| working-directory: app | |
| run: flutter pub get | |
| - name: Check Dart formatting | |
| if: needs.changes.outputs.has_dart == 'true' | |
| run: | | |
| FILES=$(grep '\.dart$' /tmp/changed-files.txt | grep -v -e '\.gen\.dart$' -e '\.g\.dart$' | while IFS= read -r f; do [ -f "$f" ] && echo "$f"; done || true) | |
| if [ -n "$FILES" ]; then | |
| echo "$FILES" | xargs dart format --line-length 120 --set-exit-if-changed --output=none | |
| fi | |
| - name: Check Python formatting | |
| if: needs.changes.outputs.has_python == 'true' | |
| run: | | |
| pip install -q black | |
| FILES=$(grep 'backend/.*\.py$' /tmp/changed-files.txt | while IFS= read -r f; do [ -f "$f" ] && echo "$f"; done || true) | |
| if [ -n "$FILES" ]; then | |
| echo "$FILES" | xargs black --check --line-length 120 --skip-string-normalization | |
| fi | |
| - name: Check ARB formatting | |
| if: needs.changes.outputs.has_arb == 'true' | |
| run: | | |
| FILES=$(grep '\.arb$' /tmp/changed-files.txt | while IFS= read -r f; do [ -f "$f" ] && echo "$f"; done || true) | |
| failed=0 | |
| for f in $FILES; do | |
| if ! python3 -m json.tool "$f" > /dev/null 2>&1; then | |
| echo "FAIL: $f is not valid JSON" | |
| failed=1 | |
| continue | |
| fi | |
| if ! python3 -c " | |
| import json, sys | |
| f = sys.argv[1] | |
| with open(f) as fh: | |
| original = fh.read() | |
| formatted = json.dumps(json.loads(original), indent=4, ensure_ascii=False) + '\n' | |
| if original != formatted: | |
| sys.exit(1) | |
| " "$f" 2>/dev/null; then | |
| echo "FAIL: $f not formatted with 4-space indent" | |
| failed=1 | |
| fi | |
| done | |
| if [ "$failed" -eq 1 ]; then | |
| echo "Fix: jq --indent 4 '.' <file> > tmp && mv tmp <file>" | |
| exit 1 | |
| fi | |
| - name: Check C/C++ formatting | |
| if: needs.changes.outputs.has_firmware == 'true' | |
| run: | | |
| FILES=$(grep -E '^(omi|omiGlass)/.*\.(c|cpp|cc|cxx|h|hpp)$' /tmp/changed-files.txt | while IFS= read -r f; do [ -f "$f" ] && echo "$f"; done || true) | |
| if [ -n "$FILES" ]; then | |
| echo "$FILES" | xargs clang-format --dry-run --Werror | |
| fi |