mirror of
https://github.com/larksuite/cli.git
synced 2026-09-14 18:42:53 +08:00
2a1613484a
* feat: add framework flag aliases and unified IM pagination Introduce declarative exact-name flag aliases at the shortcut framework boundary while keeping semantic compatibility domain-owned. Add a shared, format-aware IM pagination pipeline with consistent flags, metadata, safety bounds, resumable cursors, and request throttling. * fix: align alias attribution and pagination contracts * fix: align alias contracts and documentation * test: remove environment-dependent contact bot e2e * test: restore contact bot e2e * docs: reduce IM pagination guidance noise --------- Co-authored-by: liangshuo-1 <266696938+liangshuo-1@users.noreply.github.com>
95 lines
2.5 KiB
Go
95 lines
2.5 KiB
Go
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
// Package flagcontract keeps flag aliases on the shared framework path.
|
|
package flagcontract
|
|
|
|
import (
|
|
"go/ast"
|
|
"go/parser"
|
|
"go/token"
|
|
"io/fs"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/larksuite/cli/lint/lintapi"
|
|
)
|
|
|
|
const aliasOwnerPath = "internal/flagalias/flagalias.go"
|
|
|
|
// ScanOptions mirrors the aggregate lint runner's incremental interface. The
|
|
// alias rules are repository invariants and intentionally scan all production
|
|
// Go files; ChangedFrom is retained for a uniform caller contract.
|
|
type ScanOptions struct {
|
|
ChangedFrom string
|
|
}
|
|
|
|
func ScanRepoWithOptions(root string, _ ScanOptions) ([]lintapi.Violation, error) {
|
|
var out []lintapi.Violation
|
|
err := filepath.WalkDir(root, func(path string, entry fs.DirEntry, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if entry.IsDir() {
|
|
switch entry.Name() {
|
|
case ".git", ".claude", "vendor", "node_modules", "testdata":
|
|
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
|
|
}
|
|
rel = filepath.ToSlash(rel)
|
|
fset := token.NewFileSet()
|
|
file, err := parser.ParseFile(fset, path, nil, 0)
|
|
if err != nil {
|
|
return nil // another compiler/lint stage owns syntax errors
|
|
}
|
|
ast.Inspect(file, func(node ast.Node) bool {
|
|
value, ok := node.(*ast.CallExpr)
|
|
if !ok {
|
|
return true
|
|
}
|
|
selector, ok := value.Fun.(*ast.SelectorExpr)
|
|
if ok && selector.Sel.Name == "SetNormalizeFunc" && rel != aliasOwnerPath {
|
|
out = append(out, violation(fset, rel, value.Pos(),
|
|
"flag_alias_normalizer_owner",
|
|
"SetNormalizeFunc is owned by internal/flagalias",
|
|
"declare exact synonyms with common.Flag.Aliases or call flagalias.Bind from a framework adapter"))
|
|
}
|
|
return true
|
|
})
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sort.SliceStable(out, func(i, j int) bool {
|
|
if out[i].File != out[j].File {
|
|
return out[i].File < out[j].File
|
|
}
|
|
if out[i].Line != out[j].Line {
|
|
return out[i].Line < out[j].Line
|
|
}
|
|
return out[i].Rule < out[j].Rule
|
|
})
|
|
return out, nil
|
|
}
|
|
|
|
func violation(fset *token.FileSet, file string, pos token.Pos, rule, message, suggestion string) lintapi.Violation {
|
|
return lintapi.Violation{
|
|
Rule: rule,
|
|
Action: lintapi.ActionReject,
|
|
File: file,
|
|
Line: fset.Position(pos).Line,
|
|
Message: message,
|
|
Suggestion: suggestion,
|
|
}
|
|
}
|