diff --git a/.golangci.yaml b/.golangci.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..1e5f801706c912312d2e98bd349771bc8f664d59
--- /dev/null
+++ b/.golangci.yaml
@@ -0,0 +1,29 @@
+linters:
+  enable:
+    - errorlint
+    - forbidigo
+    - gochecknoinits
+    - gocritic
+    - goconst
+    - gocyclo
+    - gofumpt
+    - goimports
+    - misspell
+    - revive
+    - unconvert
+    - unparam
+    - wastedassign
+
+linters-settings:
+  gocyclo:
+    min-complexity: 12
+  gofumpt:
+    extra-rules: true
+  govet:
+    enable-all: true
+    disable:
+      - fieldalignment
+
+run:
+  skip-dirs:
+    - goreleaser
diff --git a/pkg/config/config.go b/pkg/config/config.go
index c5dc9d0d123366b1924f24500c1e43c9d0e47339..8c37b89a23d2243d2efb548a3753b3b9b2b37b00 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -1,6 +1,7 @@
 package config
 
 import (
+	"errors"
 	"os"
 	"strings"
 
@@ -142,18 +143,20 @@ func must(err error) {
 	}
 }
 
+var trueString = "true"
+
 func detectCI() string {
-	if os.Getenv("GITHUB_ACTIONS") == "true" {
+	if os.Getenv("GITHUB_ACTIONS") == trueString {
 		return "github"
 	}
-	if os.Getenv("GITLAB_CI") == "true" {
+	if os.Getenv("GITLAB_CI") == trueString {
 		return "gitlab"
 	}
 	return "default"
 }
 
 func defaultProvider() string {
-	if os.Getenv("GITLAB_CI") == "true" {
+	if os.Getenv("GITLAB_CI") == trueString {
 		return "gitlab"
 	}
 	return "github"
@@ -218,7 +221,8 @@ func InitConfig(cmd *cobra.Command) error {
 	viper.SetConfigType("json")
 
 	if err := viper.ReadInConfig(); err != nil {
-		if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
+		var viperConfigNotFound viper.ConfigFileNotFoundError
+		if !errors.As(err, &viperConfigNotFound) {
 			return err
 		}
 	}
diff --git a/pkg/plugin/discovery/discovery.go b/pkg/plugin/discovery/discovery.go
index 6ddf072090c95fc6c38d7ebca2f171207768b3cb..e84e6b447ad6139fb3d97bfd8395ef7a48fa888b 100644
--- a/pkg/plugin/discovery/discovery.go
+++ b/pkg/plugin/discovery/discovery.go
@@ -69,7 +69,9 @@ func (d *Discovery) FindPlugin(t, name string) (*plugin.PluginInfo, error) {
 	if err != nil {
 		return nil, err
 	}
-	if err := setAndEnsurePluginPath(pInfo); err != nil {
+
+	err = setAndEnsurePluginPath(pInfo)
+	if err != nil {
 		return nil, err
 	}
 
diff --git a/pkg/plugin/discovery/download.go b/pkg/plugin/discovery/download.go
index 492124e73bb6f23a9a11d1ce4cb7cafc04b54170..c34da1fc3ddd3f0386f0828aaa6bc8e8fc1e4da5 100644
--- a/pkg/plugin/discovery/download.go
+++ b/pkg/plugin/discovery/download.go
@@ -5,6 +5,7 @@ import (
 	"compress/gzip"
 	"crypto/sha256"
 	"encoding/hex"
+	"errors"
 	"fmt"
 	"io"
 	"os"
@@ -66,7 +67,7 @@ func extractFileFromTarGz(name, inputFile, outputFile string) error {
 	tarReader := tar.NewReader(decompressedFile)
 	for {
 		header, err := tarReader.Next()
-		if err == io.EOF {
+		if errors.Is(err, io.EOF) {
 			return fmt.Errorf("could not extract file")
 		}
 		if err != nil {
@@ -97,9 +98,9 @@ func downloadPlugin(pluginInfo *plugin.PluginInfo, downloadInfo *resolver.Plugin
 		return "", err
 	}
 	if downloadInfo.Checksum != "" {
-		sum, err := hex.DecodeString(downloadInfo.Checksum)
-		if err != nil {
-			return "", err
+		sum, decErr := hex.DecodeString(downloadInfo.Checksum)
+		if decErr != nil {
+			return "", decErr
 		}
 		req.SetChecksum(sha256.New(), sum, true)
 	}
@@ -108,7 +109,8 @@ func downloadPlugin(pluginInfo *plugin.PluginInfo, downloadInfo *resolver.Plugin
 	if showProgress {
 		showDownloadProgressBar(pluginInfo.ShortNormalizedName, res)
 	}
-	if err := res.Err(); err != nil {
+	err = res.Err()
+	if err != nil {
 		return "", err
 	}
 
diff --git a/pkg/plugin/discovery/local.go b/pkg/plugin/discovery/local.go
index dbb346d9c7a0903752f3e971d3402e4431f6281a..cfeb036bc56251abf835590d1bcde98f4ef7fec0 100644
--- a/pkg/plugin/discovery/local.go
+++ b/pkg/plugin/discovery/local.go
@@ -2,15 +2,14 @@ package discovery
 
 import (
 	"errors"
-	"io/ioutil"
+	"fmt"
 	"os"
 	"path"
 	"runtime"
 	"sort"
 
-	"github.com/go-semantic-release/semantic-release/v2/pkg/plugin"
-
 	"github.com/Masterminds/semver/v3"
+	"github.com/go-semantic-release/semantic-release/v2/pkg/plugin"
 )
 
 const PluginDir = ".semrel"
@@ -20,7 +19,8 @@ var osArchDir = runtime.GOOS + "_" + runtime.GOARCH
 func setAndEnsurePluginPath(pluginInfo *plugin.PluginInfo) error {
 	pluginPath := path.Join(PluginDir, osArchDir, pluginInfo.NormalizedName)
 	if _, err := os.Stat(pluginPath); os.IsNotExist(err) {
-		if err := os.MkdirAll(pluginPath, 0o755); err != nil {
+		err = os.MkdirAll(pluginPath, 0o755)
+		if err != nil {
 			return err
 		}
 	} else if err != nil {
@@ -33,7 +33,7 @@ func setAndEnsurePluginPath(pluginInfo *plugin.PluginInfo) error {
 var ErrPluginNotFound = errors.New("no plugin was found")
 
 func getMatchingVersionDir(pluginInfo *plugin.PluginInfo) (string, error) {
-	vDirs, err := ioutil.ReadDir(pluginInfo.PluginPath)
+	vDirs, err := os.ReadDir(pluginInfo.PluginPath)
 	if err != nil {
 		return "", err
 	}
@@ -75,7 +75,7 @@ func findPluginLocally(pluginInfo *plugin.PluginInfo) (string, error) {
 		return "", ErrPluginNotFound
 	}
 
-	files, err := ioutil.ReadDir(vPth)
+	files, err := os.ReadDir(vPth)
 	if err != nil {
 		return "", err
 	}
@@ -86,7 +86,12 @@ func findPluginLocally(pluginInfo *plugin.PluginInfo) (string, error) {
 		if f.IsDir() {
 			continue
 		}
-		if f.Mode()&0o100 == 0 {
+		fInfo, err := f.Info()
+		if err != nil {
+			return "", fmt.Errorf("failed to get file info for %s: %w", f.Name(), err)
+		}
+		// check if the file is executable by the user
+		if fInfo.Mode()&0o100 == 0 {
 			continue
 		}
 		return path.Join(vPth, f.Name()), nil
diff --git a/pkg/plugin/discovery/resolver/github/github.go b/pkg/plugin/discovery/resolver/github/github.go
index 8fd0acab168da46f7f364cfcdbe1bd592920e41e..29196052ee6c614fff8a67161508c82de8691539 100644
--- a/pkg/plugin/discovery/resolver/github/github.go
+++ b/pkg/plugin/discovery/resolver/github/github.go
@@ -3,7 +3,7 @@ package github
 import (
 	"context"
 	"fmt"
-	"io/ioutil"
+	"io"
 	"net/http"
 	"os"
 	"regexp"
@@ -18,21 +18,22 @@ import (
 	"golang.org/x/oauth2"
 )
 
-type GitHubResolver struct {
+type Resolver struct {
 	ghClient *github.Client
 }
 
-func NewResolver() *GitHubResolver {
+func NewResolver() *Resolver {
 	var tc *http.Client
 	if ghToken := os.Getenv("GITHUB_TOKEN"); ghToken != "" {
 		tc = oauth2.NewClient(context.Background(), oauth2.StaticTokenSource(&oauth2.Token{AccessToken: ghToken}))
 	}
-	return &GitHubResolver{
+	return &Resolver{
 		ghClient: github.NewClient(tc),
 	}
 }
 
-func (g *GitHubResolver) githubReleaseToDownloadInfo(repoOwner, repoName string, release *github.RepositoryRelease) (*resolver.PluginDownloadInfo, error) {
+//gocyclo:ignore
+func (g *Resolver) githubReleaseToDownloadInfo(repoOwner, repoName string, release *github.RepositoryRelease) (*resolver.PluginDownloadInfo, error) {
 	var checksumAsset *github.ReleaseAsset
 	var pluginAsset *github.ReleaseAsset
 	osArchRe := regexp.MustCompile("(?i)" + runtime.GOOS + "(_|-)" + runtime.GOARCH)
@@ -56,7 +57,7 @@ func (g *GitHubResolver) githubReleaseToDownloadInfo(repoOwner, repoName string,
 		if err != nil {
 			return nil, err
 		}
-		checksumData, err := ioutil.ReadAll(checksumDownload)
+		checksumData, err := io.ReadAll(checksumDownload)
 		checksumDownload.Close()
 		if err != nil {
 			return nil, err
@@ -91,7 +92,7 @@ func (gr ghReleases) Len() int           { return len(gr) }
 func (gr ghReleases) Less(i, j int) bool { return gr[j].version.LessThan(gr[i].version) }
 func (gr ghReleases) Swap(i, j int)      { gr[i], gr[j] = gr[j], gr[i] }
 
-func (g *GitHubResolver) getAllValidGitHubReleases(repoOwner, repoName string) (ghReleases, error) {
+func (g *Resolver) getAllValidGitHubReleases(repoOwner, repoName string) (ghReleases, error) {
 	ret := make(ghReleases, 0)
 	opts := &github.ListOptions{Page: 1, PerPage: 100}
 	for {
@@ -118,7 +119,7 @@ func (g *GitHubResolver) getAllValidGitHubReleases(repoOwner, repoName string) (
 	return ret, nil
 }
 
-func (g *GitHubResolver) ResolvePlugin(pluginInfo *plugin.PluginInfo) (*resolver.PluginDownloadInfo, error) {
+func (g *Resolver) ResolvePlugin(pluginInfo *plugin.PluginInfo) (*resolver.PluginDownloadInfo, error) {
 	if pluginInfo.RepoSlug == "" {
 		pluginInfo.RepoSlug = knownPlugins[pluginInfo.ShortNormalizedName]
 	}
@@ -155,6 +156,6 @@ func (g *GitHubResolver) ResolvePlugin(pluginInfo *plugin.PluginInfo) (*resolver
 	return di, err
 }
 
-func (g *GitHubResolver) Names() []string {
+func (g *Resolver) Names() []string {
 	return []string{"github", "gh"}
 }
diff --git a/pkg/plugin/discovery/resolver/registry/registry.go b/pkg/plugin/discovery/resolver/registry/registry.go
index 64a6405052ac2dee95511561b87a6ec292f1b89b..68eb4e206cb89ffec8df0a1cd50c90d69ba4847d 100644
--- a/pkg/plugin/discovery/resolver/registry/registry.go
+++ b/pkg/plugin/discovery/resolver/registry/registry.go
@@ -11,24 +11,24 @@ import (
 	"github.com/go-semantic-release/semantic-release/v2/pkg/plugin/discovery/resolver"
 )
 
-type RegistryResolver struct{}
+type Resolver struct{}
 
-func NewResolver() *RegistryResolver {
-	return &RegistryResolver{}
+func NewResolver() *Resolver {
+	return &Resolver{}
 }
 
-func (r *RegistryResolver) ResolvePlugin(pluginInfo *plugin.PluginInfo) (*resolver.PluginDownloadInfo, error) {
-	pluginApiRes, err := getPluginInfo(pluginInfo.NormalizedName)
+func (r *Resolver) ResolvePlugin(pluginInfo *plugin.PluginInfo) (*resolver.PluginDownloadInfo, error) {
+	pluginAPIRes, err := getPluginInfo(pluginInfo.NormalizedName)
 	if err != nil {
 		return nil, err
 	}
 
 	foundVersion := ""
 	if pluginInfo.Constraint == nil {
-		foundVersion = pluginApiRes.LatestRelease
+		foundVersion = pluginAPIRes.LatestRelease
 	} else {
 		versions := make(semver.Collection, 0)
-		for v := range pluginApiRes.Versions {
+		for v := range pluginAPIRes.Versions {
 			pv, err := semver.NewVersion(v)
 			if err != nil {
 				return nil, err
@@ -48,7 +48,7 @@ func (r *RegistryResolver) ResolvePlugin(pluginInfo *plugin.PluginInfo) (*resolv
 		return nil, errors.New("version not found")
 	}
 
-	releaseAsset := pluginApiRes.Versions[foundVersion].getMatchingAsset()
+	releaseAsset := pluginAPIRes.Versions[foundVersion].getMatchingAsset()
 	if releaseAsset == nil {
 		return nil, fmt.Errorf("a matching plugin was not found for %s/%s", runtime.GOOS, runtime.GOARCH)
 	}
@@ -60,6 +60,6 @@ func (r *RegistryResolver) ResolvePlugin(pluginInfo *plugin.PluginInfo) (*resolv
 	}, nil
 }
 
-func (r *RegistryResolver) Names() []string {
+func (r *Resolver) Names() []string {
 	return []string{"registry"}
 }