manager/magefiles/templates.go
Dan Anglin 2402833b1a
feat: a new home manager
This commit updates the scope of this project to manage the files and
directories within my home directory. The Makefile and helper bash
scripts are now replaced with mage targets so that the home directory is
now managed with Mage. The state of the home directory is managed using
a JSON configuration for each machine host. The manager is a set of mage
targets to manage various aspects of the home directory. At the moment
the manager can:

- ensure specified directories are present within the home directory.
- ensure application configuration files are up-to-date and have the
  correct symlinks within the user's home configuration directory.
- manages the user's bash profile (a.k.a bashrc) file.

Other notable changes:

- The X11 xinitrc is removed because it is not currently used and won't
  be used for the forseeable future as we slowly move to Wayland.
- All bashrc configurations are now defined in one file and is now fully
  managed by the manager.
- The dunst configuration is currently removed but will make a comeback.
- The ansible configuration is removed as it is no longer used.
- The logrotate configuration is updated and now generated from a
  template.
- Added configuration for the foot terminal.
- Added configuration for the River window manager.
2024-09-12 16:35:06 +01:00

130 lines
3.3 KiB
Go

//go:build mage
package main
import (
"fmt"
"io/fs"
"os"
"path/filepath"
"slices"
"strings"
"text/template"
)
const templateExtension string = ".gotmpl"
// Templates generates the configuration files in the managed directory from the templates and
// ensures that they the generated files are symlinked correctly to the files in the user's home
// configuration directory.
func Templates() error {
homeConfigDirectory, err := os.UserConfigDir()
if err != nil {
return fmt.Errorf("unable to get the user's home configuration directory: %w", err)
}
config, err := newConfig()
if err != nil {
return fmt.Errorf("unable to load the configuration: %w", err)
}
managedConfig := managedConfigSet(config.ManagedConfigurations)
if err = filepath.WalkDir(rootTemplateDir, manageTemplatesFunc(homeConfigDirectory, config, managedConfig)); err != nil {
return fmt.Errorf("received an error while processing the templates: %w", err)
}
return nil
}
func manageTemplatesFunc(homeConfigDirectory string, config config, managedConfig map[string]struct{}) fs.WalkDirFunc {
funcMap := template.FuncMap{
"env": env,
}
return func(templatePath string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if templatePath == rootTemplateDir {
return nil
}
relativePath := strings.TrimPrefix(templatePath, rootTemplateDir+"/")
appConfigName := strings.SplitN(relativePath, "/", 2)[0]
if _, exists := managedConfig[appConfigName]; !exists {
return nil
}
if d.IsDir() {
managedDir := filepath.Join(rootManagedDir, relativePath)
configDir := filepath.Join(homeConfigDirectory, relativePath)
dirs := []string{managedDir, configDir}
for _, dir := range slices.All(dirs) {
if err := ensureDirectory(dir); err != nil {
return fmt.Errorf("unable to ensure the existence of the directory %q: %w", dir, err)
}
}
return nil
}
if !strings.HasSuffix(templatePath, templateExtension) {
return fmt.Errorf(
"the template %s does not have the %q file extension",
templatePath,
templateExtension,
)
}
managedPath := filepath.Join(rootManagedDir, strings.TrimSuffix(relativePath, templateExtension))
configPath := filepath.Join(homeConfigDirectory, strings.TrimSuffix(relativePath, templateExtension))
fmt.Println("Processing template:", relativePath)
if err := renderTemplate(config, templatePath, managedPath, funcMap); err != nil {
return fmt.Errorf(
"unable to generate %s from template %s: %w",
managedPath,
templatePath,
err,
)
}
if err := ensureSymlink(managedPath, configPath); err != nil {
return err
}
return nil
}
}
func renderTemplate(config config, templatePath, managedPath string, funcMap template.FuncMap) error {
name := filepath.Base(templatePath)
tmpl, err := template.New(name).Funcs(funcMap).ParseFiles(templatePath)
if err != nil {
return fmt.Errorf("unable to create a new template value from %s: %w", templatePath, err)
}
output, err := os.Create(managedPath)
if err != nil {
return fmt.Errorf("unable to create %s: %w", managedPath, err)
}
defer output.Close()
if err := tmpl.Execute(output, config); err != nil {
return fmt.Errorf("unable to render the template to %s: %w", managedPath, err)
}
return nil
}
func env(value string) string {
return os.Getenv(value)
}