refactor(corecmd): retire zombie annotation paths and fix alias validation
Make enum and required-flag validation alias-aware via a shared flagNameProvided/EffectiveValue path so values passed through flag aliases are no longer skipped; drop the fully-migrated runtime risk/gate annotation bridge and the orphaned tool-metadata/title annotation writers, trimming the cli seam re-exports accordingly.
This commit is contained in:
1 parent
e135440b98
commit
ec7f4deaf4
12 files changed
+131
-239
No files matched your search
@@ -11,7 +11,6 @@ import (
|
|||||||
func TestCrossPlatformCoverageRuntimeAnnotationAPIsCoverage(t *testing.T) {
|
func TestCrossPlatformCoverageRuntimeAnnotationAPIsCoverage(t *testing.T) {
|
||||||
AttachRuntimeSchema(nil, "p", "t", "s")
|
AttachRuntimeSchema(nil, "p", "t", "s")
|
||||||
AttachRuntimeSchema(&cobra.Command{}, "", "", "")
|
AttachRuntimeSchema(&cobra.Command{}, "", "", "")
|
||||||
AnnotateRuntimeToolMetadata(nil, "", "", "")
|
|
||||||
AnnotateRuntimeFlag(nil, "x", "x", "string", false, "")
|
AnnotateRuntimeFlag(nil, "x", "x", "string", false, "")
|
||||||
AnnotateRuntimeFlagProperty(nil, "x", "x")
|
AnnotateRuntimeFlagProperty(nil, "x", "x")
|
||||||
AnnotateRuntimeRequiredFlags(nil, "x")
|
AnnotateRuntimeRequiredFlags(nil, "x")
|
||||||
@@ -23,12 +22,10 @@ func TestCrossPlatformCoverageRuntimeAnnotationAPIsCoverage(t *testing.T) {
|
|||||||
AnnotateRuntimeFlagExample(nil, "x", "a")
|
AnnotateRuntimeFlagExample(nil, "x", "a")
|
||||||
AnnotateRuntimeConstraints(nil, RuntimeSchemaConstraints{})
|
AnnotateRuntimeConstraints(nil, RuntimeSchemaConstraints{})
|
||||||
AnnotateRuntimePositionals(nil)
|
AnnotateRuntimePositionals(nil)
|
||||||
ExcludeFromRuntimeSchema(nil)
|
|
||||||
|
|
||||||
cmd := &cobra.Command{Use: "run", Run: func(*cobra.Command, []string) {}}
|
cmd := &cobra.Command{Use: "run", Run: func(*cobra.Command, []string) {}}
|
||||||
cmd.Flags().StringP("value", "v", "", "value")
|
cmd.Flags().StringP("value", "v", "", "value")
|
||||||
AttachRuntimeSchema(cmd, " product ", " tool ", " source ")
|
AttachRuntimeSchema(cmd, " product ", " tool ", " source ")
|
||||||
AnnotateRuntimeToolMetadata(cmd, " Title ", " Description ", " source ")
|
|
||||||
AnnotateRuntimeFlag(cmd, "", "property", "string", true, "")
|
AnnotateRuntimeFlag(cmd, "", "property", "string", true, "")
|
||||||
AnnotateRuntimeFlag(cmd, "missing", "property", "string", true, "")
|
AnnotateRuntimeFlag(cmd, "missing", "property", "string", true, "")
|
||||||
AnnotateRuntimeFlag(cmd, "value", " property ", " string ", true, "")
|
AnnotateRuntimeFlag(cmd, "value", " property ", " string ", true, "")
|
||||||
@@ -52,13 +49,12 @@ func TestCrossPlatformCoverageRuntimeAnnotationAPIsCoverage(t *testing.T) {
|
|||||||
contract.RuntimeSchemaPositional{Name: " second ", Index: 1, Description: " desc "},
|
contract.RuntimeSchemaPositional{Name: " second ", Index: 1, Description: " desc "},
|
||||||
contract.RuntimeSchemaPositional{Name: "first", Index: 0, Type: " number "},
|
contract.RuntimeSchemaPositional{Name: "first", Index: 0, Type: " number "},
|
||||||
)
|
)
|
||||||
ExcludeFromRuntimeSchema(cmd)
|
|
||||||
setRuntimeCommandAnnotation(cmd, "empty", " ")
|
setRuntimeCommandAnnotation(cmd, "empty", " ")
|
||||||
setFlagAnnotation(nil, "x", "y")
|
setFlagAnnotation(nil, "x", "y")
|
||||||
setFlagAnnotation(cmd.Flags().Lookup("value"), "empty", " ")
|
setFlagAnnotation(cmd.Flags().Lookup("value"), "empty", " ")
|
||||||
setFlagAnnotationValues(nil, "x", "y")
|
setFlagAnnotationValues(nil, "x", "y")
|
||||||
setFlagAnnotationValues(cmd.Flags().Lookup("value"), "empty", " ")
|
setFlagAnnotationValues(cmd.Flags().Lookup("value"), "empty", " ")
|
||||||
if cmd.Annotations[runtimeSchemaProductAnnotation] != "product" || cmd.Annotations[runtimeSchemaExcludeAnnotation] != "true" {
|
if cmd.Annotations[runtimeSchemaProductAnnotation] != "product" {
|
||||||
t.Fatalf("command annotations = %#v", cmd.Annotations)
|
t.Fatalf("command annotations = %#v", cmd.Annotations)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -253,7 +253,7 @@ func TestCrossPlatformCoverageRuntimeToolSpecFromContractFinalSafetyAnnotationFa
|
|||||||
|
|
||||||
// Safety nil + Contract Risk annotation: Risk overlay wins.
|
// Safety nil + Contract Risk annotation: Risk overlay wins.
|
||||||
riskCmd := &cobra.Command{Use: "create"}
|
riskCmd := &cobra.Command{Use: "create"}
|
||||||
runtimeannotate.AnnotateRuntimeRisk(riskCmd, "write")
|
riskCmd.Annotations = map[string]string{runtimeannotate.AnnotationRisk: "write"}
|
||||||
entry.Command = riskCmd
|
entry.Command = riskCmd
|
||||||
spec, err := runtimeToolSpecFromContractFinal(entry, contract.ContractFinalPayload{}, runtimeSchemaMetadataSources{})
|
spec, err := runtimeToolSpecFromContractFinal(entry, contract.ContractFinalPayload{}, runtimeSchemaMetadataSources{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -268,7 +268,7 @@ func TestCrossPlatformCoverageRuntimeToolSpecFromContractFinalSafetyAnnotationFa
|
|||||||
|
|
||||||
// Safety nil + runtime gate annotation: gate overlay wins.
|
// Safety nil + runtime gate annotation: gate overlay wins.
|
||||||
gateCmd := &cobra.Command{Use: "create"}
|
gateCmd := &cobra.Command{Use: "create"}
|
||||||
runtimeannotate.AnnotateRuntimeGate(gateCmd, "devAppRequireWriteGuard")
|
gateCmd.Annotations = map[string]string{runtimeannotate.AnnotationRuntimeGate: "devAppRequireWriteGuard"}
|
||||||
entry.Command = gateCmd
|
entry.Command = gateCmd
|
||||||
spec, err = runtimeToolSpecFromContractFinal(entry, contract.ContractFinalPayload{}, runtimeSchemaMetadataSources{})
|
spec, err = runtimeToolSpecFromContractFinal(entry, contract.ContractFinalPayload{}, runtimeSchemaMetadataSources{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -24,10 +24,6 @@ func TestCrossPlatformCoverageRuntimeSchemaLoaderAndAnnotationEdges(t *testing.T
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmd := &cobra.Command{Use: "run", Short: "short", Long: "long"}
|
cmd := &cobra.Command{Use: "run", Short: "short", Long: "long"}
|
||||||
AnnotateRuntimeToolMetadata(cmd, " title ", " description ", " source ")
|
|
||||||
if cmd.Annotations[runtimeannotate.AnnotationTitle] != "title" {
|
|
||||||
t.Fatalf("annotations = %#v", cmd.Annotations)
|
|
||||||
}
|
|
||||||
AnnotateRuntimePositionals(cmd, contract.RuntimeSchemaPositional{Name: " ", Index: -1})
|
AnnotateRuntimePositionals(cmd, contract.RuntimeSchemaPositional{Name: " ", Index: -1})
|
||||||
if _, ok := cmd.Annotations[runtimeSchemaArgsAnnotation]; ok {
|
if _, ok := cmd.Annotations[runtimeSchemaArgsAnnotation]; ok {
|
||||||
t.Fatal("invalid positional should not be annotated")
|
t.Fatal("invalid positional should not be annotated")
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ type RuntimeSchemaConstraints = runtimeannotate.RuntimeSchemaConstraints
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
AttachRuntimeSchema = runtimeannotate.AttachRuntimeSchema
|
AttachRuntimeSchema = runtimeannotate.AttachRuntimeSchema
|
||||||
AnnotateRuntimeToolMetadata = runtimeannotate.AnnotateRuntimeToolMetadata
|
|
||||||
AnnotateRuntimeFlag = runtimeannotate.AnnotateRuntimeFlag
|
AnnotateRuntimeFlag = runtimeannotate.AnnotateRuntimeFlag
|
||||||
AnnotateRuntimeFlagProperty = runtimeannotate.AnnotateRuntimeFlagProperty
|
AnnotateRuntimeFlagProperty = runtimeannotate.AnnotateRuntimeFlagProperty
|
||||||
AnnotateRuntimeRequiredFlags = runtimeannotate.AnnotateRuntimeRequiredFlags
|
AnnotateRuntimeRequiredFlags = runtimeannotate.AnnotateRuntimeRequiredFlags
|
||||||
@@ -43,13 +42,10 @@ var (
|
|||||||
AnnotateRuntimeFlagEnum = runtimeannotate.AnnotateRuntimeFlagEnum
|
AnnotateRuntimeFlagEnum = runtimeannotate.AnnotateRuntimeFlagEnum
|
||||||
AnnotateRuntimeFlagExample = runtimeannotate.AnnotateRuntimeFlagExample
|
AnnotateRuntimeFlagExample = runtimeannotate.AnnotateRuntimeFlagExample
|
||||||
AnnotateRuntimeContract = runtimeannotate.AnnotateRuntimeContract
|
AnnotateRuntimeContract = runtimeannotate.AnnotateRuntimeContract
|
||||||
AnnotateRuntimeRisk = runtimeannotate.AnnotateRuntimeRisk
|
|
||||||
AnnotateRuntimeGate = runtimeannotate.AnnotateRuntimeGate
|
|
||||||
RuntimeContractRisk = runtimeannotate.RuntimeContractRisk
|
RuntimeContractRisk = runtimeannotate.RuntimeContractRisk
|
||||||
RuntimeContractGate = runtimeannotate.RuntimeContractGate
|
RuntimeContractGate = runtimeannotate.RuntimeContractGate
|
||||||
AnnotateRuntimeConstraints = runtimeannotate.AnnotateRuntimeConstraints
|
AnnotateRuntimeConstraints = runtimeannotate.AnnotateRuntimeConstraints
|
||||||
AnnotateRuntimePositionals = runtimeannotate.AnnotateRuntimePositionals
|
AnnotateRuntimePositionals = runtimeannotate.AnnotateRuntimePositionals
|
||||||
ExcludeFromRuntimeSchema = runtimeannotate.ExcludeFromRuntimeSchema
|
|
||||||
HasDeclaredOrAnnotatedConfirmation = contractfinal.HasDeclaredOrAnnotatedConfirmation
|
HasDeclaredOrAnnotatedConfirmation = contractfinal.HasDeclaredOrAnnotatedConfirmation
|
||||||
RuntimeContractFinal = contractfinal.RuntimeContractFinal
|
RuntimeContractFinal = contractfinal.RuntimeContractFinal
|
||||||
HasRuntimeContractFinal = contractfinal.HasRuntimeContractFinal
|
HasRuntimeContractFinal = contractfinal.HasRuntimeContractFinal
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ func TestCrossPlatformCoverageHasDeclaredOrAnnotatedConfirmationBranches(t *test
|
|||||||
}
|
}
|
||||||
|
|
||||||
gated := &cobra.Command{Use: "gated"}
|
gated := &cobra.Command{Use: "gated"}
|
||||||
runtimeannotate.AnnotateRuntimeGate(gated, "devAppRequireWriteGuard")
|
gated.Annotations = map[string]string{runtimeannotate.AnnotationRuntimeGate: "devAppRequireWriteGuard"}
|
||||||
if !HasDeclaredOrAnnotatedConfirmation(gated) {
|
if !HasDeclaredOrAnnotatedConfirmation(gated) {
|
||||||
t.Fatal("annotated gate must satisfy declare-OR-annotate")
|
t.Fatal("annotated gate must satisfy declare-OR-annotate")
|
||||||
}
|
}
|
||||||
@@ -44,7 +44,7 @@ func TestCrossPlatformCoverageHasDeclaredOrAnnotatedConfirmationBranches(t *test
|
|||||||
}
|
}
|
||||||
|
|
||||||
risky := &cobra.Command{Use: "risky"}
|
risky := &cobra.Command{Use: "risky"}
|
||||||
runtimeannotate.AnnotateRuntimeRisk(risky, "read")
|
risky.Annotations = map[string]string{runtimeannotate.AnnotationRisk: "read"}
|
||||||
if !HasDeclaredOrAnnotatedConfirmation(risky) {
|
if !HasDeclaredOrAnnotatedConfirmation(risky) {
|
||||||
t.Fatal("Contract Risk annotation must satisfy declare-OR-annotate")
|
t.Fatal("Contract Risk annotation must satisfy declare-OR-annotate")
|
||||||
}
|
}
|
||||||
|
|||||||
+28
-10
@@ -605,15 +605,16 @@ func RegisterFlag(cmd *cobra.Command, kind FlagKind, name, def, usage string) {
|
|||||||
// EnvVar/RequiredHint report their hint separately. The plain group is checked
|
// EnvVar/RequiredHint report their hint separately. The plain group is checked
|
||||||
// before the env group to preserve the handwritten order. Both groups use the
|
// before the env group to preserve the handwritten order. Both groups use the
|
||||||
// declared "main flag → alias → env" fallback: a compatible alias counts as
|
// declared "main flag → alias → env" fallback: a compatible alias counts as
|
||||||
// provided.
|
// provided. Shortcut mode keeps its stricter contract: it demands an explicit
|
||||||
|
// token, so the main flag or an alias must be Changed on the command line — a
|
||||||
|
// registration default does not satisfy it.
|
||||||
func ValidateRequired(cmd *cobra.Command, flags []FlagSpec) error {
|
func ValidateRequired(cmd *cobra.Command, flags []FlagSpec) error {
|
||||||
for _, flag := range flags {
|
for _, flag := range flags {
|
||||||
if flag.ValidationMode != ValidationShortcut {
|
if flag.ValidationMode != ValidationShortcut {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if flag.Required {
|
if flag.Required {
|
||||||
registered := cmd.Flags().Lookup(flag.Name)
|
if !flagNameProvided(cmd, flag) {
|
||||||
if registered == nil || !registered.Changed {
|
|
||||||
message := strings.TrimSpace(flag.RequiredError)
|
message := strings.TrimSpace(flag.RequiredError)
|
||||||
if message == "" {
|
if message == "" {
|
||||||
message = fmt.Sprintf("缺少必填参数 --%s", flag.Name)
|
message = fmt.Sprintf("缺少必填参数 --%s", flag.Name)
|
||||||
@@ -622,13 +623,11 @@ func ValidateRequired(cmd *cobra.Command, flags []FlagSpec) error {
|
|||||||
}
|
}
|
||||||
switch flag.Kind {
|
switch flag.Kind {
|
||||||
case KindStringSlice:
|
case KindStringSlice:
|
||||||
values, _ := cmd.Flags().GetStringSlice(flag.Name)
|
if !sliceHasValue(sliceValue(cmd, flag)) {
|
||||||
if !sliceHasValue(values) {
|
|
||||||
return apperrors.NewValidation(fmt.Sprintf("必填参数 --%s 不能为空", flag.Name))
|
return apperrors.NewValidation(fmt.Sprintf("必填参数 --%s 不能为空", flag.Name))
|
||||||
}
|
}
|
||||||
case KindString:
|
case KindString:
|
||||||
value, _ := cmd.Flags().GetString(flag.Name)
|
if strings.TrimSpace(EffectiveValue(cmd, flag)) == "" {
|
||||||
if strings.TrimSpace(value) == "" {
|
|
||||||
return apperrors.NewValidation(fmt.Sprintf("必填参数 --%s 不能为空", flag.Name))
|
return apperrors.NewValidation(fmt.Sprintf("必填参数 --%s 不能为空", flag.Name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -680,12 +679,14 @@ func ValidateEnums(cmd *cobra.Command, flags []FlagSpec) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func validateEnum(cmd *cobra.Command, flag FlagSpec) error {
|
func validateEnum(cmd *cobra.Command, flag FlagSpec) error {
|
||||||
if len(flag.Enum) == 0 || !cmd.Flags().Changed(flag.Name) {
|
if len(flag.Enum) == 0 || !flagNameProvided(cmd, flag) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
values := []string{flagString(cmd, flag.Kind, flag.Name)}
|
var values []string
|
||||||
if flag.Kind == KindStringSlice {
|
if flag.Kind == KindStringSlice {
|
||||||
values, _ = cmd.Flags().GetStringSlice(flag.Name)
|
values = sliceValue(cmd, flag)
|
||||||
|
} else {
|
||||||
|
values = []string{EffectiveValue(cmd, flag)}
|
||||||
}
|
}
|
||||||
for _, value := range values {
|
for _, value := range values {
|
||||||
value = strings.TrimSpace(value)
|
value = strings.TrimSpace(value)
|
||||||
@@ -705,6 +706,23 @@ func validateEnum(cmd *cobra.Command, flag FlagSpec) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// flagNameProvided reports whether the flag was explicitly passed on the
|
||||||
|
// command line under its main name or any declared alias. Registration
|
||||||
|
// defaults and environment variables do not count: those feed the
|
||||||
|
// effective-value chain, not the explicit-token checks used by Shortcut
|
||||||
|
// Required and enum validation.
|
||||||
|
func flagNameProvided(cmd *cobra.Command, flag FlagSpec) bool {
|
||||||
|
if cmd.Flags().Changed(flag.Name) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for _, alias := range flag.Aliases {
|
||||||
|
if cmd.Flags().Changed(alias) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// hasEffectiveValue decides whether a Required flag is satisfied, matching the
|
// hasEffectiveValue decides whether a Required flag is satisfied, matching the
|
||||||
// BuildArgs entry predicate (KindInt non-zero, string non-empty, KindBool
|
// BuildArgs entry predicate (KindInt non-zero, string non-empty, KindBool
|
||||||
// explicitly provided, KindStringSlice has a non-empty element). Integer parse
|
// explicitly provided, KindStringSlice has a non-empty element). Integer parse
|
||||||
|
|||||||
@@ -1531,6 +1531,67 @@ func TestCrossPlatformCoverageValidateRequiredShortcutMessages(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCrossPlatformCoverageValidateAliasAwareShortcutAndEnum(t *testing.T) {
|
||||||
|
// D2: a declared alias satisfies Shortcut Required.
|
||||||
|
aliased := []FlagSpec{{
|
||||||
|
Name: "target", Usage: "T", Required: true,
|
||||||
|
ValidationMode: ValidationShortcut, Aliases: []string{"t-alias"},
|
||||||
|
}}
|
||||||
|
cmd := newTestCommand()
|
||||||
|
RegisterFlags(cmd, aliased)
|
||||||
|
_ = cmd.Flags().Set("t-alias", "hit")
|
||||||
|
if err := ValidateRequired(cmd, aliased); err != nil {
|
||||||
|
t.Fatalf("alias-satisfied shortcut required err = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// D2: a registration default alone is not an explicit token, so Shortcut
|
||||||
|
// Required still reports the missing parameter.
|
||||||
|
defaulted := []FlagSpec{{
|
||||||
|
Name: "target", Usage: "T", Required: true, Default: "reg-default",
|
||||||
|
ValidationMode: ValidationShortcut, Aliases: []string{"t-alias"},
|
||||||
|
}}
|
||||||
|
cmd = newTestCommand()
|
||||||
|
RegisterFlags(cmd, defaulted)
|
||||||
|
if err := ValidateRequired(cmd, defaulted); err == nil || err.Error() != "缺少必填参数 --target" {
|
||||||
|
t.Fatalf("default-only shortcut required err = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// D1: an enum violation arriving through the alias is validated, not
|
||||||
|
// skipped, and the message names the main flag.
|
||||||
|
scalar := []FlagSpec{{Name: "mode", Usage: "M", Enum: []string{"a", "b"}, Aliases: []string{"m-alias"}}}
|
||||||
|
cmd = newTestCommand()
|
||||||
|
RegisterFlags(cmd, scalar)
|
||||||
|
_ = cmd.Flags().Set("m-alias", "z")
|
||||||
|
if err := ValidateEnums(cmd, scalar); err == nil || !strings.Contains(err.Error(), `参数 --mode 取值 "z" 不合法`) {
|
||||||
|
t.Fatalf("alias enum violation = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// D1: a valid alias value passes.
|
||||||
|
cmd = newTestCommand()
|
||||||
|
RegisterFlags(cmd, scalar)
|
||||||
|
_ = cmd.Flags().Set("m-alias", "a")
|
||||||
|
if err := ValidateEnums(cmd, scalar); err != nil {
|
||||||
|
t.Fatalf("valid alias enum err = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// D1: slice flags honor the alias too.
|
||||||
|
sliced := []FlagSpec{{Name: "ids", Usage: "I", Kind: KindStringSlice, Enum: []string{"x", "y"}, Aliases: []string{"ids-alias"}}}
|
||||||
|
cmd = newTestCommand()
|
||||||
|
RegisterFlags(cmd, sliced)
|
||||||
|
_ = cmd.Flags().Set("ids-alias", "x,z")
|
||||||
|
if err := ValidateEnums(cmd, sliced); err == nil || !strings.Contains(err.Error(), `参数 --ids 取值 "z" 不合法`) {
|
||||||
|
t.Fatalf("alias slice enum violation = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// D1: an unprovided flag with a registered default stays unvalidated.
|
||||||
|
untouched := []FlagSpec{{Name: "mode", Usage: "M", Enum: []string{"a", "b"}, Default: "z", Aliases: []string{"m-alias"}}}
|
||||||
|
cmd = newTestCommand()
|
||||||
|
RegisterFlags(cmd, untouched)
|
||||||
|
if err := ValidateEnums(cmd, untouched); err != nil {
|
||||||
|
t.Fatalf("default-only enum must not validate, err = %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCrossPlatformCoverageBuildArgsInvalidArgDefault(t *testing.T) {
|
func TestCrossPlatformCoverageBuildArgsInvalidArgDefault(t *testing.T) {
|
||||||
flags := []FlagSpec{{Name: "page-size", Usage: "P", Kind: KindInt, ArgDefault: "not-a-number"}}
|
flags := []FlagSpec{{Name: "page-size", Usage: "P", Kind: KindInt, ArgDefault: "not-a-number"}}
|
||||||
cmd := newTestCommand()
|
cmd := newTestCommand()
|
||||||
|
|||||||
@@ -26,9 +26,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// AttachRuntimeSchema records optional implementation-side identity evidence
|
// AttachRuntimeSchema records optional implementation-side identity evidence
|
||||||
// on a runnable command. Command discovery belongs exclusively to the reviewed
|
// on a runnable command. Stable command identity is collected from
|
||||||
// CommandRegistry; the Cobra binder accepts an absent annotation and rejects
|
// ContractFinal.Identity on the live Cobra leaves (EffectiveCommandRegistry);
|
||||||
// an annotation that disagrees with the registry.
|
// the binder accepts an absent annotation and rejects an annotation that
|
||||||
|
// disagrees with the collected identity.
|
||||||
func AttachRuntimeSchema(cmd *cobra.Command, productID, toolName, source string) {
|
func AttachRuntimeSchema(cmd *cobra.Command, productID, toolName, source string) {
|
||||||
if cmd == nil {
|
if cmd == nil {
|
||||||
return
|
return
|
||||||
@@ -48,26 +49,6 @@ func AttachRuntimeSchema(cmd *cobra.Command, productID, toolName, source string)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AnnotateRuntimeToolMetadata preserves MCP-provided tool metadata on a Cobra
|
|
||||||
// leaf so `dws schema` can render richer descriptions without refetching.
|
|
||||||
func AnnotateRuntimeToolMetadata(cmd *cobra.Command, title, description, source string) {
|
|
||||||
if cmd == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if cmd.Annotations == nil {
|
|
||||||
cmd.Annotations = map[string]string{}
|
|
||||||
}
|
|
||||||
if title = strings.TrimSpace(title); title != "" {
|
|
||||||
cmd.Annotations[AnnotationTitle] = title
|
|
||||||
}
|
|
||||||
if description = strings.TrimSpace(description); description != "" {
|
|
||||||
cmd.Annotations[AnnotationDescription] = description
|
|
||||||
}
|
|
||||||
if source = strings.TrimSpace(source); source != "" {
|
|
||||||
cmd.Annotations[AnnotationMetaSource] = source
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AnnotateRuntimeFlag adds parameter metadata to an already-registered flag.
|
// AnnotateRuntimeFlag adds parameter metadata to an already-registered flag.
|
||||||
func AnnotateRuntimeFlag(cmd *cobra.Command, flagName, propertyName, paramType string, required bool, _ string) {
|
func AnnotateRuntimeFlag(cmd *cobra.Command, flagName, propertyName, paramType string, required bool, _ string) {
|
||||||
if cmd == nil {
|
if cmd == nil {
|
||||||
@@ -196,45 +177,10 @@ func AnnotateRuntimeContract(cmd *cobra.Command) {
|
|||||||
SetCommandAnnotation(cmd, AnnotationContract, "command")
|
SetCommandAnnotation(cmd, AnnotationContract, "command")
|
||||||
}
|
}
|
||||||
|
|
||||||
// AnnotateRuntimeRisk records the Contract Risk string for Schema Safety
|
|
||||||
// projection. Empty risk is a no-op so write-guard leaves that leave Risk
|
|
||||||
// unset keep Contract.Safety / runtime_gate provenance (schema_hints/ is
|
|
||||||
// retired and must not be treated as a Safety fallback).
|
|
||||||
//
|
|
||||||
// Freeze: do not add new production call sites; declare Contract SafetySpec
|
|
||||||
// instead. Existing annotate sites may remain until migrated.
|
|
||||||
func AnnotateRuntimeRisk(cmd *cobra.Command, risk string) {
|
|
||||||
if cmd == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
risk = strings.TrimSpace(risk)
|
|
||||||
if risk == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
AnnotateRuntimeContract(cmd)
|
|
||||||
SetCommandAnnotation(cmd, AnnotationRisk, risk)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AnnotateRuntimeGate records a non-Risk confirmation path (e.g.
|
|
||||||
// devAppRequireWriteGuard). Homology rule: every confirmation fact is either
|
|
||||||
// Contract-declared (SafetySpec / Risk) or explicitly annotated (this gate).
|
|
||||||
// schema_hints/ is retired and must not supply confirmation.
|
|
||||||
//
|
|
||||||
// Freeze: do not add new production call sites; declare Contract SafetySpec
|
|
||||||
// instead. Existing annotate sites may remain until migrated.
|
|
||||||
func AnnotateRuntimeGate(cmd *cobra.Command, gate string) {
|
|
||||||
if cmd == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
gate = strings.TrimSpace(gate)
|
|
||||||
if gate == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
AnnotateRuntimeContract(cmd)
|
|
||||||
SetCommandAnnotation(cmd, AnnotationRuntimeGate, gate)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RuntimeContractRisk returns the Contract Risk annotation when present.
|
// RuntimeContractRisk returns the Contract Risk annotation when present.
|
||||||
|
// Writers are retired: production leaves declare contract.SafetySpec through
|
||||||
|
// ContractFinal, and assembly consults this residual annotation only as the
|
||||||
|
// fallback when a leaf carries no ContractFinal Safety.
|
||||||
func RuntimeContractRisk(cmd *cobra.Command) (string, bool) {
|
func RuntimeContractRisk(cmd *cobra.Command) (string, bool) {
|
||||||
if cmd == nil || cmd.Annotations == nil {
|
if cmd == nil || cmd.Annotations == nil {
|
||||||
return "", false
|
return "", false
|
||||||
@@ -246,7 +192,9 @@ func RuntimeContractRisk(cmd *cobra.Command) (string, bool) {
|
|||||||
return risk, true
|
return risk, true
|
||||||
}
|
}
|
||||||
|
|
||||||
// RuntimeContractGate returns the annotated runtime confirmation gate when present.
|
// RuntimeContractGate returns the annotated runtime confirmation gate when
|
||||||
|
// present. Like RuntimeContractRisk it is read-only residual plumbing: the
|
||||||
|
// writer is retired and declared Contract SafetySpec is the production path.
|
||||||
func RuntimeContractGate(cmd *cobra.Command) (string, bool) {
|
func RuntimeContractGate(cmd *cobra.Command) (string, bool) {
|
||||||
if cmd == nil || cmd.Annotations == nil {
|
if cmd == nil || cmd.Annotations == nil {
|
||||||
return "", false
|
return "", false
|
||||||
@@ -301,12 +249,6 @@ func CommandPositionals(cmd *cobra.Command) []contract.RuntimeSchemaPositional {
|
|||||||
return positionals
|
return positionals
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExcludeFromRuntimeSchema keeps a human-facing hint or redirect in --help
|
|
||||||
// while preventing it from being advertised as an executable agent tool.
|
|
||||||
func ExcludeFromRuntimeSchema(cmd *cobra.Command) {
|
|
||||||
SetCommandAnnotation(cmd, AnnotationExclude, "true")
|
|
||||||
}
|
|
||||||
|
|
||||||
// CommandFlag resolves local flags plus product/group persistent flags.
|
// CommandFlag resolves local flags plus product/group persistent flags.
|
||||||
// Root persistent flags are intentionally available only when explicitly
|
// Root persistent flags are intentionally available only when explicitly
|
||||||
// requested; they are global execution controls, not tool parameters.
|
// requested; they are global execution controls, not tool parameters.
|
||||||
|
|||||||
@@ -1,85 +0,0 @@
|
|||||||
// Copyright 2026 Alibaba Group
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
|
|
||||||
package runtimeannotate_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Production must not grow new AnnotateRuntimeRisk / AnnotateRuntimeGate call
|
|
||||||
// sites. Prefer declared Contract SafetySpec. Existing definitions and the
|
|
||||||
// cli seam re-export remain; tests may still exercise the helpers.
|
|
||||||
func TestAnnotateRuntimeRiskAndGateHaveNoNewProductionCallSites(t *testing.T) {
|
|
||||||
root := findModuleRoot(t)
|
|
||||||
callPattern := regexp.MustCompile(`\b(?:runtimeannotate\.)?AnnotateRuntime(?:Risk|Gate)\s*\(`)
|
|
||||||
allowed := map[string]bool{
|
|
||||||
filepath.Join("internal", "corecmd", "runtimeannotate", "annotate.go"): true,
|
|
||||||
filepath.Join("internal", "cli", "runtime_schema_seam.go"): true,
|
|
||||||
}
|
|
||||||
var problems []string
|
|
||||||
for _, dir := range []string{
|
|
||||||
filepath.Join(root, "internal"),
|
|
||||||
filepath.Join(root, "cmd"),
|
|
||||||
filepath.Join(root, "pkg"),
|
|
||||||
} {
|
|
||||||
err := filepath.WalkDir(dir, func(path string, d os.DirEntry, err error) error {
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if d.IsDir() {
|
|
||||||
base := d.Name()
|
|
||||||
if base == "testdata" || base == "vendor" {
|
|
||||||
return filepath.SkipDir
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if !strings.HasSuffix(path, ".go") || strings.HasSuffix(path, "_test.go") {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
rel, err := filepath.Rel(root, path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if allowed[rel] {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
data, err := os.ReadFile(path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, match := range callPattern.FindAllString(string(data), -1) {
|
|
||||||
problems = append(problems, rel+": "+match)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("walk %s: %v", dir, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(problems) != 0 {
|
|
||||||
t.Fatalf("new production AnnotateRuntimeRisk/Gate call sites are forbidden; migrate to Contract SafetySpec:\n - %s", strings.Join(problems, "\n - "))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func findModuleRoot(t *testing.T) string {
|
|
||||||
t.Helper()
|
|
||||||
dir, err := os.Getwd()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil {
|
|
||||||
return dir
|
|
||||||
}
|
|
||||||
parent := filepath.Dir(dir)
|
|
||||||
if parent == dir {
|
|
||||||
t.Fatal("go.mod not found")
|
|
||||||
}
|
|
||||||
dir = parent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -19,9 +19,7 @@ const (
|
|||||||
AnnotationProduct = "dws.schema.product"
|
AnnotationProduct = "dws.schema.product"
|
||||||
AnnotationTool = "dws.schema.tool"
|
AnnotationTool = "dws.schema.tool"
|
||||||
AnnotationSource = "dws.schema.source"
|
AnnotationSource = "dws.schema.source"
|
||||||
AnnotationTitle = "dws.schema.title"
|
|
||||||
AnnotationDescription = "dws.schema.description"
|
AnnotationDescription = "dws.schema.description"
|
||||||
AnnotationMetaSource = "dws.schema.metadata_source"
|
|
||||||
AnnotationExclude = "dws.schema.exclude"
|
AnnotationExclude = "dws.schema.exclude"
|
||||||
AnnotationConstraints = "dws.schema.constraints"
|
AnnotationConstraints = "dws.schema.constraints"
|
||||||
AnnotationPositionals = "dws.schema.positionals"
|
AnnotationPositionals = "dws.schema.positionals"
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import (
|
|||||||
func TestCrossPlatformCoverageAnnotateRuntimeAPIs(t *testing.T) {
|
func TestCrossPlatformCoverageAnnotateRuntimeAPIs(t *testing.T) {
|
||||||
AttachRuntimeSchema(nil, "p", "t", "s")
|
AttachRuntimeSchema(nil, "p", "t", "s")
|
||||||
AttachRuntimeSchema(&cobra.Command{}, "", "", "")
|
AttachRuntimeSchema(&cobra.Command{}, "", "", "")
|
||||||
AnnotateRuntimeToolMetadata(nil, "", "", "")
|
|
||||||
AnnotateRuntimeFlag(nil, "x", "x", "string", false, "")
|
AnnotateRuntimeFlag(nil, "x", "x", "string", false, "")
|
||||||
AnnotateRuntimeFlagProperty(nil, "x", "x")
|
AnnotateRuntimeFlagProperty(nil, "x", "x")
|
||||||
AnnotateRuntimeRequiredFlags(nil, "x")
|
AnnotateRuntimeRequiredFlags(nil, "x")
|
||||||
@@ -28,7 +27,6 @@ func TestCrossPlatformCoverageAnnotateRuntimeAPIs(t *testing.T) {
|
|||||||
AnnotateRuntimeContract(nil)
|
AnnotateRuntimeContract(nil)
|
||||||
AnnotateRuntimeConstraints(nil, RuntimeSchemaConstraints{})
|
AnnotateRuntimeConstraints(nil, RuntimeSchemaConstraints{})
|
||||||
AnnotateRuntimePositionals(nil)
|
AnnotateRuntimePositionals(nil)
|
||||||
ExcludeFromRuntimeSchema(nil)
|
|
||||||
_ = CommandFlag(nil, "x")
|
_ = CommandFlag(nil, "x")
|
||||||
_ = CommandPositionals(nil)
|
_ = CommandPositionals(nil)
|
||||||
_ = CommandConstraints(nil)
|
_ = CommandConstraints(nil)
|
||||||
@@ -42,30 +40,10 @@ func TestCrossPlatformCoverageAnnotateRuntimeAPIs(t *testing.T) {
|
|||||||
parent.AddCommand(cmd)
|
parent.AddCommand(cmd)
|
||||||
cmd.Flags().String("value", "", "value")
|
cmd.Flags().String("value", "", "value")
|
||||||
|
|
||||||
freshMeta := &cobra.Command{Use: "meta"}
|
|
||||||
AnnotateRuntimeToolMetadata(freshMeta, " ", " ", " ")
|
|
||||||
AnnotateRuntimeToolMetadata(freshMeta, " Title ", " Description ", " source ")
|
|
||||||
if freshMeta.Annotations[AnnotationTitle] != "Title" || freshMeta.Annotations[AnnotationMetaSource] != "source" {
|
|
||||||
t.Fatalf("tool metadata = %#v", freshMeta.Annotations)
|
|
||||||
}
|
|
||||||
|
|
||||||
AttachRuntimeSchema(cmd, " product ", " tool ", " source ")
|
AttachRuntimeSchema(cmd, " product ", " tool ", " source ")
|
||||||
if cmd.Annotations[AnnotationProduct] != "product" || cmd.Annotations[AnnotationTool] != "tool" {
|
if cmd.Annotations[AnnotationProduct] != "product" || cmd.Annotations[AnnotationTool] != "tool" {
|
||||||
t.Fatalf("AttachRuntimeSchema annotations = %#v", cmd.Annotations)
|
t.Fatalf("AttachRuntimeSchema annotations = %#v", cmd.Annotations)
|
||||||
}
|
}
|
||||||
AnnotateRuntimeToolMetadata(cmd, " Title2 ", " Description2 ", " source2 ")
|
|
||||||
if cmd.Annotations[AnnotationTitle] != "Title2" {
|
|
||||||
t.Fatalf("tool metadata overwrite = %#v", cmd.Annotations)
|
|
||||||
}
|
|
||||||
if _, ok := RuntimeContractRisk(nil); ok {
|
|
||||||
t.Fatal("RuntimeContractRisk(nil) must miss")
|
|
||||||
}
|
|
||||||
if _, ok := RuntimeContractRisk(&cobra.Command{}); ok {
|
|
||||||
t.Fatal("RuntimeContractRisk empty annotations must miss")
|
|
||||||
}
|
|
||||||
if _, ok := RuntimeContractRisk(&cobra.Command{Annotations: map[string]string{AnnotationRisk: " "}}); ok {
|
|
||||||
t.Fatal("RuntimeContractRisk blank risk must miss")
|
|
||||||
}
|
|
||||||
|
|
||||||
AnnotateRuntimeFlag(cmd, "", "property", "string", true, "")
|
AnnotateRuntimeFlag(cmd, "", "property", "string", true, "")
|
||||||
AnnotateRuntimeFlag(cmd, "missing", "property", "string", true, "")
|
AnnotateRuntimeFlag(cmd, "missing", "property", "string", true, "")
|
||||||
@@ -154,11 +132,6 @@ func TestCrossPlatformCoverageAnnotateRuntimeAPIs(t *testing.T) {
|
|||||||
AnnotateRuntimePositionals(cmd) // empty after filters is no-op path via clean empty? name empty skipped
|
AnnotateRuntimePositionals(cmd) // empty after filters is no-op path via clean empty? name empty skipped
|
||||||
AnnotateRuntimePositionals(&cobra.Command{Use: "empty"})
|
AnnotateRuntimePositionals(&cobra.Command{Use: "empty"})
|
||||||
|
|
||||||
ExcludeFromRuntimeSchema(cmd)
|
|
||||||
if cmd.Annotations[AnnotationExclude] != "true" {
|
|
||||||
t.Fatalf("exclude annotation = %#v", cmd.Annotations)
|
|
||||||
}
|
|
||||||
|
|
||||||
SetCommandAnnotation(cmd, "k", " ")
|
SetCommandAnnotation(cmd, "k", " ")
|
||||||
SetCommandAnnotation(cmd, "k", "v")
|
SetCommandAnnotation(cmd, "k", "v")
|
||||||
flag := cmd.Flags().Lookup("value")
|
flag := cmd.Flags().Lookup("value")
|
||||||
|
|||||||
@@ -19,49 +19,41 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCrossPlatformCoverageAnnotateRuntimeRiskEmbedsContractMarker(t *testing.T) {
|
// The Risk/Gate writers are retired; assembly still reads the residual
|
||||||
cmd := &cobra.Command{Use: "x"}
|
// annotations through the accessors as a ContractFinal-less fallback, so the
|
||||||
AnnotateRuntimeRisk(cmd, "write")
|
// accessors keep coverage with directly constructed annotation maps.
|
||||||
|
|
||||||
|
func TestCrossPlatformCoverageRuntimeContractRiskAccessor(t *testing.T) {
|
||||||
|
cmd := &cobra.Command{Use: "x", Annotations: map[string]string{
|
||||||
|
AnnotationRisk: "write",
|
||||||
|
}}
|
||||||
got, ok := RuntimeContractRisk(cmd)
|
got, ok := RuntimeContractRisk(cmd)
|
||||||
if !ok || got != "write" {
|
if !ok || got != "write" {
|
||||||
t.Fatalf("RuntimeContractRisk = %q %v", got, ok)
|
t.Fatalf("RuntimeContractRisk = %q %v", got, ok)
|
||||||
}
|
}
|
||||||
if cmd.Annotations[AnnotationContract] != "command" {
|
if _, ok := RuntimeContractRisk(nil); ok {
|
||||||
t.Fatalf("contract marker = %q", cmd.Annotations[AnnotationContract])
|
t.Fatal("RuntimeContractRisk(nil) must miss")
|
||||||
}
|
}
|
||||||
AnnotateRuntimeRisk(cmd, "")
|
if _, ok := RuntimeContractRisk(&cobra.Command{}); ok {
|
||||||
if got, _ = RuntimeContractRisk(cmd); got != "write" {
|
t.Fatal("RuntimeContractRisk empty annotations must miss")
|
||||||
t.Fatalf("empty AnnotateRuntimeRisk must be no-op, got %q", got)
|
}
|
||||||
|
blank := &cobra.Command{Use: "y", Annotations: map[string]string{AnnotationRisk: " "}}
|
||||||
|
if _, ok := RuntimeContractRisk(blank); ok {
|
||||||
|
t.Fatal("blank risk annotation must not report a risk")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCrossPlatformCoverageAnnotateRuntimeGate(t *testing.T) {
|
func TestCrossPlatformCoverageRuntimeContractGateAccessor(t *testing.T) {
|
||||||
cmd := &cobra.Command{Use: "x"}
|
cmd := &cobra.Command{Use: "x", Annotations: map[string]string{
|
||||||
AnnotateRuntimeGate(cmd, "devAppRequireWriteGuard")
|
AnnotationRuntimeGate: "devAppRequireWriteGuard",
|
||||||
|
}}
|
||||||
got, ok := RuntimeContractGate(cmd)
|
got, ok := RuntimeContractGate(cmd)
|
||||||
if !ok || got != "devAppRequireWriteGuard" {
|
if !ok || got != "devAppRequireWriteGuard" {
|
||||||
t.Fatalf("RuntimeContractGate = %q %v", got, ok)
|
t.Fatalf("RuntimeContractGate = %q %v", got, ok)
|
||||||
}
|
}
|
||||||
if cmd.Annotations[AnnotationContract] != "command" {
|
if _, ok := RuntimeContractGate(nil); ok {
|
||||||
t.Fatalf("contract marker = %q", cmd.Annotations[AnnotationContract])
|
t.Fatal("RuntimeContractGate(nil) must miss")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func TestCrossPlatformCoverageRuntimeContractAnnotationNilAndBlankGuards(t *testing.T) {
|
|
||||||
AnnotateRuntimeFlagDescription(nil, "flag", "description")
|
|
||||||
AnnotateRuntimeContract(nil)
|
|
||||||
AnnotateRuntimeRisk(nil, "write")
|
|
||||||
AnnotateRuntimeGate(nil, "devAppRequireWriteGuard")
|
|
||||||
|
|
||||||
cmd := &cobra.Command{Use: "x"}
|
|
||||||
AnnotateRuntimeGate(cmd, " ")
|
|
||||||
if _, ok := RuntimeContractGate(cmd); ok {
|
|
||||||
t.Fatal("blank AnnotateRuntimeGate must not record a gate")
|
|
||||||
}
|
|
||||||
if cmd.Annotations != nil && cmd.Annotations[AnnotationContract] == "command" {
|
|
||||||
t.Fatal("blank AnnotateRuntimeGate must not mark the command as Contract")
|
|
||||||
}
|
|
||||||
|
|
||||||
blank := &cobra.Command{Use: "y", Annotations: map[string]string{
|
blank := &cobra.Command{Use: "y", Annotations: map[string]string{
|
||||||
AnnotationRuntimeGate: " ",
|
AnnotationRuntimeGate: " ",
|
||||||
}}
|
}}
|
||||||
@@ -69,3 +61,8 @@ func TestCrossPlatformCoverageRuntimeContractAnnotationNilAndBlankGuards(t *test
|
|||||||
t.Fatal("blank runtime_gate annotation must not report a gate")
|
t.Fatal("blank runtime_gate annotation must not report a gate")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCrossPlatformCoverageRuntimeContractAnnotationNilGuards(t *testing.T) {
|
||||||
|
AnnotateRuntimeFlagDescription(nil, "flag", "description")
|
||||||
|
AnnotateRuntimeContract(nil)
|
||||||
|
}
|
||||||
Reference in new issue
Block a user