Skip to main content

OtaClient

Struct OtaClient 

Source
pub struct OtaClient {
    github: GithubClient,
    tmp_dir: PathBuf,
}
Expand description

Downloads and deploys OTA updates from GitHub.

Delegates all HTTP communication to GithubClient and focuses solely on the OTA-specific workflow: finding artifacts, chunked downloading, ZIP extraction, and deploying KoboRoot.tgz to the Kobo device.

Fields§

§github: GithubClient§tmp_dir: PathBuf

Implementations§

Source§

impl OtaClient

Source

pub fn new(github: GithubClient, tmp_dir: PathBuf) -> Self

Creates a new OTA client wrapping the provided GitHub client.

§Errors

Returns OtaError::TlsConfig if the underlying HTTP client fails to build.

Source

pub fn download_pr_artifact<F>( &self, pr_number: u32, progress_callback: F, ) -> Result<PathBuf, OtaError>
where F: FnMut(OtaProgress),

Downloads the build artifact from a GitHub pull request.

This performs the complete download workflow:

  1. Verifies sufficient disk space (100MB required)
  2. Fetches PR metadata to get the commit SHA
  3. Finds the associated “Cargo” workflow run
  4. Locates artifacts matching “cadmus-kobo-pr*” pattern
  5. Downloads the artifact ZIP file to tmp_dir/cadmus-ota-{pr_number}.zip

GitHub authentication is required for this operation.

§Arguments
  • pr_number - The pull request number from ogkevin/cadmus repository
  • progress_callback - Function called with progress updates during download
§Returns

The path to the downloaded ZIP file on success.

§Errors
  • OtaError::InsufficientSpace - Less than 100MB available in the configured temp directory
  • OtaError::NoToken - GitHub token not configured
  • OtaError::PrNotFound - PR number doesn’t exist in repository
  • OtaError::ArtifactsNotFound - No matching build artifacts found for the PR
  • OtaError::Api - GitHub API request failed
  • OtaError::Request - Network communication failed
  • OtaError::Io - Failed to write downloaded file to disk
Source

pub fn download_default_branch_artifact<F>( &self, progress_callback: F, ) -> Result<PathBuf, OtaError>
where F: FnMut(OtaProgress),

Downloads the latest build artifact from the default branch.

This performs the complete download workflow for default branch builds:

  1. Verifies sufficient disk space (100MB required)
  2. Queries GitHub API for the latest successful cargo.yml workflow run on the default branch
  3. Locates artifacts matching “cadmus-kobo-{sha}” pattern (or “cadmus-kobo-test-{sha}” with test feature)
  4. Downloads the artifact ZIP file to tmp_dir/cadmus-ota-{sha}.zip

GitHub authentication is required for this operation.

§Arguments
  • progress_callback - Function called with progress updates during download
§Returns

The path to the downloaded ZIP file on success.

§Errors
  • OtaError::InsufficientSpace - Less than 100MB available in the configured temp directory
  • OtaError::NoToken - GitHub token not configured
  • OtaError::ArtifactsNotFound - No matching build artifacts found for default branch
  • OtaError::Api - GitHub API request failed
  • OtaError::Request - Network communication failed
  • OtaError::Io - Failed to write downloaded file to disk
Source

pub fn download_stable_release_artifact<F>( &self, progress_callback: F, ) -> Result<PathBuf, OtaError>
where F: FnMut(OtaProgress),

Downloads the latest stable release artifact from GitHub releases.

This performs the complete download workflow for stable releases:

  1. Verifies sufficient disk space (100MB required)
  2. Fetches the latest release from GitHub API
  3. Locates the KoboRoot.tgz asset in the release
  4. Downloads the file to tmp_dir/cadmus-ota-stable-release.tgz

GitHub authentication is not required for this operation as release assets are downloaded from public URLs without Authorization headers.

§Arguments
  • progress_callback - Function called with progress updates during download
§Returns

The path to the downloaded KoboRoot.tgz file on success.

§Errors
  • OtaError::InsufficientSpace - Less than 100MB available in the configured temp directory
  • OtaError::Api - GitHub API request failed
  • OtaError::Request - Network communication failed
  • OtaError::ArtifactsNotFound - KoboRoot.tgz not found in latest release
  • OtaError::Io - Failed to write downloaded file to disk
Source

pub fn fetch_latest_release_version(&self) -> Result<GitVersion, OtaError>

Fetches the latest stable release version from GitHub.

Retrieves and parses the version from the most recent stable release. Returns the version as a GitVersion struct for easy comparison and display.

GitHub authentication is not required for this operation as releases are public.

