Skip to content
This repository was archived by the owner on Jan 23, 2026. It is now read-only.

flashers: support flashing from OCI registries#795

Merged
mangelajo merged 3 commits intojumpstarter-dev:mainfrom
bennyz:fls-custom
Jan 13, 2026
Merged

flashers: support flashing from OCI registries#795
mangelajo merged 3 commits intojumpstarter-dev:mainfrom
bennyz:fls-custom

Conversation

@bennyz
Copy link
Member

@bennyz bennyz commented Jan 11, 2026

Summary by CodeRabbit

  • New Features

    • Added a --fls-binary-url CLI option to download a custom FLS binary (overrides versioned binary)
    • OCI URLs are now treated as direct flash targets
  • Bug Fixes / Enhancements

    • New URL-based binary download path with improved error handling and retry behavior
    • Filename derivation, progress reporting and output formatting updated to support OCI/URL flows
    • Default behavior preserved when no custom URL is provided

✏️ Tip: You can customize this high-level summary in your review settings.

@netlify
Copy link

netlify bot commented Jan 11, 2026

Deploy Preview for jumpstarter-docs ready!

Name Link
🔨 Latest commit ecf0ad9
🔍 Latest deploy log https://app.netlify.com/projects/jumpstarter-docs/deploys/6964cdf1c3152000081a9706
😎 Deploy Preview https://deploy-preview-795--jumpstarter-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 11, 2026

📝 Walkthrough

Walkthrough

