Skip to content

enhancement(dev): Add dedicated dev tool #14990

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 19 commits into from
845 changes: 845 additions & 0 deletions vdev/Cargo.lock

Large diffs are not rendered by default.

36 changes: 36 additions & 0 deletions vdev/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
[package]
name = "vdev"
version = "0.1.0"
edition = "2021"
authors = ["Vector Contributors <[email protected]>"]
license = "MPL-2.0"
readme = "README.md"
publish = false

[dependencies]
anyhow = "1.0.66"
atty = "0.2.14"
cached = "0.40.0"
clap = { version = "4.0.18", features = ["derive"] }
clap-verbosity-flag = "2.0.0"
confy = "0.5.1"
directories = "4.0.1"
# remove this when stabilized https://doc.rust-lang.org/stable/std/path/fn.absolute.html
dunce = "1.0.3"
env_logger = "0.9.1"
globset = "0.4.9"
hashlink = { version = "0.8.1", features = ["serde_impl"] }
home = "0.5.4"
indicatif = { version = "0.17.1", features = ["improved_unicode"] }
itertools = "0.10.5"
log = "0.4.17"
once_cell = "1.16.0"
os_info = { version = "3.5.1", default-features = false }
# watch https://github.com/epage/anstyle for official interop with Clap
owo-colors = { version = "3.5.0", features = ["supports-colors"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.87"
toml = "0.5.9"

# https://github.com/rust-lang/cargo/issues/6745#issuecomment-472667516
[workspace]
67 changes: 67 additions & 0 deletions vdev/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# vdev

-----

This is the command line tooling for Vector development.

Table of Contents:

- [Installation](#installation)
- [Configuration](#configuration)
- [Repository](#repository)
- [Starship](#starship)
- [CLI](#cli)

## Installation

Run the following command from the root of the Vector repository:

```text
cargo install -f --path vdev
```

## Configuration

### Repository

Setting the path to the repository explicitly allows the application to be used at any time no matter the current working directory.

```text
vdev config set repo .
```

To test, enter your home directory and then run:

```text
vdev exec ls
```

### Starship
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a really nice touch!


A custom command for the [Starship](https://starship.rs) prompt is available.

```toml
format = """
...
${custom.vdev}\
...
$line_break\
...
$character"""

# <clipped>

[custom.vdev]
command = "vdev meta starship"
when = true
# Windows
# shell = ["cmd", "/C"]
# Other
# shell = ["sh", "--norc"]
```

## CLI

The CLI uses [Clap](https://github.com/clap-rs/clap) with the `derive` construction mechanism and is stored in the [commands](src/commands) directory.

Every command group/namespace has its own directory with a `cli` module, including the root `vdev` command group. All commands have an `exec` method that provides the actual implementation, which in the case of command groups will be calling sub-commands.
114 changes: 114 additions & 0 deletions vdev/src/app.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
use anyhow::{bail, Result};
use indicatif::{ProgressBar, ProgressStyle};
use log::LevelFilter;
use once_cell::sync::OnceCell;
use std::time::Duration;
use std::{borrow::Cow, process::Command};

use crate::config::{Config, ConfigFile};

static VERBOSITY: OnceCell<LevelFilter> = OnceCell::new();
static CONFIG_FILE: OnceCell<ConfigFile> = OnceCell::new();
static CONFIG: OnceCell<Config> = OnceCell::new();
static PATH: OnceCell<String> = OnceCell::new();

pub fn verbosity() -> &'static LevelFilter {
VERBOSITY.get().expect("verbosity is not initialized")
}

pub fn config_file() -> &'static ConfigFile {
CONFIG_FILE.get().expect("config file is not initialized")
}

pub fn config() -> &'static Config {
CONFIG.get().expect("config is not initialized")
}

pub fn path() -> &'static String {
PATH.get().expect("path is not initialized")
}

pub fn construct_command(program: &str) -> Command {
let mut command = Command::new(program);
command.current_dir(path());

command
}

pub fn capture_output(command: &mut Command) -> Result<String> {
Ok(String::from_utf8(command.output()?.stdout)?)
}

pub fn run_command(command: &mut Command) -> Result<()> {
let status = command.status()?;
if status.success() {
Ok(())
} else {
bail!(
"command: {}\nfailed with exit code: {}",
render_command(command),
status.code().unwrap()
)
}
}

pub fn wait_for_command(
command: &mut Command,
message: impl Into<Cow<'static, str>>,
) -> Result<()> {
let progress_bar = get_progress_bar()?;
progress_bar.set_message(message);

let result = command.output();
progress_bar.finish_and_clear();
let output = match result {
Ok(output) => output,
Err(_) => bail!("could not run command"),
};

if output.status.success() {
Ok(())
} else {
bail!(
"{}\nfailed with exit code: {}",
String::from_utf8(output.stdout)?,
output.status.code().unwrap()
)
}
}

fn get_progress_bar() -> Result<ProgressBar> {
let progress_bar = ProgressBar::new_spinner();
progress_bar.enable_steady_tick(Duration::from_millis(125));
progress_bar.set_style(
ProgressStyle::with_template("{spinner} {msg:.magenta.bold}")?
// https://github.com/sindresorhus/cli-spinners/blob/master/spinners.json
.tick_strings(&["∙∙∙", "●∙∙", "∙●∙", "∙∙●", "∙∙∙"]),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see ∙∙∙ is repeated twice here. Is that intentional?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup that's correct as you can see in the link

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not seeing it. All the strings I'm seeing at that link don't have any repeats.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

);

Ok(progress_bar)
}

fn render_command(command: &mut Command) -> String {
format!(
"{} {}",
command.get_program().to_str().unwrap(),
Vec::from_iter(command.get_args().map(|arg| arg.to_str().unwrap())).join(" ")
)
}

pub fn set_global_verbosity(verbosity: LevelFilter) {
VERBOSITY.set(verbosity).unwrap()
}

pub fn set_global_config_file(config_file: ConfigFile) {
CONFIG_FILE.set(config_file).unwrap()
}

pub fn set_global_config(config: Config) {
CONFIG.set(config).unwrap()
}

pub fn set_global_path(path: String) {
PATH.set(path).unwrap()
}
54 changes: 54 additions & 0 deletions vdev/src/commands/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use anyhow::Result;
use clap::Args;

use crate::app;
use crate::platform;

/// Build Vector
#[derive(Args, Debug)]
#[command()]
pub struct Cli {
/// The build target e.g. x86_64-unknown-linux-musl
target: Option<String>,

/// Build with optimizations
#[arg(short, long)]
release: bool,

/// The feature to activate (multiple allowed)
#[arg(short = 'F', long)]
feature: Vec<String>,
}

impl Cli {
pub fn exec(&self) -> Result<()> {
let mut command = app::construct_command("cargo");
command.args(["build", "--no-default-features"]);

if self.release {
command.arg("--release");
}

command.arg("--features");
if !self.feature.is_empty() {
command.args([self.feature.join(",")]);
} else {
if platform::windows() {
command.arg("default-msvc");
} else {
command.arg("default");
}
};

if let Some(target) = self.target.as_deref() {
command.args(["--target", target]);
} else {
command.args(["--target", &platform::default_target()]);
};

display_waiting!("Building Vector");
app::run_command(&mut command)?;

Ok(())
}
}
54 changes: 54 additions & 0 deletions vdev/src/commands/cli.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use anyhow::Result;
use clap::{Parser, Subcommand};
use clap_verbosity_flag::{InfoLevel, Verbosity};

use crate::commands;

/// Vector's unified dev tool
#[derive(Parser, Debug)]
#[
command(
bin_name = "vdev",
author,
version,
about,
disable_help_subcommand = true,
long_about = None,
)
]
pub struct Cli {
#[clap(flatten)]
pub(crate) verbose: Verbosity<InfoLevel>,

#[command(subcommand)]
command: Commands,
}

#[derive(Subcommand, Debug)]
enum Commands {
/// Build Vector
Build(commands::build::Cli),
/// Manage the vdev config file
Config(commands::config::cli::Cli),
/// Execute a command within the repository
Exec(commands::exec::Cli),
/// Manage integrations
Int(commands::int::cli::Cli),
/// Collection of useful utilities
Meta(commands::meta::cli::Cli),
/// Show information about the current environment
Status(commands::status::Cli),
}

impl Cli {
pub fn exec(&self) -> Result<()> {
match &self.command {
Commands::Build(cli) => cli.exec(),
Commands::Config(cli) => cli.exec(),
Commands::Exec(cli) => cli.exec(),
Commands::Int(cli) => cli.exec(),
Commands::Meta(cli) => cli.exec(),
Commands::Status(cli) => cli.exec(),
}
}
}
29 changes: 29 additions & 0 deletions vdev/src/commands/config/cli.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use anyhow::Result;
use clap::{Args, Subcommand};

use crate::commands;

/// Manage the vdev config file
#[derive(Args, Debug)]
#[command()]
pub struct Cli {
#[command(subcommand)]
command: Commands,
}

#[derive(Subcommand, Debug)]
enum Commands {
/// Locate the config file
Find(commands::config::find::Cli),
/// Modify the config file
Set(commands::config::set::cli::Cli),
}

impl Cli {
pub fn exec(&self) -> Result<()> {
match &self.command {
Commands::Find(cli) => cli.exec(),
Commands::Set(cli) => cli.exec(),
}
}
}
17 changes: 17 additions & 0 deletions vdev/src/commands/config/find.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use anyhow::Result;
use clap::Args;

use crate::app;

/// Locate the config file
#[derive(Args, Debug)]
#[command()]
pub struct Cli {}

impl Cli {
pub fn exec(&self) -> Result<()> {
display!("{}", app::config_file().path().display());

Ok(())
}
}
3 changes: 3 additions & 0 deletions vdev/src/commands/config/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod cli;
pub mod find;
pub mod set;
Loading