NK-WP-0058-T07: conventions lint, server-side applies, one cluster route
All checks were successful
Bounded-execution contracts / contracts (push) Successful in 1m25s
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

tools/conventions/lint.py (make conventions-lint, contract suite): server-side-apply, one-wait-condition,
one-cluster-route, tool-numbered-receipts, managing-controller (Argo-owned paths from railiance-platform),
with reasoned exceptions. 21 runbook applies made server-side. portal-client-rollout.py (and so
keycape-client.py and the rollout tools) and identity-provisioner-reconcile.py use NK_KUBECONFIG,
default ~/.kube/config-railiance01, without a second kubeconfig or --server override.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 943911@bnt-lap001
Assistant-Session: fc322940-1c8e-4bed-ab39-86cec99f23a0
This commit is contained in:
tegwick 2026-11-06 00:34:00 +01:00
parent 80d280a340
commit cd265b84a4
13 changed files with 226 additions and 28 deletions

View file

@ -0,0 +1,71 @@
"""tools/conventions/lint.py (NK-WP-0058-T07): each rule on synthetic lines, and this repository is clean."""
import importlib.util
from pathlib import Path
import pytest
import yaml
REPO = Path(__file__).resolve().parents[3]
spec = importlib.util.spec_from_file_location('conventions_lint', REPO / 'tools/conventions/lint.py')
lint = importlib.util.module_from_spec(spec)
spec.loader.exec_module(lint)
def rules(rel, line, owned=frozenset()):
return [rule for _, _, rule in lint.check_file(rel, line + '\n', owned)]
@pytest.mark.parametrize('line, found', [
('kubectl apply -f deployment.yaml', ['server-side-apply']),
('$K apply --dry-run=server -f x.yaml && $K apply -f x.yaml', ['server-side-apply']),
('$(KUBECTL) apply -f manifests/rbac.yaml', ['server-side-apply']),
('kubectl apply --server-side -f deployment.yaml', []),
('kubectl get pods # apply nothing', []),
])
def test_server_side_apply(line, found):
assert rules('runbooks/x.md', line) == found
def test_one_wait_condition():
assert rules('x.sh', 'kubectl wait --for=condition=complete --for=condition=failed job/x') == [
'one-wait-condition']
assert rules('x.sh', 'kubectl wait --for=condition=available deploy/x') == []
@pytest.mark.parametrize('line, found', [
("args = ['kubectl', '--kubeconfig', '/home/worsch/.kube/config-hosteurope']", True),
("args = ['kubectl', '--server', 'https://127.0.0.1:16444']", True),
("KUBE = '/etc/rancher/k3s/k3s.yaml'", True),
("KUBECONFIG = os.environ.get('NK_KUBECONFIG', os.path.expanduser('~/.kube/config-railiance01'))", False),
])
def test_one_cluster_route(line, found):
assert ('one-cluster-route' in rules('tools/x.py', line)) is found
assert rules('tools/tests/test_x.py', line) == [] # tests may build synthetic routes
def test_tool_numbered_receipts():
assert rules('runbooks/x.md', 'python3 lane.py setup --receipt "$E/01-setup.json"') == ['tool-numbered-receipts']
assert rules('runbooks/x.md', 'python3 bx.py run') == []
def test_managing_controller(tmp_path):
app = tmp_path / 'railiance-platform/argocd/apps/x.yaml'
app.parent.mkdir(parents=True)
app.write_text(yaml.safe_dump({'kind': 'Application', 'spec': {'source': {
'repoURL': 'https://forgejo.coulomb.social/coulomb/informed-decision.git', 'path': 'deploy/lifecycle'}}}))
owned = lint.argo_paths(tmp_path / 'railiance-platform')
assert owned == {('informed-decision', 'deploy/lifecycle')}
line = 'kubectl apply --server-side -f ~/informed-decision/deploy/lifecycle/review-groups.yaml'
assert rules('runbooks/x.md', line, owned) == ['managing-controller']
assert rules('runbooks/x.md', 'kubectl apply --server-side -f ~/approval-engine/deploy/np.yaml', owned) == []
def test_records_and_exceptions_are_not_findings():
assert lint.RECORDS.search('docs/evidence/2026-11-05-x/README.md')
assert rules('sso-mfa/WORKPLAN.md', 'kubectl apply -f a.yaml') == [] # a listed exception with its reason
assert all(reason for reason in lint.EXCEPTIONS.values())
def test_this_repository_is_clean():
findings = lint.lint(REPO, lint.argo_paths(REPO.parent / 'railiance-platform'))
assert findings == [], findings