omoshiroi-packager 0.1.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.
Files changed (26) hide show
  1. omoshiroi_packager-0.1.0/.env.example +11 -0
  2. omoshiroi_packager-0.1.0/LICENSE +21 -0
  3. omoshiroi_packager-0.1.0/MANIFEST.in +14 -0
  4. omoshiroi_packager-0.1.0/PKG-INFO +39 -0
  5. omoshiroi_packager-0.1.0/README.md +8 -0
  6. omoshiroi_packager-0.1.0/omoshiroi_packager/__init__.py +96 -0
  7. omoshiroi_packager-0.1.0/omoshiroi_packager/config.py +104 -0
  8. omoshiroi_packager-0.1.0/omoshiroi_packager/core/__init__.py +11 -0
  9. omoshiroi_packager-0.1.0/omoshiroi_packager/core/manifest.py +160 -0
  10. omoshiroi_packager-0.1.0/omoshiroi_packager/core/packager.py +108 -0
  11. omoshiroi_packager-0.1.0/omoshiroi_packager/core/updater.py +155 -0
  12. omoshiroi_packager-0.1.0/omoshiroi_packager/exceptions.py +38 -0
  13. omoshiroi_packager-0.1.0/omoshiroi_packager/models.py +122 -0
  14. omoshiroi_packager-0.1.0/omoshiroi_packager/packages.py +196 -0
  15. omoshiroi_packager-0.1.0/omoshiroi_packager/utils/__init__.py +17 -0
  16. omoshiroi_packager-0.1.0/omoshiroi_packager/utils/file_utils.py +197 -0
  17. omoshiroi_packager-0.1.0/omoshiroi_packager/utils/hash_utils.py +71 -0
  18. omoshiroi_packager-0.1.0/omoshiroi_packager/utils/zip_utils.py +99 -0
  19. omoshiroi_packager-0.1.0/omoshiroi_packager/version.py +407 -0
  20. omoshiroi_packager-0.1.0/omoshiroi_packager.egg-info/PKG-INFO +39 -0
  21. omoshiroi_packager-0.1.0/omoshiroi_packager.egg-info/SOURCES.txt +24 -0
  22. omoshiroi_packager-0.1.0/omoshiroi_packager.egg-info/dependency_links.txt +1 -0
  23. omoshiroi_packager-0.1.0/omoshiroi_packager.egg-info/requires.txt +10 -0
  24. omoshiroi_packager-0.1.0/omoshiroi_packager.egg-info/top_level.txt +1 -0
  25. omoshiroi_packager-0.1.0/pyproject.toml +47 -0
  26. omoshiroi_packager-0.1.0/setup.cfg +4 -0
