Skip to content

Maintenance: complete workflow, config, and profile-list improvements - #131

Merged
hawkff merged 21 commits into
mainfrom
maint/remaining-improvements
Jul 11, 2026
Merged

hawkff merged 21 commits into
mainfrom
maint/remaining-improvements

Conversation

@hawkff

@hawkff hawkff commented Jul 11, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Wire the olcRTC sidecar through build, preview, release, artifact checks, and sibling-source pin guards.
  • Add ConfigBuilder characterization tests, cache resolveChain lookups, remove redundant JSON round trips, and centralize mandatory protocol dispatch.
  • Add subscription parser fixtures, value-based profile deduplication, and pure stop/reload gate coverage.
  • Run Room migration tests on a KVM-backed Namespace runner and fail CI on migration or emulator errors when KVM is available.
  • Fix profile-list listener teardown, batch drag and bulk-delete writes, and add generation-guarded DiffUtil updates with debounced in-memory filtering.

Verification

  • Namespace CI 29148971263: repository guards, native jobs, unit tests, Android lint, Spotless, Room migration emulator test, and OSS APK assembly passed.
  • Preview 29132946215: sidecar build, artifact verification, and APK assembly passed.
  • CodeRabbit CLI follow-up reported zero findings.

Notes

  • Device UI smoke testing was not run because no device was available.

@coderabbitai

coderabbitai Bot commented Jul 11, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The pull request adds OpenLRTC sidecar integration, emulator-based migration testing, service stop/reload coordination, registry-driven protocol metadata, cached configuration assembly, batch profile deletion, lifecycle-safe profile updates, parser coverage, and build validation changes.

Changes

Build and CI integration

