Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor #11

Merged
merged 2 commits into from
Sep 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.hvm
.markdownlint.yaml
/dist
/hvm
cover.out
dist/
hvm
NOTES.md
6 changes: 3 additions & 3 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ builds:
goarch: arm64
ldflags:
- -s -w
- -X github.com/jmooring/hvm/cmd.version={{.Version}}
- -X github.com/jmooring/hvm/cmd.commitHash={{.ShortCommit}}
- -X github.com/jmooring/hvm/cmd.buildDate={{.Date}}
- -X github.com/jmooring/hvm/cmd/hvm.version={{.Version}}
- -X github.com/jmooring/hvm/cmd/hvm.commitHash={{.ShortCommit}}
- -X github.com/jmooring/hvm/cmd/hvm.buildDate={{.Date}}
archives:
- format: tar.gz
name_template: >-
Expand Down
22 changes: 16 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

Hugo Version Manager (hvm) allows you to download and manage multiple versions of the extended edition of the [Hugo] static site generator. You can use hvm to specify which version of Hugo to use in the current directory.

![Demonstration](demo/hvm.gif)
![Demonstration](assets/hvm.gif)

If you do not specify a version of Hugo to use in the current directory, the Hugo executable will be found by searching the `PATH` environment variable.

Expand Down Expand Up @@ -47,12 +47,14 @@ Create an alias for the `hugo` command that overrides the executable path if a
valid `.hvm` file exists in the current directory.

<details>
<summary>Darwin: add this function to $HOME/.zshrc</summary>
<summary>Darwin</summary>
Add this function to $HOME/.zshrc

