Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
iliana committed May 11, 2024
1 parent 4dda5cb commit e100c7f
Show file tree
Hide file tree
Showing 8 changed files with 868 additions and 1 deletion.
24 changes: 24 additions & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions Cargo.toml
Expand Up @@ -21,6 +21,7 @@ members = [
"dev-tools/omicron-dev",
"dev-tools/oxlog",
"dev-tools/reconfigurator-cli",
"dev-tools/releng",
"dev-tools/xtask",
"dns-server",
"end-to-end-tests",
Expand Down Expand Up @@ -103,6 +104,7 @@ default-members = [
"dev-tools/omicron-dev",
"dev-tools/oxlog",
"dev-tools/reconfigurator-cli",
"dev-tools/releng",
# Do not include xtask in the list of default members, because this causes
# hakari to not work as well and build times to be longer.
# See omicron#4392.
Expand Down
26 changes: 26 additions & 0 deletions dev-tools/releng/Cargo.toml
@@ -0,0 +1,26 @@
[package]
name = "omicron-releng"
version = "0.1.0"
edition = "2021"
license = "MPL-2.0"

[dependencies]
anyhow.workspace = true
camino-tempfile.workspace = true
camino.workspace = true
fs-err = { workspace = true, features = ["tokio"] }
omicron-workspace-hack.workspace = true
omicron-zone-package.workspace = true
semver.workspace = true
shell-words.workspace = true
slog-async.workspace = true
slog-term.workspace = true
slog.workspace = true
tar.workspace = true
clap.workspace = true
tokio = { workspace = true, features = ["full"] }
chrono.workspace = true
futures.workspace = true

[lints]
workspace = true
115 changes: 115 additions & 0 deletions dev-tools/releng/src/cmd.rs
@@ -0,0 +1,115 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use std::ffi::OsStr;
use std::fmt::Write;
use std::process::ExitStatus;
use std::process::Output;
use std::process::Stdio;
use std::time::Instant;

use anyhow::ensure;
use anyhow::Context;
use anyhow::Result;
use slog::debug;
use slog::Logger;
use tokio::process::Command;

pub(crate) trait CommandExt {
fn check_status(&self, status: ExitStatus) -> Result<()>;
fn to_string(&self) -> String;

async fn is_success(&mut self, logger: &Logger) -> Result<bool>;
async fn ensure_success(&mut self, logger: &Logger) -> Result<()>;
async fn ensure_stdout(&mut self, logger: &Logger) -> Result<String>;
}

impl CommandExt for Command {
fn check_status(&self, status: ExitStatus) -> Result<()> {
ensure!(
status.success(),
"command `{}` exited with {}",
self.to_string(),
status
);
Ok(())
}

fn to_string(&self) -> String {
let command = self.as_std();
let mut command_str = String::new();
for (name, value) in command.get_envs() {
if let Some(value) = value {
write!(
command_str,
"{}={} ",
shell_words::quote(&name.to_string_lossy()),
shell_words::quote(&value.to_string_lossy())
)
.unwrap();
}
}
write!(
command_str,
"{}",
shell_words::join(
std::iter::once(command.get_program())
.chain(command.get_args())
.map(OsStr::to_string_lossy)
)
)
.unwrap();
command_str
}

async fn is_success(&mut self, logger: &Logger) -> Result<bool> {
let output = run(
self.stdin(Stdio::null())
.stdout(Stdio::inherit())
.stderr(Stdio::inherit()),
logger,
)
.await?;
Ok(output.status.success())
}

async fn ensure_success(&mut self, logger: &Logger) -> Result<()> {
let output = run(
self.stdin(Stdio::null())
.stdout(Stdio::inherit())
.stderr(Stdio::inherit()),
logger,
)
.await?;
self.check_status(output.status)
}

async fn ensure_stdout(&mut self, logger: &Logger) -> Result<String> {
let output = run(
self.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::inherit()),
logger,
)
.await?;
self.check_status(output.status)?;
String::from_utf8(output.stdout).context("command stdout was not UTF-8")
}
}

async fn run(command: &mut Command, logger: &Logger) -> Result<Output> {
debug!(logger, "running: {}", command.to_string());
let start = Instant::now();
let output =
command.kill_on_drop(true).output().await.with_context(|| {
format!("failed to exec `{}`", command.to_string())
})?;
debug!(
logger,
"process exited with {} ({:?})",
output.status,
Instant::now().saturating_duration_since(start)
);
Ok(output)
}

0 comments on commit e100c7f

Please sign in to comment.