Layer / File(s) Summary
OpenLRTC sidecar workflows
.github/workflows/*.yml
Build, preview, and release workflows pin, cache, build, and verify the OpenLRTC sidecar.
CI and native verification
.github/workflows/ci.yml, libcore/build.sh, scripts/verify.sh, buildScript/lib/assets.sh, app/build.gradle.kts
CI adds conditional Room migration instrumentation tests; native builds validate sibling pins, asset defaults change, and local verification runs the full Spotless check.

Service lifecycle coordination

Layer / File(s) Summary
Stop and reload coordination
app/src/main/java/io/nekohasekai/sagernet/bg/BaseService.kt, ServiceStopGate.kt
Service stop generations, teardown requests, and restart decisions are centralized in ServiceStopGate.
Race regression tests
app/src/test/java/io/nekohasekai/sagernet/bg/ServiceStopGateTest.kt
Tests cover stale reloads, repeated stops, restart retention, and explicit-stop cancellation.

Protocol and configuration model

Layer / File(s) Summary
Protocol registry dispatch
app/src/main/java/io/nekohasekai/sagernet/database/ProtocolRegistry.kt, ProxyEntity.kt
Protocol descriptors now define settings activities and standard-link behavior used by ProxyEntity.
Profile storage and identity
app/src/main/java/io/nekohasekai/sagernet/database/ProfileManager.kt, ProxyEntity.kt, Protocols.kt
Profile IDs can be deleted in transactional batches, and deduplication compares underlying bean values.
Cached configuration assembly
app/src/main/java/io/nekohasekai/sagernet/fmt/ConfigBuilder.kt, SingBoxOptions.java, Util.kt
Configuration builds use lookup caching and JSON-tree merging for TLS inspection and final assembly.
Protocol and configuration validation
app/src/test/java/io/nekohasekai/sagernet/{database,fmt}/*, app/src/test/java/moe/matsuri/nb4a/DeduplicationTest.kt
Tests validate registry metadata, generated configurations, batch deletion, overlays, DNS rules, export behavior, and deduplication.

Profile UI lifecycle and incremental updates

Layer / File(s) Summary
Pager lifecycle and deletion integration
app/src/main/java/io/nekohasekai/sagernet/ui/ConfigurationFragment.kt
Fragment and pager cleanup is centralized, post-teardown callbacks are guarded, and deletion uses background batch operations.
Incremental profile adapter
app/src/main/java/io/nekohasekai/sagernet/ui/ConfigurationGroupFragment.kt
Profile lists use cached state, asynchronous DiffUtil submissions, debounced filtering, targeted traffic binding, and coordinated removal handling.
Diff behavior validation
app/src/main/java/io/nekohasekai/sagernet/ui/ProfileDiffCallback.kt, app/src/test/java/io/nekohasekai/sagernet/ui/ProfileDiffCallbackTest.kt
Diff callbacks compare profile IDs and stamps, with tests for list changes and reordering.

Parsing and runtime support

Layer / File(s) Summary
Subscription parser coverage
app/src/test/java/io/nekohasekai/sagernet/group/RawUpdaterParseTest.kt, app/src/test/resources/subscriptions/*
Parser tests cover Clash, URI lists, Singbox, WireGuard, empty input, malformed nodes, unknown tags, and YAML alias limits.
Runtime support changes
app/src/main/java/io/nekohasekai/sagernet/ktx/Logs.kt, Utils.kt, PluginManager.kt, Executable.kt
Logging is redirected through a replaceable sink, reflection lookups become lazy, bundled Hysteria resolution is removed, and libtuic.so leaves the kill whitelist.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Poem

A rabbit hops through builds so bright,
OpenLRTC joins the flight.
Profiles diff and services gate,
Links and configs coordinate.
Tests bloom softly, row by row—
“Hop approved!” says Bunny-O. 🐇

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.42% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title is broadly aligned with the PR’s workflow, config, and profile-list changes.
Description check ✅ Passed The description clearly matches the changeset, covering sidecars, tests, CI, and profile-list updates.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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

@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: 2

🧹 Nitpick comments (6)
app/src/main/java/io/nekohasekai/sagernet/ktx/Logs.kt (1)

9-9: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Consider marking sink @Volatile for cross-thread visibility.

sink is a plain mutable var read from d/i/w/e calls that may execute on background/plugin threads, while it's expected to be swapped out (e.g. in tests) from another thread. Without @Volatile, a writer thread's update to sink isn't guaranteed to be visible to reader threads on all JVM/ART memory models.

♻️ Proposed fix
-    internal var sink: (String) -> Unit = { Libcore.nekoLogPrintln(it) }
+    `@Volatile`
+    internal var sink: (String) -> Unit = { Libcore.nekoLogPrintln(it) }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/io/nekohasekai/sagernet/ktx/Logs.kt` at line 9, Mark the
mutable `sink` property in `Logs.kt` with `@Volatile` so updates made by one
thread are immediately visible to background and plugin threads that read it.
.github/workflows/ci.yml (1)

55-97: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Consider bounding the instrumented job's runtime.

Neither the job nor the Run migration test on emulator step sets timeout-minutes. Android emulator runs are known to occasionally hang (boot loops, hung adb/QEMU threads) well past normal completion, and a stuck job here would only fall back to GitHub's default 360-minute job timeout, burning runner minutes on a Namespace runner.

⏱️ Suggested fix
   instrumented:
     name: Instrumented (Room migrations)
     runs-on: namespace-profile-nekoyay
+    timeout-minutes: 20
     needs:
       - libcore
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 55 - 97, The instrumented workflow can
run indefinitely when the Android emulator or migration test hangs. Add an
appropriate timeout-minutes value to the instrumented job, and optionally a
shorter timeout to the Run migration test on emulator step, ensuring the overall
runtime is bounded.
libcore/build.sh (1)

51-62: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Declare function-local variables with local.

dir, expected, label, and actual leak into the script's global scope since they aren't declared local inside check_sibling_pin. Low risk in this short-lived script, but worth tightening for hygiene/future edits.

🧹 Suggested fix
 check_sibling_pin() {
-  dir="$1"
-  expected="$2"
-  label="$3"
+  local dir="$1"
+  local expected="$2"
+  local label="$3"
   [ -z "$dir" ] && return
-  actual="$(git -C "$dir" rev-parse HEAD 2>/dev/null || echo unknown)"
+  local actual="$(git -C "$dir" rev-parse HEAD 2>/dev/null || echo unknown)"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libcore/build.sh` around lines 51 - 62, Declare dir, expected, label, and
actual as function-local variables using local at the start of
check_sibling_pin, while preserving the existing argument assignments and
validation behavior.
app/src/main/java/moe/matsuri/nb4a/Protocols.kt (2)

20-35: 🚀 Performance & Scalability | 🔵 Trivial

Full-bean equality is more expensive than the prior endpoint hash.

Per the inline comment, non-ConfigBean equality now serializes the whole bean (minus name) on every hashCode()/equals() call, versus the old lightweight address+port+type hash. This is a deliberate tradeoff for stricter dedup (confirmed by DeduplicationTest), but for large subscription imports this adds serialization cost per comparison.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/moe/matsuri/nb4a/Protocols.kt` around lines 20 - 35, The
Deduplication equality implementation now performs expensive full-bean
serialization for non-ConfigBean values. Preserve the stricter equality required
by DeduplicationTest while avoiding repeated serialization during large imports,
for example by caching a stable comparison key or hash in Deduplication and
reusing it from hashCode() and equals(); update relevant tests as needed.

15-36: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

type is unused in Deduplication
Deduplication.type is no longer read by hashCode() or equals(). If nothing else consumes it, consider dropping it or documenting why it is retained.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/moe/matsuri/nb4a/Protocols.kt` around lines 15 - 36, Remove
the unused type property from Deduplication and update all construction sites
and references accordingly; if it is required for an external contract, document
its purpose and include it consistently in equality and hashing.
app/src/main/java/io/nekohasekai/sagernet/fmt/ConfigBuilder.kt (1)

702-708: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add golden-test coverage for the TLS-fragment detour path.

The DataStore.enableTLSFragment detection was reworked from map-lookup to JSON-tree traversal (SingBoxOptions.toJsonTree(...).get("tls")...), but ConfigBuilderGoldenTest.kt has no test exercising this branch (e.g. a TLS-enabled outbound with fragment enabled asserting detour == TAG_FRAGMENT). Given this is exactly the kind of logic most likely to silently regress on a map-to-JsonElement rewrite, a small golden test would catch future breakage cheaply.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/io/nekohasekai/sagernet/fmt/ConfigBuilder.kt` around lines
702 - 708, Add a golden test in ConfigBuilderGoldenTest covering a TLS-enabled
outbound when TLS fragmentation is enabled, asserting that the generated
outbound detour equals TAG_FRAGMENT. Configure the test through the existing
DataStore.enableTLSFragment and outbound setup patterns, and use the current
golden-test assertion conventions to exercise the JSON-tree TLS detection
branch.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/src/main/java/io/nekohasekai/sagernet/database/ProxyEntity.kt`:
- Around line 213-216: Update ProxyEntity.toStdLink to honor the compact
parameter by passing it through the standard-link conversion or applying the
appropriate compact-link transformation before returning. Ensure both registered
protocol converters and the bean.toUniversalLink fallback produce compact output
when compact is true.

In `@app/src/main/java/io/nekohasekai/sagernet/ui/ConfigurationGroupFragment.kt`:
- Around line 782-815: In onUpdated, avoid calling undoManager?.flush() for
every profile update because normal selection changes can dismiss pending undo
actions. Only flush when the updated profile conflicts with an active pending
removal, using the undo manager’s pending-removal state and the updated profile
identity; leave unrelated updates without flushing. Keep onAdd behavior
unchanged unless the same conflict handling is required there.

---

Nitpick comments:
In @.github/workflows/ci.yml:
- Around line 55-97: The instrumented workflow can run indefinitely when the
Android emulator or migration test hangs. Add an appropriate timeout-minutes
value to the instrumented job, and optionally a shorter timeout to the Run
migration test on emulator step, ensuring the overall runtime is bounded.

In `@app/src/main/java/io/nekohasekai/sagernet/fmt/ConfigBuilder.kt`:
- Around line 702-708: Add a golden test in ConfigBuilderGoldenTest covering a
TLS-enabled outbound when TLS fragmentation is enabled, asserting that the
generated outbound detour equals TAG_FRAGMENT. Configure the test through the
existing DataStore.enableTLSFragment and outbound setup patterns, and use the
current golden-test assertion conventions to exercise the JSON-tree TLS
detection branch.

In `@app/src/main/java/io/nekohasekai/sagernet/ktx/Logs.kt`:
- Line 9: Mark the mutable `sink` property in `Logs.kt` with `@Volatile` so
updates made by one thread are immediately visible to background and plugin
threads that read it.

In `@app/src/main/java/moe/matsuri/nb4a/Protocols.kt`:
- Around line 20-35: The Deduplication equality implementation now performs
expensive full-bean serialization for non-ConfigBean values. Preserve the
stricter equality required by DeduplicationTest while avoiding repeated
serialization during large imports, for example by caching a stable comparison
key or hash in Deduplication and reusing it from hashCode() and equals(); update
relevant tests as needed.
- Around line 15-36: Remove the unused type property from Deduplication and
update all construction sites and references accordingly; if it is required for
an external contract, document its purpose and include it consistently in
equality and hashing.

In `@libcore/build.sh`:
- Around line 51-62: Declare dir, expected, label, and actual as function-local
variables using local at the start of check_sibling_pin, while preserving the
existing argument assignments and validation behavior.
🪄 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: 1bcdead9-7257-4009-8238-0b5cf8818f26

📥 Commits

Reviewing files that changed from the base of the PR and between 64210e2 and 26d6a16.

📒 Files selected for processing (36)
  • .github/workflows/build.yml
  • .github/workflows/ci.yml
  • .github/workflows/preview.yml
  • .github/workflows/release.yml
  • app/build.gradle.kts
  • app/src/main/java/io/nekohasekai/sagernet/bg/BaseService.kt
  • app/src/main/java/io/nekohasekai/sagernet/bg/Executable.kt
  • app/src/main/java/io/nekohasekai/sagernet/bg/ServiceStopGate.kt
  • app/src/main/java/io/nekohasekai/sagernet/database/ProfileManager.kt
  • app/src/main/java/io/nekohasekai/sagernet/database/ProtocolRegistry.kt
  • app/src/main/java/io/nekohasekai/sagernet/database/ProxyEntity.kt
  • app/src/main/java/io/nekohasekai/sagernet/fmt/ConfigBuilder.kt
  • app/src/main/java/io/nekohasekai/sagernet/ktx/Logs.kt
  • app/src/main/java/io/nekohasekai/sagernet/ktx/Utils.kt
  • app/src/main/java/io/nekohasekai/sagernet/plugin/PluginManager.kt
  • app/src/main/java/io/nekohasekai/sagernet/ui/ConfigurationFragment.kt
  • app/src/main/java/io/nekohasekai/sagernet/ui/ConfigurationGroupFragment.kt
  • app/src/main/java/io/nekohasekai/sagernet/ui/ProfileDiffCallback.kt
  • app/src/main/java/moe/matsuri/nb4a/Protocols.kt
  • app/src/main/java/moe/matsuri/nb4a/SingBoxOptions.java
  • app/src/main/java/moe/matsuri/nb4a/utils/Util.kt
  • app/src/test/java/io/nekohasekai/sagernet/bg/ServiceStopGateTest.kt
  • app/src/test/java/io/nekohasekai/sagernet/database/ProfileManagerBatchDeleteTest.kt
  • app/src/test/java/io/nekohasekai/sagernet/fmt/ConfigBuilderGoldenTest.kt
  • app/src/test/java/io/nekohasekai/sagernet/fmt/ConfigBuilderTestEnv.kt
  • app/src/test/java/io/nekohasekai/sagernet/fmt/ProtocolRegistryDispatchTest.kt
  • app/src/test/java/io/nekohasekai/sagernet/group/RawUpdaterParseTest.kt
  • app/src/test/java/io/nekohasekai/sagernet/ui/ProfileDiffCallbackTest.kt
  • app/src/test/java/moe/matsuri/nb4a/DeduplicationTest.kt
  • app/src/test/resources/subscriptions/clash-basic.yaml
  • app/src/test/resources/subscriptions/clash-malformed.yaml
  • app/src/test/resources/subscriptions/clash-unknown-tag.yaml
  • app/src/test/resources/subscriptions/singbox-outbounds.json
  • app/src/test/resources/subscriptions/wireguard.conf
  • libcore/build.sh
  • scripts/verify.sh
💤 Files with no reviewable changes (1)
  • app/src/main/java/io/nekohasekai/sagernet/bg/Executable.kt

Comment thread app/src/main/java/io/nekohasekai/sagernet/database/ProxyEntity.kt

@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: 1

Caution

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

⚠️ Outside diff range comments (1)
app/src/main/java/io/nekohasekai/sagernet/ui/ConfigurationFragment.kt (1)

389-406: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Refresh the group pager after batch delete

ProfileManager.deleteProfiles() skips listener callbacks, so this path can leave the empty ungrouped tab visible after deleting its last proxy. Emit a group-level reload here, or route the empty-group cleanup through the same listener path as single deletions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/io/nekohasekai/sagernet/ui/ConfigurationFragment.kt` around
lines 389 - 406, The successful batch-delete path in deleteProfilesFromGroup
must trigger a group-level reload because ProfileManager.deleteProfiles does not
emit listener callbacks. After ProfileManager.deleteProfiles(profiles)
completes, call GroupManager.postReload(groupId) before updating the adapter,
while preserving the existing failure handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/src/main/java/moe/matsuri/nb4a/Protocols.kt`:
- Line 20: Update the comparisonHash initialization in Protocols to handle
nullable ConfigBean.config safely, matching the null-safe behavior used by
equals: use the config hash when present and 0 when null, while preserving the
existing bean.hashCode() path for non-ConfigBean values.

---

Outside diff comments:
In `@app/src/main/java/io/nekohasekai/sagernet/ui/ConfigurationFragment.kt`:
- Around line 389-406: The successful batch-delete path in
deleteProfilesFromGroup must trigger a group-level reload because
ProfileManager.deleteProfiles does not emit listener callbacks. After
ProfileManager.deleteProfiles(profiles) completes, call
GroupManager.postReload(groupId) before updating the adapter, while preserving
the existing failure handling.
🪄 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: 442e85f0-8e62-4fc5-9f58-8cf967dde4d3

📥 Commits

Reviewing files that changed from the base of the PR and between 26d6a16 and 749ca54.

📒 Files selected for processing (9)
  • .github/workflows/ci.yml
  • app/src/main/java/io/nekohasekai/sagernet/group/RawUpdater.kt
  • app/src/main/java/io/nekohasekai/sagernet/ktx/Logs.kt
  • app/src/main/java/io/nekohasekai/sagernet/ui/ConfigurationFragment.kt
  • app/src/main/java/io/nekohasekai/sagernet/ui/ConfigurationGroupFragment.kt
  • app/src/main/java/moe/matsuri/nb4a/Protocols.kt
  • app/src/test/java/io/nekohasekai/sagernet/fmt/ConfigBuilderGoldenTest.kt
  • app/src/test/java/moe/matsuri/nb4a/DeduplicationTest.kt
  • libcore/build.sh
🚧 Files skipped from review as they are similar to previous changes (6)
  • .github/workflows/ci.yml
  • app/src/main/java/io/nekohasekai/sagernet/ktx/Logs.kt
  • app/src/test/java/moe/matsuri/nb4a/DeduplicationTest.kt
  • libcore/build.sh
  • app/src/test/java/io/nekohasekai/sagernet/fmt/ConfigBuilderGoldenTest.kt
  • app/src/main/java/io/nekohasekai/sagernet/ui/ConfigurationGroupFragment.kt

Comment thread app/src/main/java/moe/matsuri/nb4a/Protocols.kt Outdated
@hawkff
hawkff merged commit c63ee08 into main Jul 11, 2026
9 checks passed
@hawkff
hawkff deleted the maint/remaining-improvements branch July 11, 2026 10:18
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