Repository: kopia/kopia
Pull request #2526 (2022-10-28 05:03:29)

chore(ci): upgrade golang to 1.19.2 and linter to 1.50.1
Lack of generics support is blocking more and more dependency upgrades so this unblocks that.

Had to temporarily disable `checklocks` linter until it's fixed upstream.

codecov[bot]: # [Codecov](https://codecov.io/gh/kopia/kopia/pull/2526?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kopia) Report
Base: **74.46**% // Head: **74.50**% // Increases project coverage by **`+0.03%`** :tada:
> Coverage data is based on head [(`79804da`)](https://codecov.io/gh/kopia/kopia/pull/2526?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kopia) compared to base [(`ec5fe21`)](https://codecov.io/gh/kopia/kopia/commit/ec5fe211cf906bb51936c75eb05ce7f69236f813?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kopia).
> Patch coverage: 53.33% of modified lines in pull request are covered.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master    #2526      +/-   ##
==========================================
+ Coverage   74.46%   74.50%   +0.03%     
==========================================
  Files         442      442              
  Lines       34979    34977       -2     
==========================================
+ Hits        26048    26060      +12     
+ Misses       7041     7030      -11     
+ Partials     1890     1887       -3     
```


| [Impacted Files](https://codecov.io/gh/kopia/kopia/pull/2526?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kopia) | Coverage Δ | |
|---|---|---|
| [cli/app.go](https://codecov.io/gh/kopia/kopia/pull/2526/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kopia#diff-Y2xpL2FwcC5nbw==) | `76.69% <ø> (ø)` | |
| [cli/command\_blob\_stats.go](https://codecov.io/gh/kopia/kopia/pull/2526/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kopia#diff-Y2xpL2NvbW1hbmRfYmxvYl9zdGF0cy5nbw==) | `8.95% <ø> (+0.13%)` | :arrow_up: |
| [cli/command\_content\_stats.go](https://codecov.io/gh/kopia/kopia/pull/2526/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kopia#diff-Y2xpL2NvbW1hbmRfY29udGVudF9zdGF0cy5nbw==) | `90.19% <0.00%> (ø)` | |
| [cli/command\_logs\_session.go](https://codecov.io/gh/kopia/kopia/pull/2526/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kopia#diff-Y2xpL2NvbW1hbmRfbG9nc19zZXNzaW9uLmdv) | `71.91% <ø> (-0.32%)` | :arrow_down: |
| [cli/command\_policy\_set.go](https://codecov.io/gh/kopia/kopia/pull/2526/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kopia#diff-Y2xpL2NvbW1hbmRfcG9saWN5X3NldC5nbw==) | `64.51% <ø> (ø)` | |
| [cli/observability\_flags.go](https://codecov.io/gh/kopia/kopia/pull/2526/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kopia#diff-Y2xpL29ic2VydmFiaWxpdHlfZmxhZ3MuZ28=) | `77.16% <0.00%> (ø)` | |
| [cli/storage\_filesystem.go](https://codecov.io/gh/kopia/kopia/pull/2526/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kopia#diff-Y2xpL3N0b3JhZ2VfZmlsZXN5c3RlbS5nbw==) | `66.66% <ø> (+0.62%)` | :arrow_up: |
| [cli/throttle\_set.go](https://codecov.io/gh/kopia/kopia/pull/2526/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kopia#diff-Y2xpL3Rocm90dGxlX3NldC5nbw==) | `57.14% <ø> (ø)` | |
| [internal/ospath/ospath.go](https://codecov.io/gh/kopia/kopia/pull/2526/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kopia#diff-aW50ZXJuYWwvb3NwYXRoL29zcGF0aC5nbw==) | `60.71% <ø> (ø)` | |
| [internal/server/api\_content.go](https://codecov.io/gh/kopia/kopia/pull/2526/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kopia#diff-aW50ZXJuYWwvc2VydmVyL2FwaV9jb250ZW50Lmdv) | `50.70% <ø> (+0.70%)` | :arrow_up: |
| ... and [27 more](https://codecov.io/gh/kopia/kopia/pull/2526/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kopia) | |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kopia). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kopia)

</details>

[:umbrella: View full report at Codecov](https://codecov.io/gh/kopia/kopia/pull/2526?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kopia).   
:loudspeaker: Do you have feedback about the report comment? [Let us know in this issue](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kopia).


Patch:
diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml
index efe229c6..8f2e2514 100644
--- a/.github/workflows/code-coverage.yml
+++ b/.github/workflows/code-coverage.yml
@@ -14,7 +14,7 @@ jobs:
     - name: Set up Go.
       uses: actions/setup-go@v3
       with:
-        go-version: '1.18'
+        go-version: '1.19'
       id: go
     - name: Check out code into the Go module directory
       uses: actions/checkout@v3
diff --git a/.github/workflows/license-check.yml b/.github/workflows/license-check.yml
index 41f22dda..d0e37bc8 100644
--- a/.github/workflows/license-check.yml
+++ b/.github/workflows/license-check.yml
@@ -14,7 +14,7 @@ jobs:
     - name: Set up Go.
       uses: actions/setup-go@v3
       with:
-        go-version: '1.18'
+        go-version: '1.19'
       id: go
     - name: Check out code into the Go module directory
       uses: actions/checkout@v3
diff --git a/.github/workflows/make.yml b/.github/workflows/make.yml
index b5913ef2..879e84bb 100644
--- a/.github/workflows/make.yml
+++ b/.github/workflows/make.yml
@@ -42,11 +42,11 @@ jobs:
     - name: Set up Go.
       uses: actions/setup-go@v3
       with:
-        go-version: '1.18'
+        go-version: '1.19'
       id: go
       if: ${{ !contains(matrix.os, 'ARMHF') }}
     - name: Install GoLang for ARMHF
-      run: "echo /usr/local/go/bin >> $GITHUB_PATH; rm -rf /usr/local/go && mkdir -p /usr/local/go && curl -s -L https://go.dev/dl/go1.18.2.linux-armv6l.tar.gz | tar -C /usr/local -xz"
+      run: "echo /usr/local/go/bin >> $GITHUB_PATH; rm -rf /usr/local/go && mkdir -p /usr/local/go && curl -s -L https://go.dev/dl/go1.19.2.linux-armv6l.tar.gz | tar -C /usr/local -xz"
       if: ${{ contains(matrix.os, 'ARMHF') }}
     - name: Install Windows-specific packages
       run: "choco install --no-progress -y make zip unzip curl"
diff --git a/.github/workflows/race-detector.yml b/.github/workflows/race-detector.yml
index 9c8f1d80..fc6075cd 100644
--- a/.github/workflows/race-detector.yml
+++ b/.github/workflows/race-detector.yml
@@ -14,7 +14,7 @@ jobs:
     - name: Set up Go.
       uses: actions/setup-go@v3
       with:
-        go-version: '1.18'
+        go-version: '1.19'
       id: go
     - name: Check out code into the Go module directory
       uses: actions/checkout@v3
diff --git a/.golangci.yml b/.golangci.yml
index ba52c4c3..052e3637 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -1,5 +1,5 @@
 run:
-  go: '1.17'
+  go: '1.19'
   skip-dirs:
     - test/testdata_etc
 
@@ -70,6 +70,7 @@ linters-settings:
 linters:
   enable-all: true
   disable:
+    - deadcode
     - exhaustivestruct
     - exhaustruct
     - gochecknoinits
@@ -85,16 +86,24 @@ linters:
     - nosnakecase
     - paralleltest
     - prealloc
+    - rowserrcheck
     - scopelint
+    - sqlclosecheck
+    - structcheck
     - tagliatelle
     - testpackage
     - tparallel
+    - varcheck
     - varnamelen # this one may be interesting, but too much churn
+    - wastedassign
     - whitespace
 
 issues:
   exclude-use-default: false
   exclude-rules:
+    - path: \.pb\.go
+      linters:
+      - gofmt
     - path: _test\.go|testing|tests|test_env|fshasher|fault
       linters:
       - contextcheck
@@ -107,6 +116,7 @@ issues:
       - gomnd
       - gosec
       - nestif
+      - revive
       - nolintlint
       - wrapcheck
     - text: "log is a global variable"
@@ -133,6 +143,9 @@ issues:
     - text: "Line contains TODO"
       linters:
         - godox
+    - text: timeCmpSimplify
+      linters:
+        - gocritic
     - text: ".*Magic number\\: [01],"
       linters:
         - gomnd
diff --git a/Makefile b/Makefile
index 39633d13..f007b4f6 100644
--- a/Makefile
+++ b/Makefile
@@ -69,7 +69,7 @@ ifneq ($(GOOS)/$(GOARCH),linux/arm)
 endif
 endif
 
-lint: $(linter) check-locks
+lint: $(linter)
 ifneq ($(GOOS)/$(GOARCH),linux/arm64)
 ifneq ($(GOOS)/$(GOARCH),linux/arm)
 	$(linter) --deadline $(LINTER_DEADLINE) run $(linter_flags)
diff --git a/cli/app.go b/cli/app.go
index fd150b92..497e31d2 100644
--- a/cli/app.go
+++ b/cli/app.go
@@ -71,6 +71,8 @@ func (o *textOutput) printStderr(msg string, args ...interface{}) {
 }
 
 // appServices are the methods of *App that command handles are allowed to call.
+//
+//nolint:interfacebloat
 type appServices interface {
 	noRepositoryAction(act func(ctx context.Context) error) func(ctx *kingpin.ParseContext) error
 	serverAction(sf *serverClientFlags, act func(ctx context.Context, cli *apiclient.KopiaAPIClient) error) func(ctx *kingpin.ParseContext) error
@@ -93,6 +95,7 @@ type appServices interface {
 	EnvName(s string) string
 }
 
+//nolint:interfacebloat
 type advancedAppServices interface {
 	appServices
 	StorageProviderServices
diff --git a/cli/command_blob_stats.go b/cli/command_blob_stats.go
index 6d7e412b..53011d41 100644
--- a/cli/command_blob_stats.go
+++ b/cli/command_blob_stats.go
@@ -65,7 +65,6 @@ func(b blob.Metadata) error {
 	sizeToString := units.BytesString
 	if c.raw {
 		sizeToString = func(l int64) string {
-			//nolint:gomnd
 			return strconv.FormatInt(l, 10)
 		}
 	}
diff --git a/cli/command_content_stats.go b/cli/command_content_stats.go
index 3be6b217..af399f5e 100644
--- a/cli/command_content_stats.go
+++ b/cli/command_content_stats.go
@@ -51,7 +51,7 @@ func (c *commandContentStats) run(ctx context.Context, rep repo.DirectRepository
 	sizeToString := units.BytesString
 	if c.raw {
 		sizeToString = func(l int64) string {
-			return strconv.FormatInt(l, 10) //nolint:gomnd
+			return strconv.FormatInt(l, 10)
 		}
 	}
 
diff --git a/cli/command_logs_session.go b/cli/command_logs_session.go
index 88361898..66c00ddd 100644
--- a/cli/command_logs_session.go
+++ b/cli/command_logs_session.go
@@ -81,7 +81,6 @@ func getLogSessions(ctx context.Context, st blob.Reader) ([]*logSessionInfo, err
 
 		id := parts[2] + "_" + parts[3]
 
-		//nolint:gomnd
 		startTime, err := strconv.ParseInt(parts[4], 10, 64)
 		if err != nil {
 			log(ctx).Errorf("invalid start time - skipping unrecognized log: %v", bm.BlobID)
@@ -90,7 +89,6 @@ func getLogSessions(ctx context.Context, st blob.Reader) ([]*logSessionInfo, err
 			return nil
 		}
 
-		//nolint:gomnd
 		endTime, err := strconv.ParseInt(parts[5], 10, 64)
 		if err != nil {
 			log(ctx).Errorf("invalid end time - skipping unrecognized log: %v", bm.BlobID)
diff --git a/cli/command_policy_set.go b/cli/command_policy_set.go
index 2dce7b8a..42dccce8 100644
--- a/cli/command_policy_set.go
+++ b/cli/command_policy_set.go
@@ -187,7 +187,6 @@ func applyOptionalInt(ctx context.Context, desc string, val **policy.OptionalInt
 		return nil
 	}
 
-	//nolint:gomnd
 	v, err := strconv.ParseInt(str, 10, 32)
 	if err != nil {
 		return errors.Wrapf(err, "can't parse the %v %q", desc, str)
@@ -218,7 +217,6 @@ func applyOptionalInt64MiB(ctx context.Context, desc string, val **policy.Option
 		return nil
 	}
 
-	//nolint:gomnd
 	v, err := strconv.ParseInt(str, 10, 32)
 	if err != nil {
 		return errors.Wrapf(err, "can't parse the %v %q", desc, str)
@@ -253,7 +251,6 @@ func applyPolicyNumber64(ctx context.Context, desc string, val *int64, str strin
 		return nil
 	}
 
-	//nolint:gomnd
 	v, err := strconv.ParseInt(str, 10, 64)
 	if err != nil {
 		return errors.Wrapf(err, "can't parse the %q %q", desc, str)
diff --git a/cli/command_restore.go b/cli/command_restore.go
index 37a86450..5cfd097c 100644
--- a/cli/command_restore.go
+++ b/cli/command_restore.go
@@ -212,7 +212,7 @@ func (c *commandRestore) constructTargetPairs(rep repo.Repository) error {
 		return nil
 	case tplen == 0 && restpslen == 2:
 		// This means that none of the restoreTargetPaths are placeholders and we
-		// we have two args: a sourceID and a destination directory.
+		// have two args: a sourceID and a destination directory.
 		absp, err := filepath.Abs(c.restoreTargetPaths[1])
 		if err != nil {
 			return errors.Wrapf(err, "restore can't resolve path for %q", c.restoreTargetPaths[1])
@@ -261,7 +261,7 @@ func (c *commandRestore) restoreOutput(ctx context.Context, rep repo.Repository)
 			WriteSparseFiles:       c.restoreWriteSparseFiles,
 		}
 
-		if err := o.Init(); err != nil {
+		if err := o.Init(ctx); err != nil {
 			return nil, errors.Wrap(err, "unable to create output file")
 		}
 
diff --git a/cli/observability_flags.go b/cli/observability_flags.go
index 5b660f47..7bd91d6e 100644
--- a/cli/observability_flags.go
+++ b/cli/observability_flags.go
@@ -93,7 +93,7 @@ func (c *observabilityFlags) startMetrics(ctx context.Context) error {
 
 		log(ctx).Infof("starting prometheus metrics on %v", c.metricsListenAddr)
 
-		go http.ListenAndServe(c.metricsListenAddr, m) //nolint:errcheck
+		go http.ListenAndServe(c.metricsListenAddr, m) //nolint:errcheck,gosec
 	}
 
 	if c.metricsPushAddr != "" {
diff --git a/cli/storage_filesystem.go b/cli/storage_filesystem.go
index 10f23f87..5dbe51eb 100644
--- a/cli/storage_filesystem.go
+++ b/cli/storage_filesystem.go
@@ -82,7 +82,6 @@ func initialDirectoryShards(flat bool, formatVersion int) []int {
 }
 
 func getIntPtrValue(value string, base int) *int {
-	//nolint:gomnd
 	if int64Val, err := strconv.ParseInt(value, base, 32); err == nil {
 		intVal := int(int64Val)
 		return &intVal
@@ -92,7 +91,6 @@ func getIntPtrValue(value string, base int) *int {
 }
 
 func getFileModeValue(value string, def os.FileMode) os.FileMode {
-	//nolint:gomnd
 	if uint32Val, err := strconv.ParseUint(value, 8, 32); err == nil {
 		return os.FileMode(uint32Val)
 	}
diff --git a/cli/throttle_get.go b/cli/throttle_get.go
index 57d198a8..9212ed89 100644
--- a/cli/throttle_get.go
+++ b/cli/throttle_get.go
@@ -45,5 +45,5 @@ func (c *commonThrottleGet) printValueOrUnlimited(label string, v float64, conve
 }
 
 func (c *commonThrottleGet) floatToString(v float64) string {
-	return strconv.FormatFloat(v, 'f', 0, 64) //nolint:gomnd
+	return strconv.FormatFloat(v, 'f', 0, 64)
 }
diff --git a/cli/throttle_set.go b/cli/throttle_set.go
index b17481ae..96d4f761 100644
--- a/cli/throttle_set.go
+++ b/cli/throttle_set.go
@@ -79,7 +79,6 @@ func (c *commonThrottleSet) setThrottleFloat64(ctx context.Context, desc string,
 		return nil
 	}
 
-	//nolint:gomnd
 	v, err := strconv.ParseFloat(str, 64)
 	if err != nil {
 		return errors.Wrapf(err, "can't parse the %v %q", desc, str)
@@ -114,7 +113,6 @@ func (c *commonThrottleSet) setThrottleInt(ctx context.Context, desc string, val
 		return nil
 	}
 
-	//nolint:gomnd
 	v, err := strconv.ParseInt(str, 10, 64)
 	if err != nil {
 		return errors.Wrapf(err, "can't parse the %v %q", desc, str)
diff --git a/go.mod b/go.mod
index 91236b2c..36945f48 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
 module github.com/kopia/kopia
 
-go 1.17
+go 1.19
 
 require (
 	cloud.google.com/go/storage v1.27.0
diff --git a/internal/bigmap/bigmapbench/main.go b/internal/bigmap/bigmapbench/main.go
index c276ee4e..d2594f47 100644
--- a/internal/bigmap/bigmapbench/main.go
+++ b/internal/bigmap/bigmapbench/main.go
@@ -1,3 +1,4 @@
+// Command bigmapbench provides a benchmark for the bigmap implementation.
 package main
 
 import (
diff --git a/internal/epoch/epoch_utils.go b/internal/epoch/epoch_utils.go
index c4940e6b..b980da69 100644
--- a/internal/epoch/epoch_utils.go
+++ b/internal/epoch/epoch_utils.go
@@ -84,7 +84,7 @@ func groupByEpochRanges(bms []blob.Metadata) map[int]map[int][]blob.Metadata {
 func deletionWatermarkFromBlobID(blobID blob.ID) (time.Time, bool) {
 	str := strings.TrimPrefix(string(blobID), string(DeletionWatermarkBlobPrefix))
 
-	unixSeconds, err := strconv.ParseInt(str, 10, 64) //nolint:gomnd
+	unixSeconds, err := strconv.ParseInt(str, 10, 64)
 	if err != nil {
 		return time.Time{}, false
 	}
diff --git a/internal/ospath/ospath.go b/internal/ospath/ospath.go
index ae0903ae..2f79ac85 100644
--- a/internal/ospath/ospath.go
+++ b/internal/ospath/ospath.go
@@ -24,7 +24,7 @@ func LogsDir() string {
 	return filepath.Join(userLogsDir, "kopia")
 }
 
-// IsAbs determines if a given path is absolute, in particular treating treating \\hostname\share as absolute on Windows.
+// IsAbs determines if a given path is absolute, in particular treating \\hostname\share as absolute on Windows.
 func IsAbs(s string) bool {
 	//nolint:forbidigo
 	if filepath.IsAbs(s) {
diff --git a/internal/server/api_content.go b/internal/server/api_content.go
index 832bf75d..b4035f57 100644
--- a/internal/server/api_content.go
+++ b/internal/server/api_content.go
@@ -76,7 +76,6 @@ func handleContentPut(ctx context.Context, rc requestContext) (interface{}, *api
 	var comp compression.HeaderID
 
 	if c := rc.queryParam("compression"); c != "" {
-		//nolint:gomnd
 		v, err := strconv.ParseInt(c, 16, 32)
 		if err != nil {
 			return nil, requestError(serverapi.ErrorMalformedRequest, "malformed compression ID")
diff --git a/internal/server/api_restore.go b/internal/server/api_restore.go
index 57eb735a..18f7c015 100644
--- a/internal/server/api_restore.go
+++ b/internal/server/api_restore.go
@@ -51,7 +51,7 @@ func handleRestore(ctx context.Context, rc requestContext) (interface{}, *apiErr
 
 	switch {
 	case req.Filesystem != nil:
-		if err := req.Filesystem.Init(); err != nil {
+		if err := req.Filesystem.Init(ctx); err != nil {
 			return nil, internalServerError(err)
 		}
 
diff --git a/internal/server/request_context.go b/internal/server/request_context.go
index 878775e8..d34e2623 100644
--- a/internal/server/request_context.go
+++ b/internal/server/request_context.go
@@ -15,6 +15,7 @@
 	"github.com/kopia/kopia/snapshot"
 )
 
+//nolint:interfacebloat
 type serverInterface interface {
 	deleteSourceManager(ctx context.Context, src snapshot.SourceInfo) bool
 	generateShortTermAuthCookie(username string, now time.Time) (string, error)
diff --git a/internal/server/server.go b/internal/server/server.go
index 014b1c95..0a960051 100644
--- a/internal/server/server.go
+++ b/internal/server/server.go
@@ -42,7 +42,7 @@
 	sleepOnMaintenanceError        = 30 * time.Minute
 
 	// retry initialization of repository starting at 1s doubling delay each time up to max 5 minutes
-	// (1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 256s, 300s, 300s, 300s, ...)
+	// (1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 256s, 300s, which then stays at 300s...)
 	retryInitRepositorySleepOnError    = 1 * time.Second
 	maxRetryInitRepositorySleepOnError = 5 * time.Minute
 
@@ -288,6 +288,7 @@ func (s *Server) requireAuth(checkCSRFToken csrfTokenOption, f func(ctx context.
 	return func(w http.ResponseWriter, r *http.Request) {
 		rc := s.captureRequestContext(w, r)
 
+		//nolint:contextcheck
 		if !isAuthenticated(rc) {
 			return
 		}
@@ -813,10 +814,12 @@ func (s *Server) ServeStaticFiles(m *mux.Router, fs http.FileSystem) {
 
 		rc := s.captureRequestContext(w, r)
 
+		//nolint:contextcheck
 		if !isAuthenticated(rc) {
 			return
 		}
 
+		//nolint:contextcheck
 		if !requireUIUser(rc.req.Context(), rc) {
 			http.Error(w, `UI Access denied. See https://github.com/kopia/kopia/issues/880#issuecomment-798421751 for more information.`, http.StatusForbidden)
 			return
diff --git a/internal/timestampmeta/timestampmeta.go b/internal/timestampmeta/timestampmeta.go
index 533a9755..5361e20b 100644
--- a/internal/timestampmeta/timestampmeta.go
+++ b/internal/timestampmeta/timestampmeta.go
@@ -15,13 +15,13 @@ func ToMap(t time.Time, mapKey string) map[string]string {
 	}
 
 	return map[string]string{
-		mapKey: strconv.FormatInt(t.UnixNano(), 10), //nolint:gomnd
+		mapKey: strconv.FormatInt(t.UnixNano(), 10),
 	}
 }
 
 // FromValue attempts to convert the provided value stored in metadata into time.Time.
 func FromValue(v string) (t time.Time, ok bool) {
-	nanos, err := strconv.ParseInt(v, 10, 64) //nolint:gomnd
+	nanos, err := strconv.ParseInt(v, 10, 64)
 	if err != nil {
 		return time.Time{}, false
 	}
diff --git a/internal/webdavmount/webdavmount.go b/internal/webdavmount/webdavmount.go
index 1af482f9..0b2c6ee4 100644
--- a/internal/webdavmount/webdavmount.go
+++ b/internal/webdavmount/webdavmount.go
@@ -124,6 +124,7 @@ func (d *webdavDir) Readdir(n int) ([]os.FileInfo, error) {
 
 		if _, isSymlink := e.(fs.Symlink); isSymlink {
 			if atomic.AddInt32(symlinksAreUnsupportedLogged, 1) == 1 {
+				//nolint:contextcheck
 				log(d.ctx).Errorf("Mounting directories containing symbolic links using WebDAV is not supported. The link entries will be skipped.")
 			}
 
diff --git a/repo/blob/filesystem/filesystem_storage.go b/repo/blob/filesystem/filesystem_storage.go
index ce65205d..e763f925 100644
--- a/repo/blob/filesystem/filesystem_storage.go
+++ b/repo/blob/filesystem/filesystem_storage.go
@@ -102,7 +102,7 @@ func (fs *fsImpl) GetBlobFromPath(ctx context.Context, dirPath, path string, off
 				if st, err := f.Stat(); err == nil && st.Size() == 0 {
 					// this sometimes fails on macOS for unknown reasons, likely a bug in the filesystem
 					// retry deals with this transient state.
-					// see see https://github.com/kopia/kopia/issues/299
+					// see https://github.com/kopia/kopia/issues/299
 					return errRetriableInvalidLength
 				}
 			}
diff --git a/repo/blob/filesystem/osinterface.go b/repo/blob/filesystem/osinterface.go
index 1115dbb4..92723e09 100644
--- a/repo/blob/filesystem/osinterface.go
+++ b/repo/blob/filesystem/osinterface.go
@@ -8,6 +8,8 @@
 )
 
 // osInterface is an operating system file interface, used by filesystemStorage to support mocking.
+//
+//nolint:interfacebloat
 type osInterface interface {
 	Open(fname string) (osReadFile, error)
 	IsNotExist(err error) bool
diff --git a/repo/blob/s3/s3_pit.go b/repo/blob/s3/s3_pit.go
index 11691608..e6ca0c56 100644
--- a/repo/blob/s3/s3_pit.go
+++ b/repo/blob/s3/s3_pit.go
@@ -102,7 +102,7 @@ func newestAtUnlessDeleted(vs []versionMetadata, t time.Time) (v versionMetadata
 	return v, !v.IsDeleteMarker
 }
 
-// Removes versions that are newer than t. The filtering is done in place and
+// Removes versions that are newer than t. The filtering is done in place
 // and uses the same slice storage as vs. Assumes entries in vs are in descending
 // timestamp order.
 func getOlderThan(vs []versionMetadata, t time.Time) []versionMetadata {
diff --git a/repo/blob/storage.go b/repo/blob/storage.go
index 651644c8..10d7eb2d 100644
--- a/repo/blob/storage.go
+++ b/repo/blob/storage.go
@@ -74,7 +74,7 @@ type Volume interface {
 // Reader defines read access API to blob storage.
 type Reader interface {
 	// GetBlob returns full or partial contents of a blob with given ID.
-	// If length>0, the the function retrieves a range of bytes [offset,offset+length)
+	// If length>0, the function retrieves a range of bytes [offset,offset+length)
 	// If length<0, the entire blob must be fetched.
 	// Returns ErrInvalidRange if the fetched blob length is invalid.
 	GetBlob(ctx context.Context, blobID ID, offset, length int64, output OutputBuffer) error
diff --git a/repo/content/index/info.go b/repo/content/index/info.go
index a589f225..304f316d 100644
--- a/repo/content/index/info.go
+++ b/repo/content/index/info.go
@@ -8,6 +8,8 @@
 )
 
 // Info is an information about a single piece of content managed by Manager.
+//
+//nolint:interfacebloat
 type Info interface {
 	GetContentID() ID
 	GetPackBlobID() blob.ID
diff --git a/repo/logging/ctx.go b/repo/logging/ctx.go
index 4a2f3036..da341acb 100644
--- a/repo/logging/ctx.go
+++ b/repo/logging/ctx.go
@@ -44,7 +44,7 @@ func loggerFactoryFromContext(ctx context.Context) LoggerFactory {
 	return v.(*loggerCache).getLogger //nolint:forcetypeassert
 }
 
-// AlsoLogTo returns a context where all logging is emitted the the original output plus the provided loggers.
+// AlsoLogTo returns a context where all logging is emitted the original output plus the provided loggers.
 func AlsoLogTo(ctx context.Context, loggers ...Logger) context.Context {
 	originalLogFactory := loggerFactoryFromContext(ctx)
 
diff --git a/repo/repository.go b/repo/repository.go
index adbea642..42e371fd 100644
--- a/repo/repository.go
+++ b/repo/repository.go
@@ -21,6 +21,8 @@
 var tracer = otel.Tracer("kopia/repository")
 
 // Repository exposes public API of Kopia repository, including objects and manifests.
+//
+//nolint:interfacebloat
 type Repository interface {
 	OpenObject(ctx context.Context, id object.ID) (object.Reader, error)
 	VerifyObject(ctx context.Context, id object.ID) ([]content.ID, error)
@@ -49,6 +51,8 @@ type RepositoryWriter interface {
 }
 
 // DirectRepository provides additional low-level repository functionality.
+//
+//nolint:interfacebloat
 type DirectRepository interface {
 	Repository
 
diff --git a/site/cli2md/cli2md.go b/site/cli2md/cli2md.go
index 24c31134..e62dbe76 100644
--- a/site/cli2md/cli2md.go
+++ b/site/cli2md/cli2md.go
@@ -1,3 +1,4 @@
+// Command cli2md generates documentation pages from CLI flags.
 package main
 
 import (
diff --git a/snapshot/manifest.go b/snapshot/manifest.go
index e5b56fee..5a8d44ba 100644
--- a/snapshot/manifest.go
+++ b/snapshot/manifest.go
@@ -92,7 +92,6 @@ func (p Permissions) MarshalJSON() ([]byte, error) {
 		return nil, nil
 	}
 
-	//nolint:gomnd
 	s := "0" + strconv.FormatInt(int64(p), 8)
 
 	//nolint:wrapcheck
@@ -107,7 +106,6 @@ func (p *Permissions) UnmarshalJSON(b []byte) error {
 		return errors.Wrap(err, "unable to unmarshal JSON")
 	}
 
-	//nolint:gomnd
 	v, err := strconv.ParseInt(s, 0, 32)
 	if err != nil {
 		return errors.Wrap(err, "unable to parse permission string")
diff --git a/snapshot/restore/local_fs_output.go b/snapshot/restore/local_fs_output.go
index 238c518a..a37a5e13 100644
--- a/snapshot/restore/local_fs_output.go
+++ b/snapshot/restore/local_fs_output.go
@@ -98,8 +98,8 @@ type FilesystemOutput struct {
 
 // Init initializes the internal members of the filesystem writer output.
 // This method must be called before FilesystemOutput can be used.
-func (o *FilesystemOutput) Init() error {
-	c, err := getStreamCopier(context.TODO(), o.TargetPath, o.WriteSparseFiles)
+func (o *FilesystemOutput) Init(ctx context.Context) error {
+	c, err := getStreamCopier(ctx, o.TargetPath, o.WriteSparseFiles)
 	if err != nil {
 		return errors.Wrap(err, "unable to get stream copier")
 	}
diff --git a/snapshot/snapshotfs/snapshot_verifier.go b/snapshot/snapshotfs/snapshot_verifier.go
index 5b376a11..c2d5d2f4 100644
--- a/snapshot/snapshotfs/snapshot_verifier.go
+++ b/snapshot/snapshotfs/snapshot_verifier.go
@@ -126,7 +126,7 @@ type VerifierOptions struct {
 	BlobMap            map[blob.ID]blob.Metadata
 }
 
-// InParallel starts parallel verification and invokes the provided function which can call
+// InParallel starts parallel verification and invokes the provided function which can
 // call Process() on in the provided TreeWalker.
 func (v *Verifier) InParallel(ctx context.Context, enqueue func(tw *TreeWalker) error) error {
 	tw, twerr := NewTreeWalker(ctx, TreeWalkerOptions{
diff --git a/snapshot/snapshotfs/upload_progress.go b/snapshot/snapshotfs/upload_progress.go
index 4f078460..5ffbb810 100644
--- a/snapshot/snapshotfs/upload_progress.go
+++ b/snapshot/snapshotfs/upload_progress.go
@@ -7,7 +7,9 @@
 	"github.com/kopia/kopia/internal/uitask"
 )
 
-// UploadProgress is invoked by by uploader to report status of file and directory uploads.
+// UploadProgress is invoked by uploader to report status of file and directory uploads.
+//
+//nolint:interfacebloat
 type UploadProgress interface {
 	// UploadStarted is emitted once at the start of an upload
 	UploadStarted()
diff --git a/tools/gettool/checksums.txt b/tools/gettool/checksums.txt
index 76331582..1f07ee91 100644
--- a/tools/gettool/checksums.txt
+++ b/tools/gettool/checksums.txt
@@ -8,12 +8,12 @@ https://github.com/gohugoio/hugo/releases/download/v0.89.2/hugo_extended_0.89.2_
 https://github.com/gohugoio/hugo/releases/download/v0.89.2/hugo_extended_0.89.2_macOS-64bit.tar.gz: f9185f6d14eb84d9029d59cdd8a977f2f0be334c4f9d38f2099e56a0c0734731
 https://github.com/gohugoio/hugo/releases/download/v0.89.2/hugo_extended_0.89.2_macOS-ARM64.tar.gz: 99a5b4738528d4858a0237199eabc7aee77674c8c7edcfe269efb4b515566cec
 https://github.com/gohugoio/hugo/releases/download/v0.89.2/hugo_extended_0.89.2_windows-64bit.zip: 8d79db4f24fbf023c64862c37d09291ac216875dad91e71dd8753cb5883e4274
-https://github.com/golangci/golangci-lint/releases/download/v1.48.0/golangci-lint-1.48.0-darwin-amd64.tar.gz: ec2e1c3bb3d34268cd57baba6b631127beb185bbe8cfde8ac40ba9b4c8615784
-https://github.com/golangci/golangci-lint/releases/download/v1.48.0/golangci-lint-1.48.0-darwin-arm64.tar.gz: ce69d7b94940c197ee3d293cfae7530191c094f76f9aecca97554058b12725ac
-https://github.com/golangci/golangci-lint/releases/download/v1.48.0/golangci-lint-1.48.0-linux-amd64.tar.gz: 127c5c9d47cf3a3cf4128815dea1d9623d57a83a22005e91b986b0cbceb09233
-https://github.com/golangci/golangci-lint/releases/download/v1.48.0/golangci-lint-1.48.0-linux-arm64.tar.gz: b772408fdda4957edfe93526c7654b787695ac345d76cdf2bdc4470995f62a81
-https://github.com/golangci/golangci-lint/releases/download/v1.48.0/golangci-lint-1.48.0-linux-armv6.tar.gz: 65032d81a57660b802485c7043b030f6942c8f0b5f4e3616e79f22533cafa6f7
-https://github.com/golangci/golangci-lint/releases/download/v1.48.0/golangci-lint-1.48.0-windows-amd64.zip: 12cbd4c975a7a0c59f28f2f19324490bdca1d530279637467d918ad811de17d2
+https://github.com/golangci/golangci-lint/releases/download/v1.50.1/golangci-lint-1.50.1-darwin-amd64.tar.gz: 0f615fb8c364f6e4a213f2ed2ff7aa1fc2b208addf29511e89c03534067bbf57
+https://github.com/golangci/golangci-lint/releases/download/v1.50.1/golangci-lint-1.50.1-darwin-arm64.tar.gz: 3ca9753d7804b34f9165427fbe339dbea69bd80be8a10e3f02c6037393b2e1c4
+https://github.com/golangci/golangci-lint/releases/download/v1.50.1/golangci-lint-1.50.1-linux-amd64.tar.gz: 4ba1dc9dbdf05b7bdc6f0e04bdfe6f63aa70576f51817be1b2540bbce017b69a
+https://github.com/golangci/golangci-lint/releases/download/v1.50.1/golangci-lint-1.50.1-linux-arm64.tar.gz: 3ea0a6d5946340f6831646e2c67a317dd9b35bdc4e09b2df953a86f09ba05d74
+https://github.com/golangci/golangci-lint/releases/download/v1.50.1/golangci-lint-1.50.1-linux-armv6.tar.gz: 980832f12fbdd0a8e636666839b168c2bbf0ca573ff50b042d3977f65c4987d7
+https://github.com/golangci/golangci-lint/releases/download/v1.50.1/golangci-lint-1.50.1-windows-amd64.zip: 8c2da214884db02fb7f3d929672c515ae3b9d10defad4dd661c4ab365a316d68
 https://github.com/goreleaser/goreleaser/releases/download/v0.176.0/goreleaser_Darwin_arm64.tar.gz: 1f95e6561974f4766d8833438b646b06930563ca9867447ea03edb623d876c75
 https://github.com/goreleaser/goreleaser/releases/download/v0.176.0/goreleaser_Darwin_x86_64.tar.gz: 17ecad881a50e32f033da5a200c8417d37cae70f09e925645452937998aca506
 https://github.com/goreleaser/goreleaser/releases/download/v0.176.0/goreleaser_Linux_arm64.tar.gz: 8bf2a9b9e84498bfa239f2fe91b2d555642c87ab9d3f5d37f29e6e97116910a3
diff --git a/tools/tools.mk b/tools/tools.mk
index 958df918..e2a2180a 100644
--- a/tools/tools.mk
+++ b/tools/tools.mk
@@ -102,8 +102,8 @@ retry:=
 endif
 
 # tool versions
-GOLANGCI_LINT_VERSION=1.48.0
-CHECKLOCKS_VERSION=release-20220314.0
+GOLANGCI_LINT_VERSION=1.50.1
+CHECKLOCKS_VERSION=release-20221026.0
 NODE_VERSION=16.13.0
 HUGO_VERSION=0.89.2
 GOTESTSUM_VERSION=1.7.0


Test patch:
diff --git a/.github/workflows/compat-test.yml b/.github/workflows/compat-test.yml
index dbd660bf..0744f31a 100644
--- a/.github/workflows/compat-test.yml
+++ b/.github/workflows/compat-test.yml
@@ -16,7 +16,7 @@ jobs:
     - name: Set up Go.
       uses: actions/setup-go@v3
       with:
-        go-version: '1.18'
+        go-version: '1.19'
       id: go
     - name: Check out code into the Go module directory
       uses: actions/checkout@v3
diff --git a/.github/workflows/endurance-test.yml b/.github/workflows/endurance-test.yml
index f26d7430..75e3b329 100644
--- a/.github/workflows/endurance-test.yml
+++ b/.github/workflows/endurance-test.yml
@@ -21,7 +21,7 @@ jobs:
     - name: Set up Go.
       uses: actions/setup-go@v3
       with:
-        go-version: '1.18'
+        go-version: '1.19'
       id: go
     - name: Check out code into the Go module directory
       uses: actions/checkout@v3
diff --git a/.github/workflows/htmlui-tests.yml b/.github/workflows/htmlui-tests.yml
index 8d536ccf..98ed3dab 100644
--- a/.github/workflows/htmlui-tests.yml
+++ b/.github/workflows/htmlui-tests.yml
@@ -29,7 +29,7 @@ jobs:
     - name: Set up Go.
       uses: actions/setup-go@v3
       with:
-        go-version: '1.18'
+        go-version: '1.19'
       id: go
     - name: Check out code into the Go module directory
       uses: actions/checkout@v3
diff --git a/.github/workflows/provider-tests.yml b/.github/workflows/provider-tests.yml
index a3e2f23a..e63946b1 100644
--- a/.github/workflows/provider-tests.yml
+++ b/.github/workflows/provider-tests.yml
@@ -19,7 +19,7 @@ jobs:
     - name: Set up Go.
       uses: actions/setup-go@v3
       with:
-        go-version: '1.18'
+        go-version: '1.19'
       id: go
     - name: Check out code into the Go module directory
       uses: actions/checkout@v3
diff --git a/.github/workflows/stress-test.yml b/.github/workflows/stress-test.yml
index 2dc2674e..09f97090 100644
--- a/.github/workflows/stress-test.yml
+++ b/.github/workflows/stress-test.yml
@@ -20,7 +20,7 @@ jobs:
     - name: Set up Go.
       uses: actions/setup-go@v3
       with:
-        go-version: '1.18'
+        go-version: '1.19'
       id: go
     - name: Check out code into the Go module directory
       uses: actions/checkout@v3
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index fb5e2892..3ac4b5e3 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -40,11 +40,11 @@ jobs:
     - name: Set up Go.
       uses: actions/setup-go@v3
       with:
-        go-version: '1.18'
+        go-version: '1.19'
       id: go
       if: ${{ !contains(matrix.os, 'ARMHF') }}
     - name: Install GoLang for ARMHF
-      run: "echo /usr/local/go/bin >> $GITHUB_PATH; rm -rf /usr/local/go && mkdir -p /usr/local/go && curl -s -L https://go.dev/dl/go1.18.2.linux-armv6l.tar.gz | tar -C /usr/local -xz"
+      run: "echo /usr/local/go/bin >> $GITHUB_PATH; rm -rf /usr/local/go && mkdir -p /usr/local/go && curl -s -L https://go.dev/dl/go1.19.2.linux-armv6l.tar.gz | tar -C /usr/local -xz"
       if: ${{ contains(matrix.os, 'ARMHF') }}
     - name: Install Windows-specific packages
       run: "choco install --no-progress -y make zip unzip curl"
diff --git a/repo/content/content_manager_test.go b/repo/content/content_manager_test.go
index c2dfb15a..4e569e40 100644
--- a/repo/content/content_manager_test.go
+++ b/repo/content/content_manager_test.go
@@ -1455,7 +1455,7 @@ func (s *contentManagerSuite) TestRewriteDeleted(t *testing.T) {
 
 func (s *contentManagerSuite) TestDeleteAndRecreate(t *testing.T) {
 	ctx := testlogging.Context(t)
-	// simulate race between delete/recreate and delete
+	// simulate race between delete/recreate and
 	// delete happens at t0+10, recreate at t0+20 and second delete time is parameterized.
 	// depending on it, the second delete results will be visible.
 	cases := []struct {
diff --git a/repo/content/index_blob_manager_v0_test.go b/repo/content/index_blob_manager_v0_test.go
index f2af6da4..5cc1cc22 100644
--- a/repo/content/index_blob_manager_v0_test.go
+++ b/repo/content/index_blob_manager_v0_test.go
@@ -683,7 +683,7 @@ func getAllFakeContentsInternal(ctx context.Context, t *testing.T, m *indexBlobM
 			return nil, nil, errors.Wrap(err, "error unmarshaling")
 		}
 
-		// merge contents based based on time
+		// merge contents based on time
 		for k, v := range indexData.Entries {
 			old, ok := allContents[k]
 
diff --git a/tests/robustness/snapshotter.go b/tests/robustness/snapshotter.go
index f2a03c7d..e2df9c26 100644
--- a/tests/robustness/snapshotter.go
+++ b/tests/robustness/snapshotter.go
@@ -1,7 +1,7 @@
 //go:build darwin || (linux && amd64)
 // +build darwin linux,amd64
 
-// Package robustness contains tests that that validate data stability over time.
+// Package robustness contains tests that validate data stability over time.
 // The package, while designed for Kopia, is written with abstractions that
 // can be used to test other environments.
 package robustness
diff --git a/tests/tools/fswalker/reporter/reporter.go b/tests/tools/fswalker/reporter/reporter.go
index bb34acc6..befaf036 100644
--- a/tests/tools/fswalker/reporter/reporter.go
+++ b/tests/tools/fswalker/reporter/reporter.go
@@ -1,7 +1,7 @@
 //go:build darwin || (linux && amd64)
 // +build darwin linux,amd64
 
-// Package reporter wraps calls to the the fswalker Reporter
+// Package reporter wraps calls to the fswalker Reporter
 package reporter
 
 import (
diff --git a/tests/tools/fswalker/walker/walker.go b/tests/tools/fswalker/walker/walker.go
index be782868..cc8bd3ad 100644
--- a/tests/tools/fswalker/walker/walker.go
+++ b/tests/tools/fswalker/walker/walker.go
@@ -1,7 +1,7 @@
 //go:build darwin || (linux && amd64)
 // +build darwin linux,amd64
 
-// Package walker wraps calls to the the fswalker Walker
+// Package walker wraps calls to the fswalker Walker
 package walker
 
 import (