← back to Cli Printing Press
refactor(cli): simplify refactor-safe helpers (#311)
fd2ea4f4067a2f29e5fc07ddd28dcc5e1d12b77c · 2026-04-26 13:48:18 -0700 · Trevin Chow
* refactor(cli): share discovery naming helpers
Extract the duplicated endpoint/resource naming helpers used by browser-sniff
and crowd-sniff into a shared discovery package. The golden harness now proves
browser-sniff output and generated CLI artifacts remain byte-identical after
this simplification.
* refactor(cli): share generate polish pass
* refactor(cli): share dogfood config field collection
* refactor(cli): share patch execute insertions
Files touched
M internal/browsersniff/analysis.goM internal/browsersniff/fixtures.goM internal/browsersniff/specgen.goM internal/cli/root.goM internal/crowdsniff/specgen.goM internal/crowdsniff/specgen_test.goA internal/discovery/naming.goM internal/patch/ast_inject.goM internal/pipeline/dogfood.go
Diff
commit fd2ea4f4067a2f29e5fc07ddd28dcc5e1d12b77c
Author: Trevin Chow <trevin@trevinchow.com>
Date: Sun Apr 26 13:48:18 2026 -0700
refactor(cli): simplify refactor-safe helpers (#311)
* refactor(cli): share discovery naming helpers
Extract the duplicated endpoint/resource naming helpers used by browser-sniff
and crowd-sniff into a shared discovery package. The golden harness now proves
browser-sniff output and generated CLI artifacts remain byte-identical after
this simplification.
* refactor(cli): share generate polish pass
* refactor(cli): share dogfood config field collection
* refactor(cli): share patch execute insertions
---
internal/browsersniff/analysis.go | 5 +-
internal/browsersniff/fixtures.go | 4 +-
internal/browsersniff/specgen.go | 90 +++------------------------
internal/cli/root.go | 60 ++++++++----------
internal/crowdsniff/specgen.go | 84 ++-----------------------
internal/crowdsniff/specgen_test.go | 9 +--
internal/discovery/naming.go | 85 ++++++++++++++++++++++++++
internal/patch/ast_inject.go | 119 +++++++++++++-----------------------
internal/pipeline/dogfood.go | 64 ++++++++-----------
9 files changed, 202 insertions(+), 318 deletions(-)
diff --git a/internal/browsersniff/analysis.go b/internal/browsersniff/analysis.go
index 93da029b..84373e81 100644
--- a/internal/browsersniff/analysis.go
+++ b/internal/browsersniff/analysis.go
@@ -10,6 +10,7 @@ import (
"strings"
"time"
+ "github.com/mvanhorn/cli-printing-press/v2/internal/discovery"
"github.com/mvanhorn/cli-printing-press/v2/internal/spec"
)
@@ -873,7 +874,7 @@ func suggestCandidateCommands(clusters []EndpointCluster) []CandidateCommand {
if resource == "" {
continue
}
- name := deriveEndpointName(cluster.Method, cluster.Path)
+ name := discovery.EndpointName(cluster.Method, cluster.Path)
if seen[name] {
continue
}
@@ -1316,7 +1317,7 @@ func jsonFieldNames(body string) []string {
}
func commandResource(path string) string {
- segments := significantSegments(path)
+ segments := discovery.SignificantSegments(path)
if len(segments) == 0 {
return ""
}
diff --git a/internal/browsersniff/fixtures.go b/internal/browsersniff/fixtures.go
index 44b00426..69d13aa5 100644
--- a/internal/browsersniff/fixtures.go
+++ b/internal/browsersniff/fixtures.go
@@ -5,6 +5,8 @@ import (
"net/url"
"sort"
"strings"
+
+ "github.com/mvanhorn/cli-printing-press/v2/internal/discovery"
)
type TestFixture struct {
@@ -52,7 +54,7 @@ func GenerateFixtures(capture *EnrichedCapture) *FixtureSet {
}
fixture := SanitizeForFixture(group.Entries[0])
- fixture.EndpointName = deriveEndpointName(group.Method, group.NormalizedPath)
+ fixture.EndpointName = discovery.EndpointName(group.Method, group.NormalizedPath)
paramNames := make(map[string]struct{})
bodyFields := make(map[string]struct{})
diff --git a/internal/browsersniff/specgen.go b/internal/browsersniff/specgen.go
index d99e8183..cd5564fa 100644
--- a/internal/browsersniff/specgen.go
+++ b/internal/browsersniff/specgen.go
@@ -10,6 +10,7 @@ import (
"strings"
"unicode"
+ "github.com/mvanhorn/cli-printing-press/v2/internal/discovery"
"github.com/mvanhorn/cli-printing-press/v2/internal/spec"
"gopkg.in/yaml.v3"
)
@@ -60,7 +61,7 @@ func AnalyzeCapture(capture *EnrichedCapture) (*spec.APISpec, error) {
groups := DeduplicateEndpoints(regularEntries)
for _, group := range groups {
endpoint := buildEndpoint(group)
- resourceKey, resourceName := deriveResourceKey(group.NormalizedPath)
+ resourceKey, resourceName := discovery.ResourceKey(group.NormalizedPath)
if resourceKey == "" {
resourceKey = "default"
resourceName = "default"
@@ -74,9 +75,9 @@ func AnalyzeCapture(capture *EnrichedCapture) (*spec.APISpec, error) {
resource.Endpoints = make(map[string]spec.Endpoint)
}
- name := deriveEndpointName(group.Method, group.NormalizedPath)
+ name := discovery.EndpointName(group.Method, group.NormalizedPath)
if _, exists := resource.Endpoints[name]; exists {
- name = uniqueEndpointName(resource.Endpoints, name)
+ name = discovery.UniqueEndpointName(resource.Endpoints, name)
}
resource.Endpoints[name] = endpoint
resources[resourceKey] = resource
@@ -214,10 +215,10 @@ func addGraphQLBFFResources(resources map[string]spec.Resource, ops []graphQLOpe
name = safeGraphQLOperationName(op.OperationName)
}
if name == "" {
- name = deriveEndpointName(op.Method, op.Path)
+ name = discovery.EndpointName(op.Method, op.Path)
}
if _, exists := resource.Endpoints[name]; exists {
- name = uniqueEndpointName(resource.Endpoints, name)
+ name = discovery.UniqueEndpointName(resource.Endpoints, name)
}
resource.Endpoints[name] = endpoint
resources[resourceName] = resource
@@ -1095,83 +1096,6 @@ func normalizeBaseURL(raw string) string {
return parsed.Scheme + "://" + parsed.Host
}
-func deriveResourceKey(path string) (string, string) {
- segments := significantSegments(path)
- if len(segments) == 0 {
- return "", ""
- }
-
- // Use only the first significant segment as the resource key.
- // This prevents slashes in resource names which break the generator's
- // filepath.Join and Cobra Use field.
- return segments[0], segments[len(segments)-1]
-}
-
-func significantSegments(path string) []string {
- parts := strings.Split(path, "/")
- segments := make([]string, 0, len(parts))
- for _, segment := range parts {
- segment = strings.TrimSpace(segment)
- if segment == "" {
- continue
- }
- if strings.HasPrefix(segment, "{") && strings.HasSuffix(segment, "}") {
- continue
- }
- if segment == "api" || isVersionSegment(segment) {
- continue
- }
- segments = append(segments, segment)
- }
-
- return segments
-}
-
-func isVersionSegment(segment string) bool {
- if len(segment) < 2 || segment[0] != 'v' {
- return false
- }
- for _, r := range segment[1:] {
- if r < '0' || r > '9' {
- return false
- }
- }
- return true
-}
-
-func deriveEndpointName(method string, normalizedPath string) string {
- resource := "endpoint"
- segments := significantSegments(normalizedPath)
- if len(segments) > 0 {
- resource = strings.ReplaceAll(segments[len(segments)-1], "-", "_")
- }
-
- switch strings.ToUpper(method) {
- case "GET":
- if strings.Contains(normalizedPath, "{") {
- return "get_" + resource
- }
- return "list_" + resource
- case "POST":
- return "create_" + resource
- case "PUT", "PATCH":
- return "update_" + resource
- case "DELETE":
- return "delete_" + resource
- default:
- return strings.ToLower(method) + "_" + resource
- }
-}
-
-func uniqueEndpointName(endpoints map[string]spec.Endpoint, base string) string {
- for i := 2; ; i++ {
- name := fmt.Sprintf("%s_%d", base, i)
- if _, exists := endpoints[name]; !exists {
- return name
- }
- }
-}
-
func inferResponseType(bodies []string) string {
for _, body := range bodies {
body = strings.TrimSpace(body)
@@ -1196,7 +1120,7 @@ func inferResponseType(bodies []string) string {
}
func deriveResponseItemName(path string) string {
- segments := significantSegments(path)
+ segments := discovery.SignificantSegments(path)
if len(segments) == 0 {
return "response"
}
diff --git a/internal/cli/root.go b/internal/cli/root.go
index 062d949a..4b202b54 100644
--- a/internal/cli/root.go
+++ b/internal/cli/root.go
@@ -193,23 +193,7 @@ func newGenerateCmd() *cobra.Command {
}
}
- polished := false
- if polish {
- fmt.Fprintln(os.Stderr, "Running LLM polish pass...")
- polishResult, polishErr := llmpolish.Polish(llmpolish.PolishRequest{
- APIName: parsed.Name,
- OutputDir: absOut,
- })
- if polishErr != nil {
- fmt.Fprintf(os.Stderr, "warning: polish failed: %v\n", polishErr)
- } else if polishResult.Skipped {
- fmt.Fprintf(os.Stderr, "polish skipped: %s\n", polishResult.SkipReason)
- } else {
- polished = true
- fmt.Fprintf(os.Stderr, "Polish: %d help texts improved, %d examples added, README %v\n",
- polishResult.HelpTextsImproved, polishResult.ExamplesAdded, polishResult.READMERewritten)
- }
- }
+ polished := runGeneratePolishPass(polish, parsed.Name, absOut)
if err := pipeline.WriteManifestForGenerate(pipeline.GenerateManifestParams{
APIName: parsed.Name,
@@ -405,23 +389,7 @@ func newGenerateCmd() *cobra.Command {
}
}
- polished := false
- if polish {
- fmt.Fprintln(os.Stderr, "Running LLM polish pass...")
- polishResult, polishErr := llmpolish.Polish(llmpolish.PolishRequest{
- APIName: apiSpec.Name,
- OutputDir: absOut,
- })
- if polishErr != nil {
- fmt.Fprintf(os.Stderr, "warning: polish failed: %v\n", polishErr)
- } else if polishResult.Skipped {
- fmt.Fprintf(os.Stderr, "polish skipped: %s\n", polishResult.SkipReason)
- } else {
- polished = true
- fmt.Fprintf(os.Stderr, "Polish: %d help texts improved, %d examples added, README %v\n",
- polishResult.HelpTextsImproved, polishResult.ExamplesAdded, polishResult.READMERewritten)
- }
- }
+ polished := runGeneratePolishPass(polish, apiSpec.Name, absOut)
// When --output was not explicitly supplied, normalize the output
// directory to the spec-derived name so default-path runs land in the
@@ -504,6 +472,30 @@ func newGenerateCmd() *cobra.Command {
return cmd
}
+func runGeneratePolishPass(enabled bool, apiName, outputDir string) bool {
+ if !enabled {
+ return false
+ }
+
+ fmt.Fprintln(os.Stderr, "Running LLM polish pass...")
+ polishResult, polishErr := llmpolish.Polish(llmpolish.PolishRequest{
+ APIName: apiName,
+ OutputDir: outputDir,
+ })
+ if polishErr != nil {
+ fmt.Fprintf(os.Stderr, "warning: polish failed: %v\n", polishErr)
+ return false
+ }
+ if polishResult.Skipped {
+ fmt.Fprintf(os.Stderr, "polish skipped: %s\n", polishResult.SkipReason)
+ return false
+ }
+
+ fmt.Fprintf(os.Stderr, "Polish: %d help texts improved, %d examples added, README %v\n",
+ polishResult.HelpTextsImproved, polishResult.ExamplesAdded, polishResult.READMERewritten)
+ return true
+}
+
func normalizeSpecSource(value string) (string, error) {
switch value {
case "", "official", "community", "sniffed", "docs":
diff --git a/internal/crowdsniff/specgen.go b/internal/crowdsniff/specgen.go
index 78db97b3..e01478d3 100644
--- a/internal/crowdsniff/specgen.go
+++ b/internal/crowdsniff/specgen.go
@@ -5,6 +5,7 @@ import (
"strconv"
"strings"
+ "github.com/mvanhorn/cli-printing-press/v2/internal/discovery"
"github.com/mvanhorn/cli-printing-press/v2/internal/spec"
)
@@ -54,7 +55,7 @@ func BuildSpec(name, baseURL string, endpoints []AggregatedEndpoint, auth *Disco
endpoint.Params = specParams
}
- resourceKey, resourceName := deriveResourceKey(ep.Path)
+ resourceKey, resourceName := discovery.ResourceKey(ep.Path)
if resourceKey == "" {
resourceKey = "default"
resourceName = "default"
@@ -68,9 +69,9 @@ func BuildSpec(name, baseURL string, endpoints []AggregatedEndpoint, auth *Disco
resource.Endpoints = make(map[string]spec.Endpoint)
}
- endpointName := deriveEndpointName(ep.Method, ep.Path)
+ endpointName := discovery.EndpointName(ep.Method, ep.Path)
if _, exists := resource.Endpoints[endpointName]; exists {
- endpointName = uniqueEndpointName(resource.Endpoints, endpointName)
+ endpointName = discovery.UniqueEndpointName(resource.Endpoints, endpointName)
}
resource.Endpoints[endpointName] = endpoint
resources[resourceKey] = resource
@@ -116,83 +117,6 @@ func ResolveBaseURL(explicit string, candidates []string) string {
return ""
}
-// --- Helpers adapted from browsersniff/specgen.go ---
-
-func deriveResourceKey(path string) (string, string) {
- segments := significantSegments(path)
- if len(segments) == 0 {
- return "", ""
- }
- // Use only the first significant segment as the resource key.
- // This prevents slashes in resource names which break the generator's
- // filepath.Join and Cobra Use field.
- return segments[0], segments[len(segments)-1]
-}
-
-func significantSegments(path string) []string {
- parts := strings.Split(path, "/")
- segments := make([]string, 0, len(parts))
- for _, segment := range parts {
- segment = strings.TrimSpace(segment)
- if segment == "" {
- continue
- }
- if strings.HasPrefix(segment, "{") && strings.HasSuffix(segment, "}") {
- continue
- }
- if segment == "api" || isVersionSegment(segment) {
- continue
- }
- segments = append(segments, segment)
- }
- return segments
-}
-
-func isVersionSegment(segment string) bool {
- if len(segment) < 2 || segment[0] != 'v' {
- return false
- }
- for _, r := range segment[1:] {
- if r < '0' || r > '9' {
- return false
- }
- }
- return true
-}
-
-func deriveEndpointName(method string, normalizedPath string) string {
- resource := "endpoint"
- segments := significantSegments(normalizedPath)
- if len(segments) > 0 {
- resource = strings.ReplaceAll(segments[len(segments)-1], "-", "_")
- }
-
- switch strings.ToUpper(method) {
- case "GET":
- if strings.Contains(normalizedPath, "{") {
- return "get_" + resource
- }
- return "list_" + resource
- case "POST":
- return "create_" + resource
- case "PUT", "PATCH":
- return "update_" + resource
- case "DELETE":
- return "delete_" + resource
- default:
- return strings.ToLower(method) + "_" + resource
- }
-}
-
-func uniqueEndpointName(endpoints map[string]spec.Endpoint, base string) string {
- for i := 2; ; i++ {
- name := fmt.Sprintf("%s_%d", base, i)
- if _, exists := endpoints[name]; !exists {
- return name
- }
- }
-}
-
// buildAuthConfig converts a DiscoveredAuth into a spec.AuthConfig.
// It also derives an env var name from the API name if no hint was detected.
func buildAuthConfig(apiName string, auth *DiscoveredAuth) spec.AuthConfig {
diff --git a/internal/crowdsniff/specgen_test.go b/internal/crowdsniff/specgen_test.go
index 0d4540fc..74bed042 100644
--- a/internal/crowdsniff/specgen_test.go
+++ b/internal/crowdsniff/specgen_test.go
@@ -3,6 +3,7 @@ package crowdsniff
import (
"testing"
+ "github.com/mvanhorn/cli-printing-press/v2/internal/discovery"
"github.com/mvanhorn/cli-printing-press/v2/internal/spec"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -286,7 +287,7 @@ func TestDeriveResourceKey(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
- resource, name := deriveResourceKey(tt.path)
+ resource, name := discovery.ResourceKey(tt.path)
assert.Equal(t, tt.wantResource, resource)
assert.Equal(t, tt.wantName, name)
})
@@ -307,7 +308,7 @@ func TestDeriveResourceKey_NoSlashes(t *testing.T) {
}
for _, p := range paths {
- resource, _ := deriveResourceKey(p)
+ resource, _ := discovery.ResourceKey(p)
assert.NotContains(t, resource, "/", "resource key for %q must not contain slashes", p)
}
}
@@ -317,8 +318,8 @@ func TestDeriveResourceKey_SharedFirstSegment(t *testing.T) {
// Two paths sharing the same first significant segment must map to the
// same resource key so they end up in one file.
- res1, _ := deriveResourceKey("/v1/users")
- res2, _ := deriveResourceKey("/v1/users/{id}/posts")
+ res1, _ := discovery.ResourceKey("/v1/users")
+ res2, _ := discovery.ResourceKey("/v1/users/{id}/posts")
assert.Equal(t, res1, res2, "paths sharing first segment should have same resource key")
assert.Equal(t, "users", res1)
}
diff --git a/internal/discovery/naming.go b/internal/discovery/naming.go
new file mode 100644
index 00000000..118f95a5
--- /dev/null
+++ b/internal/discovery/naming.go
@@ -0,0 +1,85 @@
+package discovery
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/mvanhorn/cli-printing-press/v2/internal/spec"
+)
+
+func ResourceKey(path string) (string, string) {
+ segments := SignificantSegments(path)
+ if len(segments) == 0 {
+ return "", ""
+ }
+
+ // Use only the first significant segment as the resource key.
+ // This prevents slashes in resource names which break the generator's
+ // filepath.Join and Cobra Use field.
+ return segments[0], segments[len(segments)-1]
+}
+
+func SignificantSegments(path string) []string {
+ parts := strings.Split(path, "/")
+ segments := make([]string, 0, len(parts))
+ for _, segment := range parts {
+ segment = strings.TrimSpace(segment)
+ if segment == "" {
+ continue
+ }
+ if strings.HasPrefix(segment, "{") && strings.HasSuffix(segment, "}") {
+ continue
+ }
+ if segment == "api" || isVersionSegment(segment) {
+ continue
+ }
+ segments = append(segments, segment)
+ }
+
+ return segments
+}
+
+func isVersionSegment(segment string) bool {
+ if len(segment) < 2 || segment[0] != 'v' {
+ return false
+ }
+ for _, r := range segment[1:] {
+ if r < '0' || r > '9' {
+ return false
+ }
+ }
+ return true
+}
+
+func EndpointName(method string, normalizedPath string) string {
+ resource := "endpoint"
+ segments := SignificantSegments(normalizedPath)
+ if len(segments) > 0 {
+ resource = strings.ReplaceAll(segments[len(segments)-1], "-", "_")
+ }
+
+ switch strings.ToUpper(method) {
+ case "GET":
+ if strings.Contains(normalizedPath, "{") {
+ return "get_" + resource
+ }
+ return "list_" + resource
+ case "POST":
+ return "create_" + resource
+ case "PUT", "PATCH":
+ return "update_" + resource
+ case "DELETE":
+ return "delete_" + resource
+ default:
+ return strings.ToLower(method) + "_" + resource
+ }
+}
+
+func UniqueEndpointName(endpoints map[string]spec.Endpoint, base string) string {
+ for i := 2; ; i++ {
+ name := fmt.Sprintf("%s_%d", base, i)
+ if _, exists := endpoints[name]; !exists {
+ return name
+ }
+ }
+}
diff --git a/internal/patch/ast_inject.go b/internal/patch/ast_inject.go
index b89b6317..fda16f79 100644
--- a/internal/patch/ast_inject.go
+++ b/internal/patch/ast_inject.go
@@ -3,6 +3,7 @@ package patch
import (
"bytes"
"fmt"
+ "slices"
"strings"
"github.com/dave/dst"
@@ -150,55 +151,18 @@ func addImports(file *dst.File, pkgs ...string) bool {
// PersistentFlags() registration inside Execute(). Skipped features are
// omitted; if both profile and deliver are skipped, no flag is added.
func addPersistentFlags(file *dst.File, opts injectOptions) bool {
- changed := false
- dst.Inspect(file, func(n dst.Node) bool {
- fn, ok := n.(*dst.FuncDecl)
- if !ok || fn.Name.Name != "Execute" {
- return true
- }
- var newStmts []dst.Stmt
- if !opts.skip("profile") {
- // Idempotency: skip if --profile already registered.
- already := false
- for _, stmt := range fn.Body.List {
- if persistentFlagsRegisters(stmt, "profile") {
- already = true
- break
- }
- }
- if !already {
- newStmts = append(newStmts, parseStmt(`rootCmd.PersistentFlags().StringVar(&flags.profileName, "profile", "", "Apply values from a saved profile")`))
- }
- }
- if !opts.skip("deliver") {
- already := false
- for _, stmt := range fn.Body.List {
- if persistentFlagsRegisters(stmt, "deliver") {
- already = true
- break
- }
- }
- if !already {
- newStmts = append(newStmts, parseStmt(`rootCmd.PersistentFlags().StringVar(&flags.deliverSpec, "deliver", "", "Route output to a sink: stdout (default), file:<path>, webhook:<url>")`))
- }
- }
- if len(newStmts) == 0 {
- return false
- }
- lastFlagIdx := -1
- for i, stmt := range fn.Body.List {
- if isPersistentFlagsCall(stmt) {
- lastFlagIdx = i
- }
- }
- if lastFlagIdx < 0 {
- return false
- }
- fn.Body.List = append(fn.Body.List[:lastFlagIdx+1], append(newStmts, fn.Body.List[lastFlagIdx+1:]...)...)
- changed = true
- return false
- })
- return changed
+ return appendExecuteStatementsAfterLast(file, opts, []executeStatementInsertion{
+ {
+ feature: "profile",
+ stmt: `rootCmd.PersistentFlags().StringVar(&flags.profileName, "profile", "", "Apply values from a saved profile")`,
+ exists: func(stmt dst.Stmt) bool { return persistentFlagsRegisters(stmt, "profile") },
+ },
+ {
+ feature: "deliver",
+ stmt: `rootCmd.PersistentFlags().StringVar(&flags.deliverSpec, "deliver", "", "Route output to a sink: stdout (default), file:<path>, webhook:<url>")`,
+ exists: func(stmt dst.Stmt) bool { return persistentFlagsRegisters(stmt, "deliver") },
+ },
+ }, isPersistentFlagsCall)
}
// addPreRunBlocks inserts the deliver-setup and profile-lookup blocks at the
@@ -262,6 +226,27 @@ func addPreRunBlocks(file *dst.File, opts injectOptions) bool {
// addCommands appends newProfileCmd and newFeedbackCmd AddCommand calls after
// the last existing rootCmd.AddCommand entry. Skipped features are omitted.
func addCommands(file *dst.File, opts injectOptions) bool {
+ return appendExecuteStatementsAfterLast(file, opts, []executeStatementInsertion{
+ {
+ feature: "profile",
+ stmt: `rootCmd.AddCommand(newProfileCmd(&flags))`,
+ exists: func(stmt dst.Stmt) bool { return rootAddsCommand(stmt, "newProfileCmd") },
+ },
+ {
+ feature: "feedback",
+ stmt: `rootCmd.AddCommand(newFeedbackCmd(&flags))`,
+ exists: func(stmt dst.Stmt) bool { return rootAddsCommand(stmt, "newFeedbackCmd") },
+ },
+ }, isRootAddCommand)
+}
+
+type executeStatementInsertion struct {
+ feature string
+ stmt string
+ exists func(dst.Stmt) bool
+}
+
+func appendExecuteStatementsAfterLast(file *dst.File, opts injectOptions, insertions []executeStatementInsertion, anchor func(dst.Stmt) bool) bool {
changed := false
dst.Inspect(file, func(n dst.Node) bool {
fn, ok := n.(*dst.FuncDecl)
@@ -269,43 +254,27 @@ func addCommands(file *dst.File, opts injectOptions) bool {
return true
}
var newStmts []dst.Stmt
- if !opts.skip("profile") {
- already := false
- for _, stmt := range fn.Body.List {
- if rootAddsCommand(stmt, "newProfileCmd") {
- already = true
- break
- }
- }
- if !already {
- newStmts = append(newStmts, parseStmt(`rootCmd.AddCommand(newProfileCmd(&flags))`))
- }
- }
- if !opts.skip("feedback") {
- already := false
- for _, stmt := range fn.Body.List {
- if rootAddsCommand(stmt, "newFeedbackCmd") {
- already = true
- break
- }
+ for _, insertion := range insertions {
+ if opts.skip(insertion.feature) {
+ continue
}
- if !already {
- newStmts = append(newStmts, parseStmt(`rootCmd.AddCommand(newFeedbackCmd(&flags))`))
+ if !slices.ContainsFunc(fn.Body.List, insertion.exists) {
+ newStmts = append(newStmts, parseStmt(insertion.stmt))
}
}
if len(newStmts) == 0 {
return false
}
- lastAddIdx := -1
+ lastAnchorIdx := -1
for i, stmt := range fn.Body.List {
- if isRootAddCommand(stmt) {
- lastAddIdx = i
+ if anchor(stmt) {
+ lastAnchorIdx = i
}
}
- if lastAddIdx < 0 {
+ if lastAnchorIdx < 0 {
return false
}
- fn.Body.List = append(fn.Body.List[:lastAddIdx+1], append(newStmts, fn.Body.List[lastAddIdx+1:]...)...)
+ fn.Body.List = append(fn.Body.List[:lastAnchorIdx+1], append(newStmts, fn.Body.List[lastAnchorIdx+1:]...)...)
changed = true
return false
})
diff --git a/internal/pipeline/dogfood.go b/internal/pipeline/dogfood.go
index 36285e67..47cf4a55 100644
--- a/internal/pipeline/dogfood.go
+++ b/internal/pipeline/dogfood.go
@@ -1922,45 +1922,8 @@ func checkConfigConsistency(dir string) ConfigConsistResult {
}
content := string(data)
- for _, pat := range writePatterns {
- if pat.MatchString(content) {
- // Extract field names from the same lines
- matches := pat.FindAllStringSubmatch(content, -1)
- for _, m := range matches {
- if len(m) > 1 && m[1] != "" {
- writeFields[m[1]] = struct{}{}
- }
- }
- // Also extract nearby token-related string literals
- for line := range strings.SplitSeq(content, "\n") {
- if pat.MatchString(line) {
- fieldMatches := fieldExtractRe.FindAllStringSubmatch(line, -1)
- for _, fm := range fieldMatches {
- writeFields[fm[1]] = struct{}{}
- }
- }
- }
- }
- }
-
- for _, pat := range readPatterns {
- if pat.MatchString(content) {
- matches := pat.FindAllStringSubmatch(content, -1)
- for _, m := range matches {
- if len(m) > 1 && m[1] != "" {
- readFields[m[1]] = struct{}{}
- }
- }
- for line := range strings.SplitSeq(content, "\n") {
- if pat.MatchString(line) {
- fieldMatches := fieldExtractRe.FindAllStringSubmatch(line, -1)
- for _, fm := range fieldMatches {
- readFields[fm[1]] = struct{}{}
- }
- }
- }
- }
- }
+ collectConfigFields(content, writePatterns, fieldExtractRe, writeFields)
+ collectConfigFields(content, readPatterns, fieldExtractRe, readFields)
}
result.WriteFields = sortedKeys(writeFields)
@@ -1995,6 +1958,29 @@ func checkConfigConsistency(dir string) ConfigConsistResult {
return result
}
+func collectConfigFields(content string, patterns []*regexp.Regexp, fieldExtractRe *regexp.Regexp, fields map[string]struct{}) {
+ for _, pat := range patterns {
+ if !pat.MatchString(content) {
+ continue
+ }
+ matches := pat.FindAllStringSubmatch(content, -1)
+ for _, m := range matches {
+ if len(m) > 1 && m[1] != "" {
+ fields[m[1]] = struct{}{}
+ }
+ }
+ for line := range strings.SplitSeq(content, "\n") {
+ if !pat.MatchString(line) {
+ continue
+ }
+ fieldMatches := fieldExtractRe.FindAllStringSubmatch(line, -1)
+ for _, fm := range fieldMatches {
+ fields[fm[1]] = struct{}{}
+ }
+ }
+ }
+}
+
// workflowManifest represents the structure of workflow_verify.yaml.
type workflowManifest struct {
Workflows []workflowDef `yaml:"workflows"`
← 0a1e80d9 test(cli): add golden output harness (#310)
·
back to Cli Printing Press
·
refactor(cli): share generated auth helpers (#312) b8bb5c1e →