cli-backup 0.2.0__tar.gz → 0.2.1__tar.gz
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.
- {cli_backup-0.2.0/src/cli_backup.egg-info → cli_backup-0.2.1}/PKG-INFO +3 -3
- {cli_backup-0.2.0 → cli_backup-0.2.1}/README.md +2 -2
- {cli_backup-0.2.0 → cli_backup-0.2.1}/pyproject.toml +1 -1
- {cli_backup-0.2.0 → cli_backup-0.2.1}/src/cli_backup/scheduled.py +2 -0
- {cli_backup-0.2.0 → cli_backup-0.2.1/src/cli_backup.egg-info}/PKG-INFO +3 -3
- {cli_backup-0.2.0 → cli_backup-0.2.1}/tests/test_cross_project_restore.py +18 -4
- {cli_backup-0.2.0 → cli_backup-0.2.1}/LICENSE +0 -0
- {cli_backup-0.2.0 → cli_backup-0.2.1}/setup.cfg +0 -0
- {cli_backup-0.2.0 → cli_backup-0.2.1}/src/cli_backup/__init__.py +0 -0
- {cli_backup-0.2.0 → cli_backup-0.2.1}/src/cli_backup/cli.py +0 -0
- {cli_backup-0.2.0 → cli_backup-0.2.1}/src/cli_backup/config.py +0 -0
- {cli_backup-0.2.0 → cli_backup-0.2.1}/src/cli_backup/cpu_priority.py +0 -0
- {cli_backup-0.2.0 → cli_backup-0.2.1}/src/cli_backup/files.py +0 -0
- {cli_backup-0.2.0 → cli_backup-0.2.1}/src/cli_backup/mysql.py +0 -0
- {cli_backup-0.2.0 → cli_backup-0.2.1}/src/cli_backup/redis_backup.py +0 -0
- {cli_backup-0.2.0 → cli_backup-0.2.1}/src/cli_backup/rehearsal.py +0 -0
- {cli_backup-0.2.0 → cli_backup-0.2.1}/src/cli_backup/storage.py +0 -0
- {cli_backup-0.2.0 → cli_backup-0.2.1}/src/cli_backup.egg-info/SOURCES.txt +0 -0
- {cli_backup-0.2.0 → cli_backup-0.2.1}/src/cli_backup.egg-info/dependency_links.txt +0 -0
- {cli_backup-0.2.0 → cli_backup-0.2.1}/src/cli_backup.egg-info/entry_points.txt +0 -0
- {cli_backup-0.2.0 → cli_backup-0.2.1}/src/cli_backup.egg-info/requires.txt +0 -0
- {cli_backup-0.2.0 → cli_backup-0.2.1}/src/cli_backup.egg-info/top_level.txt +0 -0
- {cli_backup-0.2.0 → cli_backup-0.2.1}/tests/test_cli.py +0 -0
- {cli_backup-0.2.0 → cli_backup-0.2.1}/tests/test_config.py +0 -0
- {cli_backup-0.2.0 → cli_backup-0.2.1}/tests/test_cpu_priority.py +0 -0
- {cli_backup-0.2.0 → cli_backup-0.2.1}/tests/test_files.py +0 -0
- {cli_backup-0.2.0 → cli_backup-0.2.1}/tests/test_mysql.py +0 -0
- {cli_backup-0.2.0 → cli_backup-0.2.1}/tests/test_postgresql.py +0 -0
- {cli_backup-0.2.0 → cli_backup-0.2.1}/tests/test_redis.py +0 -0
- {cli_backup-0.2.0 → cli_backup-0.2.1}/tests/test_rehearsal.py +0 -0
- {cli_backup-0.2.0 → cli_backup-0.2.1}/tests/test_scheduled.py +0 -0
- {cli_backup-0.2.0 → cli_backup-0.2.1}/tests/test_sqlite_podman.py +0 -0
- {cli_backup-0.2.0 → cli_backup-0.2.1}/tests/test_storage.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cli-backup
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: Host backup CLI for database snapshots and directory archives
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Project-URL: Homepage, https://github.com/XakkiDev/cli-backup
|
|
@@ -76,10 +76,10 @@ make cli-backup@verify path=daily where=local file=2026-01-01.example.sql.gz
|
|
|
76
76
|
|
|
77
77
|
Additional targets `cli-backup@restore-test` and `cli-backup@restore` exist for MariaDB/MySQL. A live restore is destructive, requires an explicit file and interactive confirmation, and must target the correct stopped-writer environment. A restore rehearsal creates a disposable isolated container and should run only with appropriate authorization. For an unavailable source container, set `CLI_BACKUP_TEST_MYSQL_IMAGE` to an appropriate disposable test image before a rehearsal. Use `db=<name>` to filter when needed. Source-independent `list` and `verify` work with a stopped database container.
|
|
78
78
|
|
|
79
|
-
To restore a backup from another project into this project's selected MariaDB/MySQL database, explicitly specify the source project and database. Stop writers and run interactively only with authorization; the confirmation names both
|
|
79
|
+
To restore a backup from another project into this project's selected MariaDB/MySQL database, explicitly specify the source project and database. Source and destination database names must match: SQL dumps can contain database-qualified statements. Stop writers and run interactively only with authorization; the confirmation names both projects and the database. The destination database must already exist. This example reads from S3 without a persistent intermediary dump file:
|
|
80
80
|
|
|
81
81
|
```sh
|
|
82
|
-
make cli-backup@restore where=s3 path=daily file=2026-01-01.
|
|
82
|
+
make cli-backup@restore where=s3 path=daily file=2026-01-01.app.sql.gz source_project=production source_db=app db=app
|
|
83
83
|
```
|
|
84
84
|
|
|
85
85
|
### Storage and schedule
|
|
@@ -60,10 +60,10 @@ make cli-backup@verify path=daily where=local file=2026-01-01.example.sql.gz
|
|
|
60
60
|
|
|
61
61
|
Additional targets `cli-backup@restore-test` and `cli-backup@restore` exist for MariaDB/MySQL. A live restore is destructive, requires an explicit file and interactive confirmation, and must target the correct stopped-writer environment. A restore rehearsal creates a disposable isolated container and should run only with appropriate authorization. For an unavailable source container, set `CLI_BACKUP_TEST_MYSQL_IMAGE` to an appropriate disposable test image before a rehearsal. Use `db=<name>` to filter when needed. Source-independent `list` and `verify` work with a stopped database container.
|
|
62
62
|
|
|
63
|
-
To restore a backup from another project into this project's selected MariaDB/MySQL database, explicitly specify the source project and database. Stop writers and run interactively only with authorization; the confirmation names both
|
|
63
|
+
To restore a backup from another project into this project's selected MariaDB/MySQL database, explicitly specify the source project and database. Source and destination database names must match: SQL dumps can contain database-qualified statements. Stop writers and run interactively only with authorization; the confirmation names both projects and the database. The destination database must already exist. This example reads from S3 without a persistent intermediary dump file:
|
|
64
64
|
|
|
65
65
|
```sh
|
|
66
|
-
make cli-backup@restore where=s3 path=daily file=2026-01-01.
|
|
66
|
+
make cli-backup@restore where=s3 path=daily file=2026-01-01.app.sql.gz source_project=production source_db=app db=app
|
|
67
67
|
```
|
|
68
68
|
|
|
69
69
|
### Storage and schedule
|
|
@@ -668,6 +668,8 @@ def main(argv=None) -> int:
|
|
|
668
668
|
target = args.database or (names[0] if len(names) == 1 else selected)
|
|
669
669
|
if target not in names:
|
|
670
670
|
raise ValueError('restore target database is not configured')
|
|
671
|
+
if target != selected:
|
|
672
|
+
raise ValueError('restoring to a different database name is not supported safely')
|
|
671
673
|
prompt = f'Restore {source_project}/{selected} into {settings.project}/{target}? Type yes: '
|
|
672
674
|
if not sys.stdin.isatty() or input(prompt) != 'yes':
|
|
673
675
|
raise ValueError('restore not confirmed')
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cli-backup
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: Host backup CLI for database snapshots and directory archives
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Project-URL: Homepage, https://github.com/XakkiDev/cli-backup
|
|
@@ -76,10 +76,10 @@ make cli-backup@verify path=daily where=local file=2026-01-01.example.sql.gz
|
|
|
76
76
|
|
|
77
77
|
Additional targets `cli-backup@restore-test` and `cli-backup@restore` exist for MariaDB/MySQL. A live restore is destructive, requires an explicit file and interactive confirmation, and must target the correct stopped-writer environment. A restore rehearsal creates a disposable isolated container and should run only with appropriate authorization. For an unavailable source container, set `CLI_BACKUP_TEST_MYSQL_IMAGE` to an appropriate disposable test image before a rehearsal. Use `db=<name>` to filter when needed. Source-independent `list` and `verify` work with a stopped database container.
|
|
78
78
|
|
|
79
|
-
To restore a backup from another project into this project's selected MariaDB/MySQL database, explicitly specify the source project and database. Stop writers and run interactively only with authorization; the confirmation names both
|
|
79
|
+
To restore a backup from another project into this project's selected MariaDB/MySQL database, explicitly specify the source project and database. Source and destination database names must match: SQL dumps can contain database-qualified statements. Stop writers and run interactively only with authorization; the confirmation names both projects and the database. The destination database must already exist. This example reads from S3 without a persistent intermediary dump file:
|
|
80
80
|
|
|
81
81
|
```sh
|
|
82
|
-
make cli-backup@restore where=s3 path=daily file=2026-01-01.
|
|
82
|
+
make cli-backup@restore where=s3 path=daily file=2026-01-01.app.sql.gz source_project=production source_db=app db=app
|
|
83
83
|
```
|
|
84
84
|
|
|
85
85
|
### Storage and schedule
|
|
@@ -20,7 +20,7 @@ class CrossProjectRestoreTests(unittest.TestCase):
|
|
|
20
20
|
artifact = root / 'dump.gz'
|
|
21
21
|
with gzip.open(artifact, 'wb') as stream:
|
|
22
22
|
stream.write(b'SELECT 1;')
|
|
23
|
-
filename = '2026-11-11.
|
|
23
|
+
filename = '2026-11-11.student_dev.sql.gz'
|
|
24
24
|
store.put(artifact, make_manifest(artifact, 'sa-prod', 'mariadb', filename, '2026-11-11T00:00:00+00:00'))
|
|
25
25
|
cfg = MySQLConfig('exec', 'student_dev', 'root', 'secret', service='container')
|
|
26
26
|
env = {'CLI_BACKUP_PROJECT_NAME': 'sa-dev', 'CLI_BACKUP_DB_CONTAINER': 'mariadb',
|
|
@@ -29,7 +29,7 @@ class CrossProjectRestoreTests(unittest.TestCase):
|
|
|
29
29
|
def fake_restore(database, stream):
|
|
30
30
|
captured.append((database.database, stream.read()))
|
|
31
31
|
args = ['restore', '--storage', 's3', '--file', filename,
|
|
32
|
-
'--source-project-name', 'sa-prod', '--source-database', '
|
|
32
|
+
'--source-project-name', 'sa-prod', '--source-database', 'student_dev']
|
|
33
33
|
with patch.dict(os.environ, env, clear=True), \
|
|
34
34
|
patch('cli_backup.scheduled.connection', return_value=(cfg, ['student_dev'], 'image')), \
|
|
35
35
|
patch('cli_backup.scheduled.stores', return_value=[('s3', store)]), \
|
|
@@ -37,13 +37,27 @@ class CrossProjectRestoreTests(unittest.TestCase):
|
|
|
37
37
|
patch('sys.stdin.isatty', return_value=True), patch('builtins.input', return_value='yes') as prompt:
|
|
38
38
|
self.assertEqual(main(args), 0)
|
|
39
39
|
self.assertEqual(captured, [('student_dev', b'SELECT 1;')])
|
|
40
|
-
self.assertIn('sa-prod/
|
|
40
|
+
self.assertIn('sa-prod/student_dev into sa-dev/student_dev', prompt.call_args.args[0])
|
|
41
|
+
|
|
42
|
+
def test_different_database_name_fails_closed(self):
|
|
43
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
44
|
+
root = Path(tmp); store = LocalStorage(root / 'daily'); artifact = root / 'dump.gz'
|
|
45
|
+
with gzip.open(artifact, 'wb') as stream: stream.write(b'ALTER DATABASE `student` CHARACTER SET utf8;')
|
|
46
|
+
filename = '2026-11-11.student.sql.gz'
|
|
47
|
+
store.put(artifact, make_manifest(artifact, 'sa-prod', 'mariadb', filename, '2026-11-11T00:00:00+00:00'))
|
|
48
|
+
cfg = MySQLConfig('exec', 'student_dev', 'root', 'secret', service='container')
|
|
49
|
+
env = {'CLI_BACKUP_PROJECT_NAME': 'sa-dev', 'CLI_BACKUP_DB_CONTAINER': 'mariadb', 'CLI_BACKUP_LOCAL_ROOT': str(root)}
|
|
50
|
+
with patch.dict(os.environ, env, clear=True), patch('cli_backup.scheduled.connection', return_value=(cfg, ['student_dev'], 'image')), \
|
|
51
|
+
patch('cli_backup.scheduled.stores', return_value=[('s3', store)]), patch('cli_backup.scheduled.restore_mysql') as restore, \
|
|
52
|
+
patch('sys.stdin.isatty', return_value=True), patch('builtins.input', return_value='yes'), contextlib.redirect_stderr(io.StringIO()):
|
|
53
|
+
self.assertEqual(main(['restore', '--storage', 's3', '--file', filename, '--source-project-name', 'sa-prod']), 2)
|
|
54
|
+
restore.assert_not_called()
|
|
41
55
|
|
|
42
56
|
def test_confirmation_is_required_even_for_cross_project_restore(self):
|
|
43
57
|
with tempfile.TemporaryDirectory() as tmp:
|
|
44
58
|
root = Path(tmp); store = LocalStorage(root / 'daily'); artifact = root / 'dump.gz'
|
|
45
59
|
with gzip.open(artifact, 'wb') as stream: stream.write(b'SELECT 1;')
|
|
46
|
-
filename = '2026-11-11.
|
|
60
|
+
filename = '2026-11-11.student_dev.sql.gz'
|
|
47
61
|
store.put(artifact, make_manifest(artifact, 'sa-prod', 'mariadb', filename, '2026-11-11T00:00:00+00:00'))
|
|
48
62
|
cfg = MySQLConfig('exec', 'student_dev', 'root', 'secret', service='container')
|
|
49
63
|
env = {'CLI_BACKUP_PROJECT_NAME': 'sa-dev', 'CLI_BACKUP_DB_CONTAINER': 'mariadb', 'CLI_BACKUP_LOCAL_ROOT': str(root)}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|