Adds a new fls_binary_url parameter and CLI option --fls-binary-url, passes it through the flash orchestration, treats OCI (oci://) URLs as direct FLS targets, and implements URL-based FLS binary download, permissioning, and execution while preserving existing fls_version/default flows. (49 words)

Changes

Cohort / File(s) Summary
Core flash flow & FLS URL support
packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py
Added fls_binary_url param to flash(), _perform_flash_operation(), and _flash_with_fls(). Implemented _download_fls_binary path to fetch a provided URL, chmod +x, and execute; retained fls_version and system-binary branches.
Filename & OCI handling
packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py
Updated _filename logic to recognize oci:// sources and derive artifact names; treat OCI URLs as direct FLS targets bypassing HTTP downloader.
CLI wiring & options
packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py
Added --fls-binary-url CLI option and propagated fls_binary_url through the flash command into internal calls.
Error handling & progress paths
packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py
Enhanced exception handling for download failures (uses retryable error), adjusted progress/output formatting, and integrated download-failure paths into existing backoff/retry logic.

Sequence Diagram(s)

sequenceDiagram
    participant User as CLI/User
    participant Client as BaseFlasherClient.flash()
    participant Orchestrator as _perform_flash_operation()
    participant FLSFlow as _flash_with_fls()
    participant Downloader as HTTP Downloader / curl
    participant System as Filesystem / Exec

    User->>Client: flash(path, fls_binary_url=<url>)
    Client->>Orchestrator: _perform_flash_operation(..., fls_binary_url)
    Orchestrator->>FLSFlow: _flash_with_fls(..., fls_binary_url)

    alt fls_binary_url provided
        FLSFlow->>Downloader: curl -L <fls_binary_url> -o /sbin/fls
        Downloader-->>FLSFlow: download result (exit code)
        FLSFlow->>System: chmod +x /sbin/fls
        FLSFlow->>System: execute /sbin/fls to flash
    else fls_version provided
        FLSFlow->>Downloader: download versioned fls binary (GitHub/registry)
        Downloader-->>FLSFlow: downloaded binary
        FLSFlow->>System: execute versioned binary to flash
    else default
        FLSFlow->>System: use system fls binary to flash
    end

    System-->>Orchestrator: flash result
    Orchestrator-->>Client: operation result
    Client-->>User: report completion
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

  • jumpstarter-dev/jumpstarter#735: Modifies the same FLS-based flash flow and CLI wiring; likely predecessor or closely related change expanding FLS handling.

Poem

🐰 I found a URL, shiny and new,
I hopped and I fetched an FLS for you.
OCI lanes I skipped with cheer,
CLI flag waved — the binary's here.
Flash done, I nibble carrots near. 🥕✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main feature addition: support for flashing from OCI registries, which is the primary change reflected in the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 85.71% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing touches
  • 📝 Generate docstrings

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
Contributor

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

🤖 Fix all issues with AI agents
In @packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py:
- Around line 503-515: Add integrity and authenticity checks after downloading
when fls_binary_url is used: require/validate HTTPS URLs, fetch a provided
SHA256 checksum (or accept a user-supplied checksum arg) and verify it against
the downloaded /sbin/fls (e.g., run sha256sum and compare exit/status), perform
a basic ELF validation (e.g., run file /sbin/fls and ensure it reports ELF,
checking the exit code via console and using EXPECT_TIMEOUT_DEFAULT and prompt
as done), and raise FlashNonRetryableError for validation failures while keeping
FlashRetryableError for network/download failures; ensure you run chmod +x only
after successful checks and surface a CLI help warning about security
implications of custom fls_binary_url.
🧹 Nitpick comments (4)
packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py (4)

104-104: Add validation for fls_binary_url parameter.

The fls_binary_url parameter accepts arbitrary URLs without validation. Consider validating the URL format and potentially restricting allowed schemes (e.g., https:// only for security) to prevent misuse or configuration errors.

🔒 Proposed validation logic

Add validation after line 110:

if fls_binary_url:
    parsed = urlparse(fls_binary_url)
    if not parsed.scheme or not parsed.netloc:
        raise ArgumentError(f"Invalid fls_binary_url: {fls_binary_url}")
    if parsed.scheme not in ("http", "https"):
        raise ArgumentError(f"fls_binary_url must use http or https scheme, got: {parsed.scheme}")

117-120: Consider validating that OCI URLs are only used with the fls method.

OCI URLs are passed directly to the FLS tool. If the method parameter is set to "shell", this could lead to unexpected behavior or errors. Consider adding validation to ensure OCI URLs are only accepted when method == "fls".

✅ Proposed validation

Add validation after determining the URL type:

if path.startswith("oci://"):
    # OCI URLs are always passed directly to fls
    if method != "fls":
        raise ArgumentError("OCI URLs can only be used with method='fls'")
    image_url = path
    should_download_to_httpd = False

1205-1209: Consider enhancing the help text with security guidance.

The --fls-binary-url option allows downloading arbitrary binaries. Consider adding a security note to the help text to warn users about the implications.

📝 Enhanced help text
 @click.option(
     "--fls-binary-url",
     type=str,
-    help="Custom URL to download FLS binary from (overrides --fls-version)",
+    help="Custom URL to download FLS binary from (overrides --fls-version). "
+         "WARNING: Only use trusted sources. The binary will be executed on the target device.",
 )

955-963: Consider improving OCI URL filename extraction robustness.

The current implementation extracts only the last path segment as the repository name, which can cause collisions when multiple registries have repos with identical names (e.g., oci://r1/org/repo:v1 and oci://r2/org/repo:v1 both produce repo-v1). While the TODO comment at line 973 indicates awareness of hash-checking for collision detection, the lack of OCI URL validation and limited namespace handling means malformed URLs silently degrade.

To improve robustness, consider: (1) using a more complete OCI reference that includes registry information, or (2) adding format validation to catch malformed OCI URLs early. The current pragmatic approach works for typical single-registry usage patterns, but these enhancements would strengthen multi-registry scenarios.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ac364c4 and 1bec504.

📒 Files selected for processing (1)
  • packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py
🧰 Additional context used
📓 Path-based instructions (3)
packages/jumpstarter-driver-*/jumpstarter_driver_*/client.py

📄 CodeRabbit inference engine (.cursor/rules/creating-new-drivers.mdc)

Driver client CLIs should either implement known classes that provide a CLI interface, or implement their own CLI interface. Use CompositeClient from jumpstarter_driver_composite.client for composite drivers with child drivers.

Files:

  • packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py
packages/jumpstarter-driver-**/jumpstarter_driver_**/*.py

📄 CodeRabbit inference engine (.cursor/rules/creating-new-drivers.mdc)

Driver implementations should follow existing code style validated with make lint (fix with make lint-fix), perform static type checking with make ty-pkg-${package_name}, add comprehensive tests, and verify all tests pass with make test-pkg-${package_name} or make test

Files:

  • packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py
**/*.py

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

Ruff should be used for code formatting and linting, excluding jumpstarter-protocol package

Files:

  • packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py
🧠 Learnings (2)
📚 Learning: 2025-11-05T13:31:39.304Z
Learnt from: mangelajo
Repo: jumpstarter-dev/jumpstarter PR: 735
File: packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py:100-101
Timestamp: 2025-11-05T13:31:39.304Z
Learning: In packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py, the `flash()` method and its CLI command intentionally have different defaults for `fls_version`: the method defaults to `""` (empty string) while the CLI defaults to `"0.1.5"`. This is by design to provide CLI users with a convenient default while keeping the programmatic API ready for when FLS is included in the flasher images.

Applied to files:

  • packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py
📚 Learning: 2025-11-05T13:33:24.741Z
Learnt from: mangelajo
Repo: jumpstarter-dev/jumpstarter PR: 735
File: packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py:476-486
Timestamp: 2025-11-05T13:33:24.741Z
Learning: In packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py, the FLS flasher download in _flash_with_fls() hardcodes "aarch64-linux" architecture because the flasher always targets ARM devices currently.

Applied to files:

  • packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: Redirect rules - jumpstarter-docs
  • GitHub Check: Header rules - jumpstarter-docs
  • GitHub Check: Pages changed - jumpstarter-docs
  • GitHub Check: build
  • GitHub Check: pytest-matrix (macos-15, 3.13)
  • GitHub Check: pytest-matrix (macos-15, 3.11)
  • GitHub Check: pytest-matrix (macos-15, 3.12)
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.13)
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.12)
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.11)
  • GitHub Check: e2e
