Repository navigation
feat(nonstop_cli): generate a production-shaped app skeleton - #40
Merged
Merged
Conversation
The mono-repo brick is about to gain ~170 Dart files under __brick__. They are mustache templates, not parseable Dart, and `dart format` has no exclude of its own, so the lint script now filters the paths before handing them over. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…brick
The brick created four empty directories and a melos.yaml. Everything a real
app needs was still a day of copy-paste from the last project, so this ports
that groundwork in, adapted from an app that had already grown it.
Always generated: core (logger, DI bootstrap, route observers, error types,
BLoC observer), di, design_system (Material 3 theme, components, toasts,
loaders), localization (type-safe strings via i69n), utils, and the app shell
itself - bootstrap, router and splash, with no business logic.
Optional, each gated on a brick variable: network, auth, firestore,
notifications, analytics, crashlytics, feature_flags, developer, dashboard.
A directory named `{{<module>_dir}}` renders to an empty path segment when its
module is off, and mason skips every file underneath it - section tags cannot
be used for this because `{{/x}}` contains a path separator. pre_gen derives
those variables, resolves the two dependencies between modules and runs
`flutter create` so the brick's own lib/ and pubspec.yaml overlay it. post_gen
formats the result, because mustache line breaks depend on the answers and a
fresh project has to pass its own `melos lint`.
Every process step now checks its exit code. cli_core's trackOperation reports
success as soon as the future completes, so a failed `flutter create` or
`melos bootstrap` showed a green tick and generation carried on to produce a
half-built workspace.
Non-Firebase projects ship a boot test that runs the real bootstrap and pumps
the app; Firebase ones cannot until `flutterfire configure` has been run, so
the file name renders empty and mason drops it.
Verified across ten module combinations: melos bootstrap, melos lint and melos
test all pass, `flutter build web` succeeds, and the app reaches its first
screen at runtime.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`create` took a name, a description and an org. The brick now has nine optional modules behind it, so it asks - one multi-select, defaults pre-ticked - and prompts for the description and org name when they were not passed. Explicit `--network` / `--no-network` style flags always win, `-y` takes the recommended set, and a missing or undrivable terminal falls back to defaults so scripts and CI keep working. Both ends have to be a terminal: mason_logger reads stdin but draws on stdout, and piping either one breaks it. Two module dependencies are applied and reported rather than left to fail at compile time: notifications needs network, developer tools need feature flags. The brick's hooks apply the same rules, so `mason make` on its own still produces something that builds. The description is validated like the name and org already were. It is written into YAML, Dart string literals and Markdown, and a quote or a dollar sign silently produced a project that does not parse. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Regenerated with `mason bundle` so the CLI ships the new template. The registry lookup for the brick is version-pinned and the brick is now 2.0.0, so this bundle is what `nonstop create` actually uses. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The brick was ported from a health app, and the rename pass only caught the product name. Its domain had also settled into shared code, where it would have shipped to every project generated from the template. Removed: survey and health event groups from analytics, along with the two survey helpers on AnalyticsHelper; a survey-assignment event on core's global event channel, replaced with a single generic example; and the source project's own examples throughout the analytics README. The notification client hardcoded one deep link - a `surveyAssignments` message type routed to `/home/survey/assignment/$id`, a path this template does not even define. It now reads a `route` from the message data and pushes that, which is the contract the docs can actually describe. The auth header is drawn from the theme instead of fetching a placeholder image, so a fresh project has no artwork to ship and nothing to fetch. Also corrects the CI package filter, which excluded a workspace named after the source project and so never matched anything here. Verified by decoding all 222 files embedded in the generated bundle: no product-specific term survives in what the CLI actually ships. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The workflow runs `dart format .` directly rather than the melos lint script, so excluding __brick__ there was not enough - the job still parsed the mustache templates and failed with exit 65. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
nonstop create <name>produced four empty directories and amelos.yaml. Everything a real app needs — logging, DI, theme, routing, networking, auth — was still a day of copy-paste from the last project. This ports that groundwork into the mono-repo brick and asks which parts you want.Non-interactive:
-yfor the recommended set, or per module (--no-notifications --firestore). Description and org name are prompted for when not passed.Always generated:
core(logger, DI bootstrap, route observers, error types, BLoC observer),di,design_system(Material 3 theme, components, toasts, loaders),localization(type-safe strings via i69n),utils, and the app shell — bootstrap, router, splash, with no business logic.Optional:
network,auth,firestore,notifications,analytics,crashlytics,feature_flags,developer,dashboard.How the optional modules work
A directory named
{{<module>_dir}}renders to an empty path segment when its module is off, and mason skips every file underneath it. Section tags can't be used for this —{{/x}}contains a path separator — sopre_genderives the variables instead. It also resolves the two dependencies between modules (notifications → network, developer tools → feature flags) and reports them rather than letting them fail at compile time; the CLI applies the same rules somason makeon its own still produces something that builds.flutter createmoved intopre_genso the brick'slib/andpubspec.yamloverlay it.post_genformats the result — mustache line breaks depend on the answers, and a fresh project has to pass its ownmelos lint.Two things worth a reviewer's attention
Process failures were silent.
cli_core'strackOperationreports success as soon as the future completes, without checking the exit code — a failedflutter createormelos bootstrapshowed a green tick and generation carried on to produce a half-built workspace. Fixed locally in the brick's hooks. The right home iscli_coreitself, since all four bricks have this, but hooks resolve it from pub so that fix can't land until it's published.The brick is now
2.0.0. The CLI tries BrickHub first at^<version>; no 2.x is published there, so it falls through to the bundled copy. Publishing a 2.x brick to BrickHub later would silently take over from the bundle.Verification
Ten module combinations, each generated end to end:
melos bootstrap/melos lint/melos testflutter build webmelos lint/nonstop_clitestsNon-Firebase projects ship
apps/<name>/test/smoke_test.dart, which runs the realbootstrap.init()and pumps the app — it catches the class of bug that compiles fine and fails on device. Firebase projects can't boot beforeflutterfire configure, so the filename renders empty and mason drops it.That smoke test earned its place during review, which turned up several bugs that passed
analyzeandmelos lintcleanly: the auth screen crashed on an asset the template doesn't ship, Google/Apple buttons rendered as tofu becausefirebase_ui_authnever registers its own font, push notifications were never initialised when the dashboard was off, and a quote in--descriptionproduced an unparseable project.Note on provenance
The groundwork was adapted from an existing production app. Its domain had settled into shared code — survey and health analytics groups, a survey event on
core's global event bus, a hardcoded survey deep link in the notification client. All removed; verified by decoding all 222 files embedded in the generated bundle rather than grepping the source, since the bundle is what actually ships.🤖 Generated with Claude Code