ruborg 0.1.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 +7 -0
- data/.rspec +3 -0
- data/CHANGELOG.md +21 -0
- data/CLAUDE.md +1 -0
- data/LICENSE +21 -0
- data/README.md +186 -0
- data/Rakefile +8 -0
- data/exe/ruborg +6 -0
- data/lib/ruborg/backup.rb +61 -0
- data/lib/ruborg/cli.rb +95 -0
- data/lib/ruborg/config.rb +48 -0
- data/lib/ruborg/passbolt.rb +39 -0
- data/lib/ruborg/repository.rb +50 -0
- data/lib/ruborg/version.rb +5 -0
- data/lib/ruborg.rb +15 -0
- data/ruborg.gemspec +41 -0
- data/ruborg.yml.example +28 -0
- metadata +147 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 2e48935c72c444956ca0b7918efb14bd3d5b8a38273ec6843adec2fbb2da6968
|
|
4
|
+
data.tar.gz: f200cb546d62431ec1ff84544da0b96b9f84cf7877aa29627608f8c43119d2b7
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 4614d7f78363f420ae4ce80355725fb84e068bfac0d94591f1cee56a6d14c7c6664dd415405462086bdf2fa7543187702f346645655b2bb32b5ffb62fea288a9
|
|
7
|
+
data.tar.gz: f70898563085b03b78cf4cf140abb5efd9bd3bd6881918aa7a77c976823a15469688e3aeafd6814c4ff880b3e5c8f949264a1c03f9b1251f3467d409ebe1f302
|
data/.rspec
ADDED
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Initial gem structure
|
|
12
|
+
- Borg repository initialization and management
|
|
13
|
+
- Backup creation and restoration
|
|
14
|
+
- YAML configuration file support
|
|
15
|
+
- Passbolt CLI integration for password management
|
|
16
|
+
- Command-line interface with Thor
|
|
17
|
+
|
|
18
|
+
## [0.1.0] - 2025-10-04
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
- Initial release
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
- ruborg is a ruby gem to perform backups using borg. it reads a configuration file in yaml and instructs borg about what to do. it is a friendly fornt end of borg in ruby. it can create and access backup repositories. it can take and recall backup files or directories. it can interract with passbolt through cli to access encryption passwords.
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Michail
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# Ruborg
|
|
2
|
+
|
|
3
|
+
> **⚠️ WARNING: This project is under heavy development and is not yet functional. Do not use in production.**
|
|
4
|
+
>
|
|
5
|
+
> This gem is being developed with the assistance of Claude AI.
|
|
6
|
+
|
|
7
|
+
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.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- 📦 **Repository Management** - Create and manage Borg backup repositories
|
|
12
|
+
- 💾 **Backup & Restore** - Easy backup creation and archive restoration
|
|
13
|
+
- 📝 **YAML Configuration** - Simple, readable configuration files
|
|
14
|
+
- 🔐 **Passbolt Integration** - Secure password management via Passbolt CLI
|
|
15
|
+
- 🎯 **Pattern Exclusions** - Flexible file exclusion patterns
|
|
16
|
+
- 🗜️ **Compression Options** - Support for multiple compression algorithms
|
|
17
|
+
|
|
18
|
+
## Prerequisites
|
|
19
|
+
|
|
20
|
+
- Ruby >= 2.7.0
|
|
21
|
+
- [Borg Backup](https://www.borgbackup.org/) installed and available in PATH
|
|
22
|
+
- [Passbolt CLI](https://github.com/passbolt/go-passbolt-cli) (optional, for password management)
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
Add this line to your application's Gemfile:
|
|
27
|
+
|
|
28
|
+
```ruby
|
|
29
|
+
gem 'ruborg'
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
And then execute:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
bundle install
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Or install it yourself as:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
gem install ruborg
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Configuration
|
|
45
|
+
|
|
46
|
+
Create a `ruborg.yml` configuration file:
|
|
47
|
+
|
|
48
|
+
```yaml
|
|
49
|
+
# Repository path
|
|
50
|
+
repository: /path/to/borg/repository
|
|
51
|
+
|
|
52
|
+
# Paths to backup
|
|
53
|
+
backup_paths:
|
|
54
|
+
- /home/user/documents
|
|
55
|
+
- /home/user/projects
|
|
56
|
+
- /etc
|
|
57
|
+
|
|
58
|
+
# Exclude patterns
|
|
59
|
+
exclude_patterns:
|
|
60
|
+
- "*.tmp"
|
|
61
|
+
- "*.log"
|
|
62
|
+
- "*/.cache/*"
|
|
63
|
+
- "*/node_modules/*"
|
|
64
|
+
- "*/.git/*"
|
|
65
|
+
|
|
66
|
+
# Compression algorithm (lz4, zstd, zlib, lzma, none)
|
|
67
|
+
compression: lz4
|
|
68
|
+
|
|
69
|
+
# Encryption mode (repokey, keyfile, none)
|
|
70
|
+
encryption: repokey
|
|
71
|
+
|
|
72
|
+
# Passbolt integration (optional)
|
|
73
|
+
passbolt:
|
|
74
|
+
resource_id: "your-passbolt-resource-uuid"
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
See `ruborg.yml.example` for a complete configuration template.
|
|
78
|
+
|
|
79
|
+
## Usage
|
|
80
|
+
|
|
81
|
+
### Initialize a Repository
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
# With passphrase
|
|
85
|
+
ruborg init /path/to/repository --passphrase "your-passphrase"
|
|
86
|
+
|
|
87
|
+
# With Passbolt
|
|
88
|
+
ruborg init /path/to/repository --passbolt-id "resource-uuid"
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Create a Backup
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
# Using default configuration (ruborg.yml)
|
|
95
|
+
ruborg backup
|
|
96
|
+
|
|
97
|
+
# Using custom configuration file
|
|
98
|
+
ruborg backup --config /path/to/config.yml
|
|
99
|
+
|
|
100
|
+
# With custom archive name
|
|
101
|
+
ruborg backup --name "my-backup-2025-10-04"
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### List Archives
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
ruborg list
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Restore from Archive
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
# Restore to current directory
|
|
114
|
+
ruborg restore archive-name
|
|
115
|
+
|
|
116
|
+
# Restore to specific directory
|
|
117
|
+
ruborg restore archive-name --destination /path/to/restore
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### View Repository Information
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
ruborg info
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Passbolt Integration
|
|
127
|
+
|
|
128
|
+
Ruborg can retrieve encryption passphrases from Passbolt using the Passbolt CLI:
|
|
129
|
+
|
|
130
|
+
1. Install and configure [Passbolt CLI](https://github.com/passbolt/go-passbolt-cli)
|
|
131
|
+
2. Store your Borg repository passphrase in Passbolt
|
|
132
|
+
3. Add the resource ID to your `ruborg.yml`:
|
|
133
|
+
|
|
134
|
+
```yaml
|
|
135
|
+
passbolt:
|
|
136
|
+
resource_id: "your-passbolt-resource-uuid"
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Ruborg will automatically retrieve the passphrase when performing backup operations.
|
|
140
|
+
|
|
141
|
+
## Command Reference
|
|
142
|
+
|
|
143
|
+
| Command | Description | Options |
|
|
144
|
+
|---------|-------------|---------|
|
|
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` |
|
|
150
|
+
|
|
151
|
+
## Development
|
|
152
|
+
|
|
153
|
+
After checking out the repo, run `bundle install` to install dependencies. Then, run `rake spec` to run the tests.
|
|
154
|
+
|
|
155
|
+
To install this gem onto your local machine, run:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
bundle exec rake install
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
To release a new version, update the version number in `lib/ruborg/version.rb`, and then run:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
bundle exec rake release
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Testing
|
|
168
|
+
|
|
169
|
+
Run the test suite:
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
bundle exec rspec
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Contributing
|
|
176
|
+
|
|
177
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/mpantel/ruborg.
|
|
178
|
+
|
|
179
|
+
## License
|
|
180
|
+
|
|
181
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
182
|
+
|
|
183
|
+
## Acknowledgments
|
|
184
|
+
|
|
185
|
+
- [Borg Backup](https://www.borgbackup.org/) - The excellent backup tool this gem wraps
|
|
186
|
+
- [Passbolt](https://www.passbolt.com/) - Secure password management
|
data/Rakefile
ADDED
data/exe/ruborg
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ruborg
|
|
4
|
+
# Backup operations using Borg
|
|
5
|
+
class Backup
|
|
6
|
+
def initialize(repository, config:)
|
|
7
|
+
@repository = repository
|
|
8
|
+
@config = config
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def create(name: nil)
|
|
12
|
+
raise BorgError, "Repository does not exist" unless @repository.exists?
|
|
13
|
+
|
|
14
|
+
archive_name = name || Time.now.strftime("%Y-%m-%d_%H-%M-%S")
|
|
15
|
+
cmd = build_create_command(archive_name)
|
|
16
|
+
|
|
17
|
+
execute_borg_command(cmd)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def extract(archive_name, destination: ".")
|
|
21
|
+
raise BorgError, "Repository does not exist" unless @repository.exists?
|
|
22
|
+
|
|
23
|
+
cmd = ["borg", "extract", "#{@repository.path}::#{archive_name}"]
|
|
24
|
+
cmd += ["--destination", destination] if destination != "."
|
|
25
|
+
|
|
26
|
+
execute_borg_command(cmd)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def list_archives
|
|
30
|
+
@repository.list
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def delete(archive_name)
|
|
34
|
+
cmd = ["borg", "delete", "#{@repository.path}::#{archive_name}"]
|
|
35
|
+
execute_borg_command(cmd)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def build_create_command(archive_name)
|
|
41
|
+
cmd = ["borg", "create"]
|
|
42
|
+
cmd += ["--compression", @config.compression]
|
|
43
|
+
|
|
44
|
+
@config.exclude_patterns.each do |pattern|
|
|
45
|
+
cmd += ["--exclude", pattern]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
cmd << "#{@repository.path}::#{archive_name}"
|
|
49
|
+
cmd += @config.backup_paths
|
|
50
|
+
|
|
51
|
+
cmd
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def execute_borg_command(cmd)
|
|
55
|
+
result = system(*cmd)
|
|
56
|
+
raise BorgError, "Borg command failed: #{cmd.join(' ')}" unless result
|
|
57
|
+
|
|
58
|
+
result
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
data/lib/ruborg/cli.rb
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "thor"
|
|
4
|
+
|
|
5
|
+
module Ruborg
|
|
6
|
+
# Command-line interface for ruborg
|
|
7
|
+
class CLI < Thor
|
|
8
|
+
class_option :config, type: :string, default: "ruborg.yml", desc: "Path to configuration file"
|
|
9
|
+
|
|
10
|
+
desc "init REPOSITORY", "Initialize a new Borg repository"
|
|
11
|
+
option :passphrase, type: :string, desc: "Repository passphrase"
|
|
12
|
+
option :passbolt_id, type: :string, desc: "Passbolt resource ID for passphrase"
|
|
13
|
+
def init(repository_path)
|
|
14
|
+
passphrase = get_passphrase(options[:passphrase], options[:passbolt_id])
|
|
15
|
+
repo = Repository.new(repository_path, passphrase: passphrase)
|
|
16
|
+
repo.create
|
|
17
|
+
puts "Repository initialized at #{repository_path}"
|
|
18
|
+
rescue Error => e
|
|
19
|
+
error_exit(e)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
desc "backup", "Create a backup using configuration file"
|
|
23
|
+
option :name, type: :string, desc: "Archive name"
|
|
24
|
+
def backup
|
|
25
|
+
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
|
+
|
|
31
|
+
backup.create(name: options[:name])
|
|
32
|
+
puts "Backup created successfully"
|
|
33
|
+
rescue Error => e
|
|
34
|
+
error_exit(e)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
desc "list", "List all archives in the repository"
|
|
38
|
+
def list
|
|
39
|
+
config = Config.new(options[:config])
|
|
40
|
+
passphrase = fetch_passphrase_from_config(config)
|
|
41
|
+
|
|
42
|
+
repo = Repository.new(config.repository, passphrase: passphrase)
|
|
43
|
+
repo.list
|
|
44
|
+
rescue Error => e
|
|
45
|
+
error_exit(e)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
desc "restore ARCHIVE", "Restore files from an archive"
|
|
49
|
+
option :destination, type: :string, default: ".", desc: "Destination directory"
|
|
50
|
+
def restore(archive_name)
|
|
51
|
+
config = Config.new(options[:config])
|
|
52
|
+
passphrase = fetch_passphrase_from_config(config)
|
|
53
|
+
|
|
54
|
+
repo = Repository.new(config.repository, passphrase: passphrase)
|
|
55
|
+
backup = Backup.new(repo, config: config)
|
|
56
|
+
|
|
57
|
+
backup.extract(archive_name, destination: options[:destination])
|
|
58
|
+
puts "Archive restored to #{options[:destination]}"
|
|
59
|
+
rescue Error => e
|
|
60
|
+
error_exit(e)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
desc "info", "Show repository information"
|
|
64
|
+
def info
|
|
65
|
+
config = Config.new(options[:config])
|
|
66
|
+
passphrase = fetch_passphrase_from_config(config)
|
|
67
|
+
|
|
68
|
+
repo = Repository.new(config.repository, passphrase: passphrase)
|
|
69
|
+
repo.info
|
|
70
|
+
rescue Error => e
|
|
71
|
+
error_exit(e)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
private
|
|
75
|
+
|
|
76
|
+
def get_passphrase(passphrase, passbolt_id)
|
|
77
|
+
return passphrase if passphrase
|
|
78
|
+
return Passbolt.new(resource_id: passbolt_id).get_password if passbolt_id
|
|
79
|
+
|
|
80
|
+
nil
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def fetch_passphrase_from_config(config)
|
|
84
|
+
passbolt_config = config.passbolt_integration
|
|
85
|
+
return nil if passbolt_config.empty?
|
|
86
|
+
|
|
87
|
+
Passbolt.new(resource_id: passbolt_config["resource_id"]).get_password
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def error_exit(error)
|
|
91
|
+
puts "Error: #{error.message}"
|
|
92
|
+
exit 1
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "yaml"
|
|
4
|
+
require "psych"
|
|
5
|
+
|
|
6
|
+
module Ruborg
|
|
7
|
+
# Configuration management for ruborg
|
|
8
|
+
class Config
|
|
9
|
+
attr_reader :data
|
|
10
|
+
|
|
11
|
+
def initialize(config_path)
|
|
12
|
+
@config_path = config_path
|
|
13
|
+
load_config
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def load_config
|
|
17
|
+
raise ConfigError, "Configuration file not found: #{@config_path}" unless File.exist?(@config_path)
|
|
18
|
+
|
|
19
|
+
@data = YAML.load_file(@config_path)
|
|
20
|
+
rescue Psych::SyntaxError => e
|
|
21
|
+
raise ConfigError, "Invalid YAML syntax: #{e.message}"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def repository
|
|
25
|
+
@data["repository"]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def backup_paths
|
|
29
|
+
@data["backup_paths"] || []
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def exclude_patterns
|
|
33
|
+
@data["exclude_patterns"] || []
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def compression
|
|
37
|
+
@data["compression"] || "lz4"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def encryption_mode
|
|
41
|
+
@data["encryption"] || "repokey"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def passbolt_integration
|
|
45
|
+
@data["passbolt"] || {}
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module Ruborg
|
|
6
|
+
# Passbolt CLI integration for password management
|
|
7
|
+
class Passbolt
|
|
8
|
+
def initialize(resource_id: nil)
|
|
9
|
+
@resource_id = resource_id
|
|
10
|
+
check_passbolt_cli
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def get_password
|
|
14
|
+
raise PassboltError, "Resource ID not configured" unless @resource_id
|
|
15
|
+
|
|
16
|
+
cmd = ["passbolt", "get", @resource_id, "--json"]
|
|
17
|
+
output = `#{cmd.join(' ')}`
|
|
18
|
+
|
|
19
|
+
raise PassboltError, "Failed to retrieve password from Passbolt" unless $?.success?
|
|
20
|
+
|
|
21
|
+
parse_password(output)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def check_passbolt_cli
|
|
27
|
+
unless system("which passbolt > /dev/null 2>&1")
|
|
28
|
+
raise PassboltError, "Passbolt CLI not found. Please install it first."
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def parse_password(json_output)
|
|
33
|
+
data = JSON.parse(json_output)
|
|
34
|
+
data["password"] || data["secret"]
|
|
35
|
+
rescue JSON::ParserError => e
|
|
36
|
+
raise PassboltError, "Failed to parse Passbolt response: #{e.message}"
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ruborg
|
|
4
|
+
# Borg repository management
|
|
5
|
+
class Repository
|
|
6
|
+
attr_reader :path
|
|
7
|
+
|
|
8
|
+
def initialize(path, passphrase: nil)
|
|
9
|
+
@path = path
|
|
10
|
+
@passphrase = passphrase
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def exists?
|
|
14
|
+
File.directory?(@path) && File.exist?(File.join(@path, "config"))
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def create
|
|
18
|
+
raise BorgError, "Repository already exists at #{@path}" if exists?
|
|
19
|
+
|
|
20
|
+
cmd = ["borg", "init", "--encryption=repokey", @path]
|
|
21
|
+
execute_borg_command(cmd)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def info
|
|
25
|
+
raise BorgError, "Repository does not exist at #{@path}" unless exists?
|
|
26
|
+
|
|
27
|
+
cmd = ["borg", "info", @path]
|
|
28
|
+
execute_borg_command(cmd)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def list
|
|
32
|
+
raise BorgError, "Repository does not exist at #{@path}" unless exists?
|
|
33
|
+
|
|
34
|
+
cmd = ["borg", "list", @path]
|
|
35
|
+
execute_borg_command(cmd)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def execute_borg_command(cmd)
|
|
41
|
+
env = {}
|
|
42
|
+
env["BORG_PASSPHRASE"] = @passphrase if @passphrase
|
|
43
|
+
|
|
44
|
+
result = system(env, *cmd)
|
|
45
|
+
raise BorgError, "Borg command failed: #{cmd.join(' ')}" unless result
|
|
46
|
+
|
|
47
|
+
result
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
data/lib/ruborg.rb
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "ruborg/version"
|
|
4
|
+
require_relative "ruborg/config"
|
|
5
|
+
require_relative "ruborg/repository"
|
|
6
|
+
require_relative "ruborg/backup"
|
|
7
|
+
require_relative "ruborg/passbolt"
|
|
8
|
+
require_relative "ruborg/cli"
|
|
9
|
+
|
|
10
|
+
module Ruborg
|
|
11
|
+
class Error < StandardError; end
|
|
12
|
+
class ConfigError < Error; end
|
|
13
|
+
class BorgError < Error; end
|
|
14
|
+
class PassboltError < Error; end
|
|
15
|
+
end
|
data/ruborg.gemspec
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
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
|
data/ruborg.yml.example
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Ruborg configuration example
|
|
2
|
+
|
|
3
|
+
# Repository path
|
|
4
|
+
repository: /path/to/borg/repository
|
|
5
|
+
|
|
6
|
+
# Paths to backup
|
|
7
|
+
backup_paths:
|
|
8
|
+
- /home/user/documents
|
|
9
|
+
- /home/user/projects
|
|
10
|
+
- /etc
|
|
11
|
+
|
|
12
|
+
# Exclude patterns
|
|
13
|
+
exclude_patterns:
|
|
14
|
+
- "*.tmp"
|
|
15
|
+
- "*.log"
|
|
16
|
+
- "*/.cache/*"
|
|
17
|
+
- "*/node_modules/*"
|
|
18
|
+
- "*/.git/*"
|
|
19
|
+
|
|
20
|
+
# Compression algorithm (lz4, zstd, zlib, lzma, none)
|
|
21
|
+
compression: lz4
|
|
22
|
+
|
|
23
|
+
# Encryption mode (repokey, keyfile, none)
|
|
24
|
+
encryption: repokey
|
|
25
|
+
|
|
26
|
+
# Passbolt integration (optional)
|
|
27
|
+
passbolt:
|
|
28
|
+
resource_id: "your-passbolt-resource-uuid"
|
metadata
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: ruborg
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Michail Pantelelis
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: thor
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '1.3'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '1.3'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: psych
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '5.0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '5.0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: bundler
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '2.0'
|
|
47
|
+
type: :development
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '2.0'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: rake
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - "~>"
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '13.0'
|
|
61
|
+
type: :development
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - "~>"
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '13.0'
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: rspec
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - "~>"
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '3.0'
|
|
75
|
+
type: :development
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - "~>"
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '3.0'
|
|
82
|
+
- !ruby/object:Gem::Dependency
|
|
83
|
+
name: rubocop
|
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - "~>"
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: '1.0'
|
|
89
|
+
type: :development
|
|
90
|
+
prerelease: false
|
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - "~>"
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: '1.0'
|
|
96
|
+
description: Ruborg is a Ruby gem that provides a user-friendly interface to Borg
|
|
97
|
+
backup. It reads YAML configuration files and orchestrates backup operations, supporting
|
|
98
|
+
repository creation, backup management, and integration with Passbolt for encryption
|
|
99
|
+
password management.
|
|
100
|
+
email:
|
|
101
|
+
- mpantel@aegean.gr
|
|
102
|
+
executables:
|
|
103
|
+
- ruborg
|
|
104
|
+
extensions: []
|
|
105
|
+
extra_rdoc_files: []
|
|
106
|
+
files:
|
|
107
|
+
- ".rspec"
|
|
108
|
+
- CHANGELOG.md
|
|
109
|
+
- CLAUDE.md
|
|
110
|
+
- LICENSE
|
|
111
|
+
- README.md
|
|
112
|
+
- Rakefile
|
|
113
|
+
- exe/ruborg
|
|
114
|
+
- lib/ruborg.rb
|
|
115
|
+
- lib/ruborg/backup.rb
|
|
116
|
+
- lib/ruborg/cli.rb
|
|
117
|
+
- lib/ruborg/config.rb
|
|
118
|
+
- lib/ruborg/passbolt.rb
|
|
119
|
+
- lib/ruborg/repository.rb
|
|
120
|
+
- lib/ruborg/version.rb
|
|
121
|
+
- ruborg.gemspec
|
|
122
|
+
- ruborg.yml.example
|
|
123
|
+
homepage: https://github.com/mpantel/ruborg
|
|
124
|
+
licenses:
|
|
125
|
+
- MIT
|
|
126
|
+
metadata:
|
|
127
|
+
homepage_uri: https://github.com/mpantel/ruborg
|
|
128
|
+
source_code_uri: https://github.com/mpantel/ruborg.git
|
|
129
|
+
changelog_uri: https://github.com/mpantel/ruborg/blob/main/CHANGELOG.md
|
|
130
|
+
rdoc_options: []
|
|
131
|
+
require_paths:
|
|
132
|
+
- lib
|
|
133
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
134
|
+
requirements:
|
|
135
|
+
- - ">="
|
|
136
|
+
- !ruby/object:Gem::Version
|
|
137
|
+
version: 3.2.0
|
|
138
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
139
|
+
requirements:
|
|
140
|
+
- - ">="
|
|
141
|
+
- !ruby/object:Gem::Version
|
|
142
|
+
version: '0'
|
|
143
|
+
requirements: []
|
|
144
|
+
rubygems_version: 3.7.1
|
|
145
|
+
specification_version: 4
|
|
146
|
+
summary: A friendly Ruby frontend for Borg backup
|
|
147
|
+
test_files: []
|