diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a9c8b442b..66214d358 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -74,9 +74,9 @@ jobs: mkdir -p frontend/dist touch frontend/dist/index.html - name: golangci-lint - uses: golangci/golangci-lint-action@55c2c1448f86e01eaae002a5a3a9624417608d84 # v6 + uses: golangci/golangci-lint-action@1481404843c368bc19ca9406f87d6e0fc97bdcfd # v7 with: - version: v1.64.5 + version: v2.0.0 api-check-translations: runs-on: ubuntu-latest diff --git a/.golangci.yml b/.golangci.yml index aa0cbea68..d812af1a7 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,127 +1,160 @@ +version: "2" run: - timeout: 15m tests: true - linters: enable: - - gosimple - - staticcheck - - unused - - govet + - asasalint + - asciicheck + - bidichk + - bodyclose + - contextcheck + - err113 + - errchkjson + - errorlint + - exhaustive + - gocheckcompilerdirectives + - gochecksumtype - gocritic - gocyclo - - err113 - goheader - - gofmt - - goimports - - revive + - gosec + - gosmopolitan + - loggercheck + - makezero - misspell + - nilerr + - nilnesserr + - noctx + - protogetter + - reassign + - recvcheck + - revive + - rowserrcheck + - spancheck + - sqlclosecheck + - testifylint + - unparam + - zerologlint disable: - durationcheck - goconst - musttag - presets: - - bugs - - unused - fast: false - -linters-settings: - nestif: - min-complexity: 6 - goheader: - template-path: code-header-template.txt - -issues: - exclude-rules: - # Exclude some linters from running on tests files. - - path: _test\.go - linters: - - gocyclo - - deadcode - - errorlint - - path: pkg/integrations/* - linters: - - gocyclo - - deadcode - - varcheck - - unparam - - bodyclose - - path: pkg/integrations/* - text: "unlambda" - linters: - - gocritic - - path: pkg/modules/background/unsplash/unsplash\.go - linters: - - bodyclose - - path: pkg/migration/* - linters: - - exhaustive - - err113 - - path: pkg/models/task_collection_filter\.go - linters: - - exhaustive - - path: pkg/utils/random_string\.go - text: "G404:" # We don't care about cryptographically secure randomness when we're using that utility function. - linters: - - gosec - - path: pkg/modules/dump/* - linters: - - err113 - - path: pkg/ - text: "do not define dynamic errors, use wrapped static errors instead:" - linters: - - err113 - - text: "commentFormatting: put a space between `//` and comment text" - linters: - - gocritic - - path: pkg/modules/migration - linters: - - gocyclo - - path: pkg/routes/api/v1/docs.go - linters: - - goheader - - misspell - - gosmopolitan - - text: "Missed string" - linters: - - goheader - - path: pkg/.*/error.go - linters: - - errorlint - - path: pkg/models/favorites\.go - linters: - - nilerr - - path: pkg/models/project\.go - text: "string `parent_project_id` has 3 occurrences, make it a constant" - - path: pkg/models/events\.go - linters: - - musttag - - path: pkg/models/task_collection.go - text: 'append result not assigned to the same slice' - - path: pkg/modules/migration/ticktick/ticktick_test.go - linters: - - testifylint - - path: pkg/migration/* - text: "parameter 'tx' seems to be unused, consider removing or renaming it as" - linters: - - revive - - path: pkg/models/typesense.go - text: 'structtag: struct field Position repeats json tag "position" also at' - linters: - - govet - - path: pkg/cmd/user.go - text: 'G115: integer overflow conversion uintptr -> int' - linters: - - gosec - - text: 'G115: integer overflow conversion int64 -> uint64' - linters: - - gosec - - text: 'G115: integer overflow conversion int -> uint64' - linters: - - gosec - - text: 'the methods of "Right" use pointer receiver and non-pointer receiver.' - linters: - - recvcheck - - text: 'the methods of "SubscriptionEntityType" use pointer receiver and non-pointer receiver.' - linters: - - recvcheck + settings: + goheader: + template-path: code-header-template.txt + nestif: + min-complexity: 6 + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + rules: + - linters: + - deadcode + - errorlint + - gocyclo + path: _test\.go + - linters: + - bodyclose + - deadcode + - gocyclo + - unparam + - varcheck + path: pkg/integrations/* + - linters: + - gocritic + path: pkg/integrations/* + text: unlambda + - linters: + - bodyclose + path: pkg/modules/background/unsplash/unsplash\.go + - linters: + - err113 + - exhaustive + path: pkg/migration/* + - linters: + - exhaustive + path: pkg/models/task_collection_filter\.go + - linters: + - gosec + path: pkg/utils/random_string\.go + text: 'G404:' # We don't care about cryptographically secure randomness when we're using that utility function. + - linters: + - err113 + path: pkg/modules/dump/* + - linters: + - err113 + path: pkg/ + text: 'do not define dynamic errors, use wrapped static errors instead:' + - linters: + - gocritic + text: 'commentFormatting: put a space between `//` and comment text' + - linters: + - gocyclo + path: pkg/modules/migration + - linters: + - goheader + - gosmopolitan + - misspell + path: pkg/routes/api/v1/docs.go + - linters: + - goheader + text: Missed string + - linters: + - errorlint + path: pkg/.*/error.go + - linters: + - nilerr + path: pkg/models/favorites\.go + - path: pkg/models/project\.go + text: string `parent_project_id` has 3 occurrences, make it a constant + - linters: + - musttag + path: pkg/models/events\.go + - path: pkg/models/task_collection.go + text: append result not assigned to the same slice + - linters: + - testifylint + path: pkg/modules/migration/ticktick/ticktick_test.go + - linters: + - revive + path: pkg/migration/* + text: parameter 'tx' seems to be unused, consider removing or renaming it as + - linters: + - govet + path: pkg/models/typesense.go + text: 'structtag: struct field Position repeats json tag "position" also at' + - linters: + - gosec + path: pkg/cmd/user.go + text: 'G115: integer overflow conversion uintptr -> int' + - linters: + - gosec + text: 'G115: integer overflow conversion int64 -> uint64' + - linters: + - gosec + text: 'G115: integer overflow conversion int -> uint64' + - linters: + - recvcheck + text: the methods of "Right" use pointer receiver and non-pointer receiver. + - linters: + - recvcheck + text: the methods of "SubscriptionEntityType" use pointer receiver and non-pointer receiver. + paths: + - third_party$ + - builtin$ + - examples$ + - pkg/routes/api/v1/docs.go +formatters: + enable: + - gofmt + - goimports + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ diff --git a/pkg/migration/20241028131622.go b/pkg/migration/20241028131622.go index c85c02aca..d8a440927 100644 --- a/pkg/migration/20241028131622.go +++ b/pkg/migration/20241028131622.go @@ -54,7 +54,7 @@ func init() { for _, query := range queries { _, err := tx.Exec(query) - if err != nil && !(strings.Contains(err.Error(), "Error 1061") && strings.Contains(err.Error(), "Duplicate key name")) { + if err != nil && (!strings.Contains(err.Error(), "Error 1061") || !strings.Contains(err.Error(), "Duplicate key name")) { return err } } diff --git a/pkg/models/bulk_task.go b/pkg/models/bulk_task.go index 17896de35..a4f5852ce 100644 --- a/pkg/models/bulk_task.go +++ b/pkg/models/bulk_task.go @@ -98,7 +98,7 @@ func (bt *BulkTask) Update(s *xorm.Session, a web.Auth) (err error) { } // And because a false is considered to be a null value, we need to explicitly check that case here. - if !bt.Task.Done { + if !bt.Done { oldtask.Done = false } diff --git a/pkg/models/project.go b/pkg/models/project.go index f7596757e..ea89a3ccb 100644 --- a/pkg/models/project.go +++ b/pkg/models/project.go @@ -825,10 +825,7 @@ func checkProjectBeforeUpdateOrDelete(s *xorm.Session, project *Project) (err er // Check if there's a cycle in the parent relation parentsVisited := make(map[int64]bool) parentsVisited[project.ID] = true - for { - if parent.ParentProjectID == 0 { - break - } + for parent.ParentProjectID != 0 { parent = allProjects[parent.ParentProjectID] diff --git a/pkg/models/project_users.go b/pkg/models/project_users.go index d6b8c95f1..c46277420 100644 --- a/pkg/models/project_users.go +++ b/pkg/models/project_users.go @@ -217,7 +217,7 @@ func (lu *ProjectUser) ReadAll(s *xorm.Session, a web.Auth, search string, page // Obfuscate all user emails for _, u := range all { - u.User.Email = "" + u.Email = "" } numberOfTotalItems, err = s. diff --git a/pkg/models/subscription.go b/pkg/models/subscription.go index 6b5da305c..1ce260aa2 100644 --- a/pkg/models/subscription.go +++ b/pkg/models/subscription.go @@ -395,7 +395,7 @@ ORDER BY t.id, sh.user_id`, subscriptions = make(map[int64][]*SubscriptionWithUser) for _, sub := range rawSubscriptions { - if sub.Subscription.EntityID == 0 { + if sub.EntityID == 0 { continue } @@ -404,7 +404,7 @@ ORDER BY t.id, sh.user_id`, subscriptions[sub.OriginalEntityID] = []*SubscriptionWithUser{} } - sub.Subscription.ID = sub.SubscriptionID + sub.ID = sub.SubscriptionID if sub.User != nil { sub.User.ID = sub.UserID } diff --git a/pkg/models/tasks.go b/pkg/models/tasks.go index 56aec0828..36754299d 100644 --- a/pkg/models/tasks.go +++ b/pkg/models/tasks.go @@ -461,7 +461,7 @@ func addAssigneesToTasks(s *xorm.Session, taskIDs []int64, taskMap map[int64]*Ta // Put the assignees in the task map for i, a := range taskAssignees { if a != nil { - a.User.Email = "" // Obfuscate the email + a.Email = "" // Obfuscate the email taskMap[a.TaskID].Assignees = append(taskMap[a.TaskID].Assignees, &taskAssignees[i].User) } } diff --git a/pkg/models/tasks_test.go b/pkg/models/tasks_test.go index 3eaa6361b..d143820ce 100644 --- a/pkg/models/tasks_test.go +++ b/pkg/models/tasks_test.go @@ -81,9 +81,9 @@ func TestTask_Create(t *testing.T) { Title: "Lorem", Description: "Lorem Ipsum Dolor", ProjectID: 1, - DueDate: time.Date(2023, time.March, 7, 22, 5, 0, 0, time.Local), - StartDate: time.Date(2023, time.March, 7, 22, 5, 10, 0, time.Local), - EndDate: time.Date(2023, time.March, 7, 22, 5, 20, 0, time.Local), + DueDate: time.Date(2023, time.March, 7, 22, 5, 0, 0, time.UTC), + StartDate: time.Date(2023, time.March, 7, 22, 5, 10, 0, time.UTC), + EndDate: time.Date(2023, time.March, 7, 22, 5, 20, 0, time.UTC), Reminders: []*TaskReminder{ { RelativeTo: "due_date", @@ -98,19 +98,19 @@ func TestTask_Create(t *testing.T) { RelativePeriod: -1, }, { - Reminder: time.Date(2023, time.March, 7, 23, 0, 0, 0, time.Local), + Reminder: time.Date(2023, time.March, 7, 23, 0, 0, 0, time.UTC), }, }} err := task.Create(s, usr) require.NoError(t, err) - assert.Equal(t, time.Date(2023, time.March, 7, 22, 5, 1, 0, time.Local), task.Reminders[0].Reminder) + assert.Equal(t, time.Date(2023, time.March, 7, 22, 5, 1, 0, time.UTC), task.Reminders[0].Reminder) assert.Equal(t, int64(1), task.Reminders[0].RelativePeriod) assert.Equal(t, ReminderRelationDueDate, task.Reminders[0].RelativeTo) - assert.Equal(t, time.Date(2023, time.March, 7, 22, 5, 8, 0, time.Local), task.Reminders[1].Reminder) + assert.Equal(t, time.Date(2023, time.March, 7, 22, 5, 8, 0, time.UTC), task.Reminders[1].Reminder) assert.Equal(t, ReminderRelationStartDate, task.Reminders[1].RelativeTo) - assert.Equal(t, time.Date(2023, time.March, 7, 22, 5, 19, 0, time.Local), task.Reminders[2].Reminder) + assert.Equal(t, time.Date(2023, time.March, 7, 22, 5, 19, 0, time.UTC), task.Reminders[2].Reminder) assert.Equal(t, ReminderRelationEndDate, task.Reminders[2].RelativeTo) - assert.Equal(t, time.Date(2023, time.March, 7, 23, 0, 0, 0, time.Local), task.Reminders[3].Reminder) + assert.Equal(t, time.Date(2023, time.March, 7, 23, 0, 0, 0, time.UTC), task.Reminders[3].Reminder) err = s.Commit() require.NoError(t, err) }) @@ -359,9 +359,9 @@ func TestTask_Update(t *testing.T) { ID: 1, ProjectID: 1, Title: "test", - DueDate: time.Date(2023, time.March, 7, 22, 5, 0, 0, time.Local), - StartDate: time.Date(2023, time.March, 7, 22, 5, 10, 0, time.Local), - EndDate: time.Date(2023, time.March, 7, 22, 5, 20, 0, time.Local), + DueDate: time.Date(2023, time.March, 7, 22, 5, 0, 0, time.UTC), + StartDate: time.Date(2023, time.March, 7, 22, 5, 10, 0, time.UTC), + EndDate: time.Date(2023, time.March, 7, 22, 5, 20, 0, time.UTC), Reminders: []*TaskReminder{ { RelativeTo: "due_date", @@ -376,19 +376,19 @@ func TestTask_Update(t *testing.T) { RelativePeriod: -1, }, { - Reminder: time.Date(2023, time.March, 7, 23, 0, 0, 0, time.Local), + Reminder: time.Date(2023, time.March, 7, 23, 0, 0, 0, time.UTC), }, }} err := task.Update(s, u) require.NoError(t, err) - assert.Equal(t, time.Date(2023, time.March, 7, 22, 5, 1, 0, time.Local), task.Reminders[0].Reminder) + assert.Equal(t, time.Date(2023, time.March, 7, 22, 5, 1, 0, time.UTC), task.Reminders[0].Reminder) assert.Equal(t, int64(1), task.Reminders[0].RelativePeriod) assert.Equal(t, ReminderRelationDueDate, task.Reminders[0].RelativeTo) - assert.Equal(t, time.Date(2023, time.March, 7, 22, 5, 8, 0, time.Local), task.Reminders[1].Reminder) + assert.Equal(t, time.Date(2023, time.March, 7, 22, 5, 8, 0, time.UTC), task.Reminders[1].Reminder) assert.Equal(t, ReminderRelationStartDate, task.Reminders[1].RelativeTo) - assert.Equal(t, time.Date(2023, time.March, 7, 22, 5, 19, 0, time.Local), task.Reminders[2].Reminder) + assert.Equal(t, time.Date(2023, time.March, 7, 22, 5, 19, 0, time.UTC), task.Reminders[2].Reminder) assert.Equal(t, ReminderRelationEndDate, task.Reminders[2].RelativeTo) - assert.Equal(t, time.Date(2023, time.March, 7, 23, 0, 0, 0, time.Local), task.Reminders[3].Reminder) + assert.Equal(t, time.Date(2023, time.March, 7, 23, 0, 0, 0, time.UTC), task.Reminders[3].Reminder) err = s.Commit() require.NoError(t, err) db.AssertCount(t, "task_reminders", builder.Eq{"task_id": 1}, 4) @@ -426,7 +426,7 @@ func TestTask_Update(t *testing.T) { taskBefore := &Task{ Title: "test", ProjectID: 1, - StartDate: time.Date(2022, time.March, 8, 8, 5, 20, 0, time.Local), + StartDate: time.Date(2022, time.March, 8, 8, 5, 20, 0, time.UTC), Reminders: []*TaskReminder{ { RelativeTo: "start_date", @@ -437,16 +437,16 @@ func TestTask_Update(t *testing.T) { require.NoError(t, err) err = s.Commit() require.NoError(t, err) - assert.Equal(t, time.Date(2022, time.March, 8, 8, 4, 20, 0, time.Local), taskBefore.Reminders[0].Reminder) + assert.Equal(t, time.Date(2022, time.March, 8, 8, 4, 20, 0, time.UTC), taskBefore.Reminders[0].Reminder) // when start_date is modified task := taskBefore - task.StartDate = time.Date(2023, time.March, 8, 8, 5, 0, 0, time.Local) + task.StartDate = time.Date(2023, time.March, 8, 8, 5, 0, 0, time.UTC) err = task.Update(s, u) require.NoError(t, err) // then reminder time is updated - assert.Equal(t, time.Date(2023, time.March, 8, 8, 4, 0, 0, time.Local), task.Reminders[0].Reminder) + assert.Equal(t, time.Date(2023, time.March, 8, 8, 4, 0, 0, time.UTC), task.Reminders[0].Reminder) err = s.Commit() require.NoError(t, err) }) diff --git a/pkg/models/teams.go b/pkg/models/teams.go index 676bdef7b..9689e20f4 100644 --- a/pkg/models/teams.go +++ b/pkg/models/teams.go @@ -169,7 +169,7 @@ func addMoreInfoToTeams(s *xorm.Session, teams []*Team) (err error) { if _, exists := teamMap[u.TeamID]; !exists { continue } - u.User.Email = "" + u.Email = "" teamMap[u.TeamID].Members = append(teamMap[u.TeamID].Members, u) } diff --git a/pkg/models/typesense.go b/pkg/models/typesense.go index 17ff77f08..7a5868381 100644 --- a/pkg/models/typesense.go +++ b/pkg/models/typesense.go @@ -520,7 +520,7 @@ func convertTaskToTypesenseTask(task *Task, positions []*TaskPositionWithView, b if pos == 0 { pos = float64(task.ID) } - tt.Positions["view_"+strconv.FormatInt(position.ProjectView.ID, 10)] = pos + tt.Positions["view_"+strconv.FormatInt(position.ID, 10)] = pos } for _, bucket := range buckets { diff --git a/pkg/modules/migration/microsoft-todo/microsoft_todo.go b/pkg/modules/migration/microsoft-todo/microsoft_todo.go index 838d4f7e2..b80fdd35a 100644 --- a/pkg/modules/migration/microsoft-todo/microsoft_todo.go +++ b/pkg/modules/migration/microsoft-todo/microsoft_todo.go @@ -200,7 +200,7 @@ func makeAuthenticatedGetRequest(token, urlPart string, v interface{}) error { } if resp.StatusCode > 399 { - return fmt.Errorf("Microsoft Graph API Error: Status Code: %d, Response was: %s", resp.StatusCode, buf.String()) + return fmt.Errorf("microsoft graph api error: status code: %d, response was: %s", resp.StatusCode, buf.String()) } // If the response is an empty json array, we need to exit here, otherwise this breaks the json parser since it diff --git a/pkg/modules/migration/ticktick/ticktick.go b/pkg/modules/migration/ticktick/ticktick.go index 1dd9175ba..670b9ee07 100644 --- a/pkg/modules/migration/ticktick/ticktick.go +++ b/pkg/modules/migration/ticktick/ticktick.go @@ -121,7 +121,7 @@ func convertTickTickToVikunja(tasks []*tickTickTask) (result []*models.ProjectWi } if !t.DueDate.IsZero() && t.Reminder > 0 { - task.Task.Reminders = []*models.TaskReminder{ + task.Reminders = []*models.TaskReminder{ { RelativeTo: models.ReminderRelationDueDate, RelativePeriod: int64((t.Reminder * -1).Seconds()), diff --git a/pkg/notifications/mail_render.go b/pkg/notifications/mail_render.go index b604651e4..83dfe561e 100644 --- a/pkg/notifications/mail_render.go +++ b/pkg/notifications/mail_render.go @@ -19,7 +19,6 @@ package notifications import ( "bytes" "embed" - _ "embed" templatehtml "html/template" templatetext "text/template" diff --git a/pkg/notifications/mail_test.go b/pkg/notifications/mail_test.go index cf3733bac..5d49b47da 100644 --- a/pkg/notifications/mail_test.go +++ b/pkg/notifications/mail_test.go @@ -62,7 +62,7 @@ func TestNewMail(t *testing.T) { assert.Equal(t, "test@example.com", mail.from) assert.Equal(t, "test@otherdomain.com", mail.to) assert.Equal(t, "Testmail", mail.subject) - assert.Equal(t, "", mail.greeting) + assert.Empty(t, mail.greeting) assert.Len(t, mail.introLines, 2) assert.Equal(t, "This is a line", mail.introLines[0].Text) assert.Equal(t, "And another one", mail.introLines[1].Text) diff --git a/pkg/routes/api/v1/avatar.go b/pkg/routes/api/v1/avatar.go index b59e7ef65..deb170d59 100644 --- a/pkg/routes/api/v1/avatar.go +++ b/pkg/routes/api/v1/avatar.go @@ -65,7 +65,7 @@ func GetAvatar(c echo.Context) error { return handler.HandleHTTPError(err) } - found := !(err != nil && user.IsErrUserDoesNotExist(err)) + found := err == nil || !user.IsErrUserDoesNotExist(err) var avatarProvider avatar.Provider switch u.AvatarProvider { diff --git a/pkg/routes/static.go b/pkg/routes/static.go index 925dc3c34..98f041ab4 100644 --- a/pkg/routes/static.go +++ b/pkg/routes/static.go @@ -165,7 +165,7 @@ func static() echo.MiddlewareFunc { } var he *echo.HTTPError - if !(errors.As(err, &he) && he.Code == http.StatusNotFound) { + if !errors.As(err, &he) || he.Code != http.StatusNotFound { return err }