Skip to main content

studio_worker/
lib.rs

1//! Library surface for the `studio-worker` binary.
2//!
3//! Exposes the worker's modules so integration tests (and downstream
4//! tooling) can drive the contract without going through the CLI.
5
6// Enable the unstable `#[coverage(off)]` attribute used across the
7// crate (behind `#[cfg_attr(coverage_nightly, ...)]`) to exclude
8// host-, network-, and platform-dependent code from `cargo llvm-cov`.
9// `coverage_nightly` is set only by cargo-llvm-cov on a nightly
10// toolchain; on stable it's unset, so this gate (and every
11// `coverage(off)` annotation) compiles to nothing. Without it,
12// `cargo +nightly llvm-cov` fails to compile the crate (E0658).
13#![cfg_attr(coverage_nightly, feature(coverage_attribute))]
14
15pub mod auto_register;
16pub mod autostart;
17pub mod catalog;
18pub mod cli;
19pub mod config;
20pub mod engine;
21pub mod http;
22pub mod local;
23pub mod local_api;
24pub mod runtime;
25pub mod service;
26pub mod sys;
27pub mod telemetry;
28#[doc(hidden)]
29pub mod test_support;
30pub mod types;
31#[cfg(feature = "ui")]
32pub mod ui;
33pub mod update;
34pub mod ws;
35
36pub const AGENT_VERSION: &str = env!("CARGO_PKG_VERSION");
37
38/// Sentry release identifier in the `<pkg>@<version>` form expected by
39/// Sentry's organisation-wide *Releases* feature.  Bare version strings
40/// collide across projects in the same org, so we namespace with the
41/// crate name.  Matches what `sentry::release_name!()` would expand to.
42pub const RELEASE_NAME: &str = concat!(env!("CARGO_PKG_NAME"), "@", env!("CARGO_PKG_VERSION"));
43
44/// Tracing target for CLI lifecycle events.  Stable so operators can
45/// filter with `RUST_LOG=studio_worker::cli=info`.
46const CLI_TRACE_TARGET: &str = "studio_worker::cli";
47
48/// Emit a single startup breadcrumb naming the agent version and the
49/// subcommand about to run.  With no `SENTRY_DSN` set (the default),
50/// nothing else anchors "which version started, running what" in
51/// `journalctl` — which matters most right after an auto-update
52/// re-execs the binary or a service manager restarts the unit.
53fn log_cli_startup(command: &cli::Command) {
54    tracing::info!(
55        target: CLI_TRACE_TARGET,
56        op = "startup",
57        version = AGENT_VERSION,
58        command = command.name(),
59        "studio-worker starting"
60    );
61}
62
63/// Dispatch table for the CLI subcommands.  Lives in the library so we
64/// can drive it from tests without invoking the binary.
65pub async fn run_cli(args: cli::Cli) -> anyhow::Result<()> {
66    log_cli_startup(&args.command);
67    match args.command {
68        cli::Command::Run => runtime::run(args.config.as_deref()).await,
69        cli::Command::Register {
70            api_base_url,
71            reset,
72        } => {
73            runtime::register(
74                args.config.as_deref(),
75                runtime::RegisterArgs {
76                    api_base_url,
77                    reset,
78                },
79            )
80            .await
81        }
82        cli::Command::Status => runtime::status(args.config.as_deref()).await,
83        cli::Command::InstallService => service::install(args.config.as_deref()),
84        cli::Command::UninstallService => service::uninstall(),
85        cli::Command::SetThreshold { gb } => runtime::set_threshold(args.config.as_deref(), gb),
86        cli::Command::Config => runtime::show_config(args.config.as_deref()),
87        cli::Command::CheckUpdate => runtime::check_update(args.config.as_deref()).await,
88        cli::Command::Ui => run_ui(args.config.as_deref()).await,
89    }
90}
91
92#[cfg(feature = "ui")]
93async fn run_ui(config_path: Option<&str>) -> anyhow::Result<()> {
94    ui::run(config_path)
95}
96
97#[cfg(not(feature = "ui"))]
98async fn run_ui(_config_path: Option<&str>) -> anyhow::Result<()> {
99    anyhow::bail!(
100        "this build of studio-worker was compiled without the `ui` cargo feature \
101         (it is on by default \u{2014} you built with `--no-default-features`).\n\
102         Reinstall with `cargo install studio-worker` (UI is the default), or use \
103         the desktop installer from the releases page, to enable the native UI."
104    )
105}
106
107#[cfg(test)]
108mod tests {
109    use super::*;
110    use crate::test_support::capture;
111
112    #[test]
113    fn startup_breadcrumb_names_version_and_command() {
114        let logs = capture(|| log_cli_startup(&cli::Command::Run));
115        assert!(logs.contains("INFO"), "expected INFO event, got: {logs}");
116        assert!(
117            logs.contains("studio_worker::cli"),
118            "expected the cli target, got: {logs}"
119        );
120        assert!(
121            logs.contains("op=\"startup\""),
122            "expected op=startup, got: {logs}"
123        );
124        assert!(
125            logs.contains("command=\"run\""),
126            "expected command field, got: {logs}"
127        );
128        assert!(
129            logs.contains(AGENT_VERSION),
130            "expected agent version, got: {logs}"
131        );
132    }
133}