Class: Ruborg::Repository
- Inherits:
-
Object
- Object
- Ruborg::Repository
- Defined in:
- lib/ruborg/repository.rb
Overview
Borg repository management
Constant Summary collapse
- MINIMUM_BORG_VERSION =
"1.4.0"
Instance Attribute Summary collapse
-
#borg_path ⇒ Object
readonly
Returns the value of attribute borg_path.
-
#path ⇒ Object
readonly
Returns the value of attribute path.
Class Method Summary collapse
-
.borg_path(borg_command = "borg") ⇒ Object
Get Borg path (full path to executable).
-
.borg_version(borg_path = "borg") ⇒ Object
Get Borg version.
-
.execute_version_command(borg_path = "borg") ⇒ Object
Execute borg version command (extracted for testing).
Instance Method Summary collapse
- #break_lock ⇒ Object
- #check ⇒ Object
-
#check_compatibility ⇒ Object
Check compatibility between Borg version and repository.
- #create ⇒ Object
- #exists? ⇒ Boolean
- #force_break_lock ⇒ Object
- #get_archive_info(archive_name) ⇒ Object
- #get_file_metadata(archive_name, file_path: nil) ⇒ Object
- #info ⇒ Object
-
#initialize(path, passphrase: nil, borg_options: {}, borg_path: nil, lock_wait: nil, logger: nil) ⇒ Repository
constructor
A new instance of Repository.
- #list ⇒ Object
- #list_archive(archive_name) ⇒ Object
- #locked? ⇒ Boolean
- #prune(retention_policy = {}, retention_mode: "standard") ⇒ Object
Constructor Details
#initialize(path, passphrase: nil, borg_options: {}, borg_path: nil, lock_wait: nil, logger: nil) ⇒ Repository
Returns a new instance of Repository.
9 10 11 12 13 14 15 16 17 |
# File 'lib/ruborg/repository.rb', line 9 def initialize(path, passphrase: nil, borg_options: {}, borg_path: nil, lock_wait: nil, logger: nil) @original_path = path @path = validate_repo_path(path) @passphrase = passphrase @borg_options = @borg_path = validate_borg_path(borg_path || "borg") @lock_wait = lock_wait&.to_i @logger = logger end |
Instance Attribute Details
#borg_path ⇒ Object (readonly)
Returns the value of attribute borg_path.
7 8 9 |
# File 'lib/ruborg/repository.rb', line 7 def borg_path @borg_path end |
#path ⇒ Object (readonly)
Returns the value of attribute path.
7 8 9 |
# File 'lib/ruborg/repository.rb', line 7 def path @path end |
Class Method Details
.borg_path(borg_command = "borg") ⇒ Object
Get Borg path (full path to executable)
519 520 521 522 523 524 525 526 527 528 529 530 531 |
# File 'lib/ruborg/repository.rb', line 519 def self.borg_path(borg_command = "borg") # If it's an absolute or relative path, expand it return File.(borg_command) if borg_command.include?("/") # Otherwise, search in PATH ENV["PATH"].split(File::PATH_SEPARATOR).each do |directory| path = File.join(directory, borg_command) return path if File.executable?(path) end # Not found in PATH, return the command as-is borg_command end |
.borg_version(borg_path = "borg") ⇒ Object
Get Borg version
507 508 509 510 511 512 513 514 515 516 |
# File 'lib/ruborg/repository.rb', line 507 def self.borg_version(borg_path = "borg") output, status = execute_version_command(borg_path) raise BorgError, "Borg is not installed or not in PATH" unless status.success? # Parse version from output like "borg 1.2.8" match = output.match(/borg (\d+\.\d+\.\d+)/) raise BorgError, "Could not parse Borg version from: #{output}" unless match match[1] end |
.execute_version_command(borg_path = "borg") ⇒ Object
Execute borg version command (extracted for testing)
534 535 536 537 538 539 540 |
# File 'lib/ruborg/repository.rb', line 534 def self.execute_version_command(borg_path = "borg") require "open3" # Use Open3.capture2e for safe command execution output, status = Open3.capture2e(borg_path, "--version") [output.strip, status] end |
Instance Method Details
#break_lock ⇒ Object
30 31 32 33 34 35 36 37 |
# File 'lib/ruborg/repository.rb', line 30 def break_lock raise BorgError, "Repository does not exist at #{@path}" unless exists? check_borg_version! cmd = [@borg_path, "break-lock", @path] execute_borg_command(cmd) @logger&.info("Lock broken for repository at #{@path}") end |
#check ⇒ Object
499 500 501 502 503 504 |
# File 'lib/ruborg/repository.rb', line 499 def check raise BorgError, "Repository does not exist at #{@path}" unless exists? cmd = [@borg_path, "check", @path] execute_borg_command(cmd) end |
#check_compatibility ⇒ Object
Check compatibility between Borg version and repository
543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 |
# File 'lib/ruborg/repository.rb', line 543 def check_compatibility raise BorgError, "Repository does not exist at #{@path}" unless exists? borg_version = self.class.borg_version(@borg_path) borg_major, borg_minor = borg_version.split(".").map(&:to_i) # Get repository version from config config_file = File.join(@path, "config") config_content = File.read(config_file) # Extract version from config (format: version = 1) repo_version = config_content.match(/version\s*=\s*(\d+)/)&.captures&.first&.to_i { borg_version: borg_version, repository_version: repo_version, compatible: check_version_compatibility(borg_major, borg_minor, repo_version) } end |
#create ⇒ Object
54 55 56 57 58 59 60 61 |
# File 'lib/ruborg/repository.rb', line 54 def create raise BorgError, "Repository already exists at #{@path}" if exists? @logger&.info("Creating Borg repository at #{@path} with repokey encryption") cmd = [@borg_path, "init", "--encryption=repokey", @path] execute_borg_command(cmd) @logger&.info("Repository created successfully at #{@path}") end |
#exists? ⇒ Boolean
19 20 21 |
# File 'lib/ruborg/repository.rb', line 19 def exists? File.directory?(@path) && File.exist?(File.join(@path, "config")) end |
#force_break_lock ⇒ Object
39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/ruborg/repository.rb', line 39 def force_break_lock raise BorgError, "Repository does not exist at #{@path}" unless exists? require "fileutils" removed = %w[lock.exclusive lock.roster].select do |name| target = File.join(@path, name) next false unless File.exist?(target) FileUtils.rm_rf(target) true end @logger&.info("Force-removed lock files at #{@path}: #{removed.join(", ")}") removed end |
#get_archive_info(archive_name) ⇒ Object
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/ruborg/repository.rb', line 85 def get_archive_info(archive_name) raise BorgError, "Repository does not exist at #{@path}" unless exists? raise BorgError, "Archive name cannot be empty" if archive_name.nil? || archive_name.strip.empty? require "json" require "open3" cmd = [@borg_path, "info", "#{@path}::#{archive_name}", "--json"] env = build_borg_env stdout, stderr, status = Open3.capture3(env, *cmd) raise BorgError, "Failed to get archive info: #{stderr}" unless status.success? JSON.parse(stdout) rescue JSON::ParserError => e raise BorgError, "Failed to parse archive info: #{e.}" end |
#get_file_metadata(archive_name, file_path: nil) ⇒ Object
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/ruborg/repository.rb', line 103 def (archive_name, file_path: nil) raise BorgError, "Repository does not exist at #{@path}" unless exists? raise BorgError, "Archive name cannot be empty" if archive_name.nil? || archive_name.strip.empty? require "json" require "open3" # Get archive info to check if it's a per-file archive archive_info = get_archive_info(archive_name) comment = archive_info.dig("archives", 0, "comment") # If it's a per-file archive (has comment with original path), get metadata for that file # Otherwise, require file_path parameter if comment && !comment.empty? # Per-file archive - get metadata for the single file (archive_name, nil) else # Standard archive - require file_path raise BorgError, "file_path parameter required for standard archives" if file_path.nil? || file_path.empty? (archive_name, file_path) end end |
#info ⇒ Object
63 64 65 66 67 68 |
# File 'lib/ruborg/repository.rb', line 63 def info raise BorgError, "Repository does not exist at #{@path}" unless exists? cmd = [@borg_path, "info", @path] execute_borg_command(cmd) end |
#list ⇒ Object
70 71 72 73 74 75 |
# File 'lib/ruborg/repository.rb', line 70 def list raise BorgError, "Repository does not exist at #{@path}" unless exists? cmd = [@borg_path, "list", @path] execute_borg_command(cmd) end |
#list_archive(archive_name) ⇒ Object
77 78 79 80 81 82 83 |
# File 'lib/ruborg/repository.rb', line 77 def list_archive(archive_name) raise BorgError, "Repository does not exist at #{@path}" unless exists? raise BorgError, "Archive name cannot be empty" if archive_name.nil? || archive_name.strip.empty? cmd = [@borg_path, "list", "#{@path}::#{archive_name}"] execute_borg_command(cmd) end |
#locked? ⇒ Boolean
25 26 27 28 |
# File 'lib/ruborg/repository.rb', line 25 def locked? File.exist?(File.join(@path, "lock.exclusive")) || File.exist?(File.join(@path, "lock.roster")) end |
#prune(retention_policy = {}, retention_mode: "standard") ⇒ Object
166 167 168 169 170 171 172 173 174 175 |
# File 'lib/ruborg/repository.rb', line 166 def prune(retention_policy = {}, retention_mode: "standard") raise BorgError, "Repository does not exist at #{@path}" unless exists? raise BorgError, "No retention policy specified" if retention_policy.nil? || retention_policy.empty? if retention_mode == "per_file" prune_per_file_archives(retention_policy) else prune_standard_archives(retention_policy) end end |