🔇 Additional comments (6)
packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py (6)

179-191: LGTM - Improved parameter formatting.

The parameters are now formatted one per line, which improves readability. The new fls_binary_url parameter is correctly propagated to the flash operation.


278-299: LGTM - More defensive attribute access.

Using hasattr() and getattr() instead of direct attribute access improves robustness when traversing exception chains, preventing potential AttributeError exceptions.


503-530: LGTM - Correct precedence for FLS binary source.

The fls_binary_url parameter correctly takes precedence over fls_version, with proper fallback logic. The use of elif ensures mutual exclusivity between the two approaches.


619-624: LGTM - Improved command readability.

Breaking the long flash command across multiple lines improves readability without changing functionality.


1226-1251: LGTM - Parameter correctly propagated.

The fls_binary_url parameter is correctly passed from the CLI handler to the flash() method.


1202-1202: Ensure FLS version 0.1.9 is available before merging.

Verify that version 0.1.9 exists as a release in the jumpstarter-dev/fls repository. If this version is not yet released, the CLI will fail with a download error when attempting to flash without specifying a custom URL. Consider either confirming the release exists or updating the default to a known available version.

@mangelajo
Copy link
Member

Nice, I was thinking that we would need this after checking the fls patch, :)

Comment on lines 504 to 515
self.logger.info(f"Downloading FLS binary from custom URL: {fls_binary_url}")
console.sendline(f"curl -L {fls_binary_url} -o /sbin/fls")
console.expect(prompt, timeout=EXPECT_TIMEOUT_DEFAULT)
console.sendline("echo $?")
console.expect(prompt, timeout=EXPECT_TIMEOUT_DEFAULT)

exit_code = int(console.before.decode(errors="ignore").strip().splitlines()[-1])

if exit_code != 0:
raise FlashRetryableError(f"Failed to download FLS from {fls_binary_url}, exit code: {exit_code}")
console.sendline("chmod +x /sbin/fls")
console.expect(prompt, timeout=EXPECT_TIMEOUT_DEFAULT)
Copy link
Member

Choose a reason for hiding this comment

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

This is duplicating lines 520 to 530 now, I would extract that to a function to avoid duplicated code.

Copy link
Member Author

Choose a reason for hiding this comment

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

ack

Copy link
Member

@mangelajo mangelajo left a comment

Choose a reason for hiding this comment

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

Just a nit

useful for debugging

Signed-off-by: Benny Zlotnik <bzlotnik@redhat.com>
Accept the oci:// scheme for flashing images from the registry

Signed-off-by: Benny Zlotnik <bzlotnik@redhat.com>
Copy link
Contributor

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

🧹 Nitpick comments (1)
packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py (1)

483-493: Add defensive error handling for exit code parsing.

