Skip to content

Connect GitHub Rock to its Ktor backend - #143

Closed
SayanthRock wants to merge 16 commits into
mainfrom
integration/backend-v1
Closed

Connect GitHub Rock to its Ktor backend#143
SayanthRock wants to merge 16 commits into
mainfrom
integration/backend-v1

Conversation

@SayanthRock

@SayanthRock SayanthRock commented Jul 23, 2026

Copy link
Copy Markdown
Member

End-to-end backend connection

  • adds a Retrofit contract for backend health, config, Device Flow start/poll, and token refresh
  • adds a runtime HTTPS endpoint store with build-time GITHUB_ROCK_BACKEND_URL support
  • adds a native Backend connection centre under App information
  • tests /v1 compatibility before saving the endpoint
  • shows dependency health, API version, maintenance state, and feature availability

Authentication

  • prefers the backend for Device Flow and token refresh
  • preserves access-token and refresh-token expiry metadata
  • keeps direct GitHub Device Flow as a fail-safe when the backend is unavailable
  • never embeds the OAuth client secret in Android

Security

  • accepts HTTPS endpoints only
  • rejects URLs containing credentials, queries, or fragments
  • keeps normal repository, issue, PR, Actions, release, and download operations directly authorized by GitHub

CI and documentation

  • supports the GITHUB_ROCK_BACKEND_URL Actions variable
  • adds endpoint, token mapping, and exact HTTP path tests
  • updates App information Compose coverage
  • documents deployment and runtime connection

Coordinated backend

Companion backend PR: Sayanthrock-Developer/GitHub-Rock-Backend#24

Validation required

  • unit tests
  • Compose UI-test compilation
  • Android lint
  • debug APK
  • release APK
  • verification artifact upload

Summary by CodeRabbit

  • New Features
    • Added optional backend connectivity configuration and status management.
    • Added backend connection controls, health checks, feature availability, reconnect, and disconnect actions.
    • Added backend-assisted authentication with fallback to direct GitHub authentication.
  • Documentation
    • Added setup and security guidance for connecting to a backend service.
    • Updated local configuration examples with backend endpoint instructions.
  • Tests
    • Added coverage for backend API paths, security validation, compatibility, and authentication flows.

Copilot AI review requested due to automatic review settings July 23, 2026 08:30

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The Android app adds optional GitHub Rock backend connectivity, endpoint validation and persistence, backend-mediated device authentication with direct GitHub fallback, a Compose connection screen, build and CI configuration, tests, and documentation.

Changes

Backend connectivity and authentication

