omdev 0.0.0.dev221__py3-none-any.whl → 0.0.0.dev223__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.
Files changed (40) hide show
  1. omdev/ci/cache.py +40 -23
  2. omdev/ci/ci.py +49 -109
  3. omdev/ci/cli.py +24 -23
  4. omdev/ci/docker/__init__.py +0 -0
  5. omdev/ci/docker/buildcaching.py +69 -0
  6. omdev/ci/docker/cache.py +57 -0
  7. omdev/ci/{docker.py → docker/cmds.py} +1 -44
  8. omdev/ci/docker/imagepulling.py +64 -0
  9. omdev/ci/docker/inject.py +37 -0
  10. omdev/ci/docker/utils.py +48 -0
  11. omdev/ci/github/cache.py +15 -5
  12. omdev/ci/github/inject.py +30 -0
  13. omdev/ci/inject.py +61 -0
  14. omdev/dataserver/__init__.py +1 -0
  15. omdev/dataserver/handlers.py +198 -0
  16. omdev/dataserver/http.py +69 -0
  17. omdev/dataserver/routes.py +49 -0
  18. omdev/dataserver/server.py +90 -0
  19. omdev/dataserver/targets.py +89 -0
  20. omdev/oci/__init__.py +0 -0
  21. omdev/oci/building.py +221 -0
  22. omdev/oci/compression.py +8 -0
  23. omdev/oci/data.py +151 -0
  24. omdev/oci/datarefs.py +138 -0
  25. omdev/oci/dataserver.py +61 -0
  26. omdev/oci/loading.py +142 -0
  27. omdev/oci/media.py +179 -0
  28. omdev/oci/packing.py +381 -0
  29. omdev/oci/repositories.py +159 -0
  30. omdev/oci/tars.py +144 -0
  31. omdev/pyproject/resources/python.sh +1 -1
  32. omdev/scripts/ci.py +1841 -384
  33. omdev/scripts/interp.py +100 -22
  34. omdev/scripts/pyproject.py +122 -28
  35. {omdev-0.0.0.dev221.dist-info → omdev-0.0.0.dev223.dist-info}/METADATA +2 -2
  36. {omdev-0.0.0.dev221.dist-info → omdev-0.0.0.dev223.dist-info}/RECORD +40 -15
  37. {omdev-0.0.0.dev221.dist-info → omdev-0.0.0.dev223.dist-info}/LICENSE +0 -0
  38. {omdev-0.0.0.dev221.dist-info → omdev-0.0.0.dev223.dist-info}/WHEEL +0 -0
  39. {omdev-0.0.0.dev221.dist-info → omdev-0.0.0.dev223.dist-info}/entry_points.txt +0 -0
  40. {omdev-0.0.0.dev221.dist-info → omdev-0.0.0.dev223.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,159 @@
1
+ # ruff: noqa: UP006 UP007
2
+ # @omlish-lite
3
+ import abc
4
+ import os.path
5
+ import tarfile
6
+ import typing as ta
7
+
8
+ from omlish.lite.check import check
9
+ from omlish.os.paths import is_path_in_dir
10
+
11
+ from .datarefs import BytesOciDataRef
12
+ from .datarefs import FileOciDataRef
13
+ from .datarefs import OciDataRef
14
+ from .datarefs import TarFileOciDataRef
15
+
16
+
17
+ ##
18
+
19
+
20
+ class OciRepository(abc.ABC):
21
+ @abc.abstractmethod
22
+ def contains_blob(self, digest: str) -> bool:
23
+ raise NotImplementedError
24
+
25
+ @abc.abstractmethod
26
+ def read_blob(self, digest: str) -> bytes:
27
+ raise NotImplementedError
28
+
29
+ @abc.abstractmethod
30
+ def ref_blob(self, digest: str) -> OciDataRef:
31
+ raise NotImplementedError
32
+
33
+ @classmethod
34
+ def of(
35
+ cls,
36
+ obj: ta.Union[
37
+ 'OciRepository',
38
+ str,
39
+ tarfile.TarFile,
40
+ ta.Mapping[str, bytes],
41
+ ],
42
+ ) -> 'OciRepository':
43
+ if isinstance(obj, OciRepository):
44
+ return obj
45
+
46
+ elif isinstance(obj, str):
47
+ check.arg(os.path.isdir(obj))
48
+ return DirectoryOciRepository(obj)
49
+
50
+ elif isinstance(obj, tarfile.TarFile):
51
+ return TarFileOciRepository(obj)
52
+
53
+ elif isinstance(obj, ta.Mapping):
54
+ return DictOciRepository(obj)
55
+
56
+ else:
57
+ raise TypeError(obj)
58
+
59
+
60
+ class FileOciRepository(OciRepository, abc.ABC):
61
+ @abc.abstractmethod
62
+ def read_file(self, path: str) -> bytes:
63
+ raise NotImplementedError
64
+
65
+
66
+ #
67
+
68
+
69
+ class DirectoryOciRepository(FileOciRepository):
70
+ def __init__(self, data_dir: str) -> None:
71
+ super().__init__()
72
+
73
+ self._data_dir = check.non_empty_str(data_dir)
74
+
75
+ def read_file(self, path: str) -> bytes:
76
+ full_path = os.path.join(self._data_dir, path)
77
+ check.arg(is_path_in_dir(self._data_dir, full_path))
78
+ with open(full_path, 'rb') as f:
79
+ return f.read()
80
+
81
+ def blob_path(self, digest: str) -> str:
82
+ scheme, value = digest.split(':')
83
+ return os.path.join('blobs', scheme, value)
84
+
85
+ def blob_full_path(self, digest: str) -> str:
86
+ path = self.blob_path(digest)
87
+ full_path = os.path.join(self._data_dir, path)
88
+ check.arg(is_path_in_dir(self._data_dir, full_path))
89
+ return full_path
90
+
91
+ def contains_blob(self, digest: str) -> bool:
92
+ return os.path.isfile(self.blob_full_path(digest))
93
+
94
+ def read_blob(self, digest: str) -> bytes:
95
+ return self.read_file(self.blob_path(digest))
96
+
97
+ def ref_blob(self, digest: str) -> OciDataRef:
98
+ return FileOciDataRef(self.blob_full_path(digest))
99
+
100
+
101
+ #
102
+
103
+
104
+ class TarFileOciRepository(FileOciRepository):
105
+ def __init__(self, tar_file: tarfile.TarFile) -> None:
106
+ super().__init__()
107
+
108
+ check.arg('r' in tar_file.mode)
109
+
110
+ self._tar_file = tar_file
111
+
112
+ def read_file(self, path: str) -> bytes:
113
+ if (ti := self._tar_file.getmember(path)) is None:
114
+ raise FileNotFoundError(path)
115
+ with check.not_none(self._tar_file.extractfile(ti)) as f:
116
+ return f.read()
117
+
118
+ def blob_name(self, digest: str) -> str:
119
+ scheme, value = digest.split(':')
120
+ return os.path.join('blobs', scheme, value)
121
+
122
+ def contains_blob(self, digest: str) -> bool:
123
+ try:
124
+ self._tar_file.getmember(self.blob_name(digest))
125
+ except KeyError:
126
+ return False
127
+ else:
128
+ return True
129
+
130
+ def read_blob(self, digest: str) -> bytes:
131
+ if (ti := self._tar_file.getmember(self.blob_name(digest))) is None:
132
+ raise KeyError(digest)
133
+ with check.not_none(self._tar_file.extractfile(ti)) as f:
134
+ return f.read()
135
+
136
+ def ref_blob(self, digest: str) -> OciDataRef:
137
+ return TarFileOciDataRef(
138
+ tar_file=self._tar_file,
139
+ tar_info=self._tar_file.getmember(self.blob_name(digest)),
140
+ )
141
+
142
+
143
+ #
144
+
145
+
146
+ class DictOciRepository(OciRepository):
147
+ def __init__(self, blobs: ta.Mapping[str, bytes]) -> None:
148
+ super().__init__()
149
+
150
+ self._blobs = blobs
151
+
152
+ def contains_blob(self, digest: str) -> bool:
153
+ return digest in self._blobs
154
+
155
+ def read_blob(self, digest: str) -> bytes:
156
+ return self._blobs[digest]
157
+
158
+ def ref_blob(self, digest: str) -> OciDataRef:
159
+ return BytesOciDataRef(self._blobs[digest])
omdev/oci/tars.py ADDED
@@ -0,0 +1,144 @@
1
+ # ruff: noqa: UP006 UP007
2
+ # @omlish-lite
3
+ import gzip
4
+ import hashlib
5
+ import tarfile
6
+ import typing as ta
7
+
8
+ from omlish.lite.contextmanagers import ExitStacked
9
+
10
+ from .compression import OciCompression
11
+ from .datarefs import OciDataRef
12
+ from .datarefs import OciDataRefInfo
13
+ from .datarefs import open_oci_data_ref
14
+
15
+
16
+ ##
17
+
18
+
19
+ class WrittenOciDataTarFileInfo(ta.NamedTuple):
20
+ compressed_sz: int
21
+ compressed_sha256: str
22
+
23
+ tar_sz: int
24
+ tar_sha256: str
25
+
26
+
27
+ class OciDataTarWriter(ExitStacked):
28
+ def __init__(
29
+ self,
30
+ f: ta.BinaryIO,
31
+ compression: ta.Optional[OciCompression] = None,
32
+ *,
33
+ gzip_level: int = 1,
34
+ zstd_level: int = 10,
35
+ ) -> None:
36
+ super().__init__()
37
+
38
+ self._f = f
39
+ self._compression = compression
40
+
41
+ self._gzip_level = gzip_level
42
+ self._zstd_level = zstd_level
43
+
44
+ class _FileWrapper:
45
+ def __init__(self, f):
46
+ super().__init__()
47
+
48
+ self._f = f
49
+ self._c = 0
50
+ self._h = hashlib.sha256()
51
+
52
+ @property
53
+ def size(self) -> int:
54
+ return self._c
55
+
56
+ def sha256(self) -> str:
57
+ return self._h.hexdigest()
58
+
59
+ def write(self, d):
60
+ self._c += len(d)
61
+ self._h.update(d)
62
+ self._f.write(d)
63
+
64
+ def tell(self) -> int:
65
+ return self._f.tell()
66
+
67
+ _cw: _FileWrapper
68
+ _cf: ta.BinaryIO
69
+
70
+ _tw: _FileWrapper
71
+ _tf: tarfile.TarFile
72
+
73
+ def info(self) -> WrittenOciDataTarFileInfo:
74
+ return WrittenOciDataTarFileInfo(
75
+ compressed_sz=self._cw.size,
76
+ compressed_sha256=self._cw.sha256(),
77
+
78
+ tar_sz=self._tw.size,
79
+ tar_sha256=self._tw.sha256(),
80
+ )
81
+
82
+ def __enter__(self) -> 'OciDataTarWriter':
83
+ super().__enter__()
84
+
85
+ #
86
+
87
+ self._cw = self._FileWrapper(self._f)
88
+
89
+ if self._compression is OciCompression.GZIP:
90
+ self._cf = self._enter_context(
91
+ gzip.GzipFile( # type: ignore
92
+ fileobj=self._cw,
93
+ mode='wb',
94
+ compresslevel=self._gzip_level,
95
+ ),
96
+ )
97
+
98
+ elif self._compression is OciCompression.ZSTD:
99
+ zc = __import__('zstandard').ZstdCompressor(
100
+ level=self._zstd_level,
101
+ )
102
+ self._cf = self._enter_context(zc.stream_writer(self._cw))
103
+
104
+ elif self._compression is None:
105
+ self._cf = self._cw # type: ignore
106
+
107
+ else:
108
+ raise ValueError(self._compression)
109
+
110
+ #
111
+
112
+ self._tw = self._FileWrapper(self._cf)
113
+
114
+ self._tf = self._enter_context(
115
+ tarfile.open( # type: ignore
116
+ fileobj=self._tw,
117
+ mode='w',
118
+ ),
119
+ )
120
+
121
+ #
122
+
123
+ return self
124
+
125
+ def tar_file(self) -> tarfile.TarFile:
126
+ return self._tf
127
+
128
+ def add_file(self, ti: tarfile.TarInfo, f: ta.Optional[ta.BinaryIO] = None) -> None:
129
+ self._tf.addfile(ti, f)
130
+
131
+
132
+ def write_oci_data_tar_file(
133
+ f: ta.BinaryIO,
134
+ data: ta.Mapping[str, OciDataRef],
135
+ ) -> WrittenOciDataTarFileInfo:
136
+ with OciDataTarWriter(f) as tgw:
137
+ for n, dr in data.items():
138
+ ti = tarfile.TarInfo(name=n)
139
+ ri = OciDataRefInfo(dr)
140
+ ti.size = ri.size()
141
+ with open_oci_data_ref(dr) as df:
142
+ tgw.add_file(ti, df)
143
+
144
+ return tgw.info()
@@ -2,7 +2,7 @@
2
2
  set -e
3
3
 
4
4
  if [ -z "${VENV}" ] ; then
5
- if [ $(uname) = "Linux" ] && (cat /proc/mounts | grep -E '^overlay / .*/(docker|desktop-containerd)/' > /dev/null) ; then
5
+ if [ $(uname) = "Linux" ] && grep -E '^overlay / .*/(docker|desktop-containerd)/' /proc/mounts > /dev/null ; then
6
6
  VENV=docker
7
7
  else
8
8
  VENV=default