The exit code extraction on line 488 could raise IndexError (if splitlines() returns empty) or ValueError (if the last line isn't a valid integer) in edge cases with corrupted console output.

Suggested improvement
         console.sendline(f"curl -L {download_url} -o /sbin/fls")
         console.expect(prompt, timeout=EXPECT_TIMEOUT_DEFAULT)
         console.sendline("echo $?")
         console.expect(prompt, timeout=EXPECT_TIMEOUT_DEFAULT)
 
-        exit_code = int(console.before.decode(errors="ignore").strip().splitlines()[-1])
-
-        if exit_code != 0:
+        try:
+            lines = console.before.decode(errors="ignore").strip().splitlines()
+            exit_code = int(lines[-1]) if lines else -1
+        except (IndexError, ValueError) as e:
+            raise FlashRetryableError(f"{error_message_prefix}, failed to parse exit code") from e
+ 
+        if exit_code != 0:
             raise FlashRetryableError(f"{error_message_prefix}, exit code: {exit_code}")
         console.sendline("chmod +x /sbin/fls")
         console.expect(prompt, timeout=EXPECT_TIMEOUT_DEFAULT)
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1bec504 and b400c1d.

📒 Files selected for processing (1)
  • packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py
🧰 Additional context used
📓 Path-based instructions (3)
packages/jumpstarter-driver-*/jumpstarter_driver_*/client.py

📄 CodeRabbit inference engine (.cursor/rules/creating-new-drivers.mdc)

Driver client CLIs should either implement known classes that provide a CLI interface, or implement their own CLI interface. Use CompositeClient from jumpstarter_driver_composite.client for composite drivers with child drivers.

Files:

  • packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py
packages/jumpstarter-driver-**/jumpstarter_driver_**/*.py

📄 CodeRabbit inference engine (.cursor/rules/creating-new-drivers.mdc)

Driver implementations should follow existing code style validated with make lint (fix with make lint-fix), perform static type checking with make ty-pkg-${package_name}, add comprehensive tests, and verify all tests pass with make test-pkg-${package_name} or make test

Files:

  • packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py
**/*.py

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

Ruff should be used for code formatting and linting, excluding jumpstarter-protocol package

Files:

  • packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py
🧠 Learnings (3)
📚 Learning: 2025-11-05T13:31:39.304Z
Learnt from: mangelajo
Repo: jumpstarter-dev/jumpstarter PR: 735
File: packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py:100-101
Timestamp: 2025-11-05T13:31:39.304Z
Learning: In packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py, the `flash()` method and its CLI command intentionally have different defaults for `fls_version`: the method defaults to `""` (empty string) while the CLI defaults to `"0.1.5"`. This is by design to provide CLI users with a convenient default while keeping the programmatic API ready for when FLS is included in the flasher images.

Applied to files:

  • packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py
📚 Learning: 2025-11-05T13:33:24.741Z
Learnt from: mangelajo
Repo: jumpstarter-dev/jumpstarter PR: 735
File: packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py:476-486
Timestamp: 2025-11-05T13:33:24.741Z
Learning: In packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py, the FLS flasher download in _flash_with_fls() hardcodes "aarch64-linux" architecture because the flasher always targets ARM devices currently.

Applied to files:

  • packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py
📚 Learning: 2025-03-14T15:52:49.790Z
Learnt from: mangelajo
Repo: jumpstarter-dev/jumpstarter PR: 339
File: packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py:172-172
Timestamp: 2025-03-14T15:52:49.790Z
Learning: Shell command sanitization (using shlex.quote or similar) is not required in the flasher driver context because it operates on devices where full control is already assumed, as the code is flashing the entire disk.

Applied to files:

  • packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: Redirect rules - jumpstarter-docs
  • GitHub Check: Header rules - jumpstarter-docs
  • GitHub Check: build
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.12)
  • GitHub Check: pytest-matrix (macos-15, 3.12)
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.11)
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.13)
  • GitHub Check: e2e
  • GitHub Check: Pages changed - jumpstarter-docs
🔇 Additional comments (6)
packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py (6)

117-124: LGTM!

The OCI URL detection and bypass logic is correct. OCI URLs are properly passed directly to fls without attempting HTTP download.


527-534: LGTM!

The logic correctly prioritizes fls_binary_url over fls_version, and the refactoring to use _download_fls_binary helper reduces code duplication. Based on learnings, the hardcoded aarch64-linux architecture is intentional since the flasher always targets ARM devices.


959-971: LGTM!

The OCI URL parsing correctly uses rsplit(":", 1) to handle registry ports (e.g., registry:5000/repo:tag) and extracts meaningful filename components.


1242-1255: LGTM!

The fls_binary_url parameter is correctly propagated from CLI to the flash() method call.


