Skip to content

Add native Pushover support as a webhook format#199

Merged
tis24dev merged 4 commits into
tis24dev:devfrom
paolostivanin:feat/webhook-pushover
Apr 22, 2026
Merged

Add native Pushover support as a webhook format#199
tis24dev merged 4 commits into
tis24dev:devfrom
paolostivanin:feat/webhook-pushover

Conversation

@paolostivanin
Copy link
Copy Markdown
Contributor

@paolostivanin paolostivanin commented Apr 22, 2026

Pushover requires the application token and user/group key in the JSON body (not in headers), and its API rejects proxsave's existing generic payload shape. Until now users had no way to wire Pushover up without running an external relay.

This adds pushover as a first-class WEBHOOK_*_FORMAT alongside discord/slack/teams/generic. It reuses the existing AUTH_TOKEN / AUTH_USER fields for Pushover credentials (AUTH_TYPE stays "none"), introduces an optional WEBHOOK__PRIORITY (-2..1, default 0; emergency priority 2 deferred), and produces a Pushover-shaped JSON body with rune-aware truncation to the API's 250-char title and 1024-char message limits.

Validation: missing token/user is reported at payload-build time; out-of-range priority is rejected by NewWebhookNotifier so misconfig fails fast at startup rather than per-send.

Summary by CodeRabbit

  • New Features

    • Added Pushover as a supported webhook notification format with configurable priority settings.
  • Documentation

    • Updated configuration documentation and examples to demonstrate Pushover endpoint setup alongside existing notification formats.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 22, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a2e105b7-7dce-40a9-860d-b4d54d701c81

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This change introduces Pushover webhook notification support by adding a new Priority configuration field to webhook endpoints, implementing Pushover payload construction with message truncation and credential validation, and updating documentation and templates to reflect the new format option.

Changes

Cohort / File(s) Summary
Documentation & Templates
docs/CONFIGURATION.md, docs/EXAMPLES.md, internal/config/templates/backup.env
Added Pushover as a supported webhook format option, documented its message/title truncation limits, credential handling, priority ranges, and provided example Multi-Notification Setup with Discord + Pushover endpoints.
Configuration Structure
internal/config/config.go
Added Priority int field to WebhookEndpoint struct and updated configuration reading to populate per-endpoint priority values with a default of 0.
Webhook Implementation
internal/notify/webhook.go
Enhanced NewWebhookNotifier with Pushover priority validation (-2..1 range), modified sendToEndpoint to normalize and write back the resolved format, and updated buildPayload signature to accept full endpoint configuration and dispatch to new Pushover handler.
Pushover Payload Construction
internal/notify/webhook_payloads.go
Implemented buildPushoverPayload function with validation for required auth fields, rune-safe truncation logic (title: 250 runes, message: 1024 runes) with ellipsis suffix, and priority field population.
Test Coverage
internal/notify/webhook_test.go
Added comprehensive Pushover tests covering payload generation, auth field validation, truncation behavior with rune-length limits, priority passthrough, and endpoint configuration with priority range validation in NewWebhookNotifier.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A hoppy new format hops into place,
Pushover messages send at their pace,
With truncation and priority in play,
Notifications bounce the Pushover way,
Configuration validates with care,
And webhooks deliver everywhere! 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add native Pushover support as a webhook format' is clear, concise, and accurately summarizes the main change—adding Pushover as a first-class webhook format.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
internal/notify/webhook.go (1)

187-211: ⚠️ Potential issue | 🟠 Major

Do not log Pushover payload previews containing credentials.

Pushover puts token and user in the JSON body, so the existing debug payload preview/content logs leak both credentials.

