l3io-wordpress-backup 1.0.0__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.
- l3io_wordpress_backup-1.0.0/.gitignore +31 -0
- l3io_wordpress_backup-1.0.0/LICENSE +21 -0
- l3io_wordpress_backup-1.0.0/PKG-INFO +593 -0
- l3io_wordpress_backup-1.0.0/README.md +550 -0
- l3io_wordpress_backup-1.0.0/docs/adr/0001-ports-and-adapters-functional-core.md +32 -0
- l3io_wordpress_backup-1.0.0/docs/adr/0002-publish-wpconfigr-existing-name.md +35 -0
- l3io_wordpress_backup-1.0.0/docs/adr/0003-credential-transport-option-file.md +40 -0
- l3io_wordpress_backup-1.0.0/docs/adr/0004-pymysql-driver.md +34 -0
- l3io_wordpress_backup-1.0.0/docs/adr/0005-relocation-via-wp-cli.md +32 -0
- l3io_wordpress_backup-1.0.0/docs/adr/0006-archive-format-and-encryption.md +49 -0
- l3io_wordpress_backup-1.0.0/docs/adr/0007-packaging-uv-trusted-publishing.md +63 -0
- l3io_wordpress_backup-1.0.0/docs/adr/0008-boto3-optional-extra.md +32 -0
- l3io_wordpress_backup-1.0.0/docs/adr/0009-python-floor-3-11.md +34 -0
- l3io_wordpress_backup-1.0.0/docs/adr/0010-wpconfigr-regex-parser-scope.md +97 -0
- l3io_wordpress_backup-1.0.0/docs/adr/0011-observability-structlog-plus-api-only-tracing.md +70 -0
- l3io_wordpress_backup-1.0.0/docs/adr/0012-package-naming-l3io-wp-namespace.md +104 -0
- l3io_wordpress_backup-1.0.0/docs/adr/0013-ci-cd-local-replay-and-secrets.md +43 -0
- l3io_wordpress_backup-1.0.0/docs/adr/0014-age-encryption-via-database.md +160 -0
- l3io_wordpress_backup-1.0.0/docs/adr/0015-credentials-delegated-to-database.md +88 -0
- l3io_wordpress_backup-1.0.0/docs/adr/0016-run-context-binds-adapters-once.md +207 -0
- l3io_wordpress_backup-1.0.0/docs/adr/0017-manifest-v1-member-index.md +247 -0
- l3io_wordpress_backup-1.0.0/docs/adr/0018-redaction-at-the-boundary.md +199 -0
- l3io_wordpress_backup-1.0.0/docs/adr/0019-backup-dry-run-scope.md +132 -0
- l3io_wordpress_backup-1.0.0/docs/adr/0020-restore-undo-derived-at-read.md +121 -0
- l3io_wordpress_backup-1.0.0/docs/adr/0021-run-journal-location-lifecycle-resolution.md +101 -0
- l3io_wordpress_backup-1.0.0/docs/adr/0022-restore-planned-from-probe-executor-runs-every-phase.md +163 -0
- l3io_wordpress_backup-1.0.0/docs/adr/0023-target-wp-config-kept-unless-overwrite.md +198 -0
- l3io_wordpress_backup-1.0.0/docs/adr/0024-one-encrypted-payload-passphrase-only.md +303 -0
- l3io_wordpress_backup-1.0.0/docs/adr/0025-api-composes-through-the-cli-drivers.md +43 -0
- l3io_wordpress_backup-1.0.0/docs/adr/0027-restore-into-an-environment-driven-database.md +195 -0
- l3io_wordpress_backup-1.0.0/docs/adr/0028-layered-configuration-pydantic-settings.md +101 -0
- l3io_wordpress_backup-1.0.0/docs/adr/README.md +41 -0
- l3io_wordpress_backup-1.0.0/docs/api.md +2038 -0
- l3io_wordpress_backup-1.0.0/docs/closeout-gate.yaml +42 -0
- l3io_wordpress_backup-1.0.0/docs/exclusions.md +83 -0
- l3io_wordpress_backup-1.0.0/docs/handoff/wordpress-backup.md +715 -0
- l3io_wordpress_backup-1.0.0/docs/logging.md +310 -0
- l3io_wordpress_backup-1.0.0/docs/restore.md +1120 -0
- l3io_wordpress_backup-1.0.0/pyproject.toml +412 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/__init__.py +22 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/_version.py +31 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/adapters/__init__.py +9 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/adapters/clock.py +22 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/adapters/config_writer.py +702 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/adapters/credential_providers.py +236 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/adapters/crypto.py +237 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/adapters/database.py +1555 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/adapters/database_load.py +1263 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/adapters/exclusions.py +83 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/adapters/journal.py +507 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/adapters/lock.py +372 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/adapters/noop.py +666 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/adapters/phase.py +218 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/adapters/preflight.py +869 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/adapters/redaction.py +237 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/adapters/restore_extension.py +341 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/adapters/restore_runner.py +1030 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/adapters/site_files.py +191 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/adapters/store.py +227 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/adapters/tar.py +1648 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/adapters/tracing.py +235 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/adapters/workspace.py +299 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/adapters/wpcli.py +584 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/adapters/wpcli_child.py +135 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/api/__init__.py +104 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/api/errors.py +344 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/api/operations.py +427 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/cli/__init__.py +228 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/cli/adapter_sets.py +255 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/cli/backup.py +1118 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/cli/context.py +382 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/cli/credentials.py +310 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/cli/doctor.py +171 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/cli/exit.py +171 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/cli/logging.py +147 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/cli/output.py +111 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/cli/parser.py +853 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/cli/passphrase.py +301 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/cli/preflight.py +227 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/cli/privileges.py +327 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/cli/restore.py +1571 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/cli/restore_report.py +396 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/cli/restore_request.py +560 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/cli/scope.py +31 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/cli/settings.py +524 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/cli/verify.py +286 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/core/__init__.py +9 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/core/archive_choice.py +156 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/core/backup.py +105 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/core/config_location.py +257 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/core/exclusions.py +189 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/core/relocation.py +437 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/core/restore.py +1246 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/core/restore_journal.py +541 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/core/restore_space.py +224 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/core/verify.py +198 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/domain/__init__.py +9 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/domain/archive_format.py +1072 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/domain/backup_scope.py +21 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/domain/config.py +27 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/domain/credentials.py +125 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/domain/database.py +113 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/domain/errors.py +1434 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/domain/exclusion.py +74 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/domain/exit_codes.py +87 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/domain/flag_map.py +246 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/domain/journal.py +142 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/domain/lock.py +61 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/domain/preflight.py +126 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/domain/restore.py +181 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/domain/search_replace.py +59 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/domain/secret.py +61 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/domain/site.py +60 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/domain/storage_target.py +60 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/ports/__init__.py +33 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/ports/adapter_set.py +66 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/ports/archive.py +136 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/ports/clock.py +23 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/ports/config.py +71 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/ports/crypto.py +30 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/ports/database.py +129 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/ports/exclusion.py +33 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/ports/journal.py +53 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/ports/lock.py +48 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/ports/preflight.py +23 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/ports/restore.py +59 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/ports/search_replace.py +40 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/ports/site_files.py +95 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/ports/tracing.py +80 -0
- l3io_wordpress_backup-1.0.0/src/l3io/wp/backup/py.typed +0 -0
- l3io_wordpress_backup-1.0.0/src/wpbackup2/__init__.py +7 -0
- l3io_wordpress_backup-1.0.0/src/wpbackup2/__main__.py +8 -0
- l3io_wordpress_backup-1.0.0/tests/.gitignore +1 -0
- l3io_wordpress_backup-1.0.0/tests/__init__.py +0 -0
- l3io_wordpress_backup-1.0.0/tests/adapters/conftest.py +161 -0
- l3io_wordpress_backup-1.0.0/tests/adapters/test_age_interop.py +275 -0
- l3io_wordpress_backup-1.0.0/tests/adapters/test_backup_executor.py +736 -0
- l3io_wordpress_backup-1.0.0/tests/adapters/test_concurrent_runs_tracing.py +108 -0
- l3io_wordpress_backup-1.0.0/tests/adapters/test_config_writer.py +547 -0
- l3io_wordpress_backup-1.0.0/tests/adapters/test_crypto.py +480 -0
- l3io_wordpress_backup-1.0.0/tests/adapters/test_database_backup.py +257 -0
- l3io_wordpress_backup-1.0.0/tests/adapters/test_database_restore.py +606 -0
- l3io_wordpress_backup-1.0.0/tests/adapters/test_descriptor.py +256 -0
- l3io_wordpress_backup-1.0.0/tests/adapters/test_local_store.py +256 -0
- l3io_wordpress_backup-1.0.0/tests/adapters/test_locate_wp_config.py +109 -0
- l3io_wordpress_backup-1.0.0/tests/adapters/test_outer_gzip.py +169 -0
- l3io_wordpress_backup-1.0.0/tests/adapters/test_site_files.py +374 -0
- l3io_wordpress_backup-1.0.0/tests/adapters/test_site_lock.py +380 -0
- l3io_wordpress_backup-1.0.0/tests/adapters/test_tar_extended_header_bound.py +98 -0
- l3io_wordpress_backup-1.0.0/tests/adapters/test_tar_probe.py +891 -0
- l3io_wordpress_backup-1.0.0/tests/adapters/test_tar_traversal.py +227 -0
- l3io_wordpress_backup-1.0.0/tests/adapters/test_tar_writer.py +561 -0
- l3io_wordpress_backup-1.0.0/tests/adapters/test_trace_ids_processor.py +109 -0
- l3io_wordpress_backup-1.0.0/tests/adapters/test_tracing.py +576 -0
- l3io_wordpress_backup-1.0.0/tests/adapters/test_tracing_database_nesting.py +181 -0
- l3io_wordpress_backup-1.0.0/tests/adapters/test_v0_fixtures.py +316 -0
- l3io_wordpress_backup-1.0.0/tests/adapters/test_workspace.py +101 -0
- l3io_wordpress_backup-1.0.0/tests/adapters/test_wpcli.py +871 -0
- l3io_wordpress_backup-1.0.0/tests/archive_fixtures.py +29 -0
- l3io_wordpress_backup-1.0.0/tests/classes/__init__.py +0 -0
- l3io_wordpress_backup-1.0.0/tests/classes/test_wp_internal_backup.py +68 -0
- l3io_wordpress_backup-1.0.0/tests/classes/test_wp_internal_restore.py +95 -0
- l3io_wordpress_backup-1.0.0/tests/classes/test_wpbackup.py +76 -0
- l3io_wordpress_backup-1.0.0/tests/classes/test_wpsite.py +117 -0
- l3io_wordpress_backup-1.0.0/tests/conftest.py +596 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/conftest.py +27 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_act_dryrun_legs.py +140 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_adapter_set_owner.py +124 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_api_base_install.py +133 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_api_docs.py +249 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_api_exception_exports.py +177 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_api_one_sequence.py +164 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_api_parity.py +257 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_api_typing.py +48 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_archive_format_owner.py +191 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_child_env_explicit.py +343 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_cipher_built_only_by_factory.py +85 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_cli_docs.py +147 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_cli_surface.py +356 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_closeout_gate.py +160 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_config_name_defined_once.py +68 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_credential_hygiene.py +985 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_credential_sources_built_once.py +338 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_db_tls_root_only.py +157 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_default_exclusion_reach.py +108 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_dependencies_from_pypi.py +179 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_drivers_no_environ.py +95 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_dry_run_gate.py +144 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_exception_fields.py +196 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_exception_hierarchy.py +148 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_exception_pickle.py +83 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_exception_raise_sites.py +115 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_exclusions_documented.py +66 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_exclusions_pure.py +109 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_existence_by_admin.py +223 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_exit_codes_family.py +37 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_extraction.py +826 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_family_required.py +324 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_flag_map.py +233 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_inner_manifest_pairing.py +195 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_integration_marker_reach.py +403 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_legacy_test_exclusion.py +63 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_log_events_documented.py +172 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_logging_configured_once.py +150 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_namespace_coexistence.py +166 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_namespace_layout.py +182 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_no_committed_age_key.py +85 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_no_database_import_in_cli.py +92 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_no_db_spelling.py +93 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_no_dependency_text_in_messages.py +133 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_no_direct_crypto.py +159 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_no_dry_run_outside_roots.py +153 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_no_flag_reads.py +158 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_no_load_replace.py +230 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_no_mysql_client.py +92 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_no_mysql_pwd.py +53 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_no_pymysql_import.py +78 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_no_secret_valued_flags.py +100 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_no_sql_ddl.py +155 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_no_wpconfigr_name.py +44 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_not_yet_implemented.py +370 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_otel_dependencies.py +158 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_port_split.py +265 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_port_types.py +194 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_preflight_construction.py +127 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_private_directories.py +88 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_public_surface.py +431 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_release_gate.py +200 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_repr_clean.py +207 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_restore_docs.py +141 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_restore_harness_typed.py +44 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_run_terminated_propagates.py +158 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_secret_reveal.py +132 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_settings_spellings.py +249 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_single_config_writer.py +116 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_single_credential_chain.py +193 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_single_passphrase_chain.py +57 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_single_redaction.py +140 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_streaming.py +789 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_supported_formats.py +71 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_tar_header_hook.py +160 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_tool_docs.py +52 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_tool_requirements_documented.py +299 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_tracing_no_sdk.py +247 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_tracing_redaction.py +139 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_usage_arguments_vocabulary.py +95 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_v0_fixtures_reproducible.py +132 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_version_branching.py +170 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_version_single_source.py +101 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_workflow_invariants.py +719 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_wp_cli_pin.py +58 -0
- l3io_wordpress_backup-1.0.0/tests/contracts/test_wp_config_never_excluded.py +125 -0
- l3io_wordpress_backup-1.0.0/tests/data/wp-config.php +31 -0
- l3io_wordpress_backup-1.0.0/tests/db_reach.py +285 -0
- l3io_wordpress_backup-1.0.0/tests/exception_scope.py +126 -0
- l3io_wordpress_backup-1.0.0/tests/fixtures/archives/v0/make_v0_fixtures.py +265 -0
- l3io_wordpress_backup-1.0.0/tests/fixtures/archives/v0/malformed/escape-absolute.tar.gz +0 -0
- l3io_wordpress_backup-1.0.0/tests/fixtures/archives/v0/malformed/escape-dotdot.tar.gz +0 -0
- l3io_wordpress_backup-1.0.0/tests/fixtures/archives/v0/malformed/hardlink.tar.gz +0 -0
- l3io_wordpress_backup-1.0.0/tests/fixtures/archives/v0/malformed/missing-wp-root.tar.gz +0 -0
- l3io_wordpress_backup-1.0.0/tests/fixtures/archives/v0/malformed/stray-manifest-late.tar.gz +0 -0
- l3io_wordpress_backup-1.0.0/tests/fixtures/archives/v0/malformed/truncated.tar.gz +0 -0
- l3io_wordpress_backup-1.0.0/tests/fixtures/archives/v0/released-0.2.21-all.provenance.json +38 -0
- l3io_wordpress_backup-1.0.0/tests/fixtures/archives/v0/released-0.2.21-all.tar.gz +0 -0
- l3io_wordpress_backup-1.0.0/tests/fixtures/archives/v0/symlink-escape.tar.gz +0 -0
- l3io_wordpress_backup-1.0.0/tests/fixtures/archives/v0/synthetic-all-gnu.tar.gz +0 -0
- l3io_wordpress_backup-1.0.0/tests/fixtures/archives/v0/synthetic-all-longname.tar.gz +0 -0
- l3io_wordpress_backup-1.0.0/tests/fixtures/archives/v0/synthetic-all-pax.tar.gz +0 -0
- l3io_wordpress_backup-1.0.0/tests/fixtures/archives/v0/synthetic-all-symlink.tar.gz +0 -0
- l3io_wordpress_backup-1.0.0/tests/fixtures/archives/v0/synthetic-files-only.tar.gz +0 -0
- l3io_wordpress_backup-1.0.0/tests/fixtures/archives/v0/synthetic-renamed.bak +0 -0
- l3io_wordpress_backup-1.0.0/tests/fixtures/archives/v1/encrypted-passphrase.passphrase.txt +4 -0
- l3io_wordpress_backup-1.0.0/tests/fixtures/archives/v1/encrypted-passphrase.tar.gz +0 -0
- l3io_wordpress_backup-1.0.0/tests/fixtures/archives/v1/generate.py +120 -0
- l3io_wordpress_backup-1.0.0/tests/fixtures/archives/v1/minimal.tar.gz +0 -0
- l3io_wordpress_backup-1.0.0/tests/integration/__init__.py +0 -0
- l3io_wordpress_backup-1.0.0/tests/integration/live_cli.py +246 -0
- l3io_wordpress_backup-1.0.0/tests/integration/live_restore.py +810 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_admin_tls.py +202 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_api_roundtrip.py +138 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_backup_end_to_end.py +171 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_backup_exclusions.py +121 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_backup_scope.py +120 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_cli_credential_sources.py +145 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_cli_encrypted_roundtrip.py +179 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_config_laid_down_by_role.py +180 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_config_outside_root.py +398 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_config_parent_resolve.py +143 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_credential_sources.py +105 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_cutover_mode.py +113 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_database_restore_report.py +312 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_direct_load.py +336 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_dry_run_backup.py +96 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_dry_run_restore.py +100 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_encrypted_backup.py +260 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_encrypted_restore_loads_dump.py +170 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_exit_codes_end_to_end.py +257 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_kill_mid_restore.py +422 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_preflight_refusal.py +156 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_relocation_config.py +574 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_restore_cli.py +292 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_restore_database_side_hygiene.py +165 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_restore_inroot_config.py +123 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_restore_modes.py +92 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_restore_refusal.py +240 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_restore_skipped_symlinks.py +160 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_root_backup.py +215 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_root_restore.py +190 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_routines_only_target.py +109 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_run_correlation.py +94 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_search_replace.py +385 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_site_lock_signals.py +200 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_snapshot_joins_run.py +253 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_snapshot_reversible.py +133 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_tree_laid_config_mode.py +140 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_v0_restore_empty_target.py +181 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_verify_cli.py +112 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_wp_child_path.py +83 -0
- l3io_wordpress_backup-1.0.0/tests/integration/test_wp_cli_missing.py +335 -0
- l3io_wordpress_backup-1.0.0/tests/load_fakes.py +654 -0
- l3io_wordpress_backup-1.0.0/tests/otel_sdk.py +128 -0
- l3io_wordpress_backup-1.0.0/tests/port_roles.py +94 -0
- l3io_wordpress_backup-1.0.0/tests/qualified_names.py +83 -0
- l3io_wordpress_backup-1.0.0/tests/restore_builders.py +188 -0
- l3io_wordpress_backup-1.0.0/tests/restore_harness.py +749 -0
- l3io_wordpress_backup-1.0.0/tests/restore_root_rig.py +306 -0
- l3io_wordpress_backup-1.0.0/tests/root_mode.py +214 -0
- l3io_wordpress_backup-1.0.0/tests/sidecar/Dockerfile +102 -0
- l3io_wordpress_backup-1.0.0/tests/sidecar/compose.yaml +112 -0
- l3io_wordpress_backup-1.0.0/tests/sidecar/run.sh +463 -0
- l3io_wordpress_backup-1.0.0/tests/site_lock_holder.py +85 -0
- l3io_wordpress_backup-1.0.0/tests/test_settings.sample.json +6 -0
- l3io_wordpress_backup-1.0.0/tests/typing/api_consumer.py +55 -0
- l3io_wordpress_backup-1.0.0/tests/unit/adapters/test_config_carry.py +366 -0
- l3io_wordpress_backup-1.0.0/tests/unit/adapters/test_config_environment.py +402 -0
- l3io_wordpress_backup-1.0.0/tests/unit/adapters/test_credential_providers.py +1051 -0
- l3io_wordpress_backup-1.0.0/tests/unit/adapters/test_database_load.py +1145 -0
- l3io_wordpress_backup-1.0.0/tests/unit/adapters/test_database_tls.py +301 -0
- l3io_wordpress_backup-1.0.0/tests/unit/adapters/test_exclusions_logging.py +104 -0
- l3io_wordpress_backup-1.0.0/tests/unit/adapters/test_manifest_cap.py +185 -0
- l3io_wordpress_backup-1.0.0/tests/unit/adapters/test_noop_restore.py +379 -0
- l3io_wordpress_backup-1.0.0/tests/unit/adapters/test_phase_join.py +114 -0
- l3io_wordpress_backup-1.0.0/tests/unit/adapters/test_preflight.py +766 -0
- l3io_wordpress_backup-1.0.0/tests/unit/adapters/test_redaction.py +234 -0
- l3io_wordpress_backup-1.0.0/tests/unit/adapters/test_restore_runner.py +1648 -0
- l3io_wordpress_backup-1.0.0/tests/unit/adapters/test_run_journal.py +297 -0
- l3io_wordpress_backup-1.0.0/tests/unit/adapters/test_state_directory.py +183 -0
- l3io_wordpress_backup-1.0.0/tests/unit/adapters/test_tar.py +468 -0
- l3io_wordpress_backup-1.0.0/tests/unit/adapters/test_wpcli_config_placement.py +476 -0
- l3io_wordpress_backup-1.0.0/tests/unit/adapters/test_wpcli_dumpable.py +82 -0
- l3io_wordpress_backup-1.0.0/tests/unit/adapters/test_wpcli_extension.py +482 -0
- l3io_wordpress_backup-1.0.0/tests/unit/api/conftest.py +36 -0
- l3io_wordpress_backup-1.0.0/tests/unit/api/test_boundary.py +311 -0
- l3io_wordpress_backup-1.0.0/tests/unit/api/test_descriptor_construction.py +51 -0
- l3io_wordpress_backup-1.0.0/tests/unit/api/test_exit_code_attribute.py +55 -0
- l3io_wordpress_backup-1.0.0/tests/unit/api/test_no_credential_in_errors.py +385 -0
- l3io_wordpress_backup-1.0.0/tests/unit/api/test_on_warning.py +400 -0
- l3io_wordpress_backup-1.0.0/tests/unit/api/test_operations.py +981 -0
- l3io_wordpress_backup-1.0.0/tests/unit/api/test_process_state.py +252 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/conftest.py +124 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_adapter_sets.py +475 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_archive_not_collected.py +162 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_backup_dry_run_effects.py +495 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_backup_entry.py +128 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_backup_flags.py +239 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_binary_preflight_refusal.py +173 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_closeout_notices.py +194 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_closeout_residue.py +142 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_closeout_safety.py +310 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_credentials.py +258 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_db_tls_cli.py +143 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_doctor.py +204 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_drivers.py +258 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_dry_run_exclusions.py +97 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_environment_driven_site.py +275 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_exit_mapping.py +368 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_help_style.py +111 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_lock_joining.py +202 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_lock_order.py +203 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_log_schema.py +157 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_logging.py +183 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_new_db_password.py +344 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_output.py +109 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_passphrase_chain.py +420 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_phase_names_upper.py +93 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_preflight_failed.py +248 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_preflight_order.py +209 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_privileges.py +323 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_publish_gate.py +44 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_render_failure.py +193 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_render_flags.py +81 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_restore_admin_as_root.py +99 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_restore_config_parent.py +253 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_restore_dry_run_links.py +143 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_restore_extension.py +203 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_restore_flags.py +135 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_restore_latest.py +174 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_restore_root.py +1293 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_restore_symlink_notice.py +26 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_root_backup.py +301 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_root_preflight.py +205 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_run_context.py +196 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_scope_mapping.py +93 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_search_replace_report.py +58 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_secret_registry_order.py +130 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_settings.py +312 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_snapshot_join.py +105 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_tracing_hooks.py +288 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_verify_authenticate.py +497 -0
- l3io_wordpress_backup-1.0.0/tests/unit/cli/test_verify_platform.py +60 -0
- l3io_wordpress_backup-1.0.0/tests/unit/core/test_archive_choice.py +163 -0
- l3io_wordpress_backup-1.0.0/tests/unit/core/test_backup_plan.py +169 -0
- l3io_wordpress_backup-1.0.0/tests/unit/core/test_config_location.py +268 -0
- l3io_wordpress_backup-1.0.0/tests/unit/core/test_database_report.py +219 -0
- l3io_wordpress_backup-1.0.0/tests/unit/core/test_exclusions.py +242 -0
- l3io_wordpress_backup-1.0.0/tests/unit/core/test_expansion.py +177 -0
- l3io_wordpress_backup-1.0.0/tests/unit/core/test_orderly_failure.py +139 -0
- l3io_wordpress_backup-1.0.0/tests/unit/core/test_plan_equality.py +82 -0
- l3io_wordpress_backup-1.0.0/tests/unit/core/test_relocation.py +492 -0
- l3io_wordpress_backup-1.0.0/tests/unit/core/test_restore_journal.py +384 -0
- l3io_wordpress_backup-1.0.0/tests/unit/core/test_restore_plan.py +317 -0
- l3io_wordpress_backup-1.0.0/tests/unit/core/test_restore_plan_equality.py +105 -0
- l3io_wordpress_backup-1.0.0/tests/unit/core/test_snapshot_location.py +56 -0
- l3io_wordpress_backup-1.0.0/tests/unit/core/test_snapshot_scope.py +135 -0
- l3io_wordpress_backup-1.0.0/tests/unit/core/test_target_guard.py +408 -0
- l3io_wordpress_backup-1.0.0/tests/unit/core/test_v0_config_location.py +102 -0
- l3io_wordpress_backup-1.0.0/tests/unit/core/test_verify.py +238 -0
- l3io_wordpress_backup-1.0.0/tests/unit/core/test_wp_cli_preflight_order.py +344 -0
- l3io_wordpress_backup-1.0.0/tests/unit/domain/test_archive_format.py +481 -0
- l3io_wordpress_backup-1.0.0/tests/unit/domain/test_backup_scope.py +13 -0
- l3io_wordpress_backup-1.0.0/tests/unit/domain/test_classifier.py +219 -0
- l3io_wordpress_backup-1.0.0/tests/unit/domain/test_created_at.py +83 -0
- l3io_wordpress_backup-1.0.0/tests/unit/domain/test_credential_precedence.py +45 -0
- l3io_wordpress_backup-1.0.0/tests/unit/domain/test_exit_codes.py +48 -0
- l3io_wordpress_backup-1.0.0/tests/unit/domain/test_flag_table.py +231 -0
- l3io_wordpress_backup-1.0.0/tests/unit/domain/test_journal_types.py +133 -0
- l3io_wordpress_backup-1.0.0/tests/unit/domain/test_manifest_no_key_material.py +154 -0
- l3io_wordpress_backup-1.0.0/tests/unit/domain/test_restore_vocabulary.py +269 -0
- l3io_wordpress_backup-1.0.0/tests/unit/domain/test_secret.py +12 -0
- l3io_wordpress_backup-1.0.0/tests/unit/domain/test_site.py +68 -0
- l3io_wordpress_backup-1.0.0/tests/unit/domain/test_storage_target.py +59 -0
- l3io_wordpress_backup-1.0.0/tools/check_base_install.py +156 -0
- l3io_wordpress_backup-1.0.0/tools/check_mode_defaults.py +234 -0
- l3io_wordpress_backup-1.0.0/tools/check_purity.py +319 -0
- l3io_wordpress_backup-1.0.0/tools/gen_cli_docs.py +141 -0
- l3io_wordpress_backup-1.0.0/tools/gen_tool_docs.py +161 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
__pycache__
|
|
2
|
+
.coverage
|
|
3
|
+
.DS_Store
|
|
4
|
+
.vscode
|
|
5
|
+
build
|
|
6
|
+
dist
|
|
7
|
+
htmlcov
|
|
8
|
+
*.egg-info
|
|
9
|
+
|
|
10
|
+
# BMad/l3io tool output, regenerated by the installer. Both sibling repos ignore
|
|
11
|
+
# and untrack these; this repo had six committed, so a tool refresh showed up as
|
|
12
|
+
# repo changes. Tool output does not belong in the release or in review.
|
|
13
|
+
.github/agents/
|
|
14
|
+
|
|
15
|
+
# act's real configuration, generated from the .example files by `make act-init`.
|
|
16
|
+
# The .example versions are tracked; these are not. .act.secrets is the one that
|
|
17
|
+
# holds credentials once populated.
|
|
18
|
+
#
|
|
19
|
+
# These entries were present as an UNCOMMITTED .gitignore change and were lost to
|
|
20
|
+
# a `git reset --hard` during this session -- so for a while the file meant to
|
|
21
|
+
# hold act's secrets was not ignored at all. Nothing leaked (all three have zero
|
|
22
|
+
# commits in any branch, and .act.secrets was empty), but the protection was
|
|
23
|
+
# absent and a later commit touched .gitignore without noticing the gap.
|
|
24
|
+
.act.env
|
|
25
|
+
.act.secrets
|
|
26
|
+
.act.vars
|
|
27
|
+
|
|
28
|
+
# Local agent tool state: Codex's directory and Claude Code's per-agent git
|
|
29
|
+
# worktrees. Neither belongs in the repository (BL-E007-012).
|
|
30
|
+
.codex
|
|
31
|
+
.claude/worktrees/
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2018 Cariad Eccleston
|
|
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.
|
|
@@ -0,0 +1,593 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: l3io-wordpress-backup
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: WordPress backup, restore and relocation for automation
|
|
5
|
+
Project-URL: Homepage, https://github.com/LiquidLogicLabs/l3io-wordpress-backup
|
|
6
|
+
Project-URL: Source, https://github.com/LiquidLogicLabs/l3io-wordpress-backup
|
|
7
|
+
Project-URL: Issues, https://github.com/LiquidLogicLabs/l3io-wordpress-backup/issues
|
|
8
|
+
Author-email: Shawn Anderson <ravensorb@gmail.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: backup,migration,mysqldump,restore,wordpress,wp-cli
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: System Administrators
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: MacOS
|
|
17
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
18
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
23
|
+
Classifier: Topic :: System :: Archiving :: Backup
|
|
24
|
+
Classifier: Topic :: System :: Systems Administration
|
|
25
|
+
Classifier: Typing :: Typed
|
|
26
|
+
Requires-Python: >=3.11
|
|
27
|
+
Requires-Dist: l3io-age-passphrase<0.2,>=0.1.1
|
|
28
|
+
Requires-Dist: l3io-wordpress-config<0.2,>=0.1.7
|
|
29
|
+
Requires-Dist: l3io-wordpress-database[encryption]<0.4,>=0.3.5
|
|
30
|
+
Requires-Dist: opentelemetry-api<2,>=1.40
|
|
31
|
+
Requires-Dist: packaging<27,>=25
|
|
32
|
+
Requires-Dist: pathspec<2,>=1.1
|
|
33
|
+
Requires-Dist: platformdirs<5,>=4.3
|
|
34
|
+
Requires-Dist: pydantic-settings<3,>=2.16
|
|
35
|
+
Requires-Dist: structlog<27,>=26.1
|
|
36
|
+
Provides-Extra: aws
|
|
37
|
+
Requires-Dist: l3io-wordpress-database[aws]<0.4,>=0.3.5; extra == 'aws'
|
|
38
|
+
Provides-Extra: database
|
|
39
|
+
Provides-Extra: otel
|
|
40
|
+
Requires-Dist: opentelemetry-exporter-otlp-json-file>=0.65b0; extra == 'otel'
|
|
41
|
+
Requires-Dist: opentelemetry-sdk<2,>=1.40; extra == 'otel'
|
|
42
|
+
Description-Content-Type: text/markdown
|
|
43
|
+
|
|
44
|
+
# py-wordpress-backup
|
|
45
|
+
|
|
46
|
+
A Python package for backing up and restoring a self-hosted WordPress installation, including the database and uploaded content.
|
|
47
|
+
|
|
48
|
+
The backup file will contain:
|
|
49
|
+
|
|
50
|
+
- Your database as a MySQL dump.
|
|
51
|
+
- An exact copy of your entire WordPress directory.
|
|
52
|
+
|
|
53
|
+
The restore process will:
|
|
54
|
+
- Extract the files into the correct location
|
|
55
|
+
- Update ``wp-config.php`` with new settings (if needed)
|
|
56
|
+
- Create the database if it doesn't exist
|
|
57
|
+
- Add the user and grant it access if it doesn't exist
|
|
58
|
+
- Restore the database
|
|
59
|
+
|
|
60
|
+
## Credit
|
|
61
|
+
Credit for the original version of this goes to [Cariad Eccleston](https://github.com/cariad/py-wordpress-database).
|
|
62
|
+
|
|
63
|
+
## Installation
|
|
64
|
+
|
|
65
|
+
```shell
|
|
66
|
+
pip install l3io-wordpress-backup
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
That is everything a backup and a restore need: `l3io-wordpress-database`, `l3io-wordpress-config` and the `l3io-age-passphrase` binder are required dependencies, and every dependency installs from PyPI; no extra index is needed. The optional extras add:
|
|
70
|
+
|
|
71
|
+
- `aws` — AWS Secrets Manager credentials (boto3): `pip install 'l3io-wordpress-backup[aws]'`;
|
|
72
|
+
- `otel` — the OpenTelemetry SDK and a JSON-file exporter, for tracing.
|
|
73
|
+
|
|
74
|
+
`pip install 'l3io-wordpress-backup[database]'`, the install earlier releases documented, still works: `database` is now an empty extra that adds nothing. A feature used without its extra (`aws`) stops with exit 7 (`NOT_READY`) naming the extra to install. An installation missing a required dependency (installed with `--no-deps`, or pruned afterwards) stops with exit 7 too, saying the installation is incomplete; reinstall `l3io-wordpress-backup`.
|
|
75
|
+
|
|
76
|
+
Requirements: Python 3.11 or newer. Extracting an archive also needs a Python with the June 2025 `tarfile` security fixes (3.11.13+, 3.12.11+, 3.13.4+; CVE-2025-4517); on an older one extraction is refused — upgrade Python.
|
|
77
|
+
|
|
78
|
+
Upgrading from 0.2.21: that release can no longer be installed from PyPI, because two of its dependencies (`wpconfigr`, `chesney`) were removed from there. You do not need it: this package reads 0.2.21 Archives itself (`wpbackup2 verify --archive <file>` reports them as `STRUCTURAL_ONLY`, and `restore` restores them). The restore side is tested with Archives in 0.2.21's layout built by the test suite and by extracting the committed 0.2.21-layout fixtures; no test restores an Archive written by a 0.2.21 install.
|
|
79
|
+
|
|
80
|
+
### External tools
|
|
81
|
+
|
|
82
|
+
A Run checks every external tool its plan needs before its first effect, and stops with exit 7 (`NOT_READY`) naming the tool, the step that needed it and what was found. The table lists the tools this package runs itself and the minimum a plan in this build asks for:
|
|
83
|
+
|
|
84
|
+
<!-- BEGIN GENERATED: tool-requirements (tools/gen_tool_docs.py) -->
|
|
85
|
+
| Tool | Minimum | Purpose | Needed for | Install |
|
|
86
|
+
|---|---|---|---|---|
|
|
87
|
+
| `age` | 1.3 | encrypts the Archive under a passphrase (CLI passphrase backend) | the encrypted payload (ENCRYPT, VERIFY) | https://github.com/FiloSottile/age#installation |
|
|
88
|
+
| `age-plugin-batchpass` | — | supplies the passphrase to age non-interactively (CLI backend) | the encrypted payload (ENCRYPT, VERIFY) | https://github.com/FiloSottile/age/releases |
|
|
89
|
+
| `wp` | 2.12.0 | WP-CLI, which relocation runs to rewrite the Site's URLs | SEARCH_REPLACE: serialization-safe content URL rewrite for --new-site-url/--new-site-home-url (FR-12) | https://wp-cli.org/#installing |
|
|
90
|
+
| MySQL/MariaDB client tools | client 8.4+, MariaDB 11.4+ | the database dump and load | checked per Site by l3io-wordpress-database (`check_dump`/`check_load`), never run by this package | your distribution's MySQL or MariaDB client package |
|
|
91
|
+
<!-- END GENERATED: tool-requirements (tools/gen_tool_docs.py) -->
|
|
92
|
+
|
|
93
|
+
The `l3io-age-passphrase` binder, a required dependency, is published as wheels for Linux (glibc and musl; x86_64, aarch64, armv7l), macOS (x86_64, arm64) and Windows (amd64). On any other platform the installer builds it from its source distribution, which needs a Rust toolchain; if that build fails, the package does not install. When the binder is not importable, the passphrase backend falls back to the `age` command line (`L3IO_AGE_PASSPHRASE_BACKEND=auto`, the default), so `age` 1.3 or newer and `age-plugin-batchpass` must be on `PATH`, or an encrypted Run stops with exit 7 (`NOT_READY`) before its first effect. `L3IO_AGE_PASSPHRASE_BACKEND=cli` selects that backend explicitly.
|
|
94
|
+
|
|
95
|
+
## Usage
|
|
96
|
+
|
|
97
|
+
### Quick start: unattended, with no flags
|
|
98
|
+
|
|
99
|
+
Every option is also an `L3IO_WP_BACKUP_<NAME>` variable (`--wp-dir` is
|
|
100
|
+
`L3IO_WP_BACKUP_WP_DIR`) and a key of a TOML config file (`wp_dir = "..."`),
|
|
101
|
+
so a scheduled job needs no flags. A flag wins over a variable, a variable over
|
|
102
|
+
the file, the file over the default. The file is `--config PATH` or
|
|
103
|
+
`L3IO_WP_BACKUP_CONFIG`, else `/etc/l3io-wp-backup/config.toml` and then
|
|
104
|
+
`~/.config/l3io-wp-backup/config.toml` when present; one holding a secret
|
|
105
|
+
(`passphrase`, `db_password`, `admin_password`, `new_db_password`) must be
|
|
106
|
+
readable by its owner only (`chmod 600`), or it is refused. ADR-0028 records
|
|
107
|
+
the design.
|
|
108
|
+
|
|
109
|
+
**As a Docker sidecar** next to the official `wordpress` image, sharing its
|
|
110
|
+
site volume and its `WORDPRESS_DB_*` environment:
|
|
111
|
+
|
|
112
|
+
```yaml
|
|
113
|
+
services:
|
|
114
|
+
backup:
|
|
115
|
+
image: <your wpbackup2 image>
|
|
116
|
+
env_file: wordpress.env # the site's WORDPRESS_DB_*
|
|
117
|
+
environment:
|
|
118
|
+
L3IO_WP_BACKUP_WP_DIR: /var/www/html
|
|
119
|
+
L3IO_WP_BACKUP_ARCHIVE_DIR: /backups
|
|
120
|
+
volumes:
|
|
121
|
+
- html:/var/www/html
|
|
122
|
+
- backups:/backups
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
then `docker compose exec backup wpbackup2 backup` on a schedule, and to
|
|
126
|
+
restore the newest backup of this site:
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
docker compose exec backup wpbackup2 restore --archive latest
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Set `L3IO_WP_BACKUP_PASSPHRASE` (and add `--encrypt-with-passphrase`, or
|
|
133
|
+
`encrypt_with_passphrase = true` in the config file) to encrypt; a restore
|
|
134
|
+
then decrypts with the same passphrase. Started as root, a backup or a restore
|
|
135
|
+
runs as the owner of `/var/www/html` (www-data in the official image), after
|
|
136
|
+
reading the passphrase and credentials, so Archives and restored files belong
|
|
137
|
+
to it and both share its state directory; `/backups` must then be writable by
|
|
138
|
+
that user, or the backup is refused before it starts. `tests/sidecar/` is a
|
|
139
|
+
working example.
|
|
140
|
+
|
|
141
|
+
**From cron**, with the tool installed by `pip`:
|
|
142
|
+
|
|
143
|
+
```toml
|
|
144
|
+
# ~/.config/l3io-wp-backup/config.toml (chmod 600)
|
|
145
|
+
wp_dir = "/var/www/example"
|
|
146
|
+
archive_dir = "/var/backups/example"
|
|
147
|
+
encrypt_with_passphrase = true
|
|
148
|
+
passphrase = "a long passphrase, 12 characters or more"
|
|
149
|
+
log_format = "console"
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
15 3 * * * wpbackup2 backup
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Without `--archive` or `archive_dir`, the Archive goes into the current
|
|
157
|
+
directory, or into the per-user data directory
|
|
158
|
+
(`~/.local/share/l3io-wp-backup/archives`) when the current directory is
|
|
159
|
+
inside the site, never inside the Site Root. A target you put inside the Site
|
|
160
|
+
Root is written with a warning: the web server may serve it. A backup started
|
|
161
|
+
as root runs as the Site Root's owner: that user's data directory, and an
|
|
162
|
+
Archive directory that user can write (otherwise the backup is refused before
|
|
163
|
+
it starts, naming the directory).
|
|
164
|
+
|
|
165
|
+
### By hand
|
|
166
|
+
|
|
167
|
+
To backup:
|
|
168
|
+
|
|
169
|
+
```
|
|
170
|
+
wpbackup2 backup --wp-dir /www/wordpress --archive ~/backup.tar.gz
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
To rehearse a backup first, add `--whatIf` (before or after `backup`). The Dry Run resolves the credentials, runs the preflight and asks the database whether it can be dumped, all for real; then it prints the ordered plan and writes nothing. Its last line says so. It does not detect what only writing finds: a full disk, a file that cannot be read, or an Archive larger than the Manifest cap. A Site Lock is taken as for a real Run, so a Dry Run waits for nobody and is refused (exit 9) while another Run of the same Site by the same OS user holds the lock.
|
|
174
|
+
|
|
175
|
+
**Output for cron mail.** stderr is the log stream. On a terminal it is human-readable; anywhere else (cron, a pipe, journald) it is strict JSON lines, one object per line, so a log collector can parse every line. Where a person reads the output instead — a cron job that mails it — pass `--log-format console` before the subcommand to get human-readable lines there too, failures and warnings included:
|
|
176
|
+
|
|
177
|
+
```
|
|
178
|
+
wpbackup2 --log-format console backup --wp-dir /www/wordpress --archive ~/backup.tar.gz
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
To check an archive without touching a site:
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
wpbackup2 verify --archive ~/backup.tar.gz
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Without a key, `verify` proves the archive's checksums are **consistent** with its manifest, not that the archive is **authentic**: the manifest is unprotected, so anyone who can rewrite the archive can rewrite its checksums too. It says so (`authenticity not checked (no key)`). For an encrypted archive, `--authenticate` decrypts the whole payload with its passphrase — taken from `L3IO_WP_BACKUP_PASSPHRASE` or a terminal prompt, never from the command line — and `age` authenticates every chunk. A wrong passphrase or a tampered payload exits 8; a plain archive is refused, since there is nothing to authenticate. **Encryption protects file contents only.** The manifest stays readable in clear in an encrypted archive: the site path, the database name, every file path and size, and the exclusions are visible to anyone who can read the archive, without the passphrase. To refuse a plain archive where an encrypted one is expected (for example one substituted by someone who can write the storage target), restore with `--require-encrypted`; without it a plain archive is restored with a warning, since plain archives are not authenticated.
|
|
188
|
+
|
|
189
|
+
**Memory when reading an archive.** `verify` and `restore` read the whole manifest into memory and parse it before anything is authenticated, taking about 4.3 times its size. A manifest is capped at 256 MiB, so the worst case is about 1.1 GB; an ordinary site's manifest is a few MB. A streaming manifest reader is planned after v1.
|
|
190
|
+
|
|
191
|
+
To restore (see [docs/restore.md](docs/restore.md) for every mode, the refusals and their exit codes):
|
|
192
|
+
|
|
193
|
+
```
|
|
194
|
+
wpbackup2 restore --wp-dir /www/wordpress --archive ~/backup.tar.gz --mode all
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
A restore takes a Safety Snapshot of whatever it would change first. A database restore then loads the Dump straight into the live database, after dropping every table that carries the site's table prefix (tables without it are never touched), and reloads the Safety Snapshot automatically if the load fails; see [docs/restore.md](docs/restore.md#the-database-loaded-straight-into-the-live-database). Over a Site Root that is not empty and does not look like WordPress (none of `wp-config.php`, `wp-includes/`, `wp-content/`), or a database with tables none of which carries the site's prefix, it refuses unless `--force` is given (exit 3, naming the flag). `--archive latest` restores the newest Archive of this site in `--archive-dir` (see [docs/restore.md](docs/restore.md#--archive-latest)). A target with no `wp-config.php` takes its database from an encrypted archive's config as it says, and from an unencrypted one only when you confirm it with `--archive-database USER@HOST[:PORT]/NAME`. Database connections require no TLS unless you ask with `--db-tls {required,verify-ca,verify-identity}` (and `--db-tls-ca FILE`). Started as root, a restore (like a backup) runs as the Site Root's owner. The library (`api.backup`, `api.restore`) refuses to run as root: it never switches its host's user.
|
|
198
|
+
|
|
199
|
+
`wp-config.php` is read for your database credentials. It is looked for in the WordPress directory, then its parent; pass `--wp-config PATH` to use another.
|
|
200
|
+
|
|
201
|
+
### Credentials
|
|
202
|
+
|
|
203
|
+
No credential has to be on the command line, so a cron line needs none. Each account's credentials come from the first source that has them, in a fixed order:
|
|
204
|
+
|
|
205
|
+
| Account | 1. Explicit | 2. Environment | 3. `wp-config.php` |
|
|
206
|
+
|---|---|---|---|
|
|
207
|
+
| WordPress database (backup) | `--db-user`/`--db-password`, their config-file keys, or library `BackupRequest.credentials` | `L3IO_WP_BACKUP_DB_USER` and `L3IO_WP_BACKUP_DB_PASSWORD` | `DB_USER`/`DB_PASSWORD` |
|
|
208
|
+
| WordPress database (restore) | none | none | `DB_USER`/`DB_PASSWORD`, the account the restored site will use |
|
|
209
|
+
| Administrator (restore) | `--admin-user` (or `--admin-username`)/`--admin-password`, their config-file keys, or library `RestoreRequest.admin` | `L3IO_WP_BACKUP_ADMIN_USER` and `L3IO_WP_BACKUP_ADMIN_PASSWORD` | never |
|
|
210
|
+
|
|
211
|
+
- **Both variables of a pair, or neither.** Setting one without its partner is an error (exit 4), not a fall-through to `wp-config.php`. A variable set to an empty value counts as set. The one exception is `--admin-username` without `--admin-password`: the password then comes from `L3IO_WP_BACKUP_ADMIN_PASSWORD`, and an empty value there counts as unset. The administrator pair is read only by a restore that needs an administrator, so `--mode files` never asks for one, and neither does a database restore whose WordPress account can load its own database (owner decision 55): an administrator is needed only when the database does not exist yet or the account cannot log in or lacks a right on it.
|
|
212
|
+
- **A source that fails stops the search.** An unreadable AWS secret, for example, never becomes the next source's account. The administrator is never taken from `wp-config.php` or from the WordPress account. A restore that needs one and has none is refused before anything changes (exit 4), naming the database, the account and what is missing.
|
|
213
|
+
- **AWS Secrets Manager.** A secret holding the JSON `{"username": ..., "password": ...}` can supply either account as its explicit source. Install the `aws` extra: `pip install 'l3io-wordpress-backup[aws]'`. Without it, exit 7 names the extra. Without a region from the request or the AWS session, exit 4 names the region flag. In this release only the library selects it: `AwsSecretCredentialSpec(secret_id, region)` as `BackupRequest.credentials` or `RestoreRequest.admin`. The `--db-credentials-aws-*` and `--admin-credentials-aws-*` flags are accepted but not implemented yet.
|
|
214
|
+
- **`getenv_docker()` and `getenv()` in `wp-config.php`.** The official `wordpress` Docker image defines `DB_HOST`, `DB_NAME`, `DB_USER`, `DB_PASSWORD` and `$table_prefix` as `getenv_docker('WORDPRESS_DB_HOST', 'mysql')` and so on. Backup, `doctor` and restore resolve them as PHP would, from their own environment: the variable, a Docker secret named by `<NAME>_FILE`, else the default. Run the tool with the site's `WORDPRESS_DB_*` (a sidecar container can share the WordPress container's environment file); the resolved host is logged (`database_host`) and named in the Dry Run. Any other PHP expression in those settings is refused (exit 3) without being evaluated. A database restore loads straight into the database the environment names and leaves `wp-config.php` as it is (owner decision 49); only a relocation flag (`--new-db-*`) that would rewrite such a constant is refused (exit 3). `--overwrite-wp-config` keeps those definitions as written, and is refused (exit 3) when the Archive's `wp-config.php` does not define `getenv_docker()`: see [docs/restore.md](docs/restore.md#wp-configphp).
|
|
215
|
+
- **The log names the source, never the value.** Each resolution logs `credential_source_selected` with the source's label, for example `environment:L3IO_WP_BACKUP_DB_USER,L3IO_WP_BACKUP_DB_PASSWORD`.
|
|
216
|
+
|
|
217
|
+
The environment of a process can be read by the same user and by root (`/proc/<pid>/environ`), and a secret flag (`--passphrase`, `--db-password`, `--admin-password`, `--new-db-password`) is visible to every user in `ps` and logs WARNING `password_on_command_line`. On a shared host, prefer `wp-config.php`, a `chmod 600` config file or AWS Secrets Manager. A database password, or a passphrase that decrypts (`verify --authenticate`, `restore`), shorter than 4 characters is accepted with WARNING `credential_below_redaction_floor`: it is too short to be masked in logs. A passphrase that encrypts needs 12 characters or more.
|
|
218
|
+
|
|
219
|
+
### Command-line reference
|
|
220
|
+
|
|
221
|
+
Generated from the parser by `tools/gen_cli_docs.py`; CI fails if it drifts.
|
|
222
|
+
|
|
223
|
+
<!-- BEGIN GENERATED: cli-usage (tools/gen_cli_docs.py) -->
|
|
224
|
+
```text
|
|
225
|
+
$ wpbackup2 --help
|
|
226
|
+
usage: wpbackup2 [-h] [--version] [--config PATH]
|
|
227
|
+
[--log-level {DEBUG,INFO,WARNING,ERROR,CRITICAL}] [--log-format {console,json}]
|
|
228
|
+
[--whatIf]
|
|
229
|
+
{backup,restore,verify,doctor} ...
|
|
230
|
+
|
|
231
|
+
Backup/restore your WordPress content and database.
|
|
232
|
+
|
|
233
|
+
Every option can also be set as an L3IO_WP_BACKUP_<NAME> variable
|
|
234
|
+
(--wp-dir is L3IO_WP_BACKUP_WP_DIR) or as a key in a TOML config file
|
|
235
|
+
(wp_dir = "..."); a flag wins over a variable, a variable over the file.
|
|
236
|
+
|
|
237
|
+
positional arguments:
|
|
238
|
+
{backup,restore,verify,doctor}
|
|
239
|
+
Action being requested
|
|
240
|
+
backup Perform a WordPress backup
|
|
241
|
+
restore Perform a WordPress restore
|
|
242
|
+
verify Check an archive without touching a site
|
|
243
|
+
doctor Report whether this host is ready to back up and restore
|
|
244
|
+
|
|
245
|
+
options:
|
|
246
|
+
-h, --help Show this help message and exit
|
|
247
|
+
--version Show the program's version number and exit
|
|
248
|
+
--config PATH Read settings from this TOML file (also L3IO_WP_BACKUP_CONFIG; default:
|
|
249
|
+
/etc/l3io-wp-backup/config.toml, then the per-user ~/.config/l3io-wp-
|
|
250
|
+
backup/config.toml). A file holding a secret must be readable by its owner
|
|
251
|
+
only
|
|
252
|
+
--log-level {DEBUG,INFO,WARNING,ERROR,CRITICAL}
|
|
253
|
+
Logging level (stdlib level names; default INFO)
|
|
254
|
+
--log-format {console,json}
|
|
255
|
+
Log format on stderr (default: console on a terminal, else json)
|
|
256
|
+
--whatIf, --what-if Dry Run: resolve credentials, run preflight and every query for real, and
|
|
257
|
+
print the ordered plan; changes no Site, database or archive, but takes
|
|
258
|
+
the Site Lock (exit 9 LOCKED while another Run holds it)
|
|
259
|
+
|
|
260
|
+
exit codes:
|
|
261
|
+
0 OK the Run succeeded
|
|
262
|
+
1 FAILED the Run failed for a reason no other code names
|
|
263
|
+
2 USAGE the command line was invalid
|
|
264
|
+
3 REFUSED a check found a reason not to act; nothing was done
|
|
265
|
+
4 CREDENTIALS credentials were missing, unusable or rejected
|
|
266
|
+
5 UNREACHABLE the database server could not be reached
|
|
267
|
+
7 NOT_READY a required tool or optional extra is not available
|
|
268
|
+
8 VERIFICATION_FAILED the archive failed verification
|
|
269
|
+
9 LOCKED another Run holds the site lock
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
```text
|
|
273
|
+
$ wpbackup2 backup --help
|
|
274
|
+
usage: wpbackup2 backup [-h] [--config PATH] [--wp-dir WP_DIR] [--archive-dir DIR] [--whatIf]
|
|
275
|
+
[--archive ARCHIVE] [--wp-config PATH] [--mode {files,db,all}]
|
|
276
|
+
[--encrypt-with-passphrase] [--passphrase SECRET] [--exclude PATTERN]
|
|
277
|
+
[--no-default-excludes] [--list-default-excludes]
|
|
278
|
+
[--db-credentials-aws-secret-id ID] [--db-credentials-aws-region REGION]
|
|
279
|
+
[--db-user USER] [--db-password SECRET]
|
|
280
|
+
[--db-tls {required,verify-ca,verify-identity}] [--db-tls-ca FILE]
|
|
281
|
+
|
|
282
|
+
Perform a WordPress backup
|
|
283
|
+
|
|
284
|
+
Every option can also be set as an L3IO_WP_BACKUP_<NAME> variable
|
|
285
|
+
(--wp-dir is L3IO_WP_BACKUP_WP_DIR) or as a key in a TOML config file
|
|
286
|
+
(wp_dir = "..."); a flag wins over a variable, a variable over the file.
|
|
287
|
+
|
|
288
|
+
options:
|
|
289
|
+
-h, --help Show this help message and exit
|
|
290
|
+
--config PATH Read settings from this TOML file (also L3IO_WP_BACKUP_CONFIG; default:
|
|
291
|
+
/etc/l3io-wp-backup/config.toml, then the per-user ~/.config/l3io-wp-
|
|
292
|
+
backup/config.toml). A file holding a secret must be readable by its owner
|
|
293
|
+
only
|
|
294
|
+
--wp-dir WP_DIR Path to the root of the WordPress directory (required, except with --list-
|
|
295
|
+
default-excludes)
|
|
296
|
+
--archive-dir DIR Directory the archive is written to when --archive is not given (default:
|
|
297
|
+
the current directory, or the per-user data directory when the current
|
|
298
|
+
directory is inside the Site Root)
|
|
299
|
+
--whatIf, --what-if Dry Run: run every check and query for real and print the ordered plan;
|
|
300
|
+
writes no archive but takes the Site Lock (also accepted before `backup`)
|
|
301
|
+
--archive ARCHIVE Archive path or file: URL (default: wpbackup2-<db>-<UTC timestamp>.tar.gz
|
|
302
|
+
in --archive-dir). Inside the Site Root it is written with a warning: the
|
|
303
|
+
web server may serve it
|
|
304
|
+
--wp-config PATH Use this wp-config.php instead of searching for it
|
|
305
|
+
--mode {files,db,all}
|
|
306
|
+
What to back up: files, db, or all (default: all)
|
|
307
|
+
--encrypt-with-passphrase
|
|
308
|
+
Encrypt the archive with a passphrase (--passphrase,
|
|
309
|
+
L3IO_WP_BACKUP_PASSPHRASE, the config file or a prompt)
|
|
310
|
+
--passphrase SECRET Passphrase that encrypts the archive (with --encrypt-with-passphrase). The
|
|
311
|
+
value is visible in the process list; prefer L3IO_WP_BACKUP_PASSPHRASE or
|
|
312
|
+
the config file (chmod 600)
|
|
313
|
+
--exclude PATTERN Leave matching paths out of the archive (repeatable)
|
|
314
|
+
--no-default-excludes
|
|
315
|
+
Do not apply the default exclusion set
|
|
316
|
+
--list-default-excludes
|
|
317
|
+
Print the default exclusion set and exit without backing up (needs no
|
|
318
|
+
--wp-dir; takes no other backup flag)
|
|
319
|
+
|
|
320
|
+
database credentials:
|
|
321
|
+
--db-credentials-aws-secret-id ID
|
|
322
|
+
Read the WordPress database account from this AWS Secrets Manager secret
|
|
323
|
+
--db-credentials-aws-region REGION
|
|
324
|
+
AWS region of --db-credentials-aws-secret-id (needs the secret id)
|
|
325
|
+
--db-user USER WordPress database account to dump as, instead of wp-config.php's (give
|
|
326
|
+
--db-password with it)
|
|
327
|
+
--db-password SECRET Password of --db-user. The value is visible in the process list; prefer
|
|
328
|
+
L3IO_WP_BACKUP_DB_PASSWORD or the config file (chmod 600)
|
|
329
|
+
--db-tls {required,verify-ca,verify-identity}
|
|
330
|
+
Require TLS on every database connection, at this level (default: no
|
|
331
|
+
requirement, as WordPress connects)
|
|
332
|
+
--db-tls-ca FILE CA bundle that verifies the database server, with --db-tls verify-ca or
|
|
333
|
+
verify-identity (default: the system store)
|
|
334
|
+
|
|
335
|
+
exit codes:
|
|
336
|
+
0 OK the Run succeeded
|
|
337
|
+
1 FAILED the Run failed for a reason no other code names
|
|
338
|
+
2 USAGE the command line was invalid
|
|
339
|
+
3 REFUSED a check found a reason not to act; nothing was done
|
|
340
|
+
4 CREDENTIALS credentials were missing, unusable or rejected
|
|
341
|
+
5 UNREACHABLE the database server could not be reached
|
|
342
|
+
7 NOT_READY a required tool or optional extra is not available
|
|
343
|
+
8 VERIFICATION_FAILED the archive failed verification
|
|
344
|
+
9 LOCKED another Run holds the site lock
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
```text
|
|
348
|
+
$ wpbackup2 restore --help
|
|
349
|
+
usage: wpbackup2 restore [-h] [--config PATH] --wp-dir WP_DIR --archive ARCHIVE
|
|
350
|
+
[--archive-dir DIR] [--mode {files,db,all}] [--cleanfirst]
|
|
351
|
+
[--drop-database] [--force] [--whatIf] [--wp-config PATH]
|
|
352
|
+
[--overwrite-wp-config] [--reset-existing-db-user] [--no-safety-snapshot]
|
|
353
|
+
[--require-encrypted] [--passphrase SECRET] [--snapshot-dir PATH]
|
|
354
|
+
[--archive-database USER@HOST[:PORT]/NAME]
|
|
355
|
+
[--db-tls {required,verify-ca,verify-identity}] [--db-tls-ca FILE]
|
|
356
|
+
[--admin-tls {required,verify-ca,verify-identity}] [--admin-tls-ca FILE]
|
|
357
|
+
[--resolve-interrupted-run RUN_ID] [--admin-user USER]
|
|
358
|
+
[--admin-password SECRET] [--admin-credentials-aws-secret-id ID]
|
|
359
|
+
[--admin-credentials-aws-region REGION] [--only-appointed-in-asg]
|
|
360
|
+
[--new-site-url URL] [--new-site-home-url URL] [--new-db-host HOST]
|
|
361
|
+
[--new-db-name NAME] [--new-db-user USER] [--new-db-password SECRET]
|
|
362
|
+
[--new-db-password-file PATH] [--allow-unencrypted]
|
|
363
|
+
[--db-credentials-aws-secret-id ID] [--db-credentials-aws-region REGION]
|
|
364
|
+
|
|
365
|
+
Perform a WordPress restore
|
|
366
|
+
|
|
367
|
+
Every option can also be set as an L3IO_WP_BACKUP_<NAME> variable
|
|
368
|
+
(--wp-dir is L3IO_WP_BACKUP_WP_DIR) or as a key in a TOML config file
|
|
369
|
+
(wp_dir = "..."); a flag wins over a variable, a variable over the file.
|
|
370
|
+
|
|
371
|
+
options:
|
|
372
|
+
-h, --help Show this help message and exit
|
|
373
|
+
--config PATH Read settings from this TOML file (also L3IO_WP_BACKUP_CONFIG; default:
|
|
374
|
+
/etc/l3io-wp-backup/config.toml, then the per-user ~/.config/l3io-wp-
|
|
375
|
+
backup/config.toml). A file holding a secret must be readable by its owner
|
|
376
|
+
only
|
|
377
|
+
--wp-dir WP_DIR Path to the root of the WordPress directory
|
|
378
|
+
--archive ARCHIVE Path or file: URL of the archive to restore from, or `latest`: the newest
|
|
379
|
+
archive of this site in --archive-dir (required, --resolve-interrupted-run
|
|
380
|
+
included)
|
|
381
|
+
--archive-dir DIR Directory --archive latest searches (default: the current directory, or
|
|
382
|
+
the per-user data directory when the current directory is inside the Site
|
|
383
|
+
Root)
|
|
384
|
+
|
|
385
|
+
restore mode:
|
|
386
|
+
Together these select one Restore Mode; any other combination is refused.
|
|
387
|
+
|
|
388
|
+
--mode {files,db,all}
|
|
389
|
+
What to restore: files, db, or all (default: all)
|
|
390
|
+
--cleanfirst Clear the Site Root before restoring the files. The database is replaced
|
|
391
|
+
by every restore that loads it
|
|
392
|
+
--drop-database Deprecated; has no effect. A database restore loads into the live database
|
|
393
|
+
and drops the tables with the site's prefix first, so no previous database
|
|
394
|
+
is left to remove
|
|
395
|
+
--force Restore over a non-empty Site Root that does not look like WordPress (none
|
|
396
|
+
of wp-config.php, wp-includes/, wp-content/), or a non-empty database none
|
|
397
|
+
of whose tables has the site's table prefix. It selects no mode and
|
|
398
|
+
removes nothing
|
|
399
|
+
|
|
400
|
+
restore options:
|
|
401
|
+
The flags that do not select the Restore Mode: Dry Run, wp-config.php, the Safety Snapshot, the database administrator and relocation
|
|
402
|
+
|
|
403
|
+
--whatIf, --what-if Dry Run: run every check and query for real and print the ordered plan;
|
|
404
|
+
changes no Site or database but takes the Site Lock (also accepted before
|
|
405
|
+
`restore`)
|
|
406
|
+
--wp-config PATH Use this wp-config.php instead of searching for it
|
|
407
|
+
--overwrite-wp-config
|
|
408
|
+
Replace the target's existing wp-config.php with the archive's (only with
|
|
409
|
+
a mode that restores files), keeping the target's DB_* definitions as
|
|
410
|
+
written, getenv_docker(...) included. Refused (exit 3) when the target
|
|
411
|
+
uses getenv_docker(...) and the archive's wp-config.php does not define it
|
|
412
|
+
--reset-existing-db-user
|
|
413
|
+
When the target has no wp-config.php and the archive's database account
|
|
414
|
+
already exists: reset its password to the archive's (refused otherwise)
|
|
415
|
+
--no-safety-snapshot Do not take the Safety Snapshot before changing anything
|
|
416
|
+
--require-encrypted Refuse an archive that is not encrypted, before anything changes (guards
|
|
417
|
+
against a plain archive substituted for an encrypted one; without it a
|
|
418
|
+
plain archive is restored with a warning)
|
|
419
|
+
--passphrase SECRET Passphrase that decrypts the archive and encrypts the Safety Snapshot. The
|
|
420
|
+
value is visible in the process list; prefer L3IO_WP_BACKUP_PASSPHRASE or
|
|
421
|
+
the config file (chmod 600)
|
|
422
|
+
--snapshot-dir PATH Directory to write the Safety Snapshot to (default: the per-user state
|
|
423
|
+
directory). Refused inside the Site Root or the wp-config.php directory; a
|
|
424
|
+
directory a web server serves from elsewhere is not detected
|
|
425
|
+
--archive-database USER@HOST[:PORT]/NAME
|
|
426
|
+
When the target has no wp-config.php and the archive is not encrypted:
|
|
427
|
+
confirm the database the archive's config names (user, host, port and name
|
|
428
|
+
must all match), or the restore is refused
|
|
429
|
+
--db-tls {required,verify-ca,verify-identity}
|
|
430
|
+
Require TLS on every database connection, at this level (default: no
|
|
431
|
+
requirement, as WordPress connects)
|
|
432
|
+
--db-tls-ca FILE CA bundle that verifies the database server, with --db-tls verify-ca or
|
|
433
|
+
verify-identity (default: the system store)
|
|
434
|
+
--admin-tls {required,verify-ca,verify-identity}
|
|
435
|
+
Deprecated: use --db-tls
|
|
436
|
+
--admin-tls-ca FILE Deprecated: use --db-tls-ca
|
|
437
|
+
--resolve-interrupted-run RUN_ID
|
|
438
|
+
Resolve the journal left by the interrupted Run with this ID, then stop:
|
|
439
|
+
nothing is restored (run the restore again afterwards)
|
|
440
|
+
--admin-user, --admin-username USER
|
|
441
|
+
Database administrator username (give --admin-password with it, or set
|
|
442
|
+
L3IO_WP_BACKUP_ADMIN_PASSWORD)
|
|
443
|
+
--admin-password SECRET
|
|
444
|
+
Database administrator password. The value is visible in the process list;
|
|
445
|
+
prefer L3IO_WP_BACKUP_ADMIN_PASSWORD or the config file (chmod 600)
|
|
446
|
+
--admin-credentials-aws-secret-id ID
|
|
447
|
+
Read the database administrator from this AWS Secrets Manager secret
|
|
448
|
+
--admin-credentials-aws-region REGION
|
|
449
|
+
AWS region of --admin-credentials-aws-secret-id (needs the secret id)
|
|
450
|
+
--only-appointed-in-asg
|
|
451
|
+
Removed; accepted only to say so (not available in this build)
|
|
452
|
+
--new-site-url URL Relocate the Site to this URL
|
|
453
|
+
--new-site-home-url URL
|
|
454
|
+
Relocate the Site's home URL
|
|
455
|
+
--new-db-host HOST Relocate to this database host
|
|
456
|
+
--new-db-name NAME Relocate to this database name; it must be missing (created, and dropped
|
|
457
|
+
again if the restore fails) or empty, --force or not
|
|
458
|
+
--new-db-user USER Relocate to this database user
|
|
459
|
+
--new-db-password SECRET
|
|
460
|
+
Password of --new-db-user. The value is visible in the process list;
|
|
461
|
+
prefer --new-db-password-file, L3IO_WP_BACKUP_NEW_DB_PASSWORD or the
|
|
462
|
+
config file (chmod 600)
|
|
463
|
+
--new-db-password-file PATH
|
|
464
|
+
Read the password of --new-db-user from this file (its first line; make it
|
|
465
|
+
readable only by you)
|
|
466
|
+
--allow-unencrypted Deprecated, no effect: a plain archive restored with a passphrase proceeds
|
|
467
|
+
with a warning; --require-encrypted refuses it
|
|
468
|
+
|
|
469
|
+
database credentials:
|
|
470
|
+
--db-credentials-aws-secret-id ID
|
|
471
|
+
Read the WordPress database account from this AWS Secrets Manager secret
|
|
472
|
+
--db-credentials-aws-region REGION
|
|
473
|
+
AWS region of --db-credentials-aws-secret-id (needs the secret id)
|
|
474
|
+
|
|
475
|
+
exit codes:
|
|
476
|
+
0 OK the Run succeeded
|
|
477
|
+
1 FAILED the Run failed for a reason no other code names
|
|
478
|
+
2 USAGE the command line was invalid
|
|
479
|
+
3 REFUSED a check found a reason not to act; nothing was done
|
|
480
|
+
4 CREDENTIALS credentials were missing, unusable or rejected
|
|
481
|
+
5 UNREACHABLE the database server could not be reached
|
|
482
|
+
7 NOT_READY a required tool or optional extra is not available
|
|
483
|
+
8 VERIFICATION_FAILED the archive failed verification
|
|
484
|
+
9 LOCKED another Run holds the site lock
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
```text
|
|
488
|
+
$ wpbackup2 verify --help
|
|
489
|
+
usage: wpbackup2 verify [-h] [--config PATH] [--whatIf] --archive ARCHIVE [--authenticate]
|
|
490
|
+
[--passphrase SECRET]
|
|
491
|
+
|
|
492
|
+
Check an archive without touching a site
|
|
493
|
+
|
|
494
|
+
Every option can also be set as an L3IO_WP_BACKUP_<NAME> variable
|
|
495
|
+
(--wp-dir is L3IO_WP_BACKUP_WP_DIR) or as a key in a TOML config file
|
|
496
|
+
(wp_dir = "..."); a flag wins over a variable, a variable over the file.
|
|
497
|
+
|
|
498
|
+
options:
|
|
499
|
+
-h, --help Show this help message and exit
|
|
500
|
+
--config PATH Read settings from this TOML file (also L3IO_WP_BACKUP_CONFIG; default:
|
|
501
|
+
/etc/l3io-wp-backup/config.toml, then the per-user ~/.config/l3io-wp-
|
|
502
|
+
backup/config.toml). A file holding a secret must be readable by its owner
|
|
503
|
+
only
|
|
504
|
+
--whatIf, --what-if Accepted for symmetry: verify is read-only, so it changes nothing
|
|
505
|
+
--archive ARCHIVE Path or file: URL of the archive to check
|
|
506
|
+
--authenticate Decrypt the encrypted payload to prove the archive authentic; without it,
|
|
507
|
+
verify proves only that its checksums are consistent. The passphrase comes
|
|
508
|
+
from --passphrase, L3IO_WP_BACKUP_PASSPHRASE, the config file or a prompt.
|
|
509
|
+
It does not show the archive is the latest one: an older archive under the
|
|
510
|
+
same passphrase also passes
|
|
511
|
+
--passphrase SECRET Passphrase that decrypts the archive (with --authenticate). The value is
|
|
512
|
+
visible in the process list; prefer L3IO_WP_BACKUP_PASSPHRASE or the config
|
|
513
|
+
file (chmod 600)
|
|
514
|
+
|
|
515
|
+
exit codes:
|
|
516
|
+
0 OK the Run succeeded
|
|
517
|
+
1 FAILED the Run failed for a reason no other code names
|
|
518
|
+
2 USAGE the command line was invalid
|
|
519
|
+
3 REFUSED a check found a reason not to act; nothing was done
|
|
520
|
+
4 CREDENTIALS credentials were missing, unusable or rejected
|
|
521
|
+
5 UNREACHABLE the database server could not be reached
|
|
522
|
+
7 NOT_READY a required tool or optional extra is not available
|
|
523
|
+
8 VERIFICATION_FAILED the archive failed verification
|
|
524
|
+
9 LOCKED another Run holds the site lock
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
```text
|
|
528
|
+
$ wpbackup2 doctor --help
|
|
529
|
+
usage: wpbackup2 doctor [-h] [--config PATH] [--wp-dir DIR] [--archive TARGET] [--json]
|
|
530
|
+
|
|
531
|
+
Report whether this host is ready to back up and restore
|
|
532
|
+
|
|
533
|
+
Every option can also be set as an L3IO_WP_BACKUP_<NAME> variable
|
|
534
|
+
(--wp-dir is L3IO_WP_BACKUP_WP_DIR) or as a key in a TOML config file
|
|
535
|
+
(wp_dir = "..."); a flag wins over a variable, a variable over the file.
|
|
536
|
+
|
|
537
|
+
options:
|
|
538
|
+
-h, --help Show this help message and exit
|
|
539
|
+
--config PATH Read settings from this TOML file (also L3IO_WP_BACKUP_CONFIG; default:
|
|
540
|
+
/etc/l3io-wp-backup/config.toml, then the per-user ~/.config/l3io-wp-
|
|
541
|
+
backup/config.toml). A file holding a secret must be readable by its owner
|
|
542
|
+
only
|
|
543
|
+
--wp-dir DIR Also check this Site Root
|
|
544
|
+
--archive TARGET Also check this archive target
|
|
545
|
+
--json Print the diagnosis as JSON
|
|
546
|
+
|
|
547
|
+
exit codes:
|
|
548
|
+
0 OK the Run succeeded
|
|
549
|
+
1 FAILED the Run failed for a reason no other code names
|
|
550
|
+
2 USAGE the command line was invalid
|
|
551
|
+
3 REFUSED a check found a reason not to act; nothing was done
|
|
552
|
+
4 CREDENTIALS credentials were missing, unusable or rejected
|
|
553
|
+
5 UNREACHABLE the database server could not be reached
|
|
554
|
+
7 NOT_READY a required tool or optional extra is not available
|
|
555
|
+
8 VERIFICATION_FAILED the archive failed verification
|
|
556
|
+
9 LOCKED another Run holds the site lock
|
|
557
|
+
```
|
|
558
|
+
<!-- END GENERATED: cli-usage -->
|
|
559
|
+
|
|
560
|
+
### Exit codes
|
|
561
|
+
|
|
562
|
+
A scheduler can branch on the exit code alone: `0` means the Run succeeded and
|
|
563
|
+
every failure exits non-zero with a code naming its kind. The table is
|
|
564
|
+
append-only — a code is never renumbered or reused. Ctrl-C exits with the
|
|
565
|
+
shell's `130` (`INTERRUPTED` in the log), which is not part of the table.
|
|
566
|
+
|
|
567
|
+
<!-- BEGIN GENERATED: exit-codes (tools/gen_cli_docs.py) -->
|
|
568
|
+
| Code | Name | Meaning |
|
|
569
|
+
|---:|---|---|
|
|
570
|
+
| 0 | `OK` | the Run succeeded |
|
|
571
|
+
| 1 | `FAILED` | the Run failed for a reason no other code names |
|
|
572
|
+
| 2 | `USAGE` | the command line was invalid |
|
|
573
|
+
| 3 | `REFUSED` | a check found a reason not to act; nothing was done |
|
|
574
|
+
| 4 | `CREDENTIALS` | credentials were missing, unusable or rejected |
|
|
575
|
+
| 5 | `UNREACHABLE` | the database server could not be reached |
|
|
576
|
+
| 7 | `NOT_READY` | a required tool or optional extra is not available |
|
|
577
|
+
| 8 | `VERIFICATION_FAILED` | the archive failed verification |
|
|
578
|
+
| 9 | `LOCKED` | another Run holds the site lock |
|
|
579
|
+
|
|
580
|
+
Reserved for the `l3io-wp-*` family and never used: 6.
|
|
581
|
+
<!-- END GENERATED: exit-codes -->
|
|
582
|
+
|
|
583
|
+
## Development
|
|
584
|
+
|
|
585
|
+
### Prerequisites
|
|
586
|
+
|
|
587
|
+
Python 3.11 or newer, and [uv](https://docs.astral.sh/uv/).
|
|
588
|
+
|
|
589
|
+
### Installing dependencies
|
|
590
|
+
|
|
591
|
+
```shell
|
|
592
|
+
uv sync
|
|
593
|
+
```
|