fix(webadmin): count every Stripe product in subscription metrics #7201
Workflow file for this run
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: | |
| # Metadata events (edited/labeled/unlabeled) and code events (push/synchronize) | |
| # run disjoint job sets: `metadata-preflight` for the former, `changes`/`hygiene` | |
| # for the latter. Sharing one cancel-in-progress group let a PR-title/body edit | |
| # cancel an in-flight code run it shares no work with, and cancelled jobs render | |
| # as red "fail" checks on the PR — flaky red with no code cause. Key the group by | |
| # lane so newer-cancels-older still holds within each lane, but never across them. | |
| group: repo-checks-${{ github.event.pull_request.number || github.ref }}-${{ github.event_name == 'pull_request' && contains(fromJSON('["edited", "labeled", "unlabeled"]'), github.event.action) && 'metadata' || 'code' }} | |
| 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: Set up Java for manifest-selected Firestore checks | |
| uses: actions/setup-java@v5 | |
| with: | |
| distribution: temurin | |
| java-version: '21' | |
| - name: Run current PR metadata preflight | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: | | |
| # The PR event payload's base-branch SHA field is frozen at whatever PR | |
| # event last delivered it; a metadata-only edited/labeled/unlabeled event | |
| # days later can carry a value main has long since moved past. actions/checkout | |
| # already fetched origin/${{ github.base_ref }} live (fetch-depth: 0 | |
| # above), so diff against that instead of the stale payload field. | |
| scripts/pr-preflight \ | |
| --base "origin/${{ github.base_ref }}" \ | |
| --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: Set up Java for manifest-selected Firestore checks | |
| uses: actions/setup-java@v5 | |
| with: | |
| distribution: temurin | |
| java-version: '21' | |
| - name: Save changed files | |
| run: | | |
| scripts/changed-files "${{ needs.changes.outputs.diff_base }}"...HEAD > /tmp/changed-files.txt | |
| cat /tmp/changed-files.txt | |
| - 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: Check typed error flow-control ratchet | |
| run: python3 .github/scripts/check_isinstance_return_ratchet.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: Test desktop qualification metadata writer | |
| run: python3 desktop/macos/scripts/release-keyvalue.py self-test | |
| - name: Check release process guards | |
| run: bash scripts/run-release-process-guards.sh | |
| - 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 | |
| # Keep formatter output reproducible across runner images and local hooks. | |
| flutter-version: 3.44.5 | |
| 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 "$FLUTTER_ROOT/bin/cache/dart-sdk/bin/dart" format --line-length 120 --set-exit-if-changed --output=none | |
| fi | |
| - name: Check Python formatting | |
| if: needs.changes.outputs.has_python == 'true' | |
| run: | | |
| # Pinned: an unpinned black drifts from local/pre-commit formatting on | |
| # every black release, making this check fail on untouched files. | |
| pip install -q black==26.5.1 | |
| 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 |