§Errors
  • OtaError::Api - GitHub API request failed
  • OtaError::Request - Network communication failed
  • OtaError::VersionParse - Failed to parse the release tag as a valid version
§Example
use cadmus_core::github::GithubClient;
use cadmus_core::ota::OtaClient;

let version = client.fetch_latest_release_version()?;
println!("Latest version: {}", version);
Source

pub fn deploy(&self, kobo_root_path: PathBuf) -> Result<PathBuf, OtaError>

Deploys KoboRoot.tgz from the specified path directly without extraction.

Used when the artifact is already in the correct format (e.g., stable releases that are distributed as bare KoboRoot.tgz files).

On success, the source file is deleted as a best-effort cleanup step.

§Arguments
  • kobo_root_path - Path to the KoboRoot.tgz file to deploy
§Returns

The path where the file was deployed, or an error if deployment fails.

§Errors
  • OtaError::Io - Failed to read or write files
Source

fn deploy_path(&self) -> PathBuf

Returns the platform-specific deployment path for KoboRoot.tgz.

Build contextPath
During cargo test<temp_dir>/test-kobo-deployment/KoboRoot.tgz
Emulator builds/tmp/.kobo/KoboRoot.tgz
Kobo builds{INTERNAL_CARD_ROOT}/.kobo/KoboRoot.tgz
Source

fn ensure_deploy_dir(&self, deploy_path: &Path) -> Result<(), OtaError>

Source

fn deploy_bytes(&self, data: &[u8]) -> Result<PathBuf, OtaError>

Source

pub fn extract_and_deploy(&self, zip_path: PathBuf) -> Result<PathBuf, OtaError>

Extracts KoboRoot.tgz from the artifact and deploys it for installation.

Opens the downloaded ZIP archive, locates the KoboRoot.tgz file, extracts it, and writes it to /mnt/onboard/.kobo/KoboRoot.tgz where the Kobo device will automatically install it on next reboot. On success, the source artifact ZIP is deleted as a best-effort cleanup step.

§Arguments
  • zip_path - Path to the downloaded artifact ZIP file
§Returns

The deployment path where KoboRoot.tgz was written.

§Errors
  • OtaError::ZipError - Failed to open or read ZIP archive
  • OtaError::DeploymentError - KoboRoot.tgz not found in archive
  • OtaError::Io - Failed to write deployment file
Source

fn fetch_default_branch(&self) -> Result<String, OtaError>

Queries the GitHub API for the repository’s default branch name.

Source

fn find_artifact_in_run( &self, run_id: u64, name_prefix: &str, ) -> Result<Artifact, OtaError>

Fetches artifacts for a workflow run and finds one matching the given prefix.

Source

fn download_artifact_to_path<F>( &self, artifact: &Artifact, download_path: &PathBuf, progress_callback: &mut F, ) -> Result<(), OtaError>
where F: FnMut(OtaProgress),

Downloads an artifact ZIP to the specified path with chunked transfer and progress reporting.

GitHub authentication is required for this operation.

Source

fn download_release_asset<F>( &self, asset: &ReleaseAsset, download_path: &PathBuf, progress_callback: &mut F, ) -> Result<(), OtaError>
where F: FnMut(OtaProgress),

Downloads a release asset to the specified path with chunked transfer and progress reporting.

GitHub authentication is not required for this operation as release assets are downloaded from public URLs.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<T> Downcast for T
where T: Any,

§

fn into_any(self: Box<T>) -> Box<dyn Any>

Converts Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.
§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

Converts Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further downcast into Rc<ConcreteType> where ConcreteType implements Trait.
§

fn as_any(&self) -> &(dyn Any + 'static)

Converts &Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &Any’s vtable from &Trait’s.
§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

Converts &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
§

impl<T> DowncastSend for T
where T: Any + Send,

§

fn into_any_send(self: Box<T>) -> Box<dyn Any + Send>

Converts Box<Trait> (where Trait: DowncastSend) to Box<dyn Any + Send>, which can then be downcast into Box<ConcreteType> where ConcreteType implements Trait.
§

impl<T> DowncastSync for T
where T: Any + Send + Sync,

§

fn into_any_sync(self: Box<T>) -> Box<dyn Any + Sync + Send>

Converts Box<Trait> (where Trait: DowncastSync) to Box<dyn Any + Send + Sync>, which can then be downcast into Box<ConcreteType> where ConcreteType implements Trait.
§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Sync + Send>

Converts Arc<Trait> (where Trait: DowncastSync) to Arc<Any>, which can then be downcast into Arc<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
§

impl<T> PolicyExt for T
where T: ?Sized,

§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] only if self and other return Action::Follow. Read more
§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more