diff --git a/Dockerfile b/Dockerfile index a74ced8..b1cfbd3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,7 +7,7 @@ ENV PYTHONDONTWRITEBYTECODE=1 \ WORKDIR /app RUN apt-get update \ - && apt-get install -y --no-install-recommends curl ca-certificates \ + && apt-get install -y --no-install-recommends curl ca-certificates git openssh-client \ && rm -rf /var/lib/apt/lists/* \ && pip install --no-cache-dir uv diff --git a/api/classification.py b/api/classification.py index cefa59b..a2955bb 100644 --- a/api/classification.py +++ b/api/classification.py @@ -11,10 +11,13 @@ from pathlib import Path import yaml -# Primary path (sibling checkout); fallback relative to state-hub repo root. +# Workstation checkout, railiance01 fleet checkout, then state-hub sibling fallback. _PRIMARY_ALLOWED = Path( "/home/worsch/the-custodian/canon/standards/repo-classification.allowed.yaml" ) +_RAILIANCE_ALLOWED = Path( + "/home/tegwick/the-custodian/canon/standards/repo-classification.allowed.yaml" +) _FALLBACK_ALLOWED = ( Path(__file__).resolve().parent.parent.parent / "the-custodian" @@ -67,13 +70,12 @@ class ClassificationData: def _allowed_path() -> Path: - if _PRIMARY_ALLOWED.is_file(): - return _PRIMARY_ALLOWED - if _FALLBACK_ALLOWED.is_file(): - return _FALLBACK_ALLOWED + for candidate in (_PRIMARY_ALLOWED, _RAILIANCE_ALLOWED, _FALLBACK_ALLOWED): + if candidate.is_file(): + return candidate raise FileNotFoundError( "repo-classification.allowed.yaml not found at " - f"{_PRIMARY_ALLOWED} or {_FALLBACK_ALLOWED}" + f"{_PRIMARY_ALLOWED}, {_RAILIANCE_ALLOWED}, or {_FALLBACK_ALLOWED}" ) diff --git a/deploy/railiance/apps/charts/state-hub/templates/deployment.yaml b/deploy/railiance/apps/charts/state-hub/templates/deployment.yaml index 8821fc2..f931fe2 100644 --- a/deploy/railiance/apps/charts/state-hub/templates/deployment.yaml +++ b/deploy/railiance/apps/charts/state-hub/templates/deployment.yaml @@ -20,6 +20,17 @@ spec: {{- with .Values.imagePullSecrets }} imagePullSecrets: {{- toYaml . | nindent 8 }} {{- end }} + {{- if .Values.sweep.enabled }} + volumes: + - name: sweep-repos + hostPath: + path: {{ .Values.sweep.hostPath | quote }} + type: Directory + - name: sweep-ssh + hostPath: + path: {{ .Values.sweep.sshHostPath | quote }} + type: DirectoryOrCreate + {{- end }} containers: - name: state-hub image: {{ include "statehub.image" . | quote }} @@ -29,6 +40,26 @@ spec: - name: http containerPort: {{ .Values.service.targetPort }} protocol: TCP + {{- if .Values.sweep.enabled }} + lifecycle: + postStart: + exec: + command: + - /bin/sh + - -c + - git config --global --add safe.directory '*' + volumeMounts: + - name: sweep-repos + mountPath: {{ .Values.sweep.hostPath | quote }} + - name: sweep-ssh + mountPath: /root/.ssh + readOnly: true + env: + - name: STATE_HUB_SWEEP_HOSTNAME + value: {{ .Values.sweep.hostname | quote }} + - name: GIT_SSH_COMMAND + value: "ssh -o StrictHostKeyChecking=accept-new -F /root/.ssh/config" + {{- end }} envFrom: {{- if .Values.config.enabled }} - configMapRef: diff --git a/deploy/railiance/apps/charts/state-hub/values.yaml b/deploy/railiance/apps/charts/state-hub/values.yaml index 8d929ec..1bb454b 100644 --- a/deploy/railiance/apps/charts/state-hub/values.yaml +++ b/deploy/railiance/apps/charts/state-hub/values.yaml @@ -64,4 +64,11 @@ securityContext: {} nodeSelector: {} tolerations: [] -affinity: {} \ No newline at end of file +affinity: {} + +# Consistency sweep: mount railiance01 clone tree and match host_paths hostname. +sweep: + enabled: false + hostname: "" + hostPath: /home/tegwick + sshHostPath: /home/tegwick/.ssh \ No newline at end of file diff --git a/deploy/railiance/apps/helm/state-hub-values.yaml b/deploy/railiance/apps/helm/state-hub-values.yaml index 2b00137..3181cfe 100644 --- a/deploy/railiance/apps/helm/state-hub-values.yaml +++ b/deploy/railiance/apps/helm/state-hub-values.yaml @@ -10,3 +10,9 @@ image: ingress: enabled: false + +sweep: + enabled: true + hostname: 239.62.205.92.host.secureserver.net + hostPath: /home/tegwick + sshHostPath: /home/tegwick/.ssh diff --git a/scripts/consistency_check.py b/scripts/consistency_check.py index 4388e19..4c9adb1 100644 --- a/scripts/consistency_check.py +++ b/scripts/consistency_check.py @@ -70,6 +70,11 @@ _REPO_ROOT = Path(__file__).resolve().parent.parent if str(_REPO_ROOT) not in sys.path: sys.path.insert(0, str(_REPO_ROOT)) +def _sweep_hostname() -> str: + """Hostname for host_paths resolution; override when pod hostname != fleet FQDN.""" + return os.environ.get("STATE_HUB_SWEEP_HOSTNAME", "").strip() or socket.gethostname() + + from api.workplan_status import ( # noqa: E402 CANONICAL_WORKSTREAM_STATUSES, CLOSED_WORKSTREAM_STATUSES, @@ -617,7 +622,7 @@ def resolve_repo_path(repo: dict, override: str | None = None) -> str: """ if override: return override - hostname = socket.gethostname() + hostname = _sweep_hostname() host_paths = repo.get("host_paths") or {} return host_paths.get(hostname) or repo.get("local_path") or "" @@ -1692,7 +1697,7 @@ def fix_repo( if repo_path: repo_record = _api_get(api_base, f"/repos/{repo_slug}") if repo_record: - hostname = socket.gethostname() + hostname = _sweep_hostname() if (repo_record.get("host_paths") or {}).get(hostname) != repo_path: result = _api_post( api_base, f"/repos/{repo_slug}/paths", @@ -2491,7 +2496,7 @@ def main() -> None: sys.exit(0) else: # Resolve repo list - hostname = socket.gethostname() + hostname = _sweep_hostname() repo_slugs: list[str] = [] if args.all: repos = _api_get(args.api_base, "/repos")