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
37 changes: 25 additions & 12 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 6 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,12 @@ criterion = { version = "0.5", default-features = false, features = ["cargo_benc
derive_more = { version = "2.0", features = ["display", "error"] }
directories = "5"
ed25519-dalek = { version = "2.2", features = ["rand_core"] }
edgezero-adapter-axum = { git = "https://github.com/stackpop/edgezero", tag = "v0.0.4", default-features = false }
edgezero-adapter-cloudflare = { git = "https://github.com/stackpop/edgezero", tag = "v0.0.4", default-features = false }
edgezero-adapter-fastly = { git = "https://github.com/stackpop/edgezero", tag = "v0.0.4", default-features = false }
edgezero-adapter-spin = { git = "https://github.com/stackpop/edgezero", tag = "v0.0.4", default-features = false }
edgezero-cli = { git = "https://github.com/stackpop/edgezero", tag = "v0.0.4" }
edgezero-core = { git = "https://github.com/stackpop/edgezero", tag = "v0.0.4", default-features = false }
edgezero-adapter-axum = { git = "https://github.com/stackpop/edgezero", branch = "feature/edgezero-deploy-actions", default-features = false }
edgezero-adapter-cloudflare = { git = "https://github.com/stackpop/edgezero", branch = "feature/edgezero-deploy-actions", default-features = false }
edgezero-adapter-fastly = { git = "https://github.com/stackpop/edgezero", branch = "feature/edgezero-deploy-actions", default-features = false }
edgezero-adapter-spin = { git = "https://github.com/stackpop/edgezero", branch = "feature/edgezero-deploy-actions", default-features = false }
edgezero-cli = { git = "https://github.com/stackpop/edgezero", branch = "feature/edgezero-deploy-actions" }
edgezero-core = { git = "https://github.com/stackpop/edgezero", branch = "feature/edgezero-deploy-actions", default-features = false }
env_logger = "0.11"
error-stack = "0.6"
fastly = "0.12"
Expand Down
174 changes: 171 additions & 3 deletions crates/trusted-server-cli/src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use std::process;

use clap::{Parser, Subcommand};
use edgezero_cli::args::{
AuthArgs, BuildArgs, ConfigDiffArgs, ConfigPushArgs, ConfigValidateArgs, DeployArgs,
ProvisionArgs, ServeArgs,
ActiveVersionArgs, AuthArgs, BuildArgs, ConfigDiffArgs, ConfigPushArgs, ConfigValidateArgs,
DeployArgs, HealthcheckArgs, ProvisionArgs, RollbackArgs, ServeArgs,
};
use trusted_server_core::config::TrustedServerAppConfig;

Expand All @@ -13,14 +13,16 @@ use crate::commands::config::init::{ConfigInitArgs, run_config_init};
use crate::prebid_bundle::{NpmPrebidBundleGenerator, PrebidBundleArgs, run_bundle};

#[derive(Debug, Parser)]
#[command(name = "ts", about = "Trusted Server CLI")]
#[command(name = "ts", version, about = "Trusted Server CLI")]
struct Args {
#[command(subcommand)]
command: Command,
}

#[derive(Debug, Subcommand)]
enum Command {
/// Print the currently active deployment version for a target adapter.
ActiveVersion(ActiveVersionArgs),
/// Audit a public page and write draft Trusted Server artifacts.
Audit(AuditArgs),
/// Sign in / out / status against an `EdgeZero` adapter.
Expand All @@ -32,10 +34,14 @@ enum Command {
Config(ConfigCommand),
/// Deploy the project through a target adapter.
Deploy(DeployArgs),
/// Probe a deployed version until it reports healthy.
Healthcheck(HealthcheckArgs),
/// Trusted Server Prebid commands.
Prebid(PrebidArgs),
/// Provision platform resources through a target adapter.
Provision(ProvisionArgs),
/// Roll a service back to a previously active deployment version.
Rollback(RollbackArgs),
/// Serve the project locally through a target adapter.
Serve(ServeArgs),
/// Local developer tools (e.g. the macOS-only production-hostname proxy).
Expand Down Expand Up @@ -79,6 +85,7 @@ pub fn run_from_env() -> Result<(), String> {

fn dispatch(args: Args) -> Result<(), String> {
match args.command {
Command::ActiveVersion(args) => edgezero_cli::run_active_version(&args),
Command::Audit(args) => {
let stdout = std::io::stdout();
let mut out = stdout.lock();
Expand All @@ -102,6 +109,7 @@ fn dispatch(args: Args) -> Result<(), String> {
edgezero_cli::run_config_validate_typed::<TrustedServerAppConfig>(&args)
}
Command::Deploy(args) => edgezero_cli::run_deploy(&args),
Command::Healthcheck(args) => edgezero_cli::run_healthcheck(&args),
Command::Prebid(prebid) => {
let mut generator = NpmPrebidBundleGenerator;
let mut stdout = std::io::stdout();
Expand All @@ -113,6 +121,7 @@ fn dispatch(args: Args) -> Result<(), String> {
}
}
Command::Provision(args) => edgezero_cli::run_provision(&args),
Command::Rollback(args) => edgezero_cli::run_rollback(&args),
Command::Serve(args) => edgezero_cli::run_serve(&args),
Command::Dev(command) => crate::commands::dev::run(command),
}
Expand All @@ -131,6 +140,165 @@ mod tests {
Args::try_parse_from(args).expect("should parse args")
}

#[test]
fn parses_active_version() {
let args = parse(&[
"ts",
"active-version",
"--adapter",
"fastly",
"--service-id",
"service-123",
]);
let Command::ActiveVersion(active_version) = args.command else {
panic!("expected active-version command");
};
assert_eq!(active_version.adapter, "fastly");
assert_eq!(active_version.service_id, "service-123");
}

#[test]
fn parses_healthcheck_with_retry_defaults() {
let args = parse(&[
"ts",
"healthcheck",
"--adapter",
"fastly",
"--service-id",
"service-123",
"--version",
"7",
"--domain",
"edge.example",
]);
let Command::Healthcheck(healthcheck) = args.command else {
panic!("expected healthcheck command");
};
assert_eq!(healthcheck.domain, "edge.example");
assert_eq!(healthcheck.version, "7");
assert_eq!(healthcheck.retry, 3, "should default to 3 retries");
assert_eq!(
healthcheck.retry_delay, 5,
"should default to a 5s retry delay"
);
assert_eq!(healthcheck.timeout, 10, "should default to a 10s timeout");
assert!(!healthcheck.staging, "should probe production by default");
}

#[test]
fn parses_healthcheck_with_staging_overrides() {
let args = parse(&[
"ts",
"healthcheck",
"--adapter",
"fastly",
"--service-id",
"service-123",
"--version",
"7",
"--domain",
"edge.example",
"--staging",
"--retry",
"9",
"--retry-delay",
"2",
"--timeout",
"30",
]);
let Command::Healthcheck(healthcheck) = args.command else {
panic!("expected healthcheck command");
};
assert!(healthcheck.staging);
assert_eq!(healthcheck.retry, 9);
assert_eq!(healthcheck.retry_delay, 2);
assert_eq!(healthcheck.timeout, 30);
}

#[test]
fn healthcheck_requires_domain() {
Args::try_parse_from([
"ts",
"healthcheck",
"--adapter",
"fastly",
"--service-id",
"service-123",
"--version",
"7",
])
.expect_err("should reject healthcheck without a domain");
}

#[test]
fn parses_rollback_with_explicit_target() {
let args = parse(&[
"ts",
"rollback",
"--adapter",
"fastly",
"--service-id",
"service-123",
"--version",
"8",
"--rollback-to",
"7",
]);
let Command::Rollback(rollback) = args.command else {
panic!("expected rollback command");
};
assert_eq!(rollback.version, "8");
assert_eq!(rollback.rollback_to, Some("7".to_owned()));
assert!(!rollback.staging);
}

#[test]
fn parses_staging_rollback_without_target() {
let args = parse(&[
"ts",
"rollback",
"--adapter",
"fastly",
"--service-id",
"service-123",
"--version",
"8",
"--staging",
]);
let Command::Rollback(rollback) = args.command else {
panic!("expected rollback command");
};
assert!(rollback.staging);
assert_eq!(
rollback.rollback_to, None,
"staging rollback should not need an explicit target"
);
}

#[test]
fn rollback_requires_service_id() {
Args::try_parse_from(["ts", "rollback", "--adapter", "fastly", "--version", "8"])
.expect_err("should reject rollback without a service id");
}

#[test]
fn parses_deploy_with_staging_flags() {
let args = parse(&[
"ts",
"deploy",
"--adapter",
"fastly",
"--service-id",
"service-123",
"--stage",
]);
let Command::Deploy(deploy) = args.command else {
panic!("expected deploy command");
};
assert_eq!(deploy.service_id, Some("service-123".to_owned()));
assert!(deploy.stage);
}

#[test]
fn parses_audit_with_default_outputs() {
let args = parse(&["ts", "audit", "https://publisher.example"]);
Expand Down
4 changes: 3 additions & 1 deletion crates/trusted-server-core/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,9 @@ impl edgezero_core::app_config::AppConfigMeta for TrustedServerAppConfig {
// app-config blob. Migrating app-level secrets to `EdgeZero` secret-store
// references needs nested/array extraction support and operator migration
// work tracked separately.
const SECRET_FIELDS: &'static [edgezero_core::app_config::SecretField] = &[];
fn secret_fields() -> Vec<edgezero_core::app_config::SecretField> {
Vec::new()
}
}

/// Runs Trusted Server deploy-time validation for pushed app config.
Expand Down
Loading