Skip to content

Commit a2ada36

Browse files
authored
feat(updater): report pack version in summaries (#69)
1 parent 06dd0d4 commit a2ada36

14 files changed

Lines changed: 688 additions & 66 deletions

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ gtnh-daily-updater update-all main-client alt-server
242242
## State, Paths, and Merge Behavior
243243

244244
- Local state is stored at `<instance-dir>/.gtnh-daily-updater.json`
245+
- That state records `display_version`, the pack version as shown in game, so the next run can report what you upgraded from; instances updated before it existed fall back to the config version once
245246
- On Prism/MultiMC layouts, game files are resolved under `<instance-dir>/.minecraft/`
246247
- On server/other layouts, game files are resolved directly under `<instance-dir>/`
247248
- Config files are tracked in a git repo at `<game-dir>/.gtnh-configs/` on a `local` branch; pack updates are applied via `git merge -X theirs` (pack wins on conflicts)

cmd/status.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ var statusCmd = &cobra.Command{
2222
instanceDir = *p.InstanceDir
2323
}
2424
}
25-
return updater.Status(context.Background(), instanceDir, getGithubToken())
25+
return updater.Status(context.Background(), instanceDir, getGithubToken(), getCurseForgeKey())
2626
},
2727
}
2828

cmd/summary_test.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package cmd
2+
3+
import "testing"
4+
5+
func TestVersionTransition(t *testing.T) {
6+
tests := []struct {
7+
name string
8+
old string
9+
new string
10+
want string
11+
}{
12+
{
13+
name: "version moved",
14+
old: "2.9.x (Daily 647) - 2026-07-27",
15+
new: "2.9.x (Daily 648) - 2026-07-28",
16+
want: "2.9.x (Daily 647) - 2026-07-27 → 2.9.x (Daily 648) - 2026-07-28",
17+
},
18+
{
19+
name: "version held still",
20+
old: "2.9.x (Daily 653+) - 2026-07-30",
21+
new: "2.9.x (Daily 653+) - 2026-07-30",
22+
want: "2.9.x (Daily 653+) - 2026-07-30 (pack version unchanged)",
23+
},
24+
{
25+
// Instances updated before display versions were tracked report the
26+
// config tag on the left; the two sides differ, so it still reads
27+
// as a transition.
28+
name: "migration from a config tag",
29+
old: "2.9.0-nightly-2026-07-29-02",
30+
new: "2.9.x (Daily 653) - 2026-07-30",
31+
want: "2.9.0-nightly-2026-07-29-02 → 2.9.x (Daily 653) - 2026-07-30",
32+
},
33+
}
34+
35+
for _, tc := range tests {
36+
t.Run(tc.name, func(t *testing.T) {
37+
if got := versionTransition(tc.old, tc.new); got != tc.want {
38+
t.Errorf("versionTransition(%q, %q) = %q, want %q", tc.old, tc.new, got, tc.want)
39+
}
40+
})
41+
}
42+
}
43+
44+
func TestVersionCell(t *testing.T) {
45+
tests := []struct {
46+
name string
47+
old string
48+
new string
49+
want string
50+
}{
51+
{
52+
name: "version moved",
53+
old: "2.9.x (Daily 647) - 2026-07-27",
54+
new: "2.9.x (Daily 648) - 2026-07-28",
55+
want: "2.9.x (Daily 647) - 2026-07-27 → 2.9.x (Daily 648) - 2026-07-28",
56+
},
57+
{
58+
name: "version held still carries no note",
59+
old: "2.9.x (Daily 653+) - 2026-07-30",
60+
new: "2.9.x (Daily 653+) - 2026-07-30",
61+
want: "2.9.x (Daily 653+) - 2026-07-30",
62+
},
63+
}
64+
65+
for _, tc := range tests {
66+
t.Run(tc.name, func(t *testing.T) {
67+
if got := versionCell(tc.old, tc.new); got != tc.want {
68+
t.Errorf("versionCell(%q, %q) = %q, want %q", tc.old, tc.new, got, tc.want)
69+
}
70+
})
71+
}
72+
}

cmd/update.go

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,12 @@ var updateCmd = &cobra.Command{
5050
return nil
5151
}
5252

53-
logging.Infof("\nUpdate complete: %s → %s\n", result.OldVersion, result.NewVersion)
53+
logging.Infof("\nUpdate complete: %s\n", versionTransition(result.OldVersion, result.NewVersion))
5454
logging.Infof(" Mods: %d added, %d removed, %d updated, %d unchanged\n",
5555
result.Added, result.Removed, result.Updated, result.Unchanged)
5656

5757
if result.ConfigUpdated {
58-
logging.Infoln(" Pack configs: updated to new version")
58+
logging.Infof(" Pack configs: %s → %s\n", result.OldConfigVersion, result.NewConfigVersion)
5959
}
6060

