Created
July 6, 2025 03:03
-
-
Save jjszaniszlo/16eef0efc2b640864c9e368405021476 to your computer and use it in GitHub Desktop.
Here is a quick lockfile diff thing in Go.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package main | |
| import ( | |
| "crypto/sha256" | |
| "fmt" | |
| "io" | |
| "io/fs" | |
| "os" | |
| "path/filepath" | |
| "github.com/pelletier/go-toml/v2" | |
| ) | |
| type File struct { | |
| Path string `toml:"path"` | |
| Hash string `toml:"hash"` | |
| } | |
| type LockFile struct { | |
| HashFormat string `toml:"hash_format"` | |
| Files []File `toml:"files"` | |
| } | |
| type ChangeType int | |
| const ( | |
| Added ChangeType = iota | |
| Modified | |
| Deleted | |
| ) | |
| func (c ChangeType) String() string { | |
| switch c { | |
| case Added: | |
| return "Added" | |
| case Modified: | |
| return "Modified" | |
| case Deleted: | |
| return "Deleted" | |
| default: | |
| return "Unknown" | |
| } | |
| } | |
| type Diff struct { | |
| File string | |
| ChangeType ChangeType | |
| } | |
| func getLockFilesChanged(old []byte, new []byte) []Diff { | |
| var old_lock_file LockFile | |
| if err := toml.Unmarshal(old, &old_lock_file); err != nil { | |
| panic(fmt.Sprintf("failed to unmarshal old lock file: %v", err)) | |
| } | |
| var new_lock_file LockFile | |
| if err := toml.Unmarshal(new, &new_lock_file); err != nil { | |
| panic(fmt.Sprintf("failed to unmarshal new lock file: %v", err)) | |
| } | |
| old_files := make(map[string]string) | |
| new_files := make(map[string]string) | |
| for _, file := range old_lock_file.Files { | |
| old_files[file.Path] = file.Hash | |
| } | |
| for _, file := range new_lock_file.Files { | |
| new_files[file.Path] = file.Hash | |
| } | |
| // check for modified or added files and append them to diffs | |
| var diffs []Diff | |
| for path, new_hash := range new_files { | |
| if old_hash, exists := old_files[path]; exists { | |
| if old_hash != new_hash { | |
| diffs = append(diffs, Diff{File: path, ChangeType: Modified}) | |
| } | |
| } else { | |
| diffs = append(diffs, Diff{File: path, ChangeType: Added}) | |
| } | |
| } | |
| // check for deleted files and append them to diffs | |
| for path := range old_files { | |
| if _, exists := new_files[path]; !exists { | |
| diffs = append(diffs, Diff{File: path, ChangeType: Deleted}) | |
| } | |
| } | |
| return diffs | |
| } | |
| func updateLockFile(dir string) []Diff { | |
| // get a recursive list of all file paths in the directory | |
| var file_paths []string | |
| err := filepath.WalkDir(dir, func(path string, entry fs.DirEntry, err error) error { | |
| if err != nil { | |
| return err | |
| } | |
| if entry.IsDir() { | |
| return nil | |
| } | |
| file_paths = append(file_paths, path) | |
| return nil | |
| }) | |
| if err != nil { | |
| panic(err) | |
| } | |
| lock_file := LockFile{ | |
| HashFormat: "sha256", | |
| Files: make([]File, 0, len(file_paths)-1), | |
| } | |
| // for each path generate a checksum and append it to the lock file struct | |
| for _, path := range file_paths { | |
| // ignore the lock file itself | |
| if filepath.Base(path) == "lock.toml" { | |
| continue | |
| } | |
| f, err := os.Open(path) | |
| if err != nil { | |
| panic(err) | |
| } | |
| defer f.Close() | |
| hash := sha256.New() | |
| if _, err := io.Copy(hash, f); err != nil { | |
| panic(err) | |
| } | |
| file_entry := File{ | |
| Path: path, | |
| Hash: fmt.Sprintf("%x", hash.Sum(nil)), | |
| } | |
| lock_file.Files = append(lock_file.Files, file_entry) | |
| } | |
| lock_file_data, err := toml.Marshal(lock_file) | |
| if err != nil { | |
| panic(err) | |
| } | |
| lockfile_path := filepath.Join(dir, "lock.toml") | |
| // if the lock file exists, compare the old vs new | |
| diffs := make([]Diff, 0) | |
| if _, err := os.Stat(lockfile_path); !os.IsNotExist(err) { | |
| old_lock_file_data, err := os.ReadFile(lockfile_path) | |
| if err != nil { | |
| panic(err) | |
| } | |
| diffs = append(diffs, getLockFilesChanged(old_lock_file_data, lock_file_data)...) | |
| } | |
| if err := os.WriteFile(lockfile_path, lock_file_data, 0644); err != nil { | |
| panic(err) | |
| } | |
| return diffs | |
| } | |
| func main() { | |
| dir := os.Args[1] | |
| diffs := updateLockFile(dir) | |
| // pretty print diffs | |
| for _, diff := range diffs { | |
| fmt.Printf("%s: %s\n", diff.ChangeType, diff.File) | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment