pub trait Forge: Send + Sync {
Show 19 methods
// Required methods
fn kind(&self) -> ForgeKind;
fn host(&self) -> &str;
fn capabilities(&self, ns: &Namespace) -> Capabilities;
fn begin_bind<'life0, 'async_trait>(
&'life0 self,
req: BindRequest,
) -> Pin<Box<dyn Future<Output = Result<BindStep>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait;
fn complete_bind<'life0, 'async_trait>(
&'life0 self,
cb: BindCallback,
) -> Pin<Box<dyn Future<Output = Result<NamespaceBinding>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait;
fn begin_account_link<'life0, 'life1, 'async_trait>(
&'life0 self,
member: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<LinkStep>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait;
fn complete_account_link<'life0, 'async_trait>(
&'life0 self,
cb: LinkCallback,
) -> Pin<Box<dyn Future<Output = Result<ForgeAccount>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait;
fn inspect<'life0, 'life1, 'async_trait>(
&'life0 self,
repo: &'life1 Resource,
) -> Pin<Box<dyn Future<Output = Result<RepoState>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait;
fn create_repo<'life0, 'life1, 'async_trait>(
&'life0 self,
spec: &'life1 RepoSpec,
) -> Pin<Box<dyn Future<Output = Result<RepoState>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait;
fn archive_repo<'life0, 'life1, 'async_trait>(
&'life0 self,
repo: &'life1 Resource,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait;
fn apply_roles<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
repo: &'life1 Resource,
desired: &'life2 [RoleAssignment],
unlisted: Unlisted,
) -> Pin<Box<dyn Future<Output = Result<ApplyReport>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait;
fn bootstrap_plan(
&self,
repo: &RepoSpec,
cfg: &VgiConfig,
) -> Result<Vec<BootstrapStep>>;
fn run_step<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
repo: &'life1 Resource,
step: &'life2 BootstrapStep,
) -> Pin<Box<dyn Future<Output = Result<StepOutcome>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait;
fn parse_event(
&self,
headers: &HeaderMap,
body: &[u8],
) -> Result<Option<ForgeEvent>>;
// Provided methods
fn normalize(&self, raw: &str) -> Result<Resource> { ... }
fn map_role(
&self,
ns: &Namespace,
rights: EffectiveRights,
map: &RoleMap,
) -> ForgeRole { ... }
fn is_protected_account(&self, ns: &Namespace, account: u64) -> bool { ... }
fn indirect_access<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
repo: &'life1 Resource,
account: &'life2 ForgeAccount,
) -> Pin<Box<dyn Future<Output = Result<Option<IndirectAccess>>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait { ... }
fn diff(&self, observed: &RepoState, desired: &Projection) -> Vec<Drift> { ... }
}Expand description
One forge implementation. Stateless apart from its credentials and the namespaces it has been told about; the core owns all desired state and hands the adapter a plan.
Object-safe (through async-trait) so a bridge can hold one
Box<dyn Forge> per forge host and dispatch on a resource’s host. Methods
with a sensible forge-neutral answer have a default; an adapter overrides
only what its forge does differently.
Required Methods§
Sourcefn host(&self) -> &str
fn host(&self) -> &str
The forge host this adapter serves (github.com, a GHES host,
codeberg.org). Every resource it accepts starts with it; a
resource on another host is refused rather than sent to the wrong
forge.
Sourcefn capabilities(&self, ns: &Namespace) -> Capabilities
fn capabilities(&self, ns: &Namespace) -> Capabilities
What this forge, and this namespace on it, can do. The core and the
UX branch on this, never on Forge::kind.
Sourcefn begin_bind<'life0, 'async_trait>(
&'life0 self,
req: BindRequest,
) -> Pin<Box<dyn Future<Output = Result<BindStep>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn begin_bind<'life0, 'async_trait>(
&'life0 self,
req: BindRequest,
) -> Pin<Box<dyn Future<Output = Result<BindStep>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Start binding a namespace: where to send the admin.
Sourcefn complete_bind<'life0, 'async_trait>(
&'life0 self,
cb: BindCallback,
) -> Pin<Box<dyn Future<Output = Result<NamespaceBinding>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn complete_bind<'life0, 'async_trait>(
&'life0 self,
cb: BindCallback,
) -> Pin<Box<dyn Future<Output = Result<NamespaceBinding>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Finish a bind from the forge’s callback. Validates the state nonce and that the credential landed on the expected owner.
Sourcefn begin_account_link<'life0, 'life1, 'async_trait>(
&'life0 self,
member: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<LinkStep>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn begin_account_link<'life0, 'life1, 'async_trait>(
&'life0 self,
member: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<LinkStep>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Start linking a member’s forge account. member is their DID, for
the adapter’s audit trail; nothing forge-side sees it.
Sourcefn complete_account_link<'life0, 'async_trait>(
&'life0 self,
cb: LinkCallback,
) -> Pin<Box<dyn Future<Output = Result<ForgeAccount>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn complete_account_link<'life0, 'async_trait>(
&'life0 self,
cb: LinkCallback,
) -> Pin<Box<dyn Future<Output = Result<ForgeAccount>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Finish linking: the account’s numeric id and current login.
Sourcefn inspect<'life0, 'life1, 'async_trait>(
&'life0 self,
repo: &'life1 Resource,
) -> Pin<Box<dyn Future<Output = Result<RepoState>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn inspect<'life0, 'life1, 'async_trait>(
&'life0 self,
repo: &'life1 Resource,
) -> Pin<Box<dyn Future<Output = Result<RepoState>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Observe a repository’s current state.
Sourcefn create_repo<'life0, 'life1, 'async_trait>(
&'life0 self,
spec: &'life1 RepoSpec,
) -> Pin<Box<dyn Future<Output = Result<RepoState>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn create_repo<'life0, 'life1, 'async_trait>(
&'life0 self,
spec: &'life1 RepoSpec,
) -> Pin<Box<dyn Future<Output = Result<RepoState>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Create a repository. Refuses one that already exists with
ForgeError::AlreadyExists — adopting it is a separate, elevated
decision (§5.6), not something a retry should do silently.
Sourcefn archive_repo<'life0, 'life1, 'async_trait>(
&'life0 self,
repo: &'life1 Resource,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn archive_repo<'life0, 'life1, 'async_trait>(
&'life0 self,
repo: &'life1 Resource,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Archive a repository. Idempotent.
Sourcefn apply_roles<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
repo: &'life1 Resource,
desired: &'life2 [RoleAssignment],
unlisted: Unlisted,
) -> Pin<Box<dyn Future<Output = Result<ApplyReport>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
fn apply_roles<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
repo: &'life1 Resource,
desired: &'life2 [RoleAssignment],
unlisted: Unlisted,
) -> Pin<Box<dyn Future<Output = Result<ApplyReport>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Converge people’s direct roles on a repository to desired.
Collaborators desired does not mention are handled per unlisted.
Sourcefn bootstrap_plan(
&self,
repo: &RepoSpec,
cfg: &VgiConfig,
) -> Result<Vec<BootstrapStep>>
fn bootstrap_plan( &self, repo: &RepoSpec, cfg: &VgiConfig, ) -> Result<Vec<BootstrapStep>>
The steps that turn commit trust on for this forge’s CI.
Sourcefn run_step<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
repo: &'life1 Resource,
step: &'life2 BootstrapStep,
) -> Pin<Box<dyn Future<Output = Result<StepOutcome>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
fn run_step<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
repo: &'life1 Resource,
step: &'life2 BootstrapStep,
) -> Pin<Box<dyn Future<Output = Result<StepOutcome>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Run one step, check-then-apply.
Sourcefn parse_event(
&self,
headers: &HeaderMap,
body: &[u8],
) -> Result<Option<ForgeEvent>>
fn parse_event( &self, headers: &HeaderMap, body: &[u8], ) -> Result<Option<ForgeEvent>>
Verify and translate a webhook. Ok(None) for a verified delivery
the core has no use for; Err for one that failed verification —
which must not be acted on.
Provided Methods§
Sourcefn normalize(&self, raw: &str) -> Result<Resource>
fn normalize(&self, raw: &str) -> Result<Resource>
Canonical form of a forge path. The default applies the
owner[/repo] grammar GitHub and Forgejo share and refuses a
resource on another host.
Sourcefn map_role(
&self,
ns: &Namespace,
rights: EffectiveRights,
map: &RoleMap,
) -> ForgeRole
fn map_role( &self, ns: &Namespace, rights: EffectiveRights, map: &RoleMap, ) -> ForgeRole
Rights → this forge’s role for one person on one repository in ns.
The default asks map for a role and rounds it down onto the
namespace’s ladder.
Sourcefn is_protected_account(&self, ns: &Namespace, account: u64) -> bool
fn is_protected_account(&self, ns: &Namespace, account: u64) -> bool
Whether the account with forge id account must never be taken off a
repository in ns, whatever a job asks: the namespace’s owner (on a
personal account, the implicit admin of every repository in it) and
the adapter’s own automation identity (a Forgejo bot, a GitHub App’s
bot user), without which nothing the bridge does would keep working.
Matched by numeric id, never by login. The default protects the owner; an adapter that knows its automation account’s id adds it.
Sourcefn indirect_access<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
repo: &'life1 Resource,
account: &'life2 ForgeAccount,
) -> Pin<Box<dyn Future<Output = Result<Option<IndirectAccess>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
fn indirect_access<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
repo: &'life1 Resource,
account: &'life2 ForgeAccount,
) -> Pin<Box<dyn Future<Output = Result<Option<IndirectAccess>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Access account has to repo that is not a direct role on it —
through a team, as an owner or a member of the organisation — above
what anyone has anyway (read on a repository everyone can read).
Ok(None): none.
Asked after the account’s direct role was taken away
(git-ns/bridge/job 0.2 removeAccounts), so that access the job
could not remove is reported. It only reads: teams and organisation
membership are never changed to satisfy a job about one repository.
The default knows of no access other than direct roles.
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".