From fb26dec8dabdea58453dfc1ad9328463a6d6ee47 Mon Sep 17 00:00:00 2001 From: rogu3bear Date: Wed, 1 Jul 2026 00:34:25 -0500 Subject: [PATCH] fix lifecycle spec path resolution Resolve relative hostname and maildesk lifecycle spec paths against the caller's working directory when that file exists, falling back to the control-plane state directory for built-in examples. This keeps app-repo cfctl invocations from looking for private desired-state files under the cfctl repo while preserving the existing checked-in examples. Add a maildesk contract regression for caller-relative specs. --- scripts/cf_hostname_lifecycle.py | 9 ++++++--- scripts/cf_maildesk_cf_lifecycle.py | 9 ++++++--- scripts/verify_maildesk_cf_contract.sh | 19 ++++++++++++++++++- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/scripts/cf_hostname_lifecycle.py b/scripts/cf_hostname_lifecycle.py index f894bff..1be82d2 100755 --- a/scripts/cf_hostname_lifecycle.py +++ b/scripts/cf_hostname_lifecycle.py @@ -37,9 +37,12 @@ def default_spec_file() -> Path: def resolve_spec_path(value: str | None) -> Path: if value: path = Path(value) - if not path.is_absolute(): - path = ROOT / path - return path + if path.is_absolute(): + return path + caller_path = Path.cwd() / path + if caller_path.exists(): + return caller_path + return ROOT / path return default_spec_file() diff --git a/scripts/cf_maildesk_cf_lifecycle.py b/scripts/cf_maildesk_cf_lifecycle.py index 26621ee..e722ec0 100644 --- a/scripts/cf_maildesk_cf_lifecycle.py +++ b/scripts/cf_maildesk_cf_lifecycle.py @@ -42,9 +42,12 @@ def default_spec_file() -> Path: def resolve_spec_path(value: str | None) -> Path: if value: path = Path(value) - if not path.is_absolute(): - path = ROOT / path - return path + if path.is_absolute(): + return path + caller_path = Path.cwd() / path + if caller_path.exists(): + return caller_path + return ROOT / path return default_spec_file() diff --git a/scripts/verify_maildesk_cf_contract.sh b/scripts/verify_maildesk_cf_contract.sh index 1ceb84d..d13a905 100755 --- a/scripts/verify_maildesk_cf_contract.sh +++ b/scripts/verify_maildesk_cf_contract.sh @@ -22,7 +22,8 @@ require_source_line() { fixture_file="$(mktemp "${TMPDIR:-/tmp}/maildesk-cf-fixture.XXXXXX")" missing_fixture_file="$(mktemp "${TMPDIR:-/tmp}/maildesk-cf-missing-fixture.XXXXXX")" unverified_sender_fixture_file="$(mktemp "${TMPDIR:-/tmp}/maildesk-cf-unverified-sender.XXXXXX")" -trap 'rm -f "${fixture_file}" "${missing_fixture_file}" "${unverified_sender_fixture_file}"' EXIT +caller_spec_dir="$(mktemp -d "${TMPDIR:-/tmp}/maildesk-cf-caller-spec.XXXXXX")" +trap 'rm -f "${fixture_file}" "${missing_fixture_file}" "${unverified_sender_fixture_file}"; rm -rf "${caller_spec_dir}"' EXIT require_source_line "worker evidence lane" '"worker.script": run_cfctl(["list", "worker.script"], lane="global"),' "${ROOT_DIR}/scripts/cf_maildesk_cf_lifecycle.py" require_source_line "d1 evidence lane" '"d1.database": run_cfctl(["list", "d1.database"], lane="global"),' "${ROOT_DIR}/scripts/cf_maildesk_cf_lifecycle.py" @@ -211,6 +212,22 @@ jq -e ' and .summary.mail_ready == true ' <<< "${cfctl_output}" >/dev/null || die "cfctl provision --plan envelope did not match" +cp "${ROOT_DIR}/state/maildesk-cf/example.json" "${caller_spec_dir}/caller-relative.json" +caller_spec_physical_dir="$(cd -P "${caller_spec_dir}" && pwd)" +caller_relative_output="$( + cd "${caller_spec_dir}" + MAILDESK_CF_EVIDENCE_FILE="${fixture_file}" \ + "${ROOT_DIR}/cfctl" maildesk-cf verify --file caller-relative.json +)" +jq -e \ + --arg expected_spec "${caller_spec_physical_dir}/caller-relative.json" \ + ' + .ok == true + and .summary.spec_path == $expected_spec + and .summary.edge_ready == true + and .summary.mail_ready == true + ' <<< "${caller_relative_output}" >/dev/null || die "caller-relative maildesk-cf spec path did not resolve" + standards_output="$("${ROOT_DIR}/cfctl" standards maildesk-cf)" jq -e ' .ok == true