From a8398512fd9afaed6a2ec950392b53a037932418 Mon Sep 17 00:00:00 2001 From: Dan Anglin Date: Sat, 14 Sep 2024 09:10:46 +0100 Subject: [PATCH] checkpoint: implemented managing external configs --- hosts/sparrow.json | 8 +++ magefiles/external.go | 100 ++++++++++++++++++++++++++++ magefiles/internal/config/config.go | 59 +++++++++------- 3 files changed, 142 insertions(+), 25 deletions(-) create mode 100644 magefiles/external.go diff --git a/hosts/sparrow.json b/hosts/sparrow.json index e7d7e99..809fe4d 100644 --- a/hosts/sparrow.json +++ b/hosts/sparrow.json @@ -43,6 +43,14 @@ "Projects" ] }, + "externalConfigurations": [ + { + "label": "Neovim", + "gitRepoURL": "https://codeflow.dananglin.me.uk/linux-home/nvim.d.git", + "gitRef": "746f07efdb203a46a7a02ada987cd97b68fa92d6", + "gitRepoPath": "nvim" + } + ], "git": { "gpgSign": false, "user": { diff --git a/magefiles/external.go b/magefiles/external.go new file mode 100644 index 0000000..d501be4 --- /dev/null +++ b/magefiles/external.go @@ -0,0 +1,100 @@ +//go:build mage + +package main + +import ( + "fmt" + "os" + "path/filepath" + "slices" + "strings" + + "codeflow.dananglin.me.uk/linux-home/manager/magefiles/internal/config" + "codeflow.dananglin.me.uk/linux-home/manager/magefiles/internal/walk" + "github.com/magefile/mage/sh" +) + +// Externalconfigs downloads and manages neovim configuration from a remote git repository. +func Externalconfigs() error { + cfg, err := config.NewConfig() + if err != nil { + return fmt.Errorf( + "unable to load the configuration: %w", + err, + ) + } + + homeConfigDir, err := os.UserConfigDir() + if err != nil { + return fmt.Errorf( + "unable to get the user's home configuration directory: %w", + err, + ) + } + + for _, externalConfig := range slices.All(cfg.ExternalConfigurations) { + if err := manageExternalConfig(externalConfig, homeConfigDir); err != nil { + return fmt.Errorf("received an error while processing %s: %w", externalConfig.Label, err) + } + } + + return nil +} + +func manageExternalConfig(cfg config.ExternalConfig, homeConfigDir string) error { + clonedRepo, err := cloneNvimConfigRepo(cfg.GitRepoURL, cfg.GitRef) + if err != nil { + return fmt.Errorf("unable to clone the git repository: %w", err) + } + + fmt.Println("Git repository cloned to:", clonedRepo) + + validationFunc := func(relativePath string) bool { + split := strings.SplitN(relativePath, "/", 2) + + if len(split) < 1 { + return false + } + + rootPath := split[0] + + return rootPath == cfg.GitRepoPath + } + + if err = filepath.WalkDir( + clonedRepo, + walk.CopyFiles(homeConfigDir, clonedRepo, rootManagedDir, validationFunc), + ); err != nil { + return fmt.Errorf("received an error while copying the files: %w", err) + } + + return nil +} + +func cloneNvimConfigRepo(repoURL, repoRef string) (string, error) { + cloneDir, err := os.MkdirTemp("/tmp", "neovim-config-") + if err != nil { + return "", fmt.Errorf("unable to create the temporary directory: %w", err) + } + + git := sh.RunCmd("git", "-C", cloneDir) + + commands := [][]string{ + {"init"}, + {"remote", "add", "origin", repoURL}, + {"fetch", "origin", repoRef}, + {"checkout", "FETCH_HEAD"}, + } + + for _, command := range slices.All(commands) { + if err := git(command...); err != nil { + return "", err + } + } + + if err := os.RemoveAll(filepath.Join(cloneDir, ".git")); err != nil { + return "", fmt.Errorf("unable to remove the .git folder: %w", err) + } + + return cloneDir, nil +} diff --git a/magefiles/internal/config/config.go b/magefiles/internal/config/config.go index d9bb9cb..ad35a27 100644 --- a/magefiles/internal/config/config.go +++ b/magefiles/internal/config/config.go @@ -8,52 +8,60 @@ import ( "strings" ) -const configDir string = "hosts" +const dir string = "hosts" type Config struct { - ManagedConfigurations []string `json:"managedConfigurations"` - BashProfile ConfigBashProfile `json:"bashProfile"` - Directories ConfigDirectories `json:"directories"` - Git ConfigGit `json:"git"` + ManagedConfigurations []string `json:"managedConfigurations"` + BashProfile BashProfile `json:"bashProfile"` + Directories Directories `json:"directories"` + Git Git `json:"git"` + ExternalConfigurations []ExternalConfig `json:"externalConfigurations"` } -type ConfigDirectories struct { +type Directories struct { UseDefaultDirectories bool `json:"useDefaultDirectories"` IncludeXDGDirectories bool `json:"includeXDGDirectories"` AdditionalDirectories []string `json:"additionalDirectories"` } -type ConfigGit struct { - GpgSign bool `json:"gpgSign"` - User ConfigGitUser `json:"user"` +type Git struct { + GpgSign bool `json:"gpgSign"` + User GitUser `json:"user"` } -type ConfigGitUser struct { +type GitUser struct { Email string `json:"email"` Name string `json:"name"` SigningKey string `json:"signingKey"` } -type ConfigBashProfile struct { - Manage bool `json:"manage"` - Filename string `json:"filename"` - SessionPaths []ConfigBashProfileSessionPath `json:"sessionPaths"` - XdgDirectories map[string]string `json:"xdgDirectories"` - EnvironmentVariables map[string]string `json:"environmentVariables"` - Aliases map[string]string `json:"aliases"` - Commands []ConfigBashProfileCommand `json:"commands"` +type BashProfile struct { + Manage bool `json:"manage"` + Filename string `json:"filename"` + SessionPaths []BashProfileSessionPath `json:"sessionPaths"` + XdgDirectories map[string]string `json:"xdgDirectories"` + EnvironmentVariables map[string]string `json:"environmentVariables"` + Aliases map[string]string `json:"aliases"` + Commands []BashProfileCommand `json:"commands"` } -type ConfigBashProfileSessionPath struct { +type BashProfileSessionPath struct { Path string `json:"path"` Description string `json:"description"` } -type ConfigBashProfileCommand struct { +type BashProfileCommand struct { Command string `json:"command"` Description string `json:"description"` } +type ExternalConfig struct { + Label string `json:"label"` + GitRepoURL string `json:"gitRepoURL"` + GitRef string `json:"gitRef"` + GitRepoPath string `json:"gitRepoPath"` +} + func NewConfig() (Config, error) { cfg := defaultConfig() @@ -89,24 +97,25 @@ func configFilePath() (string, error) { identifier := hostnameParts[1] - return filepath.Join(configDir, identifier+".json"), nil + return filepath.Join(dir, identifier+".json"), nil } func defaultConfig() Config { return Config{ - Directories: ConfigDirectories{ + Directories: Directories{ UseDefaultDirectories: true, IncludeXDGDirectories: true, AdditionalDirectories: []string{}, }, - Git: ConfigGit{ + Git: Git{ GpgSign: false, - User: ConfigGitUser{ + User: GitUser{ Email: "", Name: "", SigningKey: "", }, }, - ManagedConfigurations: []string{}, + ManagedConfigurations: []string{}, + ExternalConfigurations: []ExternalConfig{}, } }