Skip to content

feat(nitrogen_cli): migrate to unrouter 0.13#1

Open
medz wants to merge 1 commit into
Shreemanarjun:mainfrom
medz:feat/unrouter-013
Open

feat(nitrogen_cli): migrate to unrouter 0.13#1
medz wants to merge 1 commit into
Shreemanarjun:mainfrom
medz:feat/unrouter-013

Conversation

@medz

@medz medz commented Mar 24, 2026

Copy link
Copy Markdown

Summary by CodeRabbit

  • Chores
    • Updated navigation framework and internal dependencies to enhance CLI stability and maintainability.

@coderabbitai

coderabbitai Bot commented Mar 24, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

The routing infrastructure in the nitrogen CLI has been migrated from nocterm_unrouter's typed Unrouter<NitroRoute> pattern to a simpler global Unrouter instance with RouterView. Route transitions now use router.push() and router.replace() instead of context-based navigation. Dependencies have been updated accordingly, and a new test validates the migration behavior.

Changes

Cohort / File(s) Summary
Router Migration
packages/nitrogen_cli/bin/nitrogen.dart
Replaced typed route setup with global Unrouter instance and RouterView. Updated command view instantiation from route builders to Inlet views passing navigation callbacks. Changed route transitions to use router.push(command.path) and router.replace('/'). Multiple UI layout formatting adjustments applied to widget arguments.
Dependency Updates
packages/nitrogen_cli/pubspec.yaml
Removed nocterm_unrouter dependency, added unrouter at ^0.13.0, and bumped nocterm from ^0.5.1 to ^0.6.0.
Link Command Logic
packages/nitrogen_cli/lib/commands/link_command.dart
Changed _failed instance variable from mutable bool to immutable final bool. This makes the flag a compile-time constant, preventing runtime updates during _run() execution.
Test Updates
packages/nitrogen_cli/test/link_command_test.dart, packages/nitrogen_cli/test/router_migration_test.dart
Removed unused local variable in link_command_test.dart. Added new test file router_migration_test.dart validating router migration with route push/replace operations and terminal output assertions.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 A router's dance, from types to flows,
Global paths where navigation goes,
Push and replace, no context needed,
Nitrogen's migration, wisely heeded!
Tests confirm the journey's right,
Hopping forward into light! 🌟

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(nitrogen_cli): migrate to unrouter 0.13' directly and clearly describes the main change across the changeset: migrating the nitrogen_cli routing layer from nocterm_unrouter to unrouter 0.13, including dependency updates, navigation refactoring, and related test coverage.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request updates the nitrogen_cli to leverage the latest unrouter package (version 0.13). This migration involves a significant refactoring of the application's internal routing mechanism, moving from an older Unrouter<NitroRoute> setup to a more streamlined createRouter and Inlet based approach. The change aims to modernize the routing implementation, improve maintainability, and align with the updated API of the unrouter package.

Highlights

  • Unrouter Migration: Migrated the nitrogen_cli application's internal routing system to unrouter version 0.13, adopting its new API patterns.
  • Routing Refactoring: Refactored the TUI (Text User Interface) application's routing logic to utilize the new createRouter and Inlet definitions, simplifying route management and navigation.
  • Dependency Updates: Updated nocterm and unrouter package dependencies in pubspec.yaml and pubspec.lock, along with numerous transitive dependencies.
  • New Test Coverage: Introduced a new test file to ensure the unrouter migration functions correctly and maintains expected routing behavior.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request primarily refactors the application's routing mechanism by migrating from nocterm_unrouter to the unrouter package. This involved updating nitrogen.dart to use the new unrouter API, defining routes with Inlets, managing navigation via a global router instance, and removing the custom NitroRoute sealed class. The changes also include a major dependency update in pubspec.yaml and pubspec.lock to support the new routing library and other related packages. A new test file, router_migration_test.dart, was added to verify the core routing functionality after the migration. Additionally, minor stylistic code reformatting was applied across several files, and an unused variable was removed in link_command.dart.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/nitrogen_cli/bin/nitrogen.dart (1)

640-697: ⚠️ Potential issue | 🟠 Major

Leaving ProcessView does not stop the spawned process.

