chtypes 2.0.0.dev9__py3-none-any.whl → 2.0.0.dev10__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.
- chtypes/__init__.py +8 -0
- chtypes/__main__.py +31 -2
- chtypes/_advisory.py +38 -0
- chtypes/_codes.py +2 -1
- chtypes/_ocifetch/__init__.py +5 -1
- chtypes/_ocifetch/_channel.py +6 -0
- chtypes/_ocifetch/_constants.py +6 -1
- chtypes/_ocifetch/_ensure.py +289 -89
- chtypes/_ocifetch/_errors.py +23 -1
- chtypes/_ocifetch/_http.py +96 -15
- chtypes/_ocifetch/_referrers.py +123 -2
- chtypes/_ocifetch/_retired.py +107 -2
- chtypes/_ocifetch/_revocation.py +642 -0
- chtypes/errors.py +26 -0
- chtypes/registry.py +45 -3
- {chtypes-2.0.0.dev9.dist-info → chtypes-2.0.0.dev10.dist-info}/METADATA +1 -1
- {chtypes-2.0.0.dev9.dist-info → chtypes-2.0.0.dev10.dist-info}/RECORD +20 -18
- {chtypes-2.0.0.dev9.dist-info → chtypes-2.0.0.dev10.dist-info}/WHEEL +0 -0
- {chtypes-2.0.0.dev9.dist-info → chtypes-2.0.0.dev10.dist-info}/entry_points.txt +0 -0
- {chtypes-2.0.0.dev9.dist-info → chtypes-2.0.0.dev10.dist-info}/licenses/LICENSE +0 -0
chtypes/__init__.py
CHANGED
|
@@ -69,6 +69,7 @@ from .errors import (
|
|
|
69
69
|
CODE_ARTIFACT_INCOMPATIBLE,
|
|
70
70
|
CODE_ARTIFACT_MISSING,
|
|
71
71
|
CODE_ARTIFACT_PINNED,
|
|
72
|
+
CODE_ARTIFACT_REVOKED,
|
|
72
73
|
CODE_ARTIFACT_UNPUBLISHED,
|
|
73
74
|
CODE_ARTIFACT_UNTRUSTED,
|
|
74
75
|
CODE_CACHE_UNUSABLE,
|
|
@@ -77,17 +78,20 @@ from .errors import (
|
|
|
77
78
|
CODE_SOURCE_RETIRED,
|
|
78
79
|
CODE_SOURCE_UNAUTHORIZED,
|
|
79
80
|
CODE_SOURCE_UNREACHABLE,
|
|
81
|
+
Advisory,
|
|
80
82
|
ArtifactCorruptError,
|
|
81
83
|
ArtifactError,
|
|
82
84
|
ArtifactIncompatibleError,
|
|
83
85
|
ArtifactMissingError,
|
|
84
86
|
ArtifactPinnedError,
|
|
87
|
+
ArtifactRevokedError,
|
|
85
88
|
ArtifactUnpublishedError,
|
|
86
89
|
ArtifactUntrustedError,
|
|
87
90
|
CacheUnusableError,
|
|
88
91
|
CallError,
|
|
89
92
|
ChtypesError,
|
|
90
93
|
InternalError,
|
|
94
|
+
Revocation,
|
|
91
95
|
SchemaError,
|
|
92
96
|
SourceForbiddenError,
|
|
93
97
|
SourceIncompatibleError,
|
|
@@ -130,6 +134,7 @@ __all__ = [
|
|
|
130
134
|
"CODE_ARTIFACT_INCOMPATIBLE",
|
|
131
135
|
"CODE_ARTIFACT_MISSING",
|
|
132
136
|
"CODE_ARTIFACT_PINNED",
|
|
137
|
+
"CODE_ARTIFACT_REVOKED",
|
|
133
138
|
"CODE_ARTIFACT_UNPUBLISHED",
|
|
134
139
|
"CODE_ARTIFACT_UNTRUSTED",
|
|
135
140
|
"CODE_CACHE_UNUSABLE",
|
|
@@ -139,11 +144,13 @@ __all__ = [
|
|
|
139
144
|
"CODE_SOURCE_UNAUTHORIZED",
|
|
140
145
|
"CODE_SOURCE_UNREACHABLE",
|
|
141
146
|
"EXPORT_NONE",
|
|
147
|
+
"Advisory",
|
|
142
148
|
"ArtifactCorruptError",
|
|
143
149
|
"ArtifactError",
|
|
144
150
|
"ArtifactIncompatibleError",
|
|
145
151
|
"ArtifactMissingError",
|
|
146
152
|
"ArtifactPinnedError",
|
|
153
|
+
"ArtifactRevokedError",
|
|
147
154
|
"ArtifactUnpublishedError",
|
|
148
155
|
"ArtifactUntrustedError",
|
|
149
156
|
"AtMergeEntry",
|
|
@@ -183,6 +190,7 @@ __all__ = [
|
|
|
183
190
|
"Reason",
|
|
184
191
|
"Registry",
|
|
185
192
|
"Resolved",
|
|
193
|
+
"Revocation",
|
|
186
194
|
"RowResult",
|
|
187
195
|
"Schema",
|
|
188
196
|
"SchemaDescription",
|
chtypes/__main__.py
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
chtypes fetch <spelling>... | --all [--platform <os-arch>] [--cache <dir>] [--lock <file>]
|
|
5
5
|
[--frozen] [--offline] [--update] [--strict]
|
|
6
|
+
[--allow-revoked] [--require-revocation-check]
|
|
6
7
|
chtypes verify [--cache <dir>] [--strict] re-verify every installed build
|
|
7
8
|
chtypes list [--cache <dir>] [--offline] [--strict]
|
|
8
9
|
chtypes where [--cache <dir>] [--strict] [--all] the cache root; --all: every search dir
|
|
@@ -37,7 +38,7 @@ from collections.abc import Sequence
|
|
|
37
38
|
|
|
38
39
|
from . import __version__ as chtypes_version
|
|
39
40
|
from . import _ocifetch as fetch_layer
|
|
40
|
-
from ._codes import CODE_ARTIFACT_CORRUPT
|
|
41
|
+
from ._codes import CODE_ARTIFACT_CORRUPT, CODE_ARTIFACT_REVOKED
|
|
41
42
|
from ._ocifetch import _channel
|
|
42
43
|
from ._ocifetch import _constants as C
|
|
43
44
|
from ._ocifetch._ensure import _translate_transport_error, detect_host_platform, missing_notes
|
|
@@ -148,6 +149,19 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
148
149
|
"(or set CHTYPES_OFFLINE=1)"
|
|
149
150
|
),
|
|
150
151
|
)
|
|
152
|
+
fetch.add_argument(
|
|
153
|
+
"--allow-revoked",
|
|
154
|
+
action="store_true",
|
|
155
|
+
help=(
|
|
156
|
+
"use a build a verified revocation refuses, with one warning "
|
|
157
|
+
"(this call only; never --lock or --update)"
|
|
158
|
+
),
|
|
159
|
+
)
|
|
160
|
+
fetch.add_argument(
|
|
161
|
+
"--require-revocation-check",
|
|
162
|
+
action="store_true",
|
|
163
|
+
help="a revocation status that cannot be read is the transport's error, not a warning",
|
|
164
|
+
)
|
|
151
165
|
|
|
152
166
|
verify = sub.add_parser(
|
|
153
167
|
"verify",
|
|
@@ -249,6 +263,8 @@ def _options(args: argparse.Namespace, *, platform: str | None = None):
|
|
|
249
263
|
lock_path=lock_path,
|
|
250
264
|
lock_write=lock_write,
|
|
251
265
|
update=getattr(args, "update", False),
|
|
266
|
+
allow_revoked=getattr(args, "allow_revoked", False),
|
|
267
|
+
require_revocation_check=getattr(args, "require_revocation_check", False),
|
|
252
268
|
)
|
|
253
269
|
return fetch._to_options(platform)
|
|
254
270
|
|
|
@@ -348,10 +364,23 @@ def _cmd_verify(args: argparse.Namespace) -> int:
|
|
|
348
364
|
return EXIT_OK
|
|
349
365
|
_say_notes(options)
|
|
350
366
|
failed = 0
|
|
367
|
+
revoked = 0
|
|
351
368
|
for r in results:
|
|
352
|
-
if not
|
|
369
|
+
if r.revocation is not None:
|
|
370
|
+
# A build a stored revocation refuses (docs/guides/fetch-v1.md,
|
|
371
|
+
# "Build-level revocation"); it is reported whatever its bytes say.
|
|
372
|
+
revoked += 1
|
|
373
|
+
_say(f"REVOKED {r.version} {r.platform} {r.dir}: {r.detail}")
|
|
374
|
+
elif not r.ok:
|
|
353
375
|
failed += 1
|
|
354
376
|
_say(f"chtypes: FAILED {r.version} {r.platform} {r.dir}: {r.detail}")
|
|
377
|
+
if revoked:
|
|
378
|
+
# Exit 11 wins over a mismatch's 1: a revocation is the more specific finding.
|
|
379
|
+
_say(
|
|
380
|
+
f"chtypes: {revoked} of {len(results)} installed build(s) are revoked "
|
|
381
|
+
f"[{CODE_ARTIFACT_REVOKED}]"
|
|
382
|
+
)
|
|
383
|
+
return C.ERROR_EXIT_CODES[CODE_ARTIFACT_REVOKED]
|
|
355
384
|
if failed:
|
|
356
385
|
_say(f"chtypes: {failed} of {len(results)} installed build(s) FAILED verification")
|
|
357
386
|
return C.ERROR_EXIT_CODES[CODE_ARTIFACT_CORRUPT]
|
chtypes/_advisory.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""What a `CHTYPES_ARTIFACT_REVOKED` error carries for programs
|
|
2
|
+
(docs/guides/fetch-v1.md, "Build-level revocation"; public issue #625).
|
|
3
|
+
|
|
4
|
+
A leaf module, so the fetch layer's errors and the public ones share the two
|
|
5
|
+
types without importing each other."""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
|
|
11
|
+
__all__ = ["Advisory", "Revocation"]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass(frozen=True)
|
|
15
|
+
class Advisory:
|
|
16
|
+
"""One advisory a revocation names, every string made safe to print. A
|
|
17
|
+
signed revocation's carries its `revoked_at`; a 410's carries the `build` it
|
|
18
|
+
names, newest first. A field the source did not send is "". `class_` is the
|
|
19
|
+
advisory's `class` (a keyword in Python)."""
|
|
20
|
+
|
|
21
|
+
id: str = ""
|
|
22
|
+
url: str = ""
|
|
23
|
+
class_: str = ""
|
|
24
|
+
summary: str = ""
|
|
25
|
+
build: str = ""
|
|
26
|
+
superseded_by: str = ""
|
|
27
|
+
revoked_at: str = ""
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass(frozen=True)
|
|
31
|
+
class Revocation:
|
|
32
|
+
"""`signed` is true for a verified, signed revocation of `manifest`, whose
|
|
33
|
+
one advisory it is, and false for a 410's body, which is unsigned, names no
|
|
34
|
+
manifest (`manifest` is "") and lists one advisory per revoked build."""
|
|
35
|
+
|
|
36
|
+
signed: bool = False
|
|
37
|
+
manifest: str = ""
|
|
38
|
+
advisories: tuple[Advisory, ...] = ()
|
chtypes/_codes.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Code generated by scripts/fetch-v1/gen-constants.py from
|
|
2
2
|
# spec/fetch-v1/constants.json. DO NOT EDIT.
|
|
3
|
-
# Source sha256:
|
|
3
|
+
# Source sha256: 5aa3d14a5d8b60399040a8242f003135fa5f6c2c402de7d4e2db4c768a3366ae
|
|
4
4
|
"""The shared error codes (docs/guides/fetch-v1.md §8), one per entry of the
|
|
5
5
|
fetch constants' `errors`, in their order. `chtypes.errors` re-exports them
|
|
6
6
|
(docs/reference/bindings-v1.md §4, the codes)."""
|
|
@@ -21,3 +21,4 @@ CODE_SOURCE_INCOMPATIBLE: Final[str] = "CHTYPES_SOURCE_INCOMPATIBLE"
|
|
|
21
21
|
CODE_ARTIFACT_INCOMPATIBLE: Final[str] = "CHTYPES_ARTIFACT_INCOMPATIBLE"
|
|
22
22
|
CODE_CACHE_UNUSABLE: Final[str] = "CHTYPES_CACHE_UNUSABLE"
|
|
23
23
|
CODE_SOURCE_RETIRED: Final[str] = "CHTYPES_SOURCE_RETIRED"
|
|
24
|
+
CODE_ARTIFACT_REVOKED: Final[str] = "CHTYPES_ARTIFACT_REVOKED"
|
chtypes/_ocifetch/__init__.py
CHANGED
|
@@ -4,7 +4,7 @@ Not part of this package's public API: every name here is subject to change
|
|
|
4
4
|
without notice until the v1 switch. `_constants` is generated; the fetch
|
|
5
5
|
logic lives in sibling modules. The seam (docs/guides/fetch-v1.md "The seam")
|
|
6
6
|
is the only surface anything outside this package may use: `ensure`,
|
|
7
|
-
`resolve_installed`, `list_installed`, `verify_installed` and
|
|
7
|
+
`resolve_installed`, `check_installed`, `list_installed`, `verify_installed` and
|
|
8
8
|
`fetch_signed`, plus the `Request`/`Options`/`Resolved`/
|
|
9
9
|
`VerifyResult` shapes they take and return; and beside it the CLI's `resolve`
|
|
10
10
|
and `prune` and the registry's in-use `hold` (§1, "In use"). Every other module here
|
|
@@ -25,9 +25,11 @@ from chtypes._ocifetch._ensure import (
|
|
|
25
25
|
Request,
|
|
26
26
|
Resolved,
|
|
27
27
|
VerifyResult,
|
|
28
|
+
check_installed,
|
|
28
29
|
ensure,
|
|
29
30
|
fetch_signed,
|
|
30
31
|
list_installed,
|
|
32
|
+
reset_revocation_memo_for_tests,
|
|
31
33
|
resolve_installed,
|
|
32
34
|
verify_installed,
|
|
33
35
|
)
|
|
@@ -43,11 +45,13 @@ __all__ = [
|
|
|
43
45
|
"Resolved",
|
|
44
46
|
"Superseded",
|
|
45
47
|
"VerifyResult",
|
|
48
|
+
"check_installed",
|
|
46
49
|
"ensure",
|
|
47
50
|
"fetch_signed",
|
|
48
51
|
"hold",
|
|
49
52
|
"list_installed",
|
|
50
53
|
"prune",
|
|
54
|
+
"reset_revocation_memo_for_tests",
|
|
51
55
|
"resolve",
|
|
52
56
|
"resolve_each",
|
|
53
57
|
"resolve_installed",
|
chtypes/_ocifetch/_channel.py
CHANGED
|
@@ -133,6 +133,10 @@ class Channel:
|
|
|
133
133
|
# lookups see only records signed with it. "" does neither (the v1 contract
|
|
134
134
|
# and every production channel).
|
|
135
135
|
own_fingerprint: str = ""
|
|
136
|
+
# Build-level revocation (_revocation.py; docs/guides/fetch-v1.md, "Build-level
|
|
137
|
+
# revocation"): generation 2, the dev channel and the production one. The v1
|
|
138
|
+
# contract has none of it.
|
|
139
|
+
revocation: bool = False
|
|
136
140
|
|
|
137
141
|
|
|
138
142
|
# What a 2.0.0-dev SDK speaks, and what every process speaks unless a test
|
|
@@ -149,6 +153,7 @@ DEV_CHANNEL: Final = Channel(
|
|
|
149
153
|
overridable=False,
|
|
150
154
|
pinnable=False,
|
|
151
155
|
own_fingerprint=CHS_ABI_FINGERPRINT.removeprefix("sha256:"),
|
|
156
|
+
revocation=True,
|
|
152
157
|
)
|
|
153
158
|
|
|
154
159
|
# The v1 contract the fetch-v1 conformance cases specify, from the generated
|
|
@@ -183,6 +188,7 @@ PROD_V2_CHANNEL: Final = Channel(
|
|
|
183
188
|
keys=C.RELEASE_KEYS,
|
|
184
189
|
overridable=True,
|
|
185
190
|
pinnable=True,
|
|
191
|
+
revocation=True,
|
|
186
192
|
)
|
|
187
193
|
|
|
188
194
|
_lock = threading.Lock()
|
chtypes/_ocifetch/_constants.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Code generated by scripts/fetch-v1/gen-constants.py from
|
|
2
2
|
# spec/fetch-v1/constants.json. DO NOT EDIT.
|
|
3
|
-
# Source sha256:
|
|
3
|
+
# Source sha256: 5aa3d14a5d8b60399040a8242f003135fa5f6c2c402de7d4e2db4c768a3366ae
|
|
4
4
|
"""The v1 fetch constants, generated: see docs/guides/fetch-v1.md."""
|
|
5
5
|
|
|
6
6
|
from __future__ import annotations
|
|
@@ -46,6 +46,7 @@ ARTIFACT_TYPE: Final[str] = "application/vnd.wavehouse.chtypes.artifact.v1"
|
|
|
46
46
|
GOLDENS_ARTIFACT_TYPE: Final[str] = "application/vnd.wavehouse.chtypes.goldens.v1"
|
|
47
47
|
FIXTURES_ARTIFACT_TYPE: Final[str] = "application/vnd.wavehouse.chtypes.sdk-fetch-fixtures.v1"
|
|
48
48
|
CHANNEL_ARTIFACT_TYPE: Final[str] = "application/vnd.wavehouse.chtypes.channel.v1"
|
|
49
|
+
REVOCATION_ARTIFACT_TYPE: Final[str] = "application/vnd.wavehouse.chtypes.revocation.v1"
|
|
49
50
|
|
|
50
51
|
DSSE_PAYLOAD_TYPE: Final[str] = "application/vnd.in-toto+json"
|
|
51
52
|
DSSE_MAX_SIGNATURES: Final[int] = 4
|
|
@@ -56,6 +57,7 @@ PREDICATE_TYPE_ARTIFACT: Final[str] = "https://artifacts.wavehouse.dev/spec/arti
|
|
|
56
57
|
PREDICATE_TYPE_GOLDENS: Final[str] = "https://artifacts.wavehouse.dev/spec/goldens/v1"
|
|
57
58
|
PREDICATE_TYPE_FIXTURES: Final[str] = "https://artifacts.wavehouse.dev/spec/sdk-fetch-fixtures/v1"
|
|
58
59
|
PREDICATE_TYPE_CHANNEL: Final[str] = "https://artifacts.wavehouse.dev/spec/channel/v1"
|
|
60
|
+
PREDICATE_TYPE_REVOCATION: Final[str] = "https://artifacts.wavehouse.dev/spec/revocation/v1"
|
|
59
61
|
|
|
60
62
|
KEYID_ALGORITHM: Final[str] = "sha256-first16hex"
|
|
61
63
|
RELEASE_KEYS: Final[tuple[dict[str, str], ...]] = (
|
|
@@ -87,6 +89,7 @@ RETIRED_STATUSES: Final[tuple[int, ...]] = (410,)
|
|
|
87
89
|
|
|
88
90
|
CONNECT_TIMEOUT_S: Final[float] = 30.0
|
|
89
91
|
IDLE_READ_TIMEOUT_S: Final[float] = 60.0
|
|
92
|
+
REVOCATION_CHECK_S: Final[float] = 5.0
|
|
90
93
|
|
|
91
94
|
MANIFEST_MAX_BYTES: Final[int] = 4194304
|
|
92
95
|
BUNDLE_MAX_BYTES: Final[int] = 1048576
|
|
@@ -109,6 +112,7 @@ PROD_V2_BASES: Final[tuple[str, ...]] = ("https://registry.wavehouse.dev/chtypes
|
|
|
109
112
|
PROD_V2_SYSTEM_DIRS: Final[tuple[str, ...]] = ("/usr/local/share/chtypes/v2", "/opt/chtypes/v2")
|
|
110
113
|
CACHE_UNPACKED_DIR: Final[str] = "unpacked/sha256"
|
|
111
114
|
CACHE_VERIFIED_RECORD: Final[str] = "verified.json"
|
|
115
|
+
CACHE_REVOCATION_RECORD: Final[str] = "revocation.json"
|
|
112
116
|
CACHE_ANNOTATION_PREFIX: Final[str] = "dev.wavehouse.chtypes."
|
|
113
117
|
|
|
114
118
|
LOCK_SCHEMA: Final[int] = 3
|
|
@@ -132,4 +136,5 @@ ERROR_EXIT_CODES: Final[dict[str, int]] = {
|
|
|
132
136
|
"CHTYPES_ARTIFACT_INCOMPATIBLE": 8,
|
|
133
137
|
"CHTYPES_CACHE_UNUSABLE": 9,
|
|
134
138
|
"CHTYPES_SOURCE_RETIRED": 10,
|
|
139
|
+
"CHTYPES_ARTIFACT_REVOKED": 11,
|
|
135
140
|
}
|