Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/dist/docs/guide/cli/configuration/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -690,8 +690,8 @@ generate:
- `remote` (string, optional) - Remote plugin URL for execution
- `path` (string, optional) - Path to plugin executable file
- `command` ([]string, optional) - Command to execute plugin
- `out` (string, required) - Output directory for generated files
- `opts` (map[string](string | []string), optional) - Plugin-specific options; each key can be a single value or an array of values
- `out` (string, optional) - Output directory for generated files; defaults to the resolved generate root
- `opts` (map[string](string | number | boolean | array<string | number | boolean>), optional) - Plugin-specific options; each key can be a single scalar value or an array of scalar values
- `with_imports` (boolean, optional) - Include imported dependencies

Plugin source is `one-of`: exactly one of `name`, `remote`, `path`, or `command` must be set.
Expand Down
4 changes: 2 additions & 2 deletions docs/dist/docs/guide/cli/generator/generator.md
Original file line number Diff line number Diff line change
Expand Up @@ -335,8 +335,8 @@ In this mode EasyP:
| `command` | []string | ❌ | - | Command to execute plugin (e.g., `["go", "run", "package"]`) |
| `remote` | string | ❌ | - | Remote plugin URL |
| `path` | string | ❌ | - | Path to plugin executable file |
| `out` | string | | - | Output directory for generated files |
| `opts` | map[string](string \| []string) | ❌ | `{}` | Plugin-specific options; list values are emitted as repeated `key=value` params |
| `out` | string | | resolved generate root | Output directory for generated files |
| `opts` | map[string](string \| number \| boolean \| array<string \| number \| boolean>) | ❌ | `{}` | Plugin-specific options; list values are emitted as repeated `key=value` params |
| `with_imports` | bool | ❌ | `false` | Include proto files from dependencies |

**Note:** Only one plugin source (`name`, `command`, `remote`, or `path`) must be specified for each plugin.
Expand Down
2 changes: 1 addition & 1 deletion docs/dist/docs/guide/introduction/what-is.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ Built for teams and organizations with features like reproducible builds, privat

EasyP simplifies protobuf development so you can focus on building great APIs instead of managing toolchain complexity. Whether you're starting a new project or migrating from existing tools, EasyP provides a smooth path to modern protobuf development.

Ready to get started? Check out our [Installation Guide](./install) and [Quickstart Tutorial](./quickstart).
Ready to get started? Check out our [Installation Guide](/docs/guide/introduction/install) and [Quickstart Tutorial](/docs/guide/introduction/quickstart).

