commit 8d15fa9731d75311b6ecf7b87c31c50ce7229570 Author: grngxd <36968271+grngxd@users.noreply.github.com> Date: Wed Jun 18 19:19:39 2025 +0100 initial diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4156987 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +mix.lock +*.mix.hjson +*.exe \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..25f4f56 --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +# 🍜 mix +> ` a declerative file-based package manager for windows.` + + +## features +- uses winget 😭 + +## requirements +- windows 10+ ofc +- recent version of [Go](https://golang.org/) +- [winget](https://github.com/microsoft/winget-cli) \ No newline at end of file diff --git a/cmd/sync.go b/cmd/sync.go new file mode 100644 index 0000000..92d8092 --- /dev/null +++ b/cmd/sync.go @@ -0,0 +1,115 @@ +package cmd + +import ( + "context" + "fmt" + "os/exec" + + "git.iwakura.rip/grng/mix/mix" + "github.com/urfave/cli/v3" +) + +var SyncCommand = &cli.Command{ + Name: "sync", + Action: func(context.Context, *cli.Command) error { + config := mix.Config + if len(config) == 0 { + return cli.Exit("No configuration files found", 1) + } + + if mix.Lock == nil { + return cli.Exit("No lockfile found", 1) + } + + var cfg mix.MixConfig + for _, c := range config { + cfg = c + break + } + + desired := make(map[string]mix.MixPackage) + for _, group := range cfg { + for _, pkg := range group.Packages { + desired[pkg.Id] = pkg + } + } + + var newLock mix.MixLock + for _, lockPkg := range mix.Lock { + if _, ok := desired[lockPkg.Id]; !ok { + rmPackage(lockPkg.Id) + } else { + newLock = append(newLock, lockPkg) + } + } + mix.Lock = newLock + + for _, pkg := range desired { + found := false + for _, lockPkg := range mix.Lock { + if lockPkg.Id == pkg.Id { + found = true + break + } + } + if !found { + err := syncPackage(pkg) + if err != nil { + return fmt.Errorf("failed to add package %s: %w", pkg.Id, err) + } + } + } + + err := mix.SaveLockFile() + if err != nil { + return fmt.Errorf("failed to save lock file: %w", err) + } + + return nil + }, +} + +func syncPackage(pkg mix.MixPackage) error { + isInstalled := false + for _, lockPkg := range mix.Lock { + if lockPkg.Id == pkg.Id { + isInstalled = true + break + } + } + + if isInstalled { + return nil + } + + fmt.Printf("Installing package %s...\n", pkg.Id) + cmd := exec.Command("winget", "install", "-h", "-e", "--id", pkg.Id, "-v", pkg.Version) + cmd.Stdout = nil + cmd.Stderr = nil + err := cmd.Run() + + if err != nil { + return fmt.Errorf("failed to install package %s: %w", pkg.Id, err) + } + + fmt.Printf("Package %s installed successfully!\n", pkg.Id) + mix.Lock = append(mix.Lock, mix.MixLockPackage{ + Id: pkg.Id, + Version: pkg.Version, + }) + + return nil +} + +func rmPackage(id string) { + fmt.Printf("Removing package %s...\n", id) + cmd := exec.Command("winget", "uninstall", "-h", "-e", "--id", id) + cmd.Stdout = nil + cmd.Stderr = nil + err := cmd.Run() + if err != nil { + fmt.Printf("Failed to uninstall package %s: %v\n", id, err) + } else { + fmt.Printf("Package %s removed successfully!\n", id) + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..84fc726 --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module git.iwakura.rip/grng/mix + +go 1.23.4 + +require ( + github.com/hjson/hjson-go/v4 v4.5.0 // indirect + github.com/urfave/cli/v3 v3.3.8 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..b6b4d75 --- /dev/null +++ b/go.sum @@ -0,0 +1,4 @@ +github.com/hjson/hjson-go/v4 v4.5.0 h1:ZHLiZ+HaGqPOtEe8T6qY8QHnoEsAeBv8wqxniQAp+CY= +github.com/hjson/hjson-go/v4 v4.5.0/go.mod h1:4zx6c7Y0vWcm8IRyVoQJUHAPJLXLvbG6X8nk1RLigSo= +github.com/urfave/cli/v3 v3.3.8 h1:BzolUExliMdet9NlJ/u4m5vHSotJ3PzEqSAZ1oPMa/E= +github.com/urfave/cli/v3 v3.3.8/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo= diff --git a/main.go b/main.go new file mode 100644 index 0000000..f862828 --- /dev/null +++ b/main.go @@ -0,0 +1,17 @@ +package main + +import ( + "context" + "os" + + "git.iwakura.rip/grng/mix/cmd" + "github.com/urfave/cli/v3" +) + +func main() { + (&cli.Command{ + Commands: []*cli.Command{ + cmd.SyncCommand, + }, + }).Run(context.Background(), os.Args) +} diff --git a/mix.hjson.example b/mix.hjson.example new file mode 100644 index 0000000..8059f54 --- /dev/null +++ b/mix.hjson.example @@ -0,0 +1,21 @@ +default: { + packages: [ + { + id: "wez.wezterm" + version: "20240203-110809-5046fc22" + config: [ + { + type: "raw" + path: "~/.wezterm.lua" + data: + ''' + local wezterm = require 'wezterm' + local config = {} + config.color_scheme = 'Batman' + return config + ''' + } + ] + } + ] +} \ No newline at end of file diff --git a/mix.lock.example b/mix.lock.example new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/mix.lock.example @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/mix/files.go b/mix/files.go new file mode 100644 index 0000000..38edbbc --- /dev/null +++ b/mix/files.go @@ -0,0 +1,107 @@ +package mix + +import ( + "os" + "strings" + + hjson "github.com/hjson/hjson-go/v4" +) + +var Config = ConfigFiles() +var Lock = LockFile() + +// *.mix.hjson +func ConfigFiles() map[string]MixConfig { + dir, err := os.ReadDir(".") + if err != nil { + panic(err) + } + + files := []string{} + + for _, file := range dir { + if file.IsDir() { + continue + } + + if strings.HasSuffix(file.Name(), ".mix.hjson") { + files = append(files, file.Name()) + } + } + + config := make(map[string]MixConfig) + + for _, file := range files { + f, err := os.ReadFile(file) + if err != nil { + panic(err) + } + + var cfg MixConfig + err = hjson.Unmarshal(f, &cfg) + if err != nil { + panic(err) + } + + if len(cfg) == 0 { + continue + } + + config[file] = cfg + } + + return config +} + +func SaveLockFile() error { + data, err := hjson.Marshal(Lock) + if err != nil { + return err + } + + err = os.WriteFile("mix.lock", data, 0644) + if err != nil { + return err + } + + return nil +} + +type MixConfig map[string]MixGroup + +type MixGroup struct { + Packages []MixPackage `json:"packages"` +} + +type MixPackage struct { + Id string `json:"id"` + Version string `json:"version"` + Config []MixPackageConfig `json:"config"` +} + +type MixPackageConfig struct { + Type string `json:"type"` + Path string `json:"path"` + Data interface{} `json:"data"` +} + +func LockFile() MixLock { + file, err := os.ReadFile("mix.lock") + if err != nil { + panic(err) + } + + var lock MixLock + err = hjson.Unmarshal(file, &lock) + if err != nil { + panic(err) + } + + return lock +} + +type MixLock []MixLockPackage +type MixLockPackage struct { + Id string `json:"id"` + Version string `json:"version"` +}