diff --git a/src/tufup/repo/__init__.py b/src/tufup/repo/__init__.py index 30883f8..34a4d76 100644 --- a/src/tufup/repo/__init__.py +++ b/src/tufup/repo/__init__.py @@ -703,15 +703,18 @@ def add_bundle( self, new_bundle_dir: Union[pathlib.Path, str], new_version: Optional[str] = None, + skip_patch: bool = False, ): """ Adds a new application bundle to the local repository. An archive file is created from the app bundle, and this file is - added to the tuf repository. If a previous archive version is - found, a patch file is also created and added to the repository. + added to the tuf repository. If a previous archive version is found, + a patch file is also created and added to the repository, unless + `skip_patch` is True. - Note the changes are not published yet: call publish_changes() for that + Note the changes are not published yet: call `publish_changes()` for + that. """ # enforce path object new_bundle_dir = pathlib.Path(new_bundle_dir) @@ -734,7 +737,7 @@ def add_bundle( # register new archive self.roles.add_or_update_target(local_path=new_archive.path) # create patch, if possible, and register that too - if latest_archive: + if latest_archive and not skip_patch: patch_path = Patcher.create_patch( src_path=self.targets_dir / latest_archive.path, dst_path=self.targets_dir / new_archive.path, diff --git a/src/tufup/repo/cli.py b/src/tufup/repo/cli.py index 6407ee2..0b8ae90 100644 --- a/src/tufup/repo/cli.py +++ b/src/tufup/repo/cli.py @@ -19,6 +19,7 @@ ), targets_add_app_version='Application version (PEP440 compliant)', targets_add_bundle_dir='Directory containing application bundle.', + targets_add_skip_patch='Skip patch creation.', targets_remove_latest='Remove latest app bundle from the repository.', keys_subcommands='Optional commands to add or replace keys.', keys_new_key_name='Name of new private key (public key gets .pub suffix).', @@ -80,6 +81,13 @@ def get_parser() -> argparse.ArgumentParser: subparser_targets_add.add_argument( 'bundle_dir', help=HELP['targets_add_bundle_dir'] ) + subparser_targets_add.add_argument( + '-s', + '--skip-patch', + action='store_true', + required=False, + help=HELP['targets_add_skip_patch'], + ) subparser_targets_remove = targets_subparsers.add_parser( 'remove-latest', help=HELP['targets_remove_latest'] ) @@ -274,7 +282,9 @@ def _cmd_targets(options: argparse.Namespace): if hasattr(options, 'app_version') and hasattr(options, 'bundle_dir'): _print_info('Adding bundle...') repository.add_bundle( - new_version=options.app_version, new_bundle_dir=options.bundle_dir + new_version=options.app_version, + new_bundle_dir=options.bundle_dir, + skip_patch=options.skip_patch, ) else: _print_info('Removing latest bundle...') diff --git a/tests/test_repo.py b/tests/test_repo.py index 80f3de9..25bbd8d 100644 --- a/tests/test_repo.py +++ b/tests/test_repo.py @@ -27,7 +27,7 @@ from tufup.common import TargetMeta import tufup.repo # for patching from tufup.repo import ( - Base, in_, Keys, make_gztar_archive, Repository, Roles, SUFFIX_PUB + Base, in_, Keys, make_gztar_archive, Repository, Roles, SUFFIX_PUB, SUFFIX_PATCH ) @@ -680,6 +680,29 @@ def test_add_bundle(self): repo.add_bundle(new_version='1.0', new_bundle_dir=bundle_dir) self.assertTrue((repo.metadata_dir / 'targets.json').exists()) + def test_add_bundle_no_patch(self): + # prepare + bundle_dir = self.temp_dir_path / 'dist' / 'test_app' + bundle_dir.mkdir(parents=True) + bundle_file = bundle_dir / 'dummy.exe' + bundle_file.write_text('this is version 1') + repo = Repository( + app_name='test', + keys_dir=self.temp_dir_path / 'keystore', + repo_dir=self.temp_dir_path / 'repo', + ) + repo.initialize() # todo: make test independent... + repo.add_bundle(new_version='1.0', new_bundle_dir=bundle_dir) + # test + bundle_file.write_text('much has changed in version 2') + repo.add_bundle( + new_version='2.0', new_bundle_dir=bundle_dir, skip_patch=True + ) + self.assertTrue((repo.metadata_dir / 'targets.json').exists()) + target_keys = list(repo.roles.targets.signed.targets.keys()) + self.assertEqual(2, len(target_keys)) + self.assertFalse(any(key.endswith(SUFFIX_PATCH) for key in target_keys)) + def test_remove_latest_bundle(self): # prepare bundle_dir = self.temp_dir_path / 'dist' / 'test_app' diff --git a/tests/test_repo_cli.py b/tests/test_repo_cli.py index a6187a0..8c16736 100644 --- a/tests/test_repo_cli.py +++ b/tests/test_repo_cli.py @@ -16,6 +16,7 @@ def test_get_parser(self): 'init --debug', 'targets add 1.0 c:\\my_bundle_dir c:\\private_keys', 'targets -d add 1.0 c:\\my_bundle_dir c:\\private_keys', + 'targets -d add -s 1.0 c:\\my_bundle_dir c:\\private_keys', 'targets remove-latest c:\\private_keys', 'keys my-key-name -c -e', 'keys my-key-name add root c:\\private_keys d:\\more_private_keys', @@ -114,13 +115,17 @@ def test__cmd_targets_add(self): version = '1.0' bundle_dir = 'dummy' key_dirs = ['c:\\my_private_keys'] + skip_patch = True options = argparse.Namespace( - app_version=version, bundle_dir=bundle_dir, key_dirs=key_dirs + app_version=version, + bundle_dir=bundle_dir, + key_dirs=key_dirs, + skip_patch=skip_patch, ) with patch('tufup.repo.cli.Repository', self.mock_repo_class): tufup.repo.cli._cmd_targets(options=options) self.mock_repo.add_bundle.assert_called_with( - new_version=version, new_bundle_dir=bundle_dir + new_version=version, new_bundle_dir=bundle_dir, skip_patch=skip_patch, ) self.mock_repo.publish_changes.assert_called_with( private_key_dirs=key_dirs