Compare commits
No commits in common. "476fa902004962abb2839b07c2a1ea8c7000f65f" and "b229ce9c5c5a4c662640ef5d5103aa82cef4c093" have entirely different histories.
476fa90200
...
b229ce9c5c
3 changed files with 29 additions and 50 deletions
48
readme.md
48
readme.md
|
@ -1,13 +1,13 @@
|
||||||
# Jirac
|
# Jirac
|
||||||
|
|
||||||
A CLI for creating and managing Jira issues directly from your terminal.
|
A CLI for creating and managing Jira tickets directly from your terminal.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Create Jira issues from Markdown files or using your preferred editor
|
- Create Jira tickets from markdown files or using your preferred editor
|
||||||
- List issues assigned to specific users
|
- List tickets assigned to specific users
|
||||||
- Simple configuration using TOML
|
- Simple configuration using TOML
|
||||||
- Interactive issue creation with templates
|
- Interactive ticket creation with templates
|
||||||
- Execute JQL
|
- Execute JQL
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
@ -27,7 +27,6 @@ Commands:
|
||||||
create Create an issue
|
create Create an issue
|
||||||
list Find issues currently assigned to you
|
list Find issues currently assigned to you
|
||||||
search Search for issues
|
search Search for issues
|
||||||
view View an issue
|
|
||||||
init Set up the configuration
|
init Set up the configuration
|
||||||
help Print this message or the help of the given subcommand(s)
|
help Print this message or the help of the given subcommand(s)
|
||||||
|
|
||||||
|
@ -36,10 +35,10 @@ Options:
|
||||||
-V, --version Print version
|
-V, --version Print version
|
||||||
```
|
```
|
||||||
|
|
||||||
### Creating ab issue
|
### Creating a ticket
|
||||||
|
|
||||||
```
|
```
|
||||||
Create an issue
|
Create a ticket
|
||||||
|
|
||||||
Usage: jirac create [OPTIONS] [MARKDOWN_FILE]
|
Usage: jirac create [OPTIONS] [MARKDOWN_FILE]
|
||||||
|
|
||||||
|
@ -47,8 +46,8 @@ Arguments:
|
||||||
[MARKDOWN_FILE] A markdown file
|
[MARKDOWN_FILE] A markdown file
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
--project <PROJECT> The project key in which to create the issue
|
--project <PROJECT> The project key in which to create the ticket
|
||||||
--open Open the new issue in a browser
|
--open Open the new ticket in a browser
|
||||||
-h, --help Print help
|
-h, --help Print help
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -58,10 +57,10 @@ Options:
|
||||||
jirac create
|
jirac create
|
||||||
```
|
```
|
||||||
|
|
||||||
*From a Markdown file*
|
*From a markdown file*
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
jirac create issue.md
|
jirac create ticket.md
|
||||||
```
|
```
|
||||||
|
|
||||||
*Specify a project*
|
*Specify a project*
|
||||||
|
@ -70,10 +69,10 @@ jirac create issue.md
|
||||||
jirac create --project KEY
|
jirac create --project KEY
|
||||||
```
|
```
|
||||||
|
|
||||||
### Listing issues
|
### Listing tickets
|
||||||
|
|
||||||
```
|
```
|
||||||
Find issues currently assigned to you
|
Find tickets currently assigned to you
|
||||||
|
|
||||||
Usage: jirac list [OPTIONS]
|
Usage: jirac list [OPTIONS]
|
||||||
|
|
||||||
|
@ -82,7 +81,7 @@ Options:
|
||||||
-h, --help Print help
|
-h, --help Print help
|
||||||
```
|
```
|
||||||
|
|
||||||
### Search for issues
|
### Search for tickets
|
||||||
|
|
||||||
```
|
```
|
||||||
Search for issues
|
Search for issues
|
||||||
|
@ -99,31 +98,12 @@ Options:
|
||||||
|
|
||||||
Use [JQL](https://support.atlassian.com/jira-software-cloud/docs/use-advanced-search-with-jira-query-language-jql/) to search for Issues.
|
Use [JQL](https://support.atlassian.com/jira-software-cloud/docs/use-advanced-search-with-jira-query-language-jql/) to search for Issues.
|
||||||
|
|
||||||
*Find all in-progress issues in a project*
|
*Find all in-progress tickets in a project*
|
||||||
|
|
||||||
```
|
```
|
||||||
jirac search 'project = KEY AND status = "In Progress" ORDER BY created DESC'
|
jirac search 'project = KEY AND status = "In Progress" ORDER BY created DESC'
|
||||||
```
|
```
|
||||||
|
|
||||||
### View an issue
|
|
||||||
|
|
||||||
```
|
|
||||||
View an issue
|
|
||||||
|
|
||||||
Usage: jirac view [OPTIONS] <ISSUE>
|
|
||||||
|
|
||||||
Arguments:
|
|
||||||
<ISSUE> An issue key, for example, KEY-123
|
|
||||||
|
|
||||||
Options:
|
|
||||||
--json Print JSON rather than pretty print
|
|
||||||
-h, --help Print help
|
|
||||||
```
|
|
||||||
|
|
||||||
```sh
|
|
||||||
./target/release/jirac view KEY-123
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
Get the following information:
|
Get the following information:
|
||||||
|
|
|
@ -40,7 +40,6 @@ pub enum Commands {
|
||||||
#[arg(value_name = "JQL")]
|
#[arg(value_name = "JQL")]
|
||||||
jql: String,
|
jql: String,
|
||||||
},
|
},
|
||||||
/// View an issue
|
|
||||||
View {
|
View {
|
||||||
/// Print JSON rather than pretty print
|
/// Print JSON rather than pretty print
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
|
|
|
@ -10,7 +10,7 @@ use std::path::PathBuf;
|
||||||
use tempfile::NamedTempFile;
|
use tempfile::NamedTempFile;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
struct IssueMetadata {
|
struct TicketMetadata {
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
status: Option<String>,
|
status: Option<String>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
@ -101,15 +101,15 @@ struct Status {
|
||||||
name: String,
|
name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_issue_template(
|
fn get_ticket_template(
|
||||||
issue_metadata: &IssueMetadata,
|
ticket_metadata: &TicketMetadata,
|
||||||
) -> Result<String, Box<dyn std::error::Error>> {
|
) -> Result<String, Box<dyn std::error::Error>> {
|
||||||
let matter = toml::to_string(issue_metadata)?;
|
let matter = toml::to_string(ticket_metadata)?;
|
||||||
let has_project = issue_metadata.project.is_some();
|
let has_project = ticket_metadata.project.is_some();
|
||||||
|
|
||||||
let template = format!(
|
let template = format!(
|
||||||
r#"---
|
r#"---
|
||||||
# The issue can contain the following properties:
|
# The ticket can contain the following properties:
|
||||||
# - status
|
# - status
|
||||||
# - project (required)
|
# - project (required)
|
||||||
# - assignee
|
# - assignee
|
||||||
|
@ -139,9 +139,9 @@ fn get_editor() -> String {
|
||||||
|
|
||||||
fn parse_markdown(
|
fn parse_markdown(
|
||||||
content: &str,
|
content: &str,
|
||||||
) -> Result<(String, String, IssueMetadata), Box<dyn std::error::Error>> {
|
) -> Result<(String, String, TicketMetadata), Box<dyn std::error::Error>> {
|
||||||
let matter = Matter::<TOML>::new();
|
let matter = Matter::<TOML>::new();
|
||||||
let result = matter.parse_with_struct::<IssueMetadata>(content);
|
let result = matter.parse_with_struct::<TicketMetadata>(content);
|
||||||
|
|
||||||
let (metadata, content) = match result {
|
let (metadata, content) = match result {
|
||||||
Some(x) => (x.data, x.content),
|
Some(x) => (x.data, x.content),
|
||||||
|
@ -153,7 +153,7 @@ fn parse_markdown(
|
||||||
|
|
||||||
// let metadata = result.data
|
// let metadata = result.data
|
||||||
// .and_then(|d| toml::from_str(d).ok())
|
// .and_then(|d| toml::from_str(d).ok())
|
||||||
// .unwrap_or_else(|| IssueMetadata {
|
// .unwrap_or_else(|| TicketMetadata {
|
||||||
// status: None,
|
// status: None,
|
||||||
// project: None,
|
// project: None,
|
||||||
// assignee: None,
|
// assignee: None,
|
||||||
|
@ -182,7 +182,7 @@ fn parse_markdown(
|
||||||
fn create_temp_markdown(project_key: Option<String>) -> Result<String, Box<dyn std::error::Error>> {
|
fn create_temp_markdown(project_key: Option<String>) -> Result<String, Box<dyn std::error::Error>> {
|
||||||
let mut temp_file = NamedTempFile::new()?;
|
let mut temp_file = NamedTempFile::new()?;
|
||||||
|
|
||||||
let metadata = IssueMetadata {
|
let metadata = TicketMetadata {
|
||||||
status: None,
|
status: None,
|
||||||
project: project_key,
|
project: project_key,
|
||||||
assignee: None,
|
assignee: None,
|
||||||
|
@ -190,7 +190,7 @@ fn create_temp_markdown(project_key: Option<String>) -> Result<String, Box<dyn s
|
||||||
extra: Default::default(),
|
extra: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let template = get_issue_template(&metadata)?;
|
let template = get_ticket_template(&metadata)?;
|
||||||
|
|
||||||
temp_file.write_all(template.as_bytes())?;
|
temp_file.write_all(template.as_bytes())?;
|
||||||
temp_file.flush()?;
|
temp_file.flush()?;
|
||||||
|
@ -215,7 +215,7 @@ async fn create_jira_issue(
|
||||||
project_key: &str,
|
project_key: &str,
|
||||||
title: &str,
|
title: &str,
|
||||||
description: &str,
|
description: &str,
|
||||||
metadata: &IssueMetadata,
|
metadata: &TicketMetadata,
|
||||||
) -> Result<JiraResponse, Box<dyn std::error::Error>> {
|
) -> Result<JiraResponse, Box<dyn std::error::Error>> {
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::Client::new();
|
||||||
|
|
||||||
|
@ -356,7 +356,7 @@ pub async fn create(
|
||||||
};
|
};
|
||||||
|
|
||||||
if content.trim().is_empty() {
|
if content.trim().is_empty() {
|
||||||
return Err("Empty content. Aborting issue creation.".into());
|
return Err("Empty content. Aborting ticket creation.".into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let (title, description, metadata) = parse_markdown(&content)?;
|
let (title, description, metadata) = parse_markdown(&content)?;
|
||||||
|
@ -375,7 +375,7 @@ pub async fn create(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Confirm creation
|
// Confirm creation
|
||||||
println!("\nAbout to create an issue:");
|
println!("\nAbout to create ticket:");
|
||||||
println!("Project: {}", selected_project);
|
println!("Project: {}", selected_project);
|
||||||
if let Some(status) = &metadata.status {
|
if let Some(status) = &metadata.status {
|
||||||
println!("Status: {}", status);
|
println!("Status: {}", status);
|
||||||
|
@ -399,7 +399,7 @@ pub async fn create(
|
||||||
|
|
||||||
let url = format!("{}/browse/{}", config.url, response.key);
|
let url = format!("{}/browse/{}", config.url, response.key);
|
||||||
|
|
||||||
println!("Successfully created issue: {}", response.key);
|
println!("Successfully created ticket: {}", response.key);
|
||||||
println!("URL: {}", url);
|
println!("URL: {}", url);
|
||||||
|
|
||||||
if open {
|
if open {
|
||||||
|
|
Loading…
Reference in a new issue