croom 0.1.0__py3-none-any.whl

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.
croom/__init__.py ADDED
@@ -0,0 +1,5 @@
1
+ from .room import Room
2
+ from .roomspec import Checksum, RoomSpec
3
+
4
+ __version__ = "0.1.0"
5
+ __all__ = ["Checksum", "Room", "RoomSpec"]
croom/helpers.py ADDED
@@ -0,0 +1,8 @@
1
+ from urllib.parse import urlparse
2
+
3
+
4
+ def is_valid_url(url: str) -> bool:
5
+ """validate an HTTP(S) url"""
6
+ parsed = urlparse(url)
7
+
8
+ return parsed.scheme in {"http", "https"} and bool(parsed.netloc)
croom/room.py ADDED
@@ -0,0 +1,2 @@
1
+ class Room:
2
+ """represents a room instance"""
croom/roomspec.py ADDED
@@ -0,0 +1,134 @@
1
+ import hashlib
2
+ import shlex
3
+ import tomllib
4
+ from dataclasses import dataclass, field
5
+ from pathlib import Path
6
+
7
+ from .helpers import is_valid_url
8
+
9
+
10
+ @dataclass
11
+ class Checksum:
12
+ """represents a checksum to verify a downloaded artifact against"""
13
+
14
+ checksum: str
15
+ type: str = "sha256"
16
+
17
+ def check(self, b: bytes) -> bool:
18
+ """check the stored checksum against bytes"""
19
+ h = hashlib.new(self.type)
20
+ h.update(b)
21
+ return h.hexdigest().strip().lower() == self.checksum.strip().lower()
22
+
23
+
24
+ @dataclass
25
+ class RoomSpecSource:
26
+ checksum: Checksum
27
+ url: str
28
+ directory: Path | None = None
29
+
30
+ def __post_init__(self):
31
+ if not is_valid_url(self.url):
32
+ raise ValueError(f"invalid url: {self.url}")
33
+
34
+ @classmethod
35
+ def from_dict(cls, d: dict) -> "RoomSpecSource":
36
+ """create a RoomSpecSource from a dict"""
37
+ dchecksum = d.get("checksum", {})
38
+ checksum_type = dchecksum.get("type", None)
39
+ checksum_value = dchecksum.get("value", None)
40
+ checksum = Checksum(checksum_value, checksum_type)
41
+
42
+ url = d.get("url", "")
43
+ directory = d.get("directory", None)
44
+
45
+ if not is_valid_url(url):
46
+ raise ValueError(f"invalid url: {url}")
47
+
48
+ return cls(checksum, url, Path(directory) if directory else None)
49
+
50
+
51
+ @dataclass
52
+ class RoomSpecConfiguration:
53
+ symlink_resolv: bool = True
54
+ can_persist: bool = True
55
+ can_cache: bool = False
56
+
57
+ @classmethod
58
+ def from_dict(cls, d: dict) -> "RoomSpecConfiguration":
59
+ """create a RoomSpecConfiguration from a dict"""
60
+ return cls(
61
+ bool(d.get("symlink-resolv", True)),
62
+ bool(d.get("can-persist", True)),
63
+ bool(d.get("can-cache", False)),
64
+ )
65
+
66
+
67
+ @dataclass
68
+ class PostExtractStep:
69
+ name: str
70
+ optional: bool = False
71
+ requires_reload: bool = False
72
+ ignore_fail: bool = False
73
+ command: list[str] = field(default_factory=list)
74
+
75
+ @classmethod
76
+ def from_dict(cls, d: dict) -> "PostExtractStep":
77
+ """create a PostExtractStep from a dict"""
78
+ name = d.get("name", "")
79
+ if not name.strip():
80
+ raise ValueError("name not found")
81
+
82
+ optional = bool(d.get("optional", False))
83
+ requires_reload = bool(d.get("requires-reload", False))
84
+ ignore_fail = bool(d.get("ignore-fail", False))
85
+ scommand = d.get("command", "")
86
+ if not scommand.strip():
87
+ raise ValueError("command not found")
88
+ command = shlex.split(scommand)
89
+
90
+ return cls(name, optional, requires_reload, ignore_fail, command)
91
+
92
+
93
+ @dataclass
94
+ class RoomSpec:
95
+ """represents a room spec"""
96
+
97
+ name: str
98
+ version: str
99
+
100
+ source: RoomSpecSource
101
+ configuration: RoomSpecConfiguration
102
+ postextract: list[PostExtractStep] = field(default_factory=list)
103
+
104
+ @classmethod
105
+ def from_toml(cls, f: Path) -> "RoomSpec":
106
+ """create a RoomSpec from a TOML file"""
107
+ with open(str(f), "rb") as fp:
108
+ d = tomllib.load(fp)
109
+ return cls.from_dict(d)
110
+
111
+ @classmethod
112
+ def from_dict(cls, d: dict) -> "RoomSpec":
113
+ """create a RoomSpec from a dict"""
114
+ name = d.get("roomspec", {}).get("name", "").strip()
115
+ version = d.get("roomspec", {}).get("version", "").strip()
116
+
117
+ if not name:
118
+ raise ValueError("name not found")
119
+
120
+ if not version:
121
+ raise ValueError("version not found")
122
+
123
+ dsrc = d.get("source", {})
124
+ rsrc = RoomSpecSource.from_dict(dsrc)
125
+
126
+ dcfg = d.get("configuration", {})
127
+ rcfg = RoomSpecConfiguration.from_dict(dcfg)
128
+
129
+ dpsteps = d.get("postextract", [])
130
+ psteps = []
131
+ for dpstep in dpsteps:
132
+ psteps.append(PostExtractStep.from_dict(dpstep))
133
+
134
+ return cls(name, version, rsrc, rcfg, psteps)
@@ -0,0 +1,52 @@
1
+ Metadata-Version: 2.5
2
+ Name: croom
3
+ Version: 0.1.0
4
+ Summary: chroot-based linux sandbox cli
5
+ Project-URL: Homepage, https://github.com/juanvel4000/croom
6
+ Project-URL: Repository, https://github.com/juanvel4000/croom
7
+ Project-URL: Issues, https://github.com/juanvel4000/croom/issues
8
+ Project-URL: Changelog, https://github.com/juanvel4000/croom/blob/main/CHANGELOG.md
9
+ Author: juanvel400
10
+ License-Expression: BSD-3-Clause
11
+ License-File: LICENSE
12
+ Keywords: chroot,cli,sandbox
13
+ Classifier: Development Status :: 2 - Pre-Alpha
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: System Administrators
16
+ Classifier: License :: OSI Approved :: BSD License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
22
+ Requires-Python: >=3.11
23
+ Provides-Extra: dev
24
+ Requires-Dist: pytest; extra == 'dev'
25
+ Requires-Dist: ruff; extra == 'dev'
26
+ Description-Content-Type: text/markdown
27
+
28
+ # croom
29
+
30
+ [![run tests](https://github.com/juanvel4000/croom/actions/workflows/tests.yml/badge.svg)](https://github.com/juanvel4000/croom/actions/workflows/tests.yml)
31
+ [![publish to pypi](https://github.com/juanvel4000/croom/actions/workflows/publish.yml/badge.svg)](https://github.com/juanvel4000/croom/actions/workflows/publish.yml)
32
+
33
+ a simple Linux sandbox CLI.
34
+
35
+ ## overview
36
+
37
+ croom provides a simpler way to create and interact with semi-isolated environments ("`Rooms`"). it uses utilities such as `unshare` and standard Unix tools to construct and provide these environments without root privileges.
38
+
39
+ croom environments are not designed to provide security isolation. instead, they provide a simple space to try, run, and test programs in custom, semi-isolated environments.
40
+
41
+ > croom uses `croom` as the project wide name and `croom` as the distribution name.
42
+
43
+ ## project status
44
+
45
+ croom is in active development. while functional, it is **not yet production-ready** for real-world usage.
46
+
47
+ - [CHANGELOG.md](CHANGELOG.md) -- release history
48
+ - [CONTRIBUTING.md](CONTRIBUTING.md) -- how to report bugs and send patches
49
+
50
+ ## license
51
+
52
+ BSD-3-Clause, see [LICENSE](LICENSE)
@@ -0,0 +1,9 @@
1
+ croom/__init__.py,sha256=uZuhD9s5qXq2lrLkyf6CKrF87C_fkKfNxVeXLmuQ0bA,130
2
+ croom/helpers.py,sha256=jTWDuCXPeYc-40p6FIj79SefuyBWQtjlaI0dBdMbN2Y,204
3
+ croom/room.py,sha256=Hj3Hil3sBUP3kH6CWLABShuF7ank4rW_sV5wqbn62Y8,49
4
+ croom/roomspec.py,sha256=Ng78HYWwfyzQbsRMTnywYh9JyTVo9WzmHSTVbc78A_g,3805
5
+ croom-0.1.0.dist-info/METADATA,sha256=nfCvJnLoaw1-7USRufeKoSicfIqfbKAGYM2oeazDeCg,2229
6
+ croom-0.1.0.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
7
+ croom-0.1.0.dist-info/entry_points.txt,sha256=f9o-tcEh5gS5OCcOoKLimH526qlnhYsoqgR0bQYM32Y,41
8
+ croom-0.1.0.dist-info/licenses/LICENSE,sha256=qioo2eoiY8QhGSbjOXvakhjPzwlmPFxxdFlDT4YIDrU,1471
9
+ croom-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ croom = croom.cli:main
@@ -0,0 +1,10 @@
1
+
2
+ Copyright (c) 2026 juanvel400.
3
+
4
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
5
+
6
+ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
7
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
8
+ 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
9
+
10
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.