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 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 r.ok:
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: 82afa8d6cba96c10b5945c6f7c1c8360a504cc8ac6231e74017cc6c20b908df6
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"
@@ -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",
@@ -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()
@@ -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: 82afa8d6cba96c10b5945c6f7c1c8360a504cc8ac6231e74017cc6c20b908df6
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
  }