Coverage for /builds/svesch/cikit/src/cikit/macros/bundle.py: 96%

36 statements  

« prev     ^ index     » next       coverage.py v7.14.1, created at 2026-10-09 14:17 +0000

1# SPDX-License-Identifier: GPL-3.0-or-later 

2"""Bundle macro helpers for cikit. 

3 

4This module creates distributable archive bundles from a single file or 

5directory. Generated archives are copied to the public artifact storage and 

6registered as bundle artifacts for the active CI run. 

7 

8Author: 

9 Sven Schlender 

10Date: 

11 2026-09-21 

12""" 

13 

14import tarfile 

15import tempfile 

16import zipfile 

17from pathlib import Path 

18 

19from cikit.artifacts import ArtifactType 

20from cikit.errors import CiToolsError 

21 

22 

23def _create_tar_bundle(output_file: Path, source: Path, mode: str): 

24 """Create a TAR archive from one file or directory. 

25 

26 Args: 

27 output_file: Target archive path. 

28 source: File or directory to package. 

29 mode: Tarfile write mode. 

30 """ 

31 with tarfile.open(output_file, mode) as archive: 

32 if source.is_dir(): 32 ↛ 36line 32 didn't jump to line 36 because the condition on line 32 was always true

33 for item in source.iterdir(): 

34 archive.add(item, arcname=item.name) 

35 else: 

36 archive.add(source, arcname=source.name) 

37 

38 

39def _create_zip_bundle(output_file: Path, source: Path): 

40 """Create a ZIP archive from one file or directory. 

41 

42 Args: 

43 output_file: Target archive path. 

44 source: File or directory to package. 

45 """ 

46 with zipfile.ZipFile(output_file, "w", zipfile.ZIP_DEFLATED) as archive: 

47 if source.is_dir(): 

48 for item in source.rglob("*"): 

49 archive.write(item, item.relative_to(source)) 

50 else: 

51 archive.write(source, source.name) 

52 

53 

54def create(env, target_name: str, source: Path) -> Path: 

55 """Create and register a bundle archive from one file or directory. 

56 

57 The archive format is derived from ``target_name``. ZIP, TAR, and TAR.GZ 

58 archives are supported. Directory contents are placed directly in the 

59 archive root. 

60 

61 Args: 

62 env: Resolved CI run environment. 

63 target_name: Final bundle filename. 

64 source: File or directory to package. 

65 

66 Returns: 

67 Path to the registered bundle artifact. 

68 

69 Raises: 

70 CiToolsError: If the source does not exist, the archive format is 

71 unsupported, or the bundle cannot be created. 

72 """ 

73 source = env.resolve_path(source) 

74 

75 if not source.exists(): 

76 raise CiToolsError(f"Bundle source '{source}' does not exist.") 

77 

78 with tempfile.TemporaryDirectory() as temp_dir: 

79 output_file = Path(temp_dir) / target_name 

80 

81 try: 

82 if target_name.endswith(".tar.gz"): 

83 _create_tar_bundle(output_file, source, "w:gz") 

84 elif output_file.suffix == ".tar": 

85 _create_tar_bundle(output_file, source, "w") 

86 elif output_file.suffix == ".zip": 

87 _create_zip_bundle(output_file, source) 

88 else: 

89 raise CiToolsError(f"Unsupported bundle format '{target_name}'. " 

90 "Supported formats are .zip, .tar, and .tar.gz.") 

91 except (OSError, tarfile.TarError, zipfile.BadZipFile, zipfile.LargeZipFile) as exp: 

92 raise CiToolsError(f"Failed to create bundle '{target_name}': {exp}") from exp 

93 

94 artifact = env.copy_artifact(output_file, ArtifactType.BUNDLE) 

95 

96 return artifact.path