🔐 Proposed fix
 	w.logger.Debug("Payload marshaled: %d bytes", len(payloadBytes))
 	if w.logger.GetLevel() <= types.LogLevelDebug {
-		if len(payloadBytes) > 200 {
+		if strings.EqualFold(format, "pushover") {
+			w.logger.Debug("Payload preview omitted for pushover payload containing credentials")
+		} else if len(payloadBytes) > 200 {
 			w.logger.Debug("Payload preview (first 200 chars): %s...", string(payloadBytes[:200]))
 		} else {
 			w.logger.Debug("Payload content: %s", string(payloadBytes))
 		}
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/notify/webhook.go` around lines 187 - 211, The debug payload preview
currently logs sensitive Pushover credentials; modify the logging path after
json.Marshal in the webhook send flow (where endpoint.Format is set and
payloadBytes exists) to detect Pushover payloads (check endpoint.Format ==
"pushover"), unmarshal payloadBytes into a temporary map[string]interface{},
remove or redact the "token" and "user" keys (or replace their values with
"<redacted>"), re-marshal that safe map for logging, and use that redacted byte
slice for the Debug "preview"/"content" logs so the original payload sent
remains unchanged but credentials are never printed.
docs/EXAMPLES.md (1)

506-506: ⚠️ Potential issue | 🟡 Minor

Keep the example narrative consistent with Pushover.

The configuration now sends Discord + Pushover, but the scenario and expected-results bullets still describe Discord-only webhook delivery.

📝 Proposed docs update
-**Scenario**: Telegram + Email + Webhook (Discord) notifications.
+**Scenario**: Telegram + Email + Webhook (Discord + Pushover) notifications.
...
 **On successful backup**:
 - ✅ Telegram message with summary
 - ✅ Email with detailed report
 - ✅ Discord embed with stats
+- ✅ Pushover push notification
 
 **On failure**:
 - ❌ Telegram alert with error
 - ❌ Email with failure details
 - ❌ Discord mention with logs
+- ❌ Pushover push notification

Also applies to: 531-533, 550-550, 600-608

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/EXAMPLES.md` at line 506, Update the example narrative and
expected-results bullets so they consistently describe Discord + Pushover
delivery instead of Discord-only: locate the "**Scenario**: Telegram + Email +
Webhook (Discord) notifications." section and all related bullets that reference
webhook/Discord delivery and replace or expand them to mention Pushover (or
“Discord + Pushover”) where appropriate; also update any expected-result lines
that still say “Discord-only” to reflect that notifications are delivered via
both Discord webhook and Pushover, and ensure the same change is applied to the
other occurrences of the example narrative and bullets (the blocks referencing
Pushover/Discord examples).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/EXAMPLES.md`:
- Around line 544-545: Replace the real-looking Pushover credentials in the
examples with obvious placeholders: update the environment variables
WEBHOOK_PUSHOVER_AUTH_TOKEN and WEBHOOK_PUSHOVER_AUTH_USER to use template
values (e.g., YOUR_PUSHOVER_TOKEN, YOUR_PUSHOVER_USER) so they no longer
resemble real secrets and won't trigger scanners or be accidentally copied.

In `@internal/notify/webhook.go`:
- Around line 62-66: NewWebhookNotifier currently validates Pushover priority
only when ep.Format is explicitly "pushover", so if DefaultFormat (e.g.,
WEBHOOK_FORMAT) is "pushover" and WebhookEndpoint.Format is empty the bad
PRIORITY (2) slips through until sendToEndpoint; update the startup validation
in NewWebhookNotifier to resolve the effective format for each endpoint (use
DefaultFormat when ep.Format is empty) and run the same PRIORITY range check
(for ep.Priority between -2..1) against that resolved format; also add a
regression test that sets DefaultFormat to "pushover" with an endpoint having
empty Format and Priority=2 to ensure validation fails at startup.

---

Outside diff comments:
In `@docs/EXAMPLES.md`:
- Line 506: Update the example narrative and expected-results bullets so they
consistently describe Discord + Pushover delivery instead of Discord-only:
locate the "**Scenario**: Telegram + Email + Webhook (Discord) notifications."
section and all related bullets that reference webhook/Discord delivery and
replace or expand them to mention Pushover (or “Discord + Pushover”) where
appropriate; also update any expected-result lines that still say “Discord-only”
to reflect that notifications are delivered via both Discord webhook and
Pushover, and ensure the same change is applied to the other occurrences of the
example narrative and bullets (the blocks referencing Pushover/Discord
examples).

In `@internal/notify/webhook.go`:
- Around line 187-211: The debug payload preview currently logs sensitive
Pushover credentials; modify the logging path after json.Marshal in the webhook
send flow (where endpoint.Format is set and payloadBytes exists) to detect
Pushover payloads (check endpoint.Format == "pushover"), unmarshal payloadBytes
into a temporary map[string]interface{}, remove or redact the "token" and "user"
keys (or replace their values with "<redacted>"), re-marshal that safe map for
logging, and use that redacted byte slice for the Debug "preview"/"content" logs
so the original payload sent remains unchanged but credentials are never
printed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3388eaae-4268-46b3-889a-c19246db6214

📥 Commits

Reviewing files that changed from the base of the PR and between e7a98d0 and 465c65f.

📒 Files selected for processing (7)
  • docs/CONFIGURATION.md
  • docs/EXAMPLES.md
  • internal/config/config.go
  • internal/config/templates/backup.env
  • internal/notify/webhook.go
  • internal/notify/webhook_payloads.go
  • internal/notify/webhook_test.go

Comment thread docs/EXAMPLES.md Outdated
Comment thread internal/notify/webhook.go Outdated
@paolostivanin
Copy link
Copy Markdown
Contributor Author

paolostivanin commented Apr 22, 2026

Pushed be1f173 addressing 3 of the 4 points:

  1. docs/EXAMPLES.md — real-looking Pushover credentials (line 544-545): Fixed. Swapped the 30-char strings for <pushover-application-token> / <pushover-user-or-group-key> placeholders to match the rest of the file's style and stop tripping secret scanners.

  2. internal/notify/webhook.go — Pushover priority validation when WEBHOOK_FORMAT=pushover and endpoint format is empty (line 62-66): No change. BuildWebhookConfig (internal/config/config.go:1403) already resolves WEBHOOK_FORMAT into each endpoint's Format field at config-build time, so by the time NewWebhookNotifier runs, ep.Format is "pushover" in that scenario and the PRIORITY check fires. The fallback chain in sendToEndpoint (lines 173-181) is duplicated/dead code in practice.

  3. internal/notify/webhook.go — debug log leaking Pushover credentials (line 187-211): Fixed. The Pushover payload has token and user as the first two JSON fields, well within the 200-byte preview window. The debug-level preview/content log now emits a "preview omitted" placeholder when the resolved format is pushover, leaving the wire payload untouched.

  4. docs/EXAMPLES.md — narrative inconsistency around Example 7 (line 506, 600-608): Fixed. Scenario header now reads "Discord + Pushover", and the success/failure expected-results lists each gain a Pushover bullet to match the four configured channels.

@paolostivanin paolostivanin force-pushed the feat/webhook-pushover branch from be1f173 to a682da2 Compare April 22, 2026 07:59
Pushover requires the application token and user/group key in the JSON
body (not in headers), and its API rejects proxsave's existing generic
payload shape. Until now users had no way to wire Pushover up without
running an external relay.

This adds `pushover` as a first-class WEBHOOK_*_FORMAT alongside
discord/slack/teams/generic. It reuses the existing AUTH_TOKEN /
AUTH_USER fields for Pushover credentials (AUTH_TYPE stays "none"),
introduces an optional WEBHOOK_<NAME>_PRIORITY (-2..1, default 0;
emergency priority 2 deferred), and produces a Pushover-shaped JSON
body with rune-aware truncation to the API's 250-char title and
1024-char message limits.

Validation: missing token/user is reported at payload-build time;
out-of-range priority is rejected by NewWebhookNotifier so misconfig
fails fast at startup rather than per-send.
- docs/EXAMPLES.md: replace real-looking Pushover token/user values with
  placeholders so secret scanners stop flagging the example, and update
  the Example 7 scenario header and expected-results bullets so the
  narrative matches the Discord + Pushover configuration block.

- internal/notify/webhook.go: skip the debug payload preview/content
  log when the resolved format is "pushover". The Pushover payload puts
  token + user in the JSON body (not headers), so the existing preview
  would write both credentials into debug logs.
@paolostivanin paolostivanin force-pushed the feat/webhook-pushover branch from a682da2 to 1a866b2 Compare April 22, 2026 08:00
@tis24dev tis24dev changed the base branch from main to dev April 22, 2026 09:15
Fail fast for invalid Pushover webhook configuration.

- resolve effective webhook format and method centrally
- validate Pushover PRIORITY after default format resolution
- require POST for Pushover endpoints at init time
- add regression tests for default-format and invalid method cases
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 22, 2026

Codecov Report

❌ Patch coverage is 83.15789% with 16 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/notify/webhook.go 72.34% 8 Missing and 5 partials ⚠️
internal/notify/webhook_payloads.go 92.30% 2 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@tis24dev tis24dev merged commit 9f1d0ee into tis24dev:dev Apr 22, 2026
1 check passed
@tis24dev
Copy link
Copy Markdown
Owner

Thanks for your help; I made a change and added it.
I always add new code to the dev branch and merge it into the main branch when it’s ready for a new release, so everything is currently in the dev branch and will be included in the next release in the coming days.

Grazie per il tuo supporto.

@paolostivanin paolostivanin deleted the feat/webhook-pushover branch April 22, 2026 11:54
@paolostivanin
Copy link
Copy Markdown
Contributor Author

thank you very much for the super fast review and merge 😄

tis24dev added a commit that referenced this pull request May 6, 2026
)

* docs upgrade - dual collection

* docs upgrade

* Update README.md

* ci: bump the actions-updates group across 1 directory with 2 updates (#196)

Bumps the actions-updates group with 2 updates in the / directory: [codecov/codecov-action](https://github.com/codecov/codecov-action) and [dependabot/fetch-metadata](https://github.com/dependabot/fetch-metadata).


Updates `codecov/codecov-action` from 5 to 6
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](codecov/codecov-action@v5...v6)

Updates `dependabot/fetch-metadata` from 2 to 3
- [Release notes](https://github.com/dependabot/fetch-metadata/releases)
- [Commits](dependabot/fetch-metadata@v2...v3)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions-updates
- dependency-name: dependabot/fetch-metadata
  dependency-version: '3'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Add native Pushover support as a webhook format (#199)

* Add native Pushover support as a webhook format

Pushover requires the application token and user/group key in the JSON
body (not in headers), and its API rejects proxsave's existing generic
payload shape. Until now users had no way to wire Pushover up without
running an external relay.

This adds `pushover` as a first-class WEBHOOK_*_FORMAT alongside
discord/slack/teams/generic. It reuses the existing AUTH_TOKEN /
AUTH_USER fields for Pushover credentials (AUTH_TYPE stays "none"),
introduces an optional WEBHOOK_<NAME>_PRIORITY (-2..1, default 0;
emergency priority 2 deferred), and produces a Pushover-shaped JSON
body with rune-aware truncation to the API's 250-char title and
1024-char message limits.

Validation: missing token/user is reported at payload-build time;
out-of-range priority is rejected by NewWebhookNotifier so misconfig
fails fast at startup rather than per-send.

* Address CodeRabbit review on PR #199

- docs/EXAMPLES.md: replace real-looking Pushover token/user values with
  placeholders so secret scanners stop flagging the example, and update
  the Example 7 scenario header and expected-results bullets so the
  narrative matches the Discord + Pushover configuration block.

- internal/notify/webhook.go: skip the debug payload preview/content
  log when the resolved format is "pushover". The Pushover payload puts
  token + user in the JSON body (not headers), so the existing preview
  would write both credentials into debug logs.

* Validate Pushover method and default-format resolution

Fail fast for invalid Pushover webhook configuration.

- resolve effective webhook format and method centrally
- validate Pushover PRIORITY after default format resolution
- require POST for Pushover endpoints at init time
- add regression tests for default-format and invalid method cases

---------

Co-authored-by: Damiano <71268257+tis24dev@users.noreply.github.com>

* Stabilize orchestrator tests and fix TUI race

Remove t.Parallel() from orchestrator tests to avoid unsafe concurrency around package-level test dependencies such as restoreFS, restoreCmd, and restoreTime.

Fix the decrypt TUI E2E test harness by taking synchronized snapshots of the tcell simulation screen instead of reading screen contents concurrently with rendering.

Verified with:
- go test ./internal/orchestrator -count=1
- go test -race ./internal/orchestrator -count=1
- go vet ./internal/orchestrator

* Use safeexec and CommandSpec for external commands

Replace direct exec.CommandContext usages with a new internal/safeexec package and introduce a CommandSpec type for validated command invocation. Collector APIs now accept CommandSpec (with validation and stringification) and use safe execution helpers; many PBS-related collector calls and tests were updated accordingly. Archiver and other components were updated to propagate command creation errors, improving error handling and security when spawning external processes. Added internal/safeexec implementation and tests.

* Refactor rollback tests, add safeexec cases

Introduce backgroundRollbackCallKey helper and update numerous rollback tests to use it; add TestRunBackgroundRollbackTimer_UsesPositionalArgsForScriptPath. Add ValidateRemoteRelativePath unit test. Extend internal/safeexec to recognize "false" and "sysctl" commands and add a nosemgrep comment for TrustedCommandContext. Remove unused os/exec import in cmd/proxsave/upgrade.go. Add .github/instructions/codacy.instructions.md with Codacy MCP Server rules and guidelines.

* Refactor backup orchestration and validation

Split the large backup startup/orchestration code into focused cmd/proxsave modules: backup_mode, backup_execution, backup_storage and backup_notifications, and updated main.go to call runBackupMode. Centralized orchestration flow (pre-checks, storage init, notifications, RunGoBackup invocation, stats persistence and exit/status logging) and improved error handling for backup runs. Improved collector validation in internal/backup: added validateExcludePatterns, combined validation checks (ensure at least one collection option, CLI timeouts, pxar concurrency, max PVE backup size and absolute system root prefix) and normalization steps. Also added/updated orchestrator helpers and tests to support the refactor.

* Refactor backup bricks into modular files

Introduce BrickID doc and helper constructors (brick, collectorBrick, pbsCommandBrick, systemCommandBrick, pbsInventoryBrick) to standardize collection-brick creation, and split the large collector_bricks implementation into multiple focused files under internal/backup (common, pbs, pbs_features, pbs_inventory, pbs_runtime, pve, pve_finalize, pve_jobs, pve_storage, system). Remove the in-file recipe and brick definitions from collector_bricks.go and update tests (collector_bricks_test.go) to match the new layout. This reorganizes the code for better separation of concerns and easier maintenance.

* Refactor main into modular run pipeline

Split and reorganize the large monolithic cmd/proxsave/main.go into a modular run pipeline. Main now delegates to staged helper functions (startMainRun, setupRunContext, preparePreRuntimeArgs, prepareRuntime, runRuntime) and most runtime logic and helpers were moved into new files under cmd/proxsave/ (main_config_modes.go, main_defers.go, main_footer.go, main_identity.go, main_lifecycle.go, main_modes.go, main_modes_test.go, main_network.go, main_restore_decrypt.go, main_runtime.go, main_runtime_log.go, main_security.go, main_signals.go, main_state.go, main_support.go, main_update.go). This reduces imports and responsibilities in main.go, improves readability and testability, and adds a focused unit test (main_modes_test.go). No behavioral changes intended—code was relocated and organized for clearer control flow and easier maintenance.

* Split restore logic into multiple files

Refactor the large restore.go by extracting archive, extraction, decompression, service/ZFS handling and cluster-apply logic into dedicated files (restore_archive.go, restore_archive_entries.go, restore_archive_extract.go, restore_archive_paths.go, restore_cluster_apply.go, restore_decompression.go, restore_services.go, restore_zfs.go and related additions). Clean up imports and unused globals in restore.go, add ErrRestoreAborted and a doc comment for RunRestoreWorkflow, and update tests to use the new restoreArchiveOptions API for archive extraction. This improves modularity and maintainability of the restore code.

* Encode email bodies as quoted-printable

Use quoted-printable encoding for text and HTML parts to ensure 7-bit-safe emails and avoid 8bit transfer encoding. Add encodeQuotedPrintableBody helper and required imports, update Content-Transfer-Encoding headers in buildEmailMessage for both plain/text and html parts, and add a test (TestEmailNotifierBuildEmailMessage_EncodesUTF8BodiesAsSevenBitSafe) that verifies quoted-printable is used and no raw non-ASCII bytes remain in the message.

* deps(deps): bump github.com/gdamore/tcell/v2 from 2.13.8 to 2.13.9 in the security-patches group across 1 directory (#200)

deps(deps): bump github.com/gdamore/tcell/v2

Bumps the security-patches group with 1 update in the / directory: [github.com/gdamore/tcell/v2](https://github.com/gdamore/tcell).


Updates `github.com/gdamore/tcell/v2` from 2.13.8 to 2.13.9
- [Release notes](https://github.com/gdamore/tcell/releases)
- [Changelog](https://github.com/gdamore/tcell/blob/main/CHANGESv3.md)
- [Commits](gdamore/tcell@v2.13.8...v2.13.9)

---
updated-dependencies:
- dependency-name: github.com/gdamore/tcell/v2
  dependency-version: 2.13.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security-patches
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Validate and resolve hardlink targets

Reject empty or absolute hardlink Linkname values and normalize them with filepath.FromSlash. Resolve the hardlink target via resolvePathWithinRootFS and use the resolved path for creation to prevent links escaping the extraction root. Add tests: recordingLinkFS to capture Link() args, TestExtractHardlink_UsesResolvedTargetPath to ensure the resolved target is used, and TestExtractHardlink_RejectsSymlinkEscapeTarget to verify symlink-escape targets are rejected.

* Add additional orchestrator tests

Add new unit tests for the orchestrator package. Tests cover estimatedBackupSizeGB scaling and minimum, BackupError wrapping and default messages for disk validation, backup metrics exit code logic, filling backup timing fields, and building backup collector config merging runtime excludes and blacklist. Also add restore tests for extractPlainArchive honoring skip functions and for runSafeClusterApplyWithUI ensuring storage/datacenter apply is skipped when storage_pve is staged.

* Improve PVE collection, mail path & restore abort

Return the first capture error from PVE collection loops (so collection continues but callers get notified) and surface errors from schedule captures. Add tests to assert capture-error behavior for backup history, replication status, and schedule collection. Introduce lookupAbsolutePath and use it to resolve systemctl/mailq paths (and use TrustedCommandContext/commandForMailTool) to make mail/service checks more robust. Clear stale restore abort info at start of runRestoreWorkflowWithUI and add a test ensuring abort info is cleared before validation.

* Strip build metadata in version compare; doc fixes

Ignore build metadata when comparing semantic versions by trimming any '+' suffix in isNewerVersion, and add unit tests covering build-metadata cases. Also update related docs and metadata: fix Codacy instructions YAML frontmatter formatting, update the PayPal donate link in README, and rename a config key in EXAMPLES.md (BACKUP_PXAR_FILES -> PXAR_SCAN_ENABLE).

* Improve restore extraction summary and guards

Report failed files in restore summary and refine logging/guards. The restore summary now prints the number of failed files and includes them in the total archive count; summary log messages omit the "see detailed log" hint when no log path is configured. shouldSkipRestoreEntryTarget was tightened to match the /etc/pve boundary (exact match or trailing slash) to avoid false positives. Also changed a PBS user-list parse log from Debug to Warning and adjusted a couple of tests to account for whitespace-only config path handling. Added tests covering the restore summary and /etc/pve boundary behavior.

* Add FS utimes/lchown and refactor command handling

Add Lchown and UtimesNano to the FS interface and implement them in osFS and test fakes; update restore code to use restoreFS.Lchown/UtimesNano instead of direct os/syscall calls. Simplify environment runCommand to use safeexec.TrustedCommandContext and update tests to call echo via LookPath. Refactor Proxmox notification header parsing: introduce parseSectionHeader with a regexp-based validation and keep parseProxmoxNotificationHeader as a wrapper; update callers. Move panic handling in main lifecycle into a deferred function and remove an extra success print in config apply. Also include minor docs/formatting tweaks and add a test simulating execCommand failure in namespaces tests.

* Refactor command execution and runtime handling

Centralize and harden command execution: introduce runAndClassifyCommand with commandRunOptions/result and classification enums; refactor safeCmdOutput and captureCommandOutput to reuse this logic, improving logging, output summarization, unprivileged-container handling, systemctl special cases, and dry-run/lookpath handling.

Runtime and signal updates: detect unprivileged container earlier in bootstrap (rt.unprivilegedInfo), remove duplicate detection in logging, stop signal notification on exit, and make stdin-close handling safe via a scoped sync.Once. Also fix heap profiling to defer file close.

Other changes: make pveContext methods use pointer receivers and nil-checks for safety; enforce Pushover webhook auth (require Token and User) and add tests; expand safeexec tests to validate the list of allowed commands.

* Return cloud FS errors and collect mode errors

Propagate filesystem detection errors for cloud backends so callers get diagnostics (detectFilesystemInfo now returns an error for LocationCloud and backup init logs include the failure reason). Adjust backup storage init logging to include the detection reason and mark cloud disabled. Change validateModeCompatibility to accumulate and return all compatibility violations (with a new test added). Rename helper 'newer' to 'meetsMinimum' for clarity and normalize directory mode literal to 0o755. Tests updated/added to cover the new behaviors.

* Propagate context to ZFS restore checks

Pass context.Context through ZFS restore helper functions and tests so operations can be cancelled and use caller-provided contexts. checkZFSPoolsAfterRestore, detectImportableZFSPools and logNoImportableZFSPools now accept a context, check ctx.Err, and pass ctx to restoreCmd.Run. The UI caller was updated to forward the workflow context. Tests were updated to supply contexts and two new tests verify context propagation and behavior on canceled contexts. FakeCommandRunner was extended to record contexts for assertions.

* Treat decompression readers as ReadClosers

Change decompression reader APIs to return io.ReadCloser and ensure readers are closed with errors propagated. Introduce closeDecompressionReader helper to defer Close() and convert close errors into the function's error return. Update callers to use the helper and simplify test cleanup; add a test (TestExtractArchiveNativeReturnsDecompressionCloseError) that verifies decompressor Close() errors surface. Also adjust imports and minor test fixes to read directly from readers.

* Convert storage/VM config apply to pvesh flag args

Refactor how VM and storage configs are applied: storageBlock now stores Type and proxmoxNotificationEntry entries instead of raw data, and VM/storage config files are parsed into --key=value pvesh arguments rather than written to temp files and passed via --filename/-conf. Added parsing helpers (parseColonConfigLine, pveshArgsFromColonConfigFile/Lines, pveshArgsFromProxmoxEntries, storageBlockPveshArgs, storageEntryValue) and updated applyVMConfigs/applyStorageCfg to build proper pvesh calls (e.g. "pvesh create /storage --storage=... --type=... ..."). Tests were updated to reflect the new argument format and to assert that deprecated flags (--filename, -conf) are no longer used.

* Add ExitEncryptionError and classify encryption failures

Add a new ExitEncryptionError exit code and string mapping to represent encryption setup/processing failures. Update createBackupArchive to report the "encryption" phase and use ExitEncryptionError when age recipient preparation fails. Add tests: backup_run_phases_test verifies age-recipient failures are classified as encryption, and exit_codes_test updated to include the new code. Also fix RunGoBackup to return the collected stats value when prepareBackupWorkspace fails so callers receive consistent results.

* Return error from checksum write; add tests

Change writeArchiveChecksum to return an error and propagate failures: writeArchiveChecksum now returns a wrapped error on write failure (uses 0o640 file mode) and verifyAndWriteBackupArtifacts returns a BackupError when checksum writing fails. Make server identity detection injectable for testing by introducing runtimeServerIdentityDetector and only run detection when ServerID is not configured; add unit tests for initializeServerIdentity. Add unit test ensuring checksum write errors are propagated. Also add Node.js 24 setup to the Dependabot automerge workflow so Dependabot metadata step runs under Node 24.

* Propagate context cancellation in collectors

Add isContextCancellationError helper and use it across PVE collection bricks and storage/metadata steps so context cancellations are propagated (returning the ctx error) instead of being treated as regular failures. Add tests ensuring guest and storage bricks propagate cancellations. Include new standalone collection flags (BackupPBSNotificationsPriv, BackupRootHome, BackupScriptRepository, BackupUserHomes) in collectionOptionFlags and add tests validating they are accepted. Change validateCloudSettings to accept absolute cloud remote refs with a new isAbsoluteCloudRemoteRef helper and add tests for absolute refs and path validation. Remove unused setBackupResult from main_state.go and update related imports in tests.

* Add completion timeout to TUI simulation tests

Introduce simAppCompletionTimeout and group timing constants. Improve test timeout handling by reporting errors and stopping the app if the initial draw doesn't occur or the simulation doesn't finish within the completion timeout after injecting keys. Also properly stop and reset the timer to avoid races.

* Update decrypt_tui_e2e_helpers_test.go

* tui: handle Stop-before-Run and test helpers

Ensure App.Stop is safe when called before Run by adding run state tracking, a mutex and a stopRequested flag; implement Run and markRunningAndStopIfRequested to defer stopping until the event loop starts. Add synchronized test helpers (setTimedSimAppStopper, stopTimedSimAppForTest) and protect simulated apps with mutexes so tests can reliably stop current TUI instances. Increase several simulation timeouts and update runDecryptWorkflowTUIForTest to use a cancellable context and trigger a timed shutdown path. Also add a test verifying pre-run Stop terminates RunWithContext as expected.

* Refactor TUI test harness and improve task start/stop

Replace ad-hoc timed simulation plumbing with a reusable timedSimHarness for decrypt TUI e2e tests. Introduce timedSimHarness, timedSimAppState, and richer timedSimKey fields (RequireNewApp, SettleAfterMatch) plus new timing constants to make key injection and app lifecycle more deterministic and to improve diagnostics on timeouts. Update helper functions and tests to return/use the harness and tighten shutdown logic (StopAll, better timeout/error messages).

Also change RunTask startup/shutdown in workflow_ui_tui_decrypt.go to trigger the background task after the first draw (SetAfterDrawFunc), add start synchronization (started, startOnce) and queueProgressUpdate to safely schedule UI updates and ensure proper waiting for task completion on exit. Minor import adjustments (sync) included.

* Pin actions; fix update check, PBS & tests

Pin GitHub Actions steps to specific SHAs for reproducible runs. Add a nil-context guard in checkForUpdates to avoid panics when ctx is nil. Include PBS user config collection by running newPBSUserConfigRecipe(). Change lookupAbsolutePath to return an error if exec.LookPath returns a non-absolute path rather than attempting to make it absolute. Enhance TUI e2e test helpers by adding a Wait field to timedSimKey and using it to control timeouts/sleeps. Update test FakeFS to simulate ownership changes via an Ownership map and a FakeOwnership struct in Lchown instead of calling os.Lchown.

* Add PBS/restore helpers, improve panic handling & tests

Multiple fixes and feature additions across the codebase: update README PayPal link; improve finishMainRun panic handling and ensure runDone/logging are called; allow --install to choose CLI vs TUI; make isNewerVersion treat stable > prerelease and add tests; add timeout to hostname resolution. Backup collector: honor CustomBackupPaths, add pbsRepositoryWithDatastore helper and use it when building PBS_REPOSITORY, plus new tests. Email notifier: centralize mailq lookup (findMailqPath) and use it for queue checks. Orchestrator/restore: introduce localNodeName and helpers (pveshGuestExists, pveshCreateGuestArgs, pveshArgValue, isPveshNotFoundError), ensure missing guests are created before applying configs, stop parsing at section headers, and add tests for these behaviors. TUI tests: add runCompleted signaling to timedSimHarness and ensure RunDecryptWorkflowTUI signals completion. Misc: small test and brick-call refactors.

* Refactor webhook endpoint validation

Extract pushover-specific validation from NewWebhookNotifier into a new WebhookNotifier.validateEndpoint method and instantiate the notifier earlier. NewWebhookNotifier now assigns the HTTP client to the notifier and returns it, improving readability and isolating endpoint validation. Also move the nosemgrep annotation to the end of the return statement in TrustedCommandContext to satisfy linters.

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Paolo Stivanin <paolostivanin@users.noreply.github.com>
@tis24dev
Copy link
Copy Markdown
Owner

tis24dev commented May 6, 2026

@paolostivanin This update is included in the new release!

@paolostivanin
Copy link
Copy Markdown
Contributor Author

@paolostivanin This update is included in the new release!

Fantastic, thanks a lot!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants