@runeditrun/rer 0.2.0 → 0.2.1

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 (70) hide show
  1. package/README.md +3 -2
  2. package/cli/README.md +4 -2
  3. package/cli/contract.mjs +2 -2
  4. package/cli/deploy.mjs +17 -3
  5. package/cli/detached-server.mjs +51 -0
  6. package/cli/hosted-output.mjs +2 -2
  7. package/cli/hydration/check.mjs +2 -2
  8. package/cli/inspect.mjs +1 -1
  9. package/cli/main.mjs +1 -0
  10. package/cli/managed-server.mjs +12 -5
  11. package/cli/parse.mjs +3 -2
  12. package/cli/release.mjs +1 -0
  13. package/cli/site-bindings.mjs +291 -0
  14. package/cli/site-browser-objects.mjs +36 -0
  15. package/cli/site-check.mjs +46 -31
  16. package/cli/site-client.mjs +50 -0
  17. package/cli/site-data.mjs +54 -0
  18. package/cli/site-entities.json +1 -0
  19. package/cli/site-input-assets.mjs +58 -0
  20. package/cli/site-inputs.mjs +79 -0
  21. package/cli/site-markup.mjs +102 -0
  22. package/cli/site-private.mjs +36 -0
  23. package/cli/site-references.mjs +93 -0
  24. package/cli/site-render.mjs +7 -5
  25. package/cli/site-syntax.mjs +21 -0
  26. package/cli/site.mjs +32 -11
  27. package/global/CHANGELOG.md +25 -0
  28. package/global/README.md +3 -2
  29. package/global/main.mjs +2 -0
  30. package/global/output.mjs +12 -0
  31. package/global/rer.mjs +3 -0
  32. package/modules/deploy-kit/deploy.mjs +5 -2
  33. package/modules/deploy-kit/diagnostics.mjs +55 -0
  34. package/modules/deploy-kit/owner-cli.mjs +10 -4
  35. package/modules/deploy-kit/owner.mjs +3 -0
  36. package/modules/deploy-kit/provider.mjs +45 -6
  37. package/modules/operator-auth/accounts/native.mjs +1 -1
  38. package/modules/operator-auth/dev/badge.mjs +4 -1
  39. package/modules/operator-auth/index.d.mts +4 -9
  40. package/modules/operator-auth/page.mjs +1 -1
  41. package/modules/operator-auth/service.mjs +1 -0
  42. package/modules/operator-auth/signin/index.d.mts +11 -0
  43. package/modules/operator-auth/signin/index.mjs +10 -7
  44. package/modules/release/README.md +63 -0
  45. package/modules/release/keygen.mjs +18 -0
  46. package/modules/release/produce.mjs +9 -0
  47. package/modules/release/sign.mjs +44 -0
  48. package/modules/release/signature.mjs +336 -0
  49. package/modules/release/signing-cli.mjs +56 -0
  50. package/modules/release/signing-io.mjs +61 -0
  51. package/modules/release/signing-key.mjs +22 -0
  52. package/modules/release/statement.mjs +80 -0
  53. package/modules/release/vectors/generate.mjs +381 -0
  54. package/modules/release/vectors/release-signature-vectors.json +2305 -0
  55. package/modules/release/verify.mjs +27 -0
  56. package/onboarding/production-assets.mjs +23 -0
  57. package/onboarding/production-runtime.mjs +8 -4
  58. package/package.json +2 -1
  59. package/scripts/publish-check/README.md +27 -17
  60. package/scripts/publish-check/content_identities.py +14 -233
  61. package/scripts/publish-check/git_history.py +2 -2
  62. package/scripts/publish-check/gitleaks.toml +1 -1
  63. package/scripts/publish-check/history_scan.py +6 -4
  64. package/scripts/publish-check/hygiene_config.py +3 -1
  65. package/scripts/publish-check/public_hygiene.py +29 -5
  66. package/scripts/publish-check/publication-config.json +3 -1
  67. package/scripts/publish-check/source_metadata.py +62 -0
  68. package/scripts/publish-check/source_shapes.py +91 -0
  69. package/scripts/publish-check/tree_scan.py +2 -2
  70. package/scripts/publish-check/source_hashes.mjs +0 -6
@@ -0,0 +1,91 @@
1
+ """Local record shapes emitted by edition-authoring, without integrity checks."""
2
+ import re
3
+ from copy_identities import relative_path
4
+ from json_identity import HEX, GIT_REVISION
5
+
6
+ IDENTITY = re.compile(r'[a-zA-Z0-9][a-zA-Z0-9._-]*/[a-zA-Z0-9][a-zA-Z0-9._-]*\Z')
7
+
8
+
9
+ def matches(value, pattern=HEX):
10
+ return isinstance(value, str) and bool(pattern.fullmatch(value))
11
+
12
+
13
+ def pin(value):
14
+ return isinstance(value, dict) and all(matches(value.get(k)) for k in ('key', 'digest', 'sha256'))
15
+
16
+
17
+ def paths(value):
18
+ return isinstance(value, list) and bool(value) and all(relative_path(p) for p in value)
19
+
20
+
21
+ def reference(value, snapshot=False):
22
+ # manifest.origin supplies the identity; snapshotOrigin additionally emits
23
+ # normalized path/paths/excludeSnapshots fields on every retained archive.
24
+ if (not isinstance(value, dict) or not isinstance(value.get('repository'), str)
25
+ or not value['repository'] or value['repository'].startswith('-')):
26
+ return False
27
+ if 'projection' in value:
28
+ projection = value['projection']
29
+ if ('revision' in value or not isinstance(projection, dict)
30
+ or set(projection) != {'schemaVersion', 'originalSha256', 'recipeSha256'}
31
+ or type(projection['schemaVersion']) is not int or projection['schemaVersion'] != 1
32
+ or not all(matches(projection[k]) for k in ('originalSha256', 'recipeSha256'))):
33
+ return False
34
+ elif not matches(value.get('revision'), GIT_REVISION):
35
+ return False
36
+ if snapshot and not {'path', 'paths', 'excludeSnapshots'} <= value.keys():
37
+ return False
38
+ return (('path' not in value or value['path'] == '.' or relative_path(value['path']))
39
+ and (value.get('paths') is None or paths(value['paths']))
40
+ and ('excludeSnapshots' not in value or type(value['excludeSnapshots']) is bool))
41
+
42
+
43
+ def file_records(value, archived=False):
44
+ if not isinstance(value, dict):
45
+ return False
46
+ # Payload bytes need not be present elsewhere or agree with a digest.
47
+ # Empty retained file maps are still metadata.
48
+ return all(relative_path(name) and isinstance(record, dict)
49
+ and type(record.get('executable')) is bool
50
+ and (isinstance(record.get('data'), str) if archived else matches(record.get('sha256')))
51
+ for name, record in value.items())
52
+
53
+
54
+ def archive(value):
55
+ return (isinstance(value, dict) and all(matches(value.get(k)) for k in ('key', 'digest'))
56
+ and reference(value.get('origin'), snapshot=True)
57
+ and file_records(value.get('files'), archived=True)
58
+ and isinstance(value.get('dependencies', []), list)
59
+ and all(pin(dependency) for dependency in value.get('dependencies', [])))
60
+
61
+
62
+ def source(value):
63
+ if not isinstance(value, dict):
64
+ return False
65
+ if value.get('kind') == 'git':
66
+ return reference(value) and relative_path(value.get('destination'))
67
+ if value.get('kind') != 'local' or not paths(value.get('paths')):
68
+ return False
69
+ mappings = value.get('mappings')
70
+ return ((value.get('origin') is None or reference(value['origin']))
71
+ and (mappings is None or (isinstance(mappings, list)
72
+ and all(isinstance(m, dict) and relative_path(m.get('path'))
73
+ and relative_path(m.get('originPath')) for m in mappings))))
74
+
75
+
76
+ def lock(value):
77
+ if (not isinstance(value, dict) or type(value.get('schemaVersion')) is not int
78
+ or value['schemaVersion'] != 1 or not matches(value.get('edition'), IDENTITY)
79
+ or not matches(value.get('manifest')) or 'parent' not in value
80
+ or (value['parent'] is not None and not pin(value['parent']))
81
+ or not isinstance(value.get('components'), list) or not file_records(value.get('files'))):
82
+ return False
83
+ toolchain = value.get('toolchain')
84
+ if (not isinstance(toolchain, dict) or toolchain.get('id') != 'runeditrun/edition-authoring'
85
+ or not isinstance(toolchain.get('version'), str) or not toolchain['version']
86
+ or not matches(toolchain.get('sha256'))):
87
+ return False
88
+ return all(isinstance(component, dict) and matches(component.get('id'), IDENTITY)
89
+ and source(component.get('source')) and 'baseline' in component
90
+ and (component['baseline'] is None or pin(component['baseline']))
91
+ for component in value['components'])
@@ -37,8 +37,8 @@ def context(history, commit, config):
37
37
  'trustBundles': trust}, invalid
38
38
 
39
39
 
40
- def text_rules(data, path, config, resolver, env_hashes):
41
- rules, provenance = inspect(data, path, config, resolver)
40
+ def text_rules(data, path, config, resolver, env_hashes, *, commit_message=False):
41
+ rules, provenance = inspect(data, path, config, resolver, commit_message=commit_message)
42
42
  path_rules, _ = inspect(path.encode('utf-8', 'surrogateescape'), '@path', config, resolver)
43
43
  rules.update(path_rules)
44
44
  if env_hashes and (environment_match(data, env_hashes) or environment_match(path.encode(), env_hashes)):
@@ -1,6 +0,0 @@
1
- // Reuse the maintained Source serializer; never approximate JSON.stringify in
2
- // another language. Input and output stay on private subprocess pipes.
3
- import fs from 'node:fs';
4
- import {hash, json} from '../../edition-authoring/files.mjs';
5
- const values = JSON.parse(fs.readFileSync(0, 'utf8'));
6
- process.stdout.write(JSON.stringify(values.map(value => hash(json(value)))));