Process.start() is held only in a local variable, but ESC and ‹ Back immediately route home. If the user exits while _running is true, the child process keeps running off-screen and another command can be launched on top of it. Either block navigation until completion or keep a Process handle and terminate/await it on exit.

🛠️ Minimal containment fix
       onKeyEvent: (event) {
-        if (event.logicalKey == LogicalKey.escape ||
-            event.logicalKey == LogicalKey.arrowLeft) {
-          router.replace('/');
+        if (!_running &&
+            (event.logicalKey == LogicalKey.escape ||
+                event.logicalKey == LogicalKey.arrowLeft)) {
+          unawaited(router.replace('/'));
           return true;
         }
         return false;
       },
@@
                 HoverButton(
                   label: '‹ Back',
-                  onTap: () => router.replace('/'),
+                  onTap: () {
+                    if (!_running) {
+                      unawaited(router.replace('/'));
+                    }
+                  },
                   color: Colors.cyan,
                 ),

Also applies to: 705-708, 790-793

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/nitrogen_cli/bin/nitrogen.dart` around lines 640 - 697, The spawned
Process created in _start() is stored only in a local variable so leaving
ProcessView doesn't stop the child; store the Process in a state field (e.g. add
a _process: Process? on the State) and assign the result of Process.start(...)
to it, then implement cleanup in dispose() (or on navigation) to terminate/kill
the process and await its exitCode (use _process!.kill(...) and await
_process!.exitCode or call _process!.kill(ProcessSignal.sigterm) then fallback
to kill if needed), also cancel _pulseTimer and any stream subscriptions; ensure
_start() checks for an existing running _process before starting another.
packages/nitrogen_cli/pubspec.yaml (1)

17-24: ⚠️ Potential issue | 🔴 Critical

Raise the declared Dart SDK floor to match these dependency bumps.

nocterm 0.6.0 requires Dart 3.5 and unrouter 0.13.0 requires Dart 3.10, so sdk: ^3.4.0 advertises toolchains that cannot resolve this pubspec. Tighten the package SDK constraint to at least ^3.10.0 before landing this.

🛠️ Suggested fix
 environment:
-  sdk: ^3.4.0
+  sdk: ^3.10.0
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/nitrogen_cli/pubspec.yaml` around lines 17 - 24, The pubspec's SDK
lower bound (sdk: ^3.4.0) is too low for the bumped dependencies; update the
environment sdk constraint in pubspec.yaml to at least ^3.10.0 so it satisfies
nocterm 0.6.0 (requires 3.5) and unrouter 0.13.0 (requires 3.10); edit the
"environment: sdk" entry to "^3.10.0" to tighten the package SDK constraint
before landing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/nitrogen_cli/bin/nitrogen.dart`:
- Around line 148-163: The route currently calls _getProjectInfo() then always
returns a ProcessView that will run build_runner even when info is null; change
the Inlet view builder to check the result of _getProjectInfo() and, if it
returns null, return an explicit “not in a Nitro project” error view (e.g., a
NotInNitroProjectView or simple ErrorView with a clear message) instead of
returning ProcessView; only construct the ProcessView (with workingDirectory:
info.directory.path and args for flutter pub run build_runner) when info is
non-null so build_runner is never started outside a Nitro project.

In `@packages/nitrogen_cli/lib/commands/link_command.dart`:
- Line 210: The _failed flag is incorrectly declared final and never updated;
change it to a mutable bool or compute it from actual step states (e.g., derive
from LinkStepState.failed) so reporting works: update the declaration of _failed
in link_command.dart to be writable (or replace with a computed value like
steps.any((s) => s.failed)), ensure places that set component.result.success =
!_failed and call shutdownApp(_failed ? 1 : 0) use that real value, and if the
code updates individual LinkStepState instances, set _failed = true when any
LinkStepState.failed becomes true (or recompute before UI branch checks such as
the if (!_failed) that controls the failure UI).

In `@packages/nitrogen_cli/test/router_migration_test.dart`:
- Around line 8-25: The test currently conditionally resets the singleton
app.router and doesn't verify that replace('/') actually returns to the
dashboard; modify the test so the final app.router.replace('/') runs
unconditionally (e.g., in a finally block or after a try/catch) and add an
assertion after that replace to confirm the dashboard is rendered (check
tester.terminalState contains 'Nitrogen CLI'); locate calls to
app.router.replace('/') and app.router.push('/init') and ensure RouterView is
pumped again before the final assertion.

---

Outside diff comments:
In `@packages/nitrogen_cli/bin/nitrogen.dart`:
- Around line 640-697: The spawned Process created in _start() is stored only in
a local variable so leaving ProcessView doesn't stop the child; store the
Process in a state field (e.g. add a _process: Process? on the State) and assign
the result of Process.start(...) to it, then implement cleanup in dispose() (or
on navigation) to terminate/kill the process and await its exitCode (use
_process!.kill(...) and await _process!.exitCode or call
_process!.kill(ProcessSignal.sigterm) then fallback to kill if needed), also
cancel _pulseTimer and any stream subscriptions; ensure _start() checks for an
existing running _process before starting another.

In `@packages/nitrogen_cli/pubspec.yaml`:
- Around line 17-24: The pubspec's SDK lower bound (sdk: ^3.4.0) is too low for
the bumped dependencies; update the environment sdk constraint in pubspec.yaml
to at least ^3.10.0 so it satisfies nocterm 0.6.0 (requires 3.5) and unrouter
0.13.0 (requires 3.10); edit the "environment: sdk" entry to "^3.10.0" to
tighten the package SDK constraint before landing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d26c30bf-baf5-48d3-811f-3999e6c81701

📥 Commits

Reviewing files that changed from the base of the PR and between 2b8c3d0 and 2ca326c.

⛔ Files ignored due to path filters (1)
  • packages/nitrogen_cli/pubspec.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • packages/nitrogen_cli/bin/nitrogen.dart
  • packages/nitrogen_cli/lib/commands/link_command.dart
  • packages/nitrogen_cli/pubspec.yaml
  • packages/nitrogen_cli/test/link_command_test.dart
  • packages/nitrogen_cli/test/router_migration_test.dart
💤 Files with no reviewable changes (1)
  • packages/nitrogen_cli/test/link_command_test.dart

Comment thread packages/nitrogen_cli/bin/nitrogen.dart
Comment thread packages/nitrogen_cli/lib/commands/link_command.dart
Comment thread packages/nitrogen_cli/test/router_migration_test.dart
Shreemanarjun added a commit that referenced this pull request Jun 21, 2026
ROOT CAUSE #1 — Record params: void* passed as jobject (CRASH)
  C bridge passed Dart's `void*` (Pointer<Uint8>) directly to JNI
  CallStaticObjectMethod as the jbyteArray argument. JVM interpreted
  the raw heap address as a Java object reference → invalid object →
  SIGSEGV/abort. This crashed the process mid-test, causing ALL tests
  from §6 (@HybridRecord) onwards to fail.

  Fix in jni_method_emitter.dart:
    // Read Dart's [4B len][payload] format:
    int32_t value_len = *((const int32_t*)value);
    jbyteArray j_value = env->NewByteArray(value_len);
    env->SetByteArrayRegion(j_value, 0, value_len,
        (const jbyte*)((const uint8_t*)value + 4));  // skip 4-byte prefix
    CallStaticObjectMethod(..., j_value);

ROOT CAUSE #2 — JVM descriptor mismatches (silent wrong values)
  GetStaticMethodID returns null when JVM type doesn't match.
  C bridge returns default (0/false/null) — no exception, no crash.

  Fixes in kotlin_generator.dart:
  a) Enum params in _call: TcStatus → Long  (J, not LTcStatus;)
     callParamsResolved decodes: Long → TcStatus.fromNative(value)
  b) Nullable enum params in _call: TcStatus? → Long  (J not LTcStatus;)
     sentinel: val xArg = if (x < 0L) null else EnumType.fromNative(x)
  c) Nullable bool? in _call: Boolean → Int  (I not Z)
     sentinel: val xArg = if (x < 0) null else (x != 0)
     (Int supports -1; jboolean only 0/1 → null now detectable on Android!)
  d) Nullable property getters: non-nullable primitives match C (J/D/Z)
     double? → Double + (?: Double.NaN); int? → Long + (?: -1L)
  e) Nullable property setters: same non-nullable primitive types
     decodes: NaN→null, -1L→null

2928 generator tests passing. dart analyze — no issues.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant