szl-substrate 0.1.0__tar.gz
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.
- szl_substrate-0.1.0/.github/CODEOWNERS +2 -0
- szl_substrate-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +57 -0
- szl_substrate-0.1.0/.github/ISSUE_TEMPLATE/config.yml +11 -0
- szl_substrate-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +17 -0
- szl_substrate-0.1.0/.github/dependabot.yml +50 -0
- szl_substrate-0.1.0/.github/scripts/szl_pypi_release_gate.py +227 -0
- szl_substrate-0.1.0/.github/workflows/base-python-ci.yml +67 -0
- szl_substrate-0.1.0/.github/workflows/ci.yml +45 -0
- szl_substrate-0.1.0/.github/workflows/codeql.yml +53 -0
- szl_substrate-0.1.0/.github/workflows/commit-lint.yml +43 -0
- szl_substrate-0.1.0/.github/workflows/doctrine.yml +12 -0
- szl_substrate-0.1.0/.github/workflows/forbidden-domain.yml +39 -0
- szl_substrate-0.1.0/.github/workflows/gitleaks.yml +81 -0
- szl_substrate-0.1.0/.github/workflows/pin-check.yml +20 -0
- szl_substrate-0.1.0/.github/workflows/publish-pypi.yml +129 -0
- szl_substrate-0.1.0/.github/workflows/scorecard.yml +24 -0
- szl_substrate-0.1.0/.gitignore +7 -0
- szl_substrate-0.1.0/.gitleaks.toml +74 -0
- szl_substrate-0.1.0/CITATION.cff +27 -0
- szl_substrate-0.1.0/CODE_OF_CONDUCT.md +36 -0
- szl_substrate-0.1.0/CONTRIBUTING.md +35 -0
- szl_substrate-0.1.0/LICENSE +201 -0
- szl_substrate-0.1.0/MIGRATION.md +490 -0
- szl_substrate-0.1.0/NOTICE +27 -0
- szl_substrate-0.1.0/NOTICE-THIRD-PARTY.md +174 -0
- szl_substrate-0.1.0/PKG-INFO +331 -0
- szl_substrate-0.1.0/README.md +277 -0
- szl_substrate-0.1.0/SECURITY.md +39 -0
- szl_substrate-0.1.0/examples/guarded_import_pattern.py +44 -0
- szl_substrate-0.1.0/pyproject.toml +97 -0
- szl_substrate-0.1.0/requirements/ci/base.in +15 -0
- szl_substrate-0.1.0/requirements/ci/base.txt +320 -0
- szl_substrate-0.1.0/requirements/ci/test.in +16 -0
- szl_substrate-0.1.0/requirements/ci/test.txt +519 -0
- szl_substrate-0.1.0/src/szl_substrate/__init__.py +378 -0
- szl_substrate-0.1.0/src/szl_substrate/_vendor_blobs.py +79 -0
- szl_substrate-0.1.0/src/szl_substrate/a11oy_agent_loop.py +844 -0
- szl_substrate-0.1.0/src/szl_substrate/a11oy_autoreview.py +1149 -0
- szl_substrate-0.1.0/src/szl_substrate/a11oy_code_engine.py +1457 -0
- szl_substrate-0.1.0/src/szl_substrate/a11oy_hf_assets.py +178 -0
- szl_substrate-0.1.0/src/szl_substrate/a11oy_mcp_client.py +276 -0
- szl_substrate-0.1.0/src/szl_substrate/a11oy_org_rag.py +3168 -0
- szl_substrate-0.1.0/src/szl_substrate/a11oy_uds_portability_nav.py +168 -0
- szl_substrate-0.1.0/src/szl_substrate/a11oy_waqay_nav.py +175 -0
- szl_substrate-0.1.0/src/szl_substrate/a11oy_yupay_nav.py +177 -0
- szl_substrate-0.1.0/src/szl_substrate/operator_shell_v4.py +569 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_agentic_loop.py +1830 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_allodial.py +339 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_alloy_models.py +831 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_anatomy_routes.py +356 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_be_hardening.py +958 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_brain.py +249 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_calibration.py +329 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_chain_of_title.py +213 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_codename_gate.py +152 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_conformal.py +735 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_conjecture_factory.py +361 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_connector_mcp.py +118 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_connectors_serve.py +145 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_contracting.py +736 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_cuas_formulas.py +939 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_deepdive_gaps.py +592 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_dsse.py +332 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_ecosystem_routes.py +488 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_energy_sovereign.py +1156 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_entanglement.py +394 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_evidence_research.py +913 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_formula_wiring.py +1428 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_formulas.py +1206 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_hf_bucket.py +726 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_joules_truth.py +145 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_ken.py +537 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_khipu_consensus.py +611 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_khipu_lmdb.py +241 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_khipu_replicate.py +150 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_live_wires.py +389 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_llm_registry.py +690 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_logging.py +45 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_mbse_cosim.py +692 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_mbse_nav.py +450 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_metrics_prom.py +261 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_neuroplasticity.py +267 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_provenance.py +440 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_qhawaq.py +982 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_quantum_bio.py +252 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_rag.py +564 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_readiness.py +974 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_restraint.py +655 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_rosie_companion.py +485 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_sapa.py +463 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_sapa_patch.py +308 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_scaling.py +374 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_spaces_proxy.py +444 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_spaces_surface.py +525 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_uds_portability.py +770 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_unay.py +443 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_unay_routes.py +237 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_unified_formulas.py +213 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_v4_fleet.py +359 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_waqay.py +1068 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_warhacker_aliases.py +252 -0
- szl_substrate-0.1.0/src/szl_substrate/szl_yupay.py +911 -0
- szl_substrate-0.1.0/src/szl_substrate/test_szl_hf_bucket.py +216 -0
- szl_substrate-0.1.0/tests/__init__.py +0 -0
- szl_substrate-0.1.0/tests/test_agentic_loop_verifier_fail_closed.py +95 -0
- szl_substrate-0.1.0/tests/test_anatomy_routes_sources.py +57 -0
- szl_substrate-0.1.0/tests/test_app_parity.py +55 -0
- szl_substrate-0.1.0/tests/test_brain.py +41 -0
- szl_substrate-0.1.0/tests/test_calibration.py +50 -0
- szl_substrate-0.1.0/tests/test_calibration_validation.py +163 -0
- szl_substrate-0.1.0/tests/test_ci_requirements.py +89 -0
- szl_substrate-0.1.0/tests/test_code_engine_mandatory_limits.py +507 -0
- szl_substrate-0.1.0/tests/test_code_engine_portability.py +78 -0
- szl_substrate-0.1.0/tests/test_conformal_score_validation.py +50 -0
- szl_substrate-0.1.0/tests/test_dependency_contract.py +328 -0
- szl_substrate-0.1.0/tests/test_dsse.py +167 -0
- szl_substrate-0.1.0/tests/test_dsse_finite_payloads.py +71 -0
- szl_substrate-0.1.0/tests/test_exception_text_not_echoed.py +127 -0
- szl_substrate-0.1.0/tests/test_mtier_reconcile.py +95 -0
- szl_substrate-0.1.0/tests/test_mtier_wave1.py +66 -0
- szl_substrate-0.1.0/tests/test_mtier_wave2.py +68 -0
- szl_substrate-0.1.0/tests/test_mtier_wave3.py +72 -0
- szl_substrate-0.1.0/tests/test_mtier_wave4.py +89 -0
- szl_substrate-0.1.0/tests/test_org_rag_lexical_reference.py +35 -0
- szl_substrate-0.1.0/tests/test_readiness_parity.py +96 -0
- szl_substrate-0.1.0/tests/test_shipped_selftests.py +25 -0
- szl_substrate-0.1.0/tests/test_vendor_blob_notices.py +35 -0
- szl_substrate-0.1.0/tests/test_wave_s_batch.py +56 -0
- szl_substrate-0.1.0/tests/test_wavek_finish.py +115 -0
- szl_substrate-0.1.0/tools/app_parity.py +120 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Standard bug report form for the szl-holdings estate.
|
|
2
|
+
# Installed by the szl-alignment engine; keep in sync with
|
|
3
|
+
# alignment/templates/ISSUE_TEMPLATE/bug_report.yml.
|
|
4
|
+
name: Bug report
|
|
5
|
+
description: File a reproducible, receipt-backed bug report
|
|
6
|
+
title: "[bug]: "
|
|
7
|
+
labels: ["bug", "triage"]
|
|
8
|
+
body:
|
|
9
|
+
- type: markdown
|
|
10
|
+
attributes:
|
|
11
|
+
value: |
|
|
12
|
+
Thanks for the report. Control before action, evidence after:
|
|
13
|
+
a bug is actionable when it is reproducible and receipt-backed.
|
|
14
|
+
- type: input
|
|
15
|
+
id: version
|
|
16
|
+
attributes:
|
|
17
|
+
label: Version / commit
|
|
18
|
+
description: What were you running? Tag, branch, or commit SHA.
|
|
19
|
+
placeholder: e.g. v14.2.0 or 1a2b3c4
|
|
20
|
+
validations:
|
|
21
|
+
required: true
|
|
22
|
+
- type: textarea
|
|
23
|
+
id: what-happened
|
|
24
|
+
attributes:
|
|
25
|
+
label: What happened?
|
|
26
|
+
description: Observed behavior, including the exact error text.
|
|
27
|
+
validations:
|
|
28
|
+
required: true
|
|
29
|
+
- type: textarea
|
|
30
|
+
id: expected
|
|
31
|
+
attributes:
|
|
32
|
+
label: Expected behavior
|
|
33
|
+
description: What should have happened instead?
|
|
34
|
+
validations:
|
|
35
|
+
required: true
|
|
36
|
+
- type: textarea
|
|
37
|
+
id: repro
|
|
38
|
+
attributes:
|
|
39
|
+
label: Minimal reproduction
|
|
40
|
+
description: Steps, commands, or a minimal repo case.
|
|
41
|
+
placeholder: "1. ...\n2. ...\n3. ..."
|
|
42
|
+
validations:
|
|
43
|
+
required: true
|
|
44
|
+
- type: textarea
|
|
45
|
+
id: receipts
|
|
46
|
+
attributes:
|
|
47
|
+
label: Receipts
|
|
48
|
+
description: Logs, CI links, screenshots. If unknown, say UNKNOWN — never guess.
|
|
49
|
+
- type: checkboxes
|
|
50
|
+
id: checks
|
|
51
|
+
attributes:
|
|
52
|
+
label: Pre-flight checks
|
|
53
|
+
options:
|
|
54
|
+
- label: I searched existing issues for duplicates
|
|
55
|
+
required: true
|
|
56
|
+
- label: This is not a security vulnerability (see SECURITY.md for private disclosure)
|
|
57
|
+
required: true
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Issue-template routing for the szl-holdings estate.
|
|
2
|
+
# Installed by the szl-alignment engine; keep in sync with
|
|
3
|
+
# alignment/templates/ISSUE_TEMPLATE/config.yml.
|
|
4
|
+
blank_issues_enabled: false
|
|
5
|
+
contact_links:
|
|
6
|
+
- name: Security vulnerability
|
|
7
|
+
url: https://github.com/szl-holdings
|
|
8
|
+
about: Report vulnerabilities privately via GitHub Security Advisories — see SECURITY.md. Never file public issues for undisclosed vulnerabilities.
|
|
9
|
+
- name: Org-wide questions
|
|
10
|
+
url: https://github.com/szl-holdings
|
|
11
|
+
about: Questions about the estate, doctrine, or alignment — start with the org profile.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
## What
|
|
2
|
+
|
|
3
|
+
<!-- What changed, and why. One paragraph, evidence-linked. -->
|
|
4
|
+
|
|
5
|
+
## Receipts
|
|
6
|
+
|
|
7
|
+
<!-- CI run links, test output, screenshots, audit artifacts.
|
|
8
|
+
UNKNOWN is never claimed as PASS. -->
|
|
9
|
+
|
|
10
|
+
## Checklist
|
|
11
|
+
|
|
12
|
+
- [ ] Control before action: the invariant (test / policy / assertion) landed before or with the behavior change
|
|
13
|
+
- [ ] Evidence after: receipts updated and linked above
|
|
14
|
+
- [ ] UNKNOWN is not claimed as PASS anywhere in this PR
|
|
15
|
+
- [ ] No new a11oy.com references — the forbidden domain must never appear (gate: `forbidden-domain.yml`)
|
|
16
|
+
- [ ] Docs updated (README / CHANGELOG) if behavior or surfaces changed
|
|
17
|
+
- [ ] Commits are conventional and signed off (`Signed-off-by:`)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
- package-ecosystem: "github-actions"
|
|
4
|
+
directory: "/"
|
|
5
|
+
schedule:
|
|
6
|
+
interval: "weekly"
|
|
7
|
+
day: "monday"
|
|
8
|
+
time: "08:00"
|
|
9
|
+
timezone: "America/New_York"
|
|
10
|
+
labels:
|
|
11
|
+
- "dependencies"
|
|
12
|
+
open-pull-requests-limit: 5
|
|
13
|
+
groups:
|
|
14
|
+
github_actions:
|
|
15
|
+
patterns:
|
|
16
|
+
- "*"
|
|
17
|
+
|
|
18
|
+
- package-ecosystem: "pip"
|
|
19
|
+
directory: "/"
|
|
20
|
+
schedule:
|
|
21
|
+
interval: "weekly"
|
|
22
|
+
day: "monday"
|
|
23
|
+
time: "08:00"
|
|
24
|
+
timezone: "America/New_York"
|
|
25
|
+
labels:
|
|
26
|
+
- "dependencies"
|
|
27
|
+
open-pull-requests-limit: 5
|
|
28
|
+
groups:
|
|
29
|
+
pip:
|
|
30
|
+
patterns:
|
|
31
|
+
- "*"
|
|
32
|
+
|
|
33
|
+
# Hash-locked CI requirements (requirements/ci/*.txt, compiled with
|
|
34
|
+
# `uv pip compile --universal --generate-hashes`; see the .in headers).
|
|
35
|
+
# tests/test_ci_requirements.py fails if a bump leaves a lock outside the
|
|
36
|
+
# pyproject.toml specifiers.
|
|
37
|
+
- package-ecosystem: "pip"
|
|
38
|
+
directory: "/requirements/ci"
|
|
39
|
+
schedule:
|
|
40
|
+
interval: "weekly"
|
|
41
|
+
day: "monday"
|
|
42
|
+
time: "08:00"
|
|
43
|
+
timezone: "America/New_York"
|
|
44
|
+
labels:
|
|
45
|
+
- "dependencies"
|
|
46
|
+
open-pull-requests-limit: 5
|
|
47
|
+
groups:
|
|
48
|
+
pip-ci:
|
|
49
|
+
patterns:
|
|
50
|
+
- "*"
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""SZL PyPI release gate — build, verify, and bind a distribution to its git tag.
|
|
3
|
+
|
|
4
|
+
Canonical file: identical bytes in every szl-holdings repository that publishes
|
|
5
|
+
to PyPI. Runs in the `build` job of `.github/workflows/publish-pypi.yml`:
|
|
6
|
+
|
|
7
|
+
* on pull_request / push -> builds and gates every declared package (readiness)
|
|
8
|
+
* on release (published) -> selects the package named by the tag, requires
|
|
9
|
+
tag == v{version} (single package) or
|
|
10
|
+
{name}-v{version} (multi-package repo), refuses
|
|
11
|
+
versions that already exist on PyPI, and emits
|
|
12
|
+
`dist/` plus a JSON receipt for the publish job.
|
|
13
|
+
|
|
14
|
+
Fails closed. Nothing here talks to PyPI with credentials; publication happens
|
|
15
|
+
only in the separate `publish` job through Trusted Publishing (OIDC).
|
|
16
|
+
"""
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import argparse
|
|
20
|
+
import hashlib
|
|
21
|
+
import json
|
|
22
|
+
import os
|
|
23
|
+
import re
|
|
24
|
+
import shutil
|
|
25
|
+
import subprocess
|
|
26
|
+
import sys
|
|
27
|
+
import tomllib
|
|
28
|
+
import urllib.error
|
|
29
|
+
import urllib.request
|
|
30
|
+
import zipfile
|
|
31
|
+
from email.parser import HeaderParser
|
|
32
|
+
from pathlib import Path
|
|
33
|
+
|
|
34
|
+
FORBIDDEN_TOP_LEVEL = {"tests", "test", "docs", "examples", "example", "scripts", "benchmarks", "build", "dist", "out", "data", "payload", "fixtures"}
|
|
35
|
+
PRIVATE_CLASSIFIER = "Private :: Do Not Upload"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def norm(name: str) -> str:
|
|
39
|
+
return re.sub(r"[-_.]+", "-", name).lower()
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def sh(cmd: list[str], **kw) -> subprocess.CompletedProcess:
|
|
43
|
+
print("+", " ".join(cmd), flush=True)
|
|
44
|
+
return subprocess.run(cmd, check=True, text=True, **kw)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def fail(msg: str) -> None:
|
|
48
|
+
print(f"::error::{msg}", flush=True)
|
|
49
|
+
sys.exit(1)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def warn(msg: str) -> None:
|
|
53
|
+
print(f"::warning::{msg}", flush=True)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def parse_packages(spec: str) -> list[dict]:
|
|
57
|
+
pkgs = []
|
|
58
|
+
for item in [s.strip() for s in spec.split(";") if s.strip()]:
|
|
59
|
+
name, _, rest = item.partition("=")
|
|
60
|
+
directory, _, env = rest.partition(":")
|
|
61
|
+
pkgs.append({"name": name.strip(), "dir": (directory or ".").strip(), "environment": (env or "pypi").strip()})
|
|
62
|
+
if not pkgs:
|
|
63
|
+
fail("SZL_PACKAGES is empty")
|
|
64
|
+
return pkgs
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def pypi_versions(name: str) -> set[str] | None:
|
|
68
|
+
req = urllib.request.Request(f"https://pypi.org/pypi/{norm(name)}/json", headers={"User-Agent": "szl-pypi-release-gate/1"})
|
|
69
|
+
try:
|
|
70
|
+
with urllib.request.urlopen(req, timeout=30) as r:
|
|
71
|
+
return set(json.loads(r.read())["releases"].keys())
|
|
72
|
+
except urllib.error.HTTPError as e:
|
|
73
|
+
if e.code == 404:
|
|
74
|
+
return set()
|
|
75
|
+
warn(f"PyPI lookup for {name} failed: HTTP {e.code}")
|
|
76
|
+
return None
|
|
77
|
+
except Exception as e: # network trouble is not a reason to publish blindly on release
|
|
78
|
+
warn(f"PyPI lookup for {name} failed: {e}")
|
|
79
|
+
return None
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def read_metadata(wheel: Path) -> dict:
|
|
83
|
+
with zipfile.ZipFile(wheel) as z:
|
|
84
|
+
meta_name = next(n for n in z.namelist() if n.endswith(".dist-info/METADATA"))
|
|
85
|
+
msg = HeaderParser().parsestr(z.read(meta_name).decode("utf-8", "replace"))
|
|
86
|
+
top = sorted({n.split("/")[0] for n in z.namelist() if not n.split("/")[0].endswith(".dist-info")})
|
|
87
|
+
py_files = [n for n in z.namelist() if n.endswith(".py")]
|
|
88
|
+
return {"msg": msg, "top_level": top, "py_count": len(py_files)}
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def gate_package(pkg: dict, repo: str, event: str, ref_type: str, ref_name: str, outdir: Path) -> dict:
|
|
92
|
+
pdir = Path(pkg["dir"]).resolve()
|
|
93
|
+
pyproject = pdir / "pyproject.toml"
|
|
94
|
+
if not pyproject.is_file():
|
|
95
|
+
fail(f"{pkg['name']}: {pyproject} not found")
|
|
96
|
+
with open(pyproject, "rb") as f:
|
|
97
|
+
pp = tomllib.load(f)
|
|
98
|
+
project = pp.get("project") or {}
|
|
99
|
+
release_cfg = ((pp.get("tool") or {}).get("szl") or {}).get("release") or {}
|
|
100
|
+
|
|
101
|
+
if norm(project.get("name", "")) != norm(pkg["name"]):
|
|
102
|
+
fail(f"{pkg['name']}: pyproject [project].name is {project.get('name')!r}, expected {pkg['name']!r}")
|
|
103
|
+
if PRIVATE_CLASSIFIER in (project.get("classifiers") or []):
|
|
104
|
+
fail(f"{pkg['name']}: carries '{PRIVATE_CLASSIFIER}' — not a publishable package")
|
|
105
|
+
if not (pp.get("build-system") or {}).get("build-backend"):
|
|
106
|
+
fail(f"{pkg['name']}: pyproject has no [build-system].build-backend (PEP 517 backend must be explicit)")
|
|
107
|
+
for key in ("description", "readme", "requires-python"):
|
|
108
|
+
if not project.get(key):
|
|
109
|
+
fail(f"{pkg['name']}: [project].{key} is required")
|
|
110
|
+
if not (project.get("license") or project.get("license-files")):
|
|
111
|
+
fail(f"{pkg['name']}: [project].license (or license-files) is required")
|
|
112
|
+
urls = project.get("urls") or {}
|
|
113
|
+
source_url = urls.get("Source") or urls.get("Repository") or urls.get("Source Code")
|
|
114
|
+
expected_source_url = f"https://github.com/{repo}"
|
|
115
|
+
if not isinstance(source_url, str) or source_url.rstrip("/").lower() != expected_source_url.lower():
|
|
116
|
+
fail(f"{pkg['name']}: [project.urls] must carry Source/Repository = {expected_source_url} (got {source_url!r}); PyPI verifies this against the Trusted Publisher")
|
|
117
|
+
|
|
118
|
+
# Build into a per-package staging dir, then verify.
|
|
119
|
+
stage = outdir.parent / f"stage-{norm(pkg['name'])}"
|
|
120
|
+
shutil.rmtree(stage, ignore_errors=True)
|
|
121
|
+
stage.mkdir(parents=True)
|
|
122
|
+
sh([sys.executable, "-m", "build", "--outdir", str(stage), str(pdir)])
|
|
123
|
+
files = sorted(stage.iterdir())
|
|
124
|
+
wheels = [f for f in files if f.suffix == ".whl"]
|
|
125
|
+
sdists = [f for f in files if f.name.endswith(".tar.gz")]
|
|
126
|
+
if len(wheels) != 1 or len(sdists) != 1:
|
|
127
|
+
fail(f"{pkg['name']}: expected exactly one wheel and one sdist, got {[f.name for f in files]}")
|
|
128
|
+
sh([sys.executable, "-m", "twine", "check", "--strict", *[str(f) for f in files]])
|
|
129
|
+
|
|
130
|
+
meta = read_metadata(wheels[0])
|
|
131
|
+
msg = meta["msg"]
|
|
132
|
+
version = msg["Version"]
|
|
133
|
+
if norm(msg["Name"]) != norm(pkg["name"]):
|
|
134
|
+
fail(f"{pkg['name']}: built metadata Name={msg['Name']!r} does not match")
|
|
135
|
+
if meta["py_count"] == 0:
|
|
136
|
+
fail(f"{pkg['name']}: wheel contains no Python files — packaging is misconfigured")
|
|
137
|
+
bad_top = sorted(set(meta["top_level"]) & FORBIDDEN_TOP_LEVEL)
|
|
138
|
+
if bad_top:
|
|
139
|
+
fail(f"{pkg['name']}: wheel leaks non-package top-level entries {bad_top}; fix package discovery")
|
|
140
|
+
declared = release_cfg.get("import_names")
|
|
141
|
+
if declared:
|
|
142
|
+
actual = {t[:-3] if t.endswith(".py") else t for t in meta["top_level"]}
|
|
143
|
+
if actual != set(declared):
|
|
144
|
+
fail(f"{pkg['name']}: wheel top-level names {sorted(actual)} != declared [tool.szl.release].import_names {sorted(declared)}")
|
|
145
|
+
if not msg.get_all("Project-URL"):
|
|
146
|
+
fail(f"{pkg['name']}: built metadata has no Project-URL entries")
|
|
147
|
+
|
|
148
|
+
# Tag binding (release only) and PyPI collision check.
|
|
149
|
+
tag_expect_single = f"v{version}"
|
|
150
|
+
tag_expect_multi = f"{norm(pkg['name'])}-v{version}"
|
|
151
|
+
if event == "release":
|
|
152
|
+
if ref_type != "tag":
|
|
153
|
+
fail(f"release event on non-tag ref {ref_type}:{ref_name}")
|
|
154
|
+
if ref_name not in (tag_expect_single, tag_expect_multi):
|
|
155
|
+
fail(f"{pkg['name']}: tag {ref_name!r} does not match built version {version!r} (expected {tag_expect_single!r} or {tag_expect_multi!r})")
|
|
156
|
+
existing = pypi_versions(pkg["name"])
|
|
157
|
+
if existing is None and event == "release":
|
|
158
|
+
fail(f"{pkg['name']}: could not confirm PyPI release list; refusing to publish blind")
|
|
159
|
+
if existing and version in existing:
|
|
160
|
+
if event == "release":
|
|
161
|
+
fail(f"{pkg['name']}=={version} already exists on PyPI; bump [project].version before releasing")
|
|
162
|
+
warn(f"{pkg['name']}=={version} already exists on PyPI; a release will require a version bump")
|
|
163
|
+
|
|
164
|
+
# Smoke-install the wheel in isolation and import-check the declared names.
|
|
165
|
+
venv = outdir.parent / f"venv-{norm(pkg['name'])}"
|
|
166
|
+
shutil.rmtree(venv, ignore_errors=True)
|
|
167
|
+
sh([sys.executable, "-m", "venv", str(venv)])
|
|
168
|
+
vpy = venv / ("Scripts" if os.name == "nt" else "bin") / "python"
|
|
169
|
+
sh([str(vpy), "-m", "pip", "install", "--disable-pip-version-check", "-q", str(wheels[0])])
|
|
170
|
+
sh([str(vpy), "-c", f"import importlib.metadata as m; assert m.version({pkg['name']!r}) == {version!r}, m.version({pkg['name']!r})"])
|
|
171
|
+
for mod in declared or []:
|
|
172
|
+
sh([str(vpy), "-c", f"import importlib; importlib.import_module({mod!r})"])
|
|
173
|
+
|
|
174
|
+
outdir.mkdir(parents=True, exist_ok=True)
|
|
175
|
+
receipt = {"package": pkg["name"], "version": version, "environment": pkg["environment"], "event": event, "ref": f"{ref_type}:{ref_name}",
|
|
176
|
+
"repository": repo, "source_sha": os.environ.get("GITHUB_SHA"), "run_id": os.environ.get("GITHUB_RUN_ID"),
|
|
177
|
+
"files": {}, "top_level": meta["top_level"]}
|
|
178
|
+
for f in files:
|
|
179
|
+
receipt["files"][f.name] = hashlib.sha256(f.read_bytes()).hexdigest()
|
|
180
|
+
shutil.copy2(f, outdir / f.name)
|
|
181
|
+
print(json.dumps(receipt, indent=2))
|
|
182
|
+
return receipt
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def main() -> None:
|
|
186
|
+
ap = argparse.ArgumentParser()
|
|
187
|
+
ap.add_argument("--packages", required=True, help="name=dir[:environment];name2=dir2[:environment]")
|
|
188
|
+
ap.add_argument("--repo", required=True)
|
|
189
|
+
ap.add_argument("--event", required=True)
|
|
190
|
+
ap.add_argument("--ref-type", required=True)
|
|
191
|
+
ap.add_argument("--ref-name", required=True)
|
|
192
|
+
ap.add_argument("--outdir", default="dist")
|
|
193
|
+
args = ap.parse_args()
|
|
194
|
+
|
|
195
|
+
pkgs = parse_packages(args.packages)
|
|
196
|
+
outdir = Path(args.outdir).resolve()
|
|
197
|
+
shutil.rmtree(outdir, ignore_errors=True)
|
|
198
|
+
|
|
199
|
+
if args.event == "release":
|
|
200
|
+
if len(pkgs) == 1:
|
|
201
|
+
selected = pkgs
|
|
202
|
+
else:
|
|
203
|
+
m = re.match(r"^(?P<name>.+?)-v(?P<ver>\d.*)$", args.ref_name)
|
|
204
|
+
if not m:
|
|
205
|
+
fail(f"multi-package repository: tag must be <package>-v<version>, got {args.ref_name!r}")
|
|
206
|
+
selected = [p for p in pkgs if norm(p["name"]) == norm(m.group("name"))]
|
|
207
|
+
if len(selected) != 1:
|
|
208
|
+
fail(f"tag {args.ref_name!r} names no declared package; declared: {[p['name'] for p in pkgs]}")
|
|
209
|
+
else:
|
|
210
|
+
selected = pkgs
|
|
211
|
+
|
|
212
|
+
receipts = []
|
|
213
|
+
for pkg in selected:
|
|
214
|
+
pkg_out = outdir if args.event == "release" else outdir / norm(pkg["name"])
|
|
215
|
+
receipts.append(gate_package(pkg, args.repo, args.event, args.ref_type, args.ref_name, pkg_out))
|
|
216
|
+
|
|
217
|
+
(outdir.parent / "release-gate.json").write_text(json.dumps(receipts, indent=2))
|
|
218
|
+
gh_out = os.environ.get("GITHUB_OUTPUT")
|
|
219
|
+
if gh_out and args.event == "release":
|
|
220
|
+
r = receipts[0]
|
|
221
|
+
with open(gh_out, "a") as f:
|
|
222
|
+
f.write(f"package={r['package']}\nversion={r['version']}\nenvironment={r['environment']}\n")
|
|
223
|
+
print(f"release gate PASS for {[r['package'] + '==' + r['version'] for r in receipts]}")
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
if __name__ == "__main__":
|
|
227
|
+
main()
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Standard Python gate for the szl-holdings estate: ruff + pytest on the two
|
|
2
|
+
# supported interpreters. Installed by the szl-alignment engine (only into
|
|
3
|
+
# repos where Python is detected) from
|
|
4
|
+
# alignment/templates/workflows/base-python-ci.yml. Local deviation from that
|
|
5
|
+
# template: the toolchain, the core dependency and the dev extra install from
|
|
6
|
+
# the hash-locked requirements/ci/base.txt (OpenSSF Scorecard
|
|
7
|
+
# Pinned-Dependencies), the package installs with --no-deps, and the
|
|
8
|
+
# template's requirements*.txt probes are dropped because this repository has
|
|
9
|
+
# no such files. The web extra stays uninstalled on purpose, so this gate
|
|
10
|
+
# keeps proving that the suite runs without fastapi/starlette/httpx.
|
|
11
|
+
name: base-python-ci
|
|
12
|
+
|
|
13
|
+
on:
|
|
14
|
+
pull_request:
|
|
15
|
+
push:
|
|
16
|
+
branches: [main, master]
|
|
17
|
+
|
|
18
|
+
permissions:
|
|
19
|
+
contents: read
|
|
20
|
+
|
|
21
|
+
jobs:
|
|
22
|
+
test:
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
strategy:
|
|
25
|
+
fail-fast: false
|
|
26
|
+
matrix:
|
|
27
|
+
python-version: ["3.11", "3.12"]
|
|
28
|
+
steps:
|
|
29
|
+
# Org policy: every action is pinned to a full-length commit SHA.
|
|
30
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
31
|
+
with:
|
|
32
|
+
persist-credentials: false
|
|
33
|
+
|
|
34
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
35
|
+
uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
|
|
36
|
+
with:
|
|
37
|
+
python-version: ${{ matrix.python-version }}
|
|
38
|
+
|
|
39
|
+
# Regenerate with the command in requirements/ci/base.in.
|
|
40
|
+
- name: Install toolchain (hash-locked)
|
|
41
|
+
run: python -m pip install --disable-pip-version-check --require-hashes -r requirements/ci/base.txt
|
|
42
|
+
|
|
43
|
+
- name: Install the package (no dependency resolution)
|
|
44
|
+
run: python -m pip install --disable-pip-version-check --no-deps --no-build-isolation -e .
|
|
45
|
+
|
|
46
|
+
# Blocking scope is the classes that indicate real breakage (syntax
|
|
47
|
+
# errors, undefined names, unused imports). The full default ruleset runs
|
|
48
|
+
# non-blocking so repos keep visibility without a forced flag day; repos
|
|
49
|
+
# with their own ruff config in pyproject.toml already get stricter
|
|
50
|
+
# behavior from their primary CI.
|
|
51
|
+
- name: "Ruff (blocking: E9/F)"
|
|
52
|
+
run: ruff check . --output-format github --select E9,F
|
|
53
|
+
|
|
54
|
+
- name: Ruff (full ruleset, informational)
|
|
55
|
+
run: ruff check . --statistics --exit-zero
|
|
56
|
+
|
|
57
|
+
# The base gate owns tests/ only. Suites living elsewhere (scripts/,
|
|
58
|
+
# tools/) are exercised by each repo's dedicated workflows with their
|
|
59
|
+
# own dependency pins; collecting them here would fail on imports this
|
|
60
|
+
# minimal install does not provide. Repos with no tests/ skip honestly.
|
|
61
|
+
- name: Pytest (blocking)
|
|
62
|
+
run: |
|
|
63
|
+
if [ -d tests ]; then
|
|
64
|
+
pytest -q tests
|
|
65
|
+
else
|
|
66
|
+
echo "::notice::No tests/ directory; base gate defers to repo-specific suites."
|
|
67
|
+
fi
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: ["**"]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: ["**"]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
test:
|
|
14
|
+
name: pytest (${{ matrix.python-version }})
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
strategy:
|
|
17
|
+
fail-fast: false
|
|
18
|
+
matrix:
|
|
19
|
+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
20
|
+
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
23
|
+
with:
|
|
24
|
+
persist-credentials: false
|
|
25
|
+
|
|
26
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
27
|
+
uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
|
|
28
|
+
with:
|
|
29
|
+
python-version: ${{ matrix.python-version }}
|
|
30
|
+
|
|
31
|
+
# Every distribution comes from the hash-locked file (core + dev + web
|
|
32
|
+
# extras, plus the build backend); the package itself then installs
|
|
33
|
+
# without resolving anything. Regenerate with the command in
|
|
34
|
+
# requirements/ci/test.in.
|
|
35
|
+
- name: Install hash-locked test dependencies
|
|
36
|
+
run: python -m pip install --disable-pip-version-check --require-hashes -r requirements/ci/test.txt
|
|
37
|
+
|
|
38
|
+
- name: Install the package (no dependency resolution)
|
|
39
|
+
run: python -m pip install --disable-pip-version-check --no-deps --no-build-isolation -e .
|
|
40
|
+
|
|
41
|
+
- name: Check installed requirements are consistent
|
|
42
|
+
run: python -m pip check
|
|
43
|
+
|
|
44
|
+
- name: Run pytest
|
|
45
|
+
run: python -m pytest -q
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
name: CodeQL
|
|
2
|
+
|
|
3
|
+
# Self-contained CodeQL SAST for Python. This repository is public, so code
|
|
4
|
+
# scanning results upload to the Security tab (the action's default) and pull
|
|
5
|
+
# requests get the code-scanning check. The earlier private-repo pattern ran with
|
|
6
|
+
# upload:false, which only failed on analysis errors and never surfaced a
|
|
7
|
+
# finding. All actions SHA-pinned (org policy).
|
|
8
|
+
|
|
9
|
+
on:
|
|
10
|
+
push:
|
|
11
|
+
branches: [main]
|
|
12
|
+
pull_request:
|
|
13
|
+
branches: [main]
|
|
14
|
+
schedule:
|
|
15
|
+
- cron: '27 4 * * 1'
|
|
16
|
+
|
|
17
|
+
permissions:
|
|
18
|
+
contents: read
|
|
19
|
+
|
|
20
|
+
concurrency:
|
|
21
|
+
group: codeql-${{ github.ref }}
|
|
22
|
+
cancel-in-progress: true
|
|
23
|
+
|
|
24
|
+
jobs:
|
|
25
|
+
analyze:
|
|
26
|
+
name: Analyze (${{ matrix.language }})
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
permissions:
|
|
29
|
+
actions: read
|
|
30
|
+
contents: read
|
|
31
|
+
security-events: write
|
|
32
|
+
strategy:
|
|
33
|
+
fail-fast: false
|
|
34
|
+
matrix:
|
|
35
|
+
language: [python]
|
|
36
|
+
steps:
|
|
37
|
+
- name: Harden the runner (Audit all outbound calls)
|
|
38
|
+
uses: step-security/harden-runner@1d88384fd173af6ecb012dfd0264a306b711241a # v2.22.4
|
|
39
|
+
with:
|
|
40
|
+
egress-policy: audit
|
|
41
|
+
- name: Checkout repository
|
|
42
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
43
|
+
with:
|
|
44
|
+
persist-credentials: false
|
|
45
|
+
- name: Initialize CodeQL
|
|
46
|
+
uses: github/codeql-action/init@33d7b85da1a04171e211befe2c739303b9a4c066 # v4.39.3
|
|
47
|
+
with:
|
|
48
|
+
languages: ${{ matrix.language }}
|
|
49
|
+
queries: security-extended
|
|
50
|
+
- name: Perform CodeQL Analysis
|
|
51
|
+
uses: github/codeql-action/analyze@33d7b85da1a04171e211befe2c739303b9a4c066 # v4.39.3
|
|
52
|
+
with:
|
|
53
|
+
category: "/language:${{ matrix.language }}"
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
name: Conventional Commits PR title lint
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
types: [opened, edited, synchronize, reopened]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
pull-requests: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
commitlint:
|
|
13
|
+
name: Lint PR title (Conventional Commits)
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- name: Check PR title follows Conventional Commits
|
|
17
|
+
env:
|
|
18
|
+
# Passed via env (never interpolated into the script) to avoid injection.
|
|
19
|
+
PR_TITLE: ${{ github.event.pull_request.title }}
|
|
20
|
+
run: |
|
|
21
|
+
set -euo pipefail
|
|
22
|
+
|
|
23
|
+
# Conventional Commits types allowed
|
|
24
|
+
types='feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert'
|
|
25
|
+
|
|
26
|
+
# <type>(optional-scope)(optional !): <subject>
|
|
27
|
+
header_re="^(${types})(\([a-z0-9._/-]+\))?(!)?: .+$"
|
|
28
|
+
|
|
29
|
+
if [[ ! "${PR_TITLE}" =~ ${header_re} ]]; then
|
|
30
|
+
echo "::error::PR title \"${PR_TITLE}\" is not a valid Conventional Commit."
|
|
31
|
+
echo "Expected format: <type>(optional-scope): <subject>"
|
|
32
|
+
echo "Allowed types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert"
|
|
33
|
+
exit 1
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
# Subject must not start with an upper-case letter
|
|
37
|
+
subject="${PR_TITLE#*: }"
|
|
38
|
+
if [[ "${subject}" =~ ^[A-Z] ]]; then
|
|
39
|
+
echo "::error::The subject \"${subject}\" does not match the required pattern. Please use lower-case for the subject."
|
|
40
|
+
exit 1
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
echo "PR title OK: ${PR_TITLE}"
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Release-blocking CRITICAL gate for the szl-holdings estate.
|
|
2
|
+
#
|
|
3
|
+
# Rule (validated on the live org): a line matching
|
|
4
|
+
# (?<!-)a11oy\.com
|
|
5
|
+
# is a violation UNLESS the same line is a prohibition/guard context, i.e. it
|
|
6
|
+
# also matches /(never|forbidden|not in|assertNotIn|does not appear|is not a
|
|
7
|
+
# surface|blocklist)/i — guard assertions, blocklists, and doctrine notes are
|
|
8
|
+
# legitimate. Canonical surfaces are a-11-oy.com (product) and a11oy.net
|
|
9
|
+
# (proof).
|
|
10
|
+
#
|
|
11
|
+
# Installed by the szl-alignment engine; keep in sync with
|
|
12
|
+
# alignment/templates/workflows/forbidden-domain.yml.
|
|
13
|
+
name: forbidden-domain
|
|
14
|
+
|
|
15
|
+
on:
|
|
16
|
+
pull_request:
|
|
17
|
+
|
|
18
|
+
permissions:
|
|
19
|
+
contents: read
|
|
20
|
+
|
|
21
|
+
jobs:
|
|
22
|
+
scan:
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
26
|
+
|
|
27
|
+
- name: Scan for forbidden domain references
|
|
28
|
+
shell: bash
|
|
29
|
+
run: |
|
|
30
|
+
set -euo pipefail
|
|
31
|
+
hits="$(rg -n --no-heading --hidden -P '(?<!-)a11oy\.com' --glob '!.git' . || true)"
|
|
32
|
+
violations="$(printf '%s' "$hits" | rg -v -i 'never|forbidden|not in|assertNotIn|does not appear|is not a surface|blocklist' || true)"
|
|
33
|
+
if [ -n "$violations" ]; then
|
|
34
|
+
echo "::error::Forbidden domain references found (release-blocking CRITICAL)."
|
|
35
|
+
echo "::error::Replace with a-11-oy.com or remove; guard contexts must say so on the same line."
|
|
36
|
+
printf '%s\n' "$violations"
|
|
37
|
+
exit 1
|
|
38
|
+
fi
|
|
39
|
+
echo "OK: no forbidden domain references outside guard contexts."
|