Skip to content

Commit

Permalink
feat: support XDG standards on new installs (#230)
Browse files Browse the repository at this point in the history
  • Loading branch information
hay-kot authored Oct 12, 2024
1 parent cf05d80 commit d1d2f8c
Show file tree
Hide file tree
Showing 3 changed files with 180 additions and 11 deletions.
9 changes: 9 additions & 0 deletions docs/schema/schema.scaffoldrc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@ export interface Schema {
* `prompt`.
* */
run_hooks?: "always" | "never" | "prompt";
/**
* the log level for the scaffold. The default is `warn`.
* */
log_level?: "debug" | "info" | "warn" | "error";
/**
* the log file path for the scaffold. The default is none. When provided without a `/` prefix
* the path is relative to scaffoldrc file.
* */
log_file?: string;
};
/**
* The aliases section allows you to define key/value pairs as shortcuts for a scaffold path.
Expand Down
156 changes: 156 additions & 0 deletions internal/appdirs/appdirs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
// Package appdirs provides a cross-platform way to find application directories for
// the configuration, cache, and any other application-specific directories.
//
// This package largely exists because of the legacy behavior of the scaffold CLI where
// we want to preserve user locations for configuration if it exists, but move forward
// to use the XDG Base Directory Specification for new installations.
package appdirs

import (
"os"
"path/filepath"

"github.com/rs/zerolog/log"
)

const (
RCFilename = "scaffoldrc.yml"
)

// RCFilepath returns the path to the scaffold configuration file. This is done
// by:
//
// 1. Checking if the legacy configuration file exists in the user's home directory.
// 2. If it does, return the path to the legacy configuration file.
// 3. If it does not, return the path to the XDG configuration file.
func RCFilepath() string {
legacyFilepath, exists := RCFilepathLegacy()
if exists {
return legacyFilepath
}

return RCFilepathXDG()
}

func RCFilepathLegacy() (path string, exists bool) {
fp := homeDir(".scaffold", RCFilename)
_, err := os.Stat(fp)
if err != nil {
return "", false
}

return fp, true
}

func RCFilepathXDG() string {
configDir := os.Getenv("XDG_CONFIG_HOME")
if configDir == "" {
home, err := os.UserHomeDir()
if err != nil {
log.Fatal().Err(err).Msg("failed to get home directory")
}

configDir = filepath.Join(home, ".config")
}

return filepath.Join(configDir, "scaffold", RCFilename)
}

// CacheDir returns the path to the scaffold cache directory. This is done by:
//
// 1. Checking if the XDG_DATA_HOME environment variable is set.
// 2. If it is, return the path to the cache directory.
// 3. If it is not, return the path to the cache directory in the user's home directory.
func CacheDir() string {
legacyFilepath, exists := CacheDirLegacy()
if exists {
return legacyFilepath
}

return CacheDirXDG()
}

func CacheDirLegacy() (path string, exists bool) {
dir := homeDir(".scaffold/cache")
_, err := os.Stat(dir)
if err != nil {
return "", false
}

return dir, true
}

func CacheDirXDG() string {
cacheDir := os.Getenv("XDG_DATA_HOME")
if cacheDir == "" {
home, err := os.UserHomeDir()
if err != nil {
log.Fatal().Err(err).Msg("failed to get home directory")
}

cacheDir = filepath.Join(home, ".local", "share")
}

return filepath.Join(cacheDir, "scaffold", "templates")
}

func homeDir(s ...string) string {
home, err := os.UserHomeDir()
if err != nil {
log.Fatal().Err(err).Msg("failed to get home directory")
}

return filepath.Join(append([]string{home}, s...)...)
}

// MigrateLegacyPaths migrates the legacy configuration and cache directories to the
// new XDG Base Directory Specification paths.
func MigrateLegacyPaths() error {
legacyFilepath, exists := RCFilepathLegacy()
if exists {
err := migrateRCFile(legacyFilepath)
if err != nil {
return err
}
}

legacyDirectory, exists := CacheDirLegacy()
if exists {
err := migrateCacheDir(legacyDirectory)
if err != nil {
return err
}
}

return nil
}

func migrateRCFile(legacyFilepath string) error {
xdgFilepath := RCFilepathXDG()
err := os.MkdirAll(filepath.Dir(xdgFilepath), os.ModePerm)
if err != nil {
return err
}

err = os.Rename(legacyFilepath, xdgFilepath)
if err != nil {
log.Error().Err(err).Msg("failed to migrate legacy configuration file")
return err
}

log.Info().Str("from", legacyFilepath).Str("to", xdgFilepath).Msg("migrated legacy configuration file")
return nil
}

func migrateCacheDir(legacyDir string) error {
xdgDir := CacheDirXDG()

err := os.Rename(legacyDir, xdgDir)
if err != nil {
log.Error().Err(err).Msg("failed to migrate legacy cache directory")
return err
}

log.Info().Str("from", legacyDir).Str("to", xdgDir).Msg("migrated legacy cache directory")
return nil
}
26 changes: 15 additions & 11 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/hay-kot/scaffold/app/commands"
"github.com/hay-kot/scaffold/app/core/engine"
"github.com/hay-kot/scaffold/app/scaffold/scaffoldrc"
"github.com/hay-kot/scaffold/internal/appdirs"
"github.com/hay-kot/scaffold/internal/printer"
"github.com/hay-kot/scaffold/internal/styles"
"github.com/rs/zerolog"
Expand All @@ -36,15 +37,6 @@ func build() string {
return fmt.Sprintf("%s (%s) %s", version, short, date)
}

func HomeDir(s ...string) string {
home, err := os.UserHomeDir()
if err != nil {
log.Fatal().Err(err).Msg("failed to get home directory")
}

return filepath.Join(append([]string{home}, s...)...)
}

func main() {
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).Level(zerolog.WarnLevel)

Expand All @@ -61,7 +53,7 @@ func main() {
&cli.PathFlag{
Name: "scaffoldrc",
Usage: "path to scaffoldrc file",
Value: HomeDir(".scaffold/scaffoldrc.yml"),
Value: appdirs.RCFilepath(),
EnvVars: []string{"SCAFFOLDRC"},
},
&cli.StringSliceFlag{
Expand All @@ -73,7 +65,7 @@ func main() {
&cli.PathFlag{
Name: "cache",
Usage: "path to the local scaffold directory default",
Value: HomeDir(".scaffold/cache"),
Value: appdirs.CacheDir(),
EnvVars: []string{"SCAFFOLD_CACHE"},
},
&cli.StringFlag{
Expand Down Expand Up @@ -386,6 +378,18 @@ func main() {
return nil
},
},
{
Name: "migrate",
Action: func(ctx *cli.Context) error {
err := appdirs.MigrateLegacyPaths()
if err != nil {
return err
}

log.Info().Msg("migrated legacy paths")
return nil
},
},
},
},
},
Expand Down

0 comments on commit d1d2f8c

Please sign in to comment.