-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for cloud installations #24
Conversation
Thanks for your contribution! :) |
Hi. Thanks again for the change. Sry, took a while to get to it. I tried it out locally, and it works great. I have a few minor/cosmetic tweaks to your change that I think make sense to be merged alongside this PR. Do you mind if I push directly to your branch? Here's the patch if it helps. expanddiff --git a/cmd/config.go b/cmd/config.go
index 10e12d5..6046753 100644
--- a/cmd/config.go
+++ b/cmd/config.go
@@ -1,30 +1,33 @@
package cmd
import (
"github.com/BurntSushi/toml"
)
+const (
+ jiraInstallationTypeOnPremise = "onpremise"
+ jiraInstallationTypeCloud = "cloud"
+)
+
type JiraConfig struct {
+ InstallationType string `toml:"installation_type"`
JiraURL *string `toml:"jira_url"`
Jql *string
JiraTimeDeltaMins int `toml:"jira_time_delta_mins"`
JiraToken *string `toml:"jira_token"`
- JiraCloudToken *string `toml:"jira_cloud_token"`
- JiraCloudUsername *string `toml:"jira_cloud_username"`
+ JiraUsername *string `toml:"jira_username"`
}
type POConfig struct {
Jira JiraConfig
}
func readConfig(filePath string) (POConfig, error) {
-
var config POConfig
_, err := toml.DecodeFile(expandTilde(filePath), &config)
if err != nil {
return config, err
}
return config, nil
-
}
diff --git a/cmd/root.go b/cmd/root.go
index 16f2bd1..daf3fe1 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -12,32 +12,31 @@ import (
jiraOnPremise "github.com/andygrunwald/go-jira/v2/onpremise"
"github.com/dhth/punchout/internal/ui"
)
func die(msg string, args ...any) {
fmt.Fprintf(os.Stderr, msg+"\n", args...)
os.Exit(1)
}
var (
+ jiraInstallationType = flag.String("jira-installation-type", "", "JIRA installation type; allowed values: [cloud, onpremise]")
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")
+ jiraUsername = flag.String("jira-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")
)
func Execute() {
currentUser, err := user.Current()
-
if err != nil {
die("Error getting your home directory, explicitly specify the path for the config file using -config-file-path")
}
defaultConfigFP := fmt.Sprintf("%s/.config/punchout/punchout.toml", currentUser.HomeDir)
configFilePath := flag.String("config-file-path", defaultConfigFP, "location of the punchout config file")
defaultDBPath := fmt.Sprintf("%s/punchout.v%s.db", currentUser.HomeDir, PUNCHOUT_DB_VERSION)
dbPath := flag.String("db-path", defaultDBPath, "location where punchout should create its DB file")
@@ -59,109 +58,109 @@ func Execute() {
dbPathFull := expandTilde(*dbPath)
var jiraTimeDeltaMins int
if *jiraTimeDeltaMinsStr != "" {
jiraTimeDeltaMins, err = strconv.Atoi(*jiraTimeDeltaMinsStr)
if err != nil {
die("couldn't convert jira-time-delta-mins to a number")
}
}
- poCfg, err := readConfig(*configFilePath)
+ cfg, err := readConfig(*configFilePath)
if err != nil {
- fmt.Fprintf(os.Stderr, "error reading config at %s: %s.\n"+
- "continue with command line args only\n", *configFilePath, err.Error())
+ die("error reading config: %s.\n", err.Error())
+ }
+
+ if *jiraInstallationType != "" {
+ cfg.Jira.InstallationType = *jiraInstallationType
}
if *jiraURL != "" {
- poCfg.Jira.JiraURL = jiraURL
+ cfg.Jira.JiraURL = jiraURL
}
if *jiraToken != "" {
- poCfg.Jira.JiraToken = jiraToken
+ cfg.Jira.JiraToken = jiraToken
}
- if *jiraCloudToken != "" {
- poCfg.Jira.JiraCloudToken = jiraCloudToken
- }
-
- if *jiraCloudUsername != "" {
- poCfg.Jira.JiraCloudUsername = jiraCloudUsername
+ if *jiraUsername != "" {
+ cfg.Jira.JiraUsername = jiraUsername
}
if *jql != "" {
- poCfg.Jira.Jql = jql
+ cfg.Jira.Jql = jql
}
+
if *jiraTimeDeltaMinsStr != "" {
- poCfg.Jira.JiraTimeDeltaMins = jiraTimeDeltaMins
+ cfg.Jira.JiraTimeDeltaMins = jiraTimeDeltaMins
}
// validations
- if poCfg.Jira.JiraURL == nil || *poCfg.Jira.JiraURL == "" {
+ var installationType ui.JiraInstallationType
+ switch cfg.Jira.InstallationType {
+ case "", jiraInstallationTypeOnPremise: // "" to maintain backwards compatibility
+ installationType = ui.OnPremiseInstallation
+ case jiraInstallationTypeCloud:
+ installationType = ui.CloudInstallation
+ default:
+ die("invalid value for jira installation type (allowed values: [%s, %s]): %q", jiraInstallationTypeOnPremise, jiraInstallationTypeCloud, cfg.Jira.InstallationType)
+ }
+
+ if cfg.Jira.JiraURL == nil || *cfg.Jira.JiraURL == "" {
die("jira-url cannot be empty")
}
- if poCfg.Jira.Jql == nil || *poCfg.Jira.Jql == "" {
+ if cfg.Jira.Jql == nil || *cfg.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 cfg.Jira.JiraToken != nil && *cfg.Jira.JiraToken == "" {
+ die("jira-token cannot be empty")
}
- 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")
+ if installationType == ui.CloudInstallation && (cfg.Jira.JiraUsername == nil || *cfg.Jira.JiraUsername == "") {
+ die("jira-username cannot be empty for installation type \"cloud\"")
}
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)
- if poCfg.Jira.JiraToken != nil {
- fmt.Fprintf(os.Stdout, "%s%s\n", ui.RightPadTrim("JIRA Token", configKeyMaxLen), *poCfg.Jira.JiraToken)
+ fmt.Fprintf(os.Stdout, "%s%s\n", ui.RightPadTrim("JIRA Installation Type", configKeyMaxLen), cfg.Jira.InstallationType)
+ fmt.Fprintf(os.Stdout, "%s%s\n", ui.RightPadTrim("JIRA URL", configKeyMaxLen), *cfg.Jira.JiraURL)
+ fmt.Fprintf(os.Stdout, "%s%s\n", ui.RightPadTrim("JIRA Token", configKeyMaxLen), *cfg.Jira.JiraToken)
+ if installationType == ui.CloudInstallation {
+ fmt.Fprintf(os.Stdout, "%s%s\n", ui.RightPadTrim("JIRA Username", configKeyMaxLen), *cfg.Jira.JiraUsername)
}
- 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)
+ fmt.Fprintf(os.Stdout, "%s%s\n", ui.RightPadTrim("JQL", configKeyMaxLen), *cfg.Jira.Jql)
+ fmt.Fprintf(os.Stdout, "%s%d\n", ui.RightPadTrim("JIRA Time Delta Mins", configKeyMaxLen), cfg.Jira.JiraTimeDeltaMins)
os.Exit(0)
}
db, err := setupDB(dbPathFull)
if err != nil {
die("couldn't set up punchout database. This is a fatal error\n")
}
- // setup jira client with one of available auth methods
- var client *http.Client
- if poCfg.Jira.JiraToken != nil {
+ var httpClient *http.Client
+ switch installationType {
+ case ui.OnPremiseInstallation:
tp := jiraOnPremise.BearerAuthTransport{
- Token: *poCfg.Jira.JiraToken,
+ Token: *cfg.Jira.JiraToken,
}
- client = tp.Client()
- } else {
+ httpClient = tp.Client()
+ case ui.CloudInstallation:
tp := jiraCloud.BasicAuthTransport{
- Username: *poCfg.Jira.JiraCloudUsername,
- APIToken: *poCfg.Jira.JiraCloudToken,
+ Username: *cfg.Jira.JiraUsername,
+ APIToken: *cfg.Jira.JiraToken,
}
- client = tp.Client()
+ httpClient = tp.Client()
}
- cl, err := jiraOnPremise.NewClient(*poCfg.Jira.JiraURL, client)
+ cl, err := jiraOnPremise.NewClient(*cfg.Jira.JiraURL, httpClient)
if err != nil {
- panic(err)
+ die("couldn't create JIRA client: %s", err)
}
- ui.RenderUI(db, cl, *poCfg.Jira.Jql, poCfg.Jira.JiraTimeDeltaMins)
+ ui.RenderUI(db, cl, installationType, *cfg.Jira.Jql, cfg.Jira.JiraTimeDeltaMins)
}
diff --git a/internal/ui/cmds.go b/internal/ui/cmds.go
index e6a75b5..9e2e861 100644
--- a/internal/ui/cmds.go
+++ b/internal/ui/cmds.go
@@ -159,48 +159,52 @@ func updateSyncStatusForEntry(db *sql.DB, entry worklogEntry, index int) tea.Cmd
return logEntrySyncUpdated{
entry: entry,
index: index,
err: err,
}
}
}
func fetchJIRAIssues(cl *jira.Client, jql string) tea.Cmd {
return func() tea.Msg {
- jIssues, err := getIssues(cl, jql)
+ jIssues, statusCode, err := getIssues(cl, jql)
var issues []Issue
+ if err != nil {
+ return issuesFetchedFromJIRAMsg{issues, statusCode, err}
+ }
+
for _, issue := range jIssues {
var assignee string
var totalSecsSpent int
var status string
if issue.Fields != nil {
if issue.Fields.Assignee != nil {
- assignee = issue.Fields.Assignee.Name
+ assignee = issue.Fields.Assignee.DisplayName
}
totalSecsSpent = issue.Fields.AggregateTimeSpent
if issue.Fields.Status != nil {
status = issue.Fields.Status.Name
}
}
issues = append(issues, Issue{
issueKey: issue.Key,
issueType: issue.Fields.Type.Name,
summary: issue.Fields.Summary,
assignee: assignee,
status: status,
aggSecondsSpent: totalSecsSpent,
trackingActive: false,
})
}
- return issuesFetchedFromJIRAMsg{issues, err}
+ return issuesFetchedFromJIRAMsg{issues, statusCode, nil}
}
}
func syncWorklogWithJIRA(cl *jira.Client, entry worklogEntry, index int, timeDeltaMins int) tea.Cmd {
return func() tea.Msg {
err := addWLtoJira(cl, entry, timeDeltaMins)
return wlAddedOnJIRA{index, entry, err}
}
}
diff --git a/internal/ui/initial.go b/internal/ui/initial.go
index 49c0fb7..36f573b 100644
--- a/internal/ui/initial.go
+++ b/internal/ui/initial.go
@@ -2,21 +2,21 @@ package ui
import (
"database/sql"
jira "github.com/andygrunwald/go-jira/v2/onpremise"
"github.com/charmbracelet/bubbles/list"
"github.com/charmbracelet/bubbles/textinput"
"github.com/charmbracelet/lipgloss"
)
-func InitialModel(db *sql.DB, jiraClient *jira.Client, jql string, jiraTimeDeltaMins int, debug bool) model {
+func InitialModel(db *sql.DB, jiraClient *jira.Client, installationType JiraInstallationType, jql string, jiraTimeDeltaMins int, debug bool) model {
var stackItems []list.Item
var worklogListItems []list.Item
var syncedWorklogListItems []list.Item
trackingInputs := make([]textinput.Model, 3)
trackingInputs[entryBeginTS] = textinput.New()
trackingInputs[entryBeginTS].Placeholder = "09:30"
trackingInputs[entryBeginTS].Focus()
trackingInputs[entryBeginTS].CharLimit = len(string(timeFormat))
trackingInputs[entryBeginTS].Width = 30
@@ -29,20 +29,21 @@ func InitialModel(db *sql.DB, jiraClient *jira.Client, jql string, jiraTimeDelta
trackingInputs[entryComment] = textinput.New()
trackingInputs[entryComment].Placeholder = "Your comment goes here"
trackingInputs[entryComment].Focus()
trackingInputs[entryComment].CharLimit = 255
trackingInputs[entryComment].Width = 60
m := model{
db: db,
jiraClient: jiraClient,
+ installationType: installationType,
jql: jql,
issueList: list.New(stackItems, newItemDelegate(lipgloss.Color(issueListColor)), listWidth, 0),
issueMap: make(map[string]*Issue),
issueIndexMap: make(map[string]int),
worklogList: list.New(worklogListItems, newItemDelegate(lipgloss.Color(worklogListColor)), listWidth, 0),
syncedWorklogList: list.New(syncedWorklogListItems, newItemDelegate(syncedWorklogListColor), listWidth, 0),
jiraTimeDeltaMins: jiraTimeDeltaMins,
showHelpIndicator: true,
trackingInputs: trackingInputs,
debug: debug,
diff --git a/internal/ui/jira.go b/internal/ui/jira.go
index c0cc823..e083715 100644
--- a/internal/ui/jira.go
+++ b/internal/ui/jira.go
@@ -1,27 +1,25 @@
package ui
import (
"context"
"errors"
"time"
jira "github.com/andygrunwald/go-jira/v2/onpremise"
)
-var (
- jiraRepliedWithEmptyWorklogErr = errors.New("JIRA replied with an empty worklog; something is probably wrong")
-)
+var jiraRepliedWithEmptyWorklogErr = errors.New("JIRA replied with an empty worklog; something is probably wrong")
-func getIssues(cl *jira.Client, jql string) ([]jira.Issue, error) {
- issues, _, err := cl.Issue.Search(context.Background(), jql, nil)
- return issues, err
+func getIssues(cl *jira.Client, jql string) ([]jira.Issue, int, error) {
+ issues, resp, err := cl.Issue.Search(context.Background(), jql, nil)
+ return issues, resp.StatusCode, err
}
func addWLtoJira(cl *jira.Client, entry worklogEntry, timeDeltaMins int) error {
start := entry.BeginTS
if timeDeltaMins != 0 {
start = start.Add(time.Minute * time.Duration(timeDeltaMins))
}
timeSpentSecs := int(entry.EndTS.Sub(entry.BeginTS).Seconds())
diff --git a/internal/ui/model.go b/internal/ui/model.go
index caf66a7..a17847f 100644
--- a/internal/ui/model.go
+++ b/internal/ui/model.go
@@ -4,20 +4,27 @@ import (
"database/sql"
"time"
jira "github.com/andygrunwald/go-jira/v2/onpremise"
"github.com/charmbracelet/bubbles/list"
"github.com/charmbracelet/bubbles/textinput"
"github.com/charmbracelet/bubbles/viewport"
tea "github.com/charmbracelet/bubbletea"
)
+type JiraInstallationType uint
+
+const (
+ OnPremiseInstallation JiraInstallationType = iota
+ CloudInstallation
+)
+
type trackingStatus uint
const (
trackingInactive trackingStatus = iota
trackingActive
)
type dBChange uint
const (
@@ -56,20 +63,21 @@ const (
dayAndTimeFormat = "Mon, 15:04"
dateFormat = "2006/01/02"
timeOnlyFormat = "15:04"
)
type model struct {
activeView stateView
lastView stateView
db *sql.DB
jiraClient *jira.Client
+ installationType JiraInstallationType
jql string
issueList list.Model
issueMap map[string]*Issue
issueIndexMap map[string]int
issuesFetched bool
worklogList list.Model
unsyncedWLCount uint
unsyncedWLSecsSpent int
syncedWorklogList list.Model
activeIssueBeginTS time.Time
diff --git a/internal/ui/msgs.go b/internal/ui/msgs.go
index 55ad2bb..323fee4 100644
--- a/internal/ui/msgs.go
+++ b/internal/ui/msgs.go
@@ -45,22 +45,23 @@ type logEntriesDeletedMsg struct {
err error
}
type logEntrySyncUpdated struct {
entry worklogEntry
index int
err error
}
type issuesFetchedFromJIRAMsg struct {
- issues []Issue
- err error
+ issues []Issue
+ responseStatusCode int
+ err error
}
type wlAddedOnJIRA struct {
index int
entry worklogEntry
err error
}
type urlOpenedinBrowserMsg struct {
url string
diff --git a/internal/ui/render_helpers.go b/internal/ui/render_helpers.go
index 0c43774..3a1f2c4 100644
--- a/internal/ui/render_helpers.go
+++ b/internal/ui/render_helpers.go
@@ -4,21 +4,21 @@ import "fmt"
func (issue *Issue) setDesc() {
// TODO: The padding here is a bit of a mess; make it more readable
var assignee string
var status string
var totalSecsSpent string
issueType := getIssueTypeStyle(issue.issueType).Render(issue.issueType)
if issue.assignee != "" {
- assignee = assigneeStyle(issue.assignee).Render(RightPadTrim("@"+issue.assignee, int(listWidth/4)))
+ assignee = assigneeStyle(issue.assignee).Render(RightPadTrim(issue.assignee, int(listWidth/4)))
} else {
assignee = assigneeStyle(issue.assignee).Render(RightPadTrim("", int(listWidth/4)))
}
status = issueStatusStyle.Render(RightPadTrim(issue.status, int(listWidth/4)))
if issue.aggSecondsSpent > 0 {
totalSecsSpent = aggTimeSpentStyle.Render(humanizeDuration(issue.aggSecondsSpent))
}
diff --git a/internal/ui/styles.go b/internal/ui/styles.go
index 901421d..c054bcf 100644
--- a/internal/ui/styles.go
+++ b/internal/ui/styles.go
@@ -1,20 +1,22 @@
package ui
import (
- "github.com/charmbracelet/lipgloss"
"hash/fnv"
+
+ "github.com/charmbracelet/lipgloss"
)
const (
defaultBackgroundColor = "#282828"
issueListUnfetchedColor = "#928374"
+ failureColor = "#fb4934"
issueListColor = "#fe8019"
worklogListColor = "#fabd2f"
syncedWorklogListColor = "#b8bb26"
trackingColor = "#fe8019"
unsyncedCountColor = "#fabd2f"
activeIssueKeyColor = "#d3869b"
activeIssueSummaryColor = "#8ec07c"
trackingBeganColor = "#fabd2f"
issueStatusColor = "#665c54"
toolNameColor = "#b8bb26"
diff --git a/internal/ui/ui.go b/internal/ui/ui.go
index 85bfab9..67a85f4 100644
--- a/internal/ui/ui.go
+++ b/internal/ui/ui.go
@@ -2,27 +2,27 @@ package ui
import (
"database/sql"
"fmt"
"os"
jira "github.com/andygrunwald/go-jira/v2/onpremise"
tea "github.com/charmbracelet/bubbletea"
)
-func RenderUI(db *sql.DB, jiraClient *jira.Client, jql string, jiraTimeDeltaMins int) {
+func RenderUI(db *sql.DB, jiraClient *jira.Client, installationType JiraInstallationType, jql string, jiraTimeDeltaMins int) {
if len(os.Getenv("DEBUG_LOG")) > 0 {
f, err := tea.LogToFile("debug.log", "debug")
if err != nil {
fmt.Println("fatal:", err)
os.Exit(1)
}
defer f.Close()
}
debug := os.Getenv("DEBUG") == "true"
- p := tea.NewProgram(InitialModel(db, jiraClient, jql, jiraTimeDeltaMins, debug), tea.WithAltScreen())
+ p := tea.NewProgram(InitialModel(db, jiraClient, installationType, jql, jiraTimeDeltaMins, debug), tea.WithAltScreen())
if _, err := p.Run(); err != nil {
fmt.Printf("Alas, there has been an error: %v", err)
os.Exit(1)
}
}
diff --git a/internal/ui/update.go b/internal/ui/update.go
index 5b9c177..3f43604 100644
--- a/internal/ui/update.go
+++ b/internal/ui/update.go
@@ -432,23 +432,35 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.helpVP.HighPerformanceRendering = useHighPerformanceRenderer
m.helpVP.SetContent(helpText)
m.helpVPReady = true
} else {
m.helpVP.Height = m.terminalHeight - 7
m.helpVP.Width = w - 5
}
case issuesFetchedFromJIRAMsg:
if msg.err != nil {
- message := "error fetching issues from JIRA: " + msg.err.Error()
- m.message = message
- m.messages = append(m.messages, message)
+ var remoteServerName string
+ if msg.responseStatusCode >= 400 && msg.responseStatusCode < 500 {
+ switch m.installationType {
+ case OnPremiseInstallation:
+ remoteServerName = "Your on-premise installation"
+ case CloudInstallation:
+ remoteServerName = "Atlassian Cloud"
+ }
+ m.message = fmt.Sprintf("%s returned a %d status codel, check if your configuration is correct", remoteServerName, msg.responseStatusCode)
+ } else {
+ m.message = fmt.Sprintf("error fetching issues from JIRA: %s", msg.err.Error())
+ }
+ m.messages = append(m.messages, m.message)
+ m.issueList.Title = "Failure"
+ m.issueList.Styles.Title = m.issueList.Styles.Title.Background(lipgloss.Color(failureColor))
} else {
issues := make([]list.Item, 0, len(msg.issues))
for i, issue := range msg.issues {
issue.setDesc()
issues = append(issues, &issue)
m.issueMap[issue.issueKey] = &issue
m.issueIndexMap[issue.issueKey] = i
}
m.issueList.SetItems(issues)
m.issueList.Title = "Issues" |
Hi. No worries, I understand that sometimes it's hard to find a moment, especially around New Year's Eve. [jira]
jira_url = "https://jira.company.com"
# allowed values: [cloud, onpremise]
# default "onpremise"
# installation_type = "onpremise"
# for on-premise installations
# you can use a JIRA PAT token here
# or if you use cloud instance
# use your API token:
jira_token = "XXX"
# required when you use cloud instance
# jira_username = "[email protected]"
# 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 and punchout \
[ -db-path='/path/to/punchout/db/file.db' ] \
[ -jira-url='https://jira.company.com' ] \
[ -jira-token='XXX' ] \
[ -jira-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 ] Go ahead, feel free to push directly to my branch👌 |
Done, thanks for the effort! Hope the tool saves you time :) |
* origin/main: feat: add support for cloud installations (#24)
Hello,
I found this repo by accident and wanted to give it a try since I'm building something similar for myself. To test it I also needed support for cloud installations and since this is an open issue #16 and go-jira already supports it I managed to add it. During my work, I also found few things to improve.
So what changed:
to
I hope it's up to your standard.