From f66f9759791051801b2e12f88d7332dbb2aea1cd Mon Sep 17 00:00:00 2001 From: tyler <165244341+Outtsett@users.noreply.github.com> Date: Mon, 4 May 2026 22:31:12 -0700 Subject: [PATCH 1/3] ci: harden permissions + 6 new pro-grade security workflows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the workflow surface that brings this repo in line with what serious public Flutter/Dart projects ship. New workflows: - TruffleHog (verified-only) — second secret scanner; complements gitleaks (regex) by validating found credentials against their APIs. Catches what gitleaks misses; eliminates false positives. - OSV-Scanner — Google's vulnerability scanner against the OSV.dev database for pubspec.lock + Gradle. Free, no API key, works on private repos without GHAS. Replaces dependency-review-action which needs GHAS on private repos. - actionlint — workflow YAML linter (shellcheck inside `run:` blocks). Would have caught the SC2086 quoting issues hit on PR #19. - OpenSSF Scorecard — weekly security posture scoring. Works on private repos with optional SCORECARD_TOKEN PAT for full check coverage; gracefully degrades without one. - CodeQL — static analysis for android/ Java/Kotlin + ios/ Swift. Gated on `github.event.repository.visibility == 'public'` because GHAS is required on private repos. - PR Title — amannn/action-semantic-pull-request validates the PR title against conventional-commits (belt-and-suspenders with commitlint when squash-merging uses the PR title as commit subject). Hardening across all existing workflows: - Pinned subosito/flutter-action@v2 -> 1a449444c387b1966244ae4d4f8c696479add0b2 - Pinned gitleaks/gitleaks-action@v2 -> ff98106e4c7b2bc287b24eaf42907196329070c7 - All third-party actions pinned to commit SHAs with version comments; Dependabot continues to keep them current. - Explicit `permissions:` block at workflow root (default-deny) and per-job (least-privilege escalation only where needed). - `concurrency:` block on every workflow to cancel superseded runs. - `timeout-minutes:` on every job (5–45m by job size). Fixes universal CI failures: - secret-scan.yml: add `pull-requests: read` permission. The gitleaks action calls `GET /repos/.../pulls/N/commits` which 403'd on every PR before this. Run on PR #20 hit `Resource not accessible by integration` because of this missing permission. CI workflow additions: - coverage-comment job posts a sticky PR comment with line/branch coverage diff vs base via romeovs/lcov-reporter-action (SHA-pinned). Soft signal — does not gate merge; rely on test job for that. Update CLAUDE.md with the full 9-workflow inventory + the required status-check list that branch protection should enforce on `main`. Local validation: actionlint passes clean against the full workflow set (Docker run, exit 0). Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/actionlint.yml | 45 ++++++++++++++++++++ .github/workflows/ci.yml | 62 ++++++++++++++++++++++++--- .github/workflows/codeql.yml | 71 +++++++++++++++++++++++++++++++ .github/workflows/commitlint.yml | 9 +++- .github/workflows/osv-scanner.yml | 59 +++++++++++++++++++++++++ .github/workflows/pr-title.yml | 49 +++++++++++++++++++++ .github/workflows/scorecard.yml | 53 +++++++++++++++++++++++ .github/workflows/secret-scan.yml | 12 +++++- .github/workflows/trufflehog.yml | 40 +++++++++++++++++ CLAUDE.md | 40 +++++++++++++++++ 10 files changed, 433 insertions(+), 7 deletions(-) create mode 100644 .github/workflows/actionlint.yml create mode 100644 .github/workflows/codeql.yml create mode 100644 .github/workflows/osv-scanner.yml create mode 100644 .github/workflows/pr-title.yml create mode 100644 .github/workflows/scorecard.yml create mode 100644 .github/workflows/trufflehog.yml diff --git a/.github/workflows/actionlint.yml b/.github/workflows/actionlint.yml new file mode 100644 index 0000000..a1c1dcc --- /dev/null +++ b/.github/workflows/actionlint.yml @@ -0,0 +1,45 @@ +name: actionlint + +on: + push: + branches: [main] + paths: + - ".github/workflows/**" + - ".github/actions/**" + - ".github/actionlint*" + pull_request: + paths: + - ".github/workflows/**" + - ".github/actions/**" + - ".github/actionlint*" + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: actionlint-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + actionlint: + name: Lint workflow YAML + runs-on: ubuntu-latest + timeout-minutes: 5 + permissions: + contents: read + steps: + - name: Checkout + uses: actions/checkout@v4 + + # Run actionlint via the official downloader script. Runs the binary + # directly on the checkout, no third-party action needed. Includes + # shellcheck for `run:` blocks and pyflakes for python in `run:` blocks. + - name: Download actionlint + id: get_actionlint + run: bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/011a6d15e749bb3f2d771eed9c7aa0e7e3e10ee7/scripts/download-actionlint.bash) + shell: bash + + - name: Run actionlint + run: ${{ steps.get_actionlint.outputs.executable }} -color + shell: bash diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3a05c50..0bdab59 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,14 +17,21 @@ concurrency: env: FLUTTER_VERSION: "3.41.5" FLUTTER_CHANNEL: stable + # subosito/flutter-action@v2 — pinned to commit SHA for supply-chain safety. + # Bump in lockstep with the Dependabot github-actions update PR. + FLUTTER_ACTION_REF: 1a449444c387b1966244ae4d4f8c696479add0b2 jobs: analyze: name: Analyze + Format runs-on: ubuntu-latest + timeout-minutes: 15 + permissions: + contents: read steps: - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v2 + - name: Setup Flutter + uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 with: flutter-version: ${{ env.FLUTTER_VERSION }} channel: ${{ env.FLUTTER_CHANNEL }} @@ -41,10 +48,14 @@ jobs: test: name: Unit + Widget tests runs-on: ubuntu-latest + timeout-minutes: 20 needs: analyze + permissions: + contents: read steps: - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v2 + - name: Setup Flutter + uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 with: flutter-version: ${{ env.FLUTTER_VERSION }} channel: ${{ env.FLUTTER_CHANNEL }} @@ -60,18 +71,23 @@ jobs: name: coverage-lcov path: coverage/lcov.info if-no-files-found: ignore + retention-days: 30 build-android: name: Build Android (debug APK) runs-on: ubuntu-latest + timeout-minutes: 30 needs: test + permissions: + contents: read steps: - uses: actions/checkout@v4 - uses: actions/setup-java@v4 with: distribution: temurin java-version: "17" - - uses: subosito/flutter-action@v2 + - name: Setup Flutter + uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 with: flutter-version: ${{ env.FLUTTER_VERSION }} channel: ${{ env.FLUTTER_CHANNEL }} @@ -90,10 +106,14 @@ jobs: build-ios: name: Build iOS (no codesign) runs-on: macos-latest + timeout-minutes: 45 needs: test + permissions: + contents: read steps: - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v2 + - name: Setup Flutter + uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 with: flutter-version: ${{ env.FLUTTER_VERSION }} channel: ${{ env.FLUTTER_CHANNEL }} @@ -106,10 +126,14 @@ jobs: build-web: name: Build Web runs-on: ubuntu-latest + timeout-minutes: 20 needs: test + permissions: + contents: read steps: - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v2 + - name: Setup Flutter + uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 with: flutter-version: ${{ env.FLUTTER_VERSION }} channel: ${{ env.FLUTTER_CHANNEL }} @@ -123,3 +147,31 @@ jobs: path: build/web if-no-files-found: error retention-days: 14 + + coverage-comment: + name: Coverage PR comment + runs-on: ubuntu-latest + timeout-minutes: 5 + needs: test + if: github.event_name == 'pull_request' + permissions: + contents: read + pull-requests: write + steps: + - uses: actions/checkout@v4 + - name: Download coverage + uses: actions/download-artifact@v4 + with: + name: coverage-lcov + path: coverage + # romeovs/lcov-reporter-action — pinned to commit SHA. Posts a sticky PR + # comment with line / branch coverage diff vs the PR base. Soft signal — + # does not gate the merge. Use branch-protection rulesets to make the + # `test` job a required check; this is just visibility. + - name: Post coverage comment + if: hashFiles('coverage/lcov.info') != '' + uses: romeovs/lcov-reporter-action@25674467b99fc58cc7706dc246d9647a94b5ba8f + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + lcov-file: coverage/lcov.info + delete-old-comments: true diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..ba6bc4b --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,71 @@ +name: CodeQL + +on: + push: + branches: [main] + paths: + - "android/**/*.java" + - "android/**/*.kt" + - "ios/**/*.swift" + - ".github/workflows/codeql.yml" + pull_request: + branches: [main] + paths: + - "android/**/*.java" + - "android/**/*.kt" + - "ios/**/*.swift" + - ".github/workflows/codeql.yml" + schedule: + - cron: '0 9 * * 1' + +permissions: + contents: read + +concurrency: + group: codeql-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + analyze: + name: CodeQL (${{ matrix.language }}) + # CodeQL is free for public repos. On private repos it requires GitHub + # Advanced Security (paid). Skip until the repo is public OR GHAS is + # enabled — otherwise runs would fail with `Resource not accessible by + # integration` on every PR. + if: github.event.repository.visibility == 'public' + runs-on: ubuntu-latest + timeout-minutes: 30 + permissions: + security-events: write + packages: read + actions: read + contents: read + + strategy: + fail-fast: false + matrix: + include: + - language: java-kotlin + build-mode: none + - language: swift + build-mode: manual + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + queries: security-extended,security-and-quality + + - name: Autobuild (Swift) + if: matrix.language == 'swift' + uses: github/codeql-action/autobuild@v3 + + - name: Perform CodeQL analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{ matrix.language }}" diff --git a/.github/workflows/commitlint.yml b/.github/workflows/commitlint.yml index 9eab17c..95405ef 100644 --- a/.github/workflows/commitlint.yml +++ b/.github/workflows/commitlint.yml @@ -8,12 +8,19 @@ on: permissions: contents: read - pull-requests: read + +concurrency: + group: commitlint-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: commitlint: name: Lint commit messages runs-on: ubuntu-latest + timeout-minutes: 5 + permissions: + contents: read + pull-requests: read steps: - uses: actions/checkout@v4 with: diff --git a/.github/workflows/osv-scanner.yml b/.github/workflows/osv-scanner.yml new file mode 100644 index 0000000..eb3bf10 --- /dev/null +++ b/.github/workflows/osv-scanner.yml @@ -0,0 +1,59 @@ +name: OSV-Scanner + +on: + push: + branches: [main] + pull_request: + branches: [main] + paths: + - "pubspec.yaml" + - "pubspec.lock" + - "android/build.gradle*" + - "android/app/build.gradle*" + - "android/settings.gradle*" + - ".github/workflows/osv-scanner.yml" + schedule: + - cron: '0 7 * * 1' + +permissions: + contents: read + +concurrency: + group: osv-scanner-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + scan: + name: OSV-Scanner (pubspec + gradle) + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + security-events: write + steps: + - name: Checkout + uses: actions/checkout@v4 + + # google/osv-scanner-action — pinned to commit SHA. Scans pubspec.lock, + # gradle build files, and any other supported manifest in the tree against + # the OSV.dev vulnerability database. Free, no API key required, works on + # private repos without GHAS. + - name: Run OSV-Scanner + uses: google/osv-scanner-action/.github/actions/osv-scanner-action@43f380b8fc43a816831a9f5ee6fc91170809c7e9 + with: + scan-args: |- + --recursive + --skip-git + --format=sarif + --output=osv-results.sarif + ./ + continue-on-error: true + + - name: Upload SARIF + if: always() && hashFiles('osv-results.sarif') != '' + uses: actions/upload-artifact@v4 + with: + name: osv-scanner-sarif + path: osv-results.sarif + if-no-files-found: ignore + retention-days: 30 diff --git a/.github/workflows/pr-title.yml b/.github/workflows/pr-title.yml new file mode 100644 index 0000000..6572588 --- /dev/null +++ b/.github/workflows/pr-title.yml @@ -0,0 +1,49 @@ +name: PR Title + +on: + pull_request_target: + types: [opened, edited, synchronize, reopened] + +permissions: + contents: read + +concurrency: + group: pr-title-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + validate: + name: Validate Conventional Commits PR title + runs-on: ubuntu-latest + timeout-minutes: 5 + permissions: + pull-requests: read + steps: + # amannn/action-semantic-pull-request — pinned to commit SHA. Validates + # PR title against the same conventional-commits convention enforced by + # commitlint.config.js. Belt-and-suspenders: commitlint validates the + # individual commits; this validates the merge-commit subject GitHub + # generates when squash-merging from PR title. + - name: Validate + uses: amannn/action-semantic-pull-request@45b9ed7cf24087a2f7785bf55be97394ba87e1c2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + types: | + feat + fix + chore + docs + refactor + test + perf + ci + build + revert + style + requireScope: false + subjectPattern: ^(?![A-Z]).+$ + subjectPatternError: | + The subject "{subject}" found in the pull request title "{title}" + didn't match the configured pattern. Please ensure the subject + doesn't start with an uppercase character. diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml new file mode 100644 index 0000000..ecd08d0 --- /dev/null +++ b/.github/workflows/scorecard.yml @@ -0,0 +1,53 @@ +name: OpenSSF Scorecard + +on: + schedule: + - cron: '0 8 * * 1' + push: + branches: [main] + workflow_dispatch: + +permissions: read-all + +concurrency: + group: scorecard-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + security-events: write + id-token: write + contents: read + actions: read + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + persist-credentials: false + + # ossf/scorecard-action — pinned to commit SHA. Free for public repos. + # On private repos, several checks (Branch-Protection, Code-Review, + # Vulnerabilities) require a fine-grained PAT in `secrets.SCORECARD_TOKEN` + # with read access to admin / pull_requests / contents. Without the + # token, those checks are skipped and the run still succeeds with a + # partial score. + - name: Run scorecard analysis + uses: ossf/scorecard-action@af76153369ae1eb1eaffc4118046b7fda9a8419e + with: + results_file: scorecard-results.sarif + results_format: sarif + repo_token: ${{ secrets.SCORECARD_TOKEN || secrets.GITHUB_TOKEN }} + publish_results: ${{ github.event.repository.visibility == 'public' }} + + - name: Upload SARIF + if: always() && hashFiles('scorecard-results.sarif') != '' + uses: actions/upload-artifact@v4 + with: + name: scorecard-sarif + path: scorecard-results.sarif + if-no-files-found: ignore + retention-days: 30 diff --git a/.github/workflows/secret-scan.yml b/.github/workflows/secret-scan.yml index 64d34e0..9560d28 100644 --- a/.github/workflows/secret-scan.yml +++ b/.github/workflows/secret-scan.yml @@ -10,10 +10,19 @@ on: permissions: contents: read +concurrency: + group: secret-scan-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: gitleaks: name: gitleaks runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + pull-requests: read + security-events: write steps: - name: Checkout uses: actions/checkout@v4 @@ -21,7 +30,8 @@ jobs: fetch-depth: 0 - name: Run gitleaks - uses: gitleaks/gitleaks-action@v2 + # gitleaks/gitleaks-action@v2 — pinned to commit SHA for supply-chain safety. + uses: gitleaks/gitleaks-action@ff98106e4c7b2bc287b24eaf42907196329070c7 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITLEAKS_ENABLE_UPLOAD_ARTIFACT: true diff --git a/.github/workflows/trufflehog.yml b/.github/workflows/trufflehog.yml new file mode 100644 index 0000000..e865239 --- /dev/null +++ b/.github/workflows/trufflehog.yml @@ -0,0 +1,40 @@ +name: TruffleHog + +on: + push: + branches: ["**"] + pull_request: + schedule: + - cron: '30 6 * * 1' + +permissions: + contents: read + +concurrency: + group: trufflehog-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + trufflehog: + name: TruffleHog (verified-only) + runs-on: ubuntu-latest + timeout-minutes: 15 + permissions: + contents: read + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Run TruffleHog + # trufflesecurity/trufflehog — pinned to commit SHA for supply-chain safety. + # --only-verified: actually attempts to validate found credentials against + # their respective APIs. Eliminates false positives at the cost of network + # calls. Complements gitleaks (regex-based) with verified secret detection. + uses: trufflesecurity/trufflehog@5f47aad1c2df34f7c6230784ce9a5a659922f479 + with: + path: ./ + base: ${{ github.event.repository.default_branch }} + head: HEAD + extra_args: --only-verified --no-update diff --git a/CLAUDE.md b/CLAUDE.md index 6de1e1b..9cff9c9 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -237,6 +237,46 @@ PowerShell on Windows: invoke `flutter` directly; no shell activation needed. enabled platforms list as far as the scaffold shows; ignore unless explicitly added. +## CI / CD surface + +Workflows live under `.github/workflows/`. All third-party actions are +pinned to commit SHAs; official `actions/*` use major-version tags. +Every workflow has explicit `permissions:`, `concurrency:`, and +`timeout-minutes:` set. `actionlint` runs on every PR that touches a +workflow file. + +| Workflow | File | Trigger | Purpose | +|---|---|---|---| +| CI | `ci.yml` | push/PR to main | analyze + format check, tests with coverage, debug Android APK, iOS no-codesign, web; coverage PR comment | +| Commitlint | `commitlint.yml` | push/PR to main | conventional-commits enforcement on every commit | +| PR Title | `pr-title.yml` | PR opened/edited | conventional-commits enforcement on PR title (squash-merge subject) | +| Secret Scan | `secret-scan.yml` | push/PR + weekly | gitleaks regex-based secret detection | +| TruffleHog | `trufflehog.yml` | push/PR + weekly | TruffleHog `--only-verified` (validates secrets against APIs) | +| OSV-Scanner | `osv-scanner.yml` | push/PR with deps + weekly | CVE detection in `pubspec.lock` + Gradle manifests | +| actionlint | `actionlint.yml` | push/PR touching `.github/workflows/**` | workflow YAML lint + shellcheck | +| OpenSSF Scorecard | `scorecard.yml` | weekly + push to main | repo security posture scoring | +| CodeQL | `codeql.yml` | push/PR with native code + weekly | static analysis of `android/` Java/Kotlin + `ios/` Swift (gated on public visibility — needs GHAS on private) | + +### Required status checks for branch protection on `main` + +Once flipping public, configure under **Settings → Rules → Branch ruleset**: +- `CI / Analyze + Format` +- `CI / Unit + Widget tests` +- `Commitlint / Lint commit messages` +- `PR Title / Validate Conventional Commits PR title` +- `Secret Scan / gitleaks` +- `TruffleHog / TruffleHog (verified-only)` +- `OSV-Scanner / OSV-Scanner (pubspec + gradle)` +- `actionlint / Lint workflow YAML` + +### Local validation before pushing CI changes + +```bash +docker run --rm -v "$PWD:/repo" -w /repo rhysd/actionlint:latest -color +docker run --rm -v "$PWD:/repo" -w /repo zricethezav/gitleaks:latest detect --source=/repo --no-banner --redact --exit-code=1 +docker run --rm -v "$PWD:/repo" trufflesecurity/trufflehog:latest git file:///repo --only-verified +``` + ## When working in this repo - Do not mark a screen "done" while `main.dart` is still the default From 7ad04c6fd96ba22a5c956b1e54b10dae046c823b Mon Sep 17 00:00:00 2001 From: tyler <165244341+Outtsett@users.noreply.github.com> Date: Mon, 4 May 2026 22:45:28 -0700 Subject: [PATCH 2/3] ci(fix): osv-scanner action path + trufflehog flag dedup + iOS deploy target MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three fixes to first run of PR #21: 1. OSV-Scanner action path was wrong (`google/osv-scanner-action/.github/ actions/osv-scanner-action@SHA` → `google/osv-scanner-action/ osv-scanner-action@SHA`). The reusable composite lives at the subdirectory root, not under `.github/actions/`. Fixed. 2. TruffleHog action wrapper already passes `--no-update` to the trufflehog binary, so duplicating it via `extra_args` triggered `flag 'no-update' cannot be repeated`. Removed the duplicate. 3. iOS build was failing on `pod install` with: The plugin "health" requires a higher minimum iOS deployment version than your application is targeting. Increase to 14.0. Pre-existing config bug — the Xcode project's three IPHONEOS_DEPLOYMENT_TARGET entries were still on the Flutter default of 12.0. Bumped to 14.0 to satisfy `health 13.x`. actionlint runs clean against the corrected workflow set. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/osv-scanner.yml | 10 +++++----- .github/workflows/trufflehog.yml | 3 ++- ios/Runner.xcodeproj/project.pbxproj | 6 +++--- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.github/workflows/osv-scanner.yml b/.github/workflows/osv-scanner.yml index eb3bf10..1868835 100644 --- a/.github/workflows/osv-scanner.yml +++ b/.github/workflows/osv-scanner.yml @@ -34,12 +34,12 @@ jobs: - name: Checkout uses: actions/checkout@v4 - # google/osv-scanner-action — pinned to commit SHA. Scans pubspec.lock, - # gradle build files, and any other supported manifest in the tree against - # the OSV.dev vulnerability database. Free, no API key required, works on - # private repos without GHAS. + # google/osv-scanner-action/osv-scanner-action — pinned to commit SHA. + # Scans pubspec.lock, gradle build files, and any other supported manifest + # in the tree against the OSV.dev vulnerability database. Free, no API key + # required, works on private repos without GHAS. - name: Run OSV-Scanner - uses: google/osv-scanner-action/.github/actions/osv-scanner-action@43f380b8fc43a816831a9f5ee6fc91170809c7e9 + uses: google/osv-scanner-action/osv-scanner-action@43f380b8fc43a816831a9f5ee6fc91170809c7e9 with: scan-args: |- --recursive diff --git a/.github/workflows/trufflehog.yml b/.github/workflows/trufflehog.yml index e865239..779e4e4 100644 --- a/.github/workflows/trufflehog.yml +++ b/.github/workflows/trufflehog.yml @@ -32,9 +32,10 @@ jobs: # --only-verified: actually attempts to validate found credentials against # their respective APIs. Eliminates false positives at the cost of network # calls. Complements gitleaks (regex-based) with verified secret detection. + # Note: the action wrapper already passes --no-update; do not duplicate. uses: trufflesecurity/trufflehog@5f47aad1c2df34f7c6230784ce9a5a659922f479 with: path: ./ base: ${{ github.event.repository.default_branch }} head: HEAD - extra_args: --only-verified --no-update + extra_args: --only-verified diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 7a30aa7..0010f4d 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -346,7 +346,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -472,7 +472,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -523,7 +523,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; From 06845baeb12fe1f83d1a40d36dafb7a1c2d52744 Mon Sep 17 00:00:00 2001 From: tyler <165244341+Outtsett@users.noreply.github.com> Date: Mon, 4 May 2026 22:58:25 -0700 Subject: [PATCH 3/3] ci: path-filter iOS build to iOS-relevant files only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit iOS build is now informational (path-filtered, doesn't run on PRs that touch no iOS files) instead of gating every PR. Three reasons: 1. Tyler's dev machine is Windows — iOS validation happens on a Mac pre-store-submission, not on every PR. 2. Transitive deps periodically ship iOS code requiring a newer Xcode SDK than the macOS runner ships. Currently device_info_plus 12.4.0 calls NSProcessInfo.isiOSAppOnVision (iOS 17+ selector) which the runner's Xcode rejects: ARC Semantic Issue (Xcode): No visible @interface for 'NSProcessInfo' declares the selector 'isiOSAppOnVision' Unrelated PRs (like this one) get red iOS checks for transitive dep churn they didn't introduce. 3. macos-latest runner minutes are 10x ubuntu-latest — not worth burning on PRs that touch no iOS files. Implementation: add a `changed` job using dorny/paths-filter@v3 (pinned to commit SHA) that reads the diff against base. Sets `ios` output to true if any of `ios/**`, `pubspec.yaml`, `pubspec.lock`, or this workflow file changed. Build iOS job gated on `needs.changed.outputs.ios == 'true'`. Tradeoff: iOS regressions in transitive Dart deps don't surface until a PR touches pubspec or iOS files. Acceptable because: - Manual iOS builds happen pre-store-submission anyway - Dependabot bumps to pubspec.lock will trigger the iOS build, catching transitive-dep iOS breakage at the bump PR - A scheduled weekly cron could be added if drift becomes an issue Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/ci.yml | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0bdab59..3273a3b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -103,11 +103,44 @@ jobs: if-no-files-found: error retention-days: 14 + # iOS build is informational, not gating. Reasons: + # 1. Tyler's dev machine is Windows; iOS validation happens on a Mac + # pre-store-submission, not on every PR. + # 2. Transitive deps (currently device_info_plus 12.4.0) periodically + # ship code that requires a newer Xcode SDK than the macOS runner + # ships, breaking unrelated PRs. + # 3. macos-latest runner minutes are 10x ubuntu-latest — not worth + # burning on PRs that touch no iOS files. + # Path-filtered to iOS-relevant files; does not gate test/analyze. + changed: + name: Detect changed paths + runs-on: ubuntu-latest + timeout-minutes: 5 + permissions: + contents: read + pull-requests: read + outputs: + ios: ${{ steps.filter.outputs.ios }} + steps: + - uses: actions/checkout@v4 + - id: filter + # dorny/paths-filter@v3 — pinned to commit SHA. Reads its filter + # config from the workflow file directly via the `filters:` input. + uses: dorny/paths-filter@d1c1ffe0248fe513906c8e24db8ea791d46f8590 + with: + filters: | + ios: + - 'ios/**' + - 'pubspec.yaml' + - 'pubspec.lock' + - '.github/workflows/ci.yml' + build-ios: name: Build iOS (no codesign) runs-on: macos-latest timeout-minutes: 45 - needs: test + needs: [test, changed] + if: needs.changed.outputs.ios == 'true' permissions: contents: read steps: