Skip to content
This repository was archived by the owner on Jan 23, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
packages/jumpstarter-driver-flashers/oci_bundles/rcar_s4/data/flasher.itb filter=lfs diff=lfs merge=lfs -text
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class FlashBundleSpecV1Alpha1(BaseModel):
targets: dict[str, str]
kernel: FileAddress
initram: Optional[FileAddress] = None
dtb: DtbVariant
dtb: Optional[DtbVariant] = None
preflash_commands: list[str] = Field(default_factory=list)


Expand All @@ -47,10 +47,15 @@ class FlasherBundleManifestV1Alpha1(BaseModel):
metadata: ObjectMeta
spec: FlashBundleSpecV1Alpha1

def get_dtb_address(self) -> str:
def get_dtb_address(self) -> str | None:
if not self.spec.dtb:
return None
return self.spec.dtb.address
Comment on lines +50 to 53
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify compatibility with existing callers.

The implementation correctly handles optional DTB with proper null checks. However, the return type change from str to str | None could break existing code.

From the relevant code snippet, driver.py line 186 shows a method that calls get_dtb_address() but still declares a str return type, creating a type mismatch.


🏁 Script executed:

#!/bin/bash
# Description: Find all callers of get_dtb_address to verify they handle None returns
# Expected: All callers should handle the new Optional return type

echo "=== Searching for get_dtb_address method calls ==="
rg -A 3 -B 1 "get_dtb_address\(\)" --type py

echo -e "\n=== Searching for get_dtb_address method definitions ==="
ast-grep --pattern 'def get_dtb_address($_) -> $ret_type:
  $$$'

Length of output: 1526


Handle Optional return of get_dtb_address in all callers

The change to return str | None from get_dtb_address introduces a breaking change: existing callers assume a non‐null string and will crash or misbehave if they receive None.

Please update the following locations:

  • packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/driver.py
    At the call in your get_kernel_address method (return manifest.get_dtb_address() around line 186):
    • Either guard against None before returning (and adjust the return type to str | None),
    • Or ensure dtb is always present so that a str is guaranteed.

  • packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py
    At the lines using dtb_address = manifest.get_dtb_address() followed by tftpboot {dtb_address} …:
    • Add a check for None and handle the absence of a DTB (e.g., throw a descriptive error or skip the tftpboot step).

Making these updates will restore type safety and prevent runtime failures when dtb is undefined.

🤖 Prompt for AI Agents
In packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/bundle.py
lines 50 to 53, the method get_dtb_address now returns str | None, which breaks
callers expecting only str. Update all callers accordingly: in driver.py around
line 186, modify get_kernel_address to handle the None case by either changing
its return type to str | None or ensuring dtb is always present; in client.py,
add checks after calling get_dtb_address to handle None safely, such as raising
an error or skipping tftpboot steps. These changes will maintain type safety and
prevent runtime errors.


def get_dtb_file(self, variant: str | None = None) -> str | None:
if not self.spec.dtb:
return None

if not variant:
variant = self.spec.dtb.default

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
EXPECT_TIMEOUT_DEFAULT = 60
EXPECT_TIMEOUT_SYNC = 1200


