Skip to content

feat: backup-restore-job-logs#60

Merged
RambokDev merged 15 commits into
mainfrom
feat/backup-restore-job-logs
Jun 7, 2026
Merged

feat: backup-restore-job-logs#60
RambokDev merged 15 commits into
mainfrom
feat/backup-restore-job-logs

Conversation

@RambokDev

@RambokDev RambokDev commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features

    • Per-job structured logging introduced for backups and restores; logs and durations are now captured and sent with status reports.
    • Command-level results (output, exit codes, timings) are recorded and surfaced in job logs and API payloads.
  • Tests

    • Integration tests updated to exercise logging-enabled backup, compression, and restore flows.
  • Chores

    • CI container workflow updated to collect and verify Rust coverage; local test recipe added for containerized tests.

@coderabbitai

coderabbitai Bot commented Jun 6, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4724f8ff-0174-47d1-9289-0b20c969ad3e

📥 Commits

Reviewing files that changed from the base of the PR and between fba72f5 and 82c7122.

📒 Files selected for processing (2)
  • docker-compose.yml
  • src/services/backup/result.rs

📝 Walkthrough

Walkthrough

Adds JobLogger and threads Arc through database backup/restore run functions, backup service execution (executor, runner, compressor, uploader, result), compression utilities, API payloads/tests; enables container-based Codecov workflow and a justfile test recipe.

Changes

Backup Job Logging and Service Integration

