Skip to content

Feature: Support local builds#20

Open
YarosMallorca wants to merge 7 commits intobetaflight:mainfrom
YarosMallorca:feat/local-builds
Open

Feature: Support local builds#20
YarosMallorca wants to merge 7 commits intobetaflight:mainfrom
YarosMallorca:feat/local-builds

Conversation

@YarosMallorca
Copy link
Contributor

@YarosMallorca YarosMallorca commented Feb 13, 2026

Adds support for locally built firmware, without cloud build ID.
Experimentally extracts things from CLI.

Screenshot 2026-02-13 at 17 32 29 Screenshot 2026-02-13 at 17 32 55

Summary by CodeRabbit

  • New Features

    • Local/offline build support with an isLocal marker and Local Build badge.
    • Utilities to extract MCU, target, manufacturer ID, and release info from CLI text.
  • Bug Fixes

    • Corrected SmartAudio detection to reliably detect the noncompliant flag.
  • UI/UX Improvements

    • Build Data Missing warning, conditional sections, release/tag display, timestamps, and download/log actions.
  • Chores

    • Settings migration adds a Local Build warning entry and versioning.

@coderabbitai
Copy link

coderabbitai bot commented Feb 13, 2026

📝 Walkthrough

Walkthrough

Adds CLI/local build support: new regex extractors for build metadata, threads an isLocal flag through types/stores/routes, settings migration for a Local Build Warning, and conditional UI/rendering for local vs cloud builds.

Changes

Cohort / File(s) Summary
Data Extraction Utilities
src/lib/extract.ts
Added four new public extractors: extractMcu(), extractTarget(), extractManufacturerId(), and extractRelease() to parse MCU, target, manufacturer_id, and Betaflight release/tag from CLI/status text; each returns the matched string or null.
Problem Checks
src/lib/problems/definitions.ts
Adjusted Noncompliant SmartAudio check to read build options from data.build?.request?.options (array) instead of data.build?.request, aligning includes checks to options array.
Stores / Types
src/lib/stores/previousIds.ts, src/lib/stores/settings.ts
PreviousId type gains optional isLocal?: boolean; allSettings version bumped to 2, added Local Build Warning IdPreviewCardSetting, and a migration on settings.update to append the local setting when upgrading.
Routes — Logic & UI
src/routes/[key]/+page.ts, src/routes/[key]/+page.svelte, src/routes/+page.svelte
Threaded isLocal through addPreviousId() and route flows; cloud build fetch now conditional on support key; when cloud build missing, derive config/request from CLI using new extractors and mark builds isLocal; UI shows Local badge/alert, hides cloud-only actions, and conditionally renders sections (build, firmware, options) based on presence of config/request/options and isLocal.

Sequence Diagram

sequenceDiagram
    participant User as User / Input
    participant Extract as Extract Module (src/lib/extract.ts)
    participant Server as Route Logic (src/routes/[key]/+page.ts)
    participant Store as PreviousId Store (src/lib/stores/previousIds.ts)
    participant UI as UI Components (src/routes/[key]/+page.svelte)

    User->>Server: Submit/view build (cloud key optional, CLI text)
    Server->>Server: If supportBuildKey -> fetch cloud build
    alt Cloud build present
        Server->>Store: create PreviousId (isLocal: false)
    else Cloud build missing
        Server->>Extract: extractMcu(), extractTarget(), extractManufacturerId(), extractRelease()
        Extract-->>Server: metadata (mcu, target, manufacturer, release)
        Server->>Store: create PreviousId (isLocal: true)
    end
    Store->>UI: emit updated PreviousId list
    alt isLocal == true
        UI->>UI: render Local badge & TriangleAlert, hide cloud-only actions
    else isLocal == false
        UI->>UI: render full firmware actions and release/changelog links
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I nibble CLI lines late at night,

regex whiskers find metadata bright,
isLocal flags hop into the stack,
badges and warnings guide the track,
Cloud and CLI now share the light.

