feat(tools/eaglemode): add configuration wrapper script
Adds a new eaglemode.withConfig function that creates a specially wrapped Eagle Mode, in which a configuration script joins the user's configuration with the config passed in. This produces a fully working and configured Eagle Mode with custom stuff out of the box. Change-Id: I6282cafd0b1ac6e77bede90cc91d4ede19ee1d2f Reviewed-on: https://cl.tvl.fyi/c/depot/+/12369 Reviewed-by: azahi <azat@bahawi.net> Tested-by: BuildkiteCI Reviewed-by: emery <emery@dmz.rs>
This commit is contained in:
parent
8c8861cb3c
commit
c4654fe373
2 changed files with 171 additions and 0 deletions
|
@ -9,6 +9,13 @@ let
|
|||
mkDesc = d: lib.concatMapStringsSep "\n"
|
||||
(x: "# Descr =${x}")
|
||||
(builtins.filter (s: s != "") (lib.splitString "\n" d));
|
||||
|
||||
configWrapper = pkgs.runCommand "eaglemode-config-wrapper" { } ''
|
||||
cp ${./wrapper.go} wrapper.go
|
||||
export HOME=$PWD
|
||||
${pkgs.go}/bin/go build wrapper.go
|
||||
install -Dm755 wrapper $out/bin/wrapper
|
||||
'';
|
||||
in
|
||||
rec {
|
||||
# mkCommand creates an Eagle Mode command for the file browser.
|
||||
|
@ -71,4 +78,12 @@ rec {
|
|||
lib.concatMapStringsSep "\n" (s: "cp -rT ${s} $out/\nchmod -R u+rw $out/\n") ([ "${eaglemode}/etc"] ++ extraPaths)
|
||||
}
|
||||
'';
|
||||
|
||||
# withConfig creates an Eagle Mode wrapper that runs it with the given
|
||||
# configuration.
|
||||
withConfig = { eaglemode ? pkgs.eaglemode, config }: pkgs.writeShellScriptBin "eaglemode" ''
|
||||
set -ue
|
||||
${configWrapper}/bin/wrapper --em-config "${config}"
|
||||
exec ${eaglemode}/bin/eaglemode "$@"
|
||||
'';
|
||||
}
|
||||
|
|
156
tools/eaglemode/wrapper.go
Normal file
156
tools/eaglemode/wrapper.go
Normal file
|
@ -0,0 +1,156 @@
|
|||
// Eagle Mode configuration wrapper that recreates the required directory
|
||||
// structure for Eagle Mode based on the output of depot.tools.eaglemode.etcDir
|
||||
//
|
||||
// This will replace *all* symlinks in the Eagle Mode configuration directory,
|
||||
// but it will not touch actual files. Missing folders will be created.
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"log"
|
||||
"os"
|
||||
"os/user"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func configDir() (string, error) {
|
||||
v := os.Getenv("EM_USER_CONFIG_DIR")
|
||||
if v != "" {
|
||||
return v, nil
|
||||
}
|
||||
|
||||
usr, err := user.Current()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get current user: %w", err)
|
||||
}
|
||||
|
||||
return path.Join(usr.HomeDir, ".eaglemode"), nil
|
||||
}
|
||||
|
||||
// cleanupConfig removes *all* existing symlinks in the configuration which do
|
||||
// not point into the right Nix store path.
|
||||
func cleanupConfig(conf string, dir string) (map[string]bool, error) {
|
||||
// In case of first launch, we might have to create the directory.
|
||||
_ = os.MkdirAll(dir, 0755)
|
||||
c := 0
|
||||
|
||||
currentFiles := map[string]bool{}
|
||||
|
||||
walker := func(p string, d fs.DirEntry, e error) error {
|
||||
if e != nil {
|
||||
return fmt.Errorf("could not walk %s in config directory: %w", p, e)
|
||||
}
|
||||
|
||||
if d.Type()&fs.ModeSymlink != 0 {
|
||||
target, err := os.Readlink(p)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not read link for %s: %w", p, err)
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(target, conf) {
|
||||
err = os.Remove(p)
|
||||
c++
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not remove stale link %q: %w", p, err)
|
||||
}
|
||||
log.Printf("removed stale symlink %q", p)
|
||||
} else {
|
||||
currentFiles[p] = false
|
||||
}
|
||||
}
|
||||
|
||||
if d.Type().IsRegular() {
|
||||
currentFiles[p] = true
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
err := filepath.WalkDir(dir, walker)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if c > 0 {
|
||||
log.Printf("removed %v stale symlinks", c)
|
||||
}
|
||||
|
||||
return currentFiles, nil
|
||||
}
|
||||
|
||||
// linkConfig traverses the given Eagle Mode configuration and links everything
|
||||
// to the expected location in the user's configuration directory.
|
||||
//
|
||||
// If the user placed actual files in the configuration directory at paths that
|
||||
// would be overwritten, they will not be touched.
|
||||
func linkConfig(conf string, dir string, existing map[string]bool) error {
|
||||
walker := func(p string, d fs.DirEntry, e error) error {
|
||||
if e != nil {
|
||||
return fmt.Errorf("could not walk %s in config directory: %w", p, e)
|
||||
}
|
||||
|
||||
target := path.Join(dir, strings.TrimPrefix(p, conf))
|
||||
|
||||
if d.Type().IsDir() {
|
||||
err := os.MkdirAll(target, 0755)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not create directory %q: %w", target, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if shadow, exists := existing[target]; exists {
|
||||
if shadow {
|
||||
log.Printf("WARN: file %q already exists and shadows a file from configuration", target)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
err := os.Symlink(p, target)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to link %q: %w", target, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return filepath.WalkDir(conf, walker)
|
||||
}
|
||||
|
||||
func main() {
|
||||
emConfig := flag.String("em-config", "", "path to em-config dir")
|
||||
|
||||
flag.Parse()
|
||||
log.Println("verifying current Eagle Mode configuration")
|
||||
|
||||
if *emConfig == "" {
|
||||
log.Fatalf("Eagle Mode configuration must be given")
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(*emConfig, "/nix/store/") {
|
||||
log.Fatalf("Eagle Mode configuration must be in Nix store")
|
||||
}
|
||||
|
||||
dir, err := configDir()
|
||||
if err != nil {
|
||||
log.Fatalf("could not determine Eagle Mode config dir: %v", err)
|
||||
}
|
||||
|
||||
currentFiles, err := cleanupConfig(*emConfig, dir)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to remove stale symlinks: %v", err)
|
||||
}
|
||||
|
||||
err = linkConfig(*emConfig, dir, currentFiles)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to link new configuration: %v", err)
|
||||
}
|
||||
|
||||
log.Println("Eagle Mode configuration updated")
|
||||
}
|
Loading…
Reference in a new issue