6161
if len(result.StampedFiles) > 0 {
@@ -81,6 +81,24 @@ func init() {
8181
rootCmd.AddCommand(updateCmd)
8282
}
8383

84+
// versionTransition renders the headline form: an arrow when the pack version
85+
// moved, otherwise the current version with a note.
86+
func versionTransition(old, new string) string {
87+
if old == new {
88+
return new + " (pack version unchanged)"
89+
}
90+
return old + " → " + new
91+
}
92+
93+
// versionCell renders the table form: an arrow when the pack version moved,
94+
// otherwise just the version. No prose — it sits in a column.
95+
func versionCell(old, new string) string {
96+
if old == new {
97+
return new
98+
}
99+
return old + " → " + new
100+
}
101+
84102
func joinSkipped(s []string) string {
85103
if len(s) == 0 {
86104
return ""

cmd/update_all.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,11 +136,11 @@ var updateAllCmd = &cobra.Command{
136136
continue
137137
}
138138

139-
logging.Infof("\nUpdate complete: %s → %s\n", res.OldVersion, res.NewVersion)
139+
logging.Infof("\nUpdate complete: %s\n", versionTransition(res.OldVersion, res.NewVersion))
140140
logging.Infof(" Mods: %d added, %d removed, %d updated, %d unchanged\n",
141141
res.Added, res.Removed, res.Updated, res.Unchanged)
142142
if res.ConfigUpdated {
143-
logging.Infoln(" Pack configs: updated to new version")
143+
logging.Infof(" Pack configs: %s → %s\n", res.OldConfigVersion, res.NewConfigVersion)
144144
}
145145
if len(res.StampedFiles) > 0 {
146146
logging.Infof(" Version stamped into %d file(s)\n", len(res.StampedFiles))
@@ -158,8 +158,8 @@ var updateAllCmd = &cobra.Command{
158158
continue
159159
}
160160
modsChanged := r.result.Added + r.result.Removed + r.result.Updated
161-
logging.Infof(" %-20s OK %s → %s %d updated\n",
162-
r.name, r.result.OldVersion, r.result.NewVersion, modsChanged)
161+
logging.Infof(" %-20s OK %s %d updated\n",
162+
r.name, versionCell(r.result.OldVersion, r.result.NewVersion), modsChanged)
163163
}
164164

165165
return firstErr

internal/config/state.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,17 @@ import (
1111
const StateFile = ".gtnh-daily-updater.json"
1212

1313
type LocalState struct {
14-
Side string `json:"side"`
15-
Mode string `json:"mode,omitempty"`
16-
ManifestDate string `json:"manifest_date"`
17-
ConfigVersion string `json:"config_version"`
18-
Mods map[string]InstalledMod `json:"mods"`
19-
ExcludeMods []string `json:"exclude_mods,omitempty"`
20-
ExtraMods map[string]ExtraModSpec `json:"extra_mods,omitempty"`
14+
Side string `json:"side"`
15+
Mode string `json:"mode,omitempty"`
16+
ManifestDate string `json:"manifest_date"`
17+
ConfigVersion string `json:"config_version"`
18+
// DisplayVersion is the pack version as shown in game, e.g.
19+
// "2.9.x (Daily 648) - 2026-07-28". Empty on state files written before
20+
// this was tracked.
21+
DisplayVersion string `json:"display_version,omitempty"`
22+
Mods map[string]InstalledMod `json:"mods"`
23+
ExcludeMods []string `json:"exclude_mods,omitempty"`
24+
ExtraMods map[string]ExtraModSpec `json:"extra_mods,omitempty"`
2125
}
2226

2327
type ExtraModSpec struct {

internal/config/state_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,49 @@ func TestGameDir(t *testing.T) {
9898
}
9999
})
100100
}
101+
102+
func TestSaveAndLoadDisplayVersion(t *testing.T) {
103+
t.Parallel()
104+
105+
tmp := t.TempDir()
106+
s := &LocalState{
107+
Side: "client",
108+
ManifestDate: "2026-07-28",
109+
ConfigVersion: "2.9.0-nightly-2026-07-28",
110+
DisplayVersion: "2.9.x (Daily 648) - 2026-07-28",
111+
}
112+
if err := s.Save(tmp); err != nil {
113+
t.Fatalf("Save failed: %v", err)
114+
}
115+
116+
loaded, err := Load(tmp)
117+
if err != nil {
118+
t.Fatalf("Load failed: %v", err)
119+
}
120+
if loaded.DisplayVersion != "2.9.x (Daily 648) - 2026-07-28" {
121+
t.Errorf("DisplayVersion = %q, want %q", loaded.DisplayVersion, "2.9.x (Daily 648) - 2026-07-28")
122+
}
123+
}
124+
125+
func TestLoadStateWithoutDisplayVersion(t *testing.T) {
126+
t.Parallel()
127+
128+
tmp := t.TempDir()
129+
older := `{
130+
"side": "client",
131+
"manifest_date": "2026-07-27",
132+
"config_version": "2.9.0-nightly-2026-07-27",
133+
"mods": {}
134+
}`
135+
if err := os.WriteFile(filepath.Join(tmp, StateFile), []byte(older), 0o644); err != nil {
136+
t.Fatal(err)
137+
}
138+
139+
loaded, err := Load(tmp)
140+
if err != nil {
141+
t.Fatalf("Load failed: %v", err)
142+
}
143+
if loaded.DisplayVersion != "" {
144+
t.Errorf("DisplayVersion = %q, want empty for a pre-feature state file", loaded.DisplayVersion)
145+
}
146+
}

0 commit comments

Comments
 (0)