🚥 Pre-merge checks | ✅ 2 | ❌ 2
❌ Failed checks (2 warnings)
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.
Merge Conflict Detection ⚠️ Warning ❌ Merge conflicts detected (14 files):

⚔️ src/app.css (content)
⚔️ src/components/TargetListItem.svelte (content)
⚔️ src/lib/cloudBuildTypes.ts (content)
⚔️ src/lib/extract.ts (content)
⚔️ src/lib/problems/definitions.ts (content)
⚔️ src/lib/stores/previousIds.ts (content)
⚔️ src/lib/stores/settings.ts (content)
⚔️ src/routes/+page.svelte (content)
⚔️ src/routes/[key]/+page.svelte (content)
⚔️ src/routes/[key]/+page.ts (content)
⚔️ src/routes/targets/+page.svelte (content)
⚔️ src/routes/targets/+page.ts (content)
⚔️ src/routes/targets/[target]/+page.svelte (content)
⚔️ src/routes/targets/[target]/+page.ts (content)

These conflicts must be resolved before merging into main.
Resolve conflicts locally and push changes to this branch.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Feature: Support local builds' directly and clearly describes the main change—adding support for locally built firmware without cloud build IDs.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
⚔️ Resolve merge conflicts (beta)
  • Auto-commit resolved conflicts to branch feat/local-builds
  • Post resolved changes as copyable diffs in a comment

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.

❤️ Share

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In `@src/lib/stores/settings.ts`:
- Around line 72-83: The migration inside settings.update that pushes into
idPreviewCardSettings uses title: "Local", which mismatches the default setting
title "Local Build Warning"; update the migration (the block that mutates
s.idPreviewCardSettings in settings.update) to use title: "Local Build Warning"
so migrated users see the same label as new users, and ensure s.version is still
set to 2 and the rest of the object (name: "isLocal", value: true) remains
unchanged.

In `@src/routes/`[key]/+page.svelte:
- Around line 282-293: The null-safety issue is that `request` can be null so
accessing `request.options` may throw; update the guard in the options block to
use optional chaining (e.g. change the if to check `request?.options?.length >
0`) so the section only renders when `request` and `options` exist; keep the
inner `{`#each` request.options as option, i (i)}` since the outer guard
guarantees `options` is present.
- Around line 148-159: The template accesses request.release and request.tag but
request may be null (request is destructured from build with a null fallback),
so guard those accesses by either wrapping this block in an explicit {`#if`
request}...{/if} or use optional chaining when rendering (request?.release and
request?.tag) and update the tag check to {`#if` request?.tag} to prevent null
dereference; locate the references to request inside the {`#if` config} block in
+page.svelte and apply the guard or optional chaining consistently.

In `@src/routes/`[key]/+page.ts:
- Around line 281-284: The description assembly in +page.ts can throw when cloud
build fields are missing; update the logic that builds the description (the
variable `description`) to safely access `build.request`,
`build.request.options`, `build.status`, `build.submitted`, and `build.elapsed`
using optional chaining and nullish coalescing, only join options if
`Array.isArray(build.request?.options)` (fallback to a sensible string like
"none"), and provide default text for status/submitted/elapsed (e.g., "unknown"
or a formatted fallback) so the template never tries to call .join or formatTime
on undefined values.
- Around line 148-154: The locally constructed BuildRequest in the block that
sets build.request is missing required fields options and tag (BuildRequest)
which later causes addPreviousId to store PreviousId with undefined options and
breaks the Svelte each loop; update the build.request assignment (the object
created when !build?.request) to include defaults for options (e.g., an empty
array) and tag (e.g., empty string or a sensible default), so request.options is
always defined when passed into addPreviousId and consumed by the UI.
🧹 Nitpick comments (2)
src/lib/problems/definitions.ts (1)

282-293: Inconsistent optional chaining between lines 283 and 286.

Line 283 uses data.build?.request?.options (guarded), but line 286 drops the guard on request: data.build?.request.options.includes(...). This is safe at runtime because the early return on line 284 ensures request.options exists, but it's fragile—if someone reorders or removes the guard, line 286 would throw.

