Skip to content

Instantly share code, notes, and snippets.

@jjszaniszlo
Created July 6, 2025 03:03
Show Gist options
  • Select an option

  • Save jjszaniszlo/16eef0efc2b640864c9e368405021476 to your computer and use it in GitHub Desktop.

Select an option

Save jjszaniszlo/16eef0efc2b640864c9e368405021476 to your computer and use it in GitHub Desktop.
Here is a quick lockfile diff thing in Go.
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