diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 17899d8..146c729 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -6,18 +6,28 @@ stages: variables: CGO_ENABLED: 0 +.install-make: + before_script: + - apk add --no-cache make + test:unit: artifacts: expire_in: 30 minutes paths: - code-coverage.html - before_script: - - apk add --no-cache make + extends: .install-make image: golang:1.13.6-alpine script: - make test_cover_report stage: test +test:lint: + extends: .install-make + image: golangci/golangci-lint:v1.23.1-alpine + script: + - make test_lint + stage: test + pages: artifacts: paths: diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..3897935 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,75 @@ +--- +run: + concurrency: 2 + timeout: 1m + issues-exit-code: 1 + tests: true + +output: + format: colored-line-number + print-issues-lines: true + print-linter-name: true + +linters-settings: + lll: + line-length: 140 + +linters: + enable: + - bodyclose + - deadcode + - depguard + - dogsled + - dupl + - errcheck + - funlen + - gochecknoglobals + - gochecknoinits + - gocognit + - goconst + - gocritic + - gocyclo + - godox + - gofmt + - goimports + - golint + - gomnd + - goprintffuncname + - gosec + - gosimple + - govet + - ineffassign + - interfacer + - lll + - maligned + - misspell + - nakedret + - prealloc + - rowserrcheck + - scopelint + - staticcheck + - structcheck + - stylecheck + - typecheck + - unconvert + - unparam + - unused + - varcheck + - whitespace + - wsl + #disable: + disabe-all: false + fast: false + +issues: + exclude-rules: + - path: version.go + linters: + - gochecknoglobals + - path: _test.go + linters: + - gomnd + - scopelint + - path: cases_test.go + linters: + - gochecknoglobals diff --git a/Makefile b/Makefile index a9d0a64..6679977 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,9 @@ test_unit: test_cover_report: test_unit @go tool cover -html=cover.out -o code-coverage.html +test_lint: + @golangci-lint run --color always + build: @go build -a -v -o $(BIN_FILE) diff --git a/alert.go b/alert.go index 17013a8..b1d4b3e 100644 --- a/alert.go +++ b/alert.go @@ -28,35 +28,36 @@ import ( const iconPath string = "assets/icon/tomato.png" -var notifier *notificator.Notificator - // initNotifier initialises the new desktop notifier. -func initNotifier() { - notifier = notificator.New(notificator.Options{ - DefaultIcon: getIconPath(), +func initNotifier() *notificator.Notificator { + return notificator.New(notificator.Options{ + DefaultIcon: getNotificationIconPath(), AppName: "Pominal", }) } -// getIconPath returns the absolute path of the tomoato icon +// getNotificationIconPath returns the absolute path of the tomoato icon // used for desktop notifications. // If there is an error getting the path to the executing program // then an empty string is returned. -func getIconPath() string { - +func getNotificationIconPath() string { var result string + exe, err := os.Executable() if err != nil { fmt.Printf("ERROR: Unable to determine path to this executable. %s", err.Error()) return result } + result = filepath.Dir(exe) + "/" + iconPath + return result } // alert creates a new desktop notification. -func alert(l string) { +func desktopAlert(l string, notifier *notificator.Notificator) { title := "Pominal notification." text := l + " session has started." - notifier.Push(title, text, "", notificator.UR_NORMAL) + + _ = notifier.Push(title, text, "", notificator.UR_NORMAL) } diff --git a/cases_test.go b/cases_test.go index b49bf19..df17dc3 100644 --- a/cases_test.go +++ b/cases_test.go @@ -135,7 +135,7 @@ var sessionsTestCases = []struct { }, { name: "Test session 4", - description: "When the second work session has finished and the long break has started because the maximum number of work sessions is reached.", + description: "When the second work session has finished and the long break has started.", expectedWorkSession: 2, expectedCycle: 1, expectedLabel: longBreakTimerLabel, diff --git a/main.go b/main.go index 8d6a49e..d6296b7 100644 --- a/main.go +++ b/main.go @@ -20,30 +20,27 @@ package main import ( "flag" - "fmt" + "log" "os" "time" "github.com/rivo/tview" ) -var ( - workTime string - shortBreakTime string - longBreakTime string - maxWorkSessions int - printVersion bool -) +func main() { + var ( + workTime string + shortBreakTime string + longBreakTime string + maxWorkSessions int + printVersion bool + ) -func init() { flag.StringVar(&workTime, "work", "25m", "sets the timer for your work session.") flag.StringVar(&shortBreakTime, "short-break", "5m", "sets the timer for your short break.") flag.StringVar(&longBreakTime, "long-break", "20m", "sets the timer for your long break.") flag.IntVar(&maxWorkSessions, "max-work-sessions", 4, "sets the maximum number of work cycles to complete before taking a long break.") flag.BoolVar(&printVersion, "version", false, "print version and exit.") -} - -func main() { flag.Parse() if printVersion { @@ -51,24 +48,19 @@ func main() { os.Exit(0) } - initNotifier() - w, err := time.ParseDuration(workTime) if err != nil { - fmt.Printf("ERROR: Unable to set the work timer. %s", err.Error()) - os.Exit(1) + log.Fatalf("ERROR: Unable to set the work timer. %s", err.Error()) } s, err := time.ParseDuration(shortBreakTime) if err != nil { - fmt.Printf("ERROR: Unable to set the work timer. %s", err.Error()) - os.Exit(1) + log.Fatalf("ERROR: Unable to set the work timer. %s", err.Error()) } l, err := time.ParseDuration(longBreakTime) if err != nil { - fmt.Printf("ERROR: Unable to set the work timer. %s", err.Error()) - os.Exit(1) + log.Fatalf("ERROR: Unable to set the work timer. %s", err.Error()) } pominal := NewPominal( @@ -84,6 +76,7 @@ func main() { flex := newFlex(infoUI, timerUI) go pominal.Run(infoUI, timerUI) + if err := app.SetRoot(flex, false).SetFocus(flex).Run(); err != nil { panic(err) } diff --git a/pominal.go b/pominal.go index 07d6592..d890087 100644 --- a/pominal.go +++ b/pominal.go @@ -65,11 +65,13 @@ type Pominal struct { // NewPominal creates a new pominal instance func NewPominal(w, s, l float64, m int) Pominal { + initWorkSession := 1 + initCycle := 1 return Pominal{ - workSession: 1, + workSession: initWorkSession, maxWorkSessions: setMaxWorkSessions(m), - cycle: 1, + cycle: initCycle, work: setSessionTime(w), shortBreak: setSessionTime(s), longBreak: setSessionTime(l), @@ -79,11 +81,14 @@ func NewPominal(w, s, l float64, m int) Pominal { // Run Pominal func (p *Pominal) Run(infoUI, timerUI *tview.TextView) { - p.stopChan = make(chan struct{}) p.UpdateSession() drawInfo(infoUI, p.cycle, p.workSession, p.maxWorkSessions, p.label) - t := time.NewTicker(1 * time.Second) + + // notifier is used for desktop notifications + notifier := initNotifier() + + t := time.NewTicker(time.Second) infinite: for { @@ -103,9 +108,9 @@ infinite: p.maxWorkSessions, p.label, ) - alert(p.label) - time.Sleep(1 * time.Second) - t = time.NewTicker(1 * time.Second) + desktopAlert(p.label, notifier) + time.Sleep(time.Second) + t = time.NewTicker(time.Second) } else { drawTimer(timerUI, p.countdown) } @@ -158,6 +163,7 @@ func setSessionTime(s float64) float64 { if s < min { return min } + return s } @@ -170,5 +176,6 @@ func setMaxWorkSessions(w int) int { if w < min { return min } + return w } diff --git a/pominal_test.go b/pominal_test.go index 99be7a6..387d277 100644 --- a/pominal_test.go +++ b/pominal_test.go @@ -71,9 +71,6 @@ func TestRun(t *testing.T) { infoUI := tview.NewTextView() timerUI := tview.NewTextView() - // TODO: Remove when support for disabling desktop notifications is in place. - initNotifier() - go pominal.Run(infoUI, timerUI) time.Sleep(1 * time.Second) diff --git a/ui.go b/ui.go index ba2812e..c7e73e0 100644 --- a/ui.go +++ b/ui.go @@ -56,5 +56,7 @@ func drawInfo(t *tview.TextView, pominalCycle, workSessions, maxWorkSessions int func drawTimer(t *tview.TextView, countdown float64) { t.Clear() - fmt.Fprintf(t, "\nTime remaining:\n%02d:%02d", int(countdown)/60, int(countdown)%60) + + secondsPerMinute := 60 + fmt.Fprintf(t, "\nTime remaining:\n%02d:%02d", int(countdown)/secondsPerMinute, int(countdown)%secondsPerMinute) }