Layer / File(s) Summary
Job logging foundation
src/services/backup/logger.rs, src/services/backup/mod.rs
Introduces JobLogEntry and JobLogger with log, log_command, and into_entries; exports logger module.
Database trait and wiring
src/domain/factory.rs, src/domain/*/database.rs
Database trait methods now accept Arc<JobLogger>; each DB impl forwards the logger into domain backup::run and restore::run.
Backup runner implementations
src/domain/*/backup.rs
Backup runners (Firebird, MariaDB, MongoDB, MSSQL, MySQL, Postgres, Redis, SQLite, Valkey) now accept a logger, capture stdout/stderr, measure duration, and emit structured log_command/log entries.
Restore runner implementations
src/domain/*/restore.rs
Restore runners updated similarly: accept logger, capture output/exit codes, measure duration, and emit structured logs; Postgres FC/FD and other restores moved to output()-based capture.
Backup service pipeline
src/services/backup/{executor,runner,compressor,uploader,result}.rs
Executor creates/shared Arc<JobLogger> and passes it into runner/compressor/uploader; send_result updated to accept logs and duration; uploader/compressor replaced tracing with logger-based reporting.
Compression utility & tests
src/utils/compress.rs, src/tests/utils/compress_tests.rs
compress_to_tar_gz_large now accepts Arc<JobLogger> and logs operations/errors; tests updated to pass a logger.
API payloads
src/services/api/endpoints/agent/backup/mod.rs, src/services/api/endpoints/agent/restore/mod.rs
BackupUpdateRequest and ResultRestoreRequest gain logs fields; restore adds durationMs; ApiClient methods extended to accept job logs and duration.

CI / Docker / Test infra

Layer / File(s) Summary
Docker Compose & Justfile
docker-compose.yml, justfile
justfile adds test recipe to build/start agent-test and run cargo test inside; docker-compose.yml small EDGE_KEY env update and commented volume formatting adjusted.
Codecov workflow
.github/workflows/codecov.yml
Enables container-based coverage job: builds agent-test image, starts container, runs tests via docker compose exec -T after clearing target/coverage dirs, verifies profraw files exist, generates lcov with grcov (using llvm-tools-preview), enforces non-empty ./lcov.info, uploads with token under with.token, and always tears down compose with --volumes.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • Portabase/agent#55: Modifies many DB backup/restore implementations — directly related to the logger-threading changes in those same codepaths.
  • Portabase/agent#47: Related Codecov/container workflow changes that overlap with the updated .github/workflows/codecov.yml.
  • Portabase/agent#56: Earlier MSSQL implementation changes that this PR updates to accept and forward Arc<JobLogger).

Poem

"I'm a rabbit with a tiny pad of logs,
Hopping through backups, timing all the progs.
Commands and stderr, stored in memory snug,
Collected as entries — a padded little hug.
Cheers to tests, to builds, and a tidy log rug! 🥕"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 19.23% 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 'feat: backup-restore-job-logs' clearly summarizes the main change: adding job logging capabilities to backup and restore operations throughout the codebase.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/backup-restore-job-logs

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 12

🧹 Nitpick comments (6)
src/services/backup/result.rs (1)

29-30: ⚡ Quick win

Consider using debug level or a compact log format.

Pretty-printing the entire logs vector at INFO level can produce verbose multi-line output if many log entries were collected. This adds noise to production observability streams.

Suggested alternatives

Option 1: Use debug level

-        info!("{:#?}",logs);
+        tracing::debug!("Job logs: {:#?}", logs);

Option 2: Log a compact summary

-        info!("{:#?}",logs);
+        info!("Sending {} job log entries to backup API", logs.len());
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/services/backup/result.rs` around lines 29 - 30, The current
info!("{:#?}", logs) call prints a pretty-printed multi-line dump of the entire
logs vector; change it to either use debug logging (replace the info-level call
with a debug-level call referencing the same variable name `logs`) or emit a
compact summary at info level (log the count and a concise preview using
`logs.len()` and a short formatted representation such as the first N entries or
a truncated debug of each entry) so production logs are not flooded; update the
statement that references `logs` in result.rs accordingly.
src/services/backup/logger.rs (1)

134-137: ⚡ Quick win

Consider adding a non-consuming entries getter.

The consuming into_entries forces callers to use Arc::try_unwrap, which requires a fallback (see executor.rs:38,52). Adding a non-consuming method would simplify call sites and avoid silent data loss if the Arc refcount is unexpectedly > 1.

Suggested addition
+    pub fn entries(&self) -> Vec<JobLogEntry> {
+        self.entries.lock().unwrap().clone()
+    }
+
     pub fn into_entries(self) -> Vec<JobLogEntry> {
         self.entries.into_inner().unwrap()
     }

Then executor.rs could call logger.entries() instead of the try_unwrap(...).unwrap_or_else(...) pattern.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/services/backup/logger.rs` around lines 134 - 137, Add a non-consuming
getter to complement into_entries: implement a pub fn entries(&self) ->
Vec<JobLogEntry> that does not consume self but instead acquires the internal
synchronization primitive (e.g., lock the Mutex/RwLock inside the Arc), clones
or otherwise collects the Vec<JobLogEntry> contents and returns it; update call
sites (e.g., in executor.rs) to call logger.entries() instead of attempting
Arc::try_unwrap(...).unwrap_or_else(...) so callers avoid forced unwraps and
silent data loss when the Arc refcount > 1.
src/domain/mongodb/restore.rs (1)

30-35: ⚡ Quick win

Verify the behavioral change from hard-fail to fallback is intentional.

This changes the restore behavior from failing when the source database name cannot be extracted from the archive to silently falling back to the configured cfg.database. While this is more lenient and may handle edge cases gracefully, it could also mask issues such as:

  • Corrupted or malformed archive metadata
  • Bugs in the extract_db_name logic
  • Archives missing expected structure

If the fallback behavior is intentional, consider logging at warn level instead of info to make it more visible in production logs, since this represents a deviation from the expected happy path.

💡 Suggested improvement
-        let source_db = extract_db_name(&dry_output).unwrap_or_else(|| {
-            info!("Could not detect source database from archive, falling back to configured database: {}", cfg.database);
-            cfg.database.clone()
-        });
+        let source_db = extract_db_name(&dry_output).unwrap_or_else(|| {
+            warn!("Could not detect source database from archive, falling back to configured database: {}", cfg.database);
+            cfg.database.clone()
+        });

And add warn to the import on line 6:

-use tracing::{debug, error, info};
+use tracing::{debug, error, info, warn};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/domain/mongodb/restore.rs` around lines 30 - 35, The change made in
restore.rs shifts from hard-failing to silently falling back when
extract_db_name(&dry_output) returns None; confirm this behavior is intended
and, if so, make the fallback more visible by logging the fallback message with
warn! instead of info! (update the import to include warn) and keep the
subsequent info!("Using source database in archive: {}", source_db); line.
Specifically, update the unwrap_or_else closure that currently calls
info!("Could not detect source database...") to use warn! and ensure the logging
macro import (e.g., warn) is added alongside info at the top of the file.
src/tests/domain/mariadb.rs (1)

61-61: 💤 Low value

Consider a test helper for JobLogger instantiation.

The pattern std::sync::Arc::new(crate::services::backup::logger::JobLogger::new()) is verbose and repeated multiple times across test files. Consider adding a test helper function to improve readability.

♻️ Suggested test helper

Add to a common test utilities module:

#[cfg(test)]
pub fn test_logger() -> std::sync::Arc<crate::services::backup::logger::JobLogger> {
    std::sync::Arc::new(crate::services::backup::logger::JobLogger::new())
}

Then use it as:

-    let file_path = db.backup(backup_path, std::sync::Arc::new(crate::services::backup::logger::JobLogger::new())).await.unwrap();
+    let file_path = db.backup(backup_path, test_logger()).await.unwrap();

Also applies to: 65-65

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/tests/domain/mariadb.rs` at line 61, Introduce a small test helper to
avoid repeating
std::sync::Arc::new(crate::services::backup::logger::JobLogger::new()): add a
public fn test_logger() ->
std::sync::Arc<crate::services::backup::logger::JobLogger> in a shared test
utilities module (guarded with #[cfg(test)]) that returns the Arc-wrapped
JobLogger, then replace occurrences in tests (e.g., the call inside the backup
test that currently calls db.backup(...
std::sync::Arc::new(crate::services::backup::logger::JobLogger::new()) ...))
with test_logger() to improve readability and reduce duplication.
src/domain/mysql/backup.rs (1)

59-59: 💤 Low value

Consider removing unused _stdout capture.

The _stdout variable is captured but never used. If stdout output is not needed for logging or diagnostics, consider removing this line to simplify the code.

♻️ Optional cleanup
-        let _stdout = String::from_utf8_lossy(&output.stdout).to_string();
         let stderr = String::from_utf8_lossy(&output.stderr).to_string();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/domain/mysql/backup.rs` at line 59, Remove the unused stdout capture:
delete the line creating `_stdout` (the `let _stdout =
String::from_utf8_lossy(&output.stdout).to_string();`) in
src/domain/mysql/backup.rs; if you intended to keep stdout for diagnostics
instead, replace the unused variable with a direct log or use `let _ =
&output.stdout;` or log via the existing logger, otherwise simply remove the
line to eliminate dead code.
src/tests/domain/mssql.rs (1)

93-93: ⚡ Quick win

Keep the logger around and assert the new behavior.

These tests now satisfy the new signature, but they still throw the JobLogger instances away immediately, so they won't catch regressions in the feature this PR adds. Please reuse a logger variable and assert that backup/compression recorded entries.

Also applies to: 122-125

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/tests/domain/mssql.rs` at line 93, The test currently discards the
JobLogger returned into the backup call; instead create and reuse a named logger
variable (e.g., let logger =
Arc::new(crate::services::backup::logger::JobLogger::new())) and pass that to
db.backup(..., logger.clone()).await.unwrap() so the test retains the logger
instance, then assert on logger entries after calling backup and compression
(check that logger recorded backup/compression messages or entry counts). Apply
the same change to the other occurrences around the 122-125 block so both backup
and compression tests verify the logger recorded expected entries.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docker-compose.yml`:
- Line 22: The docker-compose file currently hardcodes the secret value under
the EDGE_KEY environment variable; remove the literal value from the EDGE_KEY
entry and load it from an external secret (e.g., an .env file or Docker
secrets). Specifically, replace the inline value for EDGE_KEY in the
docker-compose service with a reference to the environment variable name
EDGE_KEY (or a Docker secret) and add the real secret to a .env (ensure .env is
in .gitignore) or create a Docker secret and reference it in docker-compose;
also add a .env.example with a placeholder so contributors know the variable
name.

In `@justfile`:
- Around line 63-69: The `test` recipe in the justfile doesn't match CI: it must
set CARGO_TARGET_DIR=/app/target and run cargo clean before running tests so
coverage runs match CI; update the `test` target to export
CARGO_TARGET_DIR=/app/target (same as CI), run `cargo clean` inside the
agent-test container prior to `cargo test`, and ensure LLVM_PROFILE_FILE points
to /app/coverage/*.profraw so profraws are written to the same directory CI
collects.

In `@src/domain/mariadb/backup.rs`:
- Line 33: The local variable _mariadb_dump is mistakenly prefixed with an
underscore (suggesting it's unused) but is actually logged later; rename
_mariadb_dump to mariadb_dump where it's declared (the call to
select_mariadb_path(&version).join("mariadb-dump")) and update the subsequent
logging usage to reference mariadb_dump so the variable name matches and follows
Rust conventions.

In `@src/domain/mongodb/backup.rs`:
- Line 40: Replace the tracing::error! call used to report the MongoDB backup
failure with the injected logger.log("error", ...) call so it matches other log
usage in the function: instead of error!("MongoDB backup failed for {}: {}",
cfg.name, stderr) call logger.log("error", ...) and pass a message including
cfg.name and stderr; also remove the now-unused tracing::error import. Locate
the occurrence by the error! macro and the surrounding variables (cfg.name,
stderr) in backup.rs.

In `@src/domain/mssql/backup.rs`:
- Around line 39-52: The failure branch uses labeled "stderr: {} stdout: {}"
while the success branch builds an unlabeled combined string; make them
consistent by building a labeled combined output (e.g., "stderr: {stderr}
stdout: {stdout}") and use that for logger.log_command("sqlpackage", ...) in
both paths, keeping exit_code as exit_code on failure and Some(0) on success;
update the combined variable construction (used later in logger.log_command) to
always produce the labeled format and replace the current unlabeled
concatenation in the success path so both logger.log_command and logger.log
messages use the same output format.

In `@src/domain/postgres/backup.rs`:
- Line 94: The command label currently builds cmd_label with the full connection
URL (containing the plaintext password) and passes it to logger.log_command;
change this to use a sanitized connection string instead: parse or inspect the
URL used to construct cmd_label in this module (the code creating cmd_label and
the surrounding function in backup.rs), replace or redact the password portion
(e.g., set password to "****" or remove the userinfo) and then format cmd_label
with that sanitized URL before calling logger.log_command (leave the actual
pg_dump invocation unchanged so the password is still used for execution but
never emitted to logs).
- Around line 57-76: PostgresDumpFormat::Fc is building a connection URL with
credentials and the raw stderr variable is passed into logger.log_command for
"pg_dump", risking password leakage; before calling logger.log_command (in the
Fc branch handling Command output and in the Err(e) branch), sanitize the stderr
(and any assembled connection URL) by redacting the password or replacing the
entire postgresql://... URL with a masked version (e.g. remove
user:password@host or replace with user:****`@host`) and pass the sanitized string
to logger.log_command instead of the raw stderr (or skip logging raw stderr for
pg_dump in production).

In `@src/domain/redis/backup.rs`:
- Around line 44-45: Replace uses of stdout in the JobLogger command payload
with stderr so the command tuple is (stderr, exit_code, duration) as expected;
locate the places in src/domain/redis/backup.rs where stderr and stdout are
created (variables named stderr and stdout) and where log_command or JobLogger
is invoked (the call currently passing stdout) and change that argument to
stderr, and apply the same fix for the other occurrence around the block
referenced at lines ~81-90 to ensure all command payloads use stderr for the
stderr field.
- Around line 32-34: Remove the plaintext password arg and unify backup logging:
instead of cmd.arg("-a").arg(&cfg.password) use a non-argv auth mechanism (e.g.,
set the REDISCLI_AUTH env on the Command via cmd.env("REDISCLI_AUTH",
&cfg.password) or switch to --askpass) so the secret never appears in argv; also
capture both stdout and stderr from the redis-cli run and pass a consistent
string to logger.log_command (for example, prefer stderr if non-empty else
stdout, or combine them) so jobLogs[].output semantics are consistent between
success and failure paths. Ensure the change touches the Command invocation that
uses cfg.password and the calls to logger.log_command that currently pass
Some(stdout.to_string()) or stderr to make them use the unified output value.

In `@src/domain/sqlite/backup.rs`:
- Around line 24-25: The log currently emits the absolute database path via
logger.log which can leak host filesystem details; change the logger.log call in
backup.rs (the code that constructs db_path from db_path_str and calls
logger.log) to avoid printing db_path.display(): either log only the filename
component (use db_path.file_name() or a safe fallback) or remove the path output
entirely so JobLogger does not receive absolute filesystem paths.

In `@src/domain/valkey/backup.rs`:
- Around line 44-45: The code captures both stderr and stdout but mistakenly
passes stdout to log_command; change calls to log_command (in this file, e.g.,
where you build the command metadata after running the valkey backup process) to
use stderr (the String from String::from_utf8_lossy(&output.stderr)) instead of
stdout, and make the same replacement for the second occurrence around the other
block (lines ~81-90) so the serialized metadata follows the
stderr/exit-code/duration contract.
- Around line 32-34: The current code adds the secret via argv
(cmd.arg("-a").arg(&cfg.password)), which leaks the password to process
listings; remove that argv usage and instead set the credential through a
non-argv channel: either set the VALKEYCLI_AUTH environment variable on the
Command (e.g., cmd.env("VALKEYCLI_AUTH", &cfg.password)) or invoke valkey-cli
with the interactive/askpass mode (pass "--askpass" or equivalent) and write the
password to the child process's stdin via .stdin(Some(Stdio::piped())) before
spawning. Update the code in src/domain/valkey/backup.rs where cfg.password and
cmd are used (remove cmd.arg("-a").arg(&cfg.password)) and ensure you do not log
the password anywhere.

---

Nitpick comments:
In `@src/domain/mongodb/restore.rs`:
- Around line 30-35: The change made in restore.rs shifts from hard-failing to
silently falling back when extract_db_name(&dry_output) returns None; confirm
this behavior is intended and, if so, make the fallback more visible by logging
the fallback message with warn! instead of info! (update the import to include
warn) and keep the subsequent info!("Using source database in archive: {}",
source_db); line. Specifically, update the unwrap_or_else closure that currently
calls info!("Could not detect source database...") to use warn! and ensure the
logging macro import (e.g., warn) is added alongside info at the top of the
file.

In `@src/domain/mysql/backup.rs`:
- Line 59: Remove the unused stdout capture: delete the line creating `_stdout`
(the `let _stdout = String::from_utf8_lossy(&output.stdout).to_string();`) in
src/domain/mysql/backup.rs; if you intended to keep stdout for diagnostics
instead, replace the unused variable with a direct log or use `let _ =
&output.stdout;` or log via the existing logger, otherwise simply remove the
line to eliminate dead code.

In `@src/services/backup/logger.rs`:
- Around line 134-137: Add a non-consuming getter to complement into_entries:
implement a pub fn entries(&self) -> Vec<JobLogEntry> that does not consume self
but instead acquires the internal synchronization primitive (e.g., lock the
Mutex/RwLock inside the Arc), clones or otherwise collects the Vec<JobLogEntry>
contents and returns it; update call sites (e.g., in executor.rs) to call
logger.entries() instead of attempting Arc::try_unwrap(...).unwrap_or_else(...)
so callers avoid forced unwraps and silent data loss when the Arc refcount > 1.

In `@src/services/backup/result.rs`:
- Around line 29-30: The current info!("{:#?}", logs) call prints a
pretty-printed multi-line dump of the entire logs vector; change it to either
use debug logging (replace the info-level call with a debug-level call
referencing the same variable name `logs`) or emit a compact summary at info
level (log the count and a concise preview using `logs.len()` and a short
formatted representation such as the first N entries or a truncated debug of
each entry) so production logs are not flooded; update the statement that
references `logs` in result.rs accordingly.

In `@src/tests/domain/mariadb.rs`:
- Line 61: Introduce a small test helper to avoid repeating
std::sync::Arc::new(crate::services::backup::logger::JobLogger::new()): add a
public fn test_logger() ->
std::sync::Arc<crate::services::backup::logger::JobLogger> in a shared test
utilities module (guarded with #[cfg(test)]) that returns the Arc-wrapped
JobLogger, then replace occurrences in tests (e.g., the call inside the backup
test that currently calls db.backup(...
std::sync::Arc::new(crate::services::backup::logger::JobLogger::new()) ...))
with test_logger() to improve readability and reduce duplication.

In `@src/tests/domain/mssql.rs`:
- Line 93: The test currently discards the JobLogger returned into the backup
call; instead create and reuse a named logger variable (e.g., let logger =
Arc::new(crate::services::backup::logger::JobLogger::new())) and pass that to
db.backup(..., logger.clone()).await.unwrap() so the test retains the logger
instance, then assert on logger entries after calling backup and compression
(check that logger recorded backup/compression messages or entry counts). Apply
the same change to the other occurrences around the 122-125 block so both backup
and compression tests verify the logger recorded expected entries.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 48896b4d-d70d-4f15-a4b6-a61a1c4269c6

📥 Commits

Reviewing files that changed from the base of the PR and between f149363 and 3c853ee.

📒 Files selected for processing (42)
  • .github/workflows/codecov.yml
  • docker-compose.test.yml
  • docker-compose.yml
  • justfile
  • src/domain/factory.rs
  • src/domain/firebird/backup.rs
  • src/domain/firebird/database.rs
  • src/domain/mariadb/backup.rs
  • src/domain/mariadb/database.rs
  • src/domain/mongodb/backup.rs
  • src/domain/mongodb/database.rs
  • src/domain/mongodb/restore.rs
  • src/domain/mssql/backup.rs
  • src/domain/mssql/database.rs
  • src/domain/mysql/backup.rs
  • src/domain/mysql/database.rs
  • src/domain/postgres/backup.rs
  • src/domain/postgres/database.rs
  • src/domain/redis/backup.rs
  • src/domain/redis/database.rs
  • src/domain/sqlite/backup.rs
  • src/domain/sqlite/database.rs
  • src/domain/valkey/backup.rs
  • src/domain/valkey/database.rs
  • src/services/api/endpoints/agent/backup/mod.rs
  • src/services/backup/compressor.rs
  • src/services/backup/executor.rs
  • src/services/backup/logger.rs
  • src/services/backup/mod.rs
  • src/services/backup/result.rs
  • src/services/backup/runner.rs
  • src/services/backup/uploader.rs
  • src/tests/domain/firebird.rs
  • src/tests/domain/mariadb.rs
  • src/tests/domain/mongodb.rs
  • src/tests/domain/mssql.rs
  • src/tests/domain/mysql.rs
  • src/tests/domain/postgres.rs
  • src/tests/domain/redis.rs
  • src/tests/domain/valkey.rs
  • src/tests/utils/compress_tests.rs
  • src/utils/compress.rs
💤 Files with no reviewable changes (1)
  • .github/workflows/codecov.yml

Comment thread docker-compose.yml Outdated
Comment thread justfile Outdated
Comment thread src/domain/mariadb/backup.rs
Comment thread src/domain/mongodb/backup.rs Outdated
Comment thread src/domain/mssql/backup.rs
Comment thread src/domain/redis/backup.rs
Comment thread src/domain/redis/backup.rs
Comment thread src/domain/sqlite/backup.rs
Comment thread src/domain/valkey/backup.rs
Comment thread src/domain/valkey/backup.rs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (4)
.github/workflows/codecov.yml (4)

46-50: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fail the job when no .profraw files are generated.

This step only prints a count and always exits successfully. If coverage instrumentation stops writing profiles, the workflow still continues into grcov and can upload an empty report.

Suggested fix
       - name: Verify profraw files exist
         run: |
-          docker compose -f docker-compose.test.yml exec \
-            -e LLVM_PROFILE_FILE \
-            agent-test find /app/coverage -type f -name "*.profraw" | wc -l || true
+          docker compose -f docker-compose.test.yml exec -T \
+            -e LLVM_PROFILE_FILE \
+            agent-test sh -c 'find /app/coverage -type f -name "*.profraw" -print -quit | grep -q .'
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/codecov.yml around lines 46 - 50, The "Verify profraw
files exist" step currently just prints a count and always succeeds; change it
to run the docker compose command (exec as agent-test) but capture the found
count (find /app/coverage -type f -name "*.profraw") into a variable and if the
count is zero print a clear error and exit non‑zero so the job fails before
grcov/upload; ensure the command uses the same docker compose invocation and
environment (LLVM_PROFILE_FILE) as in the diff so it correctly checks the
agent-test container output.

16-18: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add an explicit minimal GITHUB_TOKEN permission block.

This workflow currently inherits repository defaults. That gives actions/checkout and the Codecov step more scope than they need when repo/org defaults are broader than read-only.

Suggested fix
+permissions:
+  contents: read
+
 jobs:
   coverage:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/codecov.yml around lines 16 - 18, Add an explicit minimal
GITHUB_TOKEN permissions block to the workflow so it no longer inherits repo
defaults: add a top-level or job-level permissions mapping that grants only what
the steps need (e.g., permissions: contents: read) for the coverage job named
"coverage" and ensure the existing actions/checkout and Codecov upload step rely
on that token; update .github/workflows/codecov.yml to include the permissions
block (scoped minimally, e.g., contents: read) adjacent to the jobs: coverage:
runs-on declaration.

Source: Linters/SAST tools


35-40: ⚠️ Potential issue | 🟠 Major

Add -T to docker compose exec commands for GitHub Actions CI

docker compose exec allocates a pseudo-TTY by default; on non-interactive GitHub runners this can fail with “the input device is not a TTY”.

Suggested fix
-          docker compose -f docker-compose.test.yml exec \
+          docker compose -f docker-compose.test.yml exec -T \
             -e CARGO_TARGET_DIR \
             -e CARGO_INCREMENTAL \
             -e RUSTFLAGS \
             -e LLVM_PROFILE_FILE \
             agent-test bash -c "

Apply the same -T change to the other docker compose ... exec invocations in this workflow (the “Verify profraw files exist” and “Generate coverage report inside container” steps).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/codecov.yml around lines 35 - 40, The CI uses "docker
compose -f docker-compose.test.yml exec" which fails on non-interactive GitHub
runners because exec allocates a TTY by default; update each exec invocation
(the one shown and the other steps labeled "Verify profraw files exist" and
"Generate coverage report inside container") to include the -T flag so the
commands run without requesting a pseudo-TTY (i.e., change occurrences of docker
compose ... exec to docker compose ... exec -T).

56-56: ⚠️ Potential issue | 🟡 Minor

Use the current rustup LLVM component name consistently (llvm-tools).

.github/workflows/codecov.yml installs llvm-tools-preview in the toolchain step, but later runs rustup component add llvm-tools. For stable Rust (1.67+), the component name is llvm-tools (legacy llvm-tools-preview was renamed), so update the components: entry to llvm-tools to match the later command.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/codecov.yml at line 56, Update the CI workflow components
entry to use the current Rust LLVM component name: replace the components value
"llvm-tools-preview" with "llvm-tools" so it matches the later command `rustup
component add llvm-tools` and maintains consistency for modern Rust toolchains;
look for the `components:` list and the string "llvm-tools-preview" and change
it to "llvm-tools".
🧹 Nitpick comments (1)
src/tests/domain/firebird.rs (1)

70-76: ⚡ Quick win

Reuse one Arc<JobLogger> across the whole test flow.

Creating two fresh loggers here only checks the updated signatures. It does not exercise the shared accumulation path that production uses for backup plus compression.

♻️ Suggested change
+    let logger = std::sync::Arc::new(crate::services::backup::logger::JobLogger::new());
-    let file_path = db.backup(backup_path, std::sync::Arc::new(crate::services::backup::logger::JobLogger::new())).await.unwrap();
+    let file_path = db.backup(backup_path, logger.clone()).await.unwrap();
...
-    let compression = compress_to_tar_gz_large(&file_path, std::sync::Arc::new(crate::services::backup::logger::JobLogger::new())).await.unwrap();
+    let compression = compress_to_tar_gz_large(&file_path, logger.clone()).await.unwrap();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/tests/domain/firebird.rs` around lines 70 - 76, Test creates two separate
Arc<JobLogger> instances which prevents exercising shared accumulation across
backup and compression; instead instantiate a single Arc<JobLogger> once (e.g.,
let logger =
std::sync::Arc::new(crate::services::backup::logger::JobLogger::new())), pass
that same logger into db.backup(..., logger.clone()).await.unwrap() and then
reuse logger.clone() when calling compress_to_tar_gz_large(&file_path,
logger.clone()).await.unwrap(); keep the rest of the flow (sleep, assert
file_path.is_file()) unchanged so backup and compression share the same
JobLogger instance.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In @.github/workflows/codecov.yml:
- Around line 46-50: The "Verify profraw files exist" step currently just prints
a count and always succeeds; change it to run the docker compose command (exec
as agent-test) but capture the found count (find /app/coverage -type f -name
"*.profraw") into a variable and if the count is zero print a clear error and
exit non‑zero so the job fails before grcov/upload; ensure the command uses the
same docker compose invocation and environment (LLVM_PROFILE_FILE) as in the
diff so it correctly checks the agent-test container output.
- Around line 16-18: Add an explicit minimal GITHUB_TOKEN permissions block to
the workflow so it no longer inherits repo defaults: add a top-level or
job-level permissions mapping that grants only what the steps need (e.g.,
permissions: contents: read) for the coverage job named "coverage" and ensure
the existing actions/checkout and Codecov upload step rely on that token; update
.github/workflows/codecov.yml to include the permissions block (scoped
minimally, e.g., contents: read) adjacent to the jobs: coverage: runs-on
declaration.
- Around line 35-40: The CI uses "docker compose -f docker-compose.test.yml
exec" which fails on non-interactive GitHub runners because exec allocates a TTY
by default; update each exec invocation (the one shown and the other steps
labeled "Verify profraw files exist" and "Generate coverage report inside
container") to include the -T flag so the commands run without requesting a
pseudo-TTY (i.e., change occurrences of docker compose ... exec to docker
compose ... exec -T).
- Line 56: Update the CI workflow components entry to use the current Rust LLVM
component name: replace the components value "llvm-tools-preview" with
"llvm-tools" so it matches the later command `rustup component add llvm-tools`
and maintains consistency for modern Rust toolchains; look for the `components:`
list and the string "llvm-tools-preview" and change it to "llvm-tools".

---

Nitpick comments:
In `@src/tests/domain/firebird.rs`:
- Around line 70-76: Test creates two separate Arc<JobLogger> instances which
prevents exercising shared accumulation across backup and compression; instead
instantiate a single Arc<JobLogger> once (e.g., let logger =
std::sync::Arc::new(crate::services::backup::logger::JobLogger::new())), pass
that same logger into db.backup(..., logger.clone()).await.unwrap() and then
reuse logger.clone() when calling compress_to_tar_gz_large(&file_path,
logger.clone()).await.unwrap(); keep the rest of the flow (sleep, assert
file_path.is_file()) unchanged so backup and compression share the same
JobLogger instance.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a7b6e1a7-4281-4290-b09a-39be0b84df27

📥 Commits

Reviewing files that changed from the base of the PR and between 3c853ee and c840f91.

📒 Files selected for processing (4)
  • .github/workflows/codecov.yml
  • justfile
  • src/services/api/endpoints/agent/backup/mod.rs
  • src/tests/domain/firebird.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • justfile

@codecov

codecov Bot commented Jun 6, 2026

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

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

⚠️ Outside diff range comments (1)
src/domain/factory.rs (1)

11-22: 🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift

Move JobLogger out of services::backup before widening the domain trait.

Database is a domain-layer contract, but it now depends on crate::services::backup::logger::JobLogger. That pulls a service-layer, backup-namespaced type into every database implementation and the restore path as well, which inverts the dependency direction and makes this shared contract harder to evolve cleanly. A neutral module for job logging would keep src/domain/* independent of src/services/backup/*.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/domain/factory.rs` around lines 11 - 22, The Database domain trait
currently depends on crate::services::backup::logger::JobLogger; extract a
neutral logging type into the domain (e.g., create src/domain/logger.rs with a
JobLogger trait or struct) and update the Database trait signatures to use that
domain JobLogger instead of services::backup::logger::JobLogger (affecting fn
backup(&self, backup_dir: &Path, logger: Arc<JobLogger>) and fn restore(&self,
restore_file: &Path, logger: Arc<JobLogger>)). Adjust the use/import at the top
of factory.rs and then update all service implementations and callers to import
and pass the new domain JobLogger (or Arc<dyn JobLogger>) so the domain no
longer depends on services::backup.
🧹 Nitpick comments (8)
.github/workflows/codecov.yml (3)

21-21: ⚡ Quick win

Add persist-credentials: false to the checkout action.

For defense-in-depth, consider adding persist-credentials: false to prevent the GitHub token from persisting in the workspace after checkout. This reduces the attack surface if subsequent steps are compromised.

Additionally, consider pinning the action to a specific commit SHA instead of @v4 for supply chain security.

🔒 Recommended security hardening
-      - uses: actions/checkout@v4
+      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683  # v4.2.2
+        with:
+          persist-credentials: false
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/codecov.yml at line 21, Update the GitHub Actions checkout
step (the actions/checkout usage in the workflow) to include
persist-credentials: false so the GITHUB_TOKEN is not left in the workspace
after checkout; while editing the same checkout step you should also consider
replacing the `@v4` tag with a specific commit SHA (pin the action) to improve
supply-chain security.

Source: Linters/SAST tools


77-86: ⚡ Quick win

Consider pinning the Codecov action to a specific commit SHA.

Using @v5 instead of a pinned SHA hash exposes the workflow to potential supply chain attacks if the action is compromised. For critical CI workflows, pinning to a specific commit SHA is a security best practice.

🔒 Example of pinning to SHA
-      - name: Upload coverage to Codecov
-        uses: codecov/codecov-action@v5
+      - name: Upload coverage to Codecov
+        uses: codecov/codecov-action@5ecb98a3c6b747ed38dc09f787459979aebb39be  # v5.0.4

You can find the SHA for a specific version on the Codecov action releases page.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/codecov.yml around lines 77 - 86, The workflow uses the
Codecov action referenced as codecov/codecov-action@v5 which is unpinned;
replace the floating tag with a specific commit SHA for that release (the SHA
for the desired v5 release) to prevent supply-chain risks. Update the action
reference in the GitHub Actions step that currently uses
codecov/codecov-action@v5 to the full pinned ref (e.g.,
codecov/codecov-action@<COMMIT_SHA>) and keep the existing inputs (files, flags,
name, verbose, fail_ci_if_error, token, use_pypi) unchanged so the step behavior
remains the same. Ensure you pick the SHA from the official Codecov action
releases page and commit the updated reference.

Source: Linters/SAST tools


29-41: Clarify cleanup choice for /app/target in codecov workflow

CARGO_TARGET_DIR is set to /app/target and CARGO_INCREMENTAL is 0, and the workflow doesn’t use actions/cache (only cargo-registry/cargo-git volumes). So rm -rf /app/target/* mainly forces a clean rebuild for coverage and isn’t meaningfully preserving build cache anyway.

For consistency/maintainability, consider replacing the target cleanup with cargo clean (and keep the explicit cleanup for /app/coverage/*, since cargo clean won’t touch coverage).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/codecov.yml around lines 29 - 41, Replace the explicit
removal of /app/target/* in the "Run tests inside container" step with a call to
cargo clean to perform target cleanup in a cargo-aware way; keep the explicit rm
-rf /app/coverage/* to clear coverage artifacts, and ensure CARGO_TARGET_DIR and
CARGO_INCREMENTAL environment variables remain unchanged so the container runs:
cargo clean && rm -rf /app/coverage/* && cargo test --verbose && sync (update
the bash -c string in that step accordingly).
src/tests/domain/firebird.rs (1)

70-70: ⚡ Quick win

Consider adding imports to simplify test code.

The verbose std::sync::Arc::new(crate::services::backup::logger::JobLogger::new()) could be simplified by adding imports at the top of the file:

use std::sync::Arc;
use crate::services::backup::logger::JobLogger;

Then the calls become:

Arc::new(JobLogger::new())

This would improve readability across lines 70, 76, and 98.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/tests/domain/firebird.rs` at line 70, Add local imports to shorten the
fully-qualified types: add `use std::sync::Arc;` and `use
crate::services::backup::logger::JobLogger;` at the top of the test file, then
replace occurrences of
`std::sync::Arc::new(crate::services::backup::logger::JobLogger::new())` used as
the argument to `db.backup(...)` (and similar calls) with
`Arc::new(JobLogger::new())` to improve readability.
src/domain/mongodb/restore.rs (1)

70-70: ⚡ Quick win

Log actual exit code instead of hardcoded 0.

The success path (line 70) logs a hardcoded exit code of 0, but the actual exit code has already been extracted into exit_code (line 61). For consistency with the failure path (line 65) and to ensure accurate command metrics, log the actual exit_code value.

♻️ Proposed fix
-        logger.log_command("mongorestore", if stderr.is_empty() { None } else { Some(stderr) }, Some(0), Some(duration_ms));
+        logger.log_command("mongorestore", if stderr.is_empty() { None } else { Some(stderr) }, Some(exit_code), Some(duration_ms));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/domain/mongodb/restore.rs` at line 70, The logger call in the success
path uses a hardcoded exit code 0; replace that hardcoded 0 with the actual
exit_code variable (already computed earlier) so
logger.log_command("mongorestore", if stderr.is_empty() { None } else {
Some(stderr) }, Some(exit_code), Some(duration_ms)) reports the real exit
status; update the call in restore.rs near the mongorestore success branch to
use exit_code consistent with the failure path.
src/domain/mariadb/restore.rs (1)

49-49: ⚡ Quick win

Log actual exit code instead of hardcoded 0.

Both success paths (lines 49 and 89) log a hardcoded exit code of 0 via logger.log_command(...), but the actual exit code has already been extracted into drop_exit_code (line 41) and exit_code (line 80). While status.success() typically implies exit code 0, logging the actual extracted value ensures consistency and correctness in edge cases.

♻️ Proposed fix
-        logger.log_command("mariadb", if drop_stderr.is_empty() { None } else { Some(drop_stderr) }, Some(0), Some(drop_duration_ms));
+        logger.log_command("mariadb", if drop_stderr.is_empty() { None } else { Some(drop_stderr) }, Some(drop_exit_code), Some(drop_duration_ms));

And similarly for line 89:

-        logger.log_command("mariadb", if stderr.is_empty() { None } else { Some(stderr) }, Some(0), Some(duration_ms));
+        logger.log_command("mariadb", if stderr.is_empty() { None } else { Some(stderr) }, Some(exit_code), Some(duration_ms));

Also applies to: 89-89

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/domain/mariadb/restore.rs` at line 49, Replace the hardcoded exit code 0
passed to logger.log_command with the actual extracted exit code variables: use
drop_exit_code for the first success path (where drop_stderr and
drop_duration_ms are passed) and use exit_code for the second success path
(where stderr and duration_ms are passed); locate the two calls to
logger.log_command and substitute the literal 0 with the corresponding
drop_exit_code / exit_code variables to ensure the logged exit code matches the
extracted value.
src/domain/postgres/restore.rs (1)

74-74: ⚡ Quick win

Log actual exit code instead of hardcoded 0.

Both success paths (lines 74 and 176) log a hardcoded exit code of 0, but the actual exit code has already been extracted into exit_code (lines 71 and 173). For consistency with the failure paths and to ensure accurate command metrics, log the actual exit_code value.

♻️ Proposed fix

For line 74:

-                            logger.log_command("pg_restore", if combined.is_empty() { None } else { Some(combined) }, Some(0), Some(duration_ms));
+                            logger.log_command("pg_restore", if combined.is_empty() { None } else { Some(combined) }, Some(exit_code), Some(duration_ms));

For line 176:

-                            logger.log_command("pg_restore", if combined.is_empty() { None } else { Some(combined) }, Some(0), Some(duration_ms));
+                            logger.log_command("pg_restore", if combined.is_empty() { None } else { Some(combined) }, Some(exit_code), Some(duration_ms));

Also applies to: 176-176

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/domain/postgres/restore.rs` at line 74, The success-path command logging
in restore.rs uses a hardcoded exit code of 0 instead of the already computed
exit_code. In both pg_restore success branches, update the logger.log_command
calls to pass the actual exit_code variable as the exit code argument rather
than Some(0), keeping the existing combined output and duration_ms handling
unchanged so success and failure paths are consistent.
src/services/restore/result.rs (1)

33-36: 💤 Low value

Simplify redundant error conversion.

The map_err(|e| e.into()) converts ApiError to ApiError, which is a no-op. Since error logging already happens in the closure, you can simplify by removing .into() or returning e directly.

♻️ Proposed simplification
             .await
             .map_err(|e| {
                 error!("Failed to send restoration result: {}", e);
-                e.into()
+                e
             })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/services/restore/result.rs` around lines 33 - 36, The map_err closure is
doing a redundant conversion by calling e.into() on an ApiError; update the
closure used where the restoration result is sent (the .map_err(|e| {
error!("Failed to send restoration result: {}", e); e.into() }) chain) to return
the original error instead of converting it (e.g. change e.into() to e or simply
return e), leaving the logging call intact so the error is still logged but
without the no-op conversion.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/services/restore/downloader.rs`:
- Line 18: The logger call in downloader.rs currently logs only "Failed to
download"; update the error logging in the download flow (where
logger.log("error", "Failed to download".to_string()) is invoked) to include the
HTTP response status by capturing response.status() (or equivalent status value)
and append it to the message or pass it as structured context so the log shows
both the failure text and the numeric/status code (e.g., "Failed to download
(status=...)" or include a status field).

In `@src/services/restore/executor.rs`:
- Around line 30-32: The current use of Arc::try_unwrap(logger) discards logs
when there are other Arc clones; change it to preserve entries by falling back
to cloning the inner JobLogger when try_unwrap fails: match on
Arc::try_unwrap(logger) and on Err(arc) call arc.as_ref().clone().into_entries()
(requires JobLogger: Clone) so logs are preserved instead of using
JobLogger::new(); update the code around logs, Arc::try_unwrap, JobLogger::new,
and into_entries accordingly.

In `@src/tests/domain/mssql.rs`:
- Line 141: The restore_config is being created with the same generated_id as
backup_config which can cause ID collisions; update the call to make_config that
constructs restore_config (the variable restore_config in
src/tests/domain/mssql.rs) to use a new unique generated_id string different
from the one used for backup_config (the make_config call that created
backup_config for "sourcedb"), ensuring each distinct DB config has its own
generated_id to avoid file-lock/path conflicts.

---

Outside diff comments:
In `@src/domain/factory.rs`:
- Around line 11-22: The Database domain trait currently depends on
crate::services::backup::logger::JobLogger; extract a neutral logging type into
the domain (e.g., create src/domain/logger.rs with a JobLogger trait or struct)
and update the Database trait signatures to use that domain JobLogger instead of
services::backup::logger::JobLogger (affecting fn backup(&self, backup_dir:
&Path, logger: Arc<JobLogger>) and fn restore(&self, restore_file: &Path,
logger: Arc<JobLogger>)). Adjust the use/import at the top of factory.rs and
then update all service implementations and callers to import and pass the new
domain JobLogger (or Arc<dyn JobLogger>) so the domain no longer depends on
services::backup.

---

Nitpick comments:
In @.github/workflows/codecov.yml:
- Line 21: Update the GitHub Actions checkout step (the actions/checkout usage
in the workflow) to include persist-credentials: false so the GITHUB_TOKEN is
not left in the workspace after checkout; while editing the same checkout step
you should also consider replacing the `@v4` tag with a specific commit SHA (pin
the action) to improve supply-chain security.
- Around line 77-86: The workflow uses the Codecov action referenced as
codecov/codecov-action@v5 which is unpinned; replace the floating tag with a
specific commit SHA for that release (the SHA for the desired v5 release) to
prevent supply-chain risks. Update the action reference in the GitHub Actions
step that currently uses codecov/codecov-action@v5 to the full pinned ref (e.g.,
codecov/codecov-action@<COMMIT_SHA>) and keep the existing inputs (files, flags,
name, verbose, fail_ci_if_error, token, use_pypi) unchanged so the step behavior
remains the same. Ensure you pick the SHA from the official Codecov action
releases page and commit the updated reference.
- Around line 29-41: Replace the explicit removal of /app/target/* in the "Run
tests inside container" step with a call to cargo clean to perform target
cleanup in a cargo-aware way; keep the explicit rm -rf /app/coverage/* to clear
coverage artifacts, and ensure CARGO_TARGET_DIR and CARGO_INCREMENTAL
environment variables remain unchanged so the container runs: cargo clean && rm
-rf /app/coverage/* && cargo test --verbose && sync (update the bash -c string
in that step accordingly).

In `@src/domain/mariadb/restore.rs`:
- Line 49: Replace the hardcoded exit code 0 passed to logger.log_command with
the actual extracted exit code variables: use drop_exit_code for the first
success path (where drop_stderr and drop_duration_ms are passed) and use
exit_code for the second success path (where stderr and duration_ms are passed);
locate the two calls to logger.log_command and substitute the literal 0 with the
corresponding drop_exit_code / exit_code variables to ensure the logged exit
code matches the extracted value.

In `@src/domain/mongodb/restore.rs`:
- Line 70: The logger call in the success path uses a hardcoded exit code 0;
replace that hardcoded 0 with the actual exit_code variable (already computed
earlier) so logger.log_command("mongorestore", if stderr.is_empty() { None }
else { Some(stderr) }, Some(exit_code), Some(duration_ms)) reports the real exit
status; update the call in restore.rs near the mongorestore success branch to
use exit_code consistent with the failure path.

In `@src/domain/postgres/restore.rs`:
- Line 74: The success-path command logging in restore.rs uses a hardcoded exit
code of 0 instead of the already computed exit_code. In both pg_restore success
branches, update the logger.log_command calls to pass the actual exit_code
variable as the exit code argument rather than Some(0), keeping the existing
combined output and duration_ms handling unchanged so success and failure paths
are consistent.

In `@src/services/restore/result.rs`:
- Around line 33-36: The map_err closure is doing a redundant conversion by
calling e.into() on an ApiError; update the closure used where the restoration
result is sent (the .map_err(|e| { error!("Failed to send restoration result:
{}", e); e.into() }) chain) to return the original error instead of converting
it (e.g. change e.into() to e or simply return e), leaving the logging call
intact so the error is still logged but without the no-op conversion.

In `@src/tests/domain/firebird.rs`:
- Line 70: Add local imports to shorten the fully-qualified types: add `use
std::sync::Arc;` and `use crate::services::backup::logger::JobLogger;` at the
top of the test file, then replace occurrences of
`std::sync::Arc::new(crate::services::backup::logger::JobLogger::new())` used as
the argument to `db.backup(...)` (and similar calls) with
`Arc::new(JobLogger::new())` to improve readability.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1ae29897-f3c6-47c4-ae67-c2ad5080f16a

📥 Commits

Reviewing files that changed from the base of the PR and between c840f91 and fba72f5.

📒 Files selected for processing (42)
  • .github/workflows/codecov.yml
  • src/domain/factory.rs
  • src/domain/firebird/backup.rs
  • src/domain/firebird/database.rs
  • src/domain/firebird/restore.rs
  • src/domain/mariadb/backup.rs
  • src/domain/mariadb/database.rs
  • src/domain/mariadb/restore.rs
  • src/domain/mongodb/backup.rs
  • src/domain/mongodb/database.rs
  • src/domain/mongodb/restore.rs
  • src/domain/mssql/backup.rs
  • src/domain/mssql/database.rs
  • src/domain/mssql/restore.rs
  • src/domain/mysql/backup.rs
  • src/domain/mysql/database.rs
  • src/domain/mysql/restore.rs
  • src/domain/postgres/backup.rs
  • src/domain/postgres/database.rs
  • src/domain/postgres/restore.rs
  • src/domain/redis/backup.rs
  • src/domain/redis/database.rs
  • src/domain/sqlite/backup.rs
  • src/domain/sqlite/database.rs
  • src/domain/sqlite/restore.rs
  • src/domain/valkey/backup.rs
  • src/domain/valkey/database.rs
  • src/services/api/endpoints/agent/backup/mod.rs
  • src/services/api/endpoints/agent/restore/mod.rs
  • src/services/backup/executor.rs
  • src/services/backup/result.rs
  • src/services/restore/archive.rs
  • src/services/restore/downloader.rs
  • src/services/restore/executor.rs
  • src/services/restore/result.rs
  • src/services/restore/runner.rs
  • src/tests/domain/firebird.rs
  • src/tests/domain/mariadb.rs
  • src/tests/domain/mongodb.rs
  • src/tests/domain/mssql.rs
  • src/tests/domain/mysql.rs
  • src/tests/domain/postgres.rs
🚧 Files skipped from review as they are similar to previous changes (14)
  • src/services/backup/result.rs
  • src/domain/redis/database.rs
  • src/services/backup/executor.rs
  • src/services/api/endpoints/agent/backup/mod.rs
  • src/domain/valkey/database.rs
  • src/domain/mssql/backup.rs
  • src/domain/mariadb/backup.rs
  • src/domain/firebird/backup.rs
  • src/domain/sqlite/backup.rs
  • src/domain/mysql/backup.rs
  • src/domain/redis/backup.rs
  • src/domain/mongodb/backup.rs
  • src/domain/valkey/backup.rs
  • src/domain/postgres/backup.rs

Comment thread src/services/restore/downloader.rs
Comment thread src/services/restore/executor.rs
Comment thread src/tests/domain/mssql.rs
@RambokDev RambokDev merged commit d8328f6 into main Jun 7, 2026
2 of 3 checks passed
This was referenced Jun 27, 2026
Merged
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.

1 participant