feat: download configs from external sources

Download and install configurations from external sources. At the moment
downloading from external git repositories is only supported.

Download and install the Neovim configuration for sparrow.

Add an external script to install lazy.nvim.
This commit is contained in:
Dan Anglin 2024-09-14 11:41:58 +01:00
parent d29b6894e5
commit 59a6969c25
Signed by: dananglin
GPG key ID: 0C1D44CFBEE68638
5 changed files with 163 additions and 25 deletions

View file

@ -43,6 +43,14 @@
"Projects" "Projects"
] ]
}, },
"externalConfigurations": [
{
"label": "Neovim",
"gitRepoURL": "https://codeflow.dananglin.me.uk/linux-home/nvim.d.git",
"gitRef": "746f07efdb203a46a7a02ada987cd97b68fa92d6",
"gitRepoPath": "nvim"
}
],
"git": { "git": {
"gpgSign": false, "gpgSign": false,
"user": { "user": {

102
magefiles/external.go Normal file
View file

@ -0,0 +1,102 @@
//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 := downloadRepo(cfg.GitRepoURL, cfg.GitRef)
if err != nil {
return fmt.Errorf("unable to clone the git repository: %w", err)
}
defer os.RemoveAll(clonedRepo)
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 downloadRepo(repoURL, repoRef string) (string, error) {
cloneDir, err := os.MkdirTemp("/tmp", "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
}

View file

@ -8,52 +8,60 @@ import (
"strings" "strings"
) )
const configDir string = "hosts" const dir string = "hosts"
type Config struct { type Config struct {
ManagedConfigurations []string `json:"managedConfigurations"` ManagedConfigurations []string `json:"managedConfigurations"`
BashProfile ConfigBashProfile `json:"bashProfile"` BashProfile BashProfile `json:"bashProfile"`
Directories ConfigDirectories `json:"directories"` Directories Directories `json:"directories"`
Git ConfigGit `json:"git"` Git Git `json:"git"`
ExternalConfigurations []ExternalConfig `json:"externalConfigurations"`
} }
type ConfigDirectories struct { type Directories struct {
UseDefaultDirectories bool `json:"useDefaultDirectories"` UseDefaultDirectories bool `json:"useDefaultDirectories"`
IncludeXDGDirectories bool `json:"includeXDGDirectories"` IncludeXDGDirectories bool `json:"includeXDGDirectories"`
AdditionalDirectories []string `json:"additionalDirectories"` AdditionalDirectories []string `json:"additionalDirectories"`
} }
type ConfigGit struct { type Git struct {
GpgSign bool `json:"gpgSign"` GpgSign bool `json:"gpgSign"`
User ConfigGitUser `json:"user"` User GitUser `json:"user"`
} }
type ConfigGitUser struct { type GitUser struct {
Email string `json:"email"` Email string `json:"email"`
Name string `json:"name"` Name string `json:"name"`
SigningKey string `json:"signingKey"` SigningKey string `json:"signingKey"`
} }
type ConfigBashProfile struct { type BashProfile struct {
Manage bool `json:"manage"` Manage bool `json:"manage"`
Filename string `json:"filename"` Filename string `json:"filename"`
SessionPaths []ConfigBashProfileSessionPath `json:"sessionPaths"` SessionPaths []BashProfileSessionPath `json:"sessionPaths"`
XdgDirectories map[string]string `json:"xdgDirectories"` XdgDirectories map[string]string `json:"xdgDirectories"`
EnvironmentVariables map[string]string `json:"environmentVariables"` EnvironmentVariables map[string]string `json:"environmentVariables"`
Aliases map[string]string `json:"aliases"` Aliases map[string]string `json:"aliases"`
Commands []ConfigBashProfileCommand `json:"commands"` Commands []BashProfileCommand `json:"commands"`
} }
type ConfigBashProfileSessionPath struct { type BashProfileSessionPath struct {
Path string `json:"path"` Path string `json:"path"`
Description string `json:"description"` Description string `json:"description"`
} }
type ConfigBashProfileCommand struct { type BashProfileCommand struct {
Command string `json:"command"` Command string `json:"command"`
Description string `json:"description"` 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) { func NewConfig() (Config, error) {
cfg := defaultConfig() cfg := defaultConfig()
@ -89,24 +97,25 @@ func configFilePath() (string, error) {
identifier := hostnameParts[1] identifier := hostnameParts[1]
return filepath.Join(configDir, identifier+".json"), nil return filepath.Join(dir, identifier+".json"), nil
} }
func defaultConfig() Config { func defaultConfig() Config {
return Config{ return Config{
Directories: ConfigDirectories{ Directories: Directories{
UseDefaultDirectories: true, UseDefaultDirectories: true,
IncludeXDGDirectories: true, IncludeXDGDirectories: true,
AdditionalDirectories: []string{}, AdditionalDirectories: []string{},
}, },
Git: ConfigGit{ Git: Git{
GpgSign: false, GpgSign: false,
User: ConfigGitUser{ User: GitUser{
Email: "", Email: "",
Name: "", Name: "",
SigningKey: "", SigningKey: "",
}, },
}, },
ManagedConfigurations: []string{}, ManagedConfigurations: []string{},
ExternalConfigurations: []ExternalConfig{},
} }
} }

19
scripts/install_lazy.nvim Executable file
View file

@ -0,0 +1,19 @@
#!/usr/bin/env bash
set -o errexit
set -o nounset
set -o pipefail
LAZY_NVIM_SOURCE="https://github.com/folke/lazy.nvim.git"
LAZY_NVIM_DESTINATION="${XDG_DATA_HOME}/nvim/lazy/lazy.nvim"
LAZY_STATE_DIR="${XDG_STATE_HOME}/nvim/lazy"
mkdir -p "${LAZY_STATE_DIR}"
if [ -d "${LAZY_NVIM_DESTINATION}/.git" ]; then
echo "INFO: ${LAZY_NVIM_DESTINATION} is already present"
exit 0
fi
echo "INFO: Cloning ${LAZY_NVIM_SOURCE} to ${LAZY_NVIM_DESTINATION}..."
git clone --filter=blob:none --single-branch "${LAZY_NVIM_SOURCE}" "${LAZY_NVIM_DESTINATION}"