Skip to content
Open
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ cd dotx
go install ./cmd/dotx
```

#### Setup your shell to use dotx
#### Set up your shell to use dotx

```bash
eval "$(dotx init <shell>)"
Expand Down
2 changes: 2 additions & 0 deletions internal/cli/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,6 @@ func runDeploy(cfg *config.Config, force bool) {

logger.Info("successfully deployed", "dotfile", source.Filename())
}

cfg.Repo.ExecuteScripts(config.OnDeploy)
}
21 changes: 1 addition & 20 deletions internal/cli/init.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
package cli

import (
"path/filepath"
"slices"

"github.com/mxlang/dotx/internal/config"
"github.com/mxlang/dotx/internal/fs"
"github.com/mxlang/dotx/internal/git"
"github.com/mxlang/dotx/internal/logger"
"github.com/mxlang/dotx/internal/script"
"github.com/mxlang/dotx/internal/tui"
"github.com/spf13/cobra"
)
Expand Down Expand Up @@ -67,7 +65,7 @@ func runInit(cfg *config.Config, opts initOptions, url string) {
logger.Info("successfully cloned remote dotfiles")
}

runInitScripts(cfg)
cfg.Repo.ExecuteScripts(config.OnInit)

if opts.deploy {
logger.Debug("automatic deploy is active")
Expand Down Expand Up @@ -107,20 +105,3 @@ func shouldCloneDotfiles(dir fs.Path, url string) bool {

return false
}

func runInitScripts(cfg *config.Config) {
for _, scriptPath := range cfg.Repo.Scripts.Init {
fullPath := fs.NewPath(filepath.Join(cfg.RepoPath, scriptPath))
if !fullPath.Exists() {
logger.Warn("script does not exist", "script", fullPath.AbsPath())
continue
}

logger.Info("execute script", "script", fullPath.AbsPath())
if err := script.Run(fullPath.AbsPath()); err != nil {
logger.Warn(err)
} else {
logger.Debug("successfully executed script", "script", fullPath.AbsPath())
}
}
}
2 changes: 2 additions & 0 deletions internal/cli/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ func runPull(cfg *config.Config, opts pullOptions) {

logger.Info("successfully pulled from remote dotfiles")

cfg.Repo.ExecuteScripts(config.OnPull)

if opts.deploy {
logger.Debug("automatic deploy is active")
runDeploy(cfg, opts.force)
Expand Down
6 changes: 3 additions & 3 deletions internal/script/script.go → internal/cmd/cmd.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package script
package cmd

import (
"os"
"os/exec"
)

func Run(scriptPath string) error {
cmd := exec.Command("bash", scriptPath)
func Run(command string) error {
cmd := exec.Command(command)

cmd.Stdin = os.Stdin // pass input from terminal/user
cmd.Stdout = os.Stdout // print output to terminal
Expand Down
42 changes: 42 additions & 0 deletions internal/config/app.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,50 @@
package config

import (
"errors"
"os"

"github.com/goccy/go-yaml"
"github.com/mxlang/dotx/internal/fs"
"github.com/mxlang/dotx/internal/logger"
)

type appConfig struct {
Verbose bool `yaml:"verbose"`
CommitMessage string `yaml:"commitMessage"`
DeployOnInit bool `yaml:"deployOnInit"`
DeployOnPull bool `yaml:"deployOnPull"`
}

func defaultAppConfig() appConfig {
return appConfig{
Verbose: false,
CommitMessage: "update dotfiles",
DeployOnPull: false,
DeployOnInit: false,
}
}

func loadAppConfig() appConfig {
// Ensure the config directory exists
appDir := fs.NewPath(appDirPath())
if err := fs.Mkdir(appDir); err != nil {
logger.Error("error while creating dotx config directory", "error", err)
}

config := defaultAppConfig()
content, err := os.ReadFile(appConfigFilePath())
if err != nil {
if !errors.Is(err, os.ErrNotExist) {
logger.Error("error while reading dotx config", "error", err)
}

return config
}

if err := yaml.Unmarshal(content, &config); err != nil {
logger.Error("invalid dotx config", "error", err)
}

return config
}
70 changes: 0 additions & 70 deletions internal/config/config.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,5 @@
package config

import (
"errors"
"os"

"github.com/goccy/go-yaml"
"github.com/mxlang/dotx/internal/fs"
"github.com/mxlang/dotx/internal/logger"
)

type Config struct {
RepoPath string // TODO change type to fs.Path

Expand All @@ -27,64 +18,3 @@ func Load() *Config {
Repo: repo,
}
}

func loadAppConfig() appConfig {
// Ensure the config directory exists
appDir := fs.NewPath(appDirPath())
if err := fs.Mkdir(appDir); err != nil {
logger.Error("error while creating dotx config directory", "error", err)
}

config := defaultAppConfig()
path := appConfigFilePath()

content, err := os.ReadFile(path)
if err != nil {
if !errors.Is(err, os.ErrNotExist) {
logger.Warn("error while reading dotx config", "error", err)
}

return config
}

if err := yaml.Unmarshal(content, &config); err != nil {
logger.Warn("invalid dotx config", "error", err)
}

return config
}

func defaultAppConfig() appConfig {
return appConfig{
Verbose: false,
CommitMessage: "update dotfiles",
DeployOnPull: false,
DeployOnInit: false,
}
}

func loadRepoConfig() repoConfig {
// Ensure the dotfiles directory exists
repoDir := fs.NewPath(repoDirPath())
if err := fs.Mkdir(repoDir); err != nil {
logger.Error("error while creating dotfiles directory", "error", err)
}

config := repoConfig{}
path := repoConfigFilePath()

content, err := os.ReadFile(path)
if err != nil {
if !errors.Is(err, os.ErrNotExist) {
logger.Warn("error while reading dotfiles config", "error", err)
}

return config
}

if err := yaml.Unmarshal(content, &config); err != nil {
logger.Warn("invalid dotfiles config", "error", err)
}

return config
}
130 changes: 130 additions & 0 deletions internal/config/data.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package config

import (
"crypto/sha256"
"errors"
"fmt"
"io"
"os"
"path/filepath"

"github.com/goccy/go-yaml"
"github.com/mxlang/dotx/internal/logger"
)

type executed struct {
Path string `yaml:"path"` // TODO change type to fs.Path
Hash string `yaml:"hash"`
}

type dataConfig struct {
Scripts []executed `yaml:"scripts"`
}

func (d *dataConfig) alreadyExecuted(s script) bool {
for _, exec := range d.Scripts {
if exec.Path == s.Path {
return true
}
}

return false
}

func (d *dataConfig) hashChanged(s script) bool {
for _, exec := range d.Scripts {
if exec.Path == s.Path {
hash, err := getHash(s)
if err != nil {
// TODO
}

if exec.Hash != hash {
return true
}
}
}

return false
}

func (d *dataConfig) addScript(s script) error {
hash, err := getHash(s)
if err != nil {
return fmt.Errorf("unable to create hash: %w", err)
}

exec := executed{
Path: s.Path,
Hash: hash,
}

d.Scripts = append(d.Scripts, exec)

config, err := yaml.Marshal(d)
if err != nil {
return fmt.Errorf("unable to marshal data config: %w", err)
}

if err := os.WriteFile(dataConfigFilePath(), config, 0644); err != nil {
return fmt.Errorf("unable to write data config: %w", err)
}

return nil
}

func (d *dataConfig) updateScript(s script) error {
hash, err := getHash(s)
if err != nil {
return fmt.Errorf("unable to create hash: %w", err)
}

for i, exec := range d.Scripts {
if exec.Path == s.Path {
d.Scripts[i].Hash = hash
}
}

config, err := yaml.Marshal(d)
if err != nil {
return fmt.Errorf("unable to marshal data config: %w", err)
}

if err := os.WriteFile(dataConfigFilePath(), config, 0644); err != nil {
return fmt.Errorf("unable to write data config: %w", err)
}

return nil
}

func getHash(s script) (string, error) {
file, err := os.Open(filepath.Join(repoDirPath(), s.Path))
if err != nil {
return "", err
}
defer file.Close()

sha := sha256.New()
if _, err := io.Copy(sha, file); err != nil {
return "", err
}

return fmt.Sprintf("%x", sha.Sum(nil)), nil
}

func loadDataConfig() dataConfig {
content, err := os.ReadFile(dataConfigFilePath())
if err != nil {
if !errors.Is(err, os.ErrNotExist) {
logger.Warn("error while reading data config", "error", err)
}
}

config := dataConfig{}

if err := yaml.Unmarshal(content, &config); err != nil {
logger.Error("invalid data config", "error", err)
}

return config
}
Loading