diff --git a/.github/workflows/release-rs.yml b/.github/workflows/release-rs.yml index d250af0..f5851aa 100644 --- a/.github/workflows/release-rs.yml +++ b/.github/workflows/release-rs.yml @@ -17,32 +17,44 @@ jobs: run: | echo PKG_VERSION=$(awk -F ' = ' '$1 ~ /version/ { gsub(/["]/, "", $2); printf("%s",$2) }' Cargo.toml) >> $GITHUB_OUTPUT + create-release: + name: "Create release" + needs: + - get-tag + runs-on: ubuntu-latest + steps: + - uses: "marvinpinto/action-automatic-releases@latest" + with: + repo_token: "${{ secrets.GITHUB_TOKEN }}" + automatic_release_tag: "${{ needs.get-tag.outputs.crate-version}}" + prerelease: true + title: "Development Build" + files: | + LICENSE.txt + *.jar + upload-binaries: + build: name: "Build binaries and upload as artifacts" - needs: get-tag + needs: + - get-tag strategy: fail-fast: true matrix: include: - - os: self-hosted + - os: ubuntu-latest target: aarch64-unknown-linux-gnu - - os: self-hosted - target: arm-unknown-linux-gnueabi - - os: self-hosted - target: armv7-unknown-linux-gnueabihf - - os: self-hosted - target: i686-unknown-linux-gnu - - os: self-hosted - target: i686-unknown-linux-musl - - os: self-hosted + - os: ubuntu-latest target: x86_64-unknown-linux-gnu - - os: self-hosted - target: x86_64-unknown-linux-musl - - os: self-hosted + - os: windows-latest target: x86_64-pc-windows-gnu + - os: windows-latest + target: x86_64-pc-windows-msvc + runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable with: targets: "${{ matrix.target }}" @@ -54,23 +66,6 @@ jobs: name: "jig-${{ matrix.target }}" path: target/release/jig - create-release: - name: "Create release" - needs: - - get-tag - - build - runs-on: self-hosted - steps: - - uses: "marvinpinto/action-automatic-releases@latest" - with: - repo_token: "${{ secrets.GITHUB_TOKEN }}" - automatic_release_tag: "${{ needs.get-tag.outputs.crate-version}}" - prerelease: true - title: "Development Build" - files: | - LICENSE.txt - *.jar upload-binaries: - upload-binaries: name: "Create release" needs: diff --git a/jig-cli/src/commands/assign.rs b/jig-cli/src/commands/assign.rs index 4ab3ee2..3f9bb2f 100644 --- a/jig-cli/src/commands/assign.rs +++ b/jig-cli/src/commands/assign.rs @@ -1,15 +1,17 @@ -use std::fmt::{Display, Formatter}; - use crate::{ config::Config, interactivity::issue_from_branch_or_prompt, repo::Repository, ExecCommand, }; use clap::Args; -use color_eyre::eyre::{Result, WrapErr}; -use inquire::Select; +use color_eyre::eyre::{eyre, Result, WrapErr}; +use inquire::{autocompletion::Replacement, Autocomplete, Text}; use jira::{ - types::{IssueKey, User}, + types::{GetAssignableUserParams, IssueKey, User}, JiraAPIClient, }; +use std::{ + fmt::{Display, Formatter}, + rc::Rc, +}; #[derive(Args, Debug)] pub struct Assign { @@ -43,17 +45,90 @@ impl ExecCommand for Assign { issue_key }; - let users = client - .get_assignable_users(&issue_key)? - .iter() - .map(|u| JiraUser(u.to_owned())) - .collect(); - let user = Select::new("Assign:", users) + let mut completer = AssignableUsersCompleter { + client: client.clone(), + input: String::default(), + users: Rc::new(Vec::default()), + params: GetAssignableUserParams { + username: None, + project: None, + issue_key: Some(issue_key.clone()), + max_results: None, + }, + }; + + completer.users = Rc::new( + client + .get_assignable_users(&completer.params)? + .iter() + .map(|u| JiraUser(u.to_owned())) + .collect(), + ); + + let username = Text::new("Assign:") + .with_autocomplete(completer) .prompt() .wrap_err("No user selected")?; - client.post_assign_user(&issue_key, &user.0)?; - Ok(format!("Assigned {} to {}", user, issue_key)) + let maybe_user = client.get_assignable_users(&GetAssignableUserParams { + username: Some(username), + project: None, + issue_key: None, + max_results: None, + })?; + + if let Some(user) = maybe_user.first() { + client.post_assign_user(&issue_key, &user.clone())?; + Ok(format!("Assigned {} to {}", user.display_name, issue_key)) + } else { + Err(eyre!("Invalid username, user not found")) + } + } +} + +#[derive(Clone)] +struct AssignableUsersCompleter { + client: JiraAPIClient, + input: String, + users: Rc>, + params: GetAssignableUserParams, +} + +impl AssignableUsersCompleter { + pub fn stringify_users(&self) -> Vec { + self.users + .iter() + .map(|u| u.to_string()) + .collect::>() + } +} + +impl Autocomplete for AssignableUsersCompleter { + fn get_suggestions( + &mut self, + input: &str, + ) -> std::result::Result, inquire::CustomUserError> { + if input == self.input { + return Ok(self.stringify_users()); + } + + let users = self.client.get_assignable_users(&self.params)?; + self.users = Rc::new(users.iter().map(|u| JiraUser(u.to_owned())).collect()); + Ok(self.stringify_users()) + } + + fn get_completion( + &mut self, + _input: &str, + highlighted_suggestion: Option, + ) -> std::result::Result { + Ok(match highlighted_suggestion { + Some(suggestion) => Replacement::Some(suggestion), + None => match self.users.first() { + Some(user) => Replacement::Some(user.0.display_name.clone()), + None => Replacement::None, + }, + }) } } diff --git a/jig-cli/src/main.rs b/jig-cli/src/main.rs index e4cc581..bf6637f 100644 --- a/jig-cli/src/main.rs +++ b/jig-cli/src/main.rs @@ -24,6 +24,7 @@ struct Cli { enum Commands { /// Assign user to issue #[command(alias = "a")] + #[cfg(debug_assertions)] Assign(Assign), /// Create and checkout branch using issue key with(out) summary as branch name #[command(alias = "b")] @@ -59,6 +60,7 @@ impl Commands { let cfg = config::Config::load().wrap_err("Failed to load config"); match args.command { + #[cfg(debug_assertions)] Commands::Assign(assign) => assign.exec(&cfg?), Commands::Branch(branch) => branch.exec(&cfg?), Commands::Comment(comment) => comment.exec(&cfg?), @@ -87,5 +89,8 @@ fn main() -> Result<()> { } } + #[cfg(target_os = "windows")] + println!(""); + Ok(()) } diff --git a/jira/src/client.rs b/jira/src/client.rs index 95b9761..58ac480 100644 --- a/jira/src/client.rs +++ b/jira/src/client.rs @@ -7,7 +7,7 @@ use reqwest::Url; use std::convert::From; use std::time::Duration; -/// +/// Used to configure JiraApiClient upon instantiation #[derive(Debug, Clone)] pub struct JiraClientConfig { pub credential: Credential, @@ -192,16 +192,26 @@ impl JiraAPIClient { Ok(response) } - pub fn get_assignable_users(&self, issue_key: &IssueKey) -> Result> { - let users_url = format!( - "{}/rest/api/latest/user/assignable/search?issueKey={}", - self.url.clone(), - issue_key + pub fn get_assignable_users(&self, params: &GetAssignableUserParams) -> Result> { + let mut users_url = format!( + "{}/rest/api/latest/user/assignable/search", + self.url.clone() ); + users_url.push_str(format!("?maxResults={}", params.max_results.unwrap_or(1000)).as_str()); + if let Some(issue_key) = params.issue_key.clone() { + users_url.push_str(format!("&issueKey={}", issue_key).as_str()); + } + if let Some(username) = params.username.clone() { + users_url.push_str(format!("&username={}", username).as_str()); + } + if let Some(project) = params.project.clone() { + users_url.push_str(format!("&project={}", project).as_str()); + } + let response = self.client.get(users_url).send().wrap_err(format!( - "Unable to fetch assignable users for issue: {}", - issue_key + "Unable to fetch assignable users for issue: {:?}", + params ))?; response diff --git a/jira/src/types.rs b/jira/src/types.rs index ea0e80e..f50132f 100644 --- a/jira/src/types.rs +++ b/jira/src/types.rs @@ -94,6 +94,15 @@ mod versioned { pub use versioned::*; +/// Define query parameters +#[derive(Debug, Clone)] +pub struct GetAssignableUserParams { + pub username: Option, + pub project: Option, + pub issue_key: Option, + pub max_results: Option, +} + /// Comment related types #[derive(Serialize, Debug, Clone)] #[serde(rename_all = "camelCase")]