diff --git a/.rabbit/context.yaml b/.rabbit/context.yaml index b711f8a..f9791e3 100644 --- a/.rabbit/context.yaml +++ b/.rabbit/context.yaml @@ -5,8 +5,8 @@ version: udx.dev/dev.kit/v1 generator: tool: dev.kit repo: https://github.com/udx/dev.kit - version: 0.12.0 - generated_at: 2026-05-27T09:28:30Z + version: 0.13.0 + generated_at: 2026-05-27T18:39:54Z sources: homepage: https://udx.dev/kit repository: https://github.com/udx/dev.kit @@ -56,20 +56,6 @@ commands: run: make run source: docs/references/command-surfaces.md -# Gaps — Factors that are missing or only partially supported by current repo signals. -# Note: Base the result on explicit factor rules, not free-form judgment. -# Note: Include message and evidence so the status can be reviewed. -# Note: Prefer traceable refs and missing signals over vague advice. - -gaps: - - factor: config - status: partial - message: Found config-bearing repo assets in deploy.yml, but no canonical checked-in config contract is declared yet. - repair_target: deploy.yml or .env.example - reference: docs/references/config-contract-surfaces.md - evidence: - - runtime config: deploy.yml - # Dependencies — Meaningful dependency-repo contracts such as reusable workflows, images, or versioned manifests this repo relies on. # Note: Capture execution-shaping behavior defined outside the current checkout. # Note: Avoid promoting standard package inventory or ordinary GitHub action refs into top-level context. diff --git a/changes.md b/changes.md index f870310..eae0b9f 100644 --- a/changes.md +++ b/changes.md @@ -1,5 +1,9 @@ # Changes +### 0.13.0 + +- Allow repo-owned manifests to satisfy config contract coverage when they declare explicit config contract metadata or runtime config sections, avoiding forced `.env.example` files for repos with custom manifest contracts. + ### 0.12.0 - Add generator source refs to `.rabbit/context.yaml` so generated context points to the dev.kit homepage, source repo, npm package, and installation guide. diff --git a/deploy.yml b/deploy.yml index d1dcce0..61993c3 100644 --- a/deploy.yml +++ b/deploy.yml @@ -11,4 +11,3 @@ config: command: "/bin/bash" args: - "/workspace/tests/suite.sh" - diff --git a/docs/context-coverage.md b/docs/context-coverage.md index 442a167..0bf7b66 100644 --- a/docs/context-coverage.md +++ b/docs/context-coverage.md @@ -79,7 +79,7 @@ gaps: message: No explicit configuration contract was detected. ``` -That should lead to a repo change such as adding config docs, manifest metadata, or a checked-in example file, then regenerating context. +That should lead to a repo change such as adding config docs, adding explicit manifest metadata, declaring runtime config sections in a repo-owned manifest, or adding a checked-in example file, then regenerating context. ## What Does Not Belong There diff --git a/docs/references/config-contract-surfaces.md b/docs/references/config-contract-surfaces.md index 20c70da..526d79c 100644 --- a/docs/references/config-contract-surfaces.md +++ b/docs/references/config-contract-surfaces.md @@ -11,7 +11,7 @@ Configuration is often declared through: - `.env.example`, `.env.sample`, or `.env.template` - focused repo docs such as `README.md` or `docs/config.md` - deploy manifests such as `deploy.yml` -- versioned YAML/JSON manifests with explicit config metadata +- versioned YAML/JSON manifests with explicit config metadata or runtime config sections - checked-in example config files when the repo uses a custom format ## Build defaults and runtime overlays @@ -26,6 +26,8 @@ Example pattern: That is still one coherent repo contract as long as the split is explicit and checked in. +Custom manifests should not rely on filename rules. For YAML manifests, `dev.kit` treats explicit contract metadata such as `contract: config` or runtime config sections such as `config.env`, `config.environment`, `config.image`, `config.command`, or top-level `env`/`variables`/`settings` as configuration contract evidence. + ## Practical rule When config gaps are detected: diff --git a/lib/modules/repo_factors.sh b/lib/modules/repo_factors.sh index 47dac43..098b486 100644 --- a/lib/modules/repo_factors.sh +++ b/lib/modules/repo_factors.sh @@ -54,6 +54,89 @@ dev_kit_repo_has_meaningful_dependency_contract() { return 1 } +dev_kit_manifest_declares_config_contract() { + local manifest_path="$1" + + [ -f "$manifest_path" ] || return 1 + + awk ' + /^[[:space:]]*#/ { next } + /^[^[:space:]][^:]*:/ { + in_contracts = 0 + in_config = 0 + } + /^[[:space:]]*contracts:[[:space:]]*$/ { + in_contracts = 1 + next + } + /^[[:space:]]*contracts:[[:space:]]*/ { + value = $0 + sub(/^[[:space:]]*contracts:[[:space:]]*/, "", value) + gsub(/["'\''\[\],]/, " ", value) + if (value ~ /(^|[[:space:]])config([[:space:]]|$)/) { + found = 1 + } + in_contracts = 0 + next + } + /^[[:space:]]*contract:[[:space:]]*/ { + value = $0 + sub(/^[[:space:]]*contract:[[:space:]]*/, "", value) + gsub(/["'\''\[\],]/, " ", value) + if (value ~ /(^|[[:space:]])config([[:space:]]|$)/) { + found = 1 + } + next + } + in_contracts && /^[[:space:]]*-[[:space:]]*/ { + value = $0 + sub(/^[[:space:]]*-[[:space:]]*/, "", value) + gsub(/["'\'',]/, " ", value) + if (value ~ /(^|[[:space:]])config([[:space:]]|$)/) { + found = 1 + } + next + } + /^[[:space:]]*config:[[:space:]]*$/ { + in_config = 1 + next + } + /^(env|environment|secrets|variables|settings):[[:space:]]*/ { + found = 1 + next + } + in_config && /^[[:space:]]+[A-Za-z0-9_.-]+:[[:space:]]*/ { + value = $0 + sub(/^[[:space:]]+/, "", value) + sub(/:.*/, "", value) + if (value ~ /^(env|environment|secrets|variables|settings|image|images|container|containers|service|services|command|args|volumes)$/) { + found = 1 + } + next + } + END { exit(found ? 0 : 1) } + ' "$manifest_path" +} + +dev_kit_repo_config_contract_manifest_files() { + local repo_dir="$1" + local path="" + + while IFS= read -r path; do + [ -n "$path" ] || continue + if dev_kit_manifest_declares_config_contract "${repo_dir}/${path}"; then + printf '%s\n' "$path" + fi + done <"$tmp_file" && mv "$tmp_file" "$file_path" } +json_factor_status() { + local json="$1" + local factor="$2" + + printf '%s\n' "$json" | awk -v factor="$factor" ' + $0 ~ "\"" factor "\":[[:space:]]*\\{" { + in_factor = 1 + next + } + in_factor && /"status":[[:space:]]*"/ { + sub(/^.*"status":[[:space:]]*"/, "", $0) + sub(/".*$/, "", $0) + print + exit + } + in_factor && /^ }/ { + exit + } + ' +} + while [ "$#" -gt 0 ]; do case "$1" in --only) @@ -104,6 +125,8 @@ DEV_KIT_BIN_DIR="$TEST_HOME/.local/bin" mkdir -p "$DEV_KIT_BIN_DIR" ln -sf "$REPO_DIR/bin/dev-kit" "$DEV_KIT_BIN_DIR/dev.kit" export PATH="$DEV_KIT_BIN_DIR:$PATH" +assert_contains "$(command -v dev.kit)" "$DEV_KIT_BIN_DIR/dev.kit" "suite: uses local dev.kit shim" +assert_contains "$(dev.kit --version)" "$(awk -F'"' '/"version"/{print $4; exit}' "$REPO_DIR/package.json")" "suite: uses checkout dev.kit version" # shellcheck disable=SC1090 . "$DEV_KIT_HOME/bin/env/dev-kit.sh" @@ -146,6 +169,8 @@ if should_run_explicit "repo-contract"; then docker_repo_json="$(cd "$DOCKER_ACTION_REPO" && dev.kit repo --json)" assert_contains "$docker_repo_json" "\"context\":" "repo contract: docker repo reports context path" + assert_contains "$(json_factor_status "$docker_repo_json" config)" "present" "repo contract: reports present config factor" + assert_contains "$docker_repo_json" "config contract manifest: deploy.yml" "repo contract: manifest config shape satisfies config contract" docker_context_yaml="${DOCKER_ACTION_REPO}/.rabbit/context.yaml" assert_contains "$(cat "$docker_context_yaml")" "path: deploy.yml" "repo contract: includes deploy manifest" @@ -364,6 +389,8 @@ if should_run "core"; then docker_repo_json="$(cd "$DOCKER_ACTION_REPO" && dev.kit repo --json)" assert_contains "$docker_repo_json" "\"context\":" "docker repo: reports context path" + assert_contains "$(json_factor_status "$docker_repo_json" config)" "present" "docker repo: reports present config factor" + assert_contains "$docker_repo_json" "config contract manifest: deploy.yml" "docker repo: manifest config shape satisfies config contract" docker_context_yaml="${DOCKER_ACTION_REPO}/.rabbit/context.yaml" assert_contains "$(cat "$docker_context_yaml")" "generator:" "docker repo: includes generator metadata" @@ -385,6 +412,8 @@ EOF cat > "$IGNORED_ACTION_REPO/deploy.yml" <<'EOF' version: udx.io/worker-v1/deploy kind: workerDeployConfig +metadata: + env: staging EOF cat > "$IGNORED_ACTION_REPO/.next/cache/reference.txt" <<'EOF' deploy.yml @@ -392,6 +421,7 @@ EOF ignored_repo_json="$(cd "$IGNORED_ACTION_REPO" && dev.kit repo --json)" assert_contains "$ignored_repo_json" "\"context\":" "ignored repo: reports context path" + assert_contains "$ignored_repo_json" "no canonical checked-in config contract is declared yet" "ignored repo: deploy filename alone does not satisfy config contract" ignored_context_yaml="${IGNORED_ACTION_REPO}/.rabbit/context.yaml" assert_not_contains "$(cat "$ignored_context_yaml")" ".next/cache/reference.txt" "ignored repo: excludes gitignored artifact references"