Release URBM 2026.06.15.r011
This commit is contained in:
@@ -1 +0,0 @@
|
|||||||
7d5a1c417b66e7c318ccc182b61b48fc725dd561e829e34ff593d88434fa334a urbm-2026.06.15.r010-x86_64-1.txz
|
|
||||||
BIN
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
84579aa8c702a442494daa340631dc71ef485fa05bbbfdd2980a68611e226bb4 urbm-2026.06.15.r011-x86_64-1.txz
|
||||||
Vendored
+6
-2
@@ -2,13 +2,17 @@
|
|||||||
<!DOCTYPE PLUGIN [
|
<!DOCTYPE PLUGIN [
|
||||||
<!ENTITY name "urbm">
|
<!ENTITY name "urbm">
|
||||||
<!ENTITY author "Michael Roll">
|
<!ENTITY author "Michael Roll">
|
||||||
<!ENTITY version "2026.06.15.r010">
|
<!ENTITY version "2026.06.15.r011">
|
||||||
<!ENTITY pluginURL "https://git.casaderoll.de/michael/URBM/raw/branch/main/dist/urbm.plg">
|
<!ENTITY pluginURL "https://git.casaderoll.de/michael/URBM/raw/branch/main/dist/urbm.plg">
|
||||||
<!ENTITY packageURL "https://git.casaderoll.de/michael/URBM/raw/branch/main/dist/urbm-&version;-x86_64-1.txz">
|
<!ENTITY packageURL "https://git.casaderoll.de/michael/URBM/raw/branch/main/dist/urbm-&version;-x86_64-1.txz">
|
||||||
<!ENTITY packageSHA256 "7d5a1c417b66e7c318ccc182b61b48fc725dd561e829e34ff593d88434fa334a">
|
<!ENTITY packageSHA256 "84579aa8c702a442494daa340631dc71ef485fa05bbbfdd2980a68611e226bb4">
|
||||||
]>
|
]>
|
||||||
<PLUGIN name="&name;" author="&author;" version="&version;" pluginURL="&pluginURL;" min="7.0.0" support="https://git.casaderoll.de/michael/URBM/issues" icon="urbm.png">
|
<PLUGIN name="&name;" author="&author;" version="&version;" pluginURL="&pluginURL;" min="7.0.0" support="https://git.casaderoll.de/michael/URBM/issues" icon="urbm.png">
|
||||||
<CHANGES>
|
<CHANGES>
|
||||||
|
### 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
|
### 2026.06.15.r010
|
||||||
- Retry repository dashboard statistics once after safely removing stale Restic locks.
|
- 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.
|
- Show the affected repository name and concrete Restic error when statistics still cannot be loaded.
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package restic
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"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 {
|
if err := r.run(ctx, repo, args, nil, &output); err != nil {
|
||||||
return Stats{}, err
|
return Stats{}, err
|
||||||
}
|
}
|
||||||
var stats Stats
|
stats, err := decodeStats(output)
|
||||||
if err := json.Unmarshal(output, &stats); err != nil {
|
if err != nil {
|
||||||
return Stats{}, fmt.Errorf("decode repository stats: %w", err)
|
return Stats{}, fmt.Errorf("decode repository stats: %w", err)
|
||||||
}
|
}
|
||||||
return stats, nil
|
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) {
|
func (r *Runner) List(ctx context.Context, repo model.Repository, snapshot, path string) ([]map[string]any, error) {
|
||||||
args := []string{"ls", snapshot, "--json"}
|
args := []string{"ls", snapshot, "--json"}
|
||||||
if path != "" {
|
if path != "" {
|
||||||
|
|||||||
@@ -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) {
|
func TestStatsUnlocksStaleRepositoryLockAndRetries(t *testing.T) {
|
||||||
dir := t.TempDir()
|
dir := t.TempDir()
|
||||||
logPath := filepath.Join(dir, "commands")
|
logPath := filepath.Join(dir, "commands")
|
||||||
|
|||||||
+5
-1
@@ -2,13 +2,17 @@
|
|||||||
<!DOCTYPE PLUGIN [
|
<!DOCTYPE PLUGIN [
|
||||||
<!ENTITY name "urbm">
|
<!ENTITY name "urbm">
|
||||||
<!ENTITY author "Michael Roll">
|
<!ENTITY author "Michael Roll">
|
||||||
<!ENTITY version "2026.06.15.r010">
|
<!ENTITY version "2026.06.15.r011">
|
||||||
<!ENTITY pluginURL "https://git.casaderoll.de/michael/URBM/raw/branch/main/dist/urbm.plg">
|
<!ENTITY pluginURL "https://git.casaderoll.de/michael/URBM/raw/branch/main/dist/urbm.plg">
|
||||||
<!ENTITY packageURL "https://git.casaderoll.de/michael/URBM/raw/branch/main/dist/urbm-&version;-x86_64-1.txz">
|
<!ENTITY packageURL "https://git.casaderoll.de/michael/URBM/raw/branch/main/dist/urbm-&version;-x86_64-1.txz">
|
||||||
<!ENTITY packageSHA256 "REPLACE_DURING_RELEASE">
|
<!ENTITY packageSHA256 "REPLACE_DURING_RELEASE">
|
||||||
]>
|
]>
|
||||||
<PLUGIN name="&name;" author="&author;" version="&version;" pluginURL="&pluginURL;" min="7.0.0" support="https://git.casaderoll.de/michael/URBM/issues" icon="urbm.png">
|
<PLUGIN name="&name;" author="&author;" version="&version;" pluginURL="&pluginURL;" min="7.0.0" support="https://git.casaderoll.de/michael/URBM/issues" icon="urbm.png">
|
||||||
<CHANGES>
|
<CHANGES>
|
||||||
|
### 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
|
### 2026.06.15.r010
|
||||||
- Retry repository dashboard statistics once after safely removing stale Restic locks.
|
- 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.
|
- Show the affected repository name and concrete Restic error when statistics still cannot be loaded.
|
||||||
|
|||||||
+4
-4
@@ -9,12 +9,12 @@ Tag="URBM Unraid Restic Backup Manager backup snapshots restore"
|
|||||||
<?php
|
<?php
|
||||||
$pluginRoot = '/plugins/urbm';
|
$pluginRoot = '/plugins/urbm';
|
||||||
?>
|
?>
|
||||||
<link rel="stylesheet" href="<?= $pluginRoot ?>/assets/urbm.css?v=20260615r010">
|
<link rel="stylesheet" href="<?= $pluginRoot ?>/assets/urbm.css?v=20260615r011">
|
||||||
<div id="urbm-app" data-api="<?= $pluginRoot ?>/api.php">
|
<div id="urbm-app" data-api="<?= $pluginRoot ?>/api.php">
|
||||||
<header class="bu-header">
|
<header class="bu-header">
|
||||||
<div class="bu-brand">
|
<div class="bu-brand">
|
||||||
<img class="bu-logo" src="<?= $pluginRoot ?>/images/urbm.png?v=20260615r010" alt="URBM-Logo">
|
<img class="bu-logo" src="<?= $pluginRoot ?>/images/urbm.png?v=20260615r011" alt="URBM-Logo">
|
||||||
<div><h1>URBM <span class="bu-version">2026.06.15.r010</span></h1><p>Restic Backup Manager für Unraid</p></div>
|
<div><h1>URBM <span class="bu-version">2026.06.15.r011</span></h1><p>Restic Backup Manager für Unraid</p></div>
|
||||||
</div>
|
</div>
|
||||||
<div id="bu-health" class="bu-health" tabindex="0" data-tooltip="Zeigt, ob die URBM-Hintergrundkomponente erreichbar ist. Beispiel: 'Daemon online' bedeutet, dass Jobs gestartet werden können.">Verbindung wird hergestellt...</div>
|
<div id="bu-health" class="bu-health" tabindex="0" data-tooltip="Zeigt, ob die URBM-Hintergrundkomponente erreichbar ist. Beispiel: 'Daemon online' bedeutet, dass Jobs gestartet werden können.">Verbindung wird hergestellt...</div>
|
||||||
</header>
|
</header>
|
||||||
@@ -32,4 +32,4 @@ $pluginRoot = '/plugins/urbm';
|
|||||||
<div id="bu-tooltip" role="tooltip" aria-hidden="true"></div>
|
<div id="bu-tooltip" role="tooltip" aria-hidden="true"></div>
|
||||||
</div>
|
</div>
|
||||||
<script>window.URBM_CSRF = <?= json_encode($var['csrf_token'] ?? '') ?>;</script>
|
<script>window.URBM_CSRF = <?= json_encode($var['csrf_token'] ?? '') ?>;</script>
|
||||||
<script src="<?= $pluginRoot ?>/assets/urbm-2026.06.15.r010.js"></script>
|
<script src="<?= $pluginRoot ?>/assets/urbm-2026.06.15.r011.js"></script>
|
||||||
|
|||||||
Reference in New Issue
Block a user