From 86e20b97a29b6ad550964e64c494e6c738c0b09d Mon Sep 17 00:00:00 2001 From: Anilcan Cakir Date: Fri, 21 Aug 2026 13:04:01 +0300 Subject: [PATCH] ci: commit the analyzer excludes the Flutter migrator rewrites on every pub get CI's first step after checkout is `flutter pub get`, which runs the Flutter tool's `analysis_options.yaml` migrator and appends an `analyzer.exclude` block for `build/` plus the six platform runner directories. Seven steps later `dart pub publish --dry-run` found the checkout dirty, reported "1 checked-in file is modified in git", and exited 65 on the warning. That turned `Lint & Test` red on every open PR, including a Dependabot bump touching nothing but a workflow file, so the check failed identically whether or not the PR had broken something. Committing the block the migrator wants makes the migrator a no-op and the checkout clean. Reverting the file inside the workflow instead would leave every contributor's tree dirty after a `pub get` and hide the drift rather than settle it. --- CHANGELOG.md | 1 + analysis_options.yaml | 14 ++++++++++++++ example/analysis_options.yaml | 15 +++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index edae21af..afc1352a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ This project follows [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0. ### Quality +- **`Lint & Test` was red on every open PR, and not one of them had broken anything.** The Flutter tool ships an `analysis_options.yaml` migrator that appends an `analyzer.exclude` block for `build/` and the six platform runner directories, and it runs on every `flutter pub get`. CI's first step after checkout is `flutter pub get`, so by the time `dart pub publish --dry-run` ran seven steps later the checkout was dirty, the dry-run reported `1 checked-in file is modified in git`, and it exits 65 on a warning. Every gate before it was green; the failure was the toolchain editing the repo mid-run. That is the worst shape a red check can take, because it fails identically on a workflow-only Dependabot bump and on a real regression, so the signal stops carrying information. Both files now carry the block the migrator wants, which makes the migrator a no-op and the checkout clean. The alternative, reverting the file inside the workflow before the dry-run, was rejected: it would leave every contributor's tree dirty after a `pub get` and hide the drift instead of settling it. The excludes are also correct on their own terms, since none of those directories hold hand-written Dart. (`analysis_options.yaml`, `example/analysis_options.yaml`) - **48 branches had accumulated, 45 of them PRs that landed months ago.** `delete_branch_on_merge` was off, so every task branch outlived its merge and the list grew by one per PR since December 2025. It is on now, which handles everything from here without a workflow, a token or a cron: the setting fires on the merge event alone, touches only that PR's head branch, and cannot reach `master` or `v0` because both are protected. The 46 leftovers (45 merged, plus a branch from the abandoned release-please setup whose PR #80 was closed unmerged) are deleted. The tempting alternative, a scheduled stale-branch job, was rejected: with the setting on it would only ever catch branches that never merged, this repo has produced exactly one of those in its history, and its "untouched for N days" test cannot tell an abandoned branch from one you set down for a fortnight. A merge is a statement of intent; a date is not. Recorded in `CLAUDE.md` under Branching, because a policy nobody wrote down is not a policy. - **Do not reach for `git branch --merged` in this repo.** The merge button squashes, which writes a new commit and severs the branch's ancestry to `master`, so all 45 landed branches reported as unmerged while their PRs read MERGED. A cleanup script built on that signal deletes nothing, and one built on content comparison deletes the wrong thing. The audit ran off PR state instead, and its guard earned its keep: it refused a local branch whose tip had drifted from the remote by one unpushed commit, which turned out to be a popover fix that reached `master` through #157 under a different commit. - **The Dependabot auto-merge job failed on every bump, and it was never going to pass.** Its first line was `gh pr review --approve` run with `GITHUB_TOKEN`, against a repo where "Allow GitHub Actions to create and approve pull requests" is off. GitHub answered `GraphQL: GitHub Actions is not permitted to approve pull requests (addPullRequestReview)`, the step exited 1, and the `gh pr merge --auto` line underneath it never ran once. Three open bumps (#169, #170, #171) each carried that red check while every other gate was green, which is the worst training a CI surface can give you: a check that is always red is a check you stop reading. The approve line is gone and the approval stays human on purpose, because this repo pins every action by SHA and runs zizmor plus scorecard over the result, so a person checking the new SHA against its upstream tag is the point of the exercise rather than paperwork in front of it. Arming auto-merge is now the whole job, so an approval merges the PR by itself. The repo setting `allow_auto_merge` was off as well, which means even a successful approve would have failed on the very next line; it is on now. (`.github/workflows/dependabot-auto-merge.yml`) diff --git a/analysis_options.yaml b/analysis_options.yaml index f9b30346..0ab78fed 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1 +1,15 @@ +# Generated build output and the per-platform runner projects carry no +# hand-written Dart worth analyzing. Written here by the Flutter tool's +# `analysis_options.yaml` migrator, which re-applies it on every `pub get`, so +# it stays committed: an uncommitted rewrite fails `dart pub publish --dry-run` +# with "1 checked-in file is modified in git". +analyzer: + exclude: + - build/** + - android/** + - ios/** + - web/** + - windows/** + - macos/** + - linux/** include: package:flutter_lints/flutter.yaml diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml index 0d290213..22f05114 100644 --- a/example/analysis_options.yaml +++ b/example/analysis_options.yaml @@ -5,6 +5,21 @@ # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be # invoked from the command line by running `flutter analyze`. +# Generated build output and the per-platform runner projects carry no +# hand-written Dart worth analyzing. Written here by the Flutter tool's +# `analysis_options.yaml` migrator, which re-applies it on every `pub get`, so +# it stays committed: an uncommitted rewrite fails `dart pub publish --dry-run` +# with "1 checked-in file is modified in git". +analyzer: + exclude: + - build/** + - android/** + - ios/** + - web/** + - windows/** + - macos/** + - linux/** + # The following line activates a set of recommended lints for Flutter apps, # packages, and plugins designed to encourage good coding practices. include: package:flutter_lints/flutter.yaml