Merge branch 'main' into rename
Keep pouch naming and .pouch/ack project state, and bring in ACK regression mode, deployer test-environment binding, and manage-release updates from main.
This commit is contained in:
@@ -102,9 +102,9 @@ CLASSIFICATIONS = {"development", "staging", "production"}
|
||||
STOP_POINTS = {"verified", "validation_ready", "review_ready", "released"}
|
||||
INTENT_FIELDS = {"testEnvironment", "release"}
|
||||
INTENT_STOP_AT = {
|
||||
"testEnvironment": "validation_ready",
|
||||
"release": "released",
|
||||
}
|
||||
TEST_ENVIRONMENT_FIELDS = {"via", "env"}
|
||||
ACTIONS = {
|
||||
"verify",
|
||||
"pull-request",
|
||||
@@ -681,10 +681,47 @@ def _validate_profiles(
|
||||
errors.append(f"{where}: defaultProfile 不能部署 production 环境")
|
||||
|
||||
|
||||
def _validate_test_environment_intent(
|
||||
value: Any,
|
||||
errors: list[str],
|
||||
project_root: Path | None,
|
||||
) -> None:
|
||||
if value is None:
|
||||
return
|
||||
if isinstance(value, str):
|
||||
errors.append(
|
||||
"intents.testEnvironment: 已改为 deployer 绑定 "
|
||||
"{via: deployer, env: <env>},不能再使用 profile ID "
|
||||
f"{value!r}。请迁移后由 ACK 内部调用 deployer skill"
|
||||
)
|
||||
return
|
||||
if not _mapping(value):
|
||||
errors.append("intents.testEnvironment: 必须是 null 或 {via, env} 对象")
|
||||
return
|
||||
_reject_unknown(value, TEST_ENVIRONMENT_FIELDS, "intents.testEnvironment", errors)
|
||||
if value.get("via") != "deployer":
|
||||
errors.append("intents.testEnvironment.via 必须是 'deployer'")
|
||||
env = value.get("env")
|
||||
if not isinstance(env, str) or ID_RE.fullmatch(env) is None:
|
||||
errors.append("intents.testEnvironment.env 必须是小写连字符环境名")
|
||||
return
|
||||
if project_root is None:
|
||||
return
|
||||
env_dir = project_root / ".pouch" / "deployer" / env
|
||||
if not env_dir.is_dir():
|
||||
env_dir = project_root / ".skiff" / "deployer" / env
|
||||
if not env_dir.is_dir():
|
||||
errors.append(
|
||||
"intents.testEnvironment.env: 找不到 "
|
||||
f".pouch/deployer/{env};先按 deployer skill 配置项目测试环境"
|
||||
)
|
||||
|
||||
|
||||
def _validate_intents(
|
||||
values: Any,
|
||||
profiles: dict[str, Any],
|
||||
errors: list[str],
|
||||
project_root: Path | None = None,
|
||||
) -> None:
|
||||
if values is None:
|
||||
return
|
||||
@@ -692,25 +729,29 @@ def _validate_intents(
|
||||
errors.append("intents: 必须是对象")
|
||||
return
|
||||
_reject_unknown(values, INTENT_FIELDS, "intents", errors)
|
||||
for field in sorted(INTENT_FIELDS):
|
||||
if field not in values:
|
||||
errors.append(f"intents.{field}: 必填")
|
||||
continue
|
||||
profile_id = values[field]
|
||||
if profile_id is None:
|
||||
continue
|
||||
if not isinstance(profile_id, str) or ID_RE.fullmatch(profile_id) is None:
|
||||
errors.append(f"intents.{field}: 必须是 null 或小写连字符 profile ID")
|
||||
continue
|
||||
profile = profiles.get(profile_id)
|
||||
if profile is None:
|
||||
errors.append(f"intents.{field}: 未定义 profile {profile_id!r}")
|
||||
continue
|
||||
expected_stop = INTENT_STOP_AT[field]
|
||||
if _mapping(profile) and profile.get("stopAt") != expected_stop:
|
||||
errors.append(
|
||||
f"intents.{field}: profile {profile_id!r} 必须 stopAt {expected_stop}"
|
||||
)
|
||||
if "testEnvironment" not in values:
|
||||
errors.append("intents.testEnvironment: 必填")
|
||||
else:
|
||||
_validate_test_environment_intent(
|
||||
values.get("testEnvironment"), errors, project_root
|
||||
)
|
||||
if "release" not in values:
|
||||
errors.append("intents.release: 必填")
|
||||
return
|
||||
profile_id = values["release"]
|
||||
if profile_id is None:
|
||||
return
|
||||
if not isinstance(profile_id, str) or ID_RE.fullmatch(profile_id) is None:
|
||||
errors.append("intents.release: 必须是 null 或小写连字符 profile ID")
|
||||
return
|
||||
profile = profiles.get(profile_id)
|
||||
if profile is None:
|
||||
errors.append(f"intents.release: 未定义 profile {profile_id!r}")
|
||||
return
|
||||
if _mapping(profile) and profile.get("stopAt") != INTENT_STOP_AT["release"]:
|
||||
errors.append(
|
||||
f"intents.release: profile {profile_id!r} 必须 stopAt released"
|
||||
)
|
||||
|
||||
|
||||
def validate_builtin(data: dict[str, Any], project_root: Path | None = None) -> list[str]:
|
||||
@@ -758,7 +799,7 @@ def validate_builtin(data: dict[str, Any], project_root: Path | None = None) ->
|
||||
environments=environments,
|
||||
errors=errors,
|
||||
)
|
||||
_validate_intents(data.get("intents"), profiles, errors)
|
||||
_validate_intents(data.get("intents"), profiles, errors, project_root)
|
||||
|
||||
if enabled:
|
||||
if default_profile not in profiles:
|
||||
|
||||
Reference in New Issue
Block a user