chore(golang-rewrite): setup gofumpt

* Install and run gofumpt in Golang lint workflow
* Remove golangci-lint
* Run gofumpt and fix revive linter errors
This commit is contained in:
Trevor Brown 2024-04-30 10:13:05 -04:00
parent f5cfe36b18
commit b8d13190ed
10 changed files with 77 additions and 79 deletions

View File

@ -31,12 +31,16 @@ jobs:
go-version: '1.21.5'
- name: Install dependencies
run: go get .
- name: golangci-lint
uses: golangci/golangci-lint-action@v4
with:
version: v1.57
- name: Install gofumpt for formatting
run: go install mvdan.cc/gofumpt@latest
- name: Run 'gofumpt'
run: gofumpt -l -w .
- name: Check format
run: '[ -z "$(gofmt -l ./...)" ]'
- name: Install revive for linting
run: go install github.com/mgechev/revive@latest
- name: Run 'revive'
run: revive -set_exit_status ./...
- name: Vet
run: go vet
- name: Install staticcheck for linting

View File

@ -1,6 +0,0 @@
linters:
enable:
- govet
- gofmt
- revive
- staticcheck

View File

@ -8,6 +8,7 @@ build: test lint
fmt:
go fmt ./...
gofumpt -l -w .
verify:
go mod verify
@ -25,7 +26,7 @@ cover: test
lint: fmt
staticcheck -tests -show-ignored ./...
revive ./...
revive -set_exit_status ./...
vet: fmt
go vet .

View File

