From e5bd97fde6ba18247041ec33209f0ab2c1e16a36 Mon Sep 17 00:00:00 2001 From: David Date: Mon, 11 May 2026 15:03:55 +0200 Subject: [PATCH 1/2] added flags functionality to login --- cmd/login.go | 110 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 85 insertions(+), 25 deletions(-) diff --git a/cmd/login.go b/cmd/login.go index 7e74747..90bc2b6 100644 --- a/cmd/login.go +++ b/cmd/login.go @@ -26,10 +26,16 @@ var loginCmd = &cobra.Command{ Long: `Enables the login flow, which enables the user to use this tool for a specific OpenProject instance. The login needs the host URL of the OpenProject instance and a -generated API token.`, +generated API token. You can provide both with flags to +skip interactive input.`, Run: login, } +var ( + loginHostFlag string + loginAPIKeyFlag string +) + const ( urlInputError = "There was a problem parsing the input. Please try again and put in a valid URL." missingSchemeError = "URL scheme is missing, please define a complete URL." @@ -41,51 +47,84 @@ func login(_ *cobra.Command, _ []string) { var hostUrl *url.URL var token string - for { - printer.Debug(Verbose, "Parsing host URL ...") - printer.Input("OpenProject host URL: ") - - ok, msg, host := parseHostUrl() + if loginHostFlag != "" { + printer.Debug(Verbose, "Parsing host URL from --host ...") + ok, msg, host := parseHostUrlInput(loginHostFlag) if !ok { printer.ErrorText(msg) - continue + return } printer.Debug(Verbose, "Initializing requests client ...") requests.Init(host, "", Verbose) - ok = checkOpenProjectApi() - if !ok { + if !checkOpenProjectApi() { printer.ErrorText(noOpInstanceError) - continue + return } hostUrl = host - break - } - - for { - fmt.Printf("OpenProject API Token (Visit %s/my/access_tokens to generate one): ", hostUrl) - ok, t := requestApiToken() - if !ok { - fmt.Println(tokenInputError) - continue + } else { + for { + printer.Debug(Verbose, "Parsing host URL ...") + printer.Input("OpenProject host URL: ") + + ok, msg, host := parseHostUrl() + if !ok { + printer.ErrorText(msg) + continue + } + + printer.Debug(Verbose, "Initializing requests client ...") + requests.Init(host, "", Verbose) + ok = checkOpenProjectApi() + if !ok { + printer.ErrorText(noOpInstanceError) + continue + } + + hostUrl = host + break } + } - token = common.SanitizeLineBreaks(t) - + if loginAPIKeyFlag != "" { + token = common.SanitizeLineBreaks(loginAPIKeyFlag) requests.Init(hostUrl, token, Verbose) user, err := users.Me() if err != nil { printer.Error(err) - continue + return } if user.Name == "Anonymous" { printer.ErrorText("no authenticate given") - continue + return + } + } else { + for { + fmt.Printf("OpenProject API Token (Visit %s/my/access_tokens to generate one): ", hostUrl) + ok, t := requestApiToken() + if !ok { + fmt.Println(tokenInputError) + continue + } + + token = common.SanitizeLineBreaks(t) + + requests.Init(hostUrl, token, Verbose) + user, err := users.Me() + if err != nil { + printer.Error(err) + continue + } + + if user.Name == "Anonymous" { + printer.ErrorText("no authenticate given") + continue + } + + break } - - break } storeLoginData(hostUrl, token) @@ -100,6 +139,11 @@ func parseHostUrl() (ok bool, errMessage string, host *url.URL) { return false, urlInputError, nil } + return parseHostUrlInput(input) +} + +func parseHostUrlInput(input string) (ok bool, errMessage string, host *url.URL) { + printer.Debug(Verbose, fmt.Sprintf("Parsed input %q.", input)) printer.Debug(Verbose, "Sanitizing input ...") @@ -126,6 +170,22 @@ func parseHostUrl() (ok bool, errMessage string, host *url.URL) { return true, "", parsed } +func init() { + loginCmd.Flags().StringVar( + &loginHostFlag, + "host", + "", + "OpenProject host URL", + ) + + loginCmd.Flags().StringVar( + &loginAPIKeyFlag, + "api-key", + "", + "OpenProject API token", + ) +} + func checkOpenProjectApi() bool { printer.Debug(Verbose, "Fetching API root to check for instance configuration ...") From 0fac45e1b7a9a86338479b3b919bf72c6fce57ef Mon Sep 17 00:00:00 2001 From: David Date: Mon, 11 May 2026 15:17:39 +0200 Subject: [PATCH 2/2] added login flags to README.md --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index c6685be..329cfea 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,18 @@ Open a new shell and completion should work by using the `TAB` key as usual. The OpenProject CLI commands are structured in a common, human-readable pattern. Every command is built as `op VERB RESOURCE [additional information]`. You will see plenty of examples within this section. +### Login + +You are able to login interactively to your openproject instance by running: +```shell +op login +``` + +The login command takes the optional flags `--host` and `--api-key` in order to login non-interactively: +```shell +op login --host https://openproject.org --api-key aaaaabbbbbccccdddddeeeeefffffggggghhhhhiiiiijjjjjkkkkklllllmmmmm +``` + ### Discoverability Discoverability is key. As we won't document every single command within this README, it is important for the CLI tool,