feat: add run inspection commands #5
@@ -1,10 +1,14 @@
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let protoc = protoc_bin_vendored::protoc_bin_path()?;
|
||||
let mut config = tonic_prost_build::Config::new();
|
||||
config.protoc_executable(protoc);
|
||||
tonic_prost_build::configure()
|
||||
.build_server(false)
|
||||
.compile_with_config(config, &["proto/identity.proto"], &["proto"])?;
|
||||
println!("cargo:rerun-if-changed=proto/identity.proto");
|
||||
Ok(())
|
||||
}
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let protoc = protoc_bin_vendored::protoc_bin_path()?;
|
||||
let mut config = tonic_prost_build::Config::new();
|
||||
config.protoc_executable(protoc);
|
||||
tonic_prost_build::configure()
|
||||
.build_server(false)
|
||||
.compile_with_config(
|
||||
config,
|
||||
&["proto/identity.proto", "proto/actions.proto"],
|
||||
&["proto"],
|
||||
)?;
|
||||
println!("cargo:rerun-if-changed=proto");
|
||||
Ok(())
|
||||
}
|
||||
+39
File diff suppressed because it is too large
Load Diff
@@ -1,0 +1,55 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package syncode.control.v1;
|
||||
|
||||
service ActionsRead {
|
||||
rpc ListRuns(ListRunsRequest) returns (ListRunsResponse);
|
||||
rpc GetRun(GetRunRequest) returns (ActionRun);
|
||||
rpc GetJobLogs(GetJobLogsRequest) returns (GetJobLogsResponse);
|
||||
}
|
||||
|
||||
message ListRunsRequest {
|
||||
string session_token = 1;
|
||||
string owner = 2;
|
||||
string repository = 3;
|
||||
}
|
||||
|
||||
message GetRunRequest {
|
||||
string session_token = 1;
|
||||
string run_id = 2;
|
||||
}
|
||||
|
||||
message GetJobLogsRequest {
|
||||
string session_token = 1;
|
||||
string run_id = 2;
|
||||
string job_id = 3;
|
||||
}
|
||||
|
||||
message ListRunsResponse {
|
||||
repeated ActionRun runs = 1;
|
||||
}
|
||||
|
||||
message ActionRun {
|
||||
string id = 1;
|
||||
uint64 number = 2;
|
||||
string repository_id = 3;
|
||||
string commit = 4;
|
||||
string reference = 5;
|
||||
string event = 6;
|
||||
string workflow = 7;
|
||||
string state = 8;
|
||||
string conclusion = 9;
|
||||
repeated ActionJob jobs = 10;
|
||||
}
|
||||
|
||||
message ActionJob {
|
||||
string id = 1;
|
||||
string key = 2;
|
||||
string state = 3;
|
||||
string conclusion = 4;
|
||||
string node_id = 5;
|
||||
}
|
||||
|
||||
message GetJobLogsResponse {
|
||||
repeated string lines = 1;
|
||||
}
|
||||
+153
@@ -1,0 +1,153 @@
|
||||
use tonic::transport::{Channel, ClientTlsConfig, Endpoint};
|
||||
|
||||
use crate::actions_wire::actions_read_client::ActionsReadClient;
|
||||
use crate::actions_wire::{
|
||||
ActionJob, ActionRun, GetJobLogsRequest, GetRunRequest, ListRunsRequest,
|
||||
};
|
||||
use crate::api::Repository;
|
||||
use crate::error::{Error, Result};
|
||||
|
||||
async fn client(host: &str) -> Result<(ActionsReadClient<Channel>, String)> {
|
||||
let (host_key, _) = crate::config::normalize_host(host)?;
|
||||
let config = crate::config::load()?;
|
||||
let configured = config
|
||||
.hosts
|
||||
.get(&host_key)
|
||||
.ok_or_else(|| Error::Configuration(format!("not logged in to {host_key}")))?;
|
||||
let channel = Endpoint::from_shared(configured.forge_url.clone())?
|
||||
.tls_config(ClientTlsConfig::new().with_webpki_roots())?
|
||||
.connect()
|
||||
.await?;
|
||||
Ok((
|
||||
ActionsReadClient::new(channel),
|
||||
configured.session_token.clone(),
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn list(host: &str, repository: &str) -> Result<()> {
|
||||
let repository = Repository::parse(repository)?;
|
||||
let (mut client, session_token) = client(host).await?;
|
||||
let response = client
|
||||
.list_runs(ListRunsRequest {
|
||||
session_token,
|
||||
owner: repository.owner,
|
||||
repository: repository.name,
|
||||
})
|
||||
.await
|
||||
.map_err(control_error)?
|
||||
.into_inner();
|
||||
for run in response.runs {
|
||||
println!(
|
||||
"{}\t{}\t{}\t{}\t{}",
|
||||
run.number,
|
||||
run.id,
|
||||
status(&run.state, &run.conclusion),
|
||||
run.workflow,
|
||||
short_commit(&run.commit)
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn view(host: &str, run_id: &str) -> Result<()> {
|
||||
let (mut client, session_token) = client(host).await?;
|
||||
let run = get_run(&mut client, session_token, run_id).await?;
|
||||
println!("run\t{}", run.id);
|
||||
println!("number\t{}", run.number);
|
||||
println!("status\t{}", status(&run.state, &run.conclusion));
|
||||
println!("workflow\t{}", run.workflow);
|
||||
println!("commit\t{}", run.commit);
|
||||
println!("reference\t{}", run.reference);
|
||||
println!("event\t{}", run.event);
|
||||
for job in run.jobs {
|
||||
print_job(&job);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn logs(host: &str, run_id: &str, job_id: Option<&str>) -> Result<()> {
|
||||
let (mut client, session_token) = client(host).await?;
|
||||
let job_id = match job_id {
|
||||
Some(job_id) => job_id.to_owned(),
|
||||
None => {
|
||||
let run = get_run(&mut client, session_token.clone(), run_id).await?;
|
||||
match run.jobs.as_slice() {
|
||||
[job] => job.id.clone(),
|
||||
[] => {
|
||||
return Err(Error::Configuration("run has no jobs".to_owned()));
|
||||
}
|
||||
_ => {
|
||||
return Err(Error::Configuration(
|
||||
"run has multiple jobs; select one with --job <job-id>".to_owned(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
let response = client
|
||||
.get_job_logs(GetJobLogsRequest {
|
||||
session_token,
|
||||
run_id: run_id.to_owned(),
|
||||
job_id,
|
||||
})
|
||||
.await
|
||||
.map_err(control_error)?
|
||||
.into_inner();
|
||||
for line in response.lines {
|
||||
println!("{line}");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_run(
|
||||
client: &mut ActionsReadClient<Channel>,
|
||||
session_token: String,
|
||||
run_id: &str,
|
||||
) -> Result<ActionRun> {
|
||||
client
|
||||
.get_run(GetRunRequest {
|
||||
session_token,
|
||||
run_id: run_id.to_owned(),
|
||||
})
|
||||
.await
|
||||
.map_err(control_error)
|
||||
.map(tonic::Response::into_inner)
|
||||
}
|
||||
|
||||
fn print_job(job: &ActionJob) {
|
||||
println!(
|
||||
"job\t{}\t{}\t{}",
|
||||
job.id,
|
||||
job.key,
|
||||
status(&job.state, &job.conclusion)
|
||||
);
|
||||
}
|
||||
|
||||
fn status(state: &str, conclusion: &str) -> String {
|
||||
if conclusion.is_empty() {
|
||||
state.to_owned()
|
||||
} else {
|
||||
format!("{state}/{conclusion}")
|
||||
}
|
||||
}
|
||||
|
||||
fn short_commit(commit: &str) -> &str {
|
||||
commit.get(..commit.len().min(12)).unwrap_or(commit)
|
||||
}
|
||||
|
||||
fn control_error(error: tonic::Status) -> Error {
|
||||
Error::Command(format!("control rejected the request: {error}"))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{short_commit, status};
|
||||
|
||||
#[test]
|
||||
fn formats_status_and_commit() {
|
||||
assert_eq!("running", status("running", ""));
|
||||
assert_eq!("finished/success", status("finished", "success"));
|
||||
assert_eq!("0123456789ab", short_commit("0123456789abcdef"));
|
||||
assert_eq!("abc", short_commit("abc"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user