Skip to content
Closed
Show file tree
Hide file tree
Changes from 10 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
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 <vector@datadoghq.com>"]
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

Comment thread
ofek marked this conversation as resolved.
-----

This is the command line tooling for Vector development.

Table of Contents:

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

## Installation

Comment thread
ofek marked this conversation as resolved.
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
Copy Markdown
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.
199 changes: 199 additions & 0 deletions vdev/src/app.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
use anyhow::{bail, Result};
use indicatif::{ProgressBar, ProgressStyle};
use log::{Level, LevelFilter};
use once_cell::sync::OnceCell;
use owo_colors::{
OwoColorize,
Stream::{Stderr, Stdout},
};
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 display<T: AsRef<str>>(text: T) {
// Simply bold rather than bright white for terminals with white backgrounds
println!(
"{}",
text.as_ref().if_supports_color(Stdout, |text| text.bold())
);
}

#[allow(dead_code)]
pub fn display_trace<T: AsRef<str>>(text: T) {
if Level::Trace <= *verbosity() {
eprintln!(
"{}",
text.as_ref().if_supports_color(Stderr, |text| text.bold())
);
}
}

#[allow(dead_code)]
pub fn display_debug<T: AsRef<str>>(text: T) {
if Level::Debug <= *verbosity() {
eprintln!(
"{}",
text.as_ref().if_supports_color(Stderr, |text| text.bold())
);
}
}

#[allow(dead_code)]
pub fn display_info<T: AsRef<str>>(text: T) {
if Level::Info <= *verbosity() {
eprintln!(
"{}",
text.as_ref().if_supports_color(Stderr, |text| text.bold())
);
}
}

#[allow(dead_code)]
pub fn display_success<T: AsRef<str>>(text: T) {
if Level::Info <= *verbosity() {
eprintln!(
"{}",
text.as_ref()
.if_supports_color(Stderr, |text| text.bright_cyan())
);
}
}

#[allow(dead_code)]
pub fn display_waiting<T: AsRef<str>>(text: T) {
if Level::Info <= *verbosity() {
eprintln!(
"{}",
text.as_ref()
.if_supports_color(Stderr, |text| text.bright_magenta())
);
}
}

#[allow(dead_code)]
pub fn display_warning<T: AsRef<str>>(text: T) {
if Level::Warn <= *verbosity() {
eprintln!(
"{}",
text.as_ref()
.if_supports_color(Stderr, |text| text.bright_yellow())
);
}
}

pub fn display_error<T: AsRef<str>>(text: T) {
if Level::Error <= *verbosity() {
eprintln!(
"{}",
text.as_ref()
.if_supports_color(Stderr, |text| text.bright_red())
);
}
}

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()
)
}
}
Comment thread
bruceg marked this conversation as resolved.

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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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::{bail, 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");
}
};
Comment on lines +30 to +41

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You may also find script/features interesting, to pull the required features out of a config file based on the components that are in use.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ah nice, I will incorporate that soon


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

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

Ok(())
}
}
Loading