104-104: LGTM!

The fls_binary_url parameter is consistently added across all method signatures in the call chain: flash()_perform_flash_operation()_flash_with_fls().

Also applies to: 316-316, 506-506


1203-1213: CLI option addition is well-integrated; FLS version verification incomplete.

The new --fls-binary-url option integrates correctly with parameter propagation, and the help text accurately indicates it overrides --fls-version. However, the FLS version 0.1.9 cannot be verified from public sources—the jumpstarter-dev/fls repository is not publicly accessible. This aligns with the TODO comment in the code indicating the version default should change once FLS is included in images.

Signed-off-by: Benny Zlotnik <bzlotnik@redhat.com>
Copy link
Contributor

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

🧹 Nitpick comments (3)
packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py (3)

471-497: Consider adding TLS arguments and timeout for custom URL downloads.

The _download_fls_binary method doesn't pass TLS arguments (insecure_tls, stored_cacert) or a timeout to curl. If fls_binary_url points to an HTTPS endpoint with a custom CA or requires insecure mode, the download will fail. Additionally, without a timeout, curl could hang indefinitely.

♻️ Suggested improvement
-    def _download_fls_binary(self, console, prompt: str, download_url: str, error_message_prefix: str):
+    def _download_fls_binary(
+        self, console, prompt: str, download_url: str, error_message_prefix: str, tls_args: str = ""
+    ):
         """Download FLS binary to the target device.

         Args:
             console: Console object for device interaction
             prompt: Login prompt for console interaction
             download_url: URL to download the FLS binary from
             error_message_prefix: Prefix for error message if download fails
+            tls_args: TLS arguments for curl command

         Raises:
             FlashRetryableError: If download fails or binary cannot be made executable
         """
-        console.sendline(f"curl -L {download_url} -o /sbin/fls")
+        console.sendline(f"curl -L --max-time 120 {tls_args} {download_url} -o /sbin/fls")
         console.expect(prompt, timeout=EXPECT_TIMEOUT_DEFAULT)

Then update the calls in _flash_with_fls to pass tls_args:

         if fls_binary_url:
             self.logger.info(f"Downloading FLS binary from custom URL: {fls_binary_url}")
-            self._download_fls_binary(console, prompt, fls_binary_url, f"Failed to download FLS from {fls_binary_url}")
+            self._download_fls_binary(console, prompt, fls_binary_url, f"Failed to download FLS from {fls_binary_url}", tls_args)
         elif fls_version != "":
             self.logger.info(f"Downloading FLS version {fls_version} from GitHub releases")
             fls_url = f"https://github.com/jumpstarter-dev/fls/releases/download/{fls_version}/fls-aarch64-linux"
-            self._download_fls_binary(console, prompt, fls_url, f"Failed to download FLS from {fls_url}")
+            self._download_fls_binary(console, prompt, fls_url, f"Failed to download FLS from {fls_url}", tls_args)

961-975: Consider handling OCI digest references.

The OCI URL parsing handles tags (:tag) but may not correctly handle digest references (@sha256:...). If digest-based references are used, the current logic would include the digest in the filename, which could be problematic.

♻️ Optional enhancement for digest support
     def _filename(self, path: PathBuf) -> str:
         """Extract filename from url or path"""
         if path.startswith("oci://"):
             oci_path = path[6:]  # Remove "oci://" prefix
+            # Handle digest references (e.g., @sha256:abc123)
+            if "@" in oci_path:
+                repository, digest = oci_path.rsplit("@", 1)
+                repo_name = repository.split("/")[-1] if "/" in repository else repository
+                # Use first 12 chars of digest for brevity
+                digest_short = digest.replace(":", "-")[:19]  # sha256-abc123...
+                return f"{repo_name}-{digest_short}"
             if ":" in oci_path:
                 repository, tag = oci_path.rsplit(":", 1)
                 repo_name = repository.split("/")[-1] if "/" in repository else repository
                 return f"{repo_name}-{tag}"
             else:
                 repo_name = oci_path.split("/")[-1] if "/" in oci_path else oci_path
                 return repo_name

88-112: Docstring placement and documentation.

The docstring """Flash image to DUT""" at line 112 is placed after the function body has already started (after parameter validation). Additionally, the new fls_binary_url parameter is not documented in the docstring.

