use std::process::Stdio;
use anyhow::{bail, Result};
use clap::Parser;
use tokio::io::AsyncWriteExt;
use tokio::process::Command;
use crate::state::State;
use crate::utils::in_path;
#[derive(Debug, Parser)]
#[clap(about = "Authenticate the current user with Docker")]
#[group(skip)]
pub struct Options {}
pub async fn handle(_options: &Options, state: &mut State) -> Result<()> {
if !in_path("docker").await {
bail!("Docker is not installed");
}
state.login(None).await?;
let current = state.ctx.current.take().unwrap();
login(
¤t.email,
state.auth.authorized.get(¤t.id).unwrap(),
)
.await?;
log::info!("Successfully logged in as `{}` with Docker", current.email);
Ok(())
}
pub const HOP_REGISTRY_URL: &str = "registry.hop.io";
pub async fn login(username: &str, password: &str) -> Result<()> {
let status = Command::new("docker")
.arg("login")
.arg(HOP_REGISTRY_URL)
.stdin(Stdio::piped())
.stderr(Stdio::null())
.status().await?;
log::debug!("Docker login exited with {status}");
if status.success() {
log::debug!("Docker login successful");
return Ok(());
}
login_new(username, password).await
}
pub async fn login_new(username: &str, password: &str) -> Result<()> {
let mut child = Command::new("docker")
.arg("login")
.arg("--username")
.arg(username)
.arg("--password-stdin")
.arg(HOP_REGISTRY_URL)
.stdin(Stdio::piped())
.spawn()?;
log::debug!("Writing password to stdin");
child
.stdin
.take()
.unwrap()
.write_all(password.as_bytes())
.await?;
let status = child.wait().await?;
log::debug!("Docker login exited with {status}");
if status.success() {
log::debug!("Docker login successful");
return Ok(());
}
bail!("Docker login failed, is the docker daemon running?");
}