♻️ Suggested fix: consistent optional chaining
-			const includesNoncompliantSmartAudio = data.build?.request.options.includes(
+			const includesNoncompliantSmartAudio = buildOptions.includes(
 				"USE_NONCOMPLIANT_SMARTAUDIO"
 			)

This also avoids redundant property traversal since buildOptions already holds the same reference.

src/routes/[key]/+page.ts (1)

35-44: Local PreviousId type is out of sync with the store type.

The local type on lines 35-44 is missing the isLocal property that was added to the store type in src/lib/stores/previousIds.ts. Consider importing the type from the store instead of re-declaring it.

YarosMallorca and others added 3 commits February 13, 2026 17:38
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@YarosMallorca
Copy link
Contributor Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Feb 13, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

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

⚠️ Outside diff range comments (2)
src/routes/[key]/+page.svelte (1)

334-336: ⚠️ Potential issue | 🟡 Minor

Typo: "Tempreature" → "Temperature".

Pre-existing but visible to users.

Proposed fix
-							<span class="text-neutral-400 mr-1 text-base">Core Tempreature:</span>
+							<span class="text-neutral-400 mr-1 text-base">Core Temperature:</span>
src/routes/[key]/+page.ts (1)

35-44: ⚠️ Potential issue | 🟡 Minor

Local PreviousId type is missing the isLocal field.

The type defined at lines 35–44 is out of sync with the canonical type in src/lib/stores/previousIds.ts, which includes isLocal?: boolean. The addPreviousId function assigns isLocal to objects added to the store, yet the local type used to type items from that store (line 61) hides this field.

Add the field to keep types consistent:

Proposed fix
 type PreviousId = {
 	id: string
 	createdAt: number
 	manufacturer: string
 	target: string
 	version: string
 	problemDescription: string
 	options: string[]
 	armDisableFlags: string[]
+	isLocal?: boolean
 }
🤖 Fix all issues with AI agents
In `@src/routes/`[key]/+page.ts:
- Around line 248-257: The call to addPreviousId is occurring even when
build.config contains null fields; update the call site (where build is checked)
to validate critical fields (e.g., build.config.target and/or
build.config.manufacturer and build.config.mcu) are non-null before invoking
addPreviousId, or alternatively harden addPreviousId itself to early-return when
required config fields are missing (in addition to its existing !config ||
!request guard) so garbage entries are never stored; reference the addPreviousId
function and the build.config and build.request objects when making the change.
- Around line 136-156: The extractors (extractTarget, extractManufacturerId,
extractMcu, extractRelease) can return null and are being assigned directly into
build.config and build.request (used later in templates and addPreviousId), and
build.request.tag is set to null which conflicts with BuildRequest.tag:string;
update the assignments to guard against null by using nullish coalescing (e.g.,
default to '' or a sensible sentinel like 'unknown') when populating
build.config.target/manufacturer/mcu and build.request.release, and change
build.request.tag from null to an empty string to satisfy BuildRequest; ensure
types remain consistent so downstream code and templates never receive null.
🧹 Nitpick comments (2)
src/routes/[key]/+page.ts (1)

314-317: isLocal guard is unreachable on the build-key path.

The !build?.isLocal check on line 315 can never be false here because build.isLocal is only set in the support-key path (lines 138-146). For a direct build key (isBuildKey === true), the API response won't contain isLocal. The guard is harmless but misleading—and the same null-safety concern from the support path (accessing .options.join, .status, .submitted, .elapsed without checks) applies here too.

src/routes/[key]/+page.svelte (1)

93-104: build.isLocal accesses without optional chaining could throw if build is ever nullish.

Line 93 (build.isLocal), line 123 (!build.isLocal), line 162 (!build.isLocal), and line 179 (build.submitted) all access properties directly on build. While the current load function always returns a build object, a safer pattern would be build?.isLocal to guard against future regressions or unexpected API responses.

Proposed fix (line 93 example, apply similarly elsewhere)
-	{`#if` build.isLocal}
+	{`#if` build?.isLocal}

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