A shepherd for your Appimages.
0

Configure Feed

Select the types of activity you want to include in your feed.

only write icons + desktop files if modified

Aly Raffauf (Jun 17, 2026, 6:21 PM EDT) 9f4fbbda 4662baef

+91 -12
+1 -5
cmd/appherder/desktop.go
··· 3 3 import ( 4 4 "errors" 5 5 "fmt" 6 - "io" 7 6 "io/fs" 8 7 "os" 9 8 "path/filepath" ··· 237 236 if d.trailingNewline { 238 237 text += "\n" 239 238 } 240 - return writeAtomic(path, 0o644, func(w io.Writer) error { 241 - _, err := w.Write([]byte(text)) 242 - return err 243 - }) 239 + return writeIfChanged(path, 0o644, []byte(text)) 244 240 } 245 241 246 242 func (a app) patchDesktopFile(desktop *desktopFile, appName string, hasIcon bool) error {
+18 -7
cmd/appherder/files.go
··· 10 10 "path/filepath" 11 11 ) 12 12 13 + // writeIfChanged writes content to path atomically, but skips when path 14 + // already holds identical bytes so the file's mtime stays stable. 15 + func writeIfChanged(path string, perm os.FileMode, content []byte) error { 16 + if existing, err := os.ReadFile(path); err == nil { 17 + if bytes.Equal(existing, content) { 18 + return nil 19 + } 20 + } else if !errors.Is(err, os.ErrNotExist) { 21 + return err 22 + } 23 + return writeAtomic(path, perm, func(w io.Writer) error { 24 + _, err := w.Write(content) 25 + return err 26 + }) 27 + } 28 + 13 29 // writeAtomic writes via a temp file beside path and renames it into place, so 14 30 // path never holds a partially written file. 15 31 func writeAtomic(path string, perm os.FileMode, write func(io.Writer) error) (err error) { ··· 81 97 } 82 98 83 99 func copyFromFS(fsys fs.FS, name string, dest string) error { 84 - in, err := fsys.Open(name) 100 + content, err := fs.ReadFile(fsys, name) 85 101 if err != nil { 86 102 return err 87 103 } 88 - defer in.Close() 89 - 90 - return writeAtomic(dest, 0o644, func(w io.Writer) error { 91 - _, err := io.Copy(w, in) 92 - return err 93 - }) 104 + return writeIfChanged(dest, 0o644, content) 94 105 }
+72
cmd/appherder/files_test.go
··· 1 + package main 2 + 3 + import ( 4 + "os" 5 + "path/filepath" 6 + "testing" 7 + "time" 8 + ) 9 + 10 + func TestWriteIfChangedSkipsIdenticalContent(t *testing.T) { 11 + dir := t.TempDir() 12 + path := filepath.Join(dir, "f") 13 + content := []byte("hello") 14 + 15 + if err := os.WriteFile(path, content, 0o644); err != nil { 16 + t.Fatal(err) 17 + } 18 + // Pin an old mtime so a skipped write is detectable. 19 + old := time.Unix(1000, 0) 20 + if err := os.Chtimes(path, old, old); err != nil { 21 + t.Fatal(err) 22 + } 23 + 24 + if err := writeIfChanged(path, 0o644, content); err != nil { 25 + t.Fatal(err) 26 + } 27 + 28 + info, err := os.Stat(path) 29 + if err != nil { 30 + t.Fatal(err) 31 + } 32 + if !info.ModTime().Equal(old) { 33 + t.Fatalf("mtime changed on identical write: got %v, want %v", info.ModTime(), old) 34 + } 35 + } 36 + 37 + func TestWriteIfChangedWritesWhenDifferent(t *testing.T) { 38 + dir := t.TempDir() 39 + path := filepath.Join(dir, "f") 40 + if err := os.WriteFile(path, []byte("old"), 0o644); err != nil { 41 + t.Fatal(err) 42 + } 43 + 44 + if err := writeIfChanged(path, 0o644, []byte("new")); err != nil { 45 + t.Fatal(err) 46 + } 47 + 48 + got, err := os.ReadFile(path) 49 + if err != nil { 50 + t.Fatal(err) 51 + } 52 + if string(got) != "new" { 53 + t.Fatalf("content = %q, want new", got) 54 + } 55 + } 56 + 57 + func TestWriteIfChangedCreatesWhenAbsent(t *testing.T) { 58 + dir := t.TempDir() 59 + path := filepath.Join(dir, "f") 60 + 61 + if err := writeIfChanged(path, 0o644, []byte("fresh")); err != nil { 62 + t.Fatal(err) 63 + } 64 + 65 + got, err := os.ReadFile(path) 66 + if err != nil { 67 + t.Fatal(err) 68 + } 69 + if string(got) != "fresh" { 70 + t.Fatalf("content = %q, want fresh", got) 71 + } 72 + }