Repository navigation
feat: turn android host shell into real swarm node host - #3
Conversation
|
Caution Review failedPull request was closed or merged during review No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughThe PR replaces optional/reflection-based AAR integration with a pinned AAR contract: CI/release workflows now require, download (with auth), verify SHA256 and metadata for Changes
Sequence DiagramssequenceDiagram
participant User
participant MainActivity
participant SharedPrefs
participant SwarmRuntime
participant NativeBridge
participant SwarmNode
participant PeerNetwork
User->>MainActivity: Enter bootstrap & node name
MainActivity->>SharedPrefs: Save inputs
User->>MainActivity: Click start
MainActivity->>SwarmRuntime: start(context, config)
SwarmRuntime->>NativeBridge: Reflectively instantiate config
SwarmRuntime->>NativeBridge: Set bootstrap, node name, autoReconnect
SwarmRuntime->>NativeBridge: Call startNode()
NativeBridge->>SwarmNode: Initialize node runtime
SwarmNode->>PeerNetwork: Connect and register
SwarmRuntime-->>MainActivity: Return control
User->>MainActivity: Click refresh
MainActivity->>SwarmRuntime: probe()
SwarmRuntime->>NativeBridge: Call getState(), getPeerID(), getBootstrapAddr(), getLastError()
SwarmRuntime->>NativeBridge: Call getDiscoveredPeers()
NativeBridge->>SwarmNode: Query runtime state & peers
SwarmRuntime-->>MainActivity: Return SwarmStatus with peers list
MainActivity->>MainActivity: Parse discoveredPeers, render UI
sequenceDiagram
participant CI as GitHub Actions
participant Storage as Artifact Storage
participant Validator as Validation Logic
participant Gradle
participant APK
CI->>Storage: Download gomtm-swarm-android.aar
CI->>Storage: Download gomtm-swarm-android.json
CI->>Validator: Verify AAR SHA256 matches pinned
Validator-->>Validator: Parse JSON metadata
Validator-->>Validator: Assert artifact, version, sha256 match
alt Validation fails
Validator-->>CI: Fail job
else Validation passes
Validator-->>CI: Proceed
CI->>Gradle: Run build with AAR in app/libs
Gradle->>APK: Link AAR, create APK
APK-->>CI: APK + provenance JSON
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (3)
app/src/test/java/com/gomtm/android/swarm/SwarmRuntimeTest.kt (1)
41-44: Deduplicate the discovered-peers JSON fixture to reduce drift risk.The same payload appears in multiple places; extracting a shared test constant/helper would make updates safer.
Also applies to: 80-83
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/test/java/com/gomtm/android/swarm/SwarmRuntimeTest.kt` around lines 41 - 44, Extract the repeated discovered-peers JSON into a single reusable test fixture and replace the inline `json` literals in `SwarmRuntimeTest` (and the duplicate at lines ~80-83) with that constant; for example, add a `DISCOVERED_PEERS_JSON` constant in `SwarmRuntimeTest`'s companion object or a shared `TestFixtures` object and update the tests that currently define `val json = """..."""` to reference that constant so the payload is maintained in one place.app/src/main/java/com/gomtm/android/swarm/SwarmNodeConfig.kt (1)
12-12: Consider multi-bootstrap defaults for better cold-start resilience.Line 12 uses a single default bootstrap endpoint; a small fallback list (or DNS-backed endpoint) would reduce startup fragility if that peer is unavailable.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/com/gomtm/android/swarm/SwarmNodeConfig.kt` at line 12, SwarmNodeConfig currently exposes a single DEFAULT_BOOTSTRAP constant which is fragile; change it to a small ordered list/array (e.g., DEFAULT_BOOTSTRAPS or BOOTSTRAP_ENDPOINTS) containing multiple bootstrap multiaddrs (or a single DNS-backed multiaddr) and update any code that reads DEFAULT_BOOTSTRAP to iterate the list and try endpoints in order (or resolve the DNS endpoint) until one connects; modify references to DEFAULT_BOOTSTRAP in SwarmNodeConfig and any consumer code to use the new collection and implement a simple fallback/selection strategy (try each address, skip unreachable ones) so cold-starts are resilient.app/src/main/res/layout/activity_main.xml (1)
27-55: Associate the visible labels with theirEditTexts.These
TextViews are acting as persistent form labels. Withoutandroid:labelFor, screen readers have to rely on the temporary hints instead of the visible label once focus moves through the form.Suggested fix
- <TextView + <TextView + android:id="@+id/bootstrapInputLabel" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" + android:labelFor="@id/bootstrapInput" android:text="@string/bootstrap_input_label" android:textStyle="bold" /> @@ - <TextView + <TextView + android:id="@+id/nodeNameInputLabel" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="12dp" + android:labelFor="@id/nodeNameInput" android:text="@string/node_name_input_label" android:textStyle="bold" />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/res/layout/activity_main.xml` around lines 27 - 55, The two TextView labels are not associated with their EditText fields, so add android:labelFor attributes on the corresponding TextView elements to point to the EditText IDs (use android:labelFor="@id/bootstrapInput" for the bootstrap label and android:labelFor="@id/nodeNameInput" for the node name label) so screen readers announce the persistent label instead of relying only on hints; update the TextView attributes in the layout for the elements that precede the EditText with IDs bootstrapInput and nodeNameInput.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/release.yml:
- Around line 117-119: The workflow uploads the unsigned artifact
app-release-unsigned.apk; fix by adding a signing configuration in app module
build.gradle.kts (create a signingConfigs block and reference it from the
release buildType so release.buildType uses signingConfig release) and provide
keystore credentials via secure properties/ENV (so the Gradle release variant
produces a signed APK), then update the GitHub Actions workflow to publish the
signed output (e.g., the release APK path produced by the signed release variant
such as app/build/outputs/apk/release/app-release.apk) instead of
app-release-unsigned.apk. Ensure you reference the release buildType and
signingConfigs identifiers when making the changes.
In `@app/src/main/java/com/gomtm/android/MainActivity.kt`:
- Around line 23-50: The UI handlers and runAction currently call heavy/blocking
methods (swarmRuntime.start, swarmRuntime.stop, probe, drainLogs invoked via
start(), stop(), probe(), drainLogs()) on the main thread; move these calls into
a background coroutine/dispatcher (e.g., Dispatchers.IO or a dedicated
single-thread context) so reflection, file I/O and JNI round-trips run off the
UI thread, capture exceptions there, then post only the results/state back to
the main thread to update actionError and call render(); update runAction to
launch the background task and use withContext(Dispatchers.Main) or
runOnUiThread to set actionError and invoke render() so saveInputs(), render(),
and all UI updates remain on the main thread while start/stop/probe/drainLogs
execute off it.
In `@app/src/main/java/com/gomtm/android/swarm/DiscoveredPeer.kt`:
- Around line 26-35: The peer-parsing loop currently constructs DiscoveredPeer
objects even when peer_id is empty (optString returns ""), causing blank peers
to render; update the parsing in the block that reads from peers (the code
creating DiscoveredPeer instances) to first extract peerId =
item.optString("peer_id") and skip the entry if peerId is blank (continue)
before calling add(DiscoveredPeer(...)), ensuring DiscoveredPeer(peerId =
peerId, ...) is only created for non-empty peer_id values.
In `@app/src/main/java/com/gomtm/android/swarm/SwarmRuntime.kt`:
- Around line 66-70: invokeString currently only catches
ReflectiveOperationException so IllegalStateException thrown by invokeStatic
(when bridge getters like getState or getDiscoveredPeers throw) bubbles up and
crashes probe() and drainLogs(); update invokeString to also catch
IllegalStateException and return an empty string on that exception, and add a
regression test that stubs a bridge getter to throw IllegalStateException and
asserts probe() and drainLogs() return safe defaults (empty string or expected
defaults) to ensure graceful degradation; target the invokeString, invokeStatic,
probe, drainLogs, getState and getDiscoveredPeers symbols when making changes
and adding the test.
- Around line 60-63: The runtimeBaseDir function currently ignores File.mkdirs()
failures; update runtimeBaseDir(context: Context) to check the mkdirs() return
value and also verify that the resulting File is a directory (File.isDirectory),
and if either check fails throw an informative exception (e.g., IOException or
IllegalStateException) so startNode() never receives a non-existent path; also
add a unit/instrumentation regression test that simulates filesDir/gomtm being
non-creatable (or an existing file) and asserts that runtimeBaseDir throws the
expected exception instead of allowing later bridge startup errors.
In `@app/src/main/java/com/gomtm/android/swarm/SwarmUiModel.kt`:
- Around line 8-16: The UI model currently uses hardcoded user-facing strings
VALUE_NOT_AVAILABLE and VALUE_NONE inside SwarmUiModel.from; remove those
English-only constants and change the API so the caller provides localized
fallback strings instead (e.g., add parameters to SwarmUiModel.from such as
notAvailableFallback and noneFallback or accept a single resource-derived pair)
and use those values when computing connectedBootstrap and errorText; update any
call sites that construct SwarmUiModel.from to pass getString(...) resource
values so localization is preserved.
---
Nitpick comments:
In `@app/src/main/java/com/gomtm/android/swarm/SwarmNodeConfig.kt`:
- Line 12: SwarmNodeConfig currently exposes a single DEFAULT_BOOTSTRAP constant
which is fragile; change it to a small ordered list/array (e.g.,
DEFAULT_BOOTSTRAPS or BOOTSTRAP_ENDPOINTS) containing multiple bootstrap
multiaddrs (or a single DNS-backed multiaddr) and update any code that reads
DEFAULT_BOOTSTRAP to iterate the list and try endpoints in order (or resolve the
DNS endpoint) until one connects; modify references to DEFAULT_BOOTSTRAP in
SwarmNodeConfig and any consumer code to use the new collection and implement a
simple fallback/selection strategy (try each address, skip unreachable ones) so
cold-starts are resilient.
In `@app/src/main/res/layout/activity_main.xml`:
- Around line 27-55: The two TextView labels are not associated with their
EditText fields, so add android:labelFor attributes on the corresponding
TextView elements to point to the EditText IDs (use
android:labelFor="@id/bootstrapInput" for the bootstrap label and
android:labelFor="@id/nodeNameInput" for the node name label) so screen readers
announce the persistent label instead of relying only on hints; update the
TextView attributes in the layout for the elements that precede the EditText
with IDs bootstrapInput and nodeNameInput.
In `@app/src/test/java/com/gomtm/android/swarm/SwarmRuntimeTest.kt`:
- Around line 41-44: Extract the repeated discovered-peers JSON into a single
reusable test fixture and replace the inline `json` literals in
`SwarmRuntimeTest` (and the duplicate at lines ~80-83) with that constant; for
example, add a `DISCOVERED_PEERS_JSON` constant in `SwarmRuntimeTest`'s
companion object or a shared `TestFixtures` object and update the tests that
currently define `val json = """..."""` to reference that constant so the
payload is maintained in one place.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: 18b4bccd-8bef-4e15-a0b3-40b9540ad385
📒 Files selected for processing (18)
.github/workflows/ci.yml.github/workflows/release.ymlREADME.mdapp/build.gradle.ktsapp/libs/README.mdapp/src/main/AndroidManifest.xmlapp/src/main/java/com/gomtm/android/MainActivity.ktapp/src/main/java/com/gomtm/android/swarm/DiscoveredPeer.ktapp/src/main/java/com/gomtm/android/swarm/SwarmNodeConfig.ktapp/src/main/java/com/gomtm/android/swarm/SwarmRuntime.ktapp/src/main/java/com/gomtm/android/swarm/SwarmStatus.ktapp/src/main/java/com/gomtm/android/swarm/SwarmUiModel.ktapp/src/main/res/layout/activity_main.xmlapp/src/main/res/values/strings.xmlapp/src/test/java/com/gomtm/android/swarm/SwarmRuntimeTest.ktapp/src/test/java/com/gomtm/android/swarm/SwarmUiModelTest.ktdocs/roadmap.mddocs/swarm-runtime-integration.md
| files: | | ||
| app/build/outputs/apk/release/app-release-unsigned.apk | ||
| dist/gomtm-android-provenance.json |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n -C3 'assembleRelease|app-release-unsigned\.apk' .github/workflows/release.yml
build_file="$(fd 'build.gradle.kts$' app | head -n1)"
[ -n "$build_file" ]
rg -n -C5 'signingConfigs|buildTypes|release' "$build_file"Repository: codeh007/gomtm-android
Length of output: 960
🏁 Script executed:
fd 'build.gradle.kts$' app | head -n1 | xargs cat -nRepository: codeh007/gomtm-android
Length of output: 1847
Publish a signed APK here, not app-release-unsigned.apk.
app-release-unsigned.apk is the raw unsigned output from assembleRelease. The app/build.gradle.kts release buildType has no signingConfig defined, so the GitHub release currently uploads an artifact users cannot install with the Android package manager. Add a signing configuration to the release buildType and update the workflow to reference the signed output path.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/release.yml around lines 117 - 119, The workflow uploads
the unsigned artifact app-release-unsigned.apk; fix by adding a signing
configuration in app module build.gradle.kts (create a signingConfigs block and
reference it from the release buildType so release.buildType uses signingConfig
release) and provide keystore credentials via secure properties/ENV (so the
Gradle release variant produces a signed APK), then update the GitHub Actions
workflow to publish the signed output (e.g., the release APK path produced by
the signed release variant such as
app/build/outputs/apk/release/app-release.apk) instead of
app-release-unsigned.apk. Ensure you reference the release buildType and
signingConfigs identifiers when making the changes.
| findViewById<MaterialButton>(R.id.startButton).setOnClickListener { | ||
| saveInputs() | ||
| runAction { | ||
| swarmRuntime.start(this, currentConfig()) | ||
| } | ||
| } | ||
| findViewById<MaterialButton>(R.id.stopButton).setOnClickListener { | ||
| runAction { | ||
| swarmRuntime.stop() | ||
| } | ||
| } | ||
| findViewById<MaterialButton>(R.id.refreshButton).setOnClickListener { | ||
| saveInputs() | ||
| actionError = null | ||
| render() | ||
| } | ||
|
|
||
| render() | ||
| } | ||
|
|
||
| private fun runAction(action: () -> Unit) { | ||
| actionError = null | ||
| try { | ||
| action() | ||
| } catch (error: Exception) { | ||
| actionError = error.message ?: error.javaClass.simpleName | ||
| } | ||
| render() |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
file="$(fd 'SwarmRuntime\.kt$' app/src/main/java | head -n1)"
[ -n "$file" ]
rg -n -C6 'fun (start|stop|probe|drainLogs)\(' "$file"Repository: codeh007/gomtm-android
Length of output: 1750
🏁 Script executed:
wc -l app/src/main/java/com/gomtm/android/swarm/SwarmRuntime.ktRepository: codeh007/gomtm-android
Length of output: 127
🏁 Script executed:
cat -n app/src/main/java/com/gomtm/android/swarm/SwarmRuntime.ktRepository: codeh007/gomtm-android
Length of output: 4855
🏁 Script executed:
sed -n '63,75p' app/src/main/java/com/gomtm/android/MainActivity.ktRepository: codeh007/gomtm-android
Length of output: 800
Move probe() and drainLogs() off the main thread to prevent ANR.
Lines 23–50 and 63–75 call start(), stop(), probe(), and drainLogs() synchronously from UI event handlers. All of these methods perform reflection (Class.forName, Method.invoke) and blocking JNI calls:
start()doesClass.forName, constructor reflection, 2 property setter invokes, file I/O (mkdirs), and a nativestartNodecallstop()doesClass.forNameand a nativestopNodecallprobe()chains 5 sequential JNI calls:getState,getPeerID,getBootstrapAddr,getLastError,getDiscoveredPeersdrainLogs()does 1 JNI call
Each time render() is called (on every button click), it executes at least 6 JNI roundtrips. Move these operations to a background dispatcher and post results back to the main thread for UI updates only.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/src/main/java/com/gomtm/android/MainActivity.kt` around lines 23 - 50,
The UI handlers and runAction currently call heavy/blocking methods
(swarmRuntime.start, swarmRuntime.stop, probe, drainLogs invoked via start(),
stop(), probe(), drainLogs()) on the main thread; move these calls into a
background coroutine/dispatcher (e.g., Dispatchers.IO or a dedicated
single-thread context) so reflection, file I/O and JNI round-trips run off the
UI thread, capture exceptions there, then post only the results/state back to
the main thread to update actionError and call render(); update runAction to
launch the background task and use withContext(Dispatchers.Main) or
runOnUiThread to set actionError and invoke render() so saveInputs(), render(),
and all UI updates remain on the main thread while start/stop/probe/drainLogs
execute off it.
| val item = peers.optJSONObject(index) ?: continue | ||
| add( | ||
| DiscoveredPeer( | ||
| peerId = item.optString("peer_id"), | ||
| name = item.optString("name").ifBlank { null }, | ||
| state = item.optString("state", "unknown"), | ||
| discoveredInCurrentSession = item.optBoolean("discovered_in_current_session", false), | ||
| isBootstrap = item.optBoolean("is_bootstrap", false), | ||
| lastSeenAt = item.optString("last_seen_at"), | ||
| ), |
There was a problem hiding this comment.
Skip malformed peer entries that have no peer_id.
optString("peer_id") returns "" when the field is missing or not a string. In app/src/main/java/com/gomtm/android/MainActivity.kt, Lines 81-90 render that verbatim, so a bad snapshot shows a blank peer block instead of being ignored.
Suggested fix
for (index in 0 until peers.length()) {
val item = peers.optJSONObject(index) ?: continue
+ val peerId = item.optString("peer_id").trim()
+ if (peerId.isBlank()) {
+ continue
+ }
add(
DiscoveredPeer(
- peerId = item.optString("peer_id"),
+ peerId = peerId,
name = item.optString("name").ifBlank { null },
state = item.optString("state", "unknown"),
discoveredInCurrentSession = item.optBoolean("discovered_in_current_session", false),📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| val item = peers.optJSONObject(index) ?: continue | |
| add( | |
| DiscoveredPeer( | |
| peerId = item.optString("peer_id"), | |
| name = item.optString("name").ifBlank { null }, | |
| state = item.optString("state", "unknown"), | |
| discoveredInCurrentSession = item.optBoolean("discovered_in_current_session", false), | |
| isBootstrap = item.optBoolean("is_bootstrap", false), | |
| lastSeenAt = item.optString("last_seen_at"), | |
| ), | |
| val item = peers.optJSONObject(index) ?: continue | |
| val peerId = item.optString("peer_id").trim() | |
| if (peerId.isBlank()) { | |
| continue | |
| } | |
| add( | |
| DiscoveredPeer( | |
| peerId = peerId, | |
| name = item.optString("name").ifBlank { null }, | |
| state = item.optString("state", "unknown"), | |
| discoveredInCurrentSession = item.optBoolean("discovered_in_current_session", false), | |
| isBootstrap = item.optBoolean("is_bootstrap", false), | |
| lastSeenAt = item.optString("last_seen_at"), | |
| ), |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/src/main/java/com/gomtm/android/swarm/DiscoveredPeer.kt` around lines 26
- 35, The peer-parsing loop currently constructs DiscoveredPeer objects even
when peer_id is empty (optString returns ""), causing blank peers to render;
update the parsing in the block that reads from peers (the code creating
DiscoveredPeer instances) to first extract peerId = item.optString("peer_id")
and skip the entry if peerId is blank (continue) before calling
add(DiscoveredPeer(...)), ensuring DiscoveredPeer(peerId = peerId, ...) is only
created for non-empty peer_id values.
| private fun runtimeBaseDir(context: Context): String { | ||
| val baseDir = File(context.filesDir, "gomtm") | ||
| baseDir.mkdirs() | ||
| return baseDir.absolutePath |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -type f -name "SwarmRuntime.kt" | head -20Repository: codeh007/gomtm-android
Length of output: 125
🏁 Script executed:
cat -n ./app/src/main/java/com/gomtm/android/swarm/SwarmRuntime.ktRepository: codeh007/gomtm-android
Length of output: 4855
🏁 Script executed:
find . -type f -name "*SwarmRuntime*Test*" -o -type f -name "*Test*SwarmRuntime*" | head -20Repository: codeh007/gomtm-android
Length of output: 129
🏁 Script executed:
cat -n ./app/src/test/java/com/gomtm/android/swarm/SwarmRuntimeTest.ktRepository: codeh007/gomtm-android
Length of output: 3873
🏁 Script executed:
sed -n '74,87p' ./app/src/main/java/com/gomtm/android/swarm/SwarmRuntime.ktRepository: codeh007/gomtm-android
Length of output: 601
Fail fast if the runtime directory cannot be created.
Ignoring mkdirs() here can hand startNode() a path that does not exist, which hides the actual storage failure behind a later bridge startup error.
The suggested fix should check both whether directory creation succeeded and whether the path is actually a directory (in case a file exists at that location):
Suggested fix
private fun runtimeBaseDir(context: Context): String {
val baseDir = File(context.filesDir, "gomtm")
- baseDir.mkdirs()
+ if (!baseDir.exists() && !baseDir.mkdirs()) {
+ throw IllegalStateException("Unable to create runtime directory: ${baseDir.absolutePath}")
+ }
+ if (!baseDir.isDirectory) {
+ throw IllegalStateException("Runtime path is not a directory: ${baseDir.absolutePath}")
+ }
return baseDir.absolutePath
}Add a regression test where ${filesDir}/gomtm cannot be created (or is already a file) to keep the failure local to this helper rather than manifesting in the native bridge code.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| private fun runtimeBaseDir(context: Context): String { | |
| val baseDir = File(context.filesDir, "gomtm") | |
| baseDir.mkdirs() | |
| return baseDir.absolutePath | |
| private fun runtimeBaseDir(context: Context): String { | |
| val baseDir = File(context.filesDir, "gomtm") | |
| if (!baseDir.exists() && !baseDir.mkdirs()) { | |
| throw IllegalStateException("Unable to create runtime directory: ${baseDir.absolutePath}") | |
| } | |
| if (!baseDir.isDirectory) { | |
| throw IllegalStateException("Runtime path is not a directory: ${baseDir.absolutePath}") | |
| } | |
| return baseDir.absolutePath | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/src/main/java/com/gomtm/android/swarm/SwarmRuntime.kt` around lines 60 -
63, The runtimeBaseDir function currently ignores File.mkdirs() failures; update
runtimeBaseDir(context: Context) to check the mkdirs() return value and also
verify that the resulting File is a directory (File.isDirectory), and if either
check fails throw an informative exception (e.g., IOException or
IllegalStateException) so startNode() never receives a non-existent path; also
add a unit/instrumentation regression test that simulates filesDir/gomtm being
non-creatable (or an existing file) and asserts that runtimeBaseDir throws the
expected exception instead of allowing later bridge startup errors.
| private fun invokeString(methodName: String): String { | ||
| return try { | ||
| (invokeStatic(resolveBridgeClass(), methodName, emptyArray(), emptyArray()) as? String).orEmpty() | ||
| } catch (_: ReflectiveOperationException) { | ||
| "" |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's find and read the SwarmRuntime.kt file
fd -t f "SwarmRuntime.kt" --exec wc -l {} \;Repository: codeh007/gomtm-android
Length of output: 129
🏁 Script executed:
# Read the file to understand exception handling
cat -n app/src/main/java/com/gomtm/android/swarm/SwarmRuntime.ktRepository: codeh007/gomtm-android
Length of output: 4855
🏁 Script executed:
# Also check the DiscoveredPeer.kt file mentioned in the scratchpad for context
cat -n app/src/main/java/com/gomtm/android/swarm/DiscoveredPeer.ktRepository: codeh007/gomtm-android
Length of output: 2013
invokeString() does not shield polling from bridge method failures.
invokeStatic() wraps method execution failures in IllegalStateException, but invokeString() only catches ReflectiveOperationException. When a bridge getter like getState() or getDiscoveredPeers() throws, the IllegalStateException escapes uncaught, causing probe() and drainLogs() to crash instead of gracefully degrading.
Add a regression test where a bridge getter throws and confirm that probe() and drainLogs() continue to return safe defaults. Then update invokeString() to catch IllegalStateException:
Suggested fix
private fun invokeString(methodName: String): String {
return try {
(invokeStatic(resolveBridgeClass(), methodName, emptyArray(), emptyArray()) as? String).orEmpty()
} catch (_: ReflectiveOperationException) {
""
+ } catch (_: IllegalStateException) {
+ ""
}
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| private fun invokeString(methodName: String): String { | |
| return try { | |
| (invokeStatic(resolveBridgeClass(), methodName, emptyArray(), emptyArray()) as? String).orEmpty() | |
| } catch (_: ReflectiveOperationException) { | |
| "" | |
| private fun invokeString(methodName: String): String { | |
| return try { | |
| (invokeStatic(resolveBridgeClass(), methodName, emptyArray(), emptyArray()) as? String).orEmpty() | |
| } catch (_: ReflectiveOperationException) { | |
| "" | |
| } catch (_: IllegalStateException) { | |
| "" | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/src/main/java/com/gomtm/android/swarm/SwarmRuntime.kt` around lines 66 -
70, invokeString currently only catches ReflectiveOperationException so
IllegalStateException thrown by invokeStatic (when bridge getters like getState
or getDiscoveredPeers throw) bubbles up and crashes probe() and drainLogs();
update invokeString to also catch IllegalStateException and return an empty
string on that exception, and add a regression test that stubs a bridge getter
to throw IllegalStateException and asserts probe() and drainLogs() return safe
defaults (empty string or expected defaults) to ensure graceful degradation;
target the invokeString, invokeStatic, probe, drainLogs, getState and
getDiscoveredPeers symbols when making changes and adding the test.
| const val VALUE_NOT_AVAILABLE = "Not available" | ||
| const val VALUE_NONE = "None" | ||
|
|
||
| fun from(status: SwarmStatus, actionError: String?): SwarmUiModel { | ||
| return SwarmUiModel( | ||
| connectedBootstrap = status.bootstrapAddress.ifBlank { VALUE_NOT_AVAILABLE }, | ||
| errorText = actionError?.takeIf { it.isNotBlank() } | ||
| ?: status.lastError.ifBlank { VALUE_NONE }, | ||
| ) |
There was a problem hiding this comment.
Avoid hardcoded user-facing fallback strings in the UI model.
Lines 8-9 introduce English-only literals that bypass Android resource localization. Prefer caller-provided localized fallback strings.
♻️ Proposed refactor
data class SwarmUiModel(
val connectedBootstrap: String,
val errorText: String,
) {
companion object {
- const val VALUE_NOT_AVAILABLE = "Not available"
- const val VALUE_NONE = "None"
-
- fun from(status: SwarmStatus, actionError: String?): SwarmUiModel {
+ fun from(
+ status: SwarmStatus,
+ actionError: String?,
+ valueNotAvailable: String,
+ valueNone: String,
+ ): SwarmUiModel {
return SwarmUiModel(
- connectedBootstrap = status.bootstrapAddress.ifBlank { VALUE_NOT_AVAILABLE },
+ connectedBootstrap = status.bootstrapAddress.ifBlank { valueNotAvailable },
errorText = actionError?.takeIf { it.isNotBlank() }
- ?: status.lastError.ifBlank { VALUE_NONE },
+ ?: status.lastError.ifBlank { valueNone },
)
}
}
}Caller-side (outside this range) should pass getString(...) resource values when building the model.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const val VALUE_NOT_AVAILABLE = "Not available" | |
| const val VALUE_NONE = "None" | |
| fun from(status: SwarmStatus, actionError: String?): SwarmUiModel { | |
| return SwarmUiModel( | |
| connectedBootstrap = status.bootstrapAddress.ifBlank { VALUE_NOT_AVAILABLE }, | |
| errorText = actionError?.takeIf { it.isNotBlank() } | |
| ?: status.lastError.ifBlank { VALUE_NONE }, | |
| ) | |
| const val VALUE_NOT_AVAILABLE = "Not available" | |
| const val VALUE_NONE = "None" | |
| fun from( | |
| status: SwarmStatus, | |
| actionError: String?, | |
| valueNotAvailable: String, | |
| valueNone: String, | |
| ): SwarmUiModel { | |
| return SwarmUiModel( | |
| connectedBootstrap = status.bootstrapAddress.ifBlank { valueNotAvailable }, | |
| errorText = actionError?.takeIf { it.isNotBlank() } | |
| ?: status.lastError.ifBlank { valueNone }, | |
| ) | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/src/main/java/com/gomtm/android/swarm/SwarmUiModel.kt` around lines 8 -
16, The UI model currently uses hardcoded user-facing strings
VALUE_NOT_AVAILABLE and VALUE_NONE inside SwarmUiModel.from; remove those
English-only constants and change the API so the caller provides localized
fallback strings instead (e.g., add parameters to SwarmUiModel.from such as
notAvailableFallback and noneFallback or accept a single resource-derived pair)
and use those values when computing connectedBootstrap and errorText; update any
call sites that construct SwarmUiModel.from to pass getString(...) resource
values so localization is preserved.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
.github/workflows/release.yml (2)
74-82: Document the provenance artifact schema.The provenance JSON schema (fields:
gomtm_swarm_aar_url,gomtm_swarm_aar_version,gomtm_swarm_aar_sha256,gomtm_swarm_metadata_url,release_tag,git_commit) is not documented indocs/swarm-runtime-integration.md. Per the relevant code snippet, the documentation only covers pinning requirements but not the provenance output.Consider adding a section documenting the provenance artifact schema for downstream consumers.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/workflows/release.yml around lines 74 - 82, The docs are missing a description of the provenance artifact produced by the release script; add a new section to docs/swarm-runtime-integration.md that documents the provenance JSON schema produced at Path('dist/gomtm-android-provenance.json') (the provenance dict keys: gomtm_swarm_aar_url, gomtm_swarm_aar_version, gomtm_swarm_aar_sha256, gomtm_swarm_metadata_url, release_tag, git_commit), describe the type and meaning of each field, example values, and any consumer expectations (e.g., SHA256 format and where URLs point) so downstream consumers can reliably parse the artifact.
42-84: Consider extracting shared validation/download logic to a reusable workflow or composite action.Lines 45-84 duplicate the validation and download logic from
.github/workflows/ci.yml(lines 42-72). The only difference is the provenance generation (lines 74-82) which is release-specific.This duplication creates maintenance burden—any fix or enhancement needs to be applied in both places.
♻️ Suggested approach
Extract the common AAR validation and download logic into a composite action (e.g.,
.github/actions/download-gomtm-aar/action.yml), then call it from both workflows. The release workflow can add the provenance generation as an additional step.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/workflows/release.yml around lines 42 - 84, The workflow duplicates the AAR validation/download logic in the "Validate gomtm swarm AAR contract" and "Download pinned gomtm swarm AAR and metadata" steps; extract that shared logic into a reusable composite action (e.g., download-gomtm-aar) which takes inputs for GOMTM_SWARM_AAR_URL, GOMTM_SWARM_METADATA_URL, GOMTM_SWARM_AAR_VERSION, GOMTM_SWARM_AAR_SHA256, GOMTM_SWARM_AAR_NAME and GH_TOKEN and performs the mkdir/curl/sha256sum/python validation + outputs any artifacts or metadata; then replace the two duplicated steps in both workflows with a call to the composite action and keep the release workflow’s provenance generation as a small separate step that consumes the composite action’s outputs (or re-reads the downloaded metadata) so the provenance logic remains release-specific.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/ci.yml:
- Around line 54-56: The workflow currently exports GH_TOKEN from github.token
and uses it in curl Authorization headers to fetch GOMTM_SWARM_AAR_URL and
GOMTM_SWARM_METADATA_URL; since GITHUB_TOKEN is repo-scoped it cannot access
upstream private release assets, either switch to using a stored PAT (create a
repo secret like GOMTM_PAT and export GH_TOKEN="${{ secrets.GOMTM_PAT }}" before
the curl commands) or, if the upstream assets are public, remove the
Authorization header entirely and call curl without GH_TOKEN; update the step
around the export and the two curl invocations (referencing GOMTM_SWARM_AAR_URL,
GOMTM_SWARM_AAR_NAME, GOMTM_SWARM_METADATA_URL and GH_TOKEN) accordingly.
---
Nitpick comments:
In @.github/workflows/release.yml:
- Around line 74-82: The docs are missing a description of the provenance
artifact produced by the release script; add a new section to
docs/swarm-runtime-integration.md that documents the provenance JSON schema
produced at Path('dist/gomtm-android-provenance.json') (the provenance dict
keys: gomtm_swarm_aar_url, gomtm_swarm_aar_version, gomtm_swarm_aar_sha256,
gomtm_swarm_metadata_url, release_tag, git_commit), describe the type and
meaning of each field, example values, and any consumer expectations (e.g.,
SHA256 format and where URLs point) so downstream consumers can reliably parse
the artifact.
- Around line 42-84: The workflow duplicates the AAR validation/download logic
in the "Validate gomtm swarm AAR contract" and "Download pinned gomtm swarm AAR
and metadata" steps; extract that shared logic into a reusable composite action
(e.g., download-gomtm-aar) which takes inputs for GOMTM_SWARM_AAR_URL,
GOMTM_SWARM_METADATA_URL, GOMTM_SWARM_AAR_VERSION, GOMTM_SWARM_AAR_SHA256,
GOMTM_SWARM_AAR_NAME and GH_TOKEN and performs the mkdir/curl/sha256sum/python
validation + outputs any artifacts or metadata; then replace the two duplicated
steps in both workflows with a call to the composite action and keep the release
workflow’s provenance generation as a small separate step that consumes the
composite action’s outputs (or re-reads the downloaded metadata) so the
provenance logic remains release-specific.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: be7da5a3-9dea-4b27-aae2-57787879aad4
📒 Files selected for processing (2)
.github/workflows/ci.yml.github/workflows/release.yml
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
.github/workflows/release.yml (1)
121-123:⚠️ Potential issue | 🟠 MajorThis asset is still unsigned.
Publishing
app-release-unsigned.apkgives users a non-installable release artifact. Point the release to the signed APK only after the app'sreleasebuildType is wired to a signingConfig.Follow-up once release signing exists
files: | - app/build/outputs/apk/release/app-release-unsigned.apk + app/build/outputs/apk/release/app-release.apk dist/gomtm-android-provenance.jsonFor Android Gradle builds, what artifact does `assembleRelease` produce when no release `signingConfig` is set, and is `app-release-unsigned.apk` installable?🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/workflows/release.yml around lines 121 - 123, The workflow is publishing an unsigned APK (app-release-unsigned.apk) which is not installable; update the Android build/signing so the release buildType uses a signingConfig in your Gradle module (release signingConfig in build.gradle or a signingConfig provided by your CI keystore) and then change the release job to publish the signed artifact (the final signed APK/AAB produced by that signingConfig) instead of app-release-unsigned.apk; ensure the artifact path in the workflow (files block) points to the signed output name produced by your configured release signing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/ci.yml:
- Around line 27-34: The workflow-level env defines GOMTM_RELEASE_TOKEN (and
GH_FALLBACK_TOKEN) which makes the cross-repo secret available to all subsequent
steps; remove these tokens from the top-level env block and instead add them to
the specific download step that fetches the AAR (the "download" or AAR download
step) by setting env: GOMTM_RELEASE_TOKEN: ${{ secrets.GOMTM_GH_PAT }} and
GH_FALLBACK_TOKEN: ${{ github.token }} only on that step so they are not
injected into Gradle or other PR-run steps; update the .github/workflows/ci.yml
env usage to reference only per-step env for those tokens and keep other
variables (GOMTM_SWARM_*) at workflow scope.
In @.github/workflows/release.yml:
- Around line 29-37: The repository-wide env block currently exposes
GH_FALLBACK_TOKEN and GOMTM_RELEASE_TOKEN to every job step; move these tokens
out of the global env and into the env for only the "Download pinned gomtm swarm
AAR and metadata" step (the step that needs GOMTM_SWARM_AAR_URL/metadata and the
tokens) so other steps like assembleRelease, Java setup, and Android SDK
configuration don't inherit write permissions; remove GH_FALLBACK_TOKEN and
GOMTM_RELEASE_TOKEN from the top-level env and add them as step-local env
entries for that specific download step (referencing GH_FALLBACK_TOKEN and
GOMTM_RELEASE_TOKEN names and the step title "Download pinned gomtm swarm AAR
and metadata").
---
Duplicate comments:
In @.github/workflows/release.yml:
- Around line 121-123: The workflow is publishing an unsigned APK
(app-release-unsigned.apk) which is not installable; update the Android
build/signing so the release buildType uses a signingConfig in your Gradle
module (release signingConfig in build.gradle or a signingConfig provided by
your CI keystore) and then change the release job to publish the signed artifact
(the final signed APK/AAB produced by that signingConfig) instead of
app-release-unsigned.apk; ensure the artifact path in the workflow (files block)
points to the signed output name produced by your configured release signing.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: c0658bc9-3b67-47ee-b829-e6b39bfa1682
📒 Files selected for processing (2)
.github/workflows/ci.yml.github/workflows/release.yml
| env: | ||
| GOMTM_SWARM_AAR_URL: ${{ inputs.gomtm_swarm_aar_url || vars.GOMTM_SWARM_AAR_URL }} | ||
| GOMTM_SWARM_AAR_NAME: ${{ vars.GOMTM_SWARM_AAR_NAME || 'gomtm-swarm-android.aar' }} | ||
| GOMTM_SWARM_METADATA_URL: ${{ inputs.gomtm_swarm_metadata_url || vars.GOMTM_SWARM_METADATA_URL }} | ||
| GOMTM_SWARM_AAR_VERSION: ${{ inputs.gomtm_swarm_aar_version || vars.GOMTM_SWARM_AAR_VERSION }} | ||
| GOMTM_SWARM_AAR_SHA256: ${{ inputs.gomtm_swarm_aar_sha256 || vars.GOMTM_SWARM_AAR_SHA256 }} | ||
| GOMTM_SWARM_AAR_NAME: gomtm-swarm-android.aar | ||
| GH_FALLBACK_TOKEN: ${{ github.token }} | ||
| GOMTM_RELEASE_TOKEN: ${{ secrets.GOMTM_GH_PAT }} |
There was a problem hiding this comment.
Keep the cross-repo token off the PR build steps.
GOMTM_RELEASE_TOKEN is defined at workflow scope, so every later step — including the Gradle commands that run pull-request code — inherits it. That makes the cross-repo credential exfiltrable from a same-repo PR. Scope both tokens to the download step only.
Suggested fix
env:
GOMTM_SWARM_AAR_URL: ${{ inputs.gomtm_swarm_aar_url || vars.GOMTM_SWARM_AAR_URL }}
GOMTM_SWARM_METADATA_URL: ${{ inputs.gomtm_swarm_metadata_url || vars.GOMTM_SWARM_METADATA_URL }}
GOMTM_SWARM_AAR_VERSION: ${{ inputs.gomtm_swarm_aar_version || vars.GOMTM_SWARM_AAR_VERSION }}
GOMTM_SWARM_AAR_SHA256: ${{ inputs.gomtm_swarm_aar_sha256 || vars.GOMTM_SWARM_AAR_SHA256 }}
GOMTM_SWARM_AAR_NAME: gomtm-swarm-android.aar
- GH_FALLBACK_TOKEN: ${{ github.token }}
- GOMTM_RELEASE_TOKEN: ${{ secrets.GOMTM_GH_PAT }}
- name: Download pinned gomtm swarm AAR and metadata
+ env:
+ GH_FALLBACK_TOKEN: ${{ github.token }}
+ GOMTM_RELEASE_TOKEN: ${{ secrets.GOMTM_GH_PAT }}
shell: bashIn GitHub Actions, do workflow-level env variables get injected into all later `run` and `uses` steps, and are repository secrets available to `pull_request` workflows for branches in the same repository?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/ci.yml around lines 27 - 34, The workflow-level env
defines GOMTM_RELEASE_TOKEN (and GH_FALLBACK_TOKEN) which makes the cross-repo
secret available to all subsequent steps; remove these tokens from the top-level
env block and instead add them to the specific download step that fetches the
AAR (the "download" or AAR download step) by setting env: GOMTM_RELEASE_TOKEN:
${{ secrets.GOMTM_GH_PAT }} and GH_FALLBACK_TOKEN: ${{ github.token }} only on
that step so they are not injected into Gradle or other PR-run steps; update the
.github/workflows/ci.yml env usage to reference only per-step env for those
tokens and keep other variables (GOMTM_SWARM_*) at workflow scope.
| env: | ||
| RELEASE_TAG: v0.2.${{ github.run_number }} | ||
| RELEASE_TAG: v0.3.${{ github.run_number }} | ||
| GOMTM_SWARM_AAR_URL: ${{ inputs.gomtm_swarm_aar_url || vars.GOMTM_SWARM_AAR_URL }} | ||
| GOMTM_SWARM_AAR_NAME: ${{ vars.GOMTM_SWARM_AAR_NAME || 'gomtm-swarm-android.aar' }} | ||
| GOMTM_SWARM_METADATA_URL: ${{ inputs.gomtm_swarm_metadata_url || vars.GOMTM_SWARM_METADATA_URL }} | ||
| GOMTM_SWARM_AAR_VERSION: ${{ inputs.gomtm_swarm_aar_version || vars.GOMTM_SWARM_AAR_VERSION }} | ||
| GOMTM_SWARM_AAR_SHA256: ${{ inputs.gomtm_swarm_aar_sha256 || vars.GOMTM_SWARM_AAR_SHA256 }} | ||
| GOMTM_SWARM_AAR_NAME: gomtm-swarm-android.aar | ||
| GH_FALLBACK_TOKEN: ${{ github.token }} | ||
| GOMTM_RELEASE_TOKEN: ${{ secrets.GOMTM_GH_PAT }} |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
In GitHub Actions, if a job has contents: writeand a workflow-level env exposesgithub.token, can later build steps access a repo-write token via the environment?
💡 Result:
Yes, later build steps can access a repo-write token via the environment if the job has contents: write permission and the workflow-level env exposes github.token (e.g., env: GITHUB_TOKEN: ${{ github.token }}). The github.token context provides the GITHUB_TOKEN value, which inherits the job's permissions, including contents: write for repo writes like git push or API updates. Setting it in workflow-level env makes the variable available to all steps in all jobs. Steps can then use
Citations:
- 1: https://docs.github.com/actions/reference/authentication-in-a-workflow
- 2: https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs
- 3: https://docs.github.com/actions/writing-workflows/choosing-what-your-workflow-does/controlling-permissions-for-github_token
- 4: https://docs.github.com/actions/concepts/security/github_token
- 5: https://docs.github.com/en/actions/security-guides/automatic-token-authentication
- 6: https://docs.github.com/en/actions/reference/contexts-reference
- 7: https://docs.github.com/en/actions/reference/github_token-reference
- 8: https://github.blog/changelog/2021-04-20-github-actions-control-permissions-for-github_token
🏁 Script executed:
cat -n .github/workflows/release.ymlRepository: codeh007/gomtm-android
Length of output: 5964
Move repo-write tokens to the step that needs them.
GH_FALLBACK_TOKEN here carries contents: write, and both tokens are inherited by all steps—including assembleRelease, Java setup, and Android SDK configuration. Only the "Download pinned gomtm swarm AAR and metadata" step (line 55) actually uses them, so keep them step-local to follow least-privilege.
Suggested fix
env:
RELEASE_TAG: v0.3.${{ github.run_number }}
GOMTM_SWARM_AAR_URL: ${{ inputs.gomtm_swarm_aar_url || vars.GOMTM_SWARM_AAR_URL }}
GOMTM_SWARM_METADATA_URL: ${{ inputs.gomtm_swarm_metadata_url || vars.GOMTM_SWARM_METADATA_URL }}
GOMTM_SWARM_AAR_VERSION: ${{ inputs.gomtm_swarm_aar_version || vars.GOMTM_SWARM_AAR_VERSION }}
GOMTM_SWARM_AAR_SHA256: ${{ inputs.gomtm_swarm_aar_sha256 || vars.GOMTM_SWARM_AAR_SHA256 }}
GOMTM_SWARM_AAR_NAME: gomtm-swarm-android.aar
- GH_FALLBACK_TOKEN: ${{ github.token }}
- GOMTM_RELEASE_TOKEN: ${{ secrets.GOMTM_GH_PAT }}
- name: Download pinned gomtm swarm AAR and metadata
+ env:
+ GH_FALLBACK_TOKEN: ${{ github.token }}
+ GOMTM_RELEASE_TOKEN: ${{ secrets.GOMTM_GH_PAT }}
shell: bash🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/release.yml around lines 29 - 37, The repository-wide env
block currently exposes GH_FALLBACK_TOKEN and GOMTM_RELEASE_TOKEN to every job
step; move these tokens out of the global env and into the env for only the
"Download pinned gomtm swarm AAR and metadata" step (the step that needs
GOMTM_SWARM_AAR_URL/metadata and the tokens) so other steps like
assembleRelease, Java setup, and Android SDK configuration don't inherit write
permissions; remove GH_FALLBACK_TOKEN and GOMTM_RELEASE_TOKEN from the top-level
env and add them as step-local env entries for that specific download step
(referencing GH_FALLBACK_TOKEN and GOMTM_RELEASE_TOKEN names and the step title
"Download pinned gomtm swarm AAR and metadata").
Summary\n- replace the old host-shell probing flow with a real node runtime host UI\n- align the Android app with startNode/stopNode and real discovered peers\n- pin gomtm swarm AAR consumption in CI/release with metadata validation\n\n## Notes\n- CI/release requires pinned gomtm swarm AAR repo variables to be set\n- validation remains GitHub Actions based; no local Android build is treated as acceptance\n
Summary by CodeRabbit
New Features
Chores
Docs
Permissions
Tests