Layer / File(s) Summary
Backend contract and build configuration
.github/workflows/*, app/build.gradle.kts, app/src/main/java/com/sayanthrock/githubrock/core/network/*, local.properties.example
Defines backend DTOs and Retrofit routes, resolves GITHUB_ROCK_BACKEND_URL from build configuration sources, and exposes it through BuildConfig.
Endpoint storage and compatibility gateway
app/src/main/java/com/sayanthrock/githubrock/data/backend/BackendGateway.kt
Stores backend endpoints, validates HTTPS URLs and backend compatibility, caches clients, checks health/configuration, and maps authentication responses.
Backend-aware device authentication
app/src/main/java/com/sayanthrock/githubrock/data/auth/DeviceFlowAuthRepository.kt
Selects backend or direct GitHub device-flow transport for startup, polling, refresh, and fallback handling.
Backend connection navigation and UI
app/src/main/java/com/sayanthrock/githubrock/ui/screens/AppInformationScreen.kt, app/src/main/java/com/sayanthrock/githubrock/ui/screens/BackendConnectionScreen.kt
Adds navigation from app information to a connection screen with endpoint entry, status, compatibility details, retesting, and disconnection.
Backend contract validation and documentation
app/src/test/java/com/sayanthrock/githubrock/Backend*Test.kt, app/src/androidTest/java/com/sayanthrock/githubrock/AppInformationScreenTest.kt, docs/BACKEND_CONNECTION.md
Tests API paths, URL policies, version and feature validation, token mapping, and backend UI visibility; documents deployment and authentication behavior.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant BackendConnectionScreen
  participant BackendConnectionViewModel
  participant BackendGateway
  participant GitHubRockBackendApi
  User->>BackendConnectionScreen: Enter HTTPS endpoint
  BackendConnectionScreen->>BackendConnectionViewModel: connect(endpoint)
  BackendConnectionViewModel->>BackendGateway: saveAndCheck(endpoint)
  BackendGateway->>GitHubRockBackendApi: health() and config()
  GitHubRockBackendApi-->>BackendGateway: Health and public configuration
  BackendGateway-->>BackendConnectionViewModel: BackendConnectionSnapshot
  BackendConnectionViewModel-->>BackendConnectionScreen: Updated connection state
Loading

Possibly related PRs

Suggested labels: size:XL

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: integrating GitHub Rock with its Ktor backend.
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.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch integration/backend-v1

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Checkov (3.3.8)
.github/workflows/android-ci.yml

Traceback (most recent call last):
File "/usr/local/bin/checkov", line 2, in
from checkov.main import Checkov
ModuleNotFoundError: No module named 'checkov'

.github/workflows/release.yml

Traceback (most recent call last):
File "/usr/local/bin/checkov", line 2, in
from checkov.main import Checkov
ModuleNotFoundError: No module named 'checkov'

app/build.gradle.kts

Traceback (most recent call last):
File "/usr/local/bin/checkov", line 2, in
from checkov.main import Checkov
ModuleNotFoundError: No module named 'checkov'


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

@ai-coding-guardrails ai-coding-guardrails 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.

I've got 2 comments for you to consider

Some comments were hidden because we found that the quality was not high enough.

Low Quality Comments

File app/src/main/java/com/sayanthrock/githubrock/ui/screens/BackendConnectionScreen.kt:

  • line 282:
    • Comment quality: low
    • Comment quality reason: The comment identifies a UI bug where the error color is shown during loading, but it does not recommend a specific code change to fix it.
    • Comment:

Status card always shows error color when loading, even when a backend was previously connected

The connected variable is derived solely from state.snapshot != null. When refresh() is called, _state.update sets snapshot = null (via the error path) only on failure, but on the loading path the snapshot is preserved. However, in connect(), the loading state does not clear the snapshot — so connected can be true while loading. More critically, in refresh() the snapshot is also preserved during loading (only cleared on error), so the accent color is correct there.

The real bug: in BackendStatusCard, when state.loading is true and state.snapshot == null (e.g., on first connect attempt), connected is false, so accent becomes MaterialTheme.colorScheme.error and the card renders in red while the status text says "Checking backend…". This is a misleading UI state — the error color should not be shown while loading.

Why did I show this?

Category: bug
Comment Quality: low

Based on general best practices

Risk: 🟢 Low

Risk analysis

The highest scores are for blast_radius, security_impact, and operational_risk. The PR introduces a new backend connection feature affecting authentication flows across the app, with changes in DeviceFlowAuthRepository and BackendGateway impacting how tokens are handled. Security concerns arise from potential misuse of backend endpoints and transport switching logic, though input validation mitigates some risks. Operational risk is elevated due to the introduction of network-dependent authentication paths and possible inconsistencies in endpoint caching versus persistence. The blast radius is significant because these changes touch core auth components used throughout the application.

Reviewed with 🤟 by Zenable

private val pollMutex = Mutex()
private var lastTokenRequestAtMillis = 0L
private var requiredIntervalSeconds = MINIMUM_POLL_INTERVAL_SECONDS
@Volatile private var activeTransport = DeviceFlowTransport.DirectGitHub

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

activeTransport is a shared mutable field written outside the pollMutex, causing a data race with requestTokenAtAllowedInterval

The activeTransport field is written in begin() (lines 48, 59, 64) without holding pollMutex, but it is read inside requestTokenAtAllowedInterval which does hold pollMutex. This is a data race: begin() can update activeTransport concurrently with an in-progress poll() reading it inside the lock, silently switching the transport mid-poll. Either write activeTransport inside pollMutex.withLock in begin(), or protect it with a separate lock/atomic.

Why did I show this?

Category: bug
Comment Quality: high

Based on general best practices

Comment on lines +84 to +88
health = health,
config = config,
)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

saveAndCheck saves the endpoint even if check() throws, due to missing try/catch ordering

The saveAndCheck flow calls check(endpoint) and then endpointStore.save(endpoint). However, check() can throw (e.g., network error, non-2xx response), in which case save is never called — that part is fine. But the real issue is the reverse: if check() succeeds but save() throws (e.g., normalizedBackendBaseUrl returns null for the already-validated endpoint), the endpoint was already validated and the API was already called, but the store is not updated. More critically, endpoint here is the result of requireBackendBaseUrl(rawEndpoint) (already normalized), but endpointStore.save(endpoint) re-normalizes it internally — this is harmless but redundant. The actual bug risk is that check(endpoint) creates and caches a Retrofit instance for endpoint, but if save is never called (throws), subsequent calls to apiForConfiguredEndpoint() will use the old stored endpoint, not the one just checked. This is a subtle inconsistency: the cache may point to the new endpoint while the store still holds the old one.

Why did I show this?

Category: bug
Comment Quality: high

Based on general best practices

@ai-coding-guardrails ai-coding-guardrails Bot added the zenable/risk:low Zenable assessed this PR as LOW risk. label Jul 23, 2026
@SayanthRock
SayanthRock marked this pull request as ready for review July 23, 2026 08:36
Copilot AI review requested due to automatic review settings July 23, 2026 08:36

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@ai-coding-guardrails ai-coding-guardrails 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.

I've got 4 comments for you to consider

Risk: 🟡 Medium

Risk analysis

The highest scoring dimensions are blast_radius and operational_risk. blast_radius is elevated because the PR introduces a new backend connection feature that affects authentication flows and is integrated into the core app experience, touching 13 files and adding significant new surface area. operational_risk is notable due to the introduction of network-dependent authentication paths and potential performance impacts from redundant config fetching in the backend gateway. security_impact is modestly raised by the handling of OAuth tokens and backend connections, though the PR takes care to avoid embedding secrets and enforces HTTPS. test_coverage scores below neutral due to the lack of explicit tests for the new UI states and edge cases in backend connection logic, despite good unit test additions for API paths and contracts. reversibility is impacted by the new persistent backend endpoint configuration and associated UX, which would require careful cleanup if removed.

Reviewed with 🤟 by Zenable

private val pollMutex = Mutex()
private var lastTokenRequestAtMillis = 0L
private var requiredIntervalSeconds = MINIMUM_POLL_INTERVAL_SECONDS
@Volatile private var activeTransport = DeviceFlowTransport.DirectGitHub

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

activeTransport is a mutable shared field set outside pollMutex, causing a race between begin() and requestTokenAtAllowedInterval()

The activeTransport field is written in begin() (lines 48, 59, 64) without holding pollMutex, but it is read inside requestTokenAtAllowedInterval() which does hold pollMutex. A concurrent call to begin() while polling is in progress can change activeTransport mid-poll, causing the poll to use the wrong transport (e.g., switching from Backend to DirectGitHub or vice versa) after the lock is already acquired.

Either:

  1. Write activeTransport only while holding pollMutex (e.g., update it inside the locked block in begin()), or
  2. Pass the chosen transport as a parameter to requestTokenAtAllowedInterval() so it is captured at begin() time and not subject to later mutation.
Why did I show this?

Category: bug
Comment Quality: high

Based on general best practices

Comment on lines +89 to +93
suspend fun saveAndCheck(rawEndpoint: String): BackendConnectionSnapshot {
val endpoint = requireBackendBaseUrl(rawEndpoint)
val snapshot = check(endpoint)
endpointStore.save(endpoint)
return snapshot

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

saveAndCheck normalizes rawEndpoint twice, potentially saving a different value than what was validated

In saveAndCheck, requireBackendBaseUrl(rawEndpoint) normalizes the URL into endpoint, then check(endpoint) is called with the already-normalized URL. However, endpointStore.save(endpoint) is then called again — save() internally calls normalizedBackendBaseUrl again, which is harmless but redundant. The real issue is that check() is called with the normalized endpoint, but if check throws (e.g., API version mismatch), save is never called — this is correct. However, if rawEndpoint normalizes to a different string than what endpointStore.save would produce (it won't here since both call normalizedBackendBaseUrl), there could be a mismatch. This is safe as-is, but the double-normalization is a latent maintenance risk. More critically: check(endpoint) validates the API version, but saveAndCheck does not call validateBackendForApp, so a backend in maintenance mode or with a disabled feature can still be saved as the configured endpoint.

Why did I show this?

Category: bug
Comment Quality: high

Based on general best practices

Comment on lines +104 to +108
suspend fun startDeviceFlow(): DeviceCodeResponse {
val api = apiForConfiguredEndpoint()
validateBackendForApp(api.config(), "oauthDeviceProxy")
return api.startDeviceFlow()
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

startDeviceFlow makes two sequential network calls per invocation, fetching config on every device flow start

startDeviceFlow calls api.config() on every invocation via validateBackendForApp. This means every device flow start makes an extra /v1/config network round-trip. More critically, there is a TOCTOU race: the config is fetched and validated, but between validation and api.startDeviceFlow(), the backend could enter maintenance mode or disable the feature. While unavoidable at the network level, the extra config fetch on every call (also present in refreshToken) adds latency and a failure point with no caching. Consider whether the config from check()/saveAndCheck() could be reused or cached briefly.

Why did I show this?

Category: performance
Comment Quality: high

Based on general best practices

Comment on lines +280 to +282
private fun BackendStatusCard(state: BackendConnectionUiState) {
val connected = state.snapshot != null
val accent = if (connected) MaterialTheme.colorScheme.tertiary else MaterialTheme.colorScheme.error

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

BackendStatusCard shows error accent color even when backend is loading, misleading users during the check

The connected check (state.snapshot != null) and the resulting accent color do not account for the loading state. When state.loading is true but snapshot is still null (e.g., on initial load or retest), the card renders with the error color and CloudOff icon even though the backend may actually be reachable. This creates a misleading "unavailable" appearance during the check.

Consider deriving accent based on the loading state as a third case (e.g., using onSurfaceVariant or primary) so the status card accurately reflects the in-progress state.

Why did I show this?

Category: readability
Comment Quality: high

Based on general best practices

@ai-coding-guardrails ai-coding-guardrails Bot added zenable/risk:medium Zenable assessed this PR as MEDIUM risk. and removed zenable/risk:low Zenable assessed this PR as LOW risk. labels Jul 23, 2026

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c576cfddbe

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +39 to +41
fun endpoint(): String? = normalizedBackendBaseUrl(
preferences.getString(KEY_ENDPOINT, null) ?: BuildConfig.BACKEND_BASE_URL,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Allow users to disconnect bundled endpoints

When a release or CI build sets GITHUB_ROCK_BACKEND_URL, clearing the preference makes endpoint() immediately fall back to BuildConfig.BACKEND_BASE_URL. The UI reports that it has disconnected, but isConfigured remains true and subsequent login/refresh requests still use the bundled backend. Persist an explicit disabled state (or present this action as resetting to the bundled default) so that Disconnect actually disables backend use.

Useful? React with 👍 / 👎.

Comment on lines +156 to 161
val response = when (activeTransport) {
DeviceFlowTransport.Backend -> backendGateway.pollDeviceFlow(device.deviceCode)
DeviceFlowTransport.DirectGitHub -> api.requestToken(
BuildConfig.GITHUB_CLIENT_ID,
device.deviceCode,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Fall back when backend polling fails

When the backend successfully creates a device code but becomes unavailable before the user approves it, this path propagates the polling exception rather than switching to the direct GitHub token endpoint. The login therefore fails even in builds with the public client ID configured, despite the advertised backend-unavailable fallback; handle transport failures here by continuing the already-started flow through the direct client when it is available.

Useful? React with 👍 / 👎.

@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

🧹 Nitpick comments (1)
app/src/test/java/com/sayanthrock/githubrock/BackendContractTest.kt (1)

26-29: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover fragment rejection.

Line 26–29 omit the documented fragment case, so https://example.com#fragment could regress to accepted without failing this contract test.

Proposed test
         assertNull(normalizedBackendBaseUrl("https://user:secret@example.com"))
         assertNull(normalizedBackendBaseUrl("https://example.com?token=secret"))
+        assertNull(normalizedBackendBaseUrl("https://example.com#fragment"))
         assertNull(normalizedBackendBaseUrl(""))
🤖 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/test/java/com/sayanthrock/githubrock/BackendContractTest.kt` around
lines 26 - 29, Add a contract assertion in BackendContractTest alongside the
existing normalizedBackendBaseUrl rejection cases to verify that a URL
containing a fragment, such as https://example.com#fragment, returns null. Keep
the existing rejection assertions unchanged.
🤖 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 @.github/workflows/android-ci.yml:
- Line 22: The workflows reference an unusable GITHUB_-prefixed Actions
variable. In .github/workflows/android-ci.yml:22, change the vars reference to
ROCK_BACKEND_URL while preserving the exported GITHUB_ROCK_BACKEND_URL
environment name, and define the ROCK_BACKEND_URL repository or organization
variable. Apply the same vars reference change in
.github/workflows/release.yml:33.

In
`@app/src/main/java/com/sayanthrock/githubrock/ui/screens/BackendConnectionScreen.kt`:
- Around line 82-94: Serialize competing operations in the
BackendConnectionViewModel’s init-time refresh() and connect() flows so stale
results cannot overwrite newer connection state. Cancel or supersede the
previous request, or track an operation generation and guard every state
write—including success and error updates—so only the latest operation updates
_state.

In `@app/src/test/java/com/sayanthrock/githubrock/BackendApiPathTest.kt`:
- Around line 63-69: Align the token-refresh contract with the companion backend
by removing the /v1/auth/device/refresh expectation from
BackendApiPathTest.refreshToken unless the route is implemented and published;
otherwise update Retrofit and fallback behavior to use only supported start and
poll endpoints. In docs/BACKEND_CONNECTION.md lines 5, 18, and 38-41, remove or
correct the backend refresh claim, oauthRefreshProxy guidance, and
authentication-flow documentation so they match the supported contract.

---

Nitpick comments:
In `@app/src/test/java/com/sayanthrock/githubrock/BackendContractTest.kt`:
- Around line 26-29: Add a contract assertion in BackendContractTest alongside
the existing normalizedBackendBaseUrl rejection cases to verify that a URL
containing a fragment, such as https://example.com#fragment, returns null. Keep
the existing rejection assertions unchanged.
🪄 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 Plus

Run ID: 11a4b785-ed6c-4a8a-aeb8-66702cd08574

📥 Commits

Reviewing files that changed from the base of the PR and between a274d95 and c576cfd.

📒 Files selected for processing (13)
  • .github/workflows/android-ci.yml
  • .github/workflows/release.yml
  • app/build.gradle.kts
  • app/src/androidTest/java/com/sayanthrock/githubrock/AppInformationScreenTest.kt
  • app/src/main/java/com/sayanthrock/githubrock/core/network/GitHubRockBackendApi.kt
  • app/src/main/java/com/sayanthrock/githubrock/data/auth/DeviceFlowAuthRepository.kt
  • app/src/main/java/com/sayanthrock/githubrock/data/backend/BackendGateway.kt
  • app/src/main/java/com/sayanthrock/githubrock/ui/screens/AppInformationScreen.kt
  • app/src/main/java/com/sayanthrock/githubrock/ui/screens/BackendConnectionScreen.kt
  • app/src/test/java/com/sayanthrock/githubrock/BackendApiPathTest.kt
  • app/src/test/java/com/sayanthrock/githubrock/BackendContractTest.kt
  • docs/BACKEND_CONNECTION.md
  • local.properties.example

timeout-minutes: 45
env:
GITHUB_CLIENT_ID: ${{ vars.PUBLIC_GITHUB_OAUTH_CLIENT_ID || 'Ov23lim8WhLjeUMqvuMj' }}
GITHUB_ROCK_BACKEND_URL: ${{ vars.GITHUB_ROCK_BACKEND_URL || '' }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

GITHUB_-prefixed Actions config variable is unusable; backend URL is never injected in CI or release builds. GitHub blocks creation of Actions configuration variables/secrets whose names start with GITHUB_, so vars.GITHUB_ROCK_BACKEND_URL can never be set and both expressions always resolve to ''. Rename only the vars. reference (keep the exported env var name GITHUB_ROCK_BACKEND_URL so Gradle's System.getenv still matches).

  • .github/workflows/android-ci.yml#L22-L22: change ${{ vars.GITHUB_ROCK_BACKEND_URL || '' }} to ${{ vars.ROCK_BACKEND_URL || '' }} and define the ROCK_BACKEND_URL repo/org variable.
  • .github/workflows/release.yml#L33-L33: apply the same ${{ vars.ROCK_BACKEND_URL || '' }} change.
🧰 Tools
🪛 actionlint (1.7.12)

[error] 22-22: configuration variable name "github_rock_backend_url" must not start with the GITHUB_ prefix (case insensitive). note: see the convention at https://docs.github.com/en/actions/learn-github-actions/variables#naming-conventions-for-configuration-variables

(expression)

📍 Affects 2 files
  • .github/workflows/android-ci.yml#L22-L22 (this comment)
  • .github/workflows/release.yml#L33-L33
🤖 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/android-ci.yml at line 22, The workflows reference an
unusable GITHUB_-prefixed Actions variable. In
.github/workflows/android-ci.yml:22, change the vars reference to
ROCK_BACKEND_URL while preserving the exported GITHUB_ROCK_BACKEND_URL
environment name, and define the ROCK_BACKEND_URL repository or organization
variable. Apply the same vars reference change in
.github/workflows/release.yml:33.

Source: Linters/SAST tools

Comment on lines +82 to +94
init {
if (gateway.isConfigured) refresh()
}

fun connect(rawEndpoint: String) {
viewModelScope.launch {
_state.update { it.copy(loading = true, error = null, endpoint = rawEndpoint.trim()) }
try {
val snapshot = gateway.saveAndCheck(rawEndpoint)
_state.value = BackendConnectionUiState(
endpoint = snapshot.endpoint,
snapshot = snapshot,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Serialize competing connection operations.

The init-time refresh() and a user connect() can complete out of order; the older check can overwrite the newly saved endpoint and snapshot. Cancel/supersede the prior request or gate state writes with an operation generation.

🤖 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/com/sayanthrock/githubrock/ui/screens/BackendConnectionScreen.kt`
around lines 82 - 94, Serialize competing operations in the
BackendConnectionViewModel’s init-time refresh() and connect() flows so stale
results cannot overwrite newer connection state. Cancel or supersede the
previous request, or track an operation generation and guard every state
write—including success and error updates—so only the latest operation updates
_state.

Comment on lines +63 to +69
api.refreshToken(BackendTokenRefreshRequest("refresh"))

assertEquals("/v1/health", server.takeRequest().path)
assertEquals("/v1/config", server.takeRequest().path)
assertEquals("/v1/auth/device/start", server.takeRequest().path)
assertEquals("/v1/auth/device/poll", server.takeRequest().path)
assertEquals("/v1/auth/device/refresh", server.takeRequest().path)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Align the token-refresh contract with the companion backend.

Line 63 expects /v1/auth/device/refresh, while the linked backend’s published API exposes Device Flow start and poll but no refresh route. The mock test therefore passes despite the deployed-contract mismatch. (github.com)

  • app/src/test/java/com/sayanthrock/githubrock/BackendApiPathTest.kt#L63-L69: only retain this route after the companion backend implements and publishes it; otherwise align Retrofit and fallback behavior with the available API.
  • docs/BACKEND_CONNECTION.md#L5-L5: remove the claim that backend token refresh is currently provided, or ship the matching route.
  • docs/BACKEND_CONNECTION.md#L18-L18: remove or correct the oauthRefreshProxy configuration guidance to match the implemented contract.
  • docs/BACKEND_CONNECTION.md#L38-L41: correct the authentication flow so users are not promised server-side refresh that will fail at runtime.
📍 Affects 2 files
  • app/src/test/java/com/sayanthrock/githubrock/BackendApiPathTest.kt#L63-L69 (this comment)
  • docs/BACKEND_CONNECTION.md#L5-L5
  • docs/BACKEND_CONNECTION.md#L18-L18
  • docs/BACKEND_CONNECTION.md#L38-L41
🤖 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/test/java/com/sayanthrock/githubrock/BackendApiPathTest.kt` around
lines 63 - 69, Align the token-refresh contract with the companion backend by
removing the /v1/auth/device/refresh expectation from
BackendApiPathTest.refreshToken unless the route is implemented and published;
otherwise update Retrofit and fallback behavior to use only supported start and
poll endpoints. In docs/BACKEND_CONNECTION.md lines 5, 18, and 38-41, remove or
correct the backend refresh claim, oauthRefreshProxy guidance, and
authentication-flow documentation so they match the supported contract.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

zenable/risk:medium Zenable assessed this PR as MEDIUM risk.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants