• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

getdozer / dozer / 5607543735

pending completion
5607543735

push

github

web-flow
fix: Show warning when metadata collect failed (#1772)

6 of 6 new or added lines in 1 file covered. (100.0%)

43000 of 56395 relevant lines covered (76.25%)

33899.95 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.47
/dozer-cli/src/main.rs
1
use clap::Parser;
1✔
2
#[cfg(feature = "cloud")]
3
use dozer_cli::cli::cloud::CloudCommands;
4
use dozer_cli::cli::generate_config_repl;
5
use dozer_cli::cli::types::{Cli, Commands, ConnectorCommand, RunCommands, SecurityCommands};
6
use dozer_cli::cli::{init_dozer, list_sources, LOGO};
7
use dozer_cli::errors::{CliError, OrchestrationError};
8
use dozer_cli::simple::SimpleOrchestrator;
9
#[cfg(feature = "cloud")]
10
use dozer_cli::CloudOrchestrator;
11
use dozer_cli::{set_ctrl_handler, set_panic_hook, shutdown, Orchestrator};
12
use dozer_types::models::telemetry::TelemetryConfig;
13
use dozer_types::tracing::{error, info};
14
use serde::Deserialize;
15
use tokio::time;
16

17
use clap::CommandFactory;
18
#[cfg(feature = "cloud")]
19
use dozer_cli::cloud_app_context::CloudAppContext;
20
use std::cmp::Ordering;
21

22
use dozer_types::log::{debug, warn};
23
use std::time::Duration;
24
use std::{env, process};
25

26
fn main() {
×
27
    set_panic_hook();
×
28

×
29
    if let Err(e) = run() {
×
30
        error!("{}", e);
×
31
        process::exit(1);
×
32
    }
×
33
}
×
34

×
35
fn render_logo() {
×
36
    const VERSION: &str = env!("CARGO_PKG_VERSION");
×
37

×
38
    println!("{LOGO}");
×
39
    println!("\nDozer Version: {VERSION}\n");
×
40
}
×
41

×
42
#[derive(Deserialize, Debug)]
×
43
struct DozerPackage {
×
44
    #[serde(rename(deserialize = "latestVersion"))]
45
    pub latest_version: String,
46
    #[serde(rename(deserialize = "availableAssets"))]
47
    pub _available_assets: Vec<String>,
48
    pub link: String,
49
}
50

51
fn version_to_vector(version: &str) -> Vec<i32> {
×
52
    version.split('.').map(|s| s.parse().unwrap()).collect()
×
53
}
×
54

×
55
fn compare_versions(v1: Vec<i32>, v2: Vec<i32>) -> bool {
×
56
    for i in 0..v1.len() {
×
57
        match v1.get(i).cmp(&v2.get(i)) {
×
58
            Ordering::Greater => return true,
×
59
            Ordering::Less => return false,
×
60
            Ordering::Equal => continue,
×
61
        }
×
62
    }
63
    false
×
64
}
×
65

×
66
async fn check_update() {
×
67
    const VERSION: &str = env!("CARGO_PKG_VERSION");
×
68
    let dozer_env = std::env::var("DOZER_ENV").unwrap_or("local".to_string());
×
69
    let dozer_dev = std::env::var("DOZER_DEV").unwrap_or("ext".to_string());
×
70
    let query = vec![
×
71
        ("version", VERSION),
×
72
        ("build", std::env::consts::ARCH),
×
73
        ("os", std::env::consts::OS),
×
74
        ("env", &dozer_env),
×
75
        ("dev", &dozer_dev),
×
76
    ];
×
77

×
78
    let request_url = "https://metadata.dev.getdozer.io/";
×
79

×
80
    let client = reqwest::Client::new();
×
81

×
82
    let mut printed = false;
×
83

×
84
    loop {
85
        let response = client
×
86
            .get(&request_url.to_string())
×
87
            .query(&query)
×
88
            .send()
×
89
            .await;
×
90

×
91
        match response {
×
92
            Ok(r) => {
×
93
                if !printed {
×
94
                    let package: DozerPackage = r.json().await.unwrap();
×
95
                    let current = version_to_vector(VERSION);
×
96
                    let remote = version_to_vector(&package.latest_version);
×
97

×
98
                    if compare_versions(remote, current) {
×
99
                        info!("A new version of Dozer is available.");
×
100
                        info!(
×
101
                            "You can download v{}, from {}.",
×
102
                            package.latest_version, package.link
×
103
                        );
×
104
                        printed = true;
×
105
                    }
×
106
                }
×
107
            }
×
108
            Err(e) => {
×
109
                // We dont show error if error is connection error, because mostly it happens
×
110
                // when main thread is shutting down before request completes.
×
111
                if !e.is_connect() {
×
112
                    warn!("Unable to fetch the latest metadata");
×
113
                }
×
114

115
                debug!("Updates check error: {}", e);
×
116
            }
117
        }
×
118
        time::sleep(Duration::from_secs(2 * 60 * 60)).await;
×
119
    }
120
}
121

×
122
fn run() -> Result<(), OrchestrationError> {
×
123
    // Reloading trace layer seems impossible, so we are running Cli::parse in a closure
124
    // and then initializing it after reading the configuration. This is a hacky workaround, but it works.
×
125

×
126
    let cli = parse_and_generate()?;
×
127
    let mut dozer = init_orchestrator(&cli)?;
×
128

×
129
    let (shutdown_sender, shutdown_receiver) = shutdown::new(&dozer.runtime);
×
130
    set_ctrl_handler(shutdown_sender);
×
131

×
132
    // Now we have access to telemetry configuration. Telemetry must be initialized in tokio runtime.
×
133
    let _telemetry = dozer.runtime.block_on(async {
×
134
        Telemetry::new(Some(&dozer.config.app_name), dozer.config.telemetry.clone())
×
135
    });
×
136

137
    if let Some(cmd) = cli.cmd {
×
138
        // run individual servers
×
139
        match cmd {
×
140
            Commands::Run(run) => match run.command {
×
141
                RunCommands::Api => {
142
                    render_logo();
×
143

×
144
                    dozer.run_api(shutdown_receiver)
×
145
                }
×
146
                RunCommands::App => {
×
147
                    render_logo();
×
148

×
149
                    dozer.run_apps(shutdown_receiver, None, None)
×
150
                }
×
151
            },
×
152
            Commands::Security(security) => match security.command {
×
153
                SecurityCommands::GenerateToken => {
×
154
                    let token = dozer.generate_token()?;
×
155
                    info!("token: {:?} ", token);
×
156
                    Ok(())
×
157
                }
158
            },
×
159
            Commands::Build(build) => {
×
160
                let force = build.force.is_some();
×
161

×
162
                dozer.build(force)
×
163
            }
164
            Commands::Connectors(ConnectorCommand { filter }) => list_sources(
×
165
                cli.config_paths,
×
166
                cli.config_token,
×
167
                cli.config_overrides,
×
168
                filter,
×
169
            ),
×
170
            Commands::Clean => dozer.clean(),
×
171
            #[cfg(feature = "cloud")]
172
            Commands::Cloud(cloud) => {
×
173
                render_logo();
×
174

×
175
                match cloud.command.clone() {
176
                    CloudCommands::Deploy(deploy) => dozer.deploy(cloud, deploy, cli.config_paths),
177
                    CloudCommands::Api(api) => dozer.api(cloud, api),
×
178
                    CloudCommands::Login { organisation_name } => {
×
179
                        dozer.login(cloud, organisation_name)
×
180
                    }
×
181
                    CloudCommands::Secrets(command) => {
182
                        dozer.execute_secrets_command(cloud, command)
×
183
                    }
×
184
                    CloudCommands::Delete => dozer.delete(cloud),
×
185
                    CloudCommands::Status => dozer.status(cloud),
×
186
                    CloudCommands::Monitor => dozer.monitor(cloud),
×
187
                    CloudCommands::Logs(logs) => dozer.trace_logs(cloud, logs),
×
188
                    CloudCommands::Version(version) => dozer.version(cloud, version),
×
189
                    CloudCommands::List(list) => dozer.list(cloud, list),
190
                    CloudCommands::SetApp { app_id } => {
191
                        CloudAppContext::save_app_id(app_id.clone())?;
192
                        info!("Using \"{app_id}\" app");
193
                        Ok(())
194
                    }
195
                }
196
            }
197
            Commands::Init => {
198
                panic!("This should not happen as it is handled in parse_and_generate");
×
199
            }
200
        }
201
    } else {
202
        render_logo();
×
203

×
204
        dozer.run_all(shutdown_receiver, None)
×
205
    }
206
}
×
207

208
// Some commands dont need to initialize the orchestrator
209
// This function is used to run those commands
210
fn parse_and_generate() -> Result<Cli, OrchestrationError> {
×
211
    dozer_tracing::init_telemetry_closure(None, None, || -> Result<Cli, OrchestrationError> {
×
212
        let cli = Cli::parse();
×
213

214
        if let Some(Commands::Init) = cli.cmd {
×
215
            Telemetry::new(None, None);
×
216
            if let Err(e) = generate_config_repl() {
×
217
                error!("{}", e);
×
218
                Err(e)
×
219
            } else {
220
                // We need to exit here, otherwise the orchestrator will be initialized
221
                process::exit(0);
×
222
            }
×
223
        } else {
×
224
            Ok(cli)
×
225
        }
226
    })
×
227
}
×
228

×
229
fn init_orchestrator(cli: &Cli) -> Result<SimpleOrchestrator, CliError> {
×
230
    dozer_tracing::init_telemetry_closure(None, None, || -> Result<SimpleOrchestrator, CliError> {
×
231
        let res = init_dozer(
×
232
            cli.config_paths.clone(),
×
233
            cli.config_token.clone(),
×
234
            cli.config_overrides.clone(),
×
235
        );
×
236

×
237
        match res {
×
238
            Ok(dozer) => {
×
239
                dozer.runtime.spawn(check_update());
×
240
                Ok(dozer)
×
241
            }
×
242
            Err(e) => {
×
243
                if let CliError::FailedToFindConfigurationFiles(_) = &e {
×
244
                    let description = "Dozer was not able to find configuration files. \n\n\
×
245
                    Please use \"dozer init\" to create project or \"dozer -c {path}\" with path to your configuration.\n\
×
246
                    Configuration documentation can be found in https://getdozer.io/docs/configuration";
×
247

×
248
                    let mut command = Cli::command();
×
249
                    command = command.about(format!("\n\n\n{} \n {}", LOGO, description));
×
250

×
251
                    println!("{}", command.render_help());
×
252
                }
×
253

×
254
                error!("{}", e);
×
255
                Err(e)
×
256
            }
×
257
        }
×
258
    })
×
259
}
×
260

×
261
struct Telemetry();
×
262

×
263
impl Telemetry {
×
264
    fn new(app_name: Option<&str>, config: Option<TelemetryConfig>) -> Self {
×
265
        dozer_tracing::init_telemetry(app_name, config);
×
266
        Self()
×
267
    }
×
268
}
269

×
270
impl Drop for Telemetry {
×
271
    fn drop(&mut self) {
×
272
        dozer_tracing::shutdown_telemetry();
×
273
    }
×
274
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc