Compare commits

...

2 commits

3 changed files with 50 additions and 29 deletions

View file

@ -1,13 +1,13 @@
# Jirac
A CLI for creating and managing Jira tickets directly from your terminal.
A CLI for creating and managing Jira issues directly from your terminal.
## Features
- Create Jira tickets from markdown files or using your preferred editor
- List tickets assigned to specific users
- Create Jira issues from Markdown files or using your preferred editor
- List issues assigned to specific users
- Simple configuration using TOML
- Interactive ticket creation with templates
- Interactive issue creation with templates
- Execute JQL
## Installation
@ -27,6 +27,7 @@ Commands:
create Create an issue
list Find issues currently assigned to you
search Search for issues
view View an issue
init Set up the configuration
help Print this message or the help of the given subcommand(s)
@ -35,10 +36,10 @@ Options:
-V, --version Print version
```
### Creating a ticket
### Creating ab issue
```
Create a ticket
Create an issue
Usage: jirac create [OPTIONS] [MARKDOWN_FILE]
@ -46,8 +47,8 @@ Arguments:
[MARKDOWN_FILE] A markdown file
Options:
--project <PROJECT> The project key in which to create the ticket
--open Open the new ticket in a browser
--project <PROJECT> The project key in which to create the issue
--open Open the new issue in a browser
-h, --help Print help
```
@ -57,10 +58,10 @@ Options:
jirac create
```
*From a markdown file*
*From a Markdown file*
```sh
jirac create ticket.md
jirac create issue.md
```
*Specify a project*
@ -69,10 +70,10 @@ jirac create ticket.md
jirac create --project KEY
```
### Listing tickets
### Listing issues
```
Find tickets currently assigned to you
Find issues currently assigned to you
Usage: jirac list [OPTIONS]
@ -81,7 +82,7 @@ Options:
-h, --help Print help
```
### Search for tickets
### Search for issues
```
Search for issues
@ -98,12 +99,31 @@ Options:
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 tickets in a project*
*Find all in-progress issues in a project*
```
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
Get the following information:

View file

@ -40,6 +40,7 @@ pub enum Commands {
#[arg(value_name = "JQL")]
jql: String,
},
/// View an issue
View {
/// Print JSON rather than pretty print
#[arg(long)]

View file

@ -10,7 +10,7 @@ use std::path::PathBuf;
use tempfile::NamedTempFile;
#[derive(Debug, Deserialize, Serialize)]
struct TicketMetadata {
struct IssueMetadata {
#[serde(skip_serializing_if = "Option::is_none")]
status: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
@ -101,15 +101,15 @@ struct Status {
name: String,
}
fn get_ticket_template(
ticket_metadata: &TicketMetadata,
fn get_issue_template(
issue_metadata: &IssueMetadata,
) -> Result<String, Box<dyn std::error::Error>> {
let matter = toml::to_string(ticket_metadata)?;
let has_project = ticket_metadata.project.is_some();
let matter = toml::to_string(issue_metadata)?;
let has_project = issue_metadata.project.is_some();
let template = format!(
r#"---
# The ticket can contain the following properties:
# The issue can contain the following properties:
# - status
# - project (required)
# - assignee
@ -139,9 +139,9 @@ fn get_editor() -> String {
fn parse_markdown(
content: &str,
) -> Result<(String, String, TicketMetadata), Box<dyn std::error::Error>> {
) -> Result<(String, String, IssueMetadata), Box<dyn std::error::Error>> {
let matter = Matter::<TOML>::new();
let result = matter.parse_with_struct::<TicketMetadata>(content);
let result = matter.parse_with_struct::<IssueMetadata>(content);
let (metadata, content) = match result {
Some(x) => (x.data, x.content),
@ -153,7 +153,7 @@ fn parse_markdown(
// let metadata = result.data
// .and_then(|d| toml::from_str(d).ok())
// .unwrap_or_else(|| TicketMetadata {
// .unwrap_or_else(|| IssueMetadata {
// status: None,
// project: 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>> {
let mut temp_file = NamedTempFile::new()?;
let metadata = TicketMetadata {
let metadata = IssueMetadata {
status: None,
project: project_key,
assignee: None,
@ -190,7 +190,7 @@ fn create_temp_markdown(project_key: Option<String>) -> Result<String, Box<dyn s
extra: Default::default(),
};
let template = get_ticket_template(&metadata)?;
let template = get_issue_template(&metadata)?;
temp_file.write_all(template.as_bytes())?;
temp_file.flush()?;
@ -215,7 +215,7 @@ async fn create_jira_issue(
project_key: &str,
title: &str,
description: &str,
metadata: &TicketMetadata,
metadata: &IssueMetadata,
) -> Result<JiraResponse, Box<dyn std::error::Error>> {
let client = reqwest::Client::new();
@ -356,7 +356,7 @@ pub async fn create(
};
if content.trim().is_empty() {
return Err("Empty content. Aborting ticket creation.".into());
return Err("Empty content. Aborting issue creation.".into());
}
let (title, description, metadata) = parse_markdown(&content)?;
@ -375,7 +375,7 @@ pub async fn create(
}
// Confirm creation
println!("\nAbout to create ticket:");
println!("\nAbout to create an issue:");
println!("Project: {}", selected_project);
if let Some(status) = &metadata.status {
println!("Status: {}", status);
@ -399,7 +399,7 @@ pub async fn create(
let url = format!("{}/browse/{}", config.url, response.key);
println!("Successfully created ticket: {}", response.key);
println!("Successfully created issue: {}", response.key);
println!("URL: {}", url);
if open {