diff --git a/dist/urbm-2026.06.15.r010-x86_64-1.txz.sha256 b/dist/urbm-2026.06.15.r010-x86_64-1.txz.sha256 deleted file mode 100644 index f03a0a2..0000000 --- a/dist/urbm-2026.06.15.r010-x86_64-1.txz.sha256 +++ /dev/null @@ -1 +0,0 @@ -7d5a1c417b66e7c318ccc182b61b48fc725dd561e829e34ff593d88434fa334a urbm-2026.06.15.r010-x86_64-1.txz diff --git a/dist/urbm-2026.06.15.r010-x86_64-1.txz b/dist/urbm-2026.06.15.r011-x86_64-1.txz similarity index 55% rename from dist/urbm-2026.06.15.r010-x86_64-1.txz rename to dist/urbm-2026.06.15.r011-x86_64-1.txz index be8f6cf..06358d3 100644 Binary files a/dist/urbm-2026.06.15.r010-x86_64-1.txz and b/dist/urbm-2026.06.15.r011-x86_64-1.txz differ diff --git a/dist/urbm-2026.06.15.r011-x86_64-1.txz.sha256 b/dist/urbm-2026.06.15.r011-x86_64-1.txz.sha256 new file mode 100644 index 0000000..50c9ad0 --- /dev/null +++ b/dist/urbm-2026.06.15.r011-x86_64-1.txz.sha256 @@ -0,0 +1 @@ +84579aa8c702a442494daa340631dc71ef485fa05bbbfdd2980a68611e226bb4 urbm-2026.06.15.r011-x86_64-1.txz diff --git a/dist/urbm.plg b/dist/urbm.plg index 05a5a4b..306fa48 100644 --- a/dist/urbm.plg +++ b/dist/urbm.plg @@ -2,13 +2,17 @@ - + - + ]> +### 2026.06.15.r011 +- Parse Restic repository statistics correctly when progress output precedes the JSON result. +- Support compact and pretty-printed multi-line statistics JSON. + ### 2026.06.15.r010 - Retry repository dashboard statistics once after safely removing stale Restic locks. - Show the affected repository name and concrete Restic error when statistics still cannot be loaded. diff --git a/internal/restic/restic.go b/internal/restic/restic.go index 2704047..ecd9b0d 100644 --- a/internal/restic/restic.go +++ b/internal/restic/restic.go @@ -2,6 +2,7 @@ package restic import ( "bufio" + "bytes" "context" "encoding/json" "errors" @@ -217,13 +218,27 @@ func (r *Runner) stats(ctx context.Context, repo model.Repository, args []string if err := r.run(ctx, repo, args, nil, &output); err != nil { return Stats{}, err } - var stats Stats - if err := json.Unmarshal(output, &stats); err != nil { + stats, err := decodeStats(output) + if err != nil { return Stats{}, fmt.Errorf("decode repository stats: %w", err) } return stats, nil } +func decodeStats(output []byte) (Stats, error) { + for index := len(output) - 1; index >= 0; index-- { + if output[index] != '{' { + continue + } + var stats Stats + decoder := json.NewDecoder(bytes.NewReader(output[index:])) + if err := decoder.Decode(&stats); err == nil { + return stats, nil + } + } + return Stats{}, errors.New("no valid statistics object in Restic output") +} + func (r *Runner) List(ctx context.Context, repo model.Repository, snapshot, path string) ([]map[string]any, error) { args := []string{"ls", snapshot, "--json"} if path != "" { diff --git a/internal/restic/restic_test.go b/internal/restic/restic_test.go index 97e118f..a2fa6b0 100644 --- a/internal/restic/restic_test.go +++ b/internal/restic/restic_test.go @@ -130,6 +130,31 @@ func TestStatsUsesRequestedRepositoryCountingMode(t *testing.T) { } } +func TestStatsIgnoresProgressBeforePrettyPrintedJSON(t *testing.T) { + dir := t.TempDir() + script := filepath.Join(dir, "restic") + body := `#!/bin/sh +printf '%s\n' '[0:00] 100.00% 12 / 12 index files loaded' +printf '%s\n' '{' +printf '%s\n' ' "total_size": 8192,' +printf '%s\n' ' "total_file_count": 24,' +printf '%s\n' ' "total_blob_count": 16' +printf '%s\n' '}' +` + if err := os.WriteFile(script, []byte(body), 0700); err != nil { + t.Fatal(err) + } + runner := &Runner{Binary: script, RuntimeDir: dir, Secrets: fakeSecrets{"password": "secret"}} + repo := model.Repository{Type: model.RepositoryLocal, Location: "/repo", PasswordRef: "password"} + stats, err := runner.Stats(context.Background(), repo, "raw-data") + if err != nil { + t.Fatal(err) + } + if stats.TotalSize != 8192 || stats.TotalFileCount != 24 || stats.TotalBlobCount != 16 { + t.Fatalf("unexpected stats: %+v", stats) + } +} + func TestStatsUnlocksStaleRepositoryLockAndRetries(t *testing.T) { dir := t.TempDir() logPath := filepath.Join(dir, "commands") diff --git a/plugin/urbm.plg b/plugin/urbm.plg index e2381c6..1faaf2d 100644 --- a/plugin/urbm.plg +++ b/plugin/urbm.plg @@ -2,13 +2,17 @@ - + ]> +### 2026.06.15.r011 +- Parse Restic repository statistics correctly when progress output precedes the JSON result. +- Support compact and pretty-printed multi-line statistics JSON. + ### 2026.06.15.r010 - Retry repository dashboard statistics once after safely removing stale Restic locks. - Show the affected repository name and concrete Restic error when statistics still cannot be loaded. diff --git a/webgui/URBM.page b/webgui/URBM.page index 3e354e4..1281902 100644 --- a/webgui/URBM.page +++ b/webgui/URBM.page @@ -9,12 +9,12 @@ Tag="URBM Unraid Restic Backup Manager backup snapshots restore" - +
- -

URBM 2026.06.15.r010

Restic Backup Manager für Unraid

+ +

URBM 2026.06.15.r011

Restic Backup Manager für Unraid

Verbindung wird hergestellt...
@@ -32,4 +32,4 @@ $pluginRoot = '/plugins/urbm';
- +