```zsh
# Hugo Version Manager: override path to the hugo executable.
hugo() {
hvm_common_msg="Run 'hvm use' to fix or 'hvm disable' to disable version management."
hvm_show_status=true
if [ -f ".hvm" ]; then
hugo_bin=$(cat ".hvm" 2> /dev/null)
if ! echo "${hugo_bin}" | grep -q "hugo$"; then
Expand All @@ -65,6 +67,10 @@ hugo() {
>&2 printf "%s\\n" "${hvm_common_msg}"
return 1
fi
if [ "${hvm_show_status}" == true ]; then
>&2 printf "Hugo version management is enabled in this directory.\\n"
>&2 printf "Run 'hvm status' for details, or 'hvm disable' to disable.\\n\\n"
fi
else
hugo_bin=$(which hugo)
fi
Expand All @@ -75,12 +81,14 @@ hugo() {
</details>

<details>
<summary>Linux: add this function to $HOME/.bashrc</summary>
<summary>Linux</summary>
Add this function to $HOME/.bashrc

```bash
# Hugo Version Manager: override path to the hugo executable.
hugo() {
hvm_common_msg="Run 'hvm use' to fix or 'hvm disable' to disable version management."
hvm_show_status=true
if [ -f ".hvm" ]; then
hugo_bin=$(cat ".hvm" 2> /dev/null)
if ! echo "${hugo_bin}" | grep -q "hugo$"; then
Expand All @@ -93,6 +101,10 @@ hugo() {
>&2 printf "%s\\n" "${hvm_common_msg}"
return 1
fi
if [ "${hvm_show_status}" == true ]; then
>&2 printf "Hugo version management is enabled in this directory.\\n"
>&2 printf "Run 'hvm status' for details, or 'hvm disable' to disable.\\n\\n"
fi
else
hugo_bin=$(which hugo)
fi
Expand All @@ -103,11 +115,9 @@ hugo() {
</details>

<details>
<summary>Windows: place this batch file in your PATH</summary>
<summary>Windows</summary>

```bash
TBD
```

</details>

Expand Down
File renamed without changes
File renamed without changes.
6 changes: 3 additions & 3 deletions cmd/clean.go → cmd/hvm/clean.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,14 @@ func clean() error {
}

if strings.ToLower(string(r[0])) == "y" {
d, err := os.ReadDir(cacheDir)
d, err := os.ReadDir(App.CacheDirPath)
if err != nil {
return err
}

for _, f := range d {
if f.Name() != defaultDirName {
err := os.RemoveAll(filepath.Join(cacheDir, f.Name()))
if f.Name() != App.DefaultDirName {
err := os.RemoveAll(filepath.Join(App.CacheDirPath, f.Name()))
if err != nil {
return err
}
Expand Down
78 changes: 49 additions & 29 deletions cmd/root.go → cmd/hvm/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,44 +22,59 @@ import (
"os"
"path/filepath"
"runtime"
"strings"

"github.com/jmooring/hvm/helpers"
"github.com/jmooring/hvm/pkg/helpers"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

type config struct {
GithubToken string `toml:"githubToken,omitempty" comment:"GitHub personal access token"`
NumTagsToDisplay int `toml:"numTagsToDisplay" comment:"Number of tags to display with the \"use\" and \"install\" commands"`
// An application contains details about the application. Some are constants, while
// others depend on the user environment.
type application struct {
CacheDirPath string // path to the application cache directory
ConfigFilePath string // path to the application configuration
DefaultDirName string // name of the "default" directory within the application cache directory
DefaultDirPath string // path to the "default" directory within the application cache directory
DotFileName string // name of the dot file written to the current directory (e.g., .hvm)
Name string // name of the application
RepositoryName string // name of the GitHub repository without the .git extension
RepositoryOwner string // account owner of the GitHub repository
}

var Config config
// A configuration contains the current configuration parameters from environment
// variables, the configuration file, or default values, in that order.
type configuration struct {
GithubToken string // a GitHub personal access token
NumTagsToDisplay int // number of tags to display when using the "use" and "install" commands
}

var App application = application{
DefaultDirName: "default",
DotFileName: ".hvm",
Name: "hvm",
RepositoryName: "hugo",
RepositoryOwner: "gohugoio",
}
var Config configuration

var (
cacheDir string
buildDate string
buildDate string = runtime.GOOS
commitHash string
version string = "dev"
versionString string = fmt.Sprintf("%s %s %s/%s %s %s", appName, version, runtime.GOOS, runtime.GOARCH, commitHash, buildDate)
)

const (
appName string = "hvm"
dotFileName string = ".hvm"
defaultDirName string = "default"
versionString string = fmt.Sprintf("%s %s %s/%s %s %s", App.Name, version, runtime.GOOS, runtime.GOARCH, commitHash, buildDate)
)

// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: appName,
Use: App.Name,
Short: "Hugo Version Manager",
Long: `Hugo Version Manager (` + appName + `) allows you to download and manage multiple versions
Long: `Hugo Version Manager (` + App.Name + `) allows you to download and manage multiple versions
of the extended edition of the Hugo static site generator. You can use hvm to
specify which version of Hugo to use in the current directory.

If you do not specify a version of Hugo to use in the current directory, the
Hugo executable will be found by searching the PATH environment variable.
`,
Hugo executable will be found by searching the PATH environment variable.`,
Version: versionString,
}

Expand All @@ -73,10 +88,10 @@ func Execute() {
}

func init() {
cobra.OnInitialize(initConfig, initCache)
cobra.OnInitialize(initConfig, initApp)

rootCmd.PersistentFlags().BoolP("help", "h", false, "Display help")
rootCmd.PersistentFlags().BoolP("version", "v", false, "Display the "+appName+" version")
rootCmd.PersistentFlags().BoolP("version", "v", false, "Display the "+App.Name+" version")
rootCmd.SetVersionTemplate(fmt.Sprintf("%s\n", versionString))
}

Expand All @@ -89,11 +104,11 @@ func initConfig() {
// Create config directory.
userConfigDir, err := os.UserConfigDir()
cobra.CheckErr(err)
err = os.MkdirAll(filepath.Join(userConfigDir, appName), 0777)
err = os.MkdirAll(filepath.Join(userConfigDir, App.Name), 0777)
cobra.CheckErr(err)

// Define config file.
viper.AddConfigPath(filepath.Join(userConfigDir, appName))
viper.AddConfigPath(filepath.Join(userConfigDir, App.Name))
viper.SetConfigName("config")
viper.SetConfigType("toml")

Expand All @@ -110,18 +125,18 @@ func initConfig() {
}

// Get config values from env vars.
viper.SetEnvPrefix("HVM")
viper.SetEnvPrefix(strings.ToUpper(App.Name))
viper.AutomaticEnv()

// Validate config values.
k := "numTagsToDisplay"
if viper.GetInt(k) == 0 {
err = fmt.Errorf("configuration: %s must be a non-zero integer: see %s", k, viper.ConfigFileUsed())
err = fmt.Errorf("configuration: %s must be a non-zero integer: see %s", k, App.ConfigFilePath)
cobra.CheckErr(err)
}
k = "githubToken"
if !helpers.IsString(viper.Get(k)) {
err = fmt.Errorf("configuration: %s must be a string: see %s", k, viper.ConfigFileUsed())
err = fmt.Errorf("configuration: %s must be a string: see %s", k, App.ConfigFilePath)
cobra.CheckErr(err)
}

Expand All @@ -130,11 +145,16 @@ func initConfig() {
cobra.CheckErr(err)
}

// initCache creates the cache directory.
func initCache() {
// initApp initializes the application and creates the application cache
// directory.
func initApp() {
userCacheDir, err := os.UserCacheDir()
cobra.CheckErr(err)
cacheDir = filepath.Join(userCacheDir, appName)
err = os.MkdirAll(cacheDir, 0777)

App.CacheDirPath = filepath.Join(userCacheDir, App.Name)
App.ConfigFilePath = viper.ConfigFileUsed()
App.DefaultDirPath = filepath.Join(userCacheDir, App.Name, App.DefaultDirName)

err = os.MkdirAll(App.CacheDirPath, 0777)
cobra.CheckErr(err)
}
File renamed without changes.
8 changes: 2 additions & 6 deletions cmd/config.go → cmd/hvm/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import (

"github.com/pelletier/go-toml/v2"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

// configCmd represents the config command
Expand All @@ -43,12 +42,9 @@ func init() {
func displayConfig() error {
t, err := toml.Marshal(Config)
cobra.CheckErr(err)
fmt.Println(string(t))

f := viper.ConfigFileUsed()
if f != "" {
fmt.Println("Configuration file:", f)
}
fmt.Println(string(t))
fmt.Println("Configuration file:", App.ConfigFilePath)

return nil
}
6 changes: 3 additions & 3 deletions cmd/disable.go → cmd/hvm/disable.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (
"os"
"path/filepath"

"github.com/jmooring/hvm/helpers"
"github.com/jmooring/hvm/pkg/helpers"
"github.com/spf13/cobra"
)

Expand All @@ -46,13 +46,13 @@ func disable() error {
return err
}

exists, err := helpers.Exists(filepath.Join(wd, dotFileName))
exists, err := helpers.Exists(filepath.Join(wd, App.DotFileName))
if err != nil {
return err
}

if exists {
err := os.Remove(filepath.Join(wd, dotFileName))
err := os.Remove(filepath.Join(wd, App.DotFileName))
if err != nil {
return err
}
Expand Down
31 changes: 12 additions & 19 deletions cmd/install.go → cmd/hvm/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,10 @@ import (
"log"
"os"
"path/filepath"
"runtime"
"slices"

"github.com/jmooring/hvm/archive"
"github.com/jmooring/hvm/helpers"
"github.com/jmooring/hvm/pkg/archive"
"github.com/jmooring/hvm/pkg/helpers"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -53,30 +52,25 @@ func init() {
// install sets the version of the Hugo executable to use when version
// management is disabled in the current directory.
func install() error {
repo := newRepository("gohugoio", "hugo")
asset := newAsset(runtime.GOOS, runtime.GOARCH)

err := asset.fetchTags(repo)
if err != nil {
return err
}
repo := newRepository()
asset := newAsset()

msg := "Select a version to use when version management is disabled"
err = asset.selectTag(repo, msg)
err := repo.selectTag(asset, msg)
if err != nil {
return err
}
if asset.tag == "" {
return nil // the user did not select a tag; do nothing
}

exists, err := helpers.Exists(filepath.Join(cacheDir, asset.tag))
exists, err := helpers.Exists(filepath.Join(App.CacheDirPath, asset.tag))
if err != nil {
return err
}

if !exists {
err = asset.fetchURL(repo)
err = repo.fetchURL(asset)
if err != nil {
return err
}
Expand All @@ -102,24 +96,23 @@ func install() error {
return err
}

err = helpers.CopyDirectoryContent(asset.archiveDirPath, filepath.Join(cacheDir, asset.tag))
err = helpers.CopyDirectoryContent(asset.archiveDirPath, filepath.Join(App.CacheDirPath, asset.tag))
if err != nil {
return err
}
}

err = helpers.CopyFile(asset.getExecPath(), filepath.Join(cacheDir, defaultDirName, asset.getExecName()))
err = helpers.CopyFile(asset.getExecPath(), filepath.Join(App.CacheDirPath, App.DefaultDirName, asset.execName))
if err != nil {
return err
}

fmt.Println("Installation of", asset.tag, "complete.")

defaultDirPath := filepath.Join(cacheDir, defaultDirName)
if !slices.Contains(filepath.SplitList(os.Getenv("PATH")), defaultDirPath) {
if !slices.Contains(filepath.SplitList(os.Getenv("PATH")), App.DefaultDirPath) {
fmt.Println()
fmt.Printf("Please add %s to the PATH environment\n", defaultDirPath)
fmt.Println("variable. Open a new terminal after making the change.")
fmt.Printf("Please add %s to the PATH environment variable.\n", App.DefaultDirPath)
fmt.Println("Open a new terminal after making the change.")

}

Expand Down
Loading
Loading