@@ -0,0 +1,11 @@
1
+ # Omoshiroi Packager Configuration
2
+ # Copy this file to .env and update the paths
3
+
4
+ # Directory for manifest files
5
+ MANIFEST_DIR=/path/to/manifests
6
+
7
+ # Root directory of the project
8
+ PROJECT_DIR=/path/to/project
9
+
10
+ # Temporary directory for build artifacts
11
+ TMP_DIR=/path/to/tmp
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ovcharenski
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,14 @@
1
+ # Include files for distribution
2
+ include README.md
3
+ include LICENSE
4
+ include pyproject.toml
5
+ include .env.example
6
+
7
+ # Exclude test files
8
+ prune tests/
9
+ prune .github/
10
+
11
+ # Exclude development files
12
+ exclude build.py
13
+ exclude .gitignore
14
+ exclude .pre-commit-config.yaml
@@ -0,0 +1,39 @@
1
+ Metadata-Version: 2.4
2
+ Name: omoshiroi-packager
3
+ Version: 0.1.0
4
+ Summary: Version parser with multiple channels support
5
+ Author-email: ovcharenski <stun.bread.frog@outlook.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://ns-staff.ovcharenski.ru/projects/omoshiroi-packager
8
+ Project-URL: Repository, https://github.com/ovcharenski/omoshiroi-packager
9
+ Classifier: Development Status :: 5 - Production/Stable
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.8
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Requires-Python: >=3.8
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: python-dotenv>=1.0.0
22
+ Provides-Extra: dev
23
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
24
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
25
+ Requires-Dist: build>=0.10.0; extra == "dev"
26
+ Requires-Dist: twine>=4.0.0; extra == "dev"
27
+ Requires-Dist: black>=22.0.0; extra == "dev"
28
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
29
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
30
+ Dynamic: license-file
31
+
32
+ # Omoshiroi Packager
33
+
34
+ Python package for parsing and comparing software versions with multiple channels.
35
+
36
+ ## Installation
37
+
38
+ ```bash
39
+ pip install omoshiroi-packager
@@ -0,0 +1,8 @@
1
+ # Omoshiroi Packager
2
+
3
+ Python package for parsing and comparing software versions with multiple channels.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install omoshiroi-packager
@@ -0,0 +1,96 @@
1
+ """
2
+ Omoshiroi Packager - Version management and packaging utilities.
3
+
4
+ A comprehensive package for parsing, comparing, and managing software versions
5
+ with multiple channel support (dev, alpha, beta, rc, stable, hotfix).
6
+ """
7
+
8
+ from .config import Config
9
+ from .core import ManifestManager, Packager, Updater
10
+ from .exceptions import (
11
+ ConfigError,
12
+ FileError,
13
+ HashError,
14
+ ManifestError,
15
+ PackageError,
16
+ PackError,
17
+ VersionError,
18
+ )
19
+ from .models import FileProperties, Manifest, UpdateCheckResult
20
+ from .packages import (
21
+ check_update,
22
+ get_all_versions,
23
+ get_latest_version,
24
+ make_manifest,
25
+ pack_by_channel,
26
+ pack_version,
27
+ reset_config,
28
+ )
29
+ from .utils import (
30
+ IgnorePattern,
31
+ ProjectConfig,
32
+ ProjectFiles,
33
+ create_zip_from_files,
34
+ extract_zip,
35
+ generate_file_hash,
36
+ get_zip_file_list,
37
+ verify_file_hash,
38
+ )
39
+ from .version import (
40
+ InvalidVersion, # ← ДОБАВИТЬ ЭТО
41
+ Version,
42
+ is_valid,
43
+ latest_stable,
44
+ latest_version,
45
+ parse,
46
+ sort_versions,
47
+ )
48
+
49
+ __version__ = "0.1.0"
50
+ __author__ = "ovcharenski"
51
+
52
+ __all__ = [
53
+ # Version
54
+ "InvalidVersion", # ← ДОБАВИТЬ ЭТО
55
+ "Version",
56
+ "parse",
57
+ "is_valid",
58
+ "sort_versions",
59
+ "latest_version",
60
+ "latest_stable",
61
+ # Config
62
+ "Config",
63
+ # Models
64
+ "Manifest",
65
+ "FileProperties",
66
+ "UpdateCheckResult",
67
+ # Exceptions
68
+ "PackageError",
69
+ "ManifestError",
70
+ "FileError",
71
+ "HashError",
72
+ "VersionError",
73
+ "ConfigError",
74
+ "PackError",
75
+ # Main API
76
+ "get_latest_version",
77
+ "check_update",
78
+ "pack_version",
79
+ "pack_by_channel",
80
+ "make_manifest",
81
+ "get_all_versions",
82
+ "reset_config",
83
+ # Core
84
+ "ManifestManager",
85
+ "Packager",
86
+ "Updater",
87
+ # Utils
88
+ "IgnorePattern",
89
+ "ProjectFiles",
90
+ "ProjectConfig",
91
+ "generate_file_hash",
92
+ "verify_file_hash",
93
+ "create_zip_from_files",
94
+ "extract_zip",
95
+ "get_zip_file_list",
96
+ ]
@@ -0,0 +1,104 @@
1
+ """
2
+ Configuration management for the packages module.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import os
8
+ from dataclasses import dataclass
9
+ from pathlib import Path
10
+
11
+ from dotenv import load_dotenv
12
+
13
+ from .exceptions import ConfigError
14
+
15
+
16
+ @dataclass
17
+ class Config:
18
+ """
19
+ Application configuration.
20
+
21
+ Attributes:
22
+ manifest_dir: Directory for manifest files
23
+ project_dir: Root directory of the project
24
+ tmp_dir: Temporary directory for build artifacts
25
+ """
26
+
27
+ manifest_dir: Path
28
+ project_dir: Path
29
+ tmp_dir: Path
30
+
31
+ @classmethod
32
+ def from_env(cls, load_dotenv_file: bool = True) -> Config:
33
+ """
34
+ Create configuration from environment variables.
35
+
36
+ Args:
37
+ load_dotenv_file: Whether to load .env file
38
+
39
+ Returns:
40
+ Config instance
41
+
42
+ Raises:
43
+ ConfigError: If required variables are missing
44
+ """
45
+ if load_dotenv_file:
46
+ load_dotenv()
47
+
48
+ manifest_dir = os.getenv("MANIFEST_DIR")
49
+ project_dir = os.getenv("PROJECT_DIR")
50
+ tmp_dir = os.getenv("TMP_DIR")
51
+
52
+ missing = []
53
+ if not manifest_dir:
54
+ missing.append("MANIFEST_DIR")
55
+ if not project_dir:
56
+ missing.append("PROJECT_DIR")
57
+ if not tmp_dir:
58
+ missing.append("TMP_DIR")
59
+
60
+ if missing:
61
+ raise ConfigError(
62
+ f"Missing required environment variables: {', '.join(missing)}"
63
+ )
64
+
65
+ return cls(
66
+ manifest_dir=Path(manifest_dir),
67
+ project_dir=Path(project_dir),
68
+ tmp_dir=Path(tmp_dir),
69
+ )
70
+
71
+ def ensure_directories(self) -> None:
72
+ """Ensure all required directories exist."""
73
+ for dir_path in [self.manifest_dir, self.tmp_dir]:
74
+ dir_path.mkdir(parents=True, exist_ok=True)
75
+
76
+ @property
77
+ def manifest_stable_path(self) -> Path:
78
+ """Path to stable manifest file."""
79
+ return self.manifest_dir / "manifest-stable.json"
80
+
81
+ @property
82
+ def manifest_beta_path(self) -> Path:
83
+ """Path to beta manifest file."""
84
+ return self.manifest_dir / "manifest-beta.json"
85
+
86
+ def get_manifest_path(self, channel: str) -> Path:
87
+ """
88
+ Get manifest path for a channel.
89
+
90
+ Args:
91
+ channel: Channel name (stable, beta, dev, etc.)
92
+
93
+ Returns:
94
+ Path to manifest file
95
+ """
96
+ return self.manifest_dir / f"manifest-{channel}.json"
97
+
98
+ def get_ignore_file_path(self) -> Path:
99
+ """Get path to .ignore file."""
100
+ return self.project_dir / ".ignore"
101
+
102
+ def get_config_file_path(self) -> Path:
103
+ """Get path to .config file."""
104
+ return self.project_dir / ".config"
@@ -0,0 +1,11 @@
1
+ """Core functionality for omoshiroi-packager."""
2
+
3
+ from .manifest import ManifestManager
4
+ from .packager import Packager
5
+ from .updater import Updater
6
+
7
+ __all__ = [
8
+ "ManifestManager",
9
+ "Packager",
10
+ "Updater",
11
+ ]
@@ -0,0 +1,160 @@
1
+ """
2
+ Manifest management for the packages module.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import json
8
+ import time
9
+ from pathlib import Path
10
+ from typing import Any, Dict, List, Optional
11
+
12
+ from ..config import Config
13
+ from ..models import FileProperties, Manifest
14
+ from ..exceptions import ManifestError
15
+ from ..utils.file_utils import ProjectFiles
16
+ from ..utils.hash_utils import generate_file_hash
17
+
18
+
19
+ class ManifestManager:
20
+ """Manager for package manifests."""
21
+
22
+ def __init__(self, config: Config):
23
+ """
24
+ Initialize manifest manager.
25
+
26
+ Args:
27
+ config: Application configuration
28
+ """
29
+ self.config = config
30
+ self._cache: Dict[str, Optional[Manifest]] = {}
31
+
32
+ def create_manifest(
33
+ self,
34
+ version: str,
35
+ channel: str,
36
+ project_files: Optional[ProjectFiles] = None,
37
+ ) -> Manifest:
38
+ """
39
+ Create a new manifest for a version.
40
+
41
+ Args:
42
+ version: Version string
43
+ channel: Channel name
44
+ project_files: ProjectFiles instance (creates new if not provided)
45
+
46
+ Returns:
47
+ Manifest object
48
+ """
49
+ if project_files is None:
50
+ project_files = ProjectFiles(self.config.project_dir)
51
+
52
+ files_list = []
53
+ for file_path in project_files.get_files():
54
+ full_path = project_files.get_full_path(file_path)
55
+
56
+ if full_path.exists():
57
+ sha256, sha512 = generate_file_hash(full_path)
58
+ files_list.append(
59
+ FileProperties(
60
+ path=file_path,
61
+ size=full_path.stat().st_size,
62
+ sha256=sha256,
63
+ sha512=sha512,
64
+ )
65
+ )
66
+
67
+ return Manifest(
68
+ version=version,
69
+ channel=channel,
70
+ timestamp=int(time.time()),
71
+ files=files_list,
72
+ )
73
+
74
+ def save_manifest(self, manifest: Manifest, channel: Optional[str] = None) -> Path:
75
+ """
76
+ Save a manifest to disk.
77
+
78
+ Args:
79
+ manifest: Manifest object
80
+ channel: Channel name (uses manifest.channel if not provided)
81
+
82
+ Returns:
83
+ Path to saved manifest file
84
+ """
85
+ channel = channel or manifest.channel
86
+ manifest_path = self.config.get_manifest_path(channel)
87
+
88
+ # Ensure manifest directory exists
89
+ manifest_path.parent.mkdir(parents=True, exist_ok=True)
90
+
91
+ try:
92
+ with open(manifest_path, "w", encoding="utf-8") as f:
93
+ json.dump(manifest.to_dict(), f, indent=2)
94
+ except OSError as e:
95
+ raise ManifestError(f"Failed to save manifest {manifest_path}: {e}")
96
+
97
+ return manifest_path
98
+
99
+ def load_manifest(self, channel: str) -> Optional[Manifest]:
100
+ """
101
+ Load a manifest from disk.
102
+
103
+ Args:
104
+ channel: Channel name
105
+
106
+ Returns:
107
+ Manifest object or None if not found
108
+ """
109
+ # Check cache
110
+ if channel in self._cache:
111
+ return self._cache[channel]
112
+
113
+ manifest_path = self.config.get_manifest_path(channel)
114
+
115
+ if not manifest_path.exists():
116
+ return None
117
+
118
+ try:
119
+ with open(manifest_path, "r", encoding="utf-8") as f:
120
+ data = json.load(f)
121
+
122
+ manifest = Manifest.from_dict(data)
123
+ self._cache[channel] = manifest
124
+ return manifest
125
+
126
+ except (OSError, json.JSONDecodeError) as e:
127
+ raise ManifestError(f"Failed to load manifest {manifest_path}: {e}")
128
+
129
+ def get_version(self, channel: str) -> Optional[str]:
130
+ """
131
+ Get version from a channel's manifest.
132
+
133
+ Args:
134
+ channel: Channel name
135
+
136
+ Returns:
137
+ Version string or None
138
+ """
139
+ manifest = self.load_manifest(channel)
140
+ return manifest.version if manifest else None
141
+
142
+ def get_all_manifests(self) -> Dict[str, Optional[Manifest]]:
143
+ """
144
+ Get manifests for all channels.
145
+
146
+ Returns:
147
+ Dictionary mapping channel to Manifest
148
+ """
149
+ # Channels list - can be extended
150
+ channels = ["dev", "alpha", "beta", "rc", "stable", "hotfix"]
151
+
152
+ result = {}
153
+ for channel in channels:
154
+ result[channel] = self.load_manifest(channel)
155
+
156
+ return result
157
+
158
+ def clear_cache(self) -> None:
159
+ """Clear the manifest cache."""
160
+ self._cache.clear()
@@ -0,0 +1,108 @@
1
+ """
2
+ Packaging functionality for the packages module.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from io import BytesIO
8
+ from pathlib import Path
9
+ from typing import Optional, Tuple
10
+
11
+ from ..config import Config
12
+ from ..exceptions import PackError
13
+ from ..utils.file_utils import ProjectFiles, ProjectConfig
14
+ from ..utils.zip_utils import create_zip_from_files
15
+ from ..version import parse
16
+ from .manifest import ManifestManager
17
+
18
+
19
+ class Packager:
20
+ """Handler for packaging versions."""
21
+
22
+ def __init__(self, config: Config):
23
+ """
24
+ Initialize packager.
25
+
26
+ Args:
27
+ config: Application configuration
28
+ """
29
+ self.config = config
30
+ self.manifest_manager = ManifestManager(config)
31
+
32
+ def pack_version(
33
+ self,
34
+ version: Optional[str] = None,
35
+ channel: Optional[str] = None,
36
+ ) -> Tuple[BytesIO, Path]:
37
+ """
38
+ Package a version.
39
+
40
+ Args:
41
+ version: Version string (reads from .config if not provided)
42
+ channel: Channel name (parsed from version if not provided)
43
+
44
+ Returns:
45
+ Tuple of (zip_data, manifest_path)
46
+
47
+ Raises:
48
+ PackError: If packaging fails
49
+ """
50
+ try:
51
+ # Get version from config if not provided
52
+ if version is None:
53
+ project_config = ProjectConfig(self.config.get_config_file_path())
54
+ version = project_config.get_version()
55
+
56
+ if version is None:
57
+ raise PackError("Version not found in .config file")
58
+
59
+ # Parse channel from version if not provided
60
+ if channel is None:
61
+ version_obj = parse(version)
62
+ channel = version_obj.channel
63
+
64
+ # Create project files handler
65
+ project_files = ProjectFiles(self.config.project_dir)
66
+
67
+ # Create manifest
68
+ manifest = self.manifest_manager.create_manifest(
69
+ version=version,
70
+ channel=channel,
71
+ project_files=project_files,
72
+ )
73
+
74
+ # Save manifest
75
+ manifest_path = self.manifest_manager.save_manifest(manifest, channel)
76
+
77
+ # Get files to zip
78
+ files = project_files.get_files()
79
+
80
+ # Create ZIP archive
81
+ zip_data = create_zip_from_files(
82
+ files=files,
83
+ base_dir=self.config.project_dir,
84
+ manifest_path=manifest_path,
85
+ )
86
+
87
+ return zip_data, manifest_path
88
+
89
+ except Exception as e:
90
+ raise PackError(f"Failed to pack version: {e}")
91
+
92
+ def pack_by_channel(self, channel: str) -> Tuple[BytesIO, Path]:
93
+ """
94
+ Package the latest version for a channel.
95
+
96
+ Args:
97
+ channel: Channel name
98
+
99
+ Returns:
100
+ Tuple of (zip_data, manifest_path)
101
+ """
102
+ # Get version from manifest
103
+ version = self.manifest_manager.get_version(channel)
104
+
105
+ if version is None:
106
+ raise PackError(f"No version found for channel: {channel}")
107
+
108
+ return self.pack_version(version, channel)