♻️ Move docstring to correct position
     def flash(  # noqa: C901
         self,
         path: PathBuf,
         *,
         partition: str | None = None,
         ...
         fls_binary_url: str | None = None,
     ):
+        """Flash image to DUT.
+
+        Args:
+            fls_binary_url: Custom URL to download FLS binary from (overrides fls_version)
+        """
         if bearer_token:
             bearer_token = self._validate_bearer_token(bearer_token)

         if headers:
             headers = self._validate_header_dict(headers)

-        """Flash image to DUT"""
         should_download_to_httpd = True
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b400c1d and ecf0ad9.

📒 Files selected for processing (1)
  • packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py
🧰 Additional context used
📓 Path-based instructions (3)
packages/jumpstarter-driver-*/jumpstarter_driver_*/client.py

📄 CodeRabbit inference engine (.cursor/rules/creating-new-drivers.mdc)

Driver client CLIs should either implement known classes that provide a CLI interface, or implement their own CLI interface. Use CompositeClient from jumpstarter_driver_composite.client for composite drivers with child drivers.

Files:

  • packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py
packages/jumpstarter-driver-**/jumpstarter_driver_**/*.py

📄 CodeRabbit inference engine (.cursor/rules/creating-new-drivers.mdc)

Driver implementations should follow existing code style validated with make lint (fix with make lint-fix), perform static type checking with make ty-pkg-${package_name}, add comprehensive tests, and verify all tests pass with make test-pkg-${package_name} or make test

Files:

  • packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py
**/*.py

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

Ruff should be used for code formatting and linting, excluding jumpstarter-protocol package

Files:

  • packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py
🧠 Learnings (3)
📚 Learning: 2025-11-05T13:31:39.304Z
Learnt from: mangelajo
Repo: jumpstarter-dev/jumpstarter PR: 735
File: packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py:100-101
Timestamp: 2025-11-05T13:31:39.304Z
Learning: In packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py, the `flash()` method and its CLI command intentionally have different defaults for `fls_version`: the method defaults to `""` (empty string) while the CLI defaults to `"0.1.5"`. This is by design to provide CLI users with a convenient default while keeping the programmatic API ready for when FLS is included in the flasher images.

Applied to files:

  • packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py
📚 Learning: 2025-11-05T13:33:24.741Z
Learnt from: mangelajo
Repo: jumpstarter-dev/jumpstarter PR: 735
File: packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py:476-486
Timestamp: 2025-11-05T13:33:24.741Z
Learning: In packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py, the FLS flasher download in _flash_with_fls() hardcodes "aarch64-linux" architecture because the flasher always targets ARM devices currently.

Applied to files:

  • packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py
📚 Learning: 2025-03-14T15:52:49.790Z
Learnt from: mangelajo
Repo: jumpstarter-dev/jumpstarter PR: 339
File: packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py:172-172
Timestamp: 2025-03-14T15:52:49.790Z
Learning: Shell command sanitization (using shlex.quote or similar) is not required in the flasher driver context because it operates on devices where full control is already assumed, as the code is flashing the entire disk.

Applied to files:

  • packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: pytest-matrix (macos-15, 3.13)
  • GitHub Check: build
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.12)
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.13)
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.11)
  • GitHub Check: pytest-matrix (macos-15, 3.12)
  • GitHub Check: e2e
🔇 Additional comments (6)
packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py (6)

117-124: LGTM!

The OCI URL handling logic is correct. OCI URLs bypass the HTTP downloader and are passed directly to fls, which is the expected behavior for registry-based images.


531-538: LGTM!

The conditional logic correctly prioritizes fls_binary_url over fls_version, maintaining backward compatibility while enabling the new feature.


1213-1217: LGTM!

The CLI option is well-defined with clear help text indicating it overrides --fls-version. Parameter propagation through the call chain is complete.


178-192: LGTM!

The parameter list formatting and addition of fls_binary_url to _perform_flash_operation call is correct and maintains readability.


302-316: LGTM!

The _perform_flash_operation signature update correctly propagates the new fls_binary_url parameter through the internal call chain.


1207-1212: FLS version 0.1.9 exists in the GitHub releases and is valid for use.

@bennyz bennyz requested a review from mangelajo January 12, 2026 11:14
@mangelajo mangelajo merged commit cea4061 into jumpstarter-dev:main Jan 13, 2026
18 checks passed
@bennyz bennyz deleted the fls-custom branch January 13, 2026 09:52
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants