package utilities import ( "errors" "fmt" "html/template" "io/fs" "os" "path/filepath" "slices" "strings" "codeflow.dananglin.me.uk/linux-home/manager/magefiles/internal/config" "codeflow.dananglin.me.uk/linux-home/manager/magefiles/internal/templatefuncs" ) const dirModePerm fs.FileMode = 0o700 const TemplateExtension string = ".gotmpl" func EnsureDirectory(path string) error { info, err := os.Stat(path) if err != nil { if errors.Is(err, os.ErrNotExist) { if err := os.Mkdir(path, dirModePerm); err != nil { return fmt.Errorf("unable to create the directory: %w", err) } return nil } return fmt.Errorf( "received an unexpected error after attempting to get the directory information: %w", err, ) } if !info.IsDir() { return errors.New("the path exists but it is not a directory") } if info.Mode().Perm() != dirModePerm { if err := os.Chmod(path, dirModePerm); err != nil { return fmt.Errorf("unable to update the directory's mode to %d: %w", dirModePerm, err) } } return nil } func EnsureSymlink(source, dest string) error { absolutePathErrorMessageFormat := "unable to get the absolute path to %s: %w" absoluteSourcePath, err := filepath.Abs(source) if err != nil { return fmt.Errorf(absolutePathErrorMessageFormat, source, err) } absoluteDestPath, err := filepath.Abs(dest) if err != nil { return fmt.Errorf(absolutePathErrorMessageFormat, dest, err) } destInfo, err := os.Lstat(absoluteDestPath) if err != nil { if errors.Is(err, os.ErrNotExist) { fmt.Printf("Linking %s to %s\n", absoluteDestPath, absoluteSourcePath) if err := os.Symlink(absoluteSourcePath, absoluteDestPath); err != nil { return fmt.Errorf( "unable to symlink %s to %s: %w", absoluteDestPath, absoluteSourcePath, err, ) } return nil } return fmt.Errorf("unable to get the file info for %s: %w", absoluteDestPath, err) } if destInfo.Mode().Type() != fs.ModeSymlink { return fmt.Errorf("the path %s exists but it is not a symlink", absoluteDestPath) } destLinksTo, err := filepath.EvalSymlinks(absoluteDestPath) if err != nil { return fmt.Errorf("unable to evaluate the symlink %s: %w", absoluteDestPath, err) } if destLinksTo == absoluteSourcePath { return nil } fmt.Printf( "%s should link back to %s but instead links back to %s\n", absoluteDestPath, absoluteSourcePath, destLinksTo, ) fmt.Println("Recreating:", absoluteDestPath) if err := os.Remove(absoluteDestPath); err != nil { return fmt.Errorf("unable to remove %s: %w", absoluteDestPath, err) } if err := os.Symlink(absoluteSourcePath, absoluteDestPath); err != nil { return fmt.Errorf( "unable to symlink %s to %s: %w", absoluteDestPath, absoluteSourcePath, err, ) } return nil } func ManagedConfigSet(applicationConfigurationList []string) map[string]struct{} { set := make(map[string]struct{}) for _, app := range slices.All(applicationConfigurationList) { set[app] = struct{}{} } return set } func RenderTemplate(cfg config.Config, templatePath, managedPath string) error { if !strings.HasSuffix(templatePath, TemplateExtension) { return fmt.Errorf( "the template %s does not have the %q file extension", templatePath, TemplateExtension, ) } name := filepath.Base(templatePath) tmpl, err := template.New(name).Funcs(templatefuncs.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, cfg); err != nil { return fmt.Errorf("unable to render the template to %s: %w", managedPath, err) } return nil }