ruborg 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/.rspec +1 -1
 - data/CHANGELOG.md +48 -5
 - data/README.md +187 -17
 - data/lib/ruborg/backup.rb +35 -6
 - data/lib/ruborg/cli.rb +186 -8
 - data/lib/ruborg/config.rb +40 -0
 - data/lib/ruborg/logger.rb +52 -0
 - data/lib/ruborg/passbolt.rb +8 -3
 - data/lib/ruborg/repository.rb +4 -1
 - data/lib/ruborg/version.rb +1 -1
 - data/lib/ruborg.rb +1 -0
 - metadata +2 -2
 - data/ruborg.gemspec +0 -41
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: b3dfe277e22cf29ef1132f479b8e8a969e87c69b383529b7f05eb7238ac5ea07
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: fcf98cabcd5f4636fc513e69e3b446747c9ca2f054f6480f4da60416ec865f0d
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 159296240a1cec2e7791edb3689bf032569d8c1f4fda0ac5c1522e92076fec8c719dba3117326c9fcb9e074ffa295ed71a75b500657625d1ce71fab391b441c2
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: f6385fa1ff56520719b1f8b3985931b4083b3139889b34ed4d1dcb5d449eec8ba98e96f030988cf03cdcb5744f47255ef3b7a605e7bb040d6b1f2c0446ab3b81
         
     | 
    
        data/.rspec
    CHANGED
    
    
    
        data/CHANGELOG.md
    CHANGED
    
    | 
         @@ -7,6 +7,53 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 
     | 
|
| 
       7 
7 
     | 
    
         | 
| 
       8 
8 
     | 
    
         
             
            ## [Unreleased]
         
     | 
| 
       9 
9 
     | 
    
         | 
| 
      
 10 
     | 
    
         
            +
            ## [0.3.0] - 2025-10-05
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            ### Added
         
     | 
| 
      
 13 
     | 
    
         
            +
            - Auto-initialization feature: Set `auto_init: true` in config to automatically initialize repositories on first use
         
     | 
| 
      
 14 
     | 
    
         
            +
            - Multi-repository configuration support with per-repository sources
         
     | 
| 
      
 15 
     | 
    
         
            +
            - `--repository` / `-r` option to target specific repository in multi-repo configs
         
     | 
| 
      
 16 
     | 
    
         
            +
            - `--all` option to backup all repositories at once
         
     | 
| 
      
 17 
     | 
    
         
            +
            - Repository-specific Passbolt integration (overrides global settings)
         
     | 
| 
      
 18 
     | 
    
         
            +
            - Per-source exclude patterns in multi-repo configs
         
     | 
| 
      
 19 
     | 
    
         
            +
            - BackupConfig wrapper class for multi-repo compatibility
         
     | 
| 
      
 20 
     | 
    
         
            +
            - Automatic format detection (single vs multi-repo)
         
     | 
| 
      
 21 
     | 
    
         
            +
            - Support for multiple backup sources per repository
         
     | 
| 
      
 22 
     | 
    
         
            +
            - Global settings with per-repository overrides
         
     | 
| 
      
 23 
     | 
    
         
            +
            - `log_file` configuration option to set log path in config file
         
     | 
| 
      
 24 
     | 
    
         
            +
            - Log file priority: CLI option > config file > default
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
            ### Changed
         
     | 
| 
      
 27 
     | 
    
         
            +
            - Config class now detects and handles both single-repo and multi-repo formats
         
     | 
| 
      
 28 
     | 
    
         
            +
            - Backup command automatically routes to single or multi-repo implementation
         
     | 
| 
      
 29 
     | 
    
         
            +
            - Archive naming includes repository name for multi-repo configs
         
     | 
| 
      
 30 
     | 
    
         
            +
            - CLI now reads log_file from config if --log option not provided
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
            ## [0.2.0] - 2025-10-05
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
            ### Added
         
     | 
| 
      
 35 
     | 
    
         
            +
            - `--remove-source` option to delete source files after successful backup
         
     | 
| 
      
 36 
     | 
    
         
            +
            - Comprehensive logging system with daily rotation (default: `~/.ruborg/logs/ruborg.log`)
         
     | 
| 
      
 37 
     | 
    
         
            +
            - Custom log file support via `--log` option for all commands
         
     | 
| 
      
 38 
     | 
    
         
            +
            - `--path` option for restore command to extract single files/directories from archives
         
     | 
| 
      
 39 
     | 
    
         
            +
            - Comprehensive RSpec test suite with mocked Passbolt and actual Borg integration tests
         
     | 
| 
      
 40 
     | 
    
         
            +
            - Borg installation instructions for macOS and Ubuntu in README
         
     | 
| 
      
 41 
     | 
    
         
            +
            - Support for environment variables to prevent interactive prompts (`BORG_RELOCATED_REPO_ACCESS_IS_OK`, etc.)
         
     | 
| 
      
 42 
     | 
    
         
            +
            - Automatic destination directory creation for restore operations
         
     | 
| 
      
 43 
     | 
    
         
            +
            - Test helpers and fixtures for easier testing
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
            ### Fixed
         
     | 
| 
      
 46 
     | 
    
         
            +
            - Passbolt CLI command corrected from `passbolt get <id>` to `passbolt get resource <id>`
         
     | 
| 
      
 47 
     | 
    
         
            +
            - Borg commands now properly redirect stdin to prevent interactive passphrase prompts
         
     | 
| 
      
 48 
     | 
    
         
            +
            - Improved error handling and logging throughout the application
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
            ### Changed
         
     | 
| 
      
 51 
     | 
    
         
            +
            - Refactored Passbolt class to use testable `execute_command` method
         
     | 
| 
      
 52 
     | 
    
         
            +
            - Enhanced Repository and Backup classes to properly handle environment variables
         
     | 
| 
      
 53 
     | 
    
         
            +
            - Improved CLI integration with better Passbolt mock support in tests
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
            ## [0.1.0] - 2025-10-04
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
       10 
57 
     | 
    
         
             
            ### Added
         
     | 
| 
       11 
58 
     | 
    
         
             
            - Initial gem structure
         
     | 
| 
       12 
59 
     | 
    
         
             
            - Borg repository initialization and management
         
     | 
| 
         @@ -14,8 +61,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 
     | 
|
| 
       14 
61 
     | 
    
         
             
            - YAML configuration file support
         
     | 
| 
       15 
62 
     | 
    
         
             
            - Passbolt CLI integration for password management
         
     | 
| 
       16 
63 
     | 
    
         
             
            - Command-line interface with Thor
         
     | 
| 
       17 
     | 
    
         
            -
             
     | 
| 
       18 
     | 
    
         
            -
            ## [0.1.0] - 2025-10-04
         
     | 
| 
       19 
     | 
    
         
            -
             
     | 
| 
       20 
     | 
    
         
            -
            ### Added
         
     | 
| 
       21 
     | 
    
         
            -
            - Initial release
         
     | 
| 
      
 64 
     | 
    
         
            +
            - Basic error handling
         
     | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -1,7 +1,5 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # Ruborg
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
            > **⚠️ WARNING: This project is under heavy development and is not yet functional. Do not use in production.**
         
     | 
| 
       4 
     | 
    
         
            -
            >
         
     | 
| 
       5 
3 
     | 
    
         
             
            > This gem is being developed with the assistance of Claude AI.
         
     | 
| 
       6 
4 
     | 
    
         | 
| 
       7 
