From 721ee2b0407260239b4bccb1b80e223cd79c4d08 Mon Sep 17 00:00:00 2001 From: Edgar Sipki Date: Sun, 15 Feb 2026 15:59:29 +0300 Subject: [PATCH 1/2] feat: improve easyp init command - Template-based config generation with comments (text/template + go:embed) - Export rule groups from rules package (RuleGroup, AllGroups, AllRuleNames) - Interactive prompter via bubbletea (Confirm, Select, MultiSelect, Input) - Overwrite protection with interactive confirmation - Root-only buf.yml search instead of recursive WalkDir - Full buf migration: deps, breaking.ignore, logging for unsupported fields - Add Exists/Remove methods to FS interface - Fix resource leaks with defer Close() - Fix addUnary to use GetRuleName instead of hardcoded strings - Remove defaultConfig duplication - Remove Version field from Config (not needed) - Remove NoopPrompter, --yes, --force flags (init is interactive-only) # Conflicts: # go.mod --- .gitignore | 2 +- easyp.yaml | 2 - example.easyp.yaml | 1 - go.mod | 22 +- go.sum | 45 ++- internal/adapters/prompter/interactive.go | 321 ++++++++++++++++++++++ internal/adapters/prompter/prompter.go | 18 ++ internal/api/init.go | 29 +- internal/config/config_test.go | 18 +- internal/core/fs.go | 2 + internal/core/init.go | 134 ++++++--- internal/core/init_template.go | 35 +++ internal/core/init_template_test.go | 96 +++++++ internal/core/templates/easyp.yaml.tmpl | 21 ++ internal/fs/fs/adapter.go | 10 + internal/fs/fs/dir_walker.go | 2 + internal/fs/go_git/adapter.go | 9 + internal/rules/builder.go | 31 ++- internal/rules/builder_test.go | 54 ++++ 19 files changed, 788 insertions(+), 64 deletions(-) create mode 100644 internal/adapters/prompter/interactive.go create mode 100644 internal/adapters/prompter/prompter.go create mode 100644 internal/core/init_template.go create mode 100644 internal/core/init_template_test.go create mode 100644 internal/core/templates/easyp.yaml.tmpl create mode 100644 internal/rules/builder_test.go diff --git a/.gitignore b/.gitignore index cf7ea408..526ec0ac 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ certs .env bin -easyp +/easyp coverage.out easyp.lock .DS_Store diff --git a/easyp.yaml b/easyp.yaml index 247e1dc9..d833f978 100644 --- a/easyp.yaml +++ b/easyp.yaml @@ -1,5 +1,3 @@ -version: v1alpha - lint: use: diff --git a/example.easyp.yaml b/example.easyp.yaml index d8d9265d..9f3a0e75 100644 --- a/example.easyp.yaml +++ b/example.easyp.yaml @@ -1,4 +1,3 @@ -version: v1alpha lint: use: - COMMENT_ENUM diff --git a/go.mod b/go.mod index 38301d9b..96eee3d7 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,8 @@ require ( github.com/a8m/envsubst v1.4.3 github.com/brianvoe/gofakeit/v6 v6.28.0 github.com/bufbuild/protocompile v0.14.1 + github.com/charmbracelet/bubbletea v1.3.10 + github.com/charmbracelet/lipgloss v1.1.0 github.com/codeclysm/extract/v3 v3.1.1 github.com/easyp-tech/service v0.2.0 github.com/go-git/go-git/v5 v5.16.3 @@ -15,7 +17,6 @@ require ( github.com/tetratelabs/wazero v1.9.0 github.com/urfave/cli/v2 v2.27.7 github.com/wasilibs/wazero-helpers v0.0.0-20250123031827-cd30c44769bb - github.com/yakwilikk/go-yamlvalidator v0.0.0-20260216223344-568790865548 github.com/yoheimuta/go-protoparser/v4 v4.14.2 golang.org/x/mod v0.30.0 google.golang.org/grpc v1.76.0 @@ -27,11 +28,17 @@ require ( dario.cat/mergo v1.0.2 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/ProtonMail/go-crypto v1.3.0 // indirect + github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect + github.com/charmbracelet/x/ansi v0.10.1 // indirect + github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect + github.com/charmbracelet/x/term v0.2.1 // indirect github.com/cloudflare/circl v1.6.1 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect github.com/cyphar/filepath-securejoin v0.6.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect + github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.6.2 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect @@ -41,21 +48,32 @@ require ( github.com/kevinburke/ssh_config v1.4.0 // indirect github.com/klauspost/compress v1.18.1 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-localereader v0.0.1 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect + github.com/muesli/cancelreader v0.2.2 // indirect + github.com/muesli/termenv v0.16.0 // indirect github.com/otiai10/mint v1.6.3 // indirect github.com/pjbgf/sha1cd v0.5.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rivo/uniseg v0.4.7 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sergi/go-diff v1.4.0 // indirect github.com/skeema/knownhosts v1.3.2 // indirect github.com/stretchr/objx v0.5.3 // indirect github.com/ulikunitz/xz v0.5.15 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect + github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 // indirect + github.com/yakwilikk/go-yamlvalidator v0.0.0-20260216223344-568790865548 golang.org/x/crypto v0.44.0 // indirect golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect golang.org/x/net v0.47.0 // indirect golang.org/x/sync v0.18.0 // indirect - golang.org/x/sys v0.38.0 // indirect + golang.org/x/sys v0.41.0 // indirect + golang.org/x/term v0.40.0 // indirect golang.org/x/text v0.31.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba // indirect gopkg.in/warnings.v0 v0.1.2 // indirect diff --git a/go.sum b/go.sum index 35451f03..96e733f6 100644 --- a/go.sum +++ b/go.sum @@ -13,10 +13,24 @@ github.com/arduino/go-paths-helper v1.2.0 h1:qDW93PR5IZUN/jzO4rCtexiwF8P4OIcOmcS github.com/arduino/go-paths-helper v1.2.0/go.mod h1:HpxtKph+g238EJHq4geEPv9p+gl3v5YYu35Yb+w31Ck= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= +github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/brianvoe/gofakeit/v6 v6.28.0 h1:Xib46XXuQfmlLS2EXRuJpqcw8St6qSZz75OUo0tgAW4= github.com/brianvoe/gofakeit/v6 v6.28.0/go.mod h1:Xj58BMSnFqcn/fAQeSK+/PLtC5kSb7FJIq4JyGa8vEs= github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= +github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw= +github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4= +github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs= +github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk= +github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY= +github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30= +github.com/charmbracelet/x/ansi v0.10.1 h1:rL3Koar5XvX0pHGfovN03f5cxLbCF2YvLeyz7D2jVDQ= +github.com/charmbracelet/x/ansi v0.10.1/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE= +github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8= +github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= +github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= +github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/codeclysm/extract/v3 v3.1.1 h1:iHZtdEAwSTqPrd+1n4jfhr1qBhUWtHlMTjT90+fJVXg= @@ -34,6 +48,8 @@ github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= @@ -75,6 +91,20 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= +github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= +github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= +github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= +github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= +github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8= @@ -87,6 +117,9 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= @@ -115,6 +148,8 @@ github.com/wasilibs/wazero-helpers v0.0.0-20250123031827-cd30c44769bb h1:gQ+ZV4w github.com/wasilibs/wazero-helpers v0.0.0-20250123031827-cd30c44769bb/go.mod h1:jMeV4Vpbi8osrE/pKUxRZkVaA0EX7NZN0A9/oRzgpgY= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 h1:FnBeRrxr7OU4VvAzt5X7s6266i6cSVkkFPS0TuXWbIg= github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/yakwilikk/go-yamlvalidator v0.0.0-20260216223344-568790865548 h1:wKohzDUpJJIQ+Tqbo1FvOsi/T3DNYOVceq6pXeGGh8o= @@ -150,12 +185,14 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= +golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= -golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= +golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= +golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= diff --git a/internal/adapters/prompter/interactive.go b/internal/adapters/prompter/interactive.go new file mode 100644 index 00000000..fea785e5 --- /dev/null +++ b/internal/adapters/prompter/interactive.go @@ -0,0 +1,321 @@ +package prompter + +import ( + "context" + "fmt" + "strings" + + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" +) + +// InteractivePrompter provides interactive prompts via bubbletea. +type InteractivePrompter struct{} + +var _ Prompter = InteractivePrompter{} + +// Styles +var ( + questionStyle = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("6")) + cursorStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("2")) + checkedStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("2")) + helpStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("8")) +) + +// --- Confirm --- + +type confirmModel struct { + question string + defaultValue bool + value bool + done bool + cancelled bool +} + +func (m confirmModel) Init() tea.Cmd { return nil } + +func (m confirmModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + if msg, ok := msg.(tea.KeyMsg); ok { + switch msg.String() { + case "y", "Y": + m.value = true + m.done = true + return m, tea.Quit + case "n", "N": + m.value = false + m.done = true + return m, tea.Quit + case "enter": + m.value = m.defaultValue + m.done = true + return m, tea.Quit + case "ctrl+c", "q": + m.cancelled = true + return m, tea.Quit + } + } + return m, nil +} + +func (m confirmModel) View() string { + if m.done { + result := "No" + if m.value { + result = "Yes" + } + return fmt.Sprintf("%s %s\n", questionStyle.Render(m.question), result) + } + hint := "[y/N]" + if m.defaultValue { + hint = "[Y/n]" + } + return fmt.Sprintf("%s %s ", questionStyle.Render(m.question), helpStyle.Render(hint)) +} + +func (InteractivePrompter) Confirm(_ context.Context, message string, defaultValue bool) (bool, error) { + m := confirmModel{question: message, defaultValue: defaultValue} + result, err := tea.NewProgram(m).Run() + if err != nil { + return defaultValue, fmt.Errorf("bubbletea: %w", err) + } + cm := result.(confirmModel) + if cm.cancelled { + return defaultValue, context.Canceled + } + return cm.value, nil +} + +// --- Select --- + +type selectModel struct { + question string + options []string + cursor int + done bool + cancelled bool +} + +func (m selectModel) Init() tea.Cmd { return nil } + +func (m selectModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + if msg, ok := msg.(tea.KeyMsg); ok { + switch msg.String() { + case "up", "k": + if m.cursor > 0 { + m.cursor-- + } + case "down", "j": + if m.cursor < len(m.options)-1 { + m.cursor++ + } + case "enter": + m.done = true + return m, tea.Quit + case "ctrl+c", "q": + m.cancelled = true + return m, tea.Quit + } + } + return m, nil +} + +func (m selectModel) View() string { + if m.done { + return fmt.Sprintf("%s %s\n", questionStyle.Render(m.question), m.options[m.cursor]) + } + var b strings.Builder + b.WriteString(questionStyle.Render(m.question) + "\n") + for i, opt := range m.options { + cursor := " " + if i == m.cursor { + cursor = cursorStyle.Render("> ") + } + b.WriteString(fmt.Sprintf("%s%s\n", cursor, opt)) + } + b.WriteString(helpStyle.Render("↑/↓ navigate • enter select • q quit")) + return b.String() +} + +func (InteractivePrompter) Select(_ context.Context, message string, options []string, defaultIndex int) (int, error) { + m := selectModel{question: message, options: options, cursor: defaultIndex} + result, err := tea.NewProgram(m).Run() + if err != nil { + return defaultIndex, fmt.Errorf("bubbletea: %w", err) + } + sm := result.(selectModel) + if sm.cancelled { + return defaultIndex, context.Canceled + } + return sm.cursor, nil +} + +// --- MultiSelect --- + +type multiSelectModel struct { + question string + options []string + cursor int + selected map[int]bool + done bool + cancelled bool +} + +func (m multiSelectModel) Init() tea.Cmd { return nil } + +func (m multiSelectModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + if msg, ok := msg.(tea.KeyMsg); ok { + switch msg.String() { + case "up", "k": + if m.cursor > 0 { + m.cursor-- + } + case "down", "j": + if m.cursor < len(m.options)-1 { + m.cursor++ + } + case " ": + m.selected[m.cursor] = !m.selected[m.cursor] + case "a": + allSelected := true + for i := range m.options { + if !m.selected[i] { + allSelected = false + break + } + } + for i := range m.options { + m.selected[i] = !allSelected + } + case "enter": + m.done = true + return m, tea.Quit + case "ctrl+c", "q": + m.cancelled = true + return m, tea.Quit + } + } + return m, nil +} + +func (m multiSelectModel) View() string { + if m.done { + var selected []string + for i, opt := range m.options { + if m.selected[i] { + selected = append(selected, opt) + } + } + return fmt.Sprintf("%s %s\n", questionStyle.Render(m.question), strings.Join(selected, ", ")) + } + var b strings.Builder + b.WriteString(questionStyle.Render(m.question) + "\n") + for i, opt := range m.options { + cursor := " " + if i == m.cursor { + cursor = cursorStyle.Render("> ") + } + check := "[ ] " + if m.selected[i] { + check = checkedStyle.Render("[x] ") + } + b.WriteString(fmt.Sprintf("%s%s%s\n", cursor, check, opt)) + } + b.WriteString(helpStyle.Render("↑/↓ navigate • space toggle • a all • enter confirm • q quit")) + return b.String() +} + +func (InteractivePrompter) MultiSelect(_ context.Context, message string, options []string, defaults []bool) ([]int, error) { + selected := make(map[int]bool) + for i, d := range defaults { + selected[i] = d + } + m := multiSelectModel{question: message, options: options, selected: selected} + result, err := tea.NewProgram(m).Run() + if err != nil { + var indices []int + for i, d := range defaults { + if d { + indices = append(indices, i) + } + } + return indices, fmt.Errorf("bubbletea: %w", err) + } + msm := result.(multiSelectModel) + if msm.cancelled { + var indices []int + for i, d := range defaults { + if d { + indices = append(indices, i) + } + } + return indices, context.Canceled + } + var indices []int + for i := range options { + if msm.selected[i] { + indices = append(indices, i) + } + } + return indices, nil +} + +// --- Input --- + +type inputModel struct { + question string + defaultValue string + value string + done bool + cancelled bool +} + +func (m inputModel) Init() tea.Cmd { return nil } + +func (m inputModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + if msg, ok := msg.(tea.KeyMsg); ok { + switch msg.String() { + case "enter": + if m.value == "" { + m.value = m.defaultValue + } + m.done = true + return m, tea.Quit + case "ctrl+c": + m.cancelled = true + return m, tea.Quit + case "backspace": + if len(m.value) > 0 { + m.value = m.value[:len(m.value)-1] + } + default: + if len(msg.String()) == 1 { + m.value += msg.String() + } + } + } + return m, nil +} + +func (m inputModel) View() string { + if m.done { + return fmt.Sprintf("%s %s\n", questionStyle.Render(m.question), m.value) + } + defaultHint := "" + if m.defaultValue != "" { + defaultHint = helpStyle.Render(fmt.Sprintf(" (%s)", m.defaultValue)) + } + return fmt.Sprintf("%s%s %s", questionStyle.Render(m.question), defaultHint, m.value) +} + +func (InteractivePrompter) Input(_ context.Context, message string, defaultValue string) (string, error) { + m := inputModel{question: message, defaultValue: defaultValue} + result, err := tea.NewProgram(m).Run() + if err != nil { + return defaultValue, fmt.Errorf("bubbletea: %w", err) + } + im := result.(inputModel) + if im.cancelled { + return defaultValue, context.Canceled + } + return im.value, nil +} diff --git a/internal/adapters/prompter/prompter.go b/internal/adapters/prompter/prompter.go new file mode 100644 index 00000000..c1c97cb7 --- /dev/null +++ b/internal/adapters/prompter/prompter.go @@ -0,0 +1,18 @@ +package prompter + +import "context" + +// Prompter provides an interface for interactive user interaction. +type Prompter interface { + // Confirm asks a yes/no question. Returns true if the user answered "yes". + Confirm(ctx context.Context, message string, defaultValue bool) (bool, error) + + // Select offers to choose one option from the list. Returns the index of the selected option. + Select(ctx context.Context, message string, options []string, defaultIndex int) (int, error) + + // MultiSelect offers to choose multiple options. Returns the indices of the selected options. + MultiSelect(ctx context.Context, message string, options []string, defaults []bool) ([]int, error) + + // Input requests text input from the user. + Input(ctx context.Context, message string, defaultValue string) (string, error) +} diff --git a/internal/api/init.go b/internal/api/init.go index ec4c0b85..c831e528 100644 --- a/internal/api/init.go +++ b/internal/api/init.go @@ -5,8 +5,11 @@ import ( "github.com/urfave/cli/v2" + "github.com/easyp-tech/easyp/internal/adapters/prompter" "github.com/easyp-tech/easyp/internal/config" + "github.com/easyp-tech/easyp/internal/core" "github.com/easyp-tech/easyp/internal/fs/fs" + "github.com/easyp-tech/easyp/internal/rules" ) var _ Handler = (*Init)(nil) @@ -55,10 +58,34 @@ func (i Init) Action(ctx *cli.Context) error { return fmt.Errorf("buildCore: %w", err) } - err = app.Initialize(ctx.Context, dirFS, []string{"DEFAULT"}) + opts := core.InitOptions{ + TemplateData: defaultTemplateData(), + Prompter: prompter.InteractivePrompter{}, + } + + err = app.Initialize(ctx.Context, dirFS, opts) if err != nil { return fmt.Errorf("app.Initialize: %w", err) } return nil } + +// defaultTemplateData builds InitTemplateData from all available rule groups. +func defaultTemplateData() core.InitTemplateData { + groups := rules.AllGroups() + lintGroups := make([]core.LintGroup, len(groups)) + for i, g := range groups { + lintGroups[i] = core.LintGroup{ + Name: g.Name, + Rules: g.Rules, + } + } + + return core.InitTemplateData{ + LintGroups: lintGroups, + EnumZeroValueSuffix: "_NONE", + ServiceSuffix: "API", + AgainstGitRef: "master", + } +} diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 85e9895b..443f4c7d 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -19,8 +19,7 @@ func TestParseConfig_EnvironmentVariables(t *testing.T) { }{ { name: "simple environment variable expansion", - configContent: `version: v1alpha -lint: + configContent: `lint: use: - DIRECTORY_SAME_PACKAGE deps: @@ -55,8 +54,7 @@ generate: }, { name: "escape with double dollar sign", - configContent: `version: v1alpha -lint: + configContent: `lint: use: - DIRECTORY_SAME_PACKAGE deps: [] @@ -88,8 +86,7 @@ generate: }, { name: "mixed expansion and escape", - configContent: `version: v1alpha -lint: + configContent: `lint: use: - DIRECTORY_SAME_PACKAGE deps: @@ -126,8 +123,7 @@ generate: }, { name: "unset variable becomes empty", - configContent: `version: v1alpha -lint: + configContent: `lint: use: - DIRECTORY_SAME_PACKAGE deps: [] @@ -147,8 +143,7 @@ generate: }, { name: "default values with :- syntax", - configContent: `version: v1alpha -lint: + configContent: `lint: use: - DIRECTORY_SAME_PACKAGE deps: [] @@ -175,8 +170,7 @@ generate: }, { name: "default values with set variable", - configContent: `version: v1alpha -lint: + configContent: `lint: use: - DIRECTORY_SAME_PACKAGE deps: [] diff --git a/internal/core/fs.go b/internal/core/fs.go index 625d4137..d2394cf5 100644 --- a/internal/core/fs.go +++ b/internal/core/fs.go @@ -14,4 +14,6 @@ type DirWalker interface { type FS interface { Open(name string) (io.ReadCloser, error) Create(name string) (io.WriteCloser, error) + Exists(name string) bool + Remove(name string) error } diff --git a/internal/core/init.go b/internal/core/init.go index 94f64437..d5402e11 100644 --- a/internal/core/init.go +++ b/internal/core/init.go @@ -3,7 +3,6 @@ package core import ( "context" "fmt" - "path/filepath" "gopkg.in/yaml.v3" @@ -55,65 +54,98 @@ type ( } ) +// InitOptions contains options for the Initialize command. +type InitOptions struct { + TemplateData InitTemplateData + Prompter Prompter +} + +// Prompter provides an interface for interactive user interaction. +type Prompter interface { + // Confirm asks a yes/no question. Returns true if the user answered "yes". + Confirm(ctx context.Context, message string, defaultValue bool) (bool, error) +} + // Initialize initializes the EasyP configuration. -func (c *Core) Initialize(ctx context.Context, disk DirWalker, defaultLinters []string) error { - cfg := defaultConfig(defaultLinters) +func (c *Core) Initialize(ctx context.Context, disk DirWalker, opts InitOptions) (err error) { + cfg := configFromTemplateData(opts.TemplateData) + // Check for buf config only in root directory (not recursive). var migrated bool - err := disk.WalkDir(func(path string, err error) error { - switch { - case err != nil: - return err - case ctx.Err() != nil: - return ctx.Err() + for _, bufConfigName := range []string{"buf.yml", "buf.yaml"} { + if !disk.Exists(bufConfigName) { + continue } - defaultConfiguration := defaultConfig(defaultLinters) - if filepath.Base(path) == "buf.yml" || filepath.Base(path) == "buf.yaml" { - migrated = true - err = migrateFromBUF(disk, path, defaultConfiguration) - if err != nil { - return fmt.Errorf("migrateFromBUF: %w", err) - } + migrate, promptErr := opts.Prompter.Confirm(ctx, "Found "+bufConfigName+". Migrate configuration?", true) + if promptErr != nil { + return fmt.Errorf("prompter.Confirm: %w", promptErr) + } + if !migrate { + continue } - return nil - }) - if err != nil { - return fmt.Errorf("fs.WalkDir: %w", err) + migrated = true + if migrateErr := c.migrateFromBUF(ctx, disk, bufConfigName, cfg); migrateErr != nil { + return fmt.Errorf("migrateFromBUF: %w", migrateErr) + } + + break } if !migrated { filename := "easyp.yaml" - res, err := disk.Create(filename) - if err != nil { - return fmt.Errorf("disk.Create: %w", err) + + // Check for existing config (overwrite protection). + if disk.Exists(filename) { + overwrite, promptErr := opts.Prompter.Confirm(ctx, filename+" already exists. Overwrite?", false) + if promptErr != nil { + return fmt.Errorf("prompter.Confirm: %w", promptErr) + } + if !overwrite { + return nil + } } - err = yaml.NewEncoder(res).Encode(cfg) - if err != nil { - return fmt.Errorf("yaml.NewEncoder.Encode: %w", err) + res, createErr := disk.Create(filename) + if createErr != nil { + return fmt.Errorf("disk.Create: %w", createErr) + } + defer func() { + if closeErr := res.Close(); closeErr != nil && err == nil { + err = fmt.Errorf("res.Close: %w", closeErr) + } + }() + + if renderErr := renderInitConfig(res, opts.TemplateData); renderErr != nil { + return fmt.Errorf("renderInitConfig: %w", renderErr) } } return nil } -func defaultConfig(defaultLinters []string) config.Config { +// configFromTemplateData creates a config.Config from template data (used for buf migration). +func configFromTemplateData(data InitTemplateData) config.Config { + var allRules []string + for _, g := range data.LintGroups { + allRules = append(allRules, g.Rules...) + } + return config.Config{ Lint: config.LintConfig{ - Use: defaultLinters, + Use: allRules, AllowCommentIgnores: false, - EnumZeroValueSuffix: "_NONE", - ServiceSuffix: "API", + EnumZeroValueSuffix: data.EnumZeroValueSuffix, + ServiceSuffix: data.ServiceSuffix, }, BreakingCheck: config.BreakingCheck{ - AgainstGitRef: "master", + AgainstGitRef: data.AgainstGitRef, }, } } -func migrateFromBUF(disk FS, path string, defaultConfiguration config.Config) error { +func (c *Core) migrateFromBUF(ctx context.Context, disk FS, path string, defaultConfiguration config.Config) (err error) { f, err := disk.Open(path) if err != nil { return fmt.Errorf("disk.Open: %w", err) @@ -132,17 +164,27 @@ func migrateFromBUF(disk FS, path string, defaultConfiguration config.Config) er return fmt.Errorf("yaml.NewDecoder.Decode: %w", err) } - config := buildCfgFromBUF(defaultConfiguration, b) + // Log unsupported fields. + if len(b.Build.Excludes) > 0 { + c.logger.Warn(ctx, "buf build.excludes is not supported in easyp, skipping") + } + if len(b.Breaking.Use) > 0 { + c.logger.Warn(ctx, "buf breaking.use granular rules not yet supported, using default breaking check") + } - dir := filepath.Dir(path) + migratedCfg := buildCfgFromBUF(defaultConfiguration, b) - filename := filepath.Join(dir, "easyp.yaml") - res, err := disk.Create(filename) + res, err := disk.Create("easyp.yaml") if err != nil { return fmt.Errorf("disk.Create: %w", err) } + defer func() { + if closeErr := res.Close(); closeErr != nil && err == nil { + err = fmt.Errorf("res.Close: %w", closeErr) + } + }() - err = yaml.NewEncoder(res).Encode(config) + err = yaml.NewEncoder(res).Encode(migratedCfg) if err != nil { return fmt.Errorf("yaml.NewEncoder.Encode: %w", err) } @@ -151,8 +193,8 @@ func migrateFromBUF(disk FS, path string, defaultConfiguration config.Config) er } func buildCfgFromBUF(cfg config.Config, bufConfig BUFConfig) config.Config { - return config.Config{ - Deps: nil, + result := config.Config{ + Deps: bufConfig.Deps, Lint: config.LintConfig{ Use: bufConfig.Lint.Use, Except: bufConfig.Lint.Except, @@ -162,5 +204,19 @@ func buildCfgFromBUF(cfg config.Config, bufConfig BUFConfig) config.Config { EnumZeroValueSuffix: bufConfig.Lint.EnumZeroValueSuffix, ServiceSuffix: bufConfig.Lint.ServiceSuffix, }, + BreakingCheck: config.BreakingCheck{ + AgainstGitRef: cfg.BreakingCheck.AgainstGitRef, + Ignore: bufConfig.Breaking.Ignore, + }, } + + // Use defaults from the base config when buf config has empty values. + if result.Lint.EnumZeroValueSuffix == "" { + result.Lint.EnumZeroValueSuffix = cfg.Lint.EnumZeroValueSuffix + } + if result.Lint.ServiceSuffix == "" { + result.Lint.ServiceSuffix = cfg.Lint.ServiceSuffix + } + + return result } diff --git a/internal/core/init_template.go b/internal/core/init_template.go new file mode 100644 index 00000000..b72ab804 --- /dev/null +++ b/internal/core/init_template.go @@ -0,0 +1,35 @@ +package core + +import ( + _ "embed" + "fmt" + "io" + "text/template" +) + +//go:embed templates/easyp.yaml.tmpl +var initTemplate string + +// InitTemplateData contains data for rendering the easyp.yaml template. +type InitTemplateData struct { + LintGroups []LintGroup + EnumZeroValueSuffix string + ServiceSuffix string + AgainstGitRef string +} + +// LintGroup is a group of lint rules with a name and a list of rules. +type LintGroup struct { + Name string // "Minimal", "Basic", "Default", "Comments", "Unary RPC" + Rules []string // ["DIRECTORY_SAME_PACKAGE", "PACKAGE_DEFINED", ...] +} + +// renderInitConfig renders the easyp.yaml template to the given writer. +func renderInitConfig(w io.Writer, data InitTemplateData) error { + tmpl, err := template.New("easyp.yaml").Parse(initTemplate) + if err != nil { + return fmt.Errorf("template.Parse: %w", err) + } + + return tmpl.Execute(w, data) +} diff --git a/internal/core/init_template_test.go b/internal/core/init_template_test.go new file mode 100644 index 00000000..c67f17be --- /dev/null +++ b/internal/core/init_template_test.go @@ -0,0 +1,96 @@ +package core + +import ( + "bytes" + "strings" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestRenderInitConfig(t *testing.T) { + data := InitTemplateData{ + LintGroups: []LintGroup{ + { + Name: "Minimal", + Rules: []string{"DIRECTORY_SAME_PACKAGE", "PACKAGE_DEFINED"}, + }, + { + Name: "Basic", + Rules: []string{"ENUM_FIRST_VALUE_ZERO", "FIELD_LOWER_SNAKE_CASE"}, + }, + }, + EnumZeroValueSuffix: "_NONE", + ServiceSuffix: "API", + AgainstGitRef: "master", + } + + var buf bytes.Buffer + err := renderInitConfig(&buf, data) + require.NoError(t, err) + + output := buf.String() + + t.Run("contains group comments", func(t *testing.T) { + require.Contains(t, output, "# Minimal") + require.Contains(t, output, "# Basic") + }) + + t.Run("contains all rules", func(t *testing.T) { + require.Contains(t, output, "- DIRECTORY_SAME_PACKAGE") + require.Contains(t, output, "- PACKAGE_DEFINED") + require.Contains(t, output, "- ENUM_FIRST_VALUE_ZERO") + require.Contains(t, output, "- FIELD_LOWER_SNAKE_CASE") + }) + + t.Run("contains enum_zero_value_suffix", func(t *testing.T) { + require.Contains(t, output, "enum_zero_value_suffix: _NONE") + }) + + t.Run("contains service_suffix", func(t *testing.T) { + require.Contains(t, output, "service_suffix: API") + }) + + t.Run("contains breaking section", func(t *testing.T) { + require.Contains(t, output, "against_git_ref: master") + }) + + t.Run("contains documentation link", func(t *testing.T) { + require.Contains(t, output, "https://easyp.tech") + }) + + t.Run("is valid yaml structure", func(t *testing.T) { + // Verify basic YAML structure by checking indentation + lines := strings.Split(output, "\n") + foundLint := false + foundBreaking := false + for _, line := range lines { + if strings.TrimSpace(line) == "lint:" { + foundLint = true + } + if strings.TrimSpace(line) == "breaking:" { + foundBreaking = true + } + } + require.True(t, foundLint, "should contain lint: section") + require.True(t, foundBreaking, "should contain breaking: section") + }) +} + +func TestRenderInitConfig_EmptyGroups(t *testing.T) { + data := InitTemplateData{ + LintGroups: nil, + EnumZeroValueSuffix: "_UNSPECIFIED", + ServiceSuffix: "Service", + AgainstGitRef: "develop", + } + + var buf bytes.Buffer + err := renderInitConfig(&buf, data) + require.NoError(t, err) + + output := buf.String() + require.Contains(t, output, "enum_zero_value_suffix: _UNSPECIFIED") + require.Contains(t, output, "service_suffix: Service") + require.Contains(t, output, "against_git_ref: develop") +} diff --git a/internal/core/templates/easyp.yaml.tmpl b/internal/core/templates/easyp.yaml.tmpl new file mode 100644 index 00000000..f063c1b6 --- /dev/null +++ b/internal/core/templates/easyp.yaml.tmpl @@ -0,0 +1,21 @@ +# EasyP configuration file. +# Documentation: https://easyp.tech/docs/guide/introduction/what-is + +lint: + use: +{{- range .LintGroups }} + # {{ .Name }} + {{- range .Rules }} + - {{ . }} + {{- end }} +{{ end }} + # Suffix for the zero value of enums (e.g. MY_ENUM_UNSPECIFIED, MY_ENUM_NONE). + enum_zero_value_suffix: {{ .EnumZeroValueSuffix }} + + # Suffix that all services must have (e.g. UserAPI, OrderAPI). + service_suffix: {{ .ServiceSuffix }} + +# Breaking change detection settings. +breaking: + # Git ref (branch, tag, commit) to compare against. + against_git_ref: {{ .AgainstGitRef }} diff --git a/internal/fs/fs/adapter.go b/internal/fs/fs/adapter.go index 5ed715a1..a1a6fad3 100644 --- a/internal/fs/fs/adapter.go +++ b/internal/fs/fs/adapter.go @@ -21,3 +21,13 @@ func (a *FSAdapter) Create(name string) (io.WriteCloser, error) { path := filepath.Join(a.rootDir, name) return os.Create(path) } + +func (a *FSAdapter) Exists(name string) bool { + _, err := fs.Stat(a.FS, name) + return err == nil +} + +func (a *FSAdapter) Remove(name string) error { + path := filepath.Join(a.rootDir, name) + return os.Remove(path) +} diff --git a/internal/fs/fs/dir_walker.go b/internal/fs/fs/dir_walker.go index c225a8e5..17886945 100644 --- a/internal/fs/fs/dir_walker.go +++ b/internal/fs/fs/dir_walker.go @@ -10,6 +10,8 @@ import ( type FS interface { Open(name string) (io.ReadCloser, error) Create(name string) (io.WriteCloser, error) + Exists(name string) bool + Remove(name string) error } func NewFSWalker(root, path string) *FSWalker { diff --git a/internal/fs/go_git/adapter.go b/internal/fs/go_git/adapter.go index a53ab4b1..7fb9392d 100644 --- a/internal/fs/go_git/adapter.go +++ b/internal/fs/go_git/adapter.go @@ -23,3 +23,12 @@ func (a *GitTreeDiskAdapter) Open(name string) (io.ReadCloser, error) { func (a *GitTreeDiskAdapter) Create(name string) (io.WriteCloser, error) { return nil, errors.New("not implemented") } + +func (a *GitTreeDiskAdapter) Exists(name string) bool { + _, err := a.File(name) + return err == nil +} + +func (a *GitTreeDiskAdapter) Remove(_ string) error { + return errors.New("not implemented") +} diff --git a/internal/rules/builder.go b/internal/rules/builder.go index dde6dd66..58607aef 100644 --- a/internal/rules/builder.go +++ b/internal/rules/builder.go @@ -17,6 +17,33 @@ const ( unaryRPCGroup = "UNARY_RPC" ) +// RuleGroup describes a named group of lint rules. +type RuleGroup struct { + Name string // Human-readable name: "Minimal", "Basic", ... + Key string // Config key: "MINIMAL", "BASIC", ... + Rules []string // List of rule names in the group. +} + +// AllGroups returns all rule groups in canonical order. +func AllGroups() []RuleGroup { + return []RuleGroup{ + {Name: "Minimal", Key: minGroup, Rules: addMinimal(nil)}, + {Name: "Basic", Key: basicGroup, Rules: addBasic(nil)}, + {Name: "Default", Key: defaultGroup, Rules: addDefault(nil)}, + {Name: "Comments", Key: commentsGroup, Rules: addComments(nil)}, + {Name: "Unary RPC", Key: unaryRPCGroup, Rules: addUnary(nil)}, + } +} + +// AllRuleNames returns a flat list of all rule names from all groups. +func AllRuleNames() []string { + var res []string + for _, g := range AllGroups() { + res = append(res, g.Rules...) + } + return res +} + // New returns a map of rules and a map of ignore only rules by configuration. func New(cfg config.LintConfig) ([]core.Rule, map[string][]string, error) { allRules := []core.Rule{ @@ -224,7 +251,7 @@ func addComments(res []string) []string { } func addUnary(res []string) []string { - res = append(res, "RPC_NO_CLIENT_STREAMING") - res = append(res, "RPC_NO_SERVER_STREAMING") + res = append(res, core.GetRuleName(&RPCNoClientStreaming{})) + res = append(res, core.GetRuleName(&RPCNoServerStreaming{})) return res } diff --git a/internal/rules/builder_test.go b/internal/rules/builder_test.go new file mode 100644 index 00000000..9268ae84 --- /dev/null +++ b/internal/rules/builder_test.go @@ -0,0 +1,54 @@ +package rules_test + +import ( + "testing" + + "github.com/easyp-tech/easyp/internal/rules" + "github.com/stretchr/testify/require" +) + +func TestAllGroups(t *testing.T) { + groups := rules.AllGroups() + + t.Run("returns 5 groups", func(t *testing.T) { + require.Len(t, groups, 5) + }) + + t.Run("groups have correct order and keys", func(t *testing.T) { + expectedKeys := []string{"MINIMAL", "BASIC", "DEFAULT", "COMMENTS", "UNARY_RPC"} + expectedNames := []string{"Minimal", "Basic", "Default", "Comments", "Unary RPC"} + for i, g := range groups { + require.Equal(t, expectedKeys[i], g.Key, "group %d key mismatch", i) + require.Equal(t, expectedNames[i], g.Name, "group %d name mismatch", i) + } + }) + + t.Run("no group is empty", func(t *testing.T) { + for _, g := range groups { + require.NotEmpty(t, g.Rules, "group %q has no rules", g.Name) + } + }) + + t.Run("all rule names are unique", func(t *testing.T) { + seen := make(map[string]string) + for _, g := range groups { + for _, r := range g.Rules { + if prevGroup, ok := seen[r]; ok { + t.Errorf("rule %q appears in both %q and %q", r, prevGroup, g.Name) + } + seen[r] = g.Name + } + } + }) +} + +func TestAllRuleNames(t *testing.T) { + allRules := rules.AllRuleNames() + require.NotEmpty(t, allRules) + + var expected []string + for _, g := range rules.AllGroups() { + expected = append(expected, g.Rules...) + } + require.Equal(t, expected, allRules) +} From 9f7537da8db8385176235fc5b67ae7c3faaca5a9 Mon Sep 17 00:00:00 2001 From: Khasbulat Abdullin Date: Sat, 21 Feb 2026 23:59:52 +0300 Subject: [PATCH 2/2] docs: sync docs with current init and config behavior --- README.md | 7 +-- .../cli/breaking-changes/breaking-changes.md | 2 - .../guide/cli/configuration/configuration.md | 48 +++++++++---------- .../docs/guide/cli/generator/examples/go.md | 2 - .../cli/generator/examples/grpc-gateway.md | 2 - .../guide/cli/generator/examples/validate.md | 2 - .../docs/guide/cli/generator/generator.md | 25 +++++++++- docs/dist/docs/guide/cli/linter/linter.md | 2 - .../cli/package-manager/package-manager.md | 5 +- .../docs/guide/introduction/quickstart.md | 5 +- .../cli/breaking-changes/breaking-changes.md | 2 - .../cli/configuration/configuration.md | 48 +++++++++---------- .../ru-guide/cli/generator/examples/go.md | 2 - .../cli/generator/examples/grpc-gateway.md | 3 -- .../cli/generator/examples/validate.md | 2 - .../docs/ru-guide/cli/generator/generator.md | 25 +++++++++- docs/dist/docs/ru-guide/cli/linter/linter.md | 2 - .../cli/package-manager/package-manager.md | 5 +- .../docs/ru-guide/introduction/quickstart.md | 5 +- docs/dist/docs/ru-guide/migration/protoc.md | 10 +--- .../dist/docs/ru-guide/migration/prototool.md | 3 -- .../cli/breaking-changes/breaking-changes.md | 2 - .../guide/cli/configuration/configuration.md | 37 +++++++------- .../docs/guide/cli/generator/examples/go.md | 2 - .../cli/generator/examples/grpc-gateway.md | 2 - .../guide/cli/generator/examples/validate.md | 2 - .../docs/guide/cli/generator/generator.md | 2 - docs/public/docs/guide/cli/linter/linter.md | 2 - .../cli/package-manager/package-manager.md | 3 -- .../docs/guide/introduction/quickstart.md | 5 +- .../cli/breaking-changes/breaking-changes.md | 2 - .../cli/configuration/configuration.md | 37 +++++++------- .../ru-guide/cli/generator/examples/go.md | 2 - .../cli/generator/examples/grpc-gateway.md | 3 -- .../cli/generator/examples/validate.md | 2 - .../docs/ru-guide/cli/generator/generator.md | 2 - .../public/docs/ru-guide/cli/linter/linter.md | 2 - .../cli/package-manager/package-manager.md | 3 -- .../docs/ru-guide/introduction/quickstart.md | 5 +- docs/public/docs/ru-guide/migration/protoc.md | 10 +--- .../docs/ru-guide/migration/prototool.md | 3 -- 41 files changed, 146 insertions(+), 189 deletions(-) diff --git a/README.md b/README.md index 7d0ba23d..9793776f 100644 --- a/README.md +++ b/README.md @@ -115,8 +115,6 @@ While Protocol Buffers offer significant technical advantages over REST/JSON, ac EasyP uses a single `easyp.yaml` file for all configuration: ```yaml -version: v1alpha - # Dependencies deps: - github.com/googleapis/googleapis@common-protos-1_3_1 @@ -150,9 +148,8 @@ lint: # Validate the default easyp.yaml with JSON output (default) easyp validate-config -# Validate a custom file with text output -easyp validate-config --config example.easyp.yaml --format text -``` +# Validate a custom file with text output (global --format flag) +easyp --format text validate-config --config example.easyp.yaml ``` ## Community diff --git a/docs/dist/docs/guide/cli/breaking-changes/breaking-changes.md b/docs/dist/docs/guide/cli/breaking-changes/breaking-changes.md index 47357e42..fb66abe1 100644 --- a/docs/dist/docs/guide/cli/breaking-changes/breaking-changes.md +++ b/docs/dist/docs/guide/cli/breaking-changes/breaking-changes.md @@ -40,8 +40,6 @@ This provides strong compatibility guarantees while being less strict than some Configure breaking changes detection in your `easyp.yaml`: ```yaml -version: v1alpha - breaking: # Git reference to compare against (branch, tag, or commit hash) against_git_ref: "main" diff --git a/docs/dist/docs/guide/cli/configuration/configuration.md b/docs/dist/docs/guide/cli/configuration/configuration.md index 8fa5d1ce..5cad43ce 100644 --- a/docs/dist/docs/guide/cli/configuration/configuration.md +++ b/docs/dist/docs/guide/cli/configuration/configuration.md @@ -13,6 +13,7 @@ Available for all commands: | `--cfg` | `-c` | `EASYP_CFG` | Configuration file path | `easyp.yaml` | | `--config` | | `EASYP_CFG` | Alias for `--cfg` | `easyp.yaml` | | `--debug` | `-d` | `EASYP_DEBUG` | Enable debug mode | `false` | +| `--format` | `-f` | `EASYP_FORMAT` | Output format for commands that support multiple formats (`text`/`json`) | command-specific default | **Examples:** ```bash @@ -37,7 +38,7 @@ easyp lint [flags] |------|-------|-------------|-------------|---------| | `--path` | `-p` | | Directory path to lint | `.` | | `--root` | `-r` | | Base directory for file search | Current working directory | -| `--format` | `-f` | `EASYP_FORMAT` | Output format (text/json) | `text` | +| `--format` | `-f` | `EASYP_FORMAT` | Uses global format flag (`text`/`json`) | Inherits global default | **Examples:** ```bash @@ -48,7 +49,7 @@ easyp lint --path proto/ easyp lint --root src/IPC/Contracts --path . # JSON output format -easyp lint --format json +easyp lint --format json # global flag # Combined flags easyp lint -p proto/ -f json @@ -85,7 +86,7 @@ easyp breaking [flags] |------|-------|-------------|-------------|---------| | `--against` | | | Git ref to compare against | (required) | | `--path` | `-p` | | Directory path to check | `.` | -| `--format` | `-f` | `EASYP_FORMAT` | Output format (text/json) | `text` | +| `--format` | `-f` | `EASYP_FORMAT` | Uses global format flag (`text`/`json`) | Inherits global default | **Examples:** ```bash @@ -96,7 +97,7 @@ easyp breaking --against main easyp breaking --against develop --path proto/ # JSON output -easyp breaking --against main --format json +easyp breaking --against main --format json # global flag ``` **Init command:** @@ -117,6 +118,10 @@ easyp init easyp init --dir proto-project/ ``` +`easyp init` is interactive: +- If `buf.yml`/`buf.yaml` exists in the target directory root, it asks whether to migrate from Buf. +- If `easyp.yaml` already exists, it asks for overwrite confirmation. + **Validate-config command:** ```bash easyp validate-config [flags] @@ -125,15 +130,15 @@ easyp validate-config [flags] | Flag | Short | Environment | Description | Default | |------|-------|-------------|-------------|---------| | `--config` | `-c` | `EASYP_CFG` | Configuration file path | `easyp.yaml` | -| `--format` | `-f` | | Output format (`json` or `text`) | `json` | +| `--format` (global) | `-f` | `EASYP_FORMAT` | Output format for commands that support multiple formats (`json` or `text`) | command-specific (`json` for `validate-config`) | **Examples:** ```bash # Validate default config with JSON output (exit 0 when no errors) easyp validate-config -# Validate a different file with text output -easyp validate-config --config example.easyp.yaml --format text +# Validate a different file with text output (global --format) +easyp --format text validate-config --config example.easyp.yaml ``` **Package management commands:** @@ -180,7 +185,7 @@ EasyP supports environment variables for configuration: | `EASYP_CFG` | Path to configuration file | `easyp.yaml` | | `EASYP_DEBUG` | Enable debug logging | `false` | | `EASYPPATH` | Cache and modules storage directory | `$HOME/.easyp` | -| `EASYP_FORMAT` | Output format for lint command | `text` | +| `EASYP_FORMAT` | Output format for supported commands (`text`/`json`). If not set, each command uses its own default. | command-specific default | | `EASYP_ROOT_GENERATE_PATH` | Root path for generate command | `.` | | `EASYP_INIT_DIR` | Directory for init command | `.` | @@ -223,7 +228,6 @@ EasyP supports both YAML and JSON configuration formats: #### YAML Format (Recommended) ```yaml -version: v1alpha lint: use: - BASIC @@ -247,7 +251,6 @@ breaking: #### JSON Format ```json { - "version": "v1alpha", "lint": { "use": ["BASIC", "COMMENT_SERVICE"] }, @@ -281,8 +284,6 @@ EasyP supports environment variable expansion directly in the `easyp.yaml` confi **Example with all supported features:** ```yaml -version: v1alpha - deps: # Basic expansion: ${VAR} - expands to the value of VAR - ${GOOGLEAPIS_REPO}@${GOOGLEAPIS_VERSION} @@ -322,17 +323,18 @@ generate: ### `version` -**Required.** Specifies the configuration schema version. +**Optional (legacy compatibility).** This field is accepted for backward compatibility and can be omitted in new configs. **Type:** `string` -**Accepted values:** `v1alpha` -**Default:** None (must be specified) +**Default:** omitted +**Recommendation:** if you keep it, use `v1alpha` ```yaml +# Optional compatibility field (can be omitted) version: v1alpha ``` -Future versions will be `v1beta`, `v1`, etc. Currently only `v1alpha` is supported. +The runtime behavior does not depend on this field. ### `lint` @@ -760,7 +762,6 @@ Can be overridden by the `--against` CLI flag. ### Minimal Configuration ```yaml -version: v1alpha lint: use: - MINIMAL @@ -769,7 +770,6 @@ lint: ### Development Configuration ```yaml -version: v1alpha lint: use: - BASIC @@ -798,7 +798,6 @@ generate: ### Production Configuration ```yaml -version: v1alpha lint: use: - MINIMAL @@ -845,7 +844,6 @@ breaking: ### Multi-Service Configuration ```yaml -version: v1alpha lint: use: - BASIC @@ -900,8 +898,8 @@ EasyP validates configuration files on startup and provides helpful error messag # Invalid rule name Error: invalid rule: INVALID_RULE_NAME -# Missing required field -Error: version field is required +# Missing required field in generate section +Error: required field "plugins" is missing (path: generate.plugins) # Invalid dependency format Error: invalid dependency format: invalid-repo-url @@ -913,9 +911,9 @@ Use `easyp --debug` for detailed validation information. EasyP is fully compatible with Buf configurations. To migrate: -1. Rename `buf.yaml` to `easyp.yaml` -2. Change `version: v1` to `version: v1alpha` +1. Place `buf.yaml` or `buf.yml` in the project root +2. Run `easyp init` and confirm migration when prompted 3. Update `deps` format if using BSR modules -4. Adjust any custom lint rules or breaking change configurations +4. Review migrated lint/breaking settings and adjust as needed Most Buf configurations work without changes in EasyP. diff --git a/docs/dist/docs/guide/cli/generator/examples/go.md b/docs/dist/docs/guide/cli/generator/examples/go.md index 4f082df6..4d4417db 100644 --- a/docs/dist/docs/guide/cli/generator/examples/go.md +++ b/docs/dist/docs/guide/cli/generator/examples/go.md @@ -49,8 +49,6 @@ message EchoStreamResponse { Create and configure the easyp.yaml configuration file: ```yaml -version: v1alpha - generate: plugins: - name: go diff --git a/docs/dist/docs/guide/cli/generator/examples/grpc-gateway.md b/docs/dist/docs/guide/cli/generator/examples/grpc-gateway.md index 9c33fa0e..f94f5f0b 100644 --- a/docs/dist/docs/guide/cli/generator/examples/grpc-gateway.md +++ b/docs/dist/docs/guide/cli/generator/examples/grpc-gateway.md @@ -71,8 +71,6 @@ message EchoResponse { Update your `easyp.yaml` configuration file to include the necessary dependencies and plugins: ```yaml -version: v1alpha - deps: # [!code ++] - github.com/googleapis/googleapis # [!code ++] diff --git a/docs/dist/docs/guide/cli/generator/examples/validate.md b/docs/dist/docs/guide/cli/generator/examples/validate.md index 52662a09..e2dc8531 100644 --- a/docs/dist/docs/guide/cli/generator/examples/validate.md +++ b/docs/dist/docs/guide/cli/generator/examples/validate.md @@ -52,8 +52,6 @@ message EchoStreamResponse { Create and configure the easyp.yaml configuration file: ```yaml -version: v1alpha - deps: # [!code ++] - github.com/bufbuild/protoc-gen-validate # [!code ++] diff --git a/docs/dist/docs/guide/cli/generator/generator.md b/docs/dist/docs/guide/cli/generator/generator.md index cd17c0f2..be24ac96 100644 --- a/docs/dist/docs/guide/cli/generator/generator.md +++ b/docs/dist/docs/guide/cli/generator/generator.md @@ -34,8 +34,6 @@ EasyP includes a powerful generator that simplifies the process of generating co ### Complete Configuration Example ```yaml -version: v1alpha - # Package manager dependencies deps: - github.com/googleapis/googleapis@common-protos-1_3_1 @@ -821,6 +819,29 @@ When multiple rules match the same file or field, the following precedence appli EasyP's managed mode is compatible with `buf`'s managed mode. The same configuration format and behavior apply, making it easy to migrate between tools or use both in the same workflow. +## Descriptor Set Generation + +**https://protobuf.dev/programming-guides/techniques/#self-description** + +EasyP supports generating binary FileDescriptorSet files using the `--descriptor_set_out` flag. This allows you to create self-describing protobuf messages that include schema information alongside the data. + +**CLI flags:** + +- `--descriptor_set_out ` - Output path for the binary FileDescriptorSet +- `--include_imports` - Include all transitive dependencies in the FileDescriptorSet + +**Example:** + +```bash +# Generate descriptor set with only target files +easyp generate --descriptor_set_out=./schema.pb + +# Generate descriptor set with all dependencies +easyp generate --descriptor_set_out=./schema.pb --include_imports +``` + +Self-describing messages are useful for dynamic message parsing, runtime schema validation, schema registries, and building generic gRPC clients. For more information, see the [Protocol Buffers documentation on self-description](https://protobuf.dev/programming-guides/techniques/#self-description). + ## Package Manager Integration One of EasyP's most powerful features is the seamless integration between the package manager and code generator. This integration eliminates the common problem of managing proto dependencies manually and ensures that your generated code always has access to the correct versions of imported proto files. diff --git a/docs/dist/docs/guide/cli/linter/linter.md b/docs/dist/docs/guide/cli/linter/linter.md index 15194387..40c5db58 100644 --- a/docs/dist/docs/guide/cli/linter/linter.md +++ b/docs/dist/docs/guide/cli/linter/linter.md @@ -17,8 +17,6 @@ EasyP linter provides flexible configuration options to adapt to different proje ### Complete Configuration Example ```yaml -version: v1alpha - lint: # Rules and categories to use use: diff --git a/docs/dist/docs/guide/cli/package-manager/package-manager.md b/docs/dist/docs/guide/cli/package-manager/package-manager.md index b9323bb1..b3b03ce1 100644 --- a/docs/dist/docs/guide/cli/package-manager/package-manager.md +++ b/docs/dist/docs/guide/cli/package-manager/package-manager.md @@ -69,8 +69,6 @@ EasyP uses a two-tier caching system inspired by Go modules: Configure dependencies in your `easyp.yaml` file: ```yaml -version: v1alpha - deps: - github.com/googleapis/googleapis@common-protos-1_3_1 - github.com/grpc-ecosystem/grpc-gateway@v2.19.1 @@ -335,7 +333,6 @@ git config --global http.sslCAInfo /path/to/certificate.pem ```bash # 1. Create configuration cat > easyp.yaml << EOF -version: v1alpha deps: - github.com/googleapis/googleapis - github.com/grpc-ecosystem/grpc-gateway@v2.19.1 @@ -494,7 +491,7 @@ find ~/.easyp/mod -type d -name "v0.0.0-*" -mtime +30 -exec rm -rf {} \; ```dockerfile # Stage 1: Download dependencies -FROM ghcr.io/easyp-tech/easyp:latest AS deps +FROM easyp/easyp:latest AS deps WORKDIR /workspace COPY easyp.yaml easyp.lock ./ RUN easyp mod vendor diff --git a/docs/dist/docs/guide/introduction/quickstart.md b/docs/dist/docs/guide/introduction/quickstart.md index 77c53f17..03cdfe49 100644 --- a/docs/dist/docs/guide/introduction/quickstart.md +++ b/docs/dist/docs/guide/introduction/quickstart.md @@ -29,13 +29,14 @@ Start by creating an `easyp.yaml` configuration file in your project root: easyp init ``` -This creates a basic configuration file that you'll customize in the following steps. +This command generates a commented template with default lint and breaking settings. +`easyp init` is interactive: if `buf.yml`/`buf.yaml` exists in the target root, EasyP asks whether to migrate it; if `easyp.yaml` already exists, it asks before overwriting. ## Configure linting Linting helps catch errors and ensures your Protobuf files follow best practices. EasyP provides default rules compatible with Buf standards. -Add lint configuration to your `easyp.yaml`: +`easyp init` already generates a lint section. Adjust it if needed, for example: ```yaml lint: diff --git a/docs/dist/docs/ru-guide/cli/breaking-changes/breaking-changes.md b/docs/dist/docs/ru-guide/cli/breaking-changes/breaking-changes.md index 5f02a551..3bb3a084 100644 --- a/docs/dist/docs/ru-guide/cli/breaking-changes/breaking-changes.md +++ b/docs/dist/docs/ru-guide/cli/breaking-changes/breaking-changes.md @@ -40,8 +40,6 @@ EasyP реализует уровень проверки **WIRE+**: Настройка в `easyp.yaml`: ```yaml -version: v1alpha - breaking: # Git reference для сравнения (branch, tag или commit hash) against_git_ref: "main" diff --git a/docs/dist/docs/ru-guide/cli/configuration/configuration.md b/docs/dist/docs/ru-guide/cli/configuration/configuration.md index 764cd7a8..3f44bc57 100644 --- a/docs/dist/docs/ru-guide/cli/configuration/configuration.md +++ b/docs/dist/docs/ru-guide/cli/configuration/configuration.md @@ -13,6 +13,7 @@ Available for all commands: | `--cfg` | `-c` | `EASYP_CFG` | Configuration file path | `easyp.yaml` | | `--config` | | `EASYP_CFG` | Alias for `--cfg` | `easyp.yaml` | | `--debug` | `-d` | `EASYP_DEBUG` | Enable debug mode | `false` | +| `--format` | `-f` | `EASYP_FORMAT` | Формат вывода для команд с поддержкой нескольких форматов (`text`/`json`) | значение по умолчанию зависит от команды | **Examples:** ```bash @@ -37,7 +38,7 @@ easyp lint [flags] |------|-------|-------------|-------------|---------| | `--path` | `-p` | | Directory path to lint | `.` | | `--root` | `-r` | | Базовая директория для поиска файлов | Текущая рабочая директория | -| `--format` | `-f` | `EASYP_FORMAT` | Output format (text/json) | `text` | +| `--format` | `-f` | `EASYP_FORMAT` | Использует глобальный флаг формата (`text`/`json`) | Использует глобальное значение по умолчанию | **Examples:** ```bash @@ -48,7 +49,7 @@ easyp lint --path proto/ easyp lint --root src/IPC/Contracts --path . # JSON output format -easyp lint --format json +easyp lint --format json # глобальный флаг # Combined flags easyp lint -p proto/ -f json @@ -85,7 +86,7 @@ easyp breaking [flags] |------|-------|-------------|-------------|---------| | `--against` | | | Git ref to compare against | (required) | | `--path` | `-p` | | Directory path to check | `.` | -| `--format` | `-f` | `EASYP_FORMAT` | Output format (text/json) | `text` | +| `--format` | `-f` | `EASYP_FORMAT` | Использует глобальный флаг формата (`text`/`json`) | Использует глобальное значение по умолчанию | **Examples:** ```bash @@ -96,7 +97,7 @@ easyp breaking --against main easyp breaking --against develop --path proto/ # JSON output -easyp breaking --against main --format json +easyp breaking --against main --format json # глобальный флаг ``` **Init command:** @@ -117,6 +118,10 @@ easyp init easyp init --dir proto-project/ ``` +`easyp init` работает интерактивно: +- Если в корне целевой директории есть `buf.yml`/`buf.yaml`, команда предложит миграцию из Buf. +- Если `easyp.yaml` уже существует, команда запросит подтверждение на перезапись. + **Validate-config command:** ```bash easyp validate-config [flags] @@ -125,15 +130,15 @@ easyp validate-config [flags] | Flag | Short | Environment | Description | Default | |------|-------|-------------|-------------|---------| | `--config` | `-c` | `EASYP_CFG` | Путь до файла конфигурации | `easyp.yaml` | -| `--format` | `-f` | | Формат вывода (`json` или `text`) | `json` | +| `--format` (global) | `-f` | `EASYP_FORMAT` | Глобальный формат вывода для команд с несколькими форматами (`json` или `text`) | зависит от команды (`json` для `validate-config`) | **Examples:** ```bash # Проверить конфиг по умолчанию с JSON выводом (статус 0 если ошибок нет) easyp validate-config -# Проверить другой файл и вывести в текстовом формате -easyp validate-config --config example.easyp.yaml --format text +# Проверить другой файл и вывести в текстовом формате (глобальный --format) +easyp --format text validate-config --config example.easyp.yaml ``` **Package management commands:** @@ -180,7 +185,7 @@ EasyP supports environment variables for configuration: | `EASYP_CFG` | Path to configuration file | `easyp.yaml` | | `EASYP_DEBUG` | Enable debug logging | `false` | | `EASYPPATH` | Cache and modules storage directory | `$HOME/.easyp` | -| `EASYP_FORMAT` | Output format for lint command | `text` | +| `EASYP_FORMAT` | Формат вывода для поддерживаемых команд (`text`/`json`). Если не указан, каждая команда использует своё значение по умолчанию. | значение по умолчанию зависит от команды | | `EASYP_ROOT_GENERATE_PATH` | Root path for generate command | `.` | | `EASYP_INIT_DIR` | Directory for init command | `.` | @@ -223,7 +228,6 @@ EasyP supports both YAML and JSON configuration formats: #### YAML Format (Recommended) ```yaml -version: v1alpha lint: use: - BASIC @@ -247,7 +251,6 @@ breaking: #### JSON Format ```json { - "version": "v1alpha", "lint": { "use": ["BASIC", "COMMENT_SERVICE"] }, @@ -281,8 +284,6 @@ EasyP поддерживает расширение переменных окр **Пример со всеми поддерживаемыми возможностями:** ```yaml -version: v1alpha - deps: # Базовое расширение: ${VAR} - расширяется в значение переменной VAR - ${GOOGLEAPIS_REPO}@${GOOGLEAPIS_VERSION} @@ -322,17 +323,18 @@ generate: ### `version` -**Required.** Specifies the configuration schema version. +**Опционально (legacy-совместимость).** Поле поддерживается для обратной совместимости и может быть опущено в новых конфигурациях. **Type:** `string` -**Accepted values:** `v1alpha` -**Default:** None (must be specified) +**Default:** не задано +**Рекомендация:** если сохраняете поле, используйте `v1alpha` ```yaml +# Опциональное поле для совместимости (можно опустить) version: v1alpha ``` -Future versions will be `v1beta`, `v1`, etc. Currently only `v1alpha` is supported. +На поведение рантайма это поле не влияет. ### `lint` @@ -760,7 +762,6 @@ Can be overridden by the `--against` CLI flag. ### Minimal Configuration ```yaml -version: v1alpha lint: use: - MINIMAL @@ -769,7 +770,6 @@ lint: ### Development Configuration ```yaml -version: v1alpha lint: use: - BASIC @@ -798,7 +798,6 @@ generate: ### Production Configuration ```yaml -version: v1alpha lint: use: - MINIMAL @@ -845,7 +844,6 @@ breaking: ### Multi-Service Configuration ```yaml -version: v1alpha lint: use: - BASIC @@ -900,8 +898,8 @@ EasyP validates configuration files on startup and provides helpful error messag # Invalid rule name Error: invalid rule: INVALID_RULE_NAME -# Missing required field -Error: version field is required +# Missing required field in generate section +Error: required field "plugins" is missing (path: generate.plugins) # Invalid dependency format Error: invalid dependency format: invalid-repo-url @@ -913,9 +911,9 @@ Use `easyp --debug` for detailed validation information. EasyP is fully compatible with Buf configurations. To migrate: -1. Rename `buf.yaml` to `easyp.yaml` -2. Change `version: v1` to `version: v1alpha` +1. Place `buf.yaml` or `buf.yml` in the project root +2. Run `easyp init` and confirm migration when prompted 3. Update `deps` format if using BSR modules -4. Adjust any custom lint rules or breaking change configurations +4. Review migrated lint/breaking settings and adjust as needed Most Buf configurations work without changes in EasyP. diff --git a/docs/dist/docs/ru-guide/cli/generator/examples/go.md b/docs/dist/docs/ru-guide/cli/generator/examples/go.md index 402b50a5..cc3d1c4f 100644 --- a/docs/dist/docs/ru-guide/cli/generator/examples/go.md +++ b/docs/dist/docs/ru-guide/cli/generator/examples/go.md @@ -49,8 +49,6 @@ message EchoStreamResponse { Создайте и настройте файл конфигурации `easyp.yaml`: ```yaml -version: v1alpha - generate: plugins: - name: go diff --git a/docs/dist/docs/ru-guide/cli/generator/examples/grpc-gateway.md b/docs/dist/docs/ru-guide/cli/generator/examples/grpc-gateway.md index 2e060228..b40797c3 100644 --- a/docs/dist/docs/ru-guide/cli/generator/examples/grpc-gateway.md +++ b/docs/dist/docs/ru-guide/cli/generator/examples/grpc-gateway.md @@ -63,9 +63,6 @@ ## Настройка конфигурации Обновите файл `easyp.yaml`, добавив необходимые зависимости и плагины: - - version: v1alpha - deps: # [!code ++] - github.com/googleapis/googleapis # [!code ++] diff --git a/docs/dist/docs/ru-guide/cli/generator/examples/validate.md b/docs/dist/docs/ru-guide/cli/generator/examples/validate.md index 13134f8d..5460fa8a 100644 --- a/docs/dist/docs/ru-guide/cli/generator/examples/validate.md +++ b/docs/dist/docs/ru-guide/cli/generator/examples/validate.md @@ -52,8 +52,6 @@ message EchoStreamResponse { Создайте и настройте файл конфигурации `easyp.yaml`: ```yaml -version: v1alpha - deps: # [!code ++] - github.com/bufbuild/protoc-gen-validate # [!code ++] diff --git a/docs/dist/docs/ru-guide/cli/generator/generator.md b/docs/dist/docs/ru-guide/cli/generator/generator.md index 829b67bf..f793a93e 100644 --- a/docs/dist/docs/ru-guide/cli/generator/generator.md +++ b/docs/dist/docs/ru-guide/cli/generator/generator.md @@ -34,8 +34,6 @@ EasyP включает мощный генератор, который упро ### Complete Configuration Example ```yaml -version: v1alpha - # Package manager dependencies deps: - github.com/googleapis/googleapis@common-protos-1_3_1 @@ -822,6 +820,29 @@ generate: Managed mode в EasyP совместим с managed mode в `buf`. Тот же формат конфигурации и поведение применяются, что упрощает миграцию между инструментами или использование обоих в одном workflow. +## Генерация Descriptor Set + +**https://protobuf.dev/programming-guides/techniques/#self-description** + +EasyP поддерживает генерацию бинарных FileDescriptorSet файлов с помощью флага `--descriptor_set_out`. Это позволяет создавать самоописывающиеся protobuf сообщения, которые включают информацию о схеме вместе с данными. + +**Флаги CLI:** + +- `--descriptor_set_out <путь>` - Путь для вывода бинарного FileDescriptorSet +- `--include_imports` - Включить все транзитивные зависимости в FileDescriptorSet + +**Пример:** + +```bash +# Генерация descriptor set только с целевыми файлами +easyp generate --descriptor_set_out=./schema.pb + +# Генерация descriptor set со всеми зависимостями +easyp generate --descriptor_set_out=./schema.pb --include_imports +``` + +Самоописывающиеся сообщения полезны для динамического парсинга сообщений, валидации схемы во время выполнения, реестров схем и создания универсальных gRPC клиентов. Для получения дополнительной информации см. [документацию Protocol Buffers о самоописании](https://protobuf.dev/programming-guides/techniques/#self-description). + ## Интеграция с менеджером пакетов Одной из самых мощных возможностей EasyP является бесшовная интеграция генератора кода с менеджером пакетов. Это устраняет проблему ручного управления proto‑зависимостями и гарантирует, что при генерации используются корректные (зафиксированные) версии импортируемых файлов. diff --git a/docs/dist/docs/ru-guide/cli/linter/linter.md b/docs/dist/docs/ru-guide/cli/linter/linter.md index 5e939b88..e31b5063 100644 --- a/docs/dist/docs/ru-guide/cli/linter/linter.md +++ b/docs/dist/docs/ru-guide/cli/linter/linter.md @@ -17,8 +17,6 @@ ### Полный пример конфигурации ```yaml -version: v1alpha - lint: # Используемые категории и правила use: diff --git a/docs/dist/docs/ru-guide/cli/package-manager/package-manager.md b/docs/dist/docs/ru-guide/cli/package-manager/package-manager.md index 9b5fdc21..dda6328b 100644 --- a/docs/dist/docs/ru-guide/cli/package-manager/package-manager.md +++ b/docs/dist/docs/ru-guide/cli/package-manager/package-manager.md @@ -69,8 +69,6 @@ EasyP использует двухуровневый кеш, вдохновлё Зависимости описываются в `easyp.yaml`: ```yaml -version: v1alpha - deps: - github.com/googleapis/googleapis@common-protos-1_3_1 - github.com/grpc-ecosystem/grpc-gateway@v2.19.1 @@ -295,7 +293,6 @@ git config --global http.sslCAInfo /path/to/certificate.pem ```bash cat > easyp.yaml << EOF -version: v1alpha deps: - github.com/googleapis/googleapis - github.com/grpc-ecosystem/grpc-gateway@v2.19.1 @@ -402,7 +399,7 @@ find ~/.easyp/mod -type d -name "v0.0.0-*" -mtime +30 -exec rm -rf {} \; ### Docker multi-stage ```dockerfile -FROM ghcr.io/easyp-tech/easyp:latest AS deps +FROM easyp/easyp:latest AS deps WORKDIR /workspace COPY easyp.yaml easyp.lock ./ RUN easyp mod vendor diff --git a/docs/dist/docs/ru-guide/introduction/quickstart.md b/docs/dist/docs/ru-guide/introduction/quickstart.md index b0a65fea..a223a450 100644 --- a/docs/dist/docs/ru-guide/introduction/quickstart.md +++ b/docs/dist/docs/ru-guide/introduction/quickstart.md @@ -29,13 +29,14 @@ easyp --version easyp init ``` -Этот файл — отправная точка. Мы дополним его далее. +Команда генерирует шаблон `easyp.yaml` с комментариями, дефолтными lint-правилами и настройками breaking-check. +`easyp init` работает интерактивно: если в корне есть `buf.yml`/`buf.yaml`, EasyP предложит миграцию; если `easyp.yaml` уже существует, попросит подтверждение перед перезаписью. ## Настройка линтинга Линтинг помогает находить проблемы и поддерживать единый стиль. EasyP использует правила, совместимые с Buf. -Добавьте секцию lint в `easyp.yaml`: +Секция `lint` уже создаётся командой `easyp init`. При необходимости отредактируйте её, например так: ```yaml lint: diff --git a/docs/dist/docs/ru-guide/migration/protoc.md b/docs/dist/docs/ru-guide/migration/protoc.md index e5c8f05c..3d65c269 100644 --- a/docs/dist/docs/ru-guide/migration/protoc.md +++ b/docs/dist/docs/ru-guide/migration/protoc.md @@ -43,8 +43,6 @@ protoc \ Единый файл `easyp.yaml`: ```yaml -version: v1alpha - deps: - github.com/googleapis/googleapis - github.com/envoyproxy/protoc-gen-validate @@ -81,7 +79,7 @@ easyp generate # Генерация кода - Соберите список всех вызовов `protoc` (скрипты, Makefile, CI). - Зафиксируйте используемые плагины и их версии. 2. Создание `easyp.yaml`: - - Добавьте `version: v1alpha`. + - Поле `version` не обязательно (можете оставить его только для обратной совместимости). - Перенесите плагины в секцию `generate.plugins`. - Укажите опции, которые ранее передавались в командной строке. 3. Зависимости: @@ -163,8 +161,6 @@ generate: ## 10. Минимальный пример конфигурации (только Go) ```yaml -version: v1alpha - generate: plugins: - name: go @@ -181,8 +177,6 @@ easyp generate ## 11. Расширенный пример (Go + gRPC + Gateway + Validate + Breaking и Lint) ```yaml -version: v1alpha - lint: use: - ENUM_FIRST_VALUE_ZERO @@ -251,4 +245,4 @@ breaking: --- -Если нужен упрощённый вариант (только перевод без расширения), сообщи — могу сократить до минимальных двух блоков. \ No newline at end of file +Если нужен упрощённый вариант (только перевод без расширения), сообщи — могу сократить до минимальных двух блоков. diff --git a/docs/dist/docs/ru-guide/migration/prototool.md b/docs/dist/docs/ru-guide/migration/prototool.md index b30502b0..28082a85 100644 --- a/docs/dist/docs/ru-guide/migration/prototool.md +++ b/docs/dist/docs/ru-guide/migration/prototool.md @@ -59,8 +59,6 @@ generate: ```yaml # easyp.yaml (пример) -version: v1alpha - deps: - github.com/org/common-protos # вместо manual include - github.com/org/third-party-protos @@ -175,7 +173,6 @@ breaking: ## Минимальная конфигурация после миграции ```yaml -version: v1alpha lint: use: - MINIMAL diff --git a/docs/public/docs/guide/cli/breaking-changes/breaking-changes.md b/docs/public/docs/guide/cli/breaking-changes/breaking-changes.md index 47357e42..fb66abe1 100644 --- a/docs/public/docs/guide/cli/breaking-changes/breaking-changes.md +++ b/docs/public/docs/guide/cli/breaking-changes/breaking-changes.md @@ -40,8 +40,6 @@ This provides strong compatibility guarantees while being less strict than some Configure breaking changes detection in your `easyp.yaml`: ```yaml -version: v1alpha - breaking: # Git reference to compare against (branch, tag, or commit hash) against_git_ref: "main" diff --git a/docs/public/docs/guide/cli/configuration/configuration.md b/docs/public/docs/guide/cli/configuration/configuration.md index 345643e1..5cad43ce 100644 --- a/docs/public/docs/guide/cli/configuration/configuration.md +++ b/docs/public/docs/guide/cli/configuration/configuration.md @@ -118,6 +118,10 @@ easyp init easyp init --dir proto-project/ ``` +`easyp init` is interactive: +- If `buf.yml`/`buf.yaml` exists in the target directory root, it asks whether to migrate from Buf. +- If `easyp.yaml` already exists, it asks for overwrite confirmation. + **Validate-config command:** ```bash easyp validate-config [flags] @@ -126,15 +130,15 @@ easyp validate-config [flags] | Flag | Short | Environment | Description | Default | |------|-------|-------------|-------------|---------| | `--config` | `-c` | `EASYP_CFG` | Configuration file path | `easyp.yaml` | -| `--format` | `-f` | | Output format (`json` or `text`) | `json` | +| `--format` (global) | `-f` | `EASYP_FORMAT` | Output format for commands that support multiple formats (`json` or `text`) | command-specific (`json` for `validate-config`) | **Examples:** ```bash # Validate default config with JSON output (exit 0 when no errors) easyp validate-config -# Validate a different file with text output -easyp validate-config --config example.easyp.yaml --format text +# Validate a different file with text output (global --format) +easyp --format text validate-config --config example.easyp.yaml ``` **Package management commands:** @@ -224,7 +228,6 @@ EasyP supports both YAML and JSON configuration formats: #### YAML Format (Recommended) ```yaml -version: v1alpha lint: use: - BASIC @@ -248,7 +251,6 @@ breaking: #### JSON Format ```json { - "version": "v1alpha", "lint": { "use": ["BASIC", "COMMENT_SERVICE"] }, @@ -282,8 +284,6 @@ EasyP supports environment variable expansion directly in the `easyp.yaml` confi **Example with all supported features:** ```yaml -version: v1alpha - deps: # Basic expansion: ${VAR} - expands to the value of VAR - ${GOOGLEAPIS_REPO}@${GOOGLEAPIS_VERSION} @@ -323,17 +323,18 @@ generate: ### `version` -**Required.** Specifies the configuration schema version. +**Optional (legacy compatibility).** This field is accepted for backward compatibility and can be omitted in new configs. **Type:** `string` -**Accepted values:** `v1alpha` -**Default:** None (must be specified) +**Default:** omitted +**Recommendation:** if you keep it, use `v1alpha` ```yaml +# Optional compatibility field (can be omitted) version: v1alpha ``` -Future versions will be `v1beta`, `v1`, etc. Currently only `v1alpha` is supported. +The runtime behavior does not depend on this field. ### `lint` @@ -761,7 +762,6 @@ Can be overridden by the `--against` CLI flag. ### Minimal Configuration ```yaml -version: v1alpha lint: use: - MINIMAL @@ -770,7 +770,6 @@ lint: ### Development Configuration ```yaml -version: v1alpha lint: use: - BASIC @@ -799,7 +798,6 @@ generate: ### Production Configuration ```yaml -version: v1alpha lint: use: - MINIMAL @@ -846,7 +844,6 @@ breaking: ### Multi-Service Configuration ```yaml -version: v1alpha lint: use: - BASIC @@ -901,8 +898,8 @@ EasyP validates configuration files on startup and provides helpful error messag # Invalid rule name Error: invalid rule: INVALID_RULE_NAME -# Missing required field -Error: version field is required +# Missing required field in generate section +Error: required field "plugins" is missing (path: generate.plugins) # Invalid dependency format Error: invalid dependency format: invalid-repo-url @@ -914,9 +911,9 @@ Use `easyp --debug` for detailed validation information. EasyP is fully compatible with Buf configurations. To migrate: -1. Rename `buf.yaml` to `easyp.yaml` -2. Change `version: v1` to `version: v1alpha` +1. Place `buf.yaml` or `buf.yml` in the project root +2. Run `easyp init` and confirm migration when prompted 3. Update `deps` format if using BSR modules -4. Adjust any custom lint rules or breaking change configurations +4. Review migrated lint/breaking settings and adjust as needed Most Buf configurations work without changes in EasyP. diff --git a/docs/public/docs/guide/cli/generator/examples/go.md b/docs/public/docs/guide/cli/generator/examples/go.md index 4f082df6..4d4417db 100644 --- a/docs/public/docs/guide/cli/generator/examples/go.md +++ b/docs/public/docs/guide/cli/generator/examples/go.md @@ -49,8 +49,6 @@ message EchoStreamResponse { Create and configure the easyp.yaml configuration file: ```yaml -version: v1alpha - generate: plugins: - name: go diff --git a/docs/public/docs/guide/cli/generator/examples/grpc-gateway.md b/docs/public/docs/guide/cli/generator/examples/grpc-gateway.md index 9c33fa0e..f94f5f0b 100644 --- a/docs/public/docs/guide/cli/generator/examples/grpc-gateway.md +++ b/docs/public/docs/guide/cli/generator/examples/grpc-gateway.md @@ -71,8 +71,6 @@ message EchoResponse { Update your `easyp.yaml` configuration file to include the necessary dependencies and plugins: ```yaml -version: v1alpha - deps: # [!code ++] - github.com/googleapis/googleapis # [!code ++] diff --git a/docs/public/docs/guide/cli/generator/examples/validate.md b/docs/public/docs/guide/cli/generator/examples/validate.md index 52662a09..e2dc8531 100644 --- a/docs/public/docs/guide/cli/generator/examples/validate.md +++ b/docs/public/docs/guide/cli/generator/examples/validate.md @@ -52,8 +52,6 @@ message EchoStreamResponse { Create and configure the easyp.yaml configuration file: ```yaml -version: v1alpha - deps: # [!code ++] - github.com/bufbuild/protoc-gen-validate # [!code ++] diff --git a/docs/public/docs/guide/cli/generator/generator.md b/docs/public/docs/guide/cli/generator/generator.md index d26d5926..be24ac96 100644 --- a/docs/public/docs/guide/cli/generator/generator.md +++ b/docs/public/docs/guide/cli/generator/generator.md @@ -34,8 +34,6 @@ EasyP includes a powerful generator that simplifies the process of generating co ### Complete Configuration Example ```yaml -version: v1alpha - # Package manager dependencies deps: - github.com/googleapis/googleapis@common-protos-1_3_1 diff --git a/docs/public/docs/guide/cli/linter/linter.md b/docs/public/docs/guide/cli/linter/linter.md index 15194387..40c5db58 100644 --- a/docs/public/docs/guide/cli/linter/linter.md +++ b/docs/public/docs/guide/cli/linter/linter.md @@ -17,8 +17,6 @@ EasyP linter provides flexible configuration options to adapt to different proje ### Complete Configuration Example ```yaml -version: v1alpha - lint: # Rules and categories to use use: diff --git a/docs/public/docs/guide/cli/package-manager/package-manager.md b/docs/public/docs/guide/cli/package-manager/package-manager.md index 9af361cc..b3b03ce1 100644 --- a/docs/public/docs/guide/cli/package-manager/package-manager.md +++ b/docs/public/docs/guide/cli/package-manager/package-manager.md @@ -69,8 +69,6 @@ EasyP uses a two-tier caching system inspired by Go modules: Configure dependencies in your `easyp.yaml` file: ```yaml -version: v1alpha - deps: - github.com/googleapis/googleapis@common-protos-1_3_1 - github.com/grpc-ecosystem/grpc-gateway@v2.19.1 @@ -335,7 +333,6 @@ git config --global http.sslCAInfo /path/to/certificate.pem ```bash # 1. Create configuration cat > easyp.yaml << EOF -version: v1alpha deps: - github.com/googleapis/googleapis - github.com/grpc-ecosystem/grpc-gateway@v2.19.1 diff --git a/docs/public/docs/guide/introduction/quickstart.md b/docs/public/docs/guide/introduction/quickstart.md index 77c53f17..03cdfe49 100644 --- a/docs/public/docs/guide/introduction/quickstart.md +++ b/docs/public/docs/guide/introduction/quickstart.md @@ -29,13 +29,14 @@ Start by creating an `easyp.yaml` configuration file in your project root: easyp init ``` -This creates a basic configuration file that you'll customize in the following steps. +This command generates a commented template with default lint and breaking settings. +`easyp init` is interactive: if `buf.yml`/`buf.yaml` exists in the target root, EasyP asks whether to migrate it; if `easyp.yaml` already exists, it asks before overwriting. ## Configure linting Linting helps catch errors and ensures your Protobuf files follow best practices. EasyP provides default rules compatible with Buf standards. -Add lint configuration to your `easyp.yaml`: +`easyp init` already generates a lint section. Adjust it if needed, for example: ```yaml lint: diff --git a/docs/public/docs/ru-guide/cli/breaking-changes/breaking-changes.md b/docs/public/docs/ru-guide/cli/breaking-changes/breaking-changes.md index 5f02a551..3bb3a084 100644 --- a/docs/public/docs/ru-guide/cli/breaking-changes/breaking-changes.md +++ b/docs/public/docs/ru-guide/cli/breaking-changes/breaking-changes.md @@ -40,8 +40,6 @@ EasyP реализует уровень проверки **WIRE+**: Настройка в `easyp.yaml`: ```yaml -version: v1alpha - breaking: # Git reference для сравнения (branch, tag или commit hash) against_git_ref: "main" diff --git a/docs/public/docs/ru-guide/cli/configuration/configuration.md b/docs/public/docs/ru-guide/cli/configuration/configuration.md index d52e2758..3f44bc57 100644 --- a/docs/public/docs/ru-guide/cli/configuration/configuration.md +++ b/docs/public/docs/ru-guide/cli/configuration/configuration.md @@ -118,6 +118,10 @@ easyp init easyp init --dir proto-project/ ``` +`easyp init` работает интерактивно: +- Если в корне целевой директории есть `buf.yml`/`buf.yaml`, команда предложит миграцию из Buf. +- Если `easyp.yaml` уже существует, команда запросит подтверждение на перезапись. + **Validate-config command:** ```bash easyp validate-config [flags] @@ -126,15 +130,15 @@ easyp validate-config [flags] | Flag | Short | Environment | Description | Default | |------|-------|-------------|-------------|---------| | `--config` | `-c` | `EASYP_CFG` | Путь до файла конфигурации | `easyp.yaml` | -| `--format` | `-f` | | Формат вывода (`json` или `text`) | `json` | +| `--format` (global) | `-f` | `EASYP_FORMAT` | Глобальный формат вывода для команд с несколькими форматами (`json` или `text`) | зависит от команды (`json` для `validate-config`) | **Examples:** ```bash # Проверить конфиг по умолчанию с JSON выводом (статус 0 если ошибок нет) easyp validate-config -# Проверить другой файл и вывести в текстовом формате -easyp validate-config --config example.easyp.yaml --format text +# Проверить другой файл и вывести в текстовом формате (глобальный --format) +easyp --format text validate-config --config example.easyp.yaml ``` **Package management commands:** @@ -224,7 +228,6 @@ EasyP supports both YAML and JSON configuration formats: #### YAML Format (Recommended) ```yaml -version: v1alpha lint: use: - BASIC @@ -248,7 +251,6 @@ breaking: #### JSON Format ```json { - "version": "v1alpha", "lint": { "use": ["BASIC", "COMMENT_SERVICE"] }, @@ -282,8 +284,6 @@ EasyP поддерживает расширение переменных окр **Пример со всеми поддерживаемыми возможностями:** ```yaml -version: v1alpha - deps: # Базовое расширение: ${VAR} - расширяется в значение переменной VAR - ${GOOGLEAPIS_REPO}@${GOOGLEAPIS_VERSION} @@ -323,17 +323,18 @@ generate: ### `version` -**Required.** Specifies the configuration schema version. +**Опционально (legacy-совместимость).** Поле поддерживается для обратной совместимости и может быть опущено в новых конфигурациях. **Type:** `string` -**Accepted values:** `v1alpha` -**Default:** None (must be specified) +**Default:** не задано +**Рекомендация:** если сохраняете поле, используйте `v1alpha` ```yaml +# Опциональное поле для совместимости (можно опустить) version: v1alpha ``` -Future versions will be `v1beta`, `v1`, etc. Currently only `v1alpha` is supported. +На поведение рантайма это поле не влияет. ### `lint` @@ -761,7 +762,6 @@ Can be overridden by the `--against` CLI flag. ### Minimal Configuration ```yaml -version: v1alpha lint: use: - MINIMAL @@ -770,7 +770,6 @@ lint: ### Development Configuration ```yaml -version: v1alpha lint: use: - BASIC @@ -799,7 +798,6 @@ generate: ### Production Configuration ```yaml -version: v1alpha lint: use: - MINIMAL @@ -846,7 +844,6 @@ breaking: ### Multi-Service Configuration ```yaml -version: v1alpha lint: use: - BASIC @@ -901,8 +898,8 @@ EasyP validates configuration files on startup and provides helpful error messag # Invalid rule name Error: invalid rule: INVALID_RULE_NAME -# Missing required field -Error: version field is required +# Missing required field in generate section +Error: required field "plugins" is missing (path: generate.plugins) # Invalid dependency format Error: invalid dependency format: invalid-repo-url @@ -914,9 +911,9 @@ Use `easyp --debug` for detailed validation information. EasyP is fully compatible with Buf configurations. To migrate: -1. Rename `buf.yaml` to `easyp.yaml` -2. Change `version: v1` to `version: v1alpha` +1. Place `buf.yaml` or `buf.yml` in the project root +2. Run `easyp init` and confirm migration when prompted 3. Update `deps` format if using BSR modules -4. Adjust any custom lint rules or breaking change configurations +4. Review migrated lint/breaking settings and adjust as needed Most Buf configurations work without changes in EasyP. diff --git a/docs/public/docs/ru-guide/cli/generator/examples/go.md b/docs/public/docs/ru-guide/cli/generator/examples/go.md index 402b50a5..cc3d1c4f 100644 --- a/docs/public/docs/ru-guide/cli/generator/examples/go.md +++ b/docs/public/docs/ru-guide/cli/generator/examples/go.md @@ -49,8 +49,6 @@ message EchoStreamResponse { Создайте и настройте файл конфигурации `easyp.yaml`: ```yaml -version: v1alpha - generate: plugins: - name: go diff --git a/docs/public/docs/ru-guide/cli/generator/examples/grpc-gateway.md b/docs/public/docs/ru-guide/cli/generator/examples/grpc-gateway.md index 2e060228..b40797c3 100644 --- a/docs/public/docs/ru-guide/cli/generator/examples/grpc-gateway.md +++ b/docs/public/docs/ru-guide/cli/generator/examples/grpc-gateway.md @@ -63,9 +63,6 @@ ## Настройка конфигурации Обновите файл `easyp.yaml`, добавив необходимые зависимости и плагины: - - version: v1alpha - deps: # [!code ++] - github.com/googleapis/googleapis # [!code ++] diff --git a/docs/public/docs/ru-guide/cli/generator/examples/validate.md b/docs/public/docs/ru-guide/cli/generator/examples/validate.md index 13134f8d..5460fa8a 100644 --- a/docs/public/docs/ru-guide/cli/generator/examples/validate.md +++ b/docs/public/docs/ru-guide/cli/generator/examples/validate.md @@ -52,8 +52,6 @@ message EchoStreamResponse { Создайте и настройте файл конфигурации `easyp.yaml`: ```yaml -version: v1alpha - deps: # [!code ++] - github.com/bufbuild/protoc-gen-validate # [!code ++] diff --git a/docs/public/docs/ru-guide/cli/generator/generator.md b/docs/public/docs/ru-guide/cli/generator/generator.md index f84838a5..f793a93e 100644 --- a/docs/public/docs/ru-guide/cli/generator/generator.md +++ b/docs/public/docs/ru-guide/cli/generator/generator.md @@ -34,8 +34,6 @@ EasyP включает мощный генератор, который упро ### Complete Configuration Example ```yaml -version: v1alpha - # Package manager dependencies deps: - github.com/googleapis/googleapis@common-protos-1_3_1 diff --git a/docs/public/docs/ru-guide/cli/linter/linter.md b/docs/public/docs/ru-guide/cli/linter/linter.md index 5e939b88..e31b5063 100644 --- a/docs/public/docs/ru-guide/cli/linter/linter.md +++ b/docs/public/docs/ru-guide/cli/linter/linter.md @@ -17,8 +17,6 @@ ### Полный пример конфигурации ```yaml -version: v1alpha - lint: # Используемые категории и правила use: diff --git a/docs/public/docs/ru-guide/cli/package-manager/package-manager.md b/docs/public/docs/ru-guide/cli/package-manager/package-manager.md index 428c45ce..dda6328b 100644 --- a/docs/public/docs/ru-guide/cli/package-manager/package-manager.md +++ b/docs/public/docs/ru-guide/cli/package-manager/package-manager.md @@ -69,8 +69,6 @@ EasyP использует двухуровневый кеш, вдохновлё Зависимости описываются в `easyp.yaml`: ```yaml -version: v1alpha - deps: - github.com/googleapis/googleapis@common-protos-1_3_1 - github.com/grpc-ecosystem/grpc-gateway@v2.19.1 @@ -295,7 +293,6 @@ git config --global http.sslCAInfo /path/to/certificate.pem ```bash cat > easyp.yaml << EOF -version: v1alpha deps: - github.com/googleapis/googleapis - github.com/grpc-ecosystem/grpc-gateway@v2.19.1 diff --git a/docs/public/docs/ru-guide/introduction/quickstart.md b/docs/public/docs/ru-guide/introduction/quickstart.md index b0a65fea..a223a450 100644 --- a/docs/public/docs/ru-guide/introduction/quickstart.md +++ b/docs/public/docs/ru-guide/introduction/quickstart.md @@ -29,13 +29,14 @@ easyp --version easyp init ``` -Этот файл — отправная точка. Мы дополним его далее. +Команда генерирует шаблон `easyp.yaml` с комментариями, дефолтными lint-правилами и настройками breaking-check. +`easyp init` работает интерактивно: если в корне есть `buf.yml`/`buf.yaml`, EasyP предложит миграцию; если `easyp.yaml` уже существует, попросит подтверждение перед перезаписью. ## Настройка линтинга Линтинг помогает находить проблемы и поддерживать единый стиль. EasyP использует правила, совместимые с Buf. -Добавьте секцию lint в `easyp.yaml`: +Секция `lint` уже создаётся командой `easyp init`. При необходимости отредактируйте её, например так: ```yaml lint: diff --git a/docs/public/docs/ru-guide/migration/protoc.md b/docs/public/docs/ru-guide/migration/protoc.md index e5c8f05c..3d65c269 100644 --- a/docs/public/docs/ru-guide/migration/protoc.md +++ b/docs/public/docs/ru-guide/migration/protoc.md @@ -43,8 +43,6 @@ protoc \ Единый файл `easyp.yaml`: ```yaml -version: v1alpha - deps: - github.com/googleapis/googleapis - github.com/envoyproxy/protoc-gen-validate @@ -81,7 +79,7 @@ easyp generate # Генерация кода - Соберите список всех вызовов `protoc` (скрипты, Makefile, CI). - Зафиксируйте используемые плагины и их версии. 2. Создание `easyp.yaml`: - - Добавьте `version: v1alpha`. + - Поле `version` не обязательно (можете оставить его только для обратной совместимости). - Перенесите плагины в секцию `generate.plugins`. - Укажите опции, которые ранее передавались в командной строке. 3. Зависимости: @@ -163,8 +161,6 @@ generate: ## 10. Минимальный пример конфигурации (только Go) ```yaml -version: v1alpha - generate: plugins: - name: go @@ -181,8 +177,6 @@ easyp generate ## 11. Расширенный пример (Go + gRPC + Gateway + Validate + Breaking и Lint) ```yaml -version: v1alpha - lint: use: - ENUM_FIRST_VALUE_ZERO @@ -251,4 +245,4 @@ breaking: --- -Если нужен упрощённый вариант (только перевод без расширения), сообщи — могу сократить до минимальных двух блоков. \ No newline at end of file +Если нужен упрощённый вариант (только перевод без расширения), сообщи — могу сократить до минимальных двух блоков. diff --git a/docs/public/docs/ru-guide/migration/prototool.md b/docs/public/docs/ru-guide/migration/prototool.md index b30502b0..28082a85 100644 --- a/docs/public/docs/ru-guide/migration/prototool.md +++ b/docs/public/docs/ru-guide/migration/prototool.md @@ -59,8 +59,6 @@ generate: ```yaml # easyp.yaml (пример) -version: v1alpha - deps: - github.com/org/common-protos # вместо manual include - github.com/org/third-party-protos @@ -175,7 +173,6 @@ breaking: ## Минимальная конфигурация после миграции ```yaml -version: v1alpha lint: use: - MINIMAL