@ -1,11 +1,13 @@
// Package cmd contains the asdf CLI command code
package cmd
import (
"asdf/config"
"asdf/plugins"
"log"
"os"
"asdf/config"
"asdf/plugins"
"github.com/urfave/cli/v2"
)
@ -15,6 +17,7 @@ Manage all your runtime versions with one tool!
Complete documentation is available at https://asdf-vm.com/`
// Execute defines the full CLI API and then runs it
func Execute() {
logger := log.New(os.Stderr, "", 0)
log.SetFlags(0)
@ -95,7 +98,6 @@ func Execute() {
}
err := app.Run(os.Args)
if err != nil {
os.Exit(1)
}
@ -129,7 +131,6 @@ func pluginRemoveCommand(_ *cli.Context, logger *log.Logger, pluginName string)
}
err = plugins.Remove(conf, pluginName)
if err != nil {
logger.Printf("error removing plugin: %s", err)
}
@ -147,7 +148,6 @@ func pluginListCommand(cCtx *cli.Context, logger *log.Logger) error {
}
plugins, err := plugins.List(conf, urls, refs)
if err != nil {
logger.Printf("error loading plugin list: %s", err)
return err

View File

@ -1,3 +1,5 @@
// Package config provides a unified API for fetching asdf config. Either from
// the asdfrc file or environment variables.
package config
import (
@ -10,37 +12,41 @@ import (
"gopkg.in/ini.v1"
)
const ForcePrependDefault = false
const DataDirDefault = "~/.asdf"
const ConfigFileDefault = "~/.asdfrc"
const DefaultToolVersionsFilenameDefault = ".tool-versions"
const (
forcePrependDefault = false
dataDirDefault = "~/.asdf"
configFileDefault = "~/.asdfrc"
defaultToolVersionsFilenameDefault = ".tool-versions"
)
/* Struct to represent the remote plugin repo check duration (never or every N
* seconds). It's not clear to me how this should be represented in Golang so
* using a struct for maximum flexibility. */
/* PluginRepoCheckDuration represents the remote plugin repo check duration
* (never or every N seconds). It's not clear to me how this should be
* represented in Golang so using a struct for maximum flexibility. */
type PluginRepoCheckDuration struct {
Never bool
Every int
}
var PluginRepoCheckDurationDefault = PluginRepoCheckDuration{Every: 60}
var pluginRepoCheckDurationDefault = PluginRepoCheckDuration{Every: 60}
// Settings is a struct that stores config values from the asdfrc file
type Settings struct {
Loaded bool
LegacyVersionFile bool
// I don't think this setting should be supported in the Golang implementation
//UseReleaseCandidates bool
// UseReleaseCandidates bool
AlwaysKeepDownload bool
PluginRepositoryLastCheckDuration PluginRepoCheckDuration
DisablePluginShortNameRepository bool
}
// Config is the primary value this package builds and returns
type Config struct {
Home string
ConfigFile string `env:"ASDF_CONFIG_FILE, overwrite"`
DefaultToolVersionsFilename string `env:"ASDF_DEFAULT_TOOL_VERSIONS_FILENAME, overwrite"`
// Unclear if this value will be needed with the golang implementation.
//AsdfDir string
// AsdfDir string
DataDir string `env:"ASDF_DATA_DIR, overwrite"`
ForcePrepend bool `env:"ASDF_FORCE_PREPEND, overwrite"`
// Field that stores the settings struct if it is loaded
@ -52,12 +58,12 @@ func defaultSettings() *Settings {
Loaded: false,
LegacyVersionFile: false,
AlwaysKeepDownload: false,
PluginRepositoryLastCheckDuration: PluginRepoCheckDurationDefault,
PluginRepositoryLastCheckDuration: pluginRepoCheckDurationDefault,
DisablePluginShortNameRepository: false,
}
}
func NewPluginRepoCheckDuration(checkDuration string) PluginRepoCheckDuration {
func newPluginRepoCheckDuration(checkDuration string) PluginRepoCheckDuration {
if strings.ToLower(checkDuration) == "never" {
return PluginRepoCheckDuration{Never: true}
}
@ -65,21 +71,20 @@ func NewPluginRepoCheckDuration(checkDuration string) PluginRepoCheckDuration {
every, err := strconv.Atoi(checkDuration)
if err != nil {
// if error parsing config use default value
return PluginRepoCheckDurationDefault
return pluginRepoCheckDurationDefault
}
return PluginRepoCheckDuration{Every: every}
}
// LoadConfig builds the Config struct from environment variables
func LoadConfig() (Config, error) {
config, err := loadConfigEnv()
if err != nil {
return config, err
}
homeDir, err := homedir.Dir()
if err != nil {
return config, err
}
@ -92,9 +97,11 @@ func LoadConfig() (Config, error) {
// Methods on the Config struct that allow it to load and cache values from the
// Settings struct, which is loaded from file on disk and therefor somewhat
// "expensive".
// LegacyVersionFile loads the asdfrc if it isn't already loaded and fetches
// the legacy version file support flag
func (c *Config) LegacyVersionFile() (bool, error) {
err := c.loadSettings()
if err != nil {
return false, err
}
@ -102,9 +109,10 @@ func (c *Config) LegacyVersionFile() (bool, error) {
return c.Settings.LegacyVersionFile, nil
}
// AlwaysKeepDownload loads the asdfrc if it isn't already loaded and fetches
// the keep downloads boolean flag
func (c *Config) AlwaysKeepDownload() (bool, error) {
err := c.loadSettings()
if err != nil {
return false, err
}
@ -112,19 +120,21 @@ func (c *Config) AlwaysKeepDownload() (bool, error) {
return c.Settings.AlwaysKeepDownload, nil
}
// PluginRepositoryLastCheckDuration loads the asdfrc if it isn't already loaded
// and fetches the keep boolean flag
func (c *Config) PluginRepositoryLastCheckDuration() (PluginRepoCheckDuration, error) {
err := c.loadSettings()
if err != nil {
return NewPluginRepoCheckDuration(""), err
return newPluginRepoCheckDuration(""), err
}
return c.Settings.PluginRepositoryLastCheckDuration, nil
}
// DisablePluginShortNameRepository loads the asdfrc if it isn't already loaded
// and fetches the disable plugin short name repo flag
func (c *Config) DisablePluginShortNameRepository() (bool, error) {
err := c.loadSettings()
if err != nil {
return false, err
}
@ -138,7 +148,6 @@ func (c *Config) loadSettings() error {
}
settings, err := loadSettings(c.ConfigFile)
if err != nil {
return err
}
@ -149,21 +158,21 @@ func (c *Config) loadSettings() error {
}
func loadConfigEnv() (Config, error) {
dataDir, err := homedir.Expand(DataDirDefault)
dataDir, err := homedir.Expand(dataDirDefault)
if err != nil {
return Config{}, err
}
configFile, err := homedir.Expand(ConfigFileDefault)
configFile, err := homedir.Expand(configFileDefault)
if err != nil {
return Config{}, err
}
config := Config{
ForcePrepend: ForcePrependDefault,
ForcePrepend: forcePrependDefault,
DataDir: dataDir,
ConfigFile: configFile,
DefaultToolVersionsFilename: DefaultToolVersionsFilenameDefault,
DefaultToolVersionsFilename: defaultToolVersionsFilenameDefault,
}
context := context.Background()
@ -175,7 +184,6 @@ func loadConfigEnv() (Config, error) {
func loadSettings(asdfrcPath string) (Settings, error) {
// asdfrc is effectively formatted as ini
config, err := ini.Load(asdfrcPath)
if err != nil {
return Settings{}, err
}
@ -185,7 +193,7 @@ func loadSettings(asdfrcPath string) (Settings, error) {
settings := defaultSettings()
settings.Loaded = true
settings.PluginRepositoryLastCheckDuration = NewPluginRepoCheckDuration(mainConf.Key("plugin_repository_last_check_duration").String())
settings.PluginRepositoryLastCheckDuration = newPluginRepoCheckDuration(mainConf.Key("plugin_repository_last_check_duration").String())
boolOverride(&settings.LegacyVersionFile, mainConf, "legacy_version_file")
boolOverride(&settings.AlwaysKeepDownload, mainConf, "always_keep_download")

View File

@ -118,7 +118,7 @@ func TestBatsTests(t *testing.T) {
//})
}
//func runBatsFile(t *testing.T, dir, filename string) {
// func runBatsFile(t *testing.T, dir, filename string) {
// t.Helper()
// cmd := exec.Command("bats", "--verbose-run", fmt.Sprintf("test/%s", filename))
@ -149,7 +149,6 @@ func buildAsdf(t *testing.T, dir string) {
cmd := exec.Command("go", "build", "-o", dir)
err := cmd.Run()
if err != nil {
t.Fatal("Failed to build asdf")
}

View File

@ -40,7 +40,6 @@ func (g Plugin) Clone(pluginURL string) error {
_, err := git.PlainClone(g.directory, false, &git.CloneOptions{
URL: pluginURL,
})
if err != nil {
return fmt.Errorf("unable to clone plugin: %w", err)
}
@ -51,7 +50,6 @@ func (g Plugin) Clone(pluginURL string) error {
// Head returns the current HEAD ref of the plugin's Git repository
func (g Plugin) Head() (string, error) {
repo, err := gitOpen(g.directory)
if err != nil {
return "", err
}
@ -67,7 +65,6 @@ func (g Plugin) Head() (string, error) {
// RemoteURL returns the URL of the default remote for the plugin's Git repository
func (g Plugin) RemoteURL() (string, error) {
repo, err := gitOpen(g.directory)
if err != nil {
return "", err
}
@ -84,7 +81,6 @@ func (g Plugin) RemoteURL() (string, error) {
// latest commit on the current branch
func (g Plugin) Update(ref string) (string, error) {
repo, err := gitOpen(g.directory)
if err != nil {
return "", err
}
@ -94,7 +90,6 @@ func (g Plugin) Update(ref string) (string, error) {
if ref == "" {
// If no ref is provided checkout latest commit on current branch
head, err := repo.Head()
if err != nil {
return "", err
}
@ -138,7 +133,6 @@ func (g Plugin) Update(ref string) (string, error) {
func gitOpen(directory string) (*git.Repository, error) {
repo, err := git.PlainOpen(directory)
if err != nil {
return repo, fmt.Errorf("unable to open plugin Git repository: %w", err)
}

View File

@ -97,7 +97,7 @@ func TestPluginUpdate(t *testing.T) {
t.Run("returns error when plugin repo does not exist", func(t *testing.T) {
badPluginName := "badplugin"
badPluginDir := filepath.Join(tempDir, badPluginName)
err := os.MkdirAll(badPluginDir, 0777)
err := os.MkdirAll(badPluginDir, 0o777)
assert.Nil(t, err)
badPlugin := NewPlugin(badPluginDir)
@ -141,7 +141,6 @@ func TestPluginUpdate(t *testing.T) {
assert.Equal(t, updatedToRef, "")
expectedErrMsg := "couldn't find remote ref \"non-existant\""
assert.ErrorContains(t, err, expectedErrMsg)
})
t.Run("updates plugin to ref when plugin with name and ref exist", func(t *testing.T) {
@ -167,7 +166,6 @@ func getCurrentCommit(path string) (string, error) {
func getCommit(path, revision string) (string, error) {
repo, err := git.PlainOpen(path)
if err != nil {
return "", err
}
@ -179,25 +177,21 @@ func getCommit(path, revision string) (string, error) {
func checkoutPreviousCommit(path string) (string, error) {
repo, err := git.PlainOpen(path)
if err != nil {
return "", err
}
previousHash, err := repo.ResolveRevision(plumbing.Revision("HEAD~"))
if err != nil {
return "", err
}
worktree, err := repo.Worktree()
if err != nil {
return "", err
}
err = worktree.Reset(&git.ResetOptions{Commit: *previousHash})
if err != nil {
return "", err
}

View File

@ -1,20 +1,25 @@
// Package plugins provides functions for interacting with asdf plugins
package plugins
import (
"asdf/config"
"errors"
"fmt"
"os"
"path/filepath"
"regexp"
"asdf/config"
"github.com/go-git/go-git/v5"
)
const dataDirPlugins = "plugins"
const invalidPluginNameMsg = "'%q' is invalid. Name may only contain lowercase letters, numbers, '_', and '-'"
const pluginAlreadyExists = "plugin named %q already added"
const (
dataDirPlugins = "plugins"
invalidPluginNameMsg = "'%q' is invalid. Name may only contain lowercase letters, numbers, '_', and '-'"
pluginAlreadyExists = "plugin named %q already added"
)
// Plugin represents a plugin to the packages in asdf
type Plugin struct {
Name string
Dir string
@ -22,6 +27,8 @@ type Plugin struct {
URL string
}
// List takes config and flags for what to return and builds a list of plugins
// representing the currently installed plugins on the system.
func List(config config.Config, urls, refs bool) (plugins []Plugin, err error) {
pluginsDir := DataDirectory(config.DataDir)
files, err := os.ReadDir(pluginsDir)
@ -36,7 +43,6 @@ func List(config config.Config, urls, refs bool) (plugins []Plugin, err error) {
var refString string
location := filepath.Join(pluginsDir, file.Name())
repo, err := git.PlainOpen(location)
// TODO: Improve these error messages
if err != nil {
return plugins, err
@ -78,15 +84,15 @@ func List(config config.Config, urls, refs bool) (plugins []Plugin, err error) {
return plugins, nil
}
// Add takes plugin name and Git URL and installs the plugin if it isn't
// already installed
func Add(config config.Config, pluginName, pluginURL string) error {
err := validatePluginName(pluginName)
if err != nil {
return err
}
exists, err := PluginExists(config.DataDir, pluginName)
if err != nil {
return fmt.Errorf("unable to check if plugin already exists: %w", err)
}
@ -104,7 +110,6 @@ func Add(config config.Config, pluginName, pluginURL string) error {
_, err = git.PlainClone(pluginDir, false, &git.CloneOptions{
URL: pluginURL,
})
if err != nil {
return fmt.Errorf("unable to clone plugin: %w", err)
}
@ -112,15 +117,14 @@ func Add(config config.Config, pluginName, pluginURL string) error {
return nil
}
// Remove removes a plugin with the provided name if installed
func Remove(config config.Config, pluginName string) error {
err := validatePluginName(pluginName)
if err != nil {
return err
}
exists, err := PluginExists(config.DataDir, pluginName)
if err != nil {
return fmt.Errorf("unable to check if plugin exists: %w", err)
}
@ -134,6 +138,8 @@ func Remove(config config.Config, pluginName string) error {
return os.RemoveAll(pluginDir)
}
// PluginExists returns a boolean indicating whether or not a plugin with the
// provided name is currently installed
func PluginExists(dataDir, pluginName string) (bool, error) {
pluginDir := PluginDirectory(dataDir, pluginName)
fileInfo, err := os.Stat(pluginDir)
@ -148,10 +154,13 @@ func PluginExists(dataDir, pluginName string) (bool, error) {
return fileInfo.IsDir(), nil
}
// PluginDirectory returns the directory a plugin would be installed in, if it
// is installed
func PluginDirectory(dataDir, pluginName string) string {
return filepath.Join(DataDirectory(dataDir), pluginName)
}
// DataDirectory return the plugin directory inside the data directory
func DataDirectory(dataDir string) string {
return filepath.Join(dataDir, dataDirPlugins)
}

View File

@ -1,7 +1,6 @@
package plugins
import (
"asdf/config"
"fmt"
"os"
"os/exec"
@ -9,6 +8,8 @@ import (
"strings"
"testing"
"asdf/config"
"github.com/stretchr/testify/assert"
)
@ -76,7 +77,7 @@ func TestAdd(t *testing.T) {
testDataDir := t.TempDir()
t.Run("when given an invalid plugin name prints an error", func(t *testing.T) {
var invalids = []string{"plugin^name", "plugin%name", "plugin name", "PLUGIN_NAME"}
invalids := []string{"plugin^name", "plugin%name", "plugin name", "PLUGIN_NAME"}
for _, invalid := range invalids {
t.Run(invalid, func(t *testing.T) {
@ -95,7 +96,6 @@ func TestAdd(t *testing.T) {
// Add plugin
err := Add(conf, testPluginName, testRepo)
if err != nil {
t.Fatal("Expected to be able to add plugin")
}
@ -176,14 +176,13 @@ func TestRemove(t *testing.T) {
func TestPluginExists(t *testing.T) {
testDataDir := t.TempDir()
pluginDir := PluginDirectory(testDataDir, testPluginName)
err := os.MkdirAll(pluginDir, 0777)
err := os.MkdirAll(pluginDir, 0o777)
if err != nil {
t.Errorf("got %v, expected nil", err)
}
t.Run("returns true when plugin exists", func(t *testing.T) {
exists, err := PluginExists(testDataDir, testPluginName)
if err != nil {
t.Errorf("got %v, expected nil", err)
}
@ -202,7 +201,6 @@ func TestPluginExists(t *testing.T) {
}
exists, err := PluginExists(testDataDir, pluginName)
if err != nil {
t.Errorf("got %v, expected nil", err)
}
@ -214,7 +212,6 @@ func TestPluginExists(t *testing.T) {
t.Run("returns false when plugin dir does not exist", func(t *testing.T) {
exists, err := PluginExists(testDataDir, "non-existant")
if err != nil {
t.Errorf("got %v, expected nil", err)
}
@ -241,7 +238,7 @@ func TestValidatePluginName(t *testing.T) {
refuteError(t, err)
})
var invalids = []string{"plugin^name", "plugin%name", "plugin name", "PLUGIN_NAME"}
invalids := []string{"plugin^name", "plugin%name", "plugin name", "PLUGIN_NAME"}
for _, invalid := range invalids {
t.Run(invalid, func(t *testing.T) {
@ -261,7 +258,7 @@ func refuteError(t *testing.T, err error) {
}
func touchFile(name string) error {
file, err := os.OpenFile(name, os.O_RDONLY|os.O_CREATE, 0644)
file, err := os.OpenFile(name, os.O_RDONLY|os.O_CREATE, 0o644)
if err != nil {
return err
}
@ -323,7 +320,6 @@ func runCmd(cmdName string, args ...string) error {
cmd.Stderr = &stderr
err := cmd.Run()
if err != nil {
// If command fails print both stderr and stdout
fmt.Println("stdout:", stdout.String())
@ -336,7 +332,6 @@ func runCmd(cmdName string, args ...string) error {
func moduleRoot() (string, error) {
currentDir, err := os.Getwd()
if err != nil {
return "", err
}