5 
     | 
    
         
             
            A friendly Ruby frontend for [Borg Backup](https://www.borgbackup.org/). Ruborg simplifies backup management by providing a YAML-based configuration system and seamless integration with Passbolt for encryption password management.
         
     | 
| 
         @@ -14,13 +12,37 @@ A friendly Ruby frontend for [Borg Backup](https://www.borgbackup.org/). Ruborg 
     | 
|
| 
       14 
12 
     | 
    
         
             
            - 🔐 **Passbolt Integration** - Secure password management via Passbolt CLI
         
     | 
| 
       15 
13 
     | 
    
         
             
            - 🎯 **Pattern Exclusions** - Flexible file exclusion patterns
         
     | 
| 
       16 
14 
     | 
    
         
             
            - 🗜️ **Compression Options** - Support for multiple compression algorithms
         
     | 
| 
      
 15 
     | 
    
         
            +
            - 🗂️ **Selective Restore** - Restore individual files or directories from archives
         
     | 
| 
      
 16 
     | 
    
         
            +
            - 🧹 **Auto-cleanup** - Optionally remove source files after successful backup
         
     | 
| 
      
 17 
     | 
    
         
            +
            - 📊 **Logging** - Comprehensive logging with daily rotation
         
     | 
| 
      
 18 
     | 
    
         
            +
            - 🗄️ **Multi-Repository** - Manage multiple backup repositories with different sources
         
     | 
| 
      
 19 
     | 
    
         
            +
            - 🔄 **Auto-initialization** - Automatically initialize repositories on first use
         
     | 
| 
      
 20 
     | 
    
         
            +
            - ✅ **Well-tested** - Comprehensive test suite with RSpec
         
     | 
| 
       17 
21 
     | 
    
         | 
| 
       18 
22 
     | 
    
         
             
            ## Prerequisites
         
     | 
| 
       19 
23 
     | 
    
         | 
| 
       20 
     | 
    
         
            -
            - Ruby >= 2. 
     | 
| 
      
 24 
     | 
    
         
            +
            - Ruby >= 3.2.0
         
     | 
| 
       21 
25 
     | 
    
         
             
            - [Borg Backup](https://www.borgbackup.org/) installed and available in PATH
         
     | 
| 
       22 
26 
     | 
    
         
             
            - [Passbolt CLI](https://github.com/passbolt/go-passbolt-cli) (optional, for password management)
         
     | 
| 
       23 
27 
     | 
    
         | 
| 
      
 28 
     | 
    
         
            +
            ### Installing Borg Backup
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
            **macOS:**
         
     | 
| 
      
 31 
     | 
    
         
            +
            ```bash
         
     | 
| 
      
 32 
     | 
    
         
            +
            brew install borgbackup
         
     | 
| 
      
 33 
     | 
    
         
            +
            ```
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
            **Ubuntu/Debian:**
         
     | 
| 
      
 36 
     | 
    
         
            +
            ```bash
         
     | 
| 
      
 37 
     | 
    
         
            +
            sudo apt update
         
     | 
| 
      
 38 
     | 
    
         
            +
            sudo apt install borgbackup
         
     | 
| 
      
 39 
     | 
    
         
            +
            ```
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
            **Verify installation:**
         
     | 
| 
      
 42 
     | 
    
         
            +
            ```bash
         
     | 
| 
      
 43 
     | 
    
         
            +
            borg --version
         
     | 
| 
      
 44 
     | 
    
         
            +
            ```
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
       24 
46 
     | 
    
         
             
            ## Installation
         
     | 
| 
       25 
47 
     | 
    
         | 
| 
       26 
48 
     | 
    
         
             
            Add this line to your application's Gemfile:
         
     | 
| 
         @@ -43,7 +65,9 @@ gem install ruborg 
     | 
|
| 
       43 
65 
     | 
    
         | 
| 
       44 
66 
     | 
    
         
             
            ## Configuration
         
     | 
| 
       45 
67 
     | 
    
         | 
| 
       46 
     | 
    
         
            -
             
     | 
| 
      
 68 
     | 
    
         
            +
            Ruborg supports two configuration formats: **single repository** (legacy) and **multi-repository** (recommended for complex setups).
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
            ### Single Repository Configuration
         
     | 
| 
       47 
71 
     | 
    
         | 
| 
       48 
72 
     | 
    
         
             
            ```yaml
         
     | 
| 
       49 
73 
     | 
    
         
             
            # Repository path
         
     | 
| 
         @@ -53,15 +77,11 @@ repository: /path/to/borg/repository 
     | 
|
| 
       53 
77 
     | 
    
         
             
            backup_paths:
         
     | 
| 
       54 
78 
     | 
    
         
             
              - /home/user/documents
         
     | 
| 
       55 
79 
     | 
    
         
             
              - /home/user/projects
         
     | 
| 
       56 
     | 
    
         
            -
              - /etc
         
     | 
| 
       57 
80 
     | 
    
         | 
| 
       58 
81 
     | 
    
         
             
            # Exclude patterns
         
     | 
| 
       59 
82 
     | 
    
         
             
            exclude_patterns:
         
     | 
| 
       60 
83 
     | 
    
         
             
              - "*.tmp"
         
     | 
| 
       61 
84 
     | 
    
         
             
              - "*.log"
         
     | 
| 
       62 
     | 
    
         
            -
              - "*/.cache/*"
         
     | 
| 
       63 
     | 
    
         
            -
              - "*/node_modules/*"
         
     | 
| 
       64 
     | 
    
         
            -
              - "*/.git/*"
         
     | 
| 
       65 
85 
     | 
    
         | 
| 
       66 
86 
     | 
    
         
             
            # Compression algorithm (lz4, zstd, zlib, lzma, none)
         
     | 
| 
       67 
87 
     | 
    
         
             
            compression: lz4
         
     | 
| 
         @@ -72,9 +92,62 @@ encryption: repokey 
     | 
|
| 
       72 
92 
     | 
    
         
             
            # Passbolt integration (optional)
         
     | 
| 
       73 
93 
     | 
    
         
             
            passbolt:
         
     | 
| 
       74 
94 
     | 
    
         
             
              resource_id: "your-passbolt-resource-uuid"
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
            # Auto-initialize repository (optional, default: false)
         
     | 
| 
      
 97 
     | 
    
         
            +
            auto_init: true
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
            # Log file path (optional, default: ~/.ruborg/logs/ruborg.log)
         
     | 
| 
      
 100 
     | 
    
         
            +
            log_file: /var/log/ruborg.log
         
     | 
| 
       75 
101 
     | 
    
         
             
            ```
         
     | 
| 
       76 
102 
     | 
    
         | 
| 
       77 
     | 
    
         
            -
             
     | 
| 
      
 103 
     | 
    
         
            +
            ### Multi-Repository Configuration
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
            For managing multiple repositories with different sources:
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
            ```yaml
         
     | 
| 
      
 108 
     | 
    
         
            +
            # Global settings (applied to all repositories unless overridden)
         
     | 
| 
      
 109 
     | 
    
         
            +
            compression: lz4
         
     | 
| 
      
 110 
     | 
    
         
            +
            encryption: repokey
         
     | 
| 
      
 111 
     | 
    
         
            +
            auto_init: true
         
     | 
| 
      
 112 
     | 
    
         
            +
            passbolt:
         
     | 
| 
      
 113 
     | 
    
         
            +
              resource_id: "global-passbolt-id"
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
            # Multiple repositories
         
     | 
| 
      
 116 
     | 
    
         
            +
            repositories:
         
     | 
| 
      
 117 
     | 
    
         
            +
              - name: documents
         
     | 
| 
      
 118 
     | 
    
         
            +
                path: /mnt/backup/documents
         
     | 
| 
      
 119 
     | 
    
         
            +
                sources:
         
     | 
| 
      
 120 
     | 
    
         
            +
                  - name: home-docs
         
     | 
| 
      
 121 
     | 
    
         
            +
                    paths:
         
     | 
| 
      
 122 
     | 
    
         
            +
                      - /home/user/documents
         
     | 
| 
      
 123 
     | 
    
         
            +
                    exclude:
         
     | 
| 
      
 124 
     | 
    
         
            +
                      - "*.tmp"
         
     | 
| 
      
 125 
     | 
    
         
            +
                  - name: work-docs
         
     | 
| 
      
 126 
     | 
    
         
            +
                    paths:
         
     | 
| 
      
 127 
     | 
    
         
            +
                      - /home/user/work
         
     | 
| 
      
 128 
     | 
    
         
            +
                    exclude:
         
     | 
| 
      
 129 
     | 
    
         
            +
                      - "*.log"
         
     | 
| 
      
 130 
     | 
    
         
            +
             
     | 
| 
      
 131 
     | 
    
         
            +
              - name: databases
         
     | 
| 
      
 132 
     | 
    
         
            +
                path: /mnt/backup/databases
         
     | 
| 
      
 133 
     | 
    
         
            +
                # Repository-specific passbolt (overrides global)
         
     | 
| 
      
 134 
     | 
    
         
            +
                passbolt:
         
     | 
| 
      
 135 
     | 
    
         
            +
                  resource_id: "db-specific-passbolt-id"
         
     | 
| 
      
 136 
     | 
    
         
            +
                sources:
         
     | 
| 
      
 137 
     | 
    
         
            +
                  - name: mysql
         
     | 
| 
      
 138 
     | 
    
         
            +
                    paths:
         
     | 
| 
      
 139 
     | 
    
         
            +
                      - /var/lib/mysql/dumps
         
     | 
| 
      
 140 
     | 
    
         
            +
                  - name: postgres
         
     | 
| 
      
 141 
     | 
    
         
            +
                    paths:
         
     | 
| 
      
 142 
     | 
    
         
            +
                      - /var/lib/postgresql/dumps
         
     | 
| 
      
 143 
     | 
    
         
            +
            ```
         
     | 
| 
      
 144 
     | 
    
         
            +
             
     | 
| 
      
 145 
     | 
    
         
            +
            **Multi-repo benefits:**
         
     | 
| 
      
 146 
     | 
    
         
            +
            - Organize backups by type (documents, databases, media)
         
     | 
| 
      
 147 
     | 
    
         
            +
            - Different encryption keys per repository
         
     | 
| 
      
 148 
     | 
    
         
            +
            - Multiple sources per repository
         
     | 
| 
      
 149 
     | 
    
         
            +
            - Per-source exclude patterns
         
     | 
| 
      
 150 
     | 
    
         
            +
            - Repository-specific settings override global ones
         
     | 
| 
       78 
151 
     | 
    
         | 
| 
       79 
152 
     | 
    
         
             
            ## Usage
         
     | 
| 
       80 
153 
     | 
    
         | 
| 
         @@ -90,6 +163,7 @@ ruborg init /path/to/repository --passbolt-id "resource-uuid" 
     | 
|
| 
       90 
163 
     | 
    
         | 
| 
       91 
164 
     | 
    
         
             
            ### Create a Backup
         
     | 
| 
       92 
165 
     | 
    
         | 
| 
      
 166 
     | 
    
         
            +
            **Single repository:**
         
     | 
| 
       93 
167 
     | 
    
         
             
            ```bash
         
     | 
| 
       94 
168 
     | 
    
         
             
            # Using default configuration (ruborg.yml)
         
     | 
| 
       95 
169 
     | 
    
         
             
            ruborg backup
         
     | 
| 
         @@ -99,6 +173,21 @@ ruborg backup --config /path/to/config.yml 
     | 
|
| 
       99 
173 
     | 
    
         | 
| 
       100 
174 
     | 
    
         
             
            # With custom archive name
         
     | 
| 
       101 
175 
     | 
    
         
             
            ruborg backup --name "my-backup-2025-10-04"
         
     | 
| 
      
 176 
     | 
    
         
            +
             
     | 
| 
      
 177 
     | 
    
         
            +
            # Remove source files after successful backup
         
     | 
| 
      
 178 
     | 
    
         
            +
            ruborg backup --remove-source
         
     | 
| 
      
 179 
     | 
    
         
            +
            ```
         
     | 
| 
      
 180 
     | 
    
         
            +
             
     | 
| 
      
 181 
     | 
    
         
            +
            **Multi-repository:**
         
     | 
| 
      
 182 
     | 
    
         
            +
            ```bash
         
     | 
| 
      
 183 
     | 
    
         
            +
            # Backup specific repository
         
     | 
| 
      
 184 
     | 
    
         
            +
            ruborg backup --repository documents
         
     | 
| 
      
 185 
     | 
    
         
            +
             
     | 
| 
      
 186 
     | 
    
         
            +
            # Backup all repositories
         
     | 
| 
      
 187 
     | 
    
         
            +
            ruborg backup --all
         
     | 
| 
      
 188 
     | 
    
         
            +
             
     | 
| 
      
 189 
     | 
    
         
            +
            # Backup specific repository with custom name
         
     | 
| 
      
 190 
     | 
    
         
            +
            ruborg backup --repository databases --name "db-backup-2025-10-05"
         
     | 
| 
       102 
191 
     | 
    
         
             
            ```
         
     | 
| 
       103 
192 
     | 
    
         | 
| 
       104 
193 
     | 
    
         
             
            ### List Archives
         
     | 
| 
         @@ -110,11 +199,14 @@ ruborg list 
     | 
|
| 
       110 
199 
     | 
    
         
             
            ### Restore from Archive
         
     | 
| 
       111 
200 
     | 
    
         | 
| 
       112 
201 
     | 
    
         
             
            ```bash
         
     | 
| 
       113 
     | 
    
         
            -
            # Restore to current directory
         
     | 
| 
      
 202 
     | 
    
         
            +
            # Restore entire archive to current directory
         
     | 
| 
       114 
203 
     | 
    
         
             
            ruborg restore archive-name
         
     | 
| 
       115 
204 
     | 
    
         | 
| 
       116 
205 
     | 
    
         
             
            # Restore to specific directory
         
     | 
| 
       117 
206 
     | 
    
         
             
            ruborg restore archive-name --destination /path/to/restore
         
     | 
| 
      
 207 
     | 
    
         
            +
             
     | 
| 
      
 208 
     | 
    
         
            +
            # Restore a single file from archive
         
     | 
| 
      
 209 
     | 
    
         
            +
            ruborg restore archive-name --path /path/to/file.txt --destination /new/location
         
     | 
| 
       118 
210 
     | 
    
         
             
            ```
         
     | 
| 
       119 
211 
     | 
    
         | 
| 
       120 
212 
     | 
    
         
             
            ### View Repository Information
         
     | 
| 
         @@ -123,13 +215,50 @@ ruborg restore archive-name --destination /path/to/restore 
     | 
|
| 
       123 
215 
     | 
    
         
             
            ruborg info
         
     | 
| 
       124 
216 
     | 
    
         
             
            ```
         
     | 
| 
       125 
217 
     | 
    
         | 
| 
      
 218 
     | 
    
         
            +
            ## Logging
         
     | 
| 
      
 219 
     | 
    
         
            +
             
     | 
| 
      
 220 
     | 
    
         
            +
            Ruborg automatically logs all operations with daily rotation. Log file location priority:
         
     | 
| 
      
 221 
     | 
    
         
            +
             
     | 
| 
      
 222 
     | 
    
         
            +
            1. **CLI option** (highest priority): `--log /path/to/custom.log`
         
     | 
| 
      
 223 
     | 
    
         
            +
            2. **Config file**: `log_file: /path/to/log.log`
         
     | 
| 
      
 224 
     | 
    
         
            +
            3. **Default**: `~/.ruborg/logs/ruborg.log`
         
     | 
| 
      
 225 
     | 
    
         
            +
             
     | 
| 
      
 226 
     | 
    
         
            +
            **Examples:**
         
     | 
| 
      
 227 
     | 
    
         
            +
             
     | 
| 
      
 228 
     | 
    
         
            +
            ```bash
         
     | 
| 
      
 229 
     | 
    
         
            +
            # Use CLI option (overrides config)
         
     | 
| 
      
 230 
     | 
    
         
            +
            ruborg backup --log /var/log/ruborg.log
         
     | 
| 
      
 231 
     | 
    
         
            +
             
     | 
| 
      
 232 
     | 
    
         
            +
            # Or set in config file
         
     | 
| 
      
 233 
     | 
    
         
            +
            log_file: /var/log/ruborg.log
         
     | 
| 
      
 234 
     | 
    
         
            +
            ```
         
     | 
| 
      
 235 
     | 
    
         
            +
             
     | 
| 
      
 236 
     | 
    
         
            +
            **Logs include:**
         
     | 
| 
      
 237 
     | 
    
         
            +
            - Operation start/completion timestamps
         
     | 
| 
      
 238 
     | 
    
         
            +
            - Paths being backed up
         
     | 
| 
      
 239 
     | 
    
         
            +
            - Archive names created
         
     | 
| 
      
 240 
     | 
    
         
            +
            - Success and error messages
         
     | 
| 
      
 241 
     | 
    
         
            +
            - Source file removal actions
         
     | 
| 
      
 242 
     | 
    
         
            +
             
     | 
| 
       126 
243 
     | 
    
         
             
            ## Passbolt Integration
         
     | 
| 
       127 
244 
     | 
    
         | 
| 
       128 
245 
     | 
    
         
             
            Ruborg can retrieve encryption passphrases from Passbolt using the Passbolt CLI:
         
     | 
| 
       129 
246 
     | 
    
         | 
| 
       130 
247 
     | 
    
         
             
            1. Install and configure [Passbolt CLI](https://github.com/passbolt/go-passbolt-cli)
         
     | 
| 
       131 
     | 
    
         
            -
            2.  
     | 
| 
       132 
     | 
    
         
            -
             
     | 
| 
      
 248 
     | 
    
         
            +
            2. Configure Passbolt CLI with your server credentials:
         
     | 
| 
      
 249 
     | 
    
         
            +
               ```bash
         
     | 
| 
      
 250 
     | 
    
         
            +
               passbolt configure --serverAddress https://server.address \
         
     | 
| 
      
 251 
     | 
    
         
            +
                                  --userPrivateKeyFile /path/to/private.key \
         
     | 
| 
      
 252 
     | 
    
         
            +
                                  --userPassword YOUR_PASSWORD
         
     | 
| 
      
 253 
     | 
    
         
            +
               ```
         
     | 
| 
      
 254 
     | 
    
         
            +
               Or set environment variables:
         
     | 
| 
      
 255 
     | 
    
         
            +
               ```bash
         
     | 
| 
      
 256 
     | 
    
         
            +
               export PASSBOLT_SERVER_ADDRESS=https://server.address
         
     | 
| 
      
 257 
     | 
    
         
            +
               export PASSBOLT_USER_PRIVATE_KEY_FILE=/path/to/private.key
         
     | 
| 
      
 258 
     | 
    
         
            +
               export PASSBOLT_USER_PASSWORD=YOUR_PASSWORD
         
     | 
| 
      
 259 
     | 
    
         
            +
               ```
         
     | 
| 
      
 260 
     | 
    
         
            +
            3. Store your Borg repository passphrase in Passbolt
         
     | 
| 
      
 261 
     | 
    
         
            +
            4. Add the resource ID to your `ruborg.yml`:
         
     | 
| 
       133 
262 
     | 
    
         | 
| 
       134 
263 
     | 
    
         
             
            ```yaml
         
     | 
| 
       135 
264 
     | 
    
         
             
            passbolt:
         
     | 
| 
         @@ -138,15 +267,41 @@ passbolt: 
     | 
|
| 
       138 
267 
     | 
    
         | 
| 
       139 
268 
     | 
    
         
             
            Ruborg will automatically retrieve the passphrase when performing backup operations.
         
     | 
| 
       140 
269 
     | 
    
         | 
| 
      
 270 
     | 
    
         
            +
            ## Auto-initialization
         
     | 
| 
      
 271 
     | 
    
         
            +
             
     | 
| 
      
 272 
     | 
    
         
            +
            Set `auto_init: true` in your configuration file to automatically initialize the repository on first use:
         
     | 
| 
      
 273 
     | 
    
         
            +
             
     | 
| 
      
 274 
     | 
    
         
            +
            ```yaml
         
     | 
| 
      
 275 
     | 
    
         
            +
            repository: /path/to/borg/repository
         
     | 
| 
      
 276 
     | 
    
         
            +
            auto_init: true
         
     | 
| 
      
 277 
     | 
    
         
            +
            passbolt:
         
     | 
| 
      
 278 
     | 
    
         
            +
              resource_id: "your-passbolt-resource-uuid"
         
     | 
| 
      
 279 
     | 
    
         
            +
            backup_paths:
         
     | 
| 
      
 280 
     | 
    
         
            +
              - /path/to/backup
         
     | 
| 
      
 281 
     | 
    
         
            +
            ```
         
     | 
| 
      
 282 
     | 
    
         
            +
             
     | 
| 
      
 283 
     | 
    
         
            +
            When enabled, ruborg will automatically run `borg init` if the repository doesn't exist when you run `backup`, `list`, or `info` commands. The passphrase will be retrieved from Passbolt if configured.
         
     | 
| 
      
 284 
     | 
    
         
            +
             
     | 
| 
       141 
285 
     | 
    
         
             
            ## Command Reference
         
     | 
| 
       142 
286 
     | 
    
         | 
| 
       143 
287 
     | 
    
         
             
            | Command | Description | Options |
         
     | 
| 
       144 
288 
     | 
    
         
             
            |---------|-------------|---------|
         
     | 
| 
       145 
     | 
    
         
            -
            | `init REPOSITORY` | Initialize a new Borg repository | `--passphrase`, `--passbolt-id` |
         
     | 
| 
       146 
     | 
    
         
            -
            | `backup` | Create a backup using config file | `--config`, `--name` |
         
     | 
| 
       147 
     | 
    
         
            -
            | `list` | List all archives in repository | `--config` |
         
     | 
| 
       148 
     | 
    
         
            -
            | `restore ARCHIVE` | Restore files from archive | `--config`, `--destination` |
         
     | 
| 
       149 
     | 
    
         
            -
            | `info` | Show repository information | `--config` |
         
     | 
| 
      
 289 
     | 
    
         
            +
            | `init REPOSITORY` | Initialize a new Borg repository | `--passphrase`, `--passbolt-id`, `--log` |
         
     | 
| 
      
 290 
     | 
    
         
            +
            | `backup` | Create a backup using config file | `--config`, `--name`, `--remove-source`, `--repository`, `--all`, `--log` |
         
     | 
| 
      
 291 
     | 
    
         
            +
            | `list` | List all archives in repository | `--config`, `--repository`, `--log` |
         
     | 
| 
      
 292 
     | 
    
         
            +
            | `restore ARCHIVE` | Restore files from archive | `--config`, `--destination`, `--path`, `--repository`, `--log` |
         
     | 
| 
      
 293 
     | 
    
         
            +
            | `info` | Show repository information | `--config`, `--repository`, `--log` |
         
     | 
| 
      
 294 
     | 
    
         
            +
             
     | 
| 
      
 295 
     | 
    
         
            +
            ### Global Options
         
     | 
| 
      
 296 
     | 
    
         
            +
             
     | 
| 
      
 297 
     | 
    
         
            +
            - `--config`: Path to configuration file (default: `ruborg.yml`)
         
     | 
| 
      
 298 
     | 
    
         
            +
            - `--log`: Path to log file (overrides config, default: `~/.ruborg/logs/ruborg.log`)
         
     | 
| 
      
 299 
     | 
    
         
            +
            - `--repository` / `-r`: Repository name (required for multi-repo configs)
         
     | 
| 
      
 300 
     | 
    
         
            +
             
     | 
| 
      
 301 
     | 
    
         
            +
            ### Multi-Repository Options
         
     | 
| 
      
 302 
     | 
    
         
            +
             
     | 
| 
      
 303 
     | 
    
         
            +
            - `--all`: Backup all repositories (multi-repo config only)
         
     | 
| 
      
 304 
     | 
    
         
            +
            - `--repository NAME`: Target specific repository by name
         
     | 
| 
       150 
305 
     | 
    
         | 
| 
       151 
306 
     | 
    
         
             
            ## Development
         
     | 
| 
       152 
307 
     | 
    
         | 
| 
         @@ -169,9 +324,24 @@ bundle exec rake release 
     | 
|
| 
       169 
324 
     | 
    
         
             
            Run the test suite:
         
     | 
| 
       170 
325 
     | 
    
         | 
| 
       171 
326 
     | 
    
         
             
            ```bash
         
     | 
| 
      
 327 
     | 
    
         
            +
            # Run all tests
         
     | 
| 
       172 
328 
     | 
    
         
             
            bundle exec rspec
         
     | 
| 
      
 329 
     | 
    
         
            +
             
     | 
| 
      
 330 
     | 
    
         
            +
            # Run only unit tests (no Borg required)
         
     | 
| 
      
 331 
     | 
    
         
            +
            bundle exec rspec --tag ~borg
         
     | 
| 
      
 332 
     | 
    
         
            +
             
     | 
| 
      
 333 
     | 
    
         
            +
            # Run only integration tests (requires Borg)
         
     | 
| 
      
 334 
     | 
    
         
            +
            bundle exec rspec --tag borg
         
     | 
| 
       173 
335 
     | 
    
         
             
            ```
         
     | 
| 
       174 
336 
     | 
    
         | 
| 
      
 337 
     | 
    
         
            +
            The test suite includes:
         
     | 
| 
      
 338 
     | 
    
         
            +
            - Config loading and validation
         
     | 
| 
      
 339 
     | 
    
         
            +
            - Repository management (with actual Borg integration)
         
     | 
| 
      
 340 
     | 
    
         
            +
            - Backup and restore operations
         
     | 
| 
      
 341 
     | 
    
         
            +
            - Passbolt integration (mocked)
         
     | 
| 
      
 342 
     | 
    
         
            +
            - CLI commands
         
     | 
| 
      
 343 
     | 
    
         
            +
            - Logging functionality
         
     | 
| 
      
 344 
     | 
    
         
            +
             
     | 
| 
       175 
345 
     | 
    
         
             
            ## Contributing
         
     | 
| 
       176 
346 
     | 
    
         | 
| 
       177 
347 
     | 
    
         
             
            Bug reports and pull requests are welcome on GitHub at https://github.com/mpantel/ruborg.
         
     | 
    
        data/lib/ruborg/backup.rb
    CHANGED
    
    | 
         @@ -8,22 +8,33 @@ module Ruborg 
     | 
|
| 
       8 
8 
     | 
    
         
             
                  @config = config
         
     | 
| 
       9 
9 
     | 
    
         
             
                end
         
     | 
| 
       10 
10 
     | 
    
         | 
| 
       11 
     | 
    
         
            -
                def create(name: nil)
         
     | 
| 
      
 11 
     | 
    
         
            +
                def create(name: nil, remove_source: false)
         
     | 
| 
       12 
12 
     | 
    
         
             
                  raise BorgError, "Repository does not exist" unless @repository.exists?
         
     | 
| 
       13 
13 
     | 
    
         | 
| 
       14 
14 
     | 
    
         
             
                  archive_name = name || Time.now.strftime("%Y-%m-%d_%H-%M-%S")
         
     | 
| 
       15 
15 
     | 
    
         
             
                  cmd = build_create_command(archive_name)
         
     | 
| 
       16 
16 
     | 
    
         | 
| 
       17 
17 
     | 
    
         
             
                  execute_borg_command(cmd)
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  remove_source_files if remove_source
         
     | 
| 
       18 
20 
     | 
    
         
             
                end
         
     | 
| 
       19 
21 
     | 
    
         | 
| 
       20 
     | 
    
         
            -
                def extract(archive_name, destination: ".")
         
     | 
| 
      
 22 
     | 
    
         
            +
                def extract(archive_name, destination: ".", path: nil)
         
     | 
| 
       21 
23 
     | 
    
         
             
                  raise BorgError, "Repository does not exist" unless @repository.exists?
         
     | 
| 
       22 
24 
     | 
    
         | 
| 
       23 
25 
     | 
    
         
             
                  cmd = ["borg", "extract", "#{@repository.path}::#{archive_name}"]
         
     | 
| 
       24 
     | 
    
         
            -
                  cmd  
     | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
       26 
     | 
    
         
            -
                   
     | 
| 
      
 26 
     | 
    
         
            +
                  cmd << path if path
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                  # Change to destination directory if specified
         
     | 
| 
      
 29 
     | 
    
         
            +
                  if destination != "."
         
     | 
| 
      
 30 
     | 
    
         
            +
                    require "fileutils"
         
     | 
| 
      
 31 
     | 
    
         
            +
                    FileUtils.mkdir_p(destination) unless File.directory?(destination)
         
     | 
| 
      
 32 
     | 
    
         
            +
                    Dir.chdir(destination) do
         
     | 
| 
      
 33 
     | 
    
         
            +
                      execute_borg_command(cmd)
         
     | 
| 
      
 34 
     | 
    
         
            +
                    end
         
     | 
| 
      
 35 
     | 
    
         
            +
                  else
         
     | 
| 
      
 36 
     | 
    
         
            +
                    execute_borg_command(cmd)
         
     | 
| 
      
 37 
     | 
    
         
            +
                  end
         
     | 
| 
       27 
38 
     | 
    
         
             
                end
         
     | 
| 
       28 
39 
     | 
    
         | 
| 
       29 
40 
     | 
    
         
             
                def list_archives
         
     | 
| 
         @@ -52,10 +63,28 @@ module Ruborg 
     | 
|
| 
       52 
63 
     | 
    
         
             
                end
         
     | 
| 
       53 
64 
     | 
    
         | 
| 
       54 
65 
     | 
    
         
             
                def execute_borg_command(cmd)
         
     | 
| 
       55 
     | 
    
         
            -
                   
     | 
| 
      
 66 
     | 
    
         
            +
                  env = {}
         
     | 
| 
      
 67 
     | 
    
         
            +
                  passphrase = @repository.instance_variable_get(:@passphrase)
         
     | 
| 
      
 68 
     | 
    
         
            +
                  env["BORG_PASSPHRASE"] = passphrase if passphrase
         
     | 
| 
      
 69 
     | 
    
         
            +
                  env["BORG_RELOCATED_REPO_ACCESS_IS_OK"] = "yes"
         
     | 
| 
      
 70 
     | 
    
         
            +
                  env["BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK"] = "yes"
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                  result = system(env, *cmd, in: "/dev/null")
         
     | 
| 
       56 
73 
     | 
    
         
             
                  raise BorgError, "Borg command failed: #{cmd.join(' ')}" unless result
         
     | 
| 
       57 
74 
     | 
    
         | 
| 
       58 
75 
     | 
    
         
             
                  result
         
     | 
| 
       59 
76 
     | 
    
         
             
                end
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                def remove_source_files
         
     | 
| 
      
 79 
     | 
    
         
            +
                  require "fileutils"
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                  @config.backup_paths.each do |path|
         
     | 
| 
      
 82 
     | 
    
         
            +
                    if File.directory?(path)
         
     | 
| 
      
 83 
     | 
    
         
            +
                      FileUtils.rm_rf(path)
         
     | 
| 
      
 84 
     | 
    
         
            +
                    elsif File.file?(path)
         
     | 
| 
      
 85 
     | 
    
         
            +
                      FileUtils.rm(path)
         
     | 
| 
      
 86 
     | 
    
         
            +
                    end
         
     | 
| 
      
 87 
     | 
    
         
            +
                  end
         
     | 
| 
      
 88 
     | 
    
         
            +
                end
         
     | 
| 
       60 
89 
     | 
    
         
             
              end
         
     | 
| 
       61 
90 
     | 
    
         
             
            end
         
     | 
    
        data/lib/ruborg/cli.rb
    CHANGED
    
    | 
         @@ -6,68 +6,123 @@ module Ruborg 
     | 
|
| 
       6 
6 
     | 
    
         
             
              # Command-line interface for ruborg
         
     | 
| 
       7 
7 
     | 
    
         
             
              class CLI < Thor
         
     | 
| 
       8 
8 
     | 
    
         
             
                class_option :config, type: :string, default: "ruborg.yml", desc: "Path to configuration file"
         
     | 
| 
      
 9 
     | 
    
         
            +
                class_option :log, type: :string, desc: "Path to log file"
         
     | 
| 
      
 10 
     | 
    
         
            +
                class_option :repository, type: :string, aliases: "-r", desc: "Repository name (for multi-repo configs)"
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                def initialize(*args)
         
     | 
| 
      
 13 
     | 
    
         
            +
                  super
         
     | 
| 
      
 14 
     | 
    
         
            +
                  # Priority: CLI option > config file > default
         
     | 
| 
      
 15 
     | 
    
         
            +
                  log_path = options[:log]
         
     | 
| 
      
 16 
     | 
    
         
            +
                  unless log_path
         
     | 
| 
      
 17 
     | 
    
         
            +
                    # Try to load config to get log_file setting
         
     | 
| 
      
 18 
     | 
    
         
            +
                    config_path = options[:config] || "ruborg.yml"
         
     | 
| 
      
 19 
     | 
    
         
            +
                    if File.exist?(config_path)
         
     | 
| 
      
 20 
     | 
    
         
            +
                      config_data = YAML.load_file(config_path) rescue {}
         
     | 
| 
      
 21 
     | 
    
         
            +
                      log_path = config_data["log_file"]
         
     | 
| 
      
 22 
     | 
    
         
            +
                    end
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
      
 24 
     | 
    
         
            +
                  @logger = RuborgLogger.new(log_file: log_path)
         
     | 
| 
      
 25 
     | 
    
         
            +
                end
         
     | 
| 
       9 
26 
     | 
    
         | 
| 
       10 
27 
     | 
    
         
             
                desc "init REPOSITORY", "Initialize a new Borg repository"
         
     | 
| 
       11 
28 
     | 
    
         
             
                option :passphrase, type: :string, desc: "Repository passphrase"
         
     | 
| 
       12 
29 
     | 
    
         
             
                option :passbolt_id, type: :string, desc: "Passbolt resource ID for passphrase"
         
     | 
| 
       13 
30 
     | 
    
         
             
                def init(repository_path)
         
     | 
| 
      
 31 
     | 
    
         
            +
                  @logger.info("Initializing repository at #{repository_path}")
         
     | 
| 
       14 
32 
     | 
    
         
             
                  passphrase = get_passphrase(options[:passphrase], options[:passbolt_id])
         
     | 
| 
       15 
33 
     | 
    
         
             
                  repo = Repository.new(repository_path, passphrase: passphrase)
         
     | 
| 
       16 
34 
     | 
    
         
             
                  repo.create
         
     | 
| 
      
 35 
     | 
    
         
            +
                  @logger.info("Repository successfully initialized at #{repository_path}")
         
     | 
| 
       17 
36 
     | 
    
         
             
                  puts "Repository initialized at #{repository_path}"
         
     | 
| 
       18 
37 
     | 
    
         
             
                rescue Error => e
         
     | 
| 
      
 38 
     | 
    
         
            +
                  @logger.error("Failed to initialize repository: #{e.message}")
         
     | 
| 
       19 
39 
     | 
    
         
             
                  error_exit(e)
         
     | 
| 
       20 
40 
     | 
    
         
             
                end
         
     | 
| 
       21 
41 
     | 
    
         | 
| 
       22 
42 
     | 
    
         
             
                desc "backup", "Create a backup using configuration file"
         
     | 
| 
       23 
43 
     | 
    
         
             
                option :name, type: :string, desc: "Archive name"
         
     | 
| 
      
 44 
     | 
    
         
            +
                option :remove_source, type: :boolean, default: false, desc: "Remove source files after successful backup"
         
     | 
| 
      
 45 
     | 
    
         
            +
                option :all, type: :boolean, default: false, desc: "Backup all repositories (multi-repo config only)"
         
     | 
| 
       24 
46 
     | 
    
         
             
                def backup
         
     | 
| 
      
 47 
     | 
    
         
            +
                  @logger.info("Starting backup operation with config: #{options[:config]}")
         
     | 
| 
       25 
48 
     | 
    
         
             
                  config = Config.new(options[:config])
         
     | 
| 
       26 
     | 
    
         
            -
                  passphrase = fetch_passphrase_from_config(config)
         
     | 
| 
       27 
     | 
    
         
            -
             
     | 
| 
       28 
     | 
    
         
            -
                  repo = Repository.new(config.repository, passphrase: passphrase)
         
     | 
| 
       29 
     | 
    
         
            -
                  backup = Backup.new(repo, config: config)
         
     | 
| 
       30 
49 
     | 
    
         | 
| 
       31 
     | 
    
         
            -
                   
     | 
| 
       32 
     | 
    
         
            -
             
     | 
| 
      
 50 
     | 
    
         
            +
                  if config.multi_repo?
         
     | 
| 
      
 51 
     | 
    
         
            +
                    backup_multi_repo(config)
         
     | 
| 
      
 52 
     | 
    
         
            +
                  else
         
     | 
| 
      
 53 
     | 
    
         
            +
                    backup_single_repo(config)
         
     | 
| 
      
 54 
     | 
    
         
            +
                  end
         
     | 
| 
       33 
55 
     | 
    
         
             
                rescue Error => e
         
     | 
| 
      
 56 
     | 
    
         
            +
                  @logger.error("Backup failed: #{e.message}")
         
     | 
| 
       34 
57 
     | 
    
         
             
                  error_exit(e)
         
     | 
| 
       35 
58 
     | 
    
         
             
                end
         
     | 
| 
       36 
59 
     | 
    
         | 
| 
       37 
60 
     | 
    
         
             
                desc "list", "List all archives in the repository"
         
     | 
| 
       38 
61 
     | 
    
         
             
                def list
         
     | 
| 
      
 62 
     | 
    
         
            +
                  @logger.info("Listing archives in repository")
         
     | 
| 
       39 
63 
     | 
    
         
             
                  config = Config.new(options[:config])
         
     | 
| 
       40 
64 
     | 
    
         
             
                  passphrase = fetch_passphrase_from_config(config)
         
     | 
| 
       41 
65 
     | 
    
         | 
| 
       42 
66 
     | 
    
         
             
                  repo = Repository.new(config.repository, passphrase: passphrase)
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                  # Auto-initialize repository if configured
         
     | 
| 
      
 69 
     | 
    
         
            +
                  if config.auto_init? && !repo.exists?
         
     | 
| 
      
 70 
     | 
    
         
            +
                    @logger.info("Auto-initializing repository at #{config.repository}")
         
     | 
| 
      
 71 
     | 
    
         
            +
                    repo.create
         
     | 
| 
      
 72 
     | 
    
         
            +
                    puts "Repository auto-initialized at #{config.repository}"
         
     | 
| 
      
 73 
     | 
    
         
            +
                  end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
       43 
75 
     | 
    
         
             
                  repo.list
         
     | 
| 
      
 76 
     | 
    
         
            +
                  @logger.info("Successfully listed archives")
         
     | 
| 
       44 
77 
     | 
    
         
             
                rescue Error => e
         
     | 
| 
      
 78 
     | 
    
         
            +
                  @logger.error("Failed to list archives: #{e.message}")
         
     | 
| 
       45 
79 
     | 
    
         
             
                  error_exit(e)
         
     | 
| 
       46 
80 
     | 
    
         
             
                end
         
     | 
| 
       47 
81 
     | 
    
         | 
| 
       48 
82 
     | 
    
         
             
                desc "restore ARCHIVE", "Restore files from an archive"
         
     | 
| 
       49 
83 
     | 
    
         
             
                option :destination, type: :string, default: ".", desc: "Destination directory"
         
     | 
| 
      
 84 
     | 
    
         
            +
                option :path, type: :string, desc: "Specific file or directory path to restore from archive"
         
     | 
| 
       50 
85 
     | 
    
         
             
                def restore(archive_name)
         
     | 
| 
      
 86 
     | 
    
         
            +
                  restore_target = options[:path] ? "#{options[:path]} from #{archive_name}" : archive_name
         
     | 
| 
      
 87 
     | 
    
         
            +
                  @logger.info("Restoring #{restore_target} to #{options[:destination]}")
         
     | 
| 
       51 
88 
     | 
    
         
             
                  config = Config.new(options[:config])
         
     | 
| 
       52 
89 
     | 
    
         
             
                  passphrase = fetch_passphrase_from_config(config)
         
     | 
| 
       53 
90 
     | 
    
         | 
| 
       54 
91 
     | 
    
         
             
                  repo = Repository.new(config.repository, passphrase: passphrase)
         
     | 
| 
       55 
92 
     | 
    
         
             
                  backup = Backup.new(repo, config: config)
         
     | 
| 
       56 
93 
     | 
    
         | 
| 
       57 
     | 
    
         
            -
                  backup.extract(archive_name, destination: options[:destination])
         
     | 
| 
       58 
     | 
    
         
            -
                   
     | 
| 
      
 94 
     | 
    
         
            +
                  backup.extract(archive_name, destination: options[:destination], path: options[:path])
         
     | 
| 
      
 95 
     | 
    
         
            +
                  @logger.info("Successfully restored #{restore_target} to #{options[:destination]}")
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
                  if options[:path]
         
     | 
| 
      
 98 
     | 
    
         
            +
                    puts "Restored #{options[:path]} from #{archive_name} to #{options[:destination]}"
         
     | 
| 
      
 99 
     | 
    
         
            +
                  else
         
     | 
| 
      
 100 
     | 
    
         
            +
                    puts "Archive restored to #{options[:destination]}"
         
     | 
| 
      
 101 
     | 
    
         
            +
                  end
         
     | 
| 
       59 
102 
     | 
    
         
             
                rescue Error => e
         
     | 
| 
      
 103 
     | 
    
         
            +
                  @logger.error("Failed to restore archive: #{e.message}")
         
     | 
| 
       60 
104 
     | 
    
         
             
                  error_exit(e)
         
     | 
| 
       61 
105 
     | 
    
         
             
                end
         
     | 
| 
       62 
106 
     | 
    
         | 
| 
       63 
107 
     | 
    
         
             
                desc "info", "Show repository information"
         
     | 
| 
       64 
108 
     | 
    
         
             
                def info
         
     | 
| 
      
 109 
     | 
    
         
            +
                  @logger.info("Retrieving repository information")
         
     | 
| 
       65 
110 
     | 
    
         
             
                  config = Config.new(options[:config])
         
     | 
| 
       66 
111 
     | 
    
         
             
                  passphrase = fetch_passphrase_from_config(config)
         
     | 
| 
       67 
112 
     | 
    
         | 
| 
       68 
113 
     | 
    
         
             
                  repo = Repository.new(config.repository, passphrase: passphrase)
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
                  # Auto-initialize repository if configured
         
     | 
| 
      
 116 
     | 
    
         
            +
                  if config.auto_init? && !repo.exists?
         
     | 
| 
      
 117 
     | 
    
         
            +
                    @logger.info("Auto-initializing repository at #{config.repository}")
         
     | 
| 
      
 118 
     | 
    
         
            +
                    repo.create
         
     | 
| 
      
 119 
     | 
    
         
            +
                    puts "Repository auto-initialized at #{config.repository}"
         
     | 
| 
      
 120 
     | 
    
         
            +
                  end
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
       69 
122 
     | 
    
         
             
                  repo.info
         
     | 
| 
      
 123 
     | 
    
         
            +
                  @logger.info("Successfully retrieved repository information")
         
     | 
| 
       70 
124 
     | 
    
         
             
                rescue Error => e
         
     | 
| 
      
 125 
     | 
    
         
            +
                  @logger.error("Failed to get repository info: #{e.message}")
         
     | 
| 
       71 
126 
     | 
    
         
             
                  error_exit(e)
         
     | 
| 
       72 
127 
     | 
    
         
             
                end
         
     | 
| 
       73 
128 
     | 
    
         | 
| 
         @@ -91,5 +146,128 @@ module Ruborg 
     | 
|
| 
       91 
146 
     | 
    
         
             
                  puts "Error: #{error.message}"
         
     | 
| 
       92 
147 
     | 
    
         
             
                  exit 1
         
     | 
| 
       93 
148 
     | 
    
         
             
                end
         
     | 
| 
      
 149 
     | 
    
         
            +
             
     | 
| 
      
 150 
     | 
    
         
            +
                # Single repository backup (legacy)
         
     | 
| 
      
 151 
     | 
    
         
            +
                def backup_single_repo(config)
         
     | 
| 
      
 152 
     | 
    
         
            +
                  @logger.info("Backing up paths: #{config.backup_paths.join(', ')}")
         
     | 
| 
      
 153 
     | 
    
         
            +
                  passphrase = fetch_passphrase_from_config(config)
         
     | 
| 
      
 154 
     | 
    
         
            +
             
     | 
| 
      
 155 
     | 
    
         
            +
                  repo = Repository.new(config.repository, passphrase: passphrase)
         
     | 
| 
      
 156 
     | 
    
         
            +
             
     | 
| 
      
 157 
     | 
    
         
            +
                  # Auto-initialize repository if configured
         
     | 
| 
      
 158 
     | 
    
         
            +
                  if config.auto_init? && !repo.exists?
         
     | 
| 
      
 159 
     | 
    
         
            +
                    @logger.info("Auto-initializing repository at #{config.repository}")
         
     | 
| 
      
 160 
     | 
    
         
            +
                    repo.create
         
     | 
| 
      
 161 
     | 
    
         
            +
                    puts "Repository auto-initialized at #{config.repository}"
         
     | 
| 
      
 162 
     | 
    
         
            +
                  end
         
     | 
| 
      
 163 
     | 
    
         
            +
             
     | 
| 
      
 164 
     | 
    
         
            +
                  backup = Backup.new(repo, config: config)
         
     | 
| 
      
 165 
     | 
    
         
            +
             
     | 
| 
      
 166 
     | 
    
         
            +
                  archive_name = options[:name] || Time.now.strftime("%Y-%m-%d_%H-%M-%S")
         
     | 
| 
      
 167 
     | 
    
         
            +
                  @logger.info("Creating archive: #{archive_name}")
         
     | 
| 
      
 168 
     | 
    
         
            +
                  backup.create(name: options[:name], remove_source: options[:remove_source])
         
     | 
| 
      
 169 
     | 
    
         
            +
                  @logger.info("Backup created successfully: #{archive_name}")
         
     | 
| 
      
 170 
     | 
    
         
            +
             
     | 
| 
      
 171 
     | 
    
         
            +
                  if options[:remove_source]
         
     | 
| 
      
 172 
     | 
    
         
            +
                    @logger.info("Removed source files: #{config.backup_paths.join(', ')}")
         
     | 
| 
      
 173 
     | 
    
         
            +
                  end
         
     | 
| 
      
 174 
     | 
    
         
            +
             
     | 
| 
      
 175 
     | 
    
         
            +
                  puts "Backup created successfully"
         
     | 
| 
      
 176 
     | 
    
         
            +
                  puts "Source files removed" if options[:remove_source]
         
     | 
| 
      
 177 
     | 
    
         
            +
                end
         
     | 
| 
      
 178 
     | 
    
         
            +
             
     | 
| 
      
 179 
     | 
    
         
            +
                # Multi-repository backup
         
     | 
| 
      
 180 
     | 
    
         
            +
                def backup_multi_repo(config)
         
     | 
| 
      
 181 
     | 
    
         
            +
                  global_settings = config.global_settings
         
     | 
| 
      
 182 
     | 
    
         
            +
                  repos_to_backup = if options[:all]
         
     | 
| 
      
 183 
     | 
    
         
            +
                                      config.repositories
         
     | 
| 
      
 184 
     | 
    
         
            +
                                    elsif options[:repository]
         
     | 
| 
      
 185 
     | 
    
         
            +
                                      repo_config = config.get_repository(options[:repository])
         
     | 
| 
      
 186 
     | 
    
         
            +
                                      raise ConfigError, "Repository '#{options[:repository]}' not found" unless repo_config
         
     | 
| 
      
 187 
     | 
    
         
            +
                                      [repo_config]
         
     | 
| 
      
 188 
     | 
    
         
            +
                                    else
         
     | 
| 
      
 189 
     | 
    
         
            +
                                      raise ConfigError, "Please specify --repository or --all for multi-repo config"
         
     | 
| 
      
 190 
     | 
    
         
            +
                                    end
         
     | 
| 
      
 191 
     | 
    
         
            +
             
     | 
| 
      
 192 
     | 
    
         
            +
                  repos_to_backup.each do |repo_config|
         
     | 
| 
      
 193 
     | 
    
         
            +
                    backup_repository(repo_config, global_settings)
         
     | 
| 
      
 194 
     | 
    
         
            +
                  end
         
     | 
| 
      
 195 
     | 
    
         
            +
                end
         
     | 
| 
      
 196 
     | 
    
         
            +
             
     | 
| 
      
 197 
     | 
    
         
            +
                def backup_repository(repo_config, global_settings)
         
     | 
| 
      
 198 
     | 
    
         
            +
                  repo_name = repo_config["name"]
         
     | 
| 
      
 199 
     | 
    
         
            +
                  puts "\n--- Backing up repository: #{repo_name} ---"
         
     | 
| 
      
 200 
     | 
    
         
            +
                  @logger.info("Backing up repository: #{repo_name}")
         
     | 
| 
      
 201 
     | 
    
         
            +
             
     | 
| 
      
 202 
     | 
    
         
            +
                  # Merge global settings with repo-specific settings (repo-specific takes precedence)
         
     | 
| 
      
 203 
     | 
    
         
            +
                  merged_config = global_settings.merge(repo_config)
         
     | 
| 
      
 204 
     | 
    
         
            +
             
     | 
| 
      
 205 
     | 
    
         
            +
                  passphrase = fetch_passphrase_for_repo(merged_config)
         
     | 
| 
      
 206 
     | 
    
         
            +
                  repo = Repository.new(repo_config["path"], passphrase: passphrase)
         
     | 
| 
      
 207 
     | 
    
         
            +
             
     | 
| 
      
 208 
     | 
    
         
            +
                  # Auto-initialize if configured
         
     | 
| 
      
 209 
     | 
    
         
            +
                  auto_init = merged_config["auto_init"] || false
         
     | 
| 
      
 210 
     | 
    
         
            +
                  if auto_init && !repo.exists?
         
     | 
| 
      
 211 
     | 
    
         
            +
                    @logger.info("Auto-initializing repository at #{repo_config['path']}")
         
     | 
| 
      
 212 
     | 
    
         
            +
                    repo.create
         
     | 
| 
      
 213 
     | 
    
         
            +
                    puts "Repository auto-initialized at #{repo_config['path']}"
         
     | 
| 
      
 214 
     | 
    
         
            +
                  end
         
     | 
| 
      
 215 
     | 
    
         
            +
             
     | 
| 
      
 216 
     | 
    
         
            +
                  # Create backup config wrapper
         
     | 
| 
      
 217 
     | 
    
         
            +
                  backup_config = BackupConfig.new(repo_config, merged_config)
         
     | 
| 
      
 218 
     | 
    
         
            +
                  backup = Backup.new(repo, config: backup_config)
         
     | 
| 
      
 219 
     | 
    
         
            +
             
     | 
| 
      
 220 
     | 
    
         
            +
                  archive_name = options[:name] || "#{repo_name}-#{Time.now.strftime('%Y-%m-%d_%H-%M-%S')}"
         
     | 
| 
      
 221 
     | 
    
         
            +
                  @logger.info("Creating archive: #{archive_name}")
         
     | 
| 
      
 222 
     | 
    
         
            +
             
     | 
| 
      
 223 
     | 
    
         
            +
                  sources = repo_config["sources"] || []
         
     | 
| 
      
 224 
     | 
    
         
            +
                  @logger.info("Backing up #{sources.size} source(s)")
         
     | 
| 
      
 225 
     | 
    
         
            +
             
     | 
| 
      
 226 
     | 
    
         
            +
                  backup.create(name: archive_name, remove_source: options[:remove_source])
         
     | 
| 
      
 227 
     | 
    
         
            +
                  @logger.info("Backup created successfully: #{archive_name}")
         
     | 
| 
      
 228 
     | 
    
         
            +
             
     | 
| 
      
 229 
     | 
    
         
            +
                  puts "✓ Backup created: #{archive_name}"
         
     | 
| 
      
 230 
     | 
    
         
            +
                  puts "  Sources removed" if options[:remove_source]
         
     | 
| 
      
 231 
     | 
    
         
            +
                end
         
     | 
| 
      
 232 
     | 
    
         
            +
             
     | 
| 
      
 233 
     | 
    
         
            +
                def fetch_passphrase_for_repo(repo_config)
         
     | 
| 
      
 234 
     | 
    
         
            +
                  passbolt_config = repo_config["passbolt"]
         
     | 
| 
      
 235 
     | 
    
         
            +
                  return nil if passbolt_config.nil? || passbolt_config.empty?
         
     | 
| 
      
 236 
     | 
    
         
            +
             
     | 
| 
      
 237 
     | 
    
         
            +
                  Passbolt.new(resource_id: passbolt_config["resource_id"]).get_password
         
     | 
| 
      
 238 
     | 
    
         
            +
                end
         
     | 
| 
      
 239 
     | 
    
         
            +
             
     | 
| 
      
 240 
     | 
    
         
            +
                # Wrapper class to adapt multi-repo config to existing Backup class
         
     | 
| 
      
 241 
     | 
    
         
            +
                class BackupConfig
         
     | 
| 
      
 242 
     | 
    
         
            +
                  def initialize(repo_config, merged_settings)
         
     | 
| 
      
 243 
     | 
    
         
            +
                    @repo_config = repo_config
         
     | 
| 
      
 244 
     | 
    
         
            +
                    @merged_settings = merged_settings
         
     | 
| 
      
 245 
     | 
    
         
            +
                  end
         
     | 
| 
      
 246 
     | 
    
         
            +
             
     | 
| 
      
 247 
     | 
    
         
            +
                  def backup_paths
         
     | 
| 
      
 248 
     | 
    
         
            +
                    sources = @repo_config["sources"] || []
         
     | 
| 
      
 249 
     | 
    
         
            +
                    sources.flat_map do |source|
         
     | 
| 
      
 250 
     | 
    
         
            +
                      source["paths"] || []
         
     | 
| 
      
 251 
     | 
    
         
            +
                    end
         
     | 
| 
      
 252 
     | 
    
         
            +
                  end
         
     | 
| 
      
 253 
     | 
    
         
            +
             
     | 
| 
      
 254 
     | 
    
         
            +
                  def exclude_patterns
         
     | 
| 
      
 255 
     | 
    
         
            +
                    patterns = []
         
     | 
| 
      
 256 
     | 
    
         
            +
                    sources = @repo_config["sources"] || []
         
     | 
| 
      
 257 
     | 
    
         
            +
                    sources.each do |source|
         
     | 
| 
      
 258 
     | 
    
         
            +
                      patterns += (source["exclude"] || [])
         
     | 
| 
      
 259 
     | 
    
         
            +
                    end
         
     | 
| 
      
 260 
     | 
    
         
            +
                    patterns += (@merged_settings["exclude_patterns"] || [])
         
     | 
| 
      
 261 
     | 
    
         
            +
                    patterns.uniq
         
     | 
| 
      
 262 
     | 
    
         
            +
                  end
         
     | 
| 
      
 263 
     | 
    
         
            +
             
     | 
| 
      
 264 
     | 
    
         
            +
                  def compression
         
     | 
| 
      
 265 
     | 
    
         
            +
                    @merged_settings["compression"] || "lz4"
         
     | 
| 
      
 266 
     | 
    
         
            +
                  end
         
     | 
| 
      
 267 
     | 
    
         
            +
             
     | 
| 
      
 268 
     | 
    
         
            +
                  def encryption_mode
         
     | 
| 
      
 269 
     | 
    
         
            +
                    @merged_settings["encryption"] || "repokey"
         
     | 
| 
      
 270 
     | 
    
         
            +
                  end
         
     | 
| 
      
 271 
     | 
    
         
            +
                end
         
     | 
| 
       94 
272 
     | 
    
         
             
              end
         
     | 
| 
       95 
273 
     | 
    
         
             
            end
         
     | 
    
        data/lib/ruborg/config.rb
    CHANGED
    
    | 
         @@ -11,6 +11,7 @@ module Ruborg 
     | 
|
| 
       11 
11 
     | 
    
         
             
                def initialize(config_path)
         
     | 
| 
       12 
12 
     | 
    
         
             
                  @config_path = config_path
         
     | 
| 
       13 
13 
     | 
    
         
             
                  load_config
         
     | 
| 
      
 14 
     | 
    
         
            +
                  detect_format
         
     | 
| 
       14 
15 
     | 
    
         
             
                end
         
     | 
| 
       15 
16 
     | 
    
         | 
| 
       16 
17 
     | 
    
         
             
                def load_config
         
     | 
| 
         @@ -21,6 +22,7 @@ module Ruborg 
     | 
|
| 
       21 
22 
     | 
    
         
             
                  raise ConfigError, "Invalid YAML syntax: #{e.message}"
         
     | 
| 
       22 
23 
     | 
    
         
             
                end
         
     | 
| 
       23 
24 
     | 
    
         | 
| 
      
 25 
     | 
    
         
            +
                # Legacy single-repo accessors (for backward compatibility)
         
     | 
| 
       24 
26 
     | 
    
         
             
                def repository
         
     | 
| 
       25 
27 
     | 
    
         
             
                  @data["repository"]
         
     | 
| 
       26 
28 
     | 
    
         
             
                end
         
     | 
| 
         @@ -44,5 +46,43 @@ module Ruborg 
     | 
|
| 
       44 
46 
     | 
    
         
             
                def passbolt_integration
         
     | 
| 
       45 
47 
     | 
    
         
             
                  @data["passbolt"] || {}
         
     | 
| 
       46 
48 
     | 
    
         
             
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                def auto_init?
         
     | 
| 
      
 51 
     | 
    
         
            +
                  @data["auto_init"] || false
         
     | 
| 
      
 52 
     | 
    
         
            +
                end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                def log_file
         
     | 
| 
      
 55 
     | 
    
         
            +
                  @data["log_file"]
         
     | 
| 
      
 56 
     | 
    
         
            +
                end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                # New multi-repo support
         
     | 
| 
      
 59 
     | 
    
         
            +
                def multi_repo?
         
     | 
| 
      
 60 
     | 
    
         
            +
                  @multi_repo
         
     | 
| 
      
 61 
     | 
    
         
            +
                end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                def repositories
         
     | 
| 
      
 64 
     | 
    
         
            +
                  return [] unless multi_repo?
         
     | 
| 
      
 65 
     | 
    
         
            +
                  @data["repositories"] || []
         
     | 
| 
      
 66 
     | 
    
         
            +
                end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                def get_repository(name)
         
     | 
| 
      
 69 
     | 
    
         
            +
                  return nil unless multi_repo?
         
     | 
| 
      
 70 
     | 
    
         
            +
                  repositories.find { |r| r["name"] == name }
         
     | 
| 
      
 71 
     | 
    
         
            +
                end
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                def repository_names
         
     | 
| 
      
 74 
     | 
    
         
            +
                  return [] unless multi_repo?
         
     | 
| 
      
 75 
     | 
    
         
            +
                  repositories.map { |r| r["name"] }
         
     | 
| 
      
 76 
     | 
    
         
            +
                end
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                def global_settings
         
     | 
| 
      
 79 
     | 
    
         
            +
                  @data.slice("passbolt", "compression", "encryption", "auto_init")
         
     | 
| 
      
 80 
     | 
    
         
            +
                end
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                private
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                def detect_format
         
     | 
| 
      
 85 
     | 
    
         
            +
                  @multi_repo = @data.key?("repositories")
         
     | 
| 
      
 86 
     | 
    
         
            +
                end
         
     | 
| 
       47 
87 
     | 
    
         
             
              end
         
     | 
| 
       48 
88 
     | 
    
         
             
            end
         
     | 
| 
         @@ -0,0 +1,52 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require "logger"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require "fileutils"
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            module Ruborg
         
     | 
| 
      
 7 
     | 
    
         
            +
              # Logging functionality for ruborg
         
     | 
| 
      
 8 
     | 
    
         
            +
              class RuborgLogger
         
     | 
| 
      
 9 
     | 
    
         
            +
                attr_reader :logger
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                def initialize(log_file: nil)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  @log_file = log_file || default_log_file
         
     | 
| 
      
 13 
     | 
    
         
            +
                  ensure_log_directory
         
     | 
| 
      
 14 
     | 
    
         
            +
                  @logger = Logger.new(@log_file, "daily")
         
     | 
| 
      
 15 
     | 
    
         
            +
                  @logger.level = Logger::INFO
         
     | 
| 
      
 16 
     | 
    
         
            +
                  @logger.formatter = proc do |severity, datetime, progname, msg|
         
     | 
| 
      
 17 
     | 
    
         
            +
                    "[#{datetime.strftime('%Y-%m-%d %H:%M:%S')}] #{severity}: #{msg}\n"
         
     | 
| 
      
 18 
     | 
    
         
            +
                  end
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                def info(message)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  @logger.info(message)
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                def error(message)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  @logger.error(message)
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                def warn(message)
         
     | 
| 
      
 30 
     | 
    
         
            +
                  @logger.warn(message)
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                def debug(message)
         
     | 
| 
      
 34 
     | 
    
         
            +
                  @logger.debug(message)
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                private
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                def default_log_file
         
     | 
| 
      
 40 
     | 
    
         
            +
                  File.join(log_directory, "ruborg.log")
         
     | 
| 
      
 41 
     | 
    
         
            +
                end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                def log_directory
         
     | 
| 
      
 44 
     | 
    
         
            +
                  dir = File.expand_path("~/.ruborg/logs")
         
     | 
| 
      
 45 
     | 
    
         
            +
                  dir
         
     | 
| 
      
 46 
     | 
    
         
            +
                end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                def ensure_log_directory
         
     | 
| 
      
 49 
     | 
    
         
            +
                  FileUtils.mkdir_p(File.dirname(@log_file)) unless File.directory?(File.dirname(@log_file))
         
     | 
| 
      
 50 
     | 
    
         
            +
                end
         
     | 
| 
      
 51 
     | 
    
         
            +
              end
         
     | 
| 
      
 52 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/ruborg/passbolt.rb
    CHANGED
    
    | 
         @@ -13,10 +13,10 @@ module Ruborg 
     | 
|
| 
       13 
13 
     | 
    
         
             
                def get_password
         
     | 
| 
       14 
14 
     | 
    
         
             
                  raise PassboltError, "Resource ID not configured" unless @resource_id
         
     | 
| 
       15 
15 
     | 
    
         | 
| 
       16 
     | 
    
         
            -
                  cmd = ["passbolt", "get", @resource_id, "--json"]
         
     | 
| 
       17 
     | 
    
         
            -
                  output =  
     | 
| 
      
 16 
     | 
    
         
            +
                  cmd = ["passbolt", "get", "resource", @resource_id, "--json"]
         
     | 
| 
      
 17 
     | 
    
         
            +
                  output, status = execute_command(cmd)
         
     | 
| 
       18 
18 
     | 
    
         | 
| 
       19 
     | 
    
         
            -
                  raise PassboltError, "Failed to retrieve password from Passbolt" unless  
     | 
| 
      
 19 
     | 
    
         
            +
                  raise PassboltError, "Failed to retrieve password from Passbolt" unless status
         
     | 
| 
       20 
20 
     | 
    
         | 
| 
       21 
21 
     | 
    
         
             
                  parse_password(output)
         
     | 
| 
       22 
22 
     | 
    
         
             
                end
         
     | 
| 
         @@ -29,6 +29,11 @@ module Ruborg 
     | 
|
| 
       29 
29 
     | 
    
         
             
                  end
         
     | 
| 
       30 
30 
     | 
    
         
             
                end
         
     | 
| 
       31 
31 
     | 
    
         | 
| 
      
 32 
     | 
    
         
            +
                def execute_command(cmd)
         
     | 
| 
      
 33 
     | 
    
         
            +
                  output = `#{cmd.join(' ')}`
         
     | 
| 
      
 34 
     | 
    
         
            +
                  [output, $?.success?]
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
       32 
37 
     | 
    
         
             
                def parse_password(json_output)
         
     | 
| 
       33 
38 
     | 
    
         
             
                  data = JSON.parse(json_output)
         
     | 
| 
       34 
39 
     | 
    
         
             
                  data["password"] || data["secret"]
         
     | 
    
        data/lib/ruborg/repository.rb
    CHANGED
    
    | 
         @@ -40,8 +40,11 @@ module Ruborg 
     | 
|
| 
       40 
40 
     | 
    
         
             
                def execute_borg_command(cmd)
         
     | 
| 
       41 
41 
     | 
    
         
             
                  env = {}
         
     | 
| 
       42 
42 
     | 
    
         
             
                  env["BORG_PASSPHRASE"] = @passphrase if @passphrase
         
     | 
| 
      
 43 
     | 
    
         
            +
                  env["BORG_RELOCATED_REPO_ACCESS_IS_OK"] = "yes"
         
     | 
| 
      
 44 
     | 
    
         
            +
                  env["BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK"] = "yes"
         
     | 
| 
       43 
45 
     | 
    
         | 
| 
       44 
     | 
    
         
            -
                   
     | 
| 
      
 46 
     | 
    
         
            +
                  # Redirect stdin from /dev/null to prevent interactive prompts
         
     | 
| 
      
 47 
     | 
    
         
            +
                  result = system(env, *cmd, in: "/dev/null")
         
     | 
| 
       45 
48 
     | 
    
         
             
                  raise BorgError, "Borg command failed: #{cmd.join(' ')}" unless result
         
     | 
| 
       46 
49 
     | 
    
         | 
| 
       47 
50 
     | 
    
         
             
                  result
         
     | 
    
        data/lib/ruborg/version.rb
    CHANGED
    
    
    
        data/lib/ruborg.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: ruborg
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 0. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.3.0
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Michail Pantelelis
         
     | 
| 
         @@ -115,10 +115,10 @@ files: 
     | 
|
| 
       115 
115 
     | 
    
         
             
            - lib/ruborg/backup.rb
         
     | 
| 
       116 
116 
     | 
    
         
             
            - lib/ruborg/cli.rb
         
     | 
| 
       117 
117 
     | 
    
         
             
            - lib/ruborg/config.rb
         
     | 
| 
      
 118 
     | 
    
         
            +
            - lib/ruborg/logger.rb
         
     | 
| 
       118 
119 
     | 
    
         
             
            - lib/ruborg/passbolt.rb
         
     | 
| 
       119 
120 
     | 
    
         
             
            - lib/ruborg/repository.rb
         
     | 
| 
       120 
121 
     | 
    
         
             
            - lib/ruborg/version.rb
         
     | 
| 
       121 
     | 
    
         
            -
            - ruborg.gemspec
         
     | 
| 
       122 
122 
     | 
    
         
             
            - ruborg.yml.example
         
     | 
| 
       123 
123 
     | 
    
         
             
            homepage: https://github.com/mpantel/ruborg
         
     | 
| 
       124 
124 
     | 
    
         
             
            licenses:
         
     | 
    
        data/ruborg.gemspec
    DELETED
    
    | 
         @@ -1,41 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            # frozen_string_literal: true
         
     | 
| 
       2 
     | 
    
         
            -
             
     | 
| 
       3 
     | 
    
         
            -
            require_relative "lib/ruborg/version"
         
     | 
| 
       4 
     | 
    
         
            -
             
     | 
| 
       5 
     | 
    
         
            -
            Gem::Specification.new do |spec|
         
     | 
| 
       6 
     | 
    
         
            -
              spec.name = "ruborg"
         
     | 
| 
       7 
     | 
    
         
            -
              spec.version = Ruborg::VERSION
         
     | 
| 
       8 
     | 
    
         
            -
              spec.authors = ["Michail Pantelelis"]
         
     | 
| 
       9 
     | 
    
         
            -
              spec.email = ["mpantel@aegean.gr"]
         
     | 
| 
       10 
     | 
    
         
            -
             
     | 
| 
       11 
     | 
    
         
            -
              spec.summary = "A friendly Ruby frontend for Borg backup"
         
     | 
| 
       12 
     | 
    
         
            -
              spec.description = "Ruborg is a Ruby gem that provides a user-friendly interface to Borg backup. It reads YAML configuration files and orchestrates backup operations, supporting repository creation, backup management, and integration with Passbolt for encryption password management."
         
     | 
| 
       13 
     | 
    
         
            -
              spec.homepage = "https://github.com/mpantel/ruborg"
         
     | 
| 
       14 
     | 
    
         
            -
              spec.license = "MIT"
         
     | 
| 
       15 
     | 
    
         
            -
              spec.required_ruby_version = ">= 3.2.0"
         
     | 
| 
       16 
     | 
    
         
            -
             
     | 
| 
       17 
     | 
    
         
            -
              spec.metadata["homepage_uri"] = spec.homepage
         
     | 
| 
       18 
     | 
    
         
            -
              spec.metadata["source_code_uri"] = "#{spec.homepage}.git"
         
     | 
| 
       19 
     | 
    
         
            -
              spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
         
     | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
       21 
     | 
    
         
            -
              # Specify which files should be added to the gem when it is released.
         
     | 
| 
       22 
     | 
    
         
            -
              spec.files = Dir.chdir(__dir__) do
         
     | 
| 
       23 
     | 
    
         
            -
                `git ls-files -z`.split("\x0").reject do |f|
         
     | 
| 
       24 
     | 
    
         
            -
                  (File.expand_path(f) == __FILE__) ||
         
     | 
| 
       25 
     | 
    
         
            -
                    f.start_with?(*%w[bin/ test/ spec/ features/ .git .github appveyor Gemfile])
         
     | 
| 
       26 
     | 
    
         
            -
                end
         
     | 
| 
       27 
     | 
    
         
            -
              end
         
     | 
| 
       28 
     | 
    
         
            -
              spec.bindir = "exe"
         
     | 
| 
       29 
     | 
    
         
            -
              spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
         
     | 
| 
       30 
     | 
    
         
            -
              spec.require_paths = ["lib"]
         
     | 
| 
       31 
     | 
    
         
            -
             
     | 
| 
       32 
     | 
    
         
            -
              # Dependencies
         
     | 
| 
       33 
     | 
    
         
            -
              spec.add_dependency "thor", "~> 1.3"
         
     | 
| 
       34 
     | 
    
         
            -
              spec.add_dependency "psych", "~> 5.0"
         
     | 
| 
       35 
     | 
    
         
            -
             
     | 
| 
       36 
     | 
    
         
            -
              # Development dependencies
         
     | 
| 
       37 
     | 
    
         
            -
              spec.add_development_dependency "bundler", "~> 2.0"
         
     | 
| 
       38 
     | 
    
         
            -
              spec.add_development_dependency "rake", "~> 13.0"
         
     | 
| 
       39 
     | 
    
         
            -
              spec.add_development_dependency "rspec", "~> 3.0"
         
     | 
| 
       40 
     | 
    
         
            -
              spec.add_development_dependency "rubocop", "~> 1.0"
         
     | 
| 
       41 
     | 
    
         
            -
            end
         
     |