Skip to content
Draft
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
9 changes: 9 additions & 0 deletions clippy.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,12 @@ allow-expect-in-tests = true
allow-indexing-slicing-in-tests = true
allow-panic-in-tests = true
allow-unwrap-in-tests = true

# `--format json` reserves stdout for the JSON envelope, so every child that
# inherits stdout must go through `edgezero_adapter::process::status`, which
# applies the stdout-to-stderr policy. Captured spawns (`.output()`, or
# `.spawn()` with piped stdout) carry a documented `#[expect]`.
disallowed-methods = [
{ path = "std::process::Command::status", reason = "use `edgezero_adapter::process::status` so the `--format json` stdout policy applies" },
{ path = "std::process::Command::spawn", reason = "inherits stdout by default; pipe stdout and `#[expect]` with a reason, or use `edgezero_adapter::process::status`" },
]
65 changes: 45 additions & 20 deletions crates/edgezero-adapter-axum/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ use ctor::ctor;
use edgezero_adapter::cli_support::{
find_manifest_upwards, find_workspace_root, path_distance, read_package_name,
};
use edgezero_adapter::process;
use edgezero_adapter::registry::{
Adapter, AdapterAction, AdapterPushContext, ProvisionStores, ReadConfigEntry, ResolvedStoreId,
register_adapter,
ActionOutcome, Adapter, AdapterAction, AdapterPushContext, AuthState, AuthStatusOutcome,
BuildOutcome, ProvisionAction, ProvisionEntry, ProvisionReport, ProvisionStores,
ReadConfigEntry, ResolvedStoreId, StoreKind, register_adapter,
};
use edgezero_adapter::scaffold::{
AdapterBlueprint, AdapterFileSpec, CommandTemplates, DependencySpec, LoggingDefaults,
Expand Down Expand Up @@ -133,7 +135,7 @@ struct EdgezeroAxumConfig {
reason = "axum has no validate_app_config_keys / validate_adapter_manifest / validate_typed_secrets requirements; those three trait defaults are intentionally inherited. `read_config_entry` delegates to `read_config_entry_local` (axum is local-only). `single_store_kinds` IS overridden below (returns `&[\"secrets\"]`)."
)]
impl Adapter for AxumCliAdapter {
fn execute(&self, action: AdapterAction, args: &[String]) -> Result<(), String> {
fn execute(&self, action: AdapterAction, args: &[String]) -> Result<ActionOutcome, String> {
match action {
// The axum adapter is the in-process native dev server —
// there is no remote auth provider to sign in/out of.
Expand All @@ -142,11 +144,22 @@ impl Adapter for AxumCliAdapter {
log::info!(
"[edgezero] axum has no remote auth surface; `auth` is a no-op for this adapter"
);
Ok(())
Ok(if action == AdapterAction::AuthStatus {
ActionOutcome::AuthStatus(AuthStatusOutcome {
failure: None,
state: AuthState::NotApplicable,
})
} else {
ActionOutcome::Empty
})
}
AdapterAction::Build => build(args),
AdapterAction::Deploy => deploy(args),
AdapterAction::Serve => serve(args),
// `cargo build` picks the artifact path itself; axum does not
// track it, so the outcome reports none.
AdapterAction::Build => {
build(args).map(|()| ActionOutcome::Build(BuildOutcome { artifact: None }))
}
AdapterAction::Deploy => deploy(args).map(|()| ActionOutcome::Empty),
AdapterAction::Serve => serve(args).map(|()| ActionOutcome::Empty),
// The Fastly staging lifecycle is Fastly-only.
AdapterAction::DeployStaging
| AdapterAction::EmitVersion
Expand All @@ -169,12 +182,12 @@ impl Adapter for AxumCliAdapter {
_component_selector: Option<&str>,
stores: &ProvisionStores<'_>,
_dry_run: bool,
) -> Result<Vec<String>, String> {
) -> Result<ProvisionReport, String> {
//: axum has no remote resources. Print one note per
// declared store id so the operator sees the CLI heard
// them — same shape `dry_run` would have, since there is
// nothing to actually perform.
let mut out = Vec::with_capacity(
let mut entries = Vec::with_capacity(
stores
.kv
.len()
Expand All @@ -183,8 +196,11 @@ impl Adapter for AxumCliAdapter {
);
for store in stores.kv {
let logical = store.logical.as_str();
out.push(format!(
"axum KV store `{logical}` is in-memory; nothing to provision"
entries.push(ProvisionEntry::for_store(
ProvisionAction::NotApplicable,
StoreKind::Kv,
store,
format!("axum KV store `{logical}` is in-memory; nothing to provision"),
));
}
for store in stores.config {
Expand All @@ -193,20 +209,30 @@ impl Adapter for AxumCliAdapter {
// overlay isn't used for local file paths because the
// path encoding is the canonical form.
let logical = store.logical.as_str();
out.push(format!(
"axum config store `{logical}` reads `.edgezero/local-config-{logical}.json`; nothing to provision"
entries.push(ProvisionEntry::for_store(
ProvisionAction::NotApplicable,
StoreKind::Config,
store,
format!(
"axum config store `{logical}` reads `.edgezero/local-config-{logical}.json`; nothing to provision"
),
));
}
for store in stores.secrets {
let logical = store.logical.as_str();
out.push(format!(
"axum secret store `{logical}` reads env vars; nothing to provision"
entries.push(ProvisionEntry::for_store(
ProvisionAction::NotApplicable,
StoreKind::Secrets,
store,
format!("axum secret store `{logical}` reads env vars; nothing to provision"),
));
}
if out.is_empty() {
out.push("axum has no declared stores to provision".to_owned());
if entries.is_empty() {
entries.push(ProvisionEntry::note(
"axum has no declared stores to provision".to_owned(),
));
}
Ok(out)
Ok(ProvisionReport { entries })
}

fn push_config_entries(
Expand Down Expand Up @@ -418,8 +444,7 @@ fn run_cargo(project: &AxumProject, subcommand: &str, extra_args: &[String]) ->
// no-op for the child process.
command.env("EDGEZERO__ADAPTER__HOST", bind_addr.ip().to_string());
command.env("EDGEZERO__ADAPTER__PORT", bind_addr.port().to_string());
let status = command
.status()
let status = process::status(&mut command)
.map_err(|err| format!("failed to run cargo {subcommand}: {err}"))?;
if status.success() {
Ok(())
Expand Down
Loading
Loading