@dataclass(kw_only=True)
class BaseFlasherClient(FlasherClient, CompositeClient):
"""
Expand Down Expand Up @@ -123,6 +124,10 @@ def flash(
target_device = self._get_target_device(target, manifest, console)

self.logger.info(f"Using target block device: {target_device}")
console.sendline(f"export dhcp_addr={self._dhcp_details.ip_address}")
console.expect(manifest.spec.login.prompt, timeout=EXPECT_TIMEOUT_DEFAULT)
console.sendline(f"export gw_addr={self._dhcp_details.gateway}")
console.expect(manifest.spec.login.prompt, timeout=EXPECT_TIMEOUT_DEFAULT)
Comment thread
bennyz marked this conversation as resolved.

# Preflash commands are executed before the flash operation
# generally used to clean up boot entries in existing devices
Expand Down Expand Up @@ -412,15 +417,15 @@ def _busybox(self):

# if manifest has login details, we need to login
if manifest.spec.login.username:
console.expect(manifest.spec.login.login_prompt, timeout=EXPECT_TIMEOUT_DEFAULT*3)
console.expect(manifest.spec.login.login_prompt, timeout=EXPECT_TIMEOUT_DEFAULT * 3)
console.send(manifest.spec.login.username + "\n")

# if manifest has password, we need to send it
if manifest.spec.login.password:
console.expect("ssword:", timeout=EXPECT_TIMEOUT_DEFAULT)
console.send(manifest.spec.login.password + "\n")

console.expect(manifest.spec.login.prompt, timeout=EXPECT_TIMEOUT_DEFAULT*3)
console.expect(manifest.spec.login.prompt, timeout=EXPECT_TIMEOUT_DEFAULT * 3)
yield console

def use_dtb(self, path: PathBuf, operator: Operator | None = None):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,10 @@ async def setup_flasher_bundle(self, force_flash_bundle: str | None = None):
self.logger.info(f"Setting up initram in tftp: {initram_path}")
await self.tftp.storage.copy_exporter_file(initram_path, initram_path.name)

dtb_path = await self._get_file_path(manifest.get_dtb_file(self._use_dtb))
self.logger.info(f"Setting up dtb in tftp: {dtb_path}")
await self.tftp.storage.copy_exporter_file(dtb_path, dtb_path.name)
dtb_path = await self._get_file_path(manifest.get_dtb_file(self._use_dtb)) if manifest.spec.dtb else None
Comment thread
mangelajo marked this conversation as resolved.
if dtb_path:
self.logger.info(f"Setting up dtb in tftp: {dtb_path}")
await self.tftp.storage.copy_exporter_file(dtb_path, dtb_path.name)

@export
def set_dtb(self, handle):
Expand Down Expand Up @@ -202,3 +203,10 @@ class TIJ784S4Flasher(BaseFlasher):
"""driver for Jumpstarter"""

flasher_bundle: str = "quay.io/jumpstarter-dev/jumpstarter-flasher-ti-j784s4:latest"

Comment thread
bennyz marked this conversation as resolved.

@dataclass(kw_only=True)
class RCarS4Flasher(BaseFlasher):
"""RCarS4 driver for Jumpstarter"""

flasher_bundle: str = "quay.io/jumpstarter-dev/jumpstarter-flasher-rcar-s4:latest"
Git LFS file not shown
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
apiVersion: jumpstarter.dev/v1alpha1
kind: FlashBundleManifest
metadata:
name: rcar-s4
spec:
manufacturer: Renesas
link: "https://www.renesas.com/en/products/automotive-products/automotive-system-chips-socs/r-car-s4-automotive-system-chip-soc-car-servercommunication-gateway"
bootcmd: "bootm 0x58000000"
shelltype: "busybox"
login:
login_prompt: "login:"
username: "root"
prompt: "#"
default_target: "emmc"
targets:
emmc: "/dev/mmcblk0"
kernel:
file: data/flasher.itb
address: "0x58000000"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: this is the address bootm uses by deafult, but if you change that (for other boards perhaps) then "bootm" without argumetns won't work. so maybe make it more explicit setting bootcmd to "bootm $0x58000000"

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I assume it's bootm 0x58000000 or bootm $loadddr

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

per doc without an argument is uses the last tftpboot loaded address. so it's fine as is, though it depends on the order of loading things

Original file line number Diff line number Diff line change
@@ -1,15 +1,31 @@
from dataclasses import dataclass

import click
Comment thread
mangelajo marked this conversation as resolved.
from jumpstarter_driver_power.client import PowerClient

from jumpstarter.client import DriverClient


@dataclass(kw_only=True)
class DigitalOutputClient(DriverClient):
def off(self) -> None:
class DigitalOutputClient(PowerClient):
def on(self):
"""Turn power on"""
self.call("on")

def off(self):
Comment thread
bennyz marked this conversation as resolved.
Comment thread
mangelajo marked this conversation as resolved.
"""Turn power off"""
self.call("off")

def on(self) -> None:
self.call("on")
def cli(self):
Comment thread
bennyz marked this conversation as resolved.
@click.group()
def gpio():
"""GPIO power control commands"""
pass

for cmd in super().cli().commands.values():
gpio.add_command(cmd)

return gpio


@dataclass(kw_only=True)
Expand Down
2 changes: 1 addition & 1 deletion packages/jumpstarter-driver-raspberrypi/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ authors = [
readme = "README.md"
license = "Apache-2.0"
requires-python = ">=3.11"
dependencies = ["jumpstarter", "gpiozero>=2.0.1"]
dependencies = ["jumpstarter", "jumpstarter-driver-power", "gpiozero>=2.0.1", "click>=8.1.7.2"]

[project.entry-points."jumpstarter.drivers"]
DigitalInput = "jumpstarter_driver_raspberrypi.driver:DigitalInput"
Expand Down
Loading