This commit is contained in:
grngxd 2025-06-18 19:19:39 +01:00
commit 8d15fa9731
9 changed files with 287 additions and 0 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
mix.lock
*.mix.hjson
*.exe

11
README.md Normal file
View file

@ -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)

115
cmd/sync.go Normal file
View file

@ -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)
}
}

8
go.mod Normal file
View file

@ -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
)

4
go.sum Normal file
View file

@ -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=

17
main.go Normal file
View file

@ -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)
}

21
mix.hjson.example Normal file
View file

@ -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
'''
}
]
}
]
}

1
mix.lock.example Normal file
View file

@ -0,0 +1 @@
[]

107
mix/files.go Normal file
View file

@ -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"`
}