mirror of
https://github.com/sredevopsorg/deepseek-kubernetes-sandbox.git
synced 2026-11-03 01:13:24 +00:00
feat(repo)!: restructure around the dsh-harness image and one example per implementation
- the root Dockerfile is the primary artifact; versions.env is the single source of truth
- merge gateway-api/dsh-demo into examples/dsh-agents-as-a-service (manifests/, exposure variants)
- move the single-tenant template into examples/minimal-single-tenant
- publish the gateway as a nested package under the repository image
- mirror the DSH ref for release identities (first release dsh-v0.2.1-rc.2-r2)
- CI: lint/python/manifests/gateway-image, reusable multi-arch build, tag-driven release, weekly e2e
- docs: README, docs/{main-image,examples,gitflow,versioning}, example READMEs, AGENTS.md, governance
- SSO is opt-in again; gateway pull policy and cookie defaults match the documented demo
BREAKING CHANGE: example manifest paths, the gateway image name, and the removal
of deploy/ and gateway-api/ affect existing deployments; see the migration table
in README.md.
This commit is contained in:
56
scripts/check-examples.sh
Executable file
56
scripts/check-examples.sh
Executable file
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env bash
|
||||
# Enforce the example contract documented in docs/examples.md.
|
||||
# Run by "make check" locally and by the CI lint job.
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
index=examples/README.md
|
||||
fail=0
|
||||
err() { echo "check-examples: $*" >&2; fail=1; }
|
||||
|
||||
[ -f "$index" ] || err "missing $index"
|
||||
|
||||
for dir in examples/*/; do
|
||||
name="$(basename "$dir")"
|
||||
[ "$name" = "_template" ] && continue
|
||||
|
||||
[ -f "${dir}README.md" ] || err "$name: missing README.md"
|
||||
[ -d "${dir}manifests" ] || err "$name: missing manifests/"
|
||||
|
||||
if [ -d "${dir}manifests" ]; then
|
||||
count=$(find "${dir}manifests" -maxdepth 1 -type f -name '*.yaml' | wc -l | tr -d ' ')
|
||||
[ "$count" -gt 0 ] || err "$name: manifests/ has no top-level *.yaml files"
|
||||
fi
|
||||
|
||||
stray=$(find "$dir" -maxdepth 1 -type f -name '*.yaml' | wc -l | tr -d ' ')
|
||||
[ "$stray" -eq 0 ] || err "$name: manifests belong under manifests/, not at the example root"
|
||||
|
||||
if [ -d "${dir}manifests/exposure" ] && [ ! -f "${dir}manifests/exposure/README.md" ]; then
|
||||
err "$name: manifests/exposure/ requires a README.md describing the variants"
|
||||
fi
|
||||
|
||||
for component in "$dir"*/; do
|
||||
[ -d "$component" ] || continue
|
||||
case "$(basename "$component")" in
|
||||
manifests|scripts|docs) continue ;;
|
||||
esac
|
||||
if [ -f "${component}Dockerfile" ] && [ ! -f "${component}README.md" ]; then
|
||||
err "$name/$(basename "$component"): component with a Dockerfile needs a README.md"
|
||||
fi
|
||||
done
|
||||
|
||||
if ! grep -Fq "examples/$name/" "$index"; then
|
||||
err "$name: not listed in examples/README.md"
|
||||
fi
|
||||
|
||||
for script in "$dir"scripts/*.sh; do
|
||||
[ -e "$script" ] || continue
|
||||
[ -x "$script" ] || err "$script: must be executable"
|
||||
done
|
||||
done
|
||||
|
||||
if [ "$fail" -ne 0 ]; then
|
||||
echo "check-examples: FAILED" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "check-examples: ok"
|
||||
47
scripts/check-manifests.py
Normal file
47
scripts/check-manifests.py
Normal file
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Parse every example manifest and assert the minimal Kubernetes shape."""
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
import yaml
|
||||
except ModuleNotFoundError: # pragma: no cover - environment guard
|
||||
sys.exit("check-manifests: PyYAML is required (python -m pip install pyyaml)")
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
FILES = sorted(ROOT.glob("examples/*/manifests/**/*.yaml"))
|
||||
|
||||
if not FILES:
|
||||
sys.exit("check-manifests: no manifests found under examples/*/manifests/")
|
||||
|
||||
errors: list[str] = []
|
||||
documents = 0
|
||||
for path in FILES:
|
||||
relative = path.relative_to(ROOT)
|
||||
try:
|
||||
loaded = list(yaml.safe_load_all(path.read_text(encoding="utf-8")))
|
||||
except yaml.YAMLError as exc:
|
||||
errors.append(f"{relative}: invalid YAML: {exc}")
|
||||
continue
|
||||
for position, document in enumerate(loaded, start=1):
|
||||
if document is None:
|
||||
continue
|
||||
documents += 1
|
||||
if not isinstance(document, dict):
|
||||
errors.append(f"{relative} document {position}: not a mapping")
|
||||
continue
|
||||
missing = [key for key in ("apiVersion", "kind", "metadata") if key not in document]
|
||||
if missing:
|
||||
errors.append(f"{relative} document {position}: missing {', '.join(missing)}")
|
||||
continue
|
||||
metadata = document["metadata"]
|
||||
if not isinstance(metadata, dict) or not metadata.get("name"):
|
||||
errors.append(f"{relative} document {position}: metadata.name is required")
|
||||
|
||||
if errors:
|
||||
print("\n".join(errors), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
print(f"check-manifests: ok ({len(FILES)} files, {documents} documents)")
|
||||
70
scripts/check-versions.sh
Executable file
70
scripts/check-versions.sh
Executable file
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env bash
|
||||
# Verify that every version pin in the repository agrees with versions.env.
|
||||
# Run by "make check" locally and by the CI lint job.
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
# shellcheck source=/dev/null
|
||||
. ./versions.env
|
||||
|
||||
IDENTITY="$DSH_VERSION"
|
||||
if [ -n "${RELEASE_REVISION:-}" ]; then
|
||||
IDENTITY="$DSH_VERSION-$RELEASE_REVISION"
|
||||
fi
|
||||
|
||||
fail=0
|
||||
err() { echo "check-versions: $*" >&2; fail=1; }
|
||||
expect() { # <file> <literal>
|
||||
if ! grep -Fq -- "$2" "$1"; then
|
||||
err "$1: expected to contain: $2"
|
||||
fi
|
||||
}
|
||||
|
||||
# Main Dockerfile build args
|
||||
expect Dockerfile "ARG DSH_VERSION=$DSH_VERSION"
|
||||
expect Dockerfile "ARG PNPM_VERSION=$PNPM_VERSION"
|
||||
expect Dockerfile "ARG RELEASE_REVISION=$RELEASE_REVISION"
|
||||
|
||||
# Local configuration mirrored for humans
|
||||
expect .env.sample "DSH_VERSION=$DSH_VERSION"
|
||||
expect .env.sample "PNPM_VERSION=$PNPM_VERSION"
|
||||
expect .env.sample "RELEASE_REVISION=$RELEASE_REVISION"
|
||||
|
||||
# Image references in the example manifests
|
||||
for f in \
|
||||
examples/dsh-agents-as-a-service/manifests/10-sandboxtemplate.yaml \
|
||||
examples/minimal-single-tenant/manifests/sandbox-template.yaml; do
|
||||
expect "$f" "image: $GHCR_IMAGE:$IDENTITY"
|
||||
done
|
||||
expect examples/dsh-agents-as-a-service/manifests/50-gateway.yaml "image: $GATEWAY_IMAGE:$IDENTITY"
|
||||
|
||||
# The gateway image must live below the repository image (one package per
|
||||
# example component, no unrelated top-level packages).
|
||||
case "$GATEWAY_IMAGE" in
|
||||
"$GHCR_IMAGE"/*) ;;
|
||||
*) err "GATEWAY_IMAGE ($GATEWAY_IMAGE) must be nested below GHCR_IMAGE ($GHCR_IMAGE)" ;;
|
||||
esac
|
||||
|
||||
# Build tooling must read versions.env instead of hard-coding a version.
|
||||
# (scripts/ is excluded: set-version.sh documents usage examples in comments.)
|
||||
while IFS= read -r hit; do
|
||||
[ -n "$hit" ] || continue
|
||||
err "hard-coded version outside versions.env: $hit"
|
||||
done < <(grep -rnE 'dsh-v[0-9]' .github/workflows Makefile 2>/dev/null || true)
|
||||
|
||||
# Prose may mention versions, but only the current identity or its base tag.
|
||||
while IFS= read -r token; do
|
||||
[ -n "$token" ] || continue
|
||||
case "$token" in
|
||||
"$IDENTITY"|"$DSH_VERSION") ;;
|
||||
*) err "stale version reference in docs: $token" ;;
|
||||
esac
|
||||
done < <(grep -rhoE 'dsh-v[0-9][A-Za-z0-9._-]*' \
|
||||
--include='*.md' --exclude-dir=.git --exclude-dir=.venv --exclude-dir=.uvcache \
|
||||
--exclude=CHANGELOG.md . | sort -u)
|
||||
|
||||
if [ "$fail" -ne 0 ]; then
|
||||
echo "check-versions: FAILED" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "check-versions: ok ($IDENTITY)"
|
||||
57
scripts/set-version.sh
Executable file
57
scripts/set-version.sh
Executable file
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env bash
|
||||
# Rewrite every version pin in one pass, then leave the tree ready for
|
||||
# "make check".
|
||||
#
|
||||
# ./scripts/set-version.sh dsh-v0.2.2-rc.1 # new DSH ref, no revision
|
||||
# ./scripts/set-version.sh dsh-v0.2.1-rc.2 r3 # same ref, new repo revision
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
new_version="${1:-}"
|
||||
new_revision="${2:-}"
|
||||
|
||||
case "$new_version" in
|
||||
dsh-v[0-9]*) ;;
|
||||
*) echo "usage: $0 <dsh-vX.Y.Z> [rN]" >&2; exit 2 ;;
|
||||
esac
|
||||
|
||||
if [ -n "$new_revision" ]; then
|
||||
case "$new_revision" in
|
||||
r[2-9]|r[1-9][0-9]*) ;;
|
||||
*) echo "revision must look like r2, r3, ..." >&2; exit 2 ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# shellcheck source=/dev/null
|
||||
old_version="$(. ./versions.env; printf '%s' "$DSH_VERSION")"
|
||||
# shellcheck source=/dev/null
|
||||
old_revision="$(. ./versions.env; printf '%s' "${RELEASE_REVISION:-}")"
|
||||
|
||||
old_identity="$old_version"
|
||||
[ -z "$old_revision" ] || old_identity="$old_version-$old_revision"
|
||||
new_identity="$new_version"
|
||||
[ -z "$new_revision" ] || new_identity="$new_version-$new_revision"
|
||||
|
||||
if [ "$old_identity" = "$new_identity" ]; then
|
||||
echo "set-version: already at $new_identity"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Replace the identity first (it contains the base version), then the base
|
||||
# version itself. CHANGELOG.md is generated from tags, so it is left alone.
|
||||
while IFS= read -r file; do
|
||||
[ -f "$file" ] || continue
|
||||
case "$file" in
|
||||
.env.sample|Dockerfile|Makefile|*.md|*.yaml|*.yml|*.sh|*.py|*.toml|*.env) ;;
|
||||
*) continue ;;
|
||||
esac
|
||||
sed -i "s/$old_identity/$new_identity/g; s/$old_version/$new_version/g" "$file"
|
||||
done < <(git ls-files)
|
||||
|
||||
# versions.env is authoritative: write both keys explicitly so an empty
|
||||
# revision is stored as an empty value.
|
||||
sed -i "s/^DSH_VERSION=.*/DSH_VERSION=$new_version/" versions.env
|
||||
sed -i "s/^RELEASE_REVISION=.*/RELEASE_REVISION=$new_revision/" versions.env
|
||||
|
||||
echo "set-version: $old_identity -> $new_identity"
|
||||
echo "set-version: run 'make check' and review the diff"
|
||||
Reference in New Issue
Block a user