## Stargazers over time
[![Stargazers over time](https://starchart.cc/easyp-tech/easyp.svg?variant=adaptive)](https://starchart.cc/easyp-tech/easyp)
4 changes: 2 additions & 2 deletions docs/dist/docs/ru-guide/cli/configuration/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -690,8 +690,8 @@ generate:
- `remote` (string, optional) - Remote plugin URL for execution
- `path` (string, optional) - Path to plugin executable file
- `command` ([]string, optional) - Command to execute plugin
- `out` (string, required) - Output directory for generated files
- `opts` (map[string](string | []string), optional) - Plugin-specific options; each key can be a single value or an array of values
- `out` (string, optional) - Output directory for generated files; defaults to the resolved generate root
- `opts` (map[string](string | number | boolean | array<string | number | boolean>), optional) - Plugin-specific options; each key can be a single scalar value or an array of scalar values
- `with_imports` (boolean, optional) - Include imported dependencies

Plugin source is `one-of`: exactly one of `name`, `remote`, `path`, or `command` must be set.
Expand Down
4 changes: 2 additions & 2 deletions docs/dist/docs/ru-guide/cli/generator/generator.md
Original file line number Diff line number Diff line change
Expand Up @@ -335,8 +335,8 @@ plugins:
| `command` | []string | ❌ | - | Команда для выполнения плагина (например, `["go", "run", "package"]`) |
| `remote` | string | ❌ | - | URL удалённого плагина |
| `path` | string | ❌ | - | Путь к исполняемому файлу плагина |
| `out` | string | | - | Директория для сгенерированных файлов |
| `opts` | map[string](string \| []string) | ❌ | `{}` | Специфичные опции плагина; значения-списки передаются как повторяющиеся `key=value` |
| `out` | string | | resolved generate root | Директория для сгенерированных файлов |
| `opts` | map[string](string \| number \| boolean \| array<string \| number \| boolean>) | ❌ | `{}` | Специфичные опции плагина; значения-списки передаются как повторяющиеся `key=value` |
| `with_imports` | bool | ❌ | `false` | Включать proto из зависимостей |

**Примечание:** Для каждого плагина должен быть указан ровно один источник (`name`, `command`, `remote` или `path`).
Expand Down
2 changes: 1 addition & 1 deletion docs/dist/docs/ru-guide/introduction/what-is.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ EasyP изначально совместим с экосистемой buf:

EasyP упрощает работу с protobuf — фокусируйтесь на API, а не на рутине. Хотите начать?

Смотрите [Installation Guide](./install) и [Quickstart](./quickstart).
Смотрите [Installation Guide](/docs/guide/introduction/install) и [Quickstart](/docs/guide/introduction/quickstart).

## Stargazers over time
[![Stargazers over time](https://starchart.cc/easyp-tech/easyp.svg?variant=adaptive)](https://starchart.cc/easyp-tech/easyp)
2 changes: 1 addition & 1 deletion docs/dist/search/index.en.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/dist/search/index.ru.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions docs/public/docs/guide/cli/configuration/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -690,8 +690,8 @@ generate:
- `remote` (string, optional) - Remote plugin URL for execution
- `path` (string, optional) - Path to plugin executable file
- `command` ([]string, optional) - Command to execute plugin
- `out` (string, required) - Output directory for generated files
- `opts` (map[string](string | []string), optional) - Plugin-specific options; each key can be a single value or an array of values
- `out` (string, optional) - Output directory for generated files; defaults to the resolved generate root
- `opts` (map[string](string | number | boolean | array<string | number | boolean>), optional) - Plugin-specific options; each key can be a single scalar value or an array of scalar values
- `with_imports` (boolean, optional) - Include imported dependencies

Plugin source is `one-of`: exactly one of `name`, `remote`, `path`, or `command` must be set.
Expand Down
4 changes: 2 additions & 2 deletions docs/public/docs/guide/cli/generator/generator.md
Original file line number Diff line number Diff line change
Expand Up @@ -335,8 +335,8 @@ In this mode EasyP:
| `command` | []string | ❌ | - | Command to execute plugin (e.g., `["go", "run", "package"]`) |
| `remote` | string | ❌ | - | Remote plugin URL |
| `path` | string | ❌ | - | Path to plugin executable file |
| `out` | string | | - | Output directory for generated files |
| `opts` | map[string](string \| []string) | ❌ | `{}` | Plugin-specific options; list values are emitted as repeated `key=value` params |
| `out` | string | | resolved generate root | Output directory for generated files |
| `opts` | map[string](string \| number \| boolean \| array<string \| number \| boolean>) | ❌ | `{}` | Plugin-specific options; list values are emitted as repeated `key=value` params |
| `with_imports` | bool | ❌ | `false` | Include proto files from dependencies |

**Note:** Only one plugin source (`name`, `command`, `remote`, or `path`) must be specified for each plugin.
Expand Down
4 changes: 2 additions & 2 deletions docs/public/docs/ru-guide/cli/configuration/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -690,8 +690,8 @@ generate:
- `remote` (string, optional) - Remote plugin URL for execution
- `path` (string, optional) - Path to plugin executable file
- `command` ([]string, optional) - Command to execute plugin
- `out` (string, required) - Output directory for generated files
- `opts` (map[string](string | []string), optional) - Plugin-specific options; each key can be a single value or an array of values
- `out` (string, optional) - Output directory for generated files; defaults to the resolved generate root
- `opts` (map[string](string | number | boolean | array<string | number | boolean>), optional) - Plugin-specific options; each key can be a single scalar value or an array of scalar values
- `with_imports` (boolean, optional) - Include imported dependencies

Plugin source is `one-of`: exactly one of `name`, `remote`, `path`, or `command` must be set.
Expand Down
4 changes: 2 additions & 2 deletions docs/public/docs/ru-guide/cli/generator/generator.md
Original file line number Diff line number Diff line change
Expand Up @@ -335,8 +335,8 @@ plugins:
| `command` | []string | ❌ | - | Команда для выполнения плагина (например, `["go", "run", "package"]`) |
| `remote` | string | ❌ | - | URL удалённого плагина |
| `path` | string | ❌ | - | Путь к исполняемому файлу плагина |
| `out` | string | | - | Директория для сгенерированных файлов |
| `opts` | map[string](string \| []string) | ❌ | `{}` | Специфичные опции плагина; значения-списки передаются как повторяющиеся `key=value` |
| `out` | string | | resolved generate root | Директория для сгенерированных файлов |
| `opts` | map[string](string \| number \| boolean \| array<string \| number \| boolean>) | ❌ | `{}` | Специфичные опции плагина; значения-списки передаются как повторяющиеся `key=value` |
| `with_imports` | bool | ❌ | `false` | Включать proto из зависимостей |

**Примечание:** Для каждого плагина должен быть указан ровно один источник (`name`, `command`, `remote` или `path`).
Expand Down
2 changes: 1 addition & 1 deletion docs/public/search/index.en.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/public/search/index.ru.json

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions internal/config/plugin_opts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,17 @@ func TestPluginOpts_UnmarshalYAML(t *testing.T) {
"outputServices": {"grpc-js", "generic-definitions"},
},
},
{
name: "sequence of scalar bool and int",
input: `opts:
flags:
- false
- 30
`,
expected: PluginOpts{
"flags": {"false", "30"},
},
},
{
name: "sequence item must be scalar",
input: `opts:
Expand Down
5 changes: 5 additions & 0 deletions internal/config/validate_raw_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,14 @@ generate:
out: .
opts:
env: node
require_unimplemented_servers: false
timeout: 30
outputServices:
- grpc-js
- generic-definitions
flags:
- false
- 30
`

issues, err := ValidateRaw([]byte(content))
Expand Down
3 changes: 2 additions & 1 deletion internal/core/generate_managed_mode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ func TestGenerateManagedModeAppliesToGitRepoInputs(t *testing.T) {
storage := &StorageMock{}
lockFile := &LockFileMock{}

lockFile.EXPECT().DepsIter().Return(func(yield func(models.LockFileInfo) bool) {}).Once()
lockFile.EXPECT().IsEmpty().Return(false).Once()
lockFile.EXPECT().DepsIter().Return(func(yield func(models.LockFileInfo) bool) {}).Twice()
lockFile.EXPECT().Read(moduleName).Return(lockInfo, nil).Twice()
storage.EXPECT().GetInstallDir(moduleName, moduleVersion).Return(gitRepoRoot).Twice()

Expand Down
98 changes: 98 additions & 0 deletions mcp/easypconfig/easypconfig_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,57 @@ func TestSchemaByPath_GitRepoOutAbsent(t *testing.T) {
require.False(t, hasOut)
}

func TestSchemaByPath_PluginOptsAllowScalarBooleansAndNumbers(t *testing.T) {
t.Parallel()

index := SchemaByPath()
node, ok := index["generate.plugins[].opts"]
require.True(t, ok, "expected schema path generate.plugins[].opts")

additionalProperties, ok := nestedMap(node, "additionalProperties")
require.True(t, ok, "expected additionalProperties for generate.plugins[].opts")

topLevelTypes := collectOneOfTypes(t, additionalProperties)
require.Contains(t, topLevelTypes, "array")

arraySchema := findOneOfByType(t, additionalProperties, "array")
itemsSchema, ok := nestedMap(arraySchema, "items")
require.True(t, ok, "expected items schema for generate.plugins[].opts array")
arrayItemTypes := collectOneOfTypes(t, itemsSchema)
require.ElementsMatch(t, []string{"string", "number", "boolean"}, arrayItemTypes)

var scalarTypes []string
for _, schema := range oneOfSchemas(t, additionalProperties) {
typeName, _ := schema["type"].(string)
if typeName != "" {
scalarTypes = append(scalarTypes, typeName)
}
}
require.ElementsMatch(t, []string{"string", "number", "boolean", "array"}, scalarTypes)
}

func TestSchemaByPath_PluginOutOptional(t *testing.T) {
t.Parallel()

index := SchemaByPath()
node, ok := index["generate.plugins[]"]
require.True(t, ok, "expected schema path generate.plugins[]")

var required []string
requiredRaw, ok := node["required"].([]any)
if !ok {
requiredRaw = nil
}

for _, item := range requiredRaw {
field, ok := item.(string)
require.True(t, ok, "expected string required field, got %T", item)
required = append(required, field)
}

require.NotContains(t, required, "out")
}

func TestDescribe_ManagedRulePackageFieldPresent(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -321,6 +372,53 @@ func decodeStructured(t *testing.T, res *mcp.CallToolResult, dst any) {
require.NoError(t, json.Unmarshal(data, dst))
}

func oneOfSchemas(t *testing.T, schema map[string]any) []map[string]any {
t.Helper()

raw, ok := schema["oneOf"]
require.True(t, ok, "expected oneOf in schema")

items, ok := raw.([]any)
require.True(t, ok, "expected oneOf to be an array")

out := make([]map[string]any, 0, len(items))
for _, item := range items {
itemSchema, ok := item.(map[string]any)
require.True(t, ok, "expected oneOf item to be an object")
out = append(out, itemSchema)
}

return out
}

func collectOneOfTypes(t *testing.T, schema map[string]any) []string {
t.Helper()

out := make([]string, 0, 4)
for _, item := range oneOfSchemas(t, schema) {
typeName, _ := item["type"].(string)
if typeName != "" {
out = append(out, typeName)
}
}

return out
}

func findOneOfByType(t *testing.T, schema map[string]any, wantType string) map[string]any {
t.Helper()

for _, item := range oneOfSchemas(t, schema) {
typeName, _ := item["type"].(string)
if typeName == wantType {
return item
}
}

t.Fatalf("expected oneOf branch with type %q", wantType)
return nil
}

func toolText(res *mcp.CallToolResult) string {
parts := make([]string, 0, len(res.Content))
for _, c := range res.Content {
Expand Down
14 changes: 11 additions & 3 deletions mcp/easypconfig/schema_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ type configSchemaPlugin struct {
Remote string `json:"remote,omitempty"`
Path string `json:"path,omitempty"`
Command []string `json:"command,omitempty"`
Out string `json:"out"`
Out string `json:"out,omitempty"`
Comment thread
Yakwilik marked this conversation as resolved.
Opts configSchemaPluginOpts `json:"opts,omitempty"`
WithImports bool `json:"with_imports,omitempty"`
}
Expand All @@ -102,9 +102,17 @@ func (configSchemaPluginOpts) JSONSchema() *invjsonschema.Schema {
AdditionalProperties: &invjsonschema.Schema{
OneOf: []*invjsonschema.Schema{
{Type: "string"},
{Type: "number"},
{Type: "boolean"},
{
Type: "array",
Items: &invjsonschema.Schema{Type: "string"},
Type: "array",
Items: &invjsonschema.Schema{
OneOf: []*invjsonschema.Schema{
{Type: "string"},
{Type: "number"},
{Type: "boolean"},
Comment thread
Yakwilik marked this conversation as resolved.
Comment thread
Yakwilik marked this conversation as resolved.
},
},
},
},
},
Expand Down
6 changes: 3 additions & 3 deletions mcp/easypconfig/spec_docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ func docsByPath() map[string]nodeDoc {
},
"generate.plugins": {
Fields: []FieldDoc{
{Path: "generate.plugins[]", Type: "object", Required: true, Description: "Plugin item with exactly one source and required output directory."},
{Path: "generate.plugins[]", Type: "object", Required: true, Description: "Plugin item with exactly one source and optional output directory."},
},
Examples: []Example{
{
Expand All @@ -155,8 +155,8 @@ func docsByPath() map[string]nodeDoc {
{Path: "generate.plugins[].remote", Type: "string", Required: false, Description: "Remote plugin endpoint (one source option).", Examples: []string{"api.easyp.tech/protobuf/go:v1.36.10"}},
{Path: "generate.plugins[].path", Type: "string", Required: false, Description: "Explicit path to plugin binary (one source option)."},
{Path: "generate.plugins[].command", Type: "array<string>", Required: false, Description: "Command invocation for plugin (one source option).", Examples: []string{`["go","run","github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway@v2.25.1"]`}},
{Path: "generate.plugins[].out", Type: "string", Required: true, Description: "Output directory for generated files.", Examples: []string{".", "gen/go"}},
{Path: "generate.plugins[].opts", Type: "map<string, string | []string>", Required: false, Description: "Plugin options; value can be scalar or array of scalars."},
{Path: "generate.plugins[].out", Type: "string", Required: false, Description: "Output directory for generated files.", DefaultValue: "\"\" (resolved generate root)", Examples: []string{".", "gen/go"}},
{Path: "generate.plugins[].opts", Type: "map<string, string | number | boolean | array<string | number | boolean>>", Required: false, Description: "Plugin options; value can be scalar or array of scalars."},
Comment thread
Yakwilik marked this conversation as resolved.
{Path: "generate.plugins[].with_imports", Type: "boolean", Required: false, Description: "Include dependency protos in generation.", DefaultValue: "false"},
},
Examples: []Example{
Expand Down
23 changes: 18 additions & 5 deletions schemas/easyp-config-v1.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,25 @@
{
"type": "string"
},
{
"type": "number"
},
{
"type": "boolean"
},
{
"items": {
"type": "string"
"oneOf": [
{
"type": "string"
},
{
"type": "number"
},
{
"type": "boolean"
}
]
},
"type": "array"
}
Expand All @@ -180,10 +196,7 @@
}
},
"additionalProperties": false,
"type": "object",
"required": [
"out"
]
"type": "object"
},
"type": "array",
"minItems": 1
Expand Down
23 changes: 18 additions & 5 deletions schemas/easyp-config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,25 @@
{
"type": "string"
},
{
"type": "number"
},
{
"type": "boolean"
},
{
"items": {
"type": "string"
"oneOf": [
{
"type": "string"
},
{
"type": "number"
},
{
"type": "boolean"
}
]
},
"type": "array"
}
Expand All @@ -180,10 +196,7 @@
}
},
"additionalProperties": false,
"type": "object",
"required": [
"out"
]
"type": "object"
},
"type": "array",
"minItems": 1
Expand Down
Loading