Skip to content

Commit

Permalink
feat: support for cloud installations
Browse files Browse the repository at this point in the history
  • Loading branch information
xederro committed Dec 29, 2024
1 parent e27b642 commit 4d4578f
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 34 deletions.
35 changes: 22 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,39 +38,48 @@ file is `~/.config/punchout/punchout.toml`.
```toml
[jira]
jira_url = "https://jira.company.com"
jira_token = "XXX"

# for on-premise installations
# you can use a JIRA PAT token here:
# jira_token = "XXX"

# or if you use cloud instance
# use your jira username and API token:
# jira_cloud_username = "[email protected]"
# jira_cloud_token = "XXX"

# put whatever JQL you want to query for
jql = "assignee = currentUser() AND updatedDate >= -14d ORDER BY updatedDate DESC"

# I don't know how many people will find use for this.
# I need this, since the JIRA server I use runs 5 hours behind
# the actual time, for whatever reason 🤷
jira_time_delta_mins = 300
# jira_time_delta_mins = 300
```

*Note: `punchout` only supports [on-premise] installations of JIRA for now. I
might add support for cloud installations in the future.*

### Using command line flags

Use `punchout -h` for help.

```bash
punchout \
db-path='/path/to/punchout/db/file.db' \
jira-url='https://jira.company.com' \
jira-token='XXX' \
jql='assignee = currentUser() AND updatedDate >= -14d ORDER BY updatedDate DESC' \
jira-time-delta-mins='300'
[ -db-path='/path/to/punchout/db/file.db' ] \
[ -jira-url='https://jira.company.com' ] \
[ -jira-token='XXX' | { jira-cloud-token='XXX' jira-cloud-username='[email protected]' } ] \
[ -jql='assignee = currentUser() AND updatedDate >= -14d ORDER BY updatedDate DESC' ] \
[ -jira-time-delta-mins='300' ] \
[ -config-file-path='/path/to/punchout/config/file.toml' ] \
[ -list-config ]
```
Both the config file and the command line flags can be used in conjunction, but
the latter will take precedence over the former.
```bash
punchout \
config-file-path='/path/to/punchout/config/file.toml' \
jira-token='XXX' \
jql='assignee = currentUser() AND updatedDate >= -14d ORDER BY updatedDate DESC'
-config-file-path='/path/to/punchout/config/file.toml' \
-jira-token='XXX' \
-jql='assignee = currentUser() AND updatedDate >= -14d ORDER BY updatedDate DESC'
```
🖥️ Screenshots
Expand Down
6 changes: 4 additions & 2 deletions cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ import (

type JiraConfig struct {
JiraURL *string `toml:"jira_url"`
JiraToken *string `toml:"jira_token"`
Jql *string
JiraTimeDeltaMins int `toml:"jira_time_delta_mins"`
JiraTimeDeltaMins int `toml:"jira_time_delta_mins"`
JiraToken *string `toml:"jira_token"`
JiraCloudToken *string `toml:"jira_cloud_token"`
JiraCloudUsername *string `toml:"jira_cloud_username"`
}

type POConfig struct {
Expand Down
79 changes: 60 additions & 19 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ package cmd
import (
"flag"
"fmt"
"net/http"
"os"
"os/user"
"strconv"

jira "github.com/andygrunwald/go-jira/v2/onpremise"
jiraCloud "github.com/andygrunwald/go-jira/v2/cloud"
jiraOnPremise "github.com/andygrunwald/go-jira/v2/onpremise"
"github.com/dhth/punchout/internal/ui"
)

Expand All @@ -19,6 +21,8 @@ func die(msg string, args ...any) {
var (
jiraURL = flag.String("jira-url", "", "URL of the JIRA server")
jiraToken = flag.String("jira-token", "", "personal access token for the JIRA server")
jiraCloudToken = flag.String("jira-cloud-token", "", "API token for the JIRA cloud")
jiraCloudUsername = flag.String("jira-cloud-username", "", "username for the JIRA cloud")
jql = flag.String("jql", "", "JQL to use to query issues at startup")
jiraTimeDeltaMinsStr = flag.String("jira-time-delta-mins", "", "Time delta (in minutes) between your timezone and the timezone of the server; can be +/-")
listConfig = flag.Bool("list-config", false, "Whether to only print out the config that punchout will use or not")
Expand Down Expand Up @@ -76,51 +80,88 @@ func Execute() {
poCfg.Jira.JiraToken = jiraToken
}

if *jiraCloudToken != "" {
poCfg.Jira.JiraCloudToken = jiraCloudToken
}

if *jiraCloudUsername != "" {
poCfg.Jira.JiraCloudUsername = jiraCloudUsername
}

if *jql != "" {
poCfg.Jira.Jql = jql
}
if *jiraTimeDeltaMinsStr != "" {
poCfg.Jira.JiraTimeDeltaMins = jiraTimeDeltaMins
}

// validations
if poCfg.Jira.JiraURL == nil || *poCfg.Jira.JiraURL == "" {
die("jira-url cannot be empty")
}

if poCfg.Jira.Jql == nil || *poCfg.Jira.Jql == "" {
die("jql cannot be empty")
}

if (poCfg.Jira.JiraToken == nil) == (poCfg.Jira.JiraCloudToken == nil) {
die("only one of on-premise or cloud auth method must be provided")
}

if poCfg.Jira.JiraToken != nil && *poCfg.Jira.JiraToken == "" {
die("jira-token cannot be empty for on premise auth")
}

if poCfg.Jira.JiraCloudToken != nil && *poCfg.Jira.JiraCloudToken == "" {
die("jira-token cannot be empty for cloud auth")
}

if poCfg.Jira.JiraCloudToken != nil && (poCfg.Jira.JiraCloudUsername == nil || *poCfg.Jira.JiraCloudUsername == "") {
die("jira-username cannot be empty for cloud auth")
}

configKeyMaxLen := 40
if *listConfig {
fmt.Fprint(os.Stdout, "Config:\n\n")
fmt.Fprintf(os.Stdout, "%s%s\n", ui.RightPadTrim("Config File Path", configKeyMaxLen), *configFilePath)
fmt.Fprintf(os.Stdout, "%s%s\n", ui.RightPadTrim("DB File Path", configKeyMaxLen), dbPathFull)
fmt.Fprintf(os.Stdout, "%s%s\n", ui.RightPadTrim("JIRA URL", configKeyMaxLen), *poCfg.Jira.JiraURL)
fmt.Fprintf(os.Stdout, "%s%s\n", ui.RightPadTrim("JIRA Token", configKeyMaxLen), *poCfg.Jira.JiraToken)
if poCfg.Jira.JiraToken != nil {
fmt.Fprintf(os.Stdout, "%s%s\n", ui.RightPadTrim("JIRA Token", configKeyMaxLen), *poCfg.Jira.JiraToken)
}
if poCfg.Jira.JiraCloudToken != nil {
fmt.Fprintf(os.Stdout, "%s%s\n", ui.RightPadTrim("JIRA API Token", configKeyMaxLen), *poCfg.Jira.JiraCloudToken)
fmt.Fprintf(os.Stdout, "%s%s\n", ui.RightPadTrim("JIRA Username", configKeyMaxLen), *poCfg.Jira.JiraCloudUsername)
}
fmt.Fprintf(os.Stdout, "%s%s\n", ui.RightPadTrim("JQL", configKeyMaxLen), *poCfg.Jira.Jql)
fmt.Fprintf(os.Stdout, "%s%d\n", ui.RightPadTrim("JIRA Time Delta Mins", configKeyMaxLen), poCfg.Jira.JiraTimeDeltaMins)
os.Exit(0)
}

// validations
if poCfg.Jira.JiraURL == nil || *poCfg.Jira.JiraURL == "" {
die("jira-url cannot be empty")
}

if poCfg.Jira.JiraToken == nil || *poCfg.Jira.JiraToken == "" {
die("jira-token cannot be empty")
}

if poCfg.Jira.Jql == nil || *poCfg.Jira.Jql == "" {
die("jql cannot be empty")
}

db, err := setupDB(dbPathFull)
if err != nil {
die("couldn't set up punchout database. This is a fatal error\n")
}

tp := jira.BearerAuthTransport{
Token: *poCfg.Jira.JiraToken,
// setup jira client with one of available auth methods
var client *http.Client
if poCfg.Jira.JiraToken != nil {
tp := jiraOnPremise.BearerAuthTransport{
Token: *poCfg.Jira.JiraToken,
}
client = tp.Client()
} else {
tp := jiraCloud.BasicAuthTransport{
Username: *poCfg.Jira.JiraCloudUsername,
APIToken: *poCfg.Jira.JiraCloudToken,
}
client = tp.Client()
}
cl, err := jira.NewClient(*poCfg.Jira.JiraURL, tp.Client())

cl, err := jiraOnPremise.NewClient(*poCfg.Jira.JiraURL, client)
if err != nil {
panic(err)
}

ui.RenderUI(db, cl, *poCfg.Jira.Jql, poCfg.Jira.JiraTimeDeltaMins)

}

0 comments on commit 4d4578f

Please sign in to comment.