[go: up one dir, main page]

aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorn1 <hrdina.pavel@gmail.com>2024-06-25 14:35:33 +0200
committern1 <hrdina.pavel@gmail.com>2024-06-25 14:35:33 +0200
commit7e2b732e0466cdf07b74f2b9cdfafd360838b625 (patch)
tree3b8c7e32242a62ded088f2f807dcbaa9f3de83c3
parent35752af64b1adba7108d8004d661eef09758677f (diff)
Added: retrying.0.1.4
-rw-r--r--Cargo.lock2
-rw-r--r--Cargo.toml2
-rw-r--r--README.md3
-rw-r--r--src/app.rs36
-rw-r--r--src/args.rs9
-rw-r--r--src/config.rs15
-rw-r--r--src/job.rs39
-rw-r--r--src/pipeline.rs56
-rw-r--r--src/ui.rs10
9 files changed, 151 insertions, 21 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 3e12495..9044b36 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -420,7 +420,7 @@ dependencies = [
[[package]]
name = "glp"
-version = "0.1.3"
+version = "0.1.4"
dependencies = [
"chrono",
"clap",
diff --git a/Cargo.toml b/Cargo.toml
index 5569ed3..08d614b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "glp"
-version = "0.1.3"
+version = "0.1.4"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
diff --git a/README.md b/README.md
index 7d59b64..a4612d6 100644
--- a/README.md
+++ b/README.md
@@ -34,6 +34,9 @@ $ echo "456" > .glp; glp --trace=789 | less # fetches traces for all failed job
## Changelog
+### 0.1.4
+- retrying added (-r) - possibility to retry a job or a whole pipeline
+
### 0.1.3
- spinner added
- tracing added (--trace) - possibility to fetch failed jobs traces (logs)
diff --git a/src/app.rs b/src/app.rs
index b0254d9..d19356e 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -27,11 +27,12 @@ impl App {
.trace_relatively(trace.chars().skip(1).collect::<String>().parse::<u8>()?)
.await;
} else {
- result = self.trace_by_id(trace.to_string()).await;
+ result = self
+ .trace_by_id(trace.parse::<usize>().expect("Cannot parse pipeline ID."))
+ .await;
}
- } else if false {
- // Other future cases.
- unimplemented!()
+ } else if let Some(retry) = &self.config.retry {
+ result = self.retry(retry).await;
} else {
// 2. Default behavior
result = self.run_without_arguments().await;
@@ -78,7 +79,7 @@ impl App {
Ok(())
}
- async fn trace_by_id(&self, pipeline_id: String) -> Result<(), Box<dyn Error + Send + Sync>> {
+ async fn trace_by_id(&self, pipeline_id: usize) -> Result<(), Box<dyn Error + Send + Sync>> {
let stages = Pipeline::fetch_stages(self.config.clone(), pipeline_id).await?;
self.print_job_traces(self.fetch_failed_jobs(stages).await?);
@@ -132,4 +133,29 @@ impl App {
self.ui.print_job_trace(job);
}
}
+
+ async fn retry(
+ &self,
+ retry: &(usize, Option<String>),
+ ) -> Result<(), Box<dyn Error + Send + Sync>> {
+ if let Some(job_name) = &retry.1 {
+ let jobs = Pipeline::fetch_jobs(self.config.clone(), retry.0).await?;
+ let job = jobs.members().find(|i| i["name"].to_string() == *job_name);
+ let job_id;
+
+ if let Some(job) = job {
+ job_id = job["id"].as_usize().expect("Cannot parse job ID.");
+ } else {
+ return Err(From::from(JobError::NotFound));
+ }
+
+ Job::retry(self.config.clone(), job_id).await?;
+ self.ui.print_job_retry(retry.0, job_name);
+ } else {
+ Pipeline::retry(self.config.clone(), retry.0).await?;
+ self.ui.print_pipeline_retry(retry.0);
+ }
+
+ Ok(())
+ }
}
diff --git a/src/args.rs b/src/args.rs
index b06acd6..3922d2f 100644
--- a/src/args.rs
+++ b/src/args.rs
@@ -40,5 +40,14 @@ pub fn parse() -> ArgMatches {
.conflicts_with("limit")
.help("Fetches traces (logs) from all failed pipeline jobs. Specify pipeline ID or it's relative number with '-' prefix."),
)
+ .arg(
+ Arg::new("retry")
+ .short('r')
+ .long("retry")
+ .action(ArgAction::Append)
+ .value_parser(value_parser!(String))
+ .num_args(1..=2)
+ .help("Retry a job or a whole pipleline - arguments: <pipeline_id> or <pipeline_id> <job_name>.")
+ )
.get_matches()
}
diff --git a/src/config.rs b/src/config.rs
index aa7f03f..64c60cf 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -8,6 +8,7 @@ pub struct Config {
pub limit: u8,
pub finished: bool,
pub trace: Option<String>,
+ pub retry: Option<(usize, Option<String>)>,
_args: ArgMatches,
}
@@ -18,7 +19,9 @@ impl Config {
Some(id) => id.to_owned(),
None => fs::read_to_string(".glp")
.await
- .expect("No project ID (no parameter nor .glp file."),
+ .expect("No project ID (no parameter nor .glp file.")
+ .trim()
+ .to_owned(),
};
let finished = args.get_one::<bool>("finished").unwrap().to_owned();
@@ -26,6 +29,15 @@ impl Config {
.expect("No Gitlab private token found - set GLP_PRIVATE_TOKEN environment variable.");
let limit = args.get_one::<u8>("limit").unwrap().to_owned();
let trace = args.get_one::<String>("trace").cloned();
+ let retry = args
+ .get_many::<String>("retry")
+ .map(|mut vals| match vals.len() {
+ 1 => (vals.nth(0).unwrap().parse::<usize>().unwrap(), None),
+ _ => (
+ vals.next().unwrap().parse::<usize>().unwrap(),
+ Some(vals.next().unwrap().to_owned()),
+ ),
+ });
Self {
project_id,
@@ -33,6 +45,7 @@ impl Config {
limit,
finished,
trace,
+ retry,
_args: args,
}
}
diff --git a/src/job.rs b/src/job.rs
index 29aa1ae..06b4709 100644
--- a/src/job.rs
+++ b/src/job.rs
@@ -9,12 +9,14 @@ use thiserror::Error;
#[derive(Error, Debug)]
pub enum JobError {
- // #[error("Job not found.")]
- // NotFound,
+ #[error("Job not found.")]
+ NotFound,
#[error("Fetching error: {0:?}")]
FetchingError(#[source] reqwest::Error),
- #[error("Parsing error: {0:?}.")]
- ParsingError(#[source] reqwest::Error),
+ #[error("Parsing error.")]
+ ParsingError,
+ #[error("Retrying error.")]
+ RetryingError,
}
#[derive(Debug, Clone, Default)]
@@ -74,9 +76,36 @@ impl Job {
.map_err(JobError::FetchingError)?
.text()
.await
- .map_err(JobError::ParsingError)?,
+ .map_err(JobError::FetchingError)?,
);
Ok(())
}
+
+ pub async fn retry(config: Arc<Config>, job_id: usize) -> Result<(), JobError> {
+ let client = reqwest::Client::new();
+ let job = json::parse(
+ client
+ .post(format!(
+ "https://gitlab.com/api/v4/projects/{}/jobs/{}/retry",
+ &config.project_id, job_id
+ ))
+ .header("PRIVATE-TOKEN", &config.private_token)
+ .send()
+ .await
+ .map_err(|_| JobError::RetryingError)?
+ .text()
+ .await
+ .map_err(|_| JobError::RetryingError)?
+ .as_str(),
+ )
+ .map_err(|_| JobError::ParsingError)?;
+
+ // Check for errors.
+ if job.has_key("error") {
+ return Err(JobError::NotFound);
+ }
+
+ Ok(())
+ }
}
diff --git a/src/pipeline.rs b/src/pipeline.rs
index d4a95f6..4dffbdf 100644
--- a/src/pipeline.rs
+++ b/src/pipeline.rs
@@ -28,6 +28,8 @@ pub enum PipelineError {
FetchingError,
#[error("Parsing error.")]
ParsingError,
+ #[error("Retrying error.")]
+ RetryingError,
}
/// Represents Gitlab pipeline.
@@ -98,11 +100,10 @@ impl Pipeline {
.map_err(|_| PipelineError::ParsingError)
}
- // Fetch stages + jobs for the given pipeline.
- pub async fn fetch_stages(
+ pub async fn fetch_jobs(
config: Arc<Config>,
- pipeline_id: String,
- ) -> Result<Vec<Stage>, PipelineError> {
+ pipeline_id: usize,
+ ) -> Result<JsonValue, PipelineError> {
// Fetch data.
let client = reqwest::Client::new();
let jobs = json::parse(
@@ -127,9 +128,18 @@ impl Pipeline {
return Err(PipelineError::NotFound);
}
+ Ok(jobs)
+ }
+
+ // Fetch stages + jobs for the given pipeline.
+ pub async fn fetch_stages(
+ config: Arc<Config>,
+ pipeline_id: usize,
+ ) -> Result<Vec<Stage>, PipelineError> {
// Process JSON response.
// Construct jobs.
let mut stages: HashMap<String, Vec<Job>> = HashMap::new();
+ let jobs = Pipeline::fetch_jobs(config.clone(), pipeline_id).await?;
for j in 0..jobs.len() {
let job = &jobs[j];
@@ -186,11 +196,11 @@ impl Pipeline {
.collect::<Vec<&Job>>();
b_jobs.sort_by_key(|j| j.started_at.clone());
- let a_started_at = match a_jobs.get(0) {
+ let a_started_at = match a_jobs.first() {
Some(j) => j.started_at.clone(),
_ => None,
};
- let b_started_at = match b_jobs.get(0) {
+ let b_started_at = match b_jobs.first() {
Some(j) => j.started_at.clone(),
_ => None,
};
@@ -232,8 +242,11 @@ impl Pipeline {
let xconfig = config.clone();
tasks.push(tokio::spawn(async move {
- let pip_stages =
- Self::fetch_stages(xconfig.clone(), pip["id"].clone().to_string()).await?;
+ let pip_stages = Self::fetch_stages(
+ xconfig.clone(),
+ pip["id"].as_usize().expect("Cannot parse pipeline ID."),
+ )
+ .await?;
let mut pip = Pipeline {
id: Label(pip["id"].as_usize().unwrap().to_string()),
@@ -342,4 +355,31 @@ impl Pipeline {
None
}
+
+ pub async fn retry(config: Arc<Config>, pipeline_id: usize) -> Result<(), PipelineError> {
+ let client = reqwest::Client::new();
+ let pipeline = json::parse(
+ client
+ .post(format!(
+ "https://gitlab.com/api/v4/projects/{}/pipelines/{}/retry",
+ &config.project_id, pipeline_id
+ ))
+ .header("PRIVATE-TOKEN", &config.private_token)
+ .send()
+ .await
+ .map_err(|_| PipelineError::RetryingError)?
+ .text()
+ .await
+ .map_err(|_| PipelineError::RetryingError)?
+ .as_str(),
+ )
+ .map_err(|_| PipelineError::ParsingError)?;
+
+ // Check for errors.
+ if pipeline.has_key("error") {
+ return Err(PipelineError::NotFound);
+ }
+
+ Ok(())
+ }
}
diff --git a/src/ui.rs b/src/ui.rs
index 709eda6..026f8aa 100644
--- a/src/ui.rs
+++ b/src/ui.rs
@@ -69,4 +69,14 @@ impl Ui {
eprintln!("Original error: {}", source);
}
}
+
+ pub fn print_pipeline_retry(&self, pipeline_id: usize) {
+ println!("Pipeline {} is gonna run once again.", pipeline_id);
+ }
+ pub fn print_job_retry(&self, pipeline_id: usize, job: &String) {
+ println!(
+ "Job {} from pipeline {} is gonna run once again.",
+ job, pipeline_id
+ );
+ }
}