Skip to main content

PackageCache

Struct PackageCache 

Source
pub struct PackageCache { /* private fields */ }
Expand description

The versioned, immutable, checksum-verified runner package cache.

Implementations§

Source§

impl PackageCache

Source

pub fn new(paths: &AppPaths, os: Os, arch: Arch, ports: CachePorts) -> Self

Build a cache rooted under paths, for a host running os/arch.

os and arch are parameters rather than probed here, following d1’s pattern: runner_manager_platform::os::detect_host is called once at the composition edge, and everything below takes the pair as a value. That is the whole injection seam — there is no host-probe trait — and it is what lets a Linux CI leg exercise the Windows selection path.

Source

pub fn with_pins(self, pins: PinnedDigests) -> Self

Source

pub const fn with_freshness(self, freshness: Freshness) -> Self

Source

pub const fn with_retry_budget(self, budget: u32) -> Self

Override the retry budget. Zero is rejected in favour of one attempt, because a cache that never attempts anything is not a cache.

Source

pub fn root(&self) -> &Path

The cache root. Nothing outside this module writes here.

Source

pub fn tool_cache_dir(&self) -> &Path

Where approved tool caches are retained.

A sibling of the package entries, not a child: an entry is immutable and a tool cache is by definition written to. Both are retained across attempts, and both are outside AppPaths::runtime_dir, which is where disposable job workspaces live — that is the separation 05-infrastructure.md asks for.

Source

pub async fn ensure_installed(&self) -> Result<InstalledPackage, PackageError>

Make a verified runner package available, and answer which one.

This is the cold-start path. It decides three things in order:

  1. Is this host one the product documents? An undocumented pair is refused here, before the catalog is consulted and long before anything is downloaded.
  2. Is a check due? The published version is re-checked on Freshness::check_interval, and always when the cache is empty. A cold start inside that interval reuses what a previous one resolved, which is what “a bounded interval” buys.
  3. Is what we hold still good enough? See Self::is_stale.
§Errors

Any PackageError. A terminal one is returned on the spot and spends none of the retry budget; a retryable one is retried up to Self::with_retry_budget times and then wrapped in PackageError::Exhausted.

Source

pub fn is_stale(&self, package: &InstalledPackage, now: Timestamp) -> bool

Whether a cached entry has fallen outside the freshness window.

§Why install time, and why this is the fail-closed direction

GitHub’s rule is about the release: a runner more than FRESHNESS_WINDOW_DAYS days behind the latest one is refused. The agent cannot see release dates — GitHub’s runner-downloads response carries no version field, let alone a publication timestamp — so the supersession moment has to be bounded rather than read.

It can be bounded exactly. This cache only ever installs the version GitHub was publishing at the time, so an entry installed at T was the latest release at T. Whatever superseded it did so at some R >= T, hence now - R <= now - T. So:

  • now - T <= window proves now - R <= window: the entry cannot be past the deadline, and reusing it is safe.
  • now - T > window proves nothing either way, so the entry is treated as stale and refreshed.

The error is therefore always toward refreshing early, never toward running a package GitHub will refuse. Refreshing early costs a download; refreshing late costs every job on the host, for a reason the operator has no way to guess.

Source

pub fn entry( &self, version: &RunnerVersion, ) -> Result<Option<InstalledPackage>, PackageError>

One entry, if it is installed and complete.

A directory with no readable manifest is not an entry: the manifest lands with the directory in a single rename, so its absence means the directory was not produced by this module and nothing may be assumed about its contents.

§Errors

PackageError::Io when the cache cannot be read.

Source

pub fn installed(&self) -> Result<Vec<InstalledPackage>, PackageError>

Every complete entry, oldest version first.

§Errors

PackageError::Io when the cache directory cannot be read.

Source

pub fn lease( &self, attempt: &RunnerAttempt, version: &RunnerVersion, ) -> Result<(), PackageError>

Record that attempt holds version, so a prune cannot remove it.

Two refusals, both structural:

  • A version that is not installed cannot be held.
  • An attempt whose runtime directory is inside the cache root is refused outright. Job workspaces are disposable and per-attempt; a package entry is shared and immutable. A workspace nested in one would be destroyed the moment that version was pruned, and would mutate an entry that every other runtime is copied from. This is checked rather than documented because a documented invariant with no enforcement is how the property silently stops holding.

The lease survives a restart — it is a file, not a field — which is what makes the guard meaningful across the agent’s own lifetime.

§Errors

PackageError::NotInstalled, PackageError::WorkspaceInsideCache, or PackageError::Io.

Source

pub fn release(&self, attempt: AttemptId) -> Result<(), PackageError>

Drop attempt’s hold, whatever it was holding.

Idempotent: releasing a lease that was never taken is not an error, so a cleanup path may call it unconditionally.

§Errors

PackageError::Io when the lease file cannot be removed.

Source

pub fn holders( &self, version: &RunnerVersion, ) -> Result<Vec<AttemptId>, PackageError>

Every attempt currently holding version.

§Errors

PackageError::Io when the lease directory cannot be read.

Source

pub fn sweep_staging(&self) -> Result<usize, PackageError>

Remove staging litter left by an interrupted install.

Nothing under .staging/ is ever part of an entry, so this is always safe to call. It is separate from installing because a sweep that ran automatically would race a concurrent install’s staging directory.

§Errors

PackageError::Io when the staging root cannot be read.

Trait Implementations§

Source§

impl Debug for PackageCache

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

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
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

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

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

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> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + 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
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + 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 = !

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

fn try_from(value: U) -> Result<T, !>

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.
Source§

impl<T> WithSubscriber for T

Source§

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
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

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