diff --git a/.agents/skills/epctl-commands/SKILL.md b/.agents/skills/epctl-commands/SKILL.md new file mode 100644 index 0000000..4e861f3 --- /dev/null +++ b/.agents/skills/epctl-commands/SKILL.md @@ -0,0 +1,202 @@ +--- +name: epctl-commands +description: "Pattern for adding CLI commands to epctl utility. Use when: creating new epctl subcommands, extending CLI functionality, working with cmd/epctl/ files. Covers file structure, urfave/cli v3 patterns, output formatting, and command wiring." +argument-hint: "Describe the CLI command you want to add" +--- + +# epctl CLI Commands — Pattern Guide + +You follow this pattern when adding or modifying CLI commands for the `epctl` utility. + +## When to Use + +- Adding a new subcommand to `epctl` +- Modifying existing CLI commands +- Working with files in `cmd/epctl/` +- Creating new command groups (like `plugins`, `config`) + +## Project Structure + +``` +cmd/epctl/ +├── main.go # Entry point: app.Execute() +└── internal/ + ├── output/ + │ └── printer.go # Printer (text/JSON dual output) + └── app/ + ├── root.go # Root command + getPrinter() helper + ├── builder.go # PluginBuilder (business logic) + ├── path_filter.go # PathFilter (business logic) + ├── plugins_build.go # epctl plugins build + ├── plugins_register.go # epctl plugins register + ├── plugins_list.go # epctl plugins list + └── config_validate.go # epctl config validate +``` + +### Shared vs CLI-only + +| Location | Scope | Example | +|----------|-------|---------| +| `internal/config/` | Shared (server + CLI) | Config types, `Validate()` | +| `cmd/epctl/internal/` | CLI-only | Printer, commands, builder | + +**Rule:** if a package is only used by `epctl`, it lives in `cmd/epctl/internal/`. If both server and CLI use it, it stays in `internal/`. + +## CLI Framework + +**urfave/cli v3** (`github.com/urfave/cli/v3`). + +Key patterns: +- Commands are `*cli.Command` structs +- Action signature: `func(ctx context.Context, cmd *cli.Command) error` +- Global flags defined on root command propagate to all subcommands +- Global `--output` / `-o` flag controls text vs JSON output + +## File Naming + +Pattern: `_.go` + +| File | Command | +|------|---------| +| `plugins_build.go` | `epctl plugins build` | +| `plugins_register.go` | `epctl plugins register` | +| `plugins_list.go` | `epctl plugins list` | +| `config_validate.go` | `epctl config validate` | + +If a file defines the parent group command (e.g., `newPluginsCmd()`), it goes in the first action file of that group (currently `plugins_build.go`). + +## Command Pattern + +### Constructor + runner + +Every command consists of two functions: + +```go +// Constructor — returns the *cli.Command definition. +func newCmd() *cli.Command { + return &cli.Command{ + Name: "", + Usage: "Short description", + ArgsUsage: "[optional args]", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "some-flag", + Value: "default", + Usage: "what it does", + }, + }, + Action: run, + } +} + +// Runner — implements the command logic. +func run(ctx context.Context, cmd *cli.Command) error { + printer := getPrinter(cmd) + + // 1. Read flags + someFlag := cmd.String("some-flag") + + // 2. Business logic + result, err := doSomething(ctx, someFlag) + if err != nil { + return fmt.Errorf("doSomething: %w", err) + } + + // 3. Output via Printer + return printer.Table(headers, rows) +} +``` + +### Group command + +```go +func newSomeGroupCmd() *cli.Command { + return &cli.Command{ + Name: "", + Usage: "Group description", + Commands: []*cli.Command{ + newSomeGroupActionCmd(), + newSomeGroupOtherCmd(), + }, + } +} +``` + +## Output via Printer + +**Every command MUST use `getPrinter(cmd)`** for output. Never use `fmt.Println` directly. + +```go +printer := getPrinter(cmd) + +// Simple message +printer.Message("Done!") // text: "Done!\n" json: {"message":"Done!"} + +// Table +printer.Table( + []string{"NAME", "STATUS"}, // headers + [][]string{{"foo", "ok"}}, // rows +) +// text: tabwriter table json: [{"NAME":"foo","STATUS":"ok"}] + +// Error +printer.Error(err) // text: "Error: ...\n" json: {"error":"..."} +``` + +## Wiring a New Command + +1. Create `cmd/epctl/internal/app/_.go` +2. Implement `newCmd()` + `run()` +3. Add to parent group's `Commands` slice: + +```go +// In the group command constructor: +Commands: []*cli.Command{ + newExistingCmd(), + newYourNewCmd(), // ← add here +}, +``` + +4. If it's a new group, add to root in `root.go`: + +```go +Commands: []*cli.Command{ + newPluginsCmd(), + newConfigCmd(), + newYourGroupCmd(), // ← add here +}, +``` + +## Connecting to EasyP Service + +Commands that talk to the service use the SDK: + +```go +client, err := sdk.NewClient(addr, sdk.WithInsecure()) +if err != nil { + return fmt.Errorf("cannot connect to %s: %w", addr, err) +} + +defer func() { _ = client.Close() }() +``` + +Standard flags for service-connected commands: +```go +&cli.StringFlag{ + Name: "addr", + Value: "localhost:8080", + Usage: "gRPC server address", +}, +``` + +## Quick Checklist + +Before submitting a new CLI command, verify: + +- [ ] File named `_.go` in `cmd/epctl/internal/app/` +- [ ] Uses `getPrinter(cmd)` for all output +- [ ] Constructor `newCmd()` returns `*cli.Command` +- [ ] Runner `run(ctx, cmd) error` handles business logic +- [ ] Wired into parent group's `Commands` slice +- [ ] Flags have defaults and usage descriptions +- [ ] Errors wrapped with `fmt.Errorf(": %w", err)` diff --git a/.agents/skills/go-code-style/SKILL.md b/.agents/skills/go-code-style/SKILL.md index bfad0ad..17492aa 100644 --- a/.agents/skills/go-code-style/SKILL.md +++ b/.agents/skills/go-code-style/SKILL.md @@ -162,11 +162,14 @@ import ( type Feature int const ( - FeatureCodeGeneration Feature = iota + _ Feature = iota + FeatureCodeGeneration FeaturePluginListing ) ``` +**Rule:** zero value (`0`) is always reserved with `_` so that uninitialized enum variables are never silently valid. + ## Interface Rules 1. **Context first:** `func (r *Registry) Get(ctx context.Context, ...) (Plugin, error)` @@ -210,3 +213,4 @@ Before submitting any Go code, verify: - [ ] All exported symbols have godoc comments in English - [ ] No inline comments on control flow lines - [ ] Domain errors defined only in `core/domain.go` +- [ ] Enum types use `_ = iota` to reserve zero value diff --git a/.agents/skills/go-testing/SKILL.md b/.agents/skills/go-testing/SKILL.md new file mode 100644 index 0000000..8ae16a4 --- /dev/null +++ b/.agents/skills/go-testing/SKILL.md @@ -0,0 +1,394 @@ +--- +name: go-testing +description: "Go testing conventions enforcer. Use when: writing tests, reviewing test code, adding test cases, creating test files. Covers table-driven tests, t.Parallel, mocks, assertions, naming conventions." +argument-hint: "Describe the test you are writing or reviewing" +--- + +# Go Testing — EasyP Service + +You enforce project-specific Go testing conventions for the EasyP Service codebase. These rules are mandatory and take precedence over general Go testing guides. + +## When to Use + +- Writing any Go test in this project +- Reviewing or refactoring existing tests +- Adding test cases to existing table-driven tests +- Creating new test files +- Diagnosing test failures related to shared state or race conditions + +Every Go test change MUST comply with these rules. Apply them automatically — do not ask the user for permission. + +## Anti-Patterns (NEVER DO) + +These are the most common mistakes. Memorize and never repeat them. + +### 1. Test case without a name + +```go +// ❌ WRONG — anonymous struct without name field +tests := []struct { + input string + wantErr bool +}{ + {"foo", false}, + {"", true}, +} + +// ✅ CORRECT — every case has a descriptive name +tests := []struct { + name string + input string + wantErr bool +}{ + { + name: "valid_input", + input: "foo", + }, + { + name: "empty_input", + input: "", + wantErr: true, + }, +} +``` + +### 2. Test without t.Parallel() + +```go +// ❌ WRONG — missing t.Parallel() at both levels +func TestCreatePlugin(t *testing.T) { + tests := []struct{ ... }{...} + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // test body + }) + } +} + +// ✅ CORRECT — t.Parallel() at top-level AND inside each sub-test +func TestCreatePlugin(t *testing.T) { + t.Parallel() + + tests := []struct{ ... }{...} + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + // test body + }) + } +} +``` + +### 3. Shared mutable state between test cases + +```go +// ❌ WRONG — cases share and mutate the same mock +func TestListPlugins(t *testing.T) { + t.Parallel() + + reg := &mockRegistry{} // shared across cases! + + tests := []struct { + name string + setup func() + }{ + { + name: "empty", + setup: func() { reg.listResult = nil }, + }, + { + name: "with_results", + setup: func() { reg.listResult = []PluginInfo{{}} }, // mutates shared reg! + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + tt.setup() // RACE: parallel sub-tests mutate shared state + // ... + }) + } +} + +// ✅ CORRECT — each case creates its own dependencies +func TestListPlugins(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + setup func(*mockRegistry) + // ... + }{ + { + name: "empty", + setup: func(r *mockRegistry) { r.listResult = nil }, + }, + { + name: "with_results", + setup: func(r *mockRegistry) { r.listResult = []PluginInfo{{}} }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + reg := &mockRegistry{} // fresh per sub-test + tt.setup(reg) + // ... + }) + } +} +``` + +> **Red flag:** if one test case can affect another, it means shared mutable state exists. This is a bug in the test (or the code under test). Fix the coupling — do not disable `t.Parallel()`. + +### 4. Using assert for fatal preconditions + +```go +// ❌ WRONG — assert does not stop the test; nil dereference follows +result, err := c.GetPlugin(ctx, req) +assert.NoError(t, err) +assert.Equal(t, "grpc", result.Group) // panic if result is nil + +// ✅ CORRECT — require stops the test on failure +result, err := c.GetPlugin(ctx, req) +require.NoError(t, err) +assert.Equal(t, "grpc", result.Group) +``` + +### 5. Ignoring the error case + +```go +// ❌ WRONG — only checks happy path +require.NoError(t, err) + +// ✅ CORRECT — check specific error with ErrorIs +if tt.wantErr != nil { + require.ErrorIs(t, err, tt.wantErr) + return +} +require.NoError(t, err) +``` + +## Table-Driven Test Template + +This is the canonical test structure for the project: + +```go +func TestMethodName(t *testing.T) { + t.Parallel() + + tests := []struct { + name string // REQUIRED, always first field + // inputs + req SomeRequest + // setup / dependencies + setup func(*mockDep) + // expected outputs + want *SomeResult + wantErr error + }{ + { + name: "success", + req: SomeRequest{Field: "value"}, + setup: func(m *mockDep) { + m.result = &SomeResult{Field: "value"} + }, + want: &SomeResult{Field: "value"}, + }, + { + name: "not_found", + req: SomeRequest{Field: "missing"}, + setup: func(m *mockDep) { + m.err = ErrNotFound + }, + wantErr: ErrNotFound, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + // Create fresh dependencies per sub-test + dep := &mockDep{} + if tt.setup != nil { + tt.setup(dep) + } + + // Create the system under test + sut := New(dep) + + // Execute + got, err := sut.Method(context.Background(), tt.req) + + // Assert + if tt.wantErr != nil { + require.ErrorIs(t, err, tt.wantErr) + return + } + require.NoError(t, err) + assert.Equal(t, tt.want, got) + }) + } +} +``` + +### Struct field order + +Always follow this order in the test case struct: + +1. `name string` — descriptive test case name (always first) +2. Input fields — request, arguments, parameters +3. Setup fields — `setup func(...)` for configuring mocks +4. Expected output fields — `want*`, `wantErr` + +## t.Parallel() Rules + +### Every table-driven test uses t.Parallel() at two levels + +1. **Top-level** — `t.Parallel()` right after `func TestXxx(t *testing.T) {` +2. **Sub-test** — `t.Parallel()` right after `t.Run(tt.name, func(t *testing.T) {` + +### Why two levels? + +- **Top-level** `t.Parallel()`: allows this test function to run in parallel with other `Test*` functions in the same package. +- **Sub-test** `t.Parallel()`: allows table-driven sub-tests to run in parallel with each other within the same `Test*` function. + +### When a test cannot be parallel + +If a test case cannot run in parallel, it indicates one of these problems: + +| Symptom | Root Cause | Fix | +|---------|-----------|-----| +| Test modifies package-level variable | Global mutable state | Inject dependency via constructor | +| Test reads file written by another case | Shared filesystem side effect | Use `t.TempDir()` per case | +| Test binds to a fixed port | Port conflict | Use port `0` (OS-assigned) | +| Tests share a mock instance | Shared mutable state | Create mock inside `t.Run` | + +**Rule:** never disable `t.Parallel()` to work around coupling. Refactor the code or test instead. + +## Mock Patterns + +### Inline mocks — no code generation + +All mocks are hand-written structs defined in `_test.go` files. No `mockgen`, `moq`, or similar tools. + +```go +type mockRegistry struct { + getResult Plugin + getErr error + listResult []PluginInfo + createResult *PluginInfo + createErr error +} + +func (m *mockRegistry) Get(ctx context.Context, group, name, version string) (Plugin, error) { + return m.getResult, m.getErr +} + +func (m *mockRegistry) Create(ctx context.Context, info PluginInfo) (*PluginInfo, error) { + return m.createResult, m.createErr +} + +func (m *mockRegistry) List(ctx context.Context, filter PluginFilter) ([]PluginInfo, error) { + return m.listResult, nil +} +``` + +### Mock struct naming + +- Prefix: `mock` (lowercase — unexported) +- Name: the interface being mocked +- Example: `mockRegistry` implements `Registry`, `mockFeatureGate` implements `FeatureGate` + +### Mock location + +Same `_test.go` file that uses them. If multiple test files in a package need the same mock, put it in a shared `helpers_test.go`. + +## Assertion Patterns + +Uses `testify` (`github.com/stretchr/testify`): + +| Function | Package | When to Use | +|----------|---------|-------------| +| `require.NoError(t, err)` | `require` | Error must be nil to continue | +| `require.ErrorIs(t, err, target)` | `require` | Error must match sentinel | +| `require.ErrorContains(t, err, substr)` | `require` | Error message must contain text | +| `require.NotNil(t, result)` | `require` | Result must exist before accessing fields | +| `assert.Equal(t, expected, actual)` | `assert` | Value comparison (test continues on failure) | +| `assert.Len(t, slice, n)` | `assert` | Collection length check | +| `assert.Empty(t, slice)` | `assert` | Collection must be empty | +| `assert.True(t, cond)` | `assert` | Boolean condition | + +**Rule of thumb:** +- `require` — for preconditions: if this fails, the rest of the test is meaningless (stops test) +- `assert` — for assertions: check values, the test can continue to report multiple failures + +## Naming Conventions + +### Test files + +| Source File | Test File | +|-------------|-----------| +| `core.go` | `core_test.go` | +| `crud.go` | `crud_test.go` | +| `registry.go` | `registry_test.go` | + +### Test functions + +| Target | Pattern | Example | +|--------|---------|---------| +| Exported method | `Test` | `TestCreatePlugin` | +| Unexported function | `Test_` | `Test_validateName` | +| Multiple scenarios | `Test_` | `TestCreatePlugin_WithTags` | + +### Test case names + +- Lowercase, descriptive, use underscores for readability +- Describe the scenario, not the assertion + +```go +// ✅ Good names +"success" +"not_found" +"feature_denied" +"invalid_plugin_name" +"empty_version_defaults_to_latest" +"duplicate_returns_conflict" + +// ❌ Bad names +"test1" +"case_2" +"should_work" +"error" +"TestCreatePlugin" // don't repeat the function name +``` + +### Test package + +Tests use the **same package** (not `_test` suffix). This allows access to unexported symbols: + +```go +// internal/core/crud_test.go +package core // same package — NOT core_test +``` + +## Quick Checklist + +Before submitting any Go test code, verify: + +- [ ] Every test case struct has `name string` as the first field +- [ ] `t.Parallel()` is called at top-level AND inside each `t.Run` +- [ ] No shared mutable state between test cases +- [ ] Mocks are created fresh inside each `t.Run` (not shared) +- [ ] `require` is used for preconditions, `assert` for value checks +- [ ] Error cases use `require.ErrorIs(t, err, expectedErr)` +- [ ] Test file uses the same package (no `_test` suffix) +- [ ] Test case names are lowercase and descriptive +- [ ] `setup` function (if used) receives mock pointers, does not capture outer scope diff --git a/.gitignore b/.gitignore index 6836dc7..897c7f5 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,8 @@ *.dylib bin/ main +easyp +epctl plugins/ # Test binary, built with `go test -c` diff --git a/.golangci.yml b/.golangci.yml index 8fe8fef..6c0777c 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -78,8 +78,6 @@ linters: - varnamelen - testpackage - nilnil - - paralleltest - - tparallel - dogsled - err113 path: _test\.go diff --git a/.spec/TESTING.md b/.spec/TESTING.md index 43aa66f..3203143 100644 --- a/.spec/TESTING.md +++ b/.spec/TESTING.md @@ -95,6 +95,36 @@ for _, tt := range tests { } ``` +### Parallel Tests + +All table-driven tests MUST use `t.Parallel()` at two levels: + +```go +func TestXxx(t *testing.T) { + t.Parallel() // top-level: run alongside other Test* functions + + tests := []struct { + name string + // ... + }{...} + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() // sub-test: run cases concurrently + + // Create fresh dependencies per sub-test (no shared mutable state) + // ... + }) + } +} +``` + +**Rules:** +- Every table-driven test has `t.Parallel()` at top-level and inside each `t.Run` +- Each sub-test creates its own mocks/dependencies (no shared mutable state) +- If a test cannot be parallel, it indicates shared state — refactor the code or test +- Enforced by `paralleltest` and `tparallel` linters + ### Inline Mocks Mocks are defined as simple structs in test files — no code generation: diff --git a/.spec/features/disk-plugin-execution/pipeline.json b/.spec/features/disk-plugin-execution/pipeline.json index 1cccb73..fe75264 100644 --- a/.spec/features/disk-plugin-execution/pipeline.json +++ b/.spec/features/disk-plugin-execution/pipeline.json @@ -15,7 +15,7 @@ "branch": "feature/disk-plugin-execution", "worktree": ".worktrees/disk-plugin-execution", "last_completed_task": null, - "finish_action": null, - "finished_at": null, + "finish_action": "keep", + "finished_at": "2026-05-28T19:46:31Z", "finish_base": null } diff --git a/.spec/features/disk-plugin-execution/pipeline.kv b/.spec/features/disk-plugin-execution/pipeline.kv index ab93aa3..d2fc0bd 100644 --- a/.spec/features/disk-plugin-execution/pipeline.kv +++ b/.spec/features/disk-plugin-execution/pipeline.kv @@ -27,3 +27,5 @@ revision_count_review=1 history_5_phase=review history_5_artifact=.spec/features/disk-plugin-execution/review.md history_5_approved_at=2026-05-24T14:47:07Z +finish_action=keep +finished_at=2026-05-28T19:46:31Z diff --git a/.spec/features/epctl-cli/approved/design.md b/.spec/features/epctl-cli/approved/design.md new file mode 100644 index 0000000..8aee214 --- /dev/null +++ b/.spec/features/epctl-cli/approved/design.md @@ -0,0 +1,340 @@ +# epctl CLI — Design + +## 2.1 Обзор + +Реализация CLI-утилиты `epctl` как отдельного бинарника. Задача делится на 5 логических частей: + +1. **Рефакторинг структуры** — перенос `cmd/main.go` → `cmd/easyp/main.go`, фиксация namespace +2. **CLI-каркас** — `cmd/epctl/main.go` на cobra с глобальными флагами (`--output`) +3. **Команды plugins** — `build`, `register`, `list` с path-filter и JSON-выводом +4. **Команда config validate** — структурная валидация YAML +5. **Инфраструктура** — обновление Dockerfile, Taskfile, удаление bash-скриптов + +## 2.2 Архитектура + +```mermaid +graph TD + subgraph "cmd/ (entry points)" + EASYP["cmd/easyp/main.go"] + EPCTL["cmd/epctl/main.go"] + end + + subgraph "internal/epctl/ (CLI логика)" + ROOT["root.go — cobra root cmd"] + BUILD["plugins_build.go"] + REGISTER["plugins_register.go"] + LIST["plugins_list.go"] + VALIDATE["config_validate.go"] + PFILTER["path_filter.go"] + BUILDER["builder.go — PluginBuilder"] + end + + subgraph "internal/ (shared packages)" + OUTPUT["output/ — Printer"] + CONFIG["config/ — Config + Validate"] + end + + subgraph "Existing (unchanged)" + SDK["sdk/ — Client"] + API["api/generator/v1/"] + REGISTRY_DIR["registry/ (plugin.yaml)"] + PLUGINS_DIR["plugins/ (binaries)"] + end + + EASYP --> CONFIG + EPCTL --> ROOT + ROOT --> BUILD + ROOT --> REGISTER + ROOT --> LIST + ROOT --> VALIDATE + + BUILD --> BUILDER + BUILD --> PFILTER + BUILD --> OUTPUT + BUILDER --> REGISTRY_DIR + BUILDER -->|docker build| PLUGINS_DIR + + REGISTER --> SDK + REGISTER --> PFILTER + REGISTER --> OUTPUT + REGISTER --> PLUGINS_DIR + LIST --> SDK + LIST --> OUTPUT + + VALIDATE --> CONFIG + VALIDATE --> OUTPUT + + SDK --> API + + style EPCTL fill:#90EE90 + style ROOT fill:#90EE90 + style BUILD fill:#90EE90 + style REGISTER fill:#90EE90 + style LIST fill:#90EE90 + style VALIDATE fill:#90EE90 + style OUTPUT fill:#90EE90 + style PFILTER fill:#90EE90 + style BUILDER fill:#90EE90 + style CONFIG fill:#90EE90 + style EASYP fill:#FFD700 + style SDK fill:#FFD700 +``` + +**Порядок реализации:** +1. `internal/config/` — shared config types + `Validate()` (REQ-5.*) +2. `internal/output/` — shared `Printer` (REQ-6.*) +3. `cmd/easyp/main.go` — переезд + namespace + вызов `cfg.Validate()` при старте (REQ-1.*) +4. `internal/epctl/path_filter.go` + `builder.go` — инфраструктурный слой +5. `cmd/epctl/` + `internal/epctl/root.go` — CLI-каркас +6. `plugins build` → `plugins register` → `plugins list` → `config validate` +7. SDK: `CreatePlugin` метод +8. Dockerfile, Taskfile, удаление скриптов + +## 2.3 Компоненты и интерфейсы + +### Файлы, требующие изменений + +| Файл | Тип | Описание | +|------|-----|----------| +| `internal/config/config.go` | `[NEW]` | Shared-пакет: типы Config, Server, Ports, etc. + метод `Validate()`. Используется и сервером, и `epctl` | +| `internal/output/printer.go` | `[NEW]` | Shared-пакет: `Printer` — абстракция вывода text/JSON. Используется `epctl`, может использоваться сервером | +| `cmd/easyp/main.go` | `[NEW]` | Перенос из `cmd/main.go`. `const serviceNamespace = "easyp"`, импорт `internal/config`, вызов `cfg.Validate()` при старте | +| `cmd/main.go` | `[DELETED]` | Перемещён в `cmd/easyp/main.go` | +| `cmd/epctl/main.go` | `[NEW]` | Entry point CLI: `epctl.Execute()` | +| `internal/epctl/root.go` | `[NEW]` | Cobra root command, глобальные флаги `--output` | +| `internal/epctl/path_filter.go` | `[NEW]` | `PathFilter` — фильтрация по `group` или `group/name` | +| `internal/epctl/builder.go` | `[NEW]` | `PluginBuilder` — сборка плагинов (порт legacy builder) | +| `internal/epctl/plugins_build.go` | `[NEW]` | Cobra command `plugins build` | +| `internal/epctl/plugins_register.go` | `[NEW]` | Cobra command `plugins register` | +| `internal/epctl/plugins_list.go` | `[NEW]` | Cobra command `plugins list` | +| `internal/epctl/config_validate.go` | `[NEW]` | Cobra command `config validate` | +| `sdk/client.go` | `[MODIFIED]` | Добавление метода `CreatePlugin(ctx, group, name, version string, config map[string]any, tags []string)` | +| `Dockerfile` | `[MODIFIED]` | `go build -o easyp ./cmd/easyp/`, путь COPY | +| `Taskfile.yml` | `[MODIFIED]` | Замена `./build-plugins.sh` → `go run ./cmd/epctl plugins build`, `./register-plugins.sh` → `go run ./cmd/epctl plugins register` | +| `build-plugins.sh` | `[DELETED]` | Заменён на `epctl plugins build` | +| `register-plugins.sh` | `[DELETED]` | Заменён на `epctl plugins register` | + +### Файлы, НЕ требующие изменений + +| Файл | Причина | +|------|---------| +| `api/generator/v1/generator.proto` | Protobuf API не изменяется | +| `api/generator/v1/*.pb.go` | Сгенерированный код, не трогаем | +| `internal/core/` | Domain-логика не затрагивается | +| `internal/api/` | gRPC handlers не изменяются | +| `internal/adapters/registry/` | Адаптер не изменяется | +| `cmd/mcp-smoke/main.go` | Остаётся как есть (deferred) | +| `sdk/filter.go`, `sdk/health.go`, `sdk/retry.go` | Существующий SDK код не меняется | +| `registry/` | Plugin.yaml и Dockerfiles не изменяются | +| `config.yml`, `config.local.yml` | Формат конфига не изменяется | + +### Интерфейсы + +```go +// internal/output/printer.go +// Shared-пакет: используется и epctl, и потенциально сервером +// (например, для CLI-вывода health/status информации). + +// Printer абстрагирует вывод результатов в text или JSON формат. +type Printer struct { + format string // "text" | "json" + w io.Writer +} + +func NewPrinter(format string, w io.Writer) *Printer +func (p *Printer) Table(headers []string, rows [][]string) error +func (p *Printer) JSON(v any) error +func (p *Printer) Message(msg string) error +func (p *Printer) Error(err error) error +``` + +```go +// internal/epctl/path_filter.go + +// PathFilter фильтрует плагины по group и опционально name. +type PathFilter struct { + Group string // Обязательное (если фильтр задан) + Name string // Опциональное +} + +// ParsePathFilter парсит "group" или "group/name" в PathFilter. +func ParsePathFilter(arg string) (PathFilter, error) + +// Match проверяет, подходит ли plugin path под фильтр. +func (f PathFilter) Match(group, name string) bool +``` + +```go +// internal/config/config.go +// Shared-пакет: используется cmd/easyp/ (сервер вызывает Validate при старте) +// и internal/epctl/config_validate.go (команда epctl config validate). + +// Validate выполняет структурную валидацию конфигурации. +// Вызывается: +// 1. При старте сервера (cmd/easyp/main.go → start() → cfg.Validate()) +// 2. Из CLI команды (epctl config validate ) +func (c *Config) Validate() error + +// LoadAndValidate загружает YAML из файла и валидирует. +// Используется в epctl config validate. +func LoadAndValidate(path string) (*Config, []string, error) +// Возвращает: конфиг, список warnings (unknown fields), ошибку. +``` + +```go +// internal/epctl/builder.go + +// PluginConfig — конфигурация плагина из plugin.yaml. +type PluginConfig struct { + Binary string `yaml:"binary"` + BuildArgs map[string]string `yaml:"build_args,omitempty"` + Versions []VersionEntry `yaml:"versions"` +} + +// VersionEntry поддерживает строковый и map-формат версий. +type VersionEntry struct { + Version string +} + +// BuildJob представляет одну задачу сборки плагина. +type BuildJob struct { + Group, Name, Version, Binary string + BuildArgs map[string]string + PluginDir, OutputDir string +} + +// BuildResult — результат сборки одного плагина. +type BuildResult struct { + Job BuildJob + Success bool + Cached bool + Size string + Error error + Duration time.Duration +} + +// BuildSummary — итоговый отчёт сборки. +type BuildSummary struct { + Total, Built, Failed, Cached int + Elapsed time.Duration + Results []BuildResult +} + +// PluginBuilder собирает плагины из registry/. +type PluginBuilder struct { + registryDir string + outputDir string + parallelism int + continueOnError bool +} + +func NewPluginBuilder(registryDir, outputDir string, parallelism int, continueOnError bool) *PluginBuilder + +// DiscoverJobs находит все BuildJob из plugin.yaml файлов, применяя фильтр. +func (b *PluginBuilder) DiscoverJobs(filter *PathFilter) ([]BuildJob, error) + +// Build выполняет сборку всех jobs. +func (b *PluginBuilder) Build(ctx context.Context, jobs []BuildJob) (*BuildSummary, error) +``` + +```go +// sdk/client.go (дополнение) + +// CreatePlugin registers a new plugin in the service. +func (c *Client) CreatePlugin( + ctx context.Context, + group, name, version string, + config map[string]any, + tags []string, +) (*generator.PluginInfo, error) +``` + +## 2.4 Ключевые решения (ADR) + +### Decision: CLI-фреймворк — cobra + +- **Context:** Нужен фреймворк для вложенных подкоманд: `epctl plugins build`, `epctl plugins list` +- **Options:** (A) stdlib `flag` + ручной роутинг, (B) cobra, (C) urfave/cli +- **Decision:** cobra (B) +- **Rationale:** Де-факто стандарт для Go CLI. Нативная поддержка вложенных подкоманд, help generation, completion. Пользователь подтвердил в explore-фазе +- **Consequences:** Новая зависимость `github.com/spf13/cobra` в `go.mod` + +### Decision: Отдельный пакет `internal/epctl/` для CLI-логики + +- **Context:** Где размещать логику команд — в `cmd/epctl/` или в отдельном internal-пакете? +- **Options:** (A) Всё в `cmd/epctl/main.go`, (B) `internal/epctl/` с тонким `cmd/epctl/main.go` +- **Decision:** (B) — `internal/epctl/` +- **Rationale:** Тестируемость: логику `PluginBuilder`, `PathFilter`, `Printer` можно юнит-тестировать без запуска cobra. `cmd/epctl/main.go` остаётся 10-строчным entry point +- **Consequences:** Больше файлов, но каждый фокусированный и тестируемый + +### Decision: Параллельная сборка через errgroup с контролируемым прерыванием + +- **Context:** Legacy builder использует `errgroup.SetLimit(3)` и всегда `return nil`. Нужен режим fail-fast и `--continue-on-error` +- **Options:** (A) Всегда fail-fast, (B) Всегда continue, (C) Флаг `--continue-on-error` +- **Decision:** (C) — по умолчанию fail-fast, с флагом для продолжения +- **Rationale:** Fail-fast безопаснее для CI/CD (обнаруживаем проблему быстро). Continue полезен для локальной разработки (собираем что можем) +- **Consequences:** `PluginBuilder` принимает `continueOnError bool`. При fail-fast используется `context.WithCancel` для остановки других горутин + +### Decision: Shared-пакеты для переиспользования между сервером и CLI + +- **Context:** `Printer` и `Config` нужны и серверу, и CLI. Типы конфига (`config`, `server`, `ports`, etc.) сейчас в `cmd/main.go` (package `main`), сервер не валидирует конфиг при старте +- **Options:** (A) Дублировать типы в `internal/epctl/`, (B) Вынести в shared internal-пакеты, (C) Импортировать из `cmd/easyp/` (невозможно — package main) +- **Decision:** (B) — `internal/config/` и `internal/output/` +- **Rationale:** Единый источник правды. Сервер вызывает `cfg.Validate()` при старте (fail-fast при невалидном конфиге). CLI переиспользует тот же `Printer` что доступен серверу. Нет дублирования +- **Consequences:** Рефакторинг `cmd/easyp/main.go` — типы переезжают в `internal/config/config.go`, `Printer` живёт в `internal/output/printer.go`. Сервер добавляет вызов `cfg.Validate()` в `start()` до инициализации компонентов + +### Decision: Backward compatibility — немедленная замена bash-скриптов + +- **Context:** Оставить скрипты для переходного периода или удалить сразу? +- **Options:** (A) Оставить как fallback, (B) Удалить сразу +- **Decision:** (B) — удалить сразу (пользователь подтвердил) +- **Rationale:** Два способа делать одно и то же — source of confusion. Taskfile обновляется одновременно +- **Consequences:** Breaking change для операторов, использующих скрипты напрямую. Mitigation: документация в CHANGELOG + +## 2.5 Модели данных + +```go +// [NEW] internal/config/config.go +// Shared-пакет: используется сервером и CLI. +// Перенос типов из cmd/main.go + добавление Validate() и LoadAndValidate(). + +type Config struct { + Server Server `env:", prefix=SERVER_" yaml:"server"` + DB DBConfig `env:", prefix=DB_" yaml:"db"` + Registry RegistryConfig `env:", prefix=REGISTRY_" yaml:"registry"` + Telemetry TelemetryConfig `env:", prefix=TELEMETRY_" yaml:"telemetry"` + WorkerPool WorkerPoolConfig `env:", prefix=WORKER_POOL_" yaml:"worker_pool"` + License LicenseConfig `env:", prefix=LICENSE_" yaml:"license"` + RateLimit RateLimitConfig `env:", prefix=RATE_LIMIT_" yaml:"rate_limit"` +} + +// Validate выполняет структурную валидацию конфигурации. +// Используется: +// - сервером при старте (cmd/easyp/main.go) +// - CLI командой (epctl config validate) +func (c *Config) Validate() error + +// LoadAndValidate загружает YAML из файла, парсит с KnownFields(true), +// возвращает Config, warnings (unknown fields), error. +// Используется в epctl config validate. +func LoadAndValidate(path string) (*Config, []string, error) +``` + +```go +// [NEW] internal/epctl/builder.go +// PluginConfig, VersionEntry, BuildJob, BuildResult, BuildSummary — +// определены в §2.3 Components. +``` + +## 2.6 Тестирование + +> **Deferred (v2).** Тесты будут добавлены отдельным этапом. Текущий скоуп — только реализация функциональности. + +**Project Commands (для ручной верификации):** + +| Действие | Команда | +|----------|---------| +| Build (server) | `go build -o easyp ./cmd/easyp/` | +| Build (CLI) | `go build -o epctl ./cmd/epctl/` | +| Lint | `golangci-lint run ./...` | + diff --git a/.spec/features/epctl-cli/approved/explore.md b/.spec/features/epctl-cli/approved/explore.md new file mode 100644 index 0000000..e55bbc1 --- /dev/null +++ b/.spec/features/epctl-cli/approved/explore.md @@ -0,0 +1,243 @@ +# Exploration: epctl CLI + +## Намерение + +Создать CLI-утилиту `epctl` — отдельный бинарник для управления сервисом EasyP. Текущее состояние: вся операционная работа (сборка плагинов, регистрация, диагностика) выполняется через разрозненные bash-скрипты (`build-plugins.sh`, `register-plugins.sh`), Taskfile-таски, и требует внешних инструментов (`grpcurl`, `curl`). Это greenfield-разработка нового бинарника рядом с существующим сервером. + +Мотивация: +- Унифицировать операционные инструменты в одном бинарнике +- Убрать зависимость от `grpcurl` и bash-скриптов +- Портировать legacy builder ([.spec/legacy_builder/main.go](file:///Users/zergslaw/Projects/easyp/service/.spec/legacy_builder/main.go)) в продакшн-качество +- Обеспечить кроссплатформенность (bash-скрипты не работают нативно на Windows) + +## Исследование + +### Текущая структура entry points + +Проект имеет один основной бинарник и один smoke-test: + +``` +cmd/ + main.go # Сервер — gRPC/HTTP, 472 строки + mcp-smoke/main.go # MCP smoke test client, 157 строк +``` + +Сервер запускается как `go run ./cmd/main.go -cfg config.yml`. Имя бинарника (`filepath.Base(os.Args[0])`) используется как namespace для метрик Prometheus ([cmd/main.go:121](file:///Users/zergslaw/Projects/easyp/service/cmd/main.go#L121)): + +```go +appName := filepath.Base(os.Args[0]) +``` + +Это означает, что при переименовании бинарника (`easyp` → `epctl`) namespace метрик изменится. Решение обсуждено с пользователем: захардкодить `const serviceNamespace = "easyp"` для метрик, чтобы отвязать от имени бинарника. + +### Bash-скрипты, подлежащие замене + +**[build-plugins.sh](file:///Users/zergslaw/Projects/easyp/service/build-plugins.sh)** (45 строк): +- Итерирует `registry/` → ищет Dockerfiles +- Парсит путь `registry/{group}/{name}/{version}/Dockerfile` +- Запускает `docker build --output` → извлекает бинарник в `plugins/` +- Нет параллелизма, нет кэширования, нет прогресс-трекера + +**[register-plugins.sh](file:///Users/zergslaw/Projects/easyp/service/register-plugins.sh)** (70 строк): +- Итерирует `plugins/` → ищет бинарники +- Вызывает `grpcurl` → gRPC `CreatePlugin` API +- Генерирует JSON config с путём к бинарнику +- Зависит от внешнего `grpcurl` + +### Legacy builder + +[.spec/legacy_builder/main.go](file:///Users/zergslaw/Projects/easyp/service/.spec/legacy_builder/main.go) (303 строки) — Go-реализация сборки плагинов с хорошими идеями: +- Читает `plugin.yaml` (не Dockerfiles напрямую) +- Параллельная сборка через `errgroup` с `SetLimit(3)` +- `Tracker` — прогресс-бар с отслеживанием stalled builds +- Кэширование: `needsBuild()` проверяет наличие бинарника +- Запись `build.log` для диагностики ошибок + +Проблемы legacy builder: +- Жёстко захардкожен на `apple/swift:v1.25.2` (строки 168-176) +- Standalone `main.go`, не интегрирован в проект +- Нет CLI-фреймворка (flag parsing минимальный) +- Нет тестов + +### Формат plugin.yaml + +В `registry/` у каждого плагина есть `plugin.yaml` + `Dockerfile`: + +``` +registry/ + grpc/go/ + plugin.yaml # binary: protoc-gen-go-grpc, versions: [v1.6.2, v1.5.1, ...] + Dockerfile + apple/swift/ + plugin.yaml # binary: protoc-gen-swift, versions: [v1.38.0, ...] + Dockerfile +``` + +Найдено **80 plugin.yaml** файлов и **80 Dockerfiles**. Формат YAML: + +```yaml +binary: protoc-gen-go-grpc # имя бинарника +description: "..." # опционально +source_url: "..." # опционально +build_args: # опционально, доп. docker build args + KEY: value +versions: + - v1.6.2 + - v1.5.1 + # или расширенный формат: + - version: v1.0.0 +``` + +### SDK клиент + +[sdk/](file:///Users/zergslaw/Projects/easyp/service/sdk) уже содержит полноценный Go-клиент для gRPC API: +- `client.go` — `Client` с functional options +- `retry.go` — retry с backoff +- `health.go` — health check +- `filter.go` — client-side plugin filtering +- `interceptors.go` — gRPC interceptors + +SDK можно использовать в `epctl` для `plugins list` и `plugins register`. + +### Конфигурация сервера + +Конфиг валидация ([config.yml](file:///Users/zergslaw/Projects/easyp/service/config.yml), [config.local.yml](file:///Users/zergslaw/Projects/easyp/service/config.local.yml)) — YAML-файл с 30+ параметрами. Сейчас валидация происходит только при запуске сервера (fail-fast). Отдельная `config validate` команда позволит ловить проблемы без запуска. + +### Taskfile.yml + +[Taskfile.yml](file:///Users/zergslaw/Projects/easyp/service/Taskfile.yml) — 96 строк, содержит таски: +- `up` / `up-minimal` / `down` — docker-compose +- `build-plugins` → вызывает `build-plugins.sh` +- `register-plugins` → вызывает `register-plugins.sh` +- `run` / `setup` — полный цикл +- `run-local` — `go run ./cmd/main.go` +- `generate` / `generate-local` — `easyp generate` + +После создания `epctl` Taskfile будет обновлён для вызова `epctl` вместо bash-скриптов. + +## Build Tooling + +- **Оркестратор:** Taskfile v3 ([Taskfile.yml](file:///Users/zergslaw/Projects/easyp/service/Taskfile.yml)) +- **Тесты:** `go test ./...` +- **Сборка:** `go build -o easyp ./cmd/main.go` (сервер), `go build -o epctl ./cmd/epctl/main.go` (CLI — новый) +- **Линтер:** `golangci-lint run ./...` ([.golangci.yml](file:///Users/zergslaw/Projects/easyp/service/.golangci.yml)) +- **Кодоген:** `easyp --cfg easyp.yaml generate` (proto → Go stubs + MCP bindings) +- **Источник:** `Taskfile.yml`, `build-plugins.sh`, `register-plugins.sh` + +## Рассмотренные варианты + +### Вариант A: Единый бинарник с подкомандами + +Текущий `cmd/main.go` превращается в multi-command CLI: `easyp serve`, `easyp plugins build`, и т.д. + +**Плюсы:** +- Один бинарник для сборки и дистрибуции +- Docker image содержит всё + +**Минусы:** +- Namespace метрик ломается (имя бинарника = namespace) +- Docker image раздувается CLI-зависимостями (cobra, docker SDK) +- `plugins build` тянет Docker-зависимости в серверный бинарник +- Нарушает принцип разделения ответственности сервер/клиент + +### Вариант B: Два отдельных бинарника (K8s-стиль) + +``` +cmd/ + easyp/main.go # Сервер (текущий main.go, минимальные изменения) + epctl/main.go # CLI-утилита (новый) +``` + +**Плюсы:** +- Чистое разделение: сервер остаётся лёгким, CLI — отдельно +- Docker image содержит только `easyp`, без CLI overhead +- Namespace метрик не меняется +- `epctl` можно распространять отдельно (оператор ставит на ноутбук) +- Существующий `cmd/main.go` почти не меняется +- Индустриальный стандарт (kubectl/kube-apiserver, docker/dockerd, etcdctl/etcd) + +**Минусы:** +- Два бинарника для сборки/распространения (но GoReleaser уже поддерживает multiple builds) + +### Вариант C: Подкоманды в оригинальном easyp CLI + +Вложить `plugins build/register/list` и `config validate` в оригинальный `easyp` CLI (другой репозиторий). + +**Плюсы:** +- Единая точка входа для разработчика + +**Минусы:** +- `plugins build` нуждается в доступе к `registry/`, Dockerfiles — это серверная инфраструктура +- `plugins register` генерирует `config.command` с серверными путями вида `/plugins/grpc/go/v1.5.1/plugin` +- `config validate` валидирует серверный конфиг, не `easyp.yaml` +- `serve` — физически невозможно перенести +- Разные циклы релиза и зависимости + +Обсуждено с пользователем и **отвергнуто** — команды тесно связаны с серверной инфраструктурой. + +## Ограничения и риски + +- **Docker-зависимость для `plugins build`:** команда вызывает `docker build`, Docker daemon должен быть запущен. Это ожидаемо для серверного оператора. +- **GoReleaser:** нужно обновить [.goreleaser.yaml](file:///Users/zergslaw/Projects/easyp/service/.goreleaser.yaml) для сборки двух бинарников. +- **Dockerfile:** нужно обновить [Dockerfile](file:///Users/zergslaw/Projects/easyp/service/Dockerfile) — переименовать `cmd/main.go` → `cmd/easyp/main.go`. +- **Backward compatibility:** bash-скрипты и Taskfile продолжат работать (пока), но постепенно заменяются на `epctl`. +- **`mcp-smoke/`:** оставляем как есть или переносим в `epctl` подкоманду в будущем. + +## Рекомендуемое направление + +**Вариант B — два бинарника**. Обсуждено и одобрено пользователем. + +Структура: +``` +cmd/ + easyp/main.go # Сервер (переезд из cmd/main.go) + epctl/main.go # CLI-утилита (новый) +``` + +CLI-команды `epctl`: +1. **`epctl plugins build`** — сборка плагинов из `registry/` (порт legacy builder) +2. **`epctl plugins register`** — регистрация плагинов через SDK (замена register-plugins.sh + grpcurl) +3. **`epctl plugins list`** — список плагинов через SDK +4. **`epctl config validate`** — валидация серверного YAML-конфига + +CLI-фреймворк: стандартная библиотека (`flag` + ручной роутинг подкоманд) или `cobra`. [ASSUMPTION: используем cobra — де-факто стандарт для Go CLI, хорошо поддерживает вложенные подкоманды (`plugins build`, `plugins list`)] + +## Границы скоупа + +**Must-have (v1):** +- Перенос `cmd/main.go` → `cmd/easyp/main.go` с захардкоженным namespace +- Новый `cmd/epctl/main.go` с CLI-каркасом +- `epctl plugins build` — порт legacy builder с поддержкой всех `plugin.yaml` +- `epctl plugins register --addr host:port` — регистрация через SDK +- `epctl plugins list --addr host:port` — список плагинов через SDK +- `epctl config validate ` — валидация YAML-конфига +- Обновление Dockerfile +- Обновление Taskfile.yml + +**Deferred (v2):** +- Обновление `.goreleaser.yaml` для двух бинарников +- `epctl plugins inspect ` — детальная информация о плагине +- `epctl serve` — прокси к запуску сервера (удобство) +- Перенос `mcp-smoke` в `epctl mcp smoke` +- `epctl migrate` — управление миграциями (status/up/down) +- `epctl health` — проверка здоровья сервиса +- Удаление bash-скриптов (после стабилизации epctl) + +**Needs spike:** +- Нет + +## Допущения и открытые вопросы + +[ASSUMPTION: CLI-фреймворк — cobra. Альтернатива: stdlib `flag` + ручной роутинг, но для вложенных подкоманд (`plugins build`, `plugins list`) cobra значительно удобнее] + +[ASSUMPTION: `epctl plugins register` использует SDK из `sdk/` для gRPC-вызовов. SDK уже содержит retry, health check, interceptors] + +[ASSUMPTION: `epctl plugins build` вызывает `docker` через `exec.Command`, как legacy builder. Альтернатива — Docker SDK (Go library), но exec проще и достаточен] + +[ASSUMPTION: `cmd/easyp/main.go` — минимальные изменения: переезд файла + `const serviceNamespace = "easyp"` вместо `filepath.Base(os.Args[0])`] + +**Открытые вопросы:** + +1. **Формат вывода `plugins list`:** plain text таблица, JSON, или оба (с флагом `--output json`)? +2. **`plugins register` — UX:** команда регистрирует ВСЕ плагины из `plugins/` или можно указать конкретный (`epctl plugins register grpc/go:v1.5.1`)? +3. **Cobra vs stdlib:** пользователь предпочитает cobra или минималистичный подход без внешних зависимостей? diff --git a/.spec/features/epctl-cli/approved/requirements.md b/.spec/features/epctl-cli/approved/requirements.md new file mode 100644 index 0000000..9a344f5 --- /dev/null +++ b/.spec/features/epctl-cli/approved/requirements.md @@ -0,0 +1,146 @@ +# epctl CLI — Requirements + +**Status:** Draft +**Author:** AI agent +**Date:** 2026-05-29 + +## Обзор + +Создание CLI-утилиты `epctl` как отдельного бинарника для управления сервисом EasyP. Перенос серверного entry point из `cmd/main.go` в `cmd/easyp/main.go` с фиксацией namespace метрик. Замена bash-скриптов (`build-plugins.sh`, `register-plugins.sh`) на подкоманды `epctl`. Обновление Taskfile и Dockerfile. + +## Глоссарий + +| Термин | Определение | Code Artifact | +|--------|-------------|---------------| +| `path-filter` | Необязательный аргумент вида `group` или `group/name`, ограничивающий операцию до подмножества плагинов | `cmd/epctl/` | +| `plugin.yaml` | YAML-файл в `registry/{group}/{name}/` с описанием плагина: binary, versions, build_args | `registry/` | +| `PluginConfig` | Конфигурация плагина, передаваемая в gRPC `CreatePlugin` (command, env, timeout) | `internal/adapters/registry/registry.go` | +| `serviceNamespace` | Константа с именем сервиса для метрик Prometheus, не зависящая от имени бинарника | `cmd/easyp/main.go` | + +## User Stories + +- As a **сервисный оператор**, I want собирать плагины одной командой (`epctl plugins build`) so that я не зависел от bash-скриптов и Docker знания. +- As a **сервисный оператор**, I want регистрировать плагины без `grpcurl` (`epctl plugins register`) so that не нужны внешние инструменты. +- As a **сервисный оператор**, I want видеть список плагинов в терминале (`epctl plugins list`) so that я могу быстро проверить состояние сервиса. +- As a **сервисный оператор**, I want валидировать конфиг до запуска сервера (`epctl config validate`) so that ловить ошибки конфигурации заранее. +- As a **CI/CD pipeline**, I want получать JSON-вывод от всех команд (`--output json`) so that парсить результат программно. + +## Требования + +### 1. Структура проекта + +**REQ-1.1** WHEN проект собирается командой `go build ./cmd/easyp/`, the system SHALL создать серверный бинарник из `cmd/easyp/main.go`. + +**REQ-1.2** WHEN проект собирается командой `go build ./cmd/epctl/`, the system SHALL создать CLI-бинарник из `cmd/epctl/main.go`. + +**REQ-1.3** WHEN сервер запускается из `cmd/easyp/main.go`, the system SHALL использовать константу `serviceNamespace = "easyp"` для namespace всех Prometheus-метрик вместо `filepath.Base(os.Args[0])`. + +**REQ-1.4** WHEN `cmd/main.go` (старый entry point) запрашивается в сборке, the system SHALL не компилироваться — файл удалён и перемещён в `cmd/easyp/main.go`. + +### 2. epctl plugins build + +**REQ-2.1** WHEN оператор запускает `epctl plugins build`, the system SHALL обнаружить все `plugin.yaml` файлы в директории `registry/`, прочитать их, и собрать каждую версию каждого плагина через `docker build --output` в директорию `plugins/{group}/{name}/{version}/`. + +**REQ-2.2** WHEN оператор запускает `epctl plugins build `, the system SHALL ограничить сборку плагинами, чей путь `{group}` или `{group}/{name}` совпадает с указанным фильтром. + +**REQ-2.3** WHEN бинарник плагина уже существует в `plugins/{group}/{name}/{version}/` (файл `plugin` или файл с именем из поля `binary`), the system SHALL пропустить сборку этой версии и сообщить о кэш-попадании. + +**REQ-2.4** WHEN Docker build завершается ошибкой и флаг `--continue-on-error` не установлен, the system SHALL прекратить сборку оставшихся плагинов и завершиться с exit code 1. + +**REQ-2.5** WHEN Docker build завершается ошибкой и флаг `--continue-on-error` установлен, the system SHALL продолжить сборку оставшихся плагинов и по завершению вывести суммарный отчёт (сколько собрано, сколько ошибок, сколько из кэша). + +**REQ-2.6** WHEN сборка завершена, the system SHALL вывести суммарный отчёт: количество собранных, ошибочных и кэшированных плагинов, общее время выполнения. + +**REQ-2.7** WHEN `plugin.yaml` содержит поле `build_args`, the system SHALL передать каждую пару ключ-значение как `--build-arg KEY=VALUE` при вызове `docker build`. + +**REQ-2.8** WHEN выходной бинарник не называется `plugin`, the system SHALL переименовать его в `plugin` и установить права `0755`. + +**REQ-2.9** WHEN в директории `registry/` не найдено ни одного `plugin.yaml`, the system SHALL вывести предупреждение и завершиться с exit code 0. + +### 3. epctl plugins register + +**REQ-3.1** WHEN оператор запускает `epctl plugins register --addr `, the system SHALL обнаружить все собранные плагины в директории `plugins/` и зарегистрировать каждый через gRPC `CreatePlugin` API. + +**REQ-3.2** WHEN оператор запускает `epctl plugins register --addr `, the system SHALL ограничить регистрацию плагинами, чей путь `{group}` или `{group}/{name}` совпадает с указанным фильтром. + +**REQ-3.3** WHEN gRPC `CreatePlugin` возвращает ошибку `AlreadyExists`, the system SHALL пропустить этот плагин и вывести предупреждение, не прерывая регистрацию остальных. + +**REQ-3.4** WHEN gRPC `CreatePlugin` возвращает иную ошибку, the system SHALL прекратить регистрацию и завершиться с exit code 1, выводя описание ошибки. + +**REQ-3.5** WHEN регистрация плагина выполняется, the system SHALL сформировать `PluginConfig` с полем `command`, содержащим путь к бинарнику вида `{plugins_prefix}/{group}/{name}/{version}/plugin`. + +**REQ-3.6** WHEN флаг `--addr` не указан, the system SHALL использовать значение по умолчанию `localhost:8080`. + +**REQ-3.7** WHEN флаг `--plugins-prefix` указан, the system SHALL использовать его значение вместо стандартного `/plugins` при формировании `PluginConfig.command`. + +**REQ-3.8** WHEN регистрация завершена, the system SHALL вывести суммарный отчёт: количество зарегистрированных, пропущенных (already exists), и общее количество обработанных плагинов. + +### 4. epctl plugins list + +**REQ-4.1** WHEN оператор запускает `epctl plugins list --addr `, the system SHALL получить список плагинов через gRPC `Plugins` API и вывести таблицу с колонками: group, name, version, tags, created_at. + +**REQ-4.2** WHEN флаг `--group`, `--name`, `--version`, или `--tags` указан, the system SHALL передать соответствующие фильтры в gRPC `Plugins` API. + +**REQ-4.3** WHEN gRPC `Plugins` API возвращает пустой список, the system SHALL вывести сообщение "No plugins found" и завершиться с exit code 0. + +**REQ-4.4** WHEN gRPC-соединение не устанавливается, the system SHALL вывести сообщение об ошибке с адресом сервера и завершиться с exit code 1. + +**REQ-4.5** WHEN флаг `--addr` не указан, the system SHALL использовать значение по умолчанию `localhost:8080`. + +### 5. epctl config validate + +**REQ-5.1** WHEN оператор запускает `epctl config validate `, the system SHALL прочитать YAML-файл, десериализовать его в структуру `config` из `cmd/easyp/main.go`, и сообщить об успехе или ошибках. + +**REQ-5.2** WHEN YAML-файл не существует или не читается, the system SHALL вывести сообщение об ошибке с путём к файлу и завершиться с exit code 1. + +**REQ-5.3** WHEN YAML содержит неизвестные поля, the system SHALL вывести предупреждение с перечислением неизвестных полей. + +**REQ-5.4** WHEN YAML валиден структурно, the system SHALL вывести "Config is valid" и завершиться с exit code 0. + +**REQ-5.5** WHEN аргумент `` не указан, the system SHALL вывести usage-справку и завершиться с exit code 1. + +### 6. Глобальные флаги и вывод + +**REQ-6.1** WHEN флаг `--output json` указан для любой команды, the system SHALL выводить результат в формате JSON вместо human-readable текста. + +**REQ-6.2** WHEN флаг `--output` не указан или равен `text`, the system SHALL выводить результат в human-readable формате (таблицы, текстовые сообщения). + +**REQ-6.3** WHEN команда завершается ошибкой и `--output json` установлен, the system SHALL вывести JSON-объект с полем `error` и завершиться с exit code 1. + +### 7. Инфраструктура + +**REQ-7.1** WHEN `Taskfile.yml` таска `build-plugins` выполняется, the system SHALL вызывать `epctl plugins build` вместо `./build-plugins.sh`. + +**REQ-7.2** WHEN `Taskfile.yml` таска `register-plugins` выполняется, the system SHALL вызывать `epctl plugins register` вместо `./register-plugins.sh`. + +**REQ-7.3** WHEN Docker image собирается из `Dockerfile`, the system SHALL собирать `cmd/easyp/main.go` в бинарник `/easyp` и использовать его как `ENTRYPOINT`. + +**REQ-7.4** WHEN файлы `build-plugins.sh` и `register-plugins.sh` присутствуют в репозитории, the system SHALL их удалить — их функциональность полностью заменена `epctl`. + +## Топологический порядок + +``` +REQ-1.1 → REQ-1.3 → REQ-7.3 +Причина: серверный бинарник должен переехать и собираться (1.1), + namespace должен быть зафиксирован (1.3), + затем Dockerfile обновляется (7.3). + +REQ-1.2 → REQ-2.* → REQ-3.* → REQ-7.1, REQ-7.2 +Причина: CLI-бинарник должен компилироваться (1.2), + затем реализуются команды build (2.*) и register (3.*), + затем Taskfile обновляется (7.1, 7.2). + +REQ-4.* (независимый — может выполняться параллельно с REQ-2-3) +REQ-5.* (независимый — может выполняться параллельно с REQ-2-3) +REQ-6.* (зависит от всех команд — реализуется сквозно) +``` + +## Команды верификации + +| Действие | Команда | Источник | +|----------|---------|----------| +| Test | `go test ./...` | Taskfile.yml | +| Build (server) | `go build -o easyp ./cmd/easyp/` | Taskfile.yml | +| Build (CLI) | `go build -o epctl ./cmd/epctl/` | новый | +| Lint | `golangci-lint run ./...` | .golangci.yml | +| Generate | `easyp --cfg easyp.yaml generate` | Taskfile.yml | diff --git a/.spec/features/epctl-cli/approved/task-plan.md b/.spec/features/epctl-cli/approved/task-plan.md new file mode 100644 index 0000000..387cf9c --- /dev/null +++ b/.spec/features/epctl-cli/approved/task-plan.md @@ -0,0 +1,414 @@ +# epctl CLI — Task Plan + +## Преамбула + +**Work Type:** Pure feature + Migration (переезд сервера + новые команды CLI) + +**Test Style Source:** Deferred (v2) +- Тесты не входят в текущий скоуп по решению пользователя + +**Commands:** + +| Action | Command | Source | +|--------|---------|--------| +| Build (server) | `go build -o easyp ./cmd/easyp/` | design.md §2.6 | +| Build (CLI) | `go build -o epctl ./cmd/epctl/` | design.md §2.6 | +| Lint | `golangci-lint run ./...` | design.md §2.6 | + +## Coverage Matrix + +| Requirement | Task(s) | Описание | +|-------------|---------|----------| +| REQ-1.1 | T-2 | Серверный бинарник из `cmd/easyp/` | +| REQ-1.2 | T-4 | CLI-бинарник из `cmd/epctl/` | +| REQ-1.3 | T-2 | Namespace метрик = "easyp" | +| REQ-1.4 | T-2 | Удаление `cmd/main.go` | +| REQ-2.1 | T-3, T-5 | Сборка плагинов из registry/ | +| REQ-2.2 | T-3, T-5 | Path-filter для build | +| REQ-2.3 | T-3 | Кэш-попадание | +| REQ-2.4 | T-3 | Fail-fast по умолчанию | +| REQ-2.5 | T-3, T-5 | --continue-on-error | +| REQ-2.6 | T-3, T-5 | Суммарный отчёт | +| REQ-2.7 | T-3 | build_args | +| REQ-2.8 | T-3 | Переименование бинарника | +| REQ-2.9 | T-3 | Пустой registry | +| REQ-3.1 | T-6 | Регистрация через gRPC | +| REQ-3.2 | T-6 | Path-filter для register | +| REQ-3.3 | T-6 | AlreadyExists → skip | +| REQ-3.4 | T-6 | Иная ошибка → stop | +| REQ-3.5 | T-6 | Формирование config.command | +| REQ-3.6 | T-6 | Default addr localhost:8080 | +| REQ-3.7 | T-6 | --plugins-prefix | +| REQ-3.8 | T-6 | Суммарный отчёт register | +| REQ-4.1 | T-6 | List через gRPC | +| REQ-4.2 | T-6 | Флаги фильтрации list | +| REQ-4.3 | T-6 | Пустой список → exit 0 | +| REQ-4.4 | T-6 | Connection failure | +| REQ-4.5 | T-6 | Default addr list | +| REQ-5.1 | T-1, T-7 | Валидация YAML | +| REQ-5.2 | T-7 | Файл не существует | +| REQ-5.3 | T-1, T-7 | Unknown fields warning | +| REQ-5.4 | T-1, T-7 | Валидный YAML → exit 0 | +| REQ-5.5 | T-7 | Нет аргумента → usage | +| REQ-6.1 | T-1 | --output json | +| REQ-6.2 | T-1 | --output text (default) | +| REQ-6.3 | T-1 | JSON error | +| REQ-7.1 | T-8 | Taskfile → epctl plugins build | +| REQ-7.2 | T-8 | Taskfile → epctl plugins register | +| REQ-7.3 | T-8 | Dockerfile → cmd/easyp/ | +| REQ-7.4 | T-8 | Удаление bash-скриптов | + +--- + +## T-1: Создать shared-пакеты `internal/config/` и `internal/output/` + +*_Requirements: REQ-5.1, REQ-5.3, REQ-5.4, REQ-6.1, REQ-6.2, REQ-6.3_* +*_Complexity: standard_* + +**GOAL:** Создать переиспользуемые пакеты для конфигурации и вывода, доступные и серверу, и CLI. + +### T-1.1 Создать `internal/config/config.go` + +Создать файл `internal/config/config.go` с package `config`. +- Перенести типы из `cmd/main.go` (строки 51-98): `Config`, `Server`, `Ports`, `DBConfig`, `RegistryConfig`, `TelemetryConfig`, `WorkerPoolConfig`, `LicenseConfig`, `RateLimitConfig`. +- Экспортировать все типы (заглавные буквы уже есть, подтипы переименовать: `config` → `Config`, `server` → `Server`, `ports` → `Ports`, `dbConfig` → `DBConfig`, `registryConfig` → `RegistryConfig`, `telemetryConfig` → `TelemetryConfig`, `workerPoolConfig` → `WorkerPoolConfig`, `licenseConfig` → `LicenseConfig`, `rateLimitConfig` → `RateLimitConfig`). +- Сохранить все `env` и `yaml` теги. +- Добавить метод `Validate() error` — проверяет обязательные поля (GRPC port, DB driver). +- Добавить функцию `LoadAndValidate(path string) (*Config, []string, error)` — читает YAML с `yaml.NewDecoder` + `KnownFields(true)`, возвращает config, warnings (unknown fields), ошибку. +- Импорты: `time`, `gopkg.in/yaml.v3`, `os`. + +### T-1.2 Создать `internal/output/printer.go` + +Создать файл `internal/output/printer.go` с package `output`. +- Определить тип `Printer` с полями `format string` и `w io.Writer`. +- Реализовать `NewPrinter(format string, w io.Writer) *Printer` — валидирует format ("text"/"json"), default "text". +- Реализовать `Table(headers []string, rows [][]string) error`: + - При format "text" — вывод `text/tabwriter` с табуляцией. + - При format "json" — вывод `[]map[string]string` где ключи = headers. +- Реализовать `JSON(v any) error` — `json.NewEncoder(w).Encode(v)`. +- Реализовать `Message(msg string) error`: + - При format "text" — `fmt.Fprintln(w, msg)`. + - При format "json" — `{"message": msg}`. +- Реализовать `Error(err error) error`: + - При format "json" — `{"error": err.Error()}`. + - При format "text" — `fmt.Fprintf(w, "Error: %s\n", err)`. +- Импорты: `encoding/json`, `fmt`, `io`, `text/tabwriter`. + +### T-1.3 Верификация + +Запустить `go build ./internal/config/ && go build ./internal/output/`. +CRITICAL: оба пакета должны компилироваться независимо. + +--- + +## T-2: Перенести серверный entry point в `cmd/easyp/main.go` + +*_Requirements: REQ-1.1, REQ-1.3, REQ-1.4_* +*_Complexity: standard_* + +**GOAL:** Перенести `cmd/main.go` → `cmd/easyp/main.go`, зафиксировать namespace метрик, добавить валидацию конфига при старте. + +### T-2.1 Создать `cmd/easyp/main.go` + +Скопировать `cmd/main.go` в `cmd/easyp/main.go`. +- Заменить все ссылки на локальные типы конфигурации (`config`, `server`, `ports` и т.д.) на импорты из `internal/config`: + - `cfg := config{}` → `cfg := config.Config{}` + - Все поля доступны через `cfg.Server.Port.GRPC` и т.д. +- Добавить `const serviceNamespace = "easyp"`. +- Заменить строку 121 `appName := filepath.Base(os.Args[0])` на `appName := serviceNamespace`. +- Удалить `"path/filepath"` из импортов (если больше нигде не используется). +- Добавить вызов `cfg.Validate()` в функцию `start()` сразу после десериализации конфига (после строки envconfig, ~строка 155), перед инициализацией компонентов: + ``` + if err := cfg.Validate(); err != nil { + return fmt.Errorf("config validation: %w", err) + } + ``` +- Добавить `"github.com/easyp-tech/service/internal/config"` в импорты. + +### T-2.2 Удалить `cmd/main.go` + +Удалить файл `cmd/main.go`. + +### T-2.3 Верификация + +Запустить `go build -o easyp ./cmd/easyp/`. +CRITICAL: бинарник должен собраться без ошибок. + +--- + +## T-3: Реализовать `PluginBuilder` и `PathFilter` + +*_Requirements: REQ-2.1, REQ-2.2, REQ-2.3, REQ-2.4, REQ-2.5, REQ-2.6, REQ-2.7, REQ-2.8, REQ-2.9_* +*_Complexity: complex_* + +**GOAL:** Портировать логику сборки плагинов из legacy builder в переиспользуемый пакет. + +### T-3.1 Создать `internal/epctl/path_filter.go` + +Создать файл с package `epctl`. +- Определить `PathFilter` struct: `Group string`, `Name string`. +- `ParsePathFilter(arg string) (PathFilter, error)`: + - Пустая строка → пустой filter (без ограничений). + - "group" → `PathFilter{Group: "group"}`. + - "group/name" → `PathFilter{Group: "group", Name: "name"}`. + - Более 1 слеша → `fmt.Errorf("invalid filter: %q, expected 'group' or 'group/name'", arg)`. +- `Match(group, name string) bool`: + - Если Group == "" → true (пустой фильтр). + - Если Group != group → false. + - Если Name == "" → true (group match). + - Если Name != name → false. + - Иначе → true. + +### T-3.2 Создать `internal/epctl/builder.go` + +Создать файл с package `epctl`. +- Определить типы: `PluginConfig`, `VersionEntry`, `BuildJob`, `BuildResult`, `BuildSummary` (как в design §2.3). +- Реализовать `UnmarshalYAML` для `VersionEntry` — поддержка строкового ("v1.0.0") и map-формата (`version: "v1.0.0"`). +- `NewPluginBuilder(registryDir, outputDir string, parallelism int, continueOnError bool) *PluginBuilder`. +- `DiscoverJobs(filter *PathFilter) ([]BuildJob, error)`: + - `filepath.WalkDir(registryDir)` ищет `plugin.yaml` файлы. + - Парсит каждый YAML в `PluginConfig`. + - Извлекает group/name из пути (parent dirs). + - Для каждой версии создаёт `BuildJob` с `OutputDir = outputDir/group/name/version/`. + - Применяет `filter.Match(group, name)`. + - Если 0 jobs — возвращает пустой slice, nil (не ошибка). +- `needsBuild(job BuildJob) bool`: + - Проверяет `os.Stat(job.OutputDir + "/plugin")`. + - Если файл существует → false (cached). + - Иначе → true. +- `buildDockerArgs(job BuildJob) []string`: + - Формирует: `["build", "--output", "type=local,dest=" + tmpDir, "--build-arg", "VERSION=" + job.Version, "--build-arg", "BINARY_NAME=" + job.Binary]`. + - Для каждого `key, value` в `job.BuildArgs` добавляет `"--build-arg", key + "=" + value`. + - Добавляет `job.PluginDir` (контекст сборки). +- `Build(ctx context.Context, jobs []BuildJob) (*BuildSummary, error)`: + - Использует `errgroup.Group` с `SetLimit(parallelism)`. + - Для каждого job: если `needsBuild` = false → cached; иначе `exec.CommandContext(ctx, "docker", buildDockerArgs(job)...)`. + - После Docker build: `os.Rename(tmpDir/binaryName, job.OutputDir/plugin)`, `os.Chmod(0o755)`. + - При ошибке: если `continueOnError` = true → записать в results, продолжить; иначе → `cancel()` контекста и вернуть ошибку. + - Собрать `BuildSummary` с подсчётом Total/Built/Failed/Cached. +- Импорты: `context`, `fmt`, `os`, `os/exec`, `path/filepath`, `sync`, `time`, `golang.org/x/sync/errgroup`, `gopkg.in/yaml.v3`. + +### T-3.3 Верификация + +Запустить `go build ./internal/epctl/`. +CRITICAL: пакет должен компилироваться без ошибок. + +--- + +## T-4: Создать CLI-каркас `cmd/epctl/` и cobra root + +*_Requirements: REQ-1.2_* +*_Complexity: mechanical_* + +**GOAL:** Создать entry point для epctl и cobra root command с глобальным `--output` флагом. + +### T-4.1 Создать `internal/epctl/root.go` + +Создать файл с package `epctl`. +- Определить `func NewRootCmd() *cobra.Command`: + - `Use: "epctl"`, `Short: "EasyP Service control utility"`. + - Persistent flag `--output` (string, default "text", valid: "text", "json"). + - Добавить subcommands: `newPluginsCmd()`, `newConfigCmd()` (пока заглушки, будут реализованы в T-5, T-6, T-7). +- Определить `func Execute()`: + - `cmd := NewRootCmd()`, `if err := cmd.Execute(); err != nil { os.Exit(1) }`. +- Определить `func getPrinter(cmd *cobra.Command) *output.Printer`: + - Получить значение `--output` из persistent flags. + - Вернуть `output.NewPrinter(format, os.Stdout)`. +- Импорты: `os`, `github.com/spf13/cobra`, `github.com/easyp-tech/service/internal/output`. + +### T-4.2 Создать `cmd/epctl/main.go` + +Создать файл с package `main`. +- Импортировать `github.com/easyp-tech/service/internal/epctl`. +- `func main() { epctl.Execute() }`. + +### T-4.3 Добавить зависимость cobra + +Запустить `go get github.com/spf13/cobra`. + +### T-4.4 Верификация + +Запустить `go build -o epctl ./cmd/epctl/`. +Запустить `./epctl --help`. +CRITICAL: должен вывести help с подкомандами `plugins` и `config`. + +--- + +## T-5: Реализовать команду `plugins build` + +*_Requirements: REQ-2.1, REQ-2.2, REQ-2.3, REQ-2.4, REQ-2.5, REQ-2.6_* +*_Complexity: standard_* + +**GOAL:** Подключить PluginBuilder к cobra command. + +### T-5.1 Создать `internal/epctl/plugins_build.go` + +Создать файл с package `epctl`. +- `func newPluginsBuildCmd() *cobra.Command`: + - `Use: "build [filter]"`, `Short: "Build plugins from registry"`. + - `Args: cobra.MaximumNArgs(1)`. + - Flags: `--registry-dir` (string, default "registry"), `--output-dir` (string, default "plugins"), `--parallelism` (int, default 3), `--continue-on-error` (bool, default false). + - `RunE`: + 1. Получить `printer` через `getPrinter(cmd)`. + 2. Парсить filter: если args[0] есть → `ParsePathFilter(args[0])`. + 3. Создать `NewPluginBuilder(registryDir, outputDir, parallelism, continueOnError)`. + 4. `jobs, err := builder.DiscoverJobs(&filter)`. + 5. Если 0 jobs → `printer.Message("No plugins found in registry")`, return nil. + 6. `summary, err := builder.Build(cmd.Context(), jobs)`. + 7. Вывести summary через printer: Table или JSON в зависимости от формата. + 8. Если `summary.Failed > 0 && !continueOnError` → return error. + +### T-5.2 Подключить к plugins subcommand в `root.go` + +В `root.go` добавить `newPluginsCmd()` функцию: +- `Use: "plugins"`, `Short: "Plugin management commands"`. +- `AddCommand(newPluginsBuildCmd())`. + +NOTE: `register` и `list` будут добавлены в T-6. + +### T-5.3 Верификация + +Запустить `go build -o epctl ./cmd/epctl/ && ./epctl plugins build --help`. +CRITICAL: должен показать help с флагами `--registry-dir`, `--output-dir`, `--parallelism`, `--continue-on-error`. + +--- + +## T-6: Реализовать команды `plugins register` и `plugins list` + +*_Requirements: REQ-3.1, REQ-3.2, REQ-3.3, REQ-3.4, REQ-3.5, REQ-3.6, REQ-3.7, REQ-3.8, REQ-4.1, REQ-4.2, REQ-4.3, REQ-4.4, REQ-4.5_* +*_Complexity: standard_* + +**GOAL:** Реализовать gRPC-взаимодействие с сервером через SDK. + +### T-6.1 Добавить `CreatePlugin` в SDK + +В `sdk/client.go` добавить метод: +```go +func (c *Client) CreatePlugin( + ctx context.Context, + group, name, version string, + pluginConfig map[string]any, + tags []string, +) (*generator.PluginInfo, error) +``` +- Внутри: конвертировать `pluginConfig` в `*structpb.Struct` через `structpb.NewStruct(pluginConfig)`. +- Вызвать `c.genClient.CreatePlugin(ctx, &generator.CreatePluginRequest{...})`. +- Вернуть `resp.GetPlugin(), nil`. +- Использовать `c.withTimeout(ctx, c.cfg.createPluginTimeout)` — добавить `createPluginTimeout` в SDK config с default 30s. + +### T-6.2 Создать `internal/epctl/plugins_register.go` + +Создать файл с package `epctl`. +- `func newPluginsRegisterCmd() *cobra.Command`: + - `Use: "register [filter]"`, `Short: "Register plugins in EasyP service"`. + - `Args: cobra.MaximumNArgs(1)`. + - Flags: `--addr` (string, default "localhost:8080"), `--plugins-dir` (string, default "plugins"), `--plugins-prefix` (string, default "/plugins"). + - `RunE`: + 1. Парсить filter. + 2. Сканировать `plugins-dir`: найти все директории вида `{group}/{name}/{version}/plugin`. + 3. Применить filter. + 4. Создать `sdk.NewClient(addr, sdk.WithInsecure())`. + 5. Для каждого плагина: + - Сформировать `config = map[string]any{"command": pluginsPrefix + "/" + group + "/" + name + "/" + version + "/plugin"}`. + - Вызвать `client.CreatePlugin(ctx, group, name, version, config, nil)`. + - Если `status.Code(err) == codes.AlreadyExists` → warning, skipped++, continue. + - Если другая ошибка → вернуть ошибку с exit code 1. + - Иначе → registered++. + 6. Вывести отчёт через printer: registered, skipped, total. + +### T-6.3 Создать `internal/epctl/plugins_list.go` + +Создать файл с package `epctl`. +- `func newPluginsListCmd() *cobra.Command`: + - `Use: "list"`, `Short: "List registered plugins"`. + - Flags: `--addr` (string, default "localhost:8080"), `--group`, `--name`, `--version`, `--tags` (string slice). + - `RunE`: + 1. Создать `sdk.NewClient(addr, sdk.WithInsecure())`. + 2. Сформировать `sdk.PluginFilter{Group, Name, Version, Tags}`. + 3. `plugins, err := client.ListPlugins(ctx, filter)`. + 4. Если connection error → `fmt.Errorf("cannot connect to %s: %w", addr, err)`. + 5. Если 0 плагинов → `printer.Message("No plugins found")`, exit 0. + 6. Вывести таблицу через printer: group, name, version, tags, created_at. + +### T-6.4 Подключить к plugins subcommand + +В `root.go` обновить `newPluginsCmd()`: добавить `newPluginsRegisterCmd()` и `newPluginsListCmd()`. + +### T-6.5 Верификация + +Запустить `go build -o epctl ./cmd/epctl/`. +Запустить `./epctl plugins register --help` и `./epctl plugins list --help`. +CRITICAL: обе команды должны показывать корректный help. + +--- + +## T-7: Реализовать команду `config validate` + +*_Requirements: REQ-5.1, REQ-5.2, REQ-5.3, REQ-5.4, REQ-5.5_* +*_Complexity: mechanical_* + +**GOAL:** Подключить валидацию конфига из shared-пакета к cobra command. + +### T-7.1 Создать `internal/epctl/config_validate.go` + +Создать файл с package `epctl`. +- `func newConfigValidateCmd() *cobra.Command`: + - `Use: "validate "`, `Short: "Validate service config YAML"`. + - `Args: cobra.ExactArgs(1)`. + - `RunE`: + 1. Получить printer. + 2. `cfg, warnings, err := config.LoadAndValidate(args[0])`. + 3. Если err != nil → `printer.Error(err)`, return err. + 4. Если len(warnings) > 0 → вывести каждый warning. + 5. Если `cfg.Validate()` != nil → вывести ошибки. + 6. Иначе → `printer.Message("Config is valid")`. + +### T-7.2 Подключить к config subcommand + +В `root.go` добавить `newConfigCmd()`: +- `Use: "config"`, `Short: "Configuration management"`. +- `AddCommand(newConfigValidateCmd())`. + +### T-7.3 Верификация + +Запустить `go build -o epctl ./cmd/epctl/ && ./epctl config validate --help`. +CRITICAL: должен показать help с обязательным аргументом ``. + +--- + +## T-8: Обновить инфраструктуру (Dockerfile, Taskfile, удалить скрипты) + +*_Requirements: REQ-7.1, REQ-7.2, REQ-7.3, REQ-7.4_* +*_Complexity: mechanical_* + +**GOAL:** Обновить сборочную инфраструктуру и удалить замещённые bash-скрипты. + +### T-8.1 Обновить `Dockerfile` + +В `Dockerfile` заменить: +- `go build -o /easyp ./cmd/` → `go build -o /easyp ./cmd/easyp/`. +- Убедиться что `COPY` и `ENTRYPOINT` указывают на `/easyp`. + +### T-8.2 Обновить `Taskfile.yml` + +- Таска `build-plugins`: заменить `./build-plugins.sh` на `go run ./cmd/epctl plugins build`. +- Таска `register-plugins`: заменить `./register-plugins.sh` на `go run ./cmd/epctl plugins register`. + +### T-8.3 Удалить bash-скрипты + +Удалить файлы: +- `build-plugins.sh` +- `register-plugins.sh` + +### T-8.4 Верификация (GATE) + +Запустить: +1. `go build -o easyp ./cmd/easyp/` — серверный бинарник. +2. `go build -o epctl ./cmd/epctl/` — CLI бинарник. +3. `./epctl --help` — help выводится. +4. `./epctl plugins build --help` — help выводится. +5. `./epctl plugins register --help` — help выводится. +6. `./epctl plugins list --help` — help выводится. +7. `./epctl config validate --help` — help выводится. + +CRITICAL: все 7 проверок должны пройти без ошибок. diff --git a/.spec/features/epctl-cli/design.md b/.spec/features/epctl-cli/design.md new file mode 100644 index 0000000..fca536d --- /dev/null +++ b/.spec/features/epctl-cli/design.md @@ -0,0 +1,340 @@ +# epctl CLI — Design + +## 2.1 Обзор + +Реализация CLI-утилиты `epctl` как отдельного бинарника. Задача делится на 5 логических частей: + +1. **Рефакторинг структуры** — перенос `cmd/main.go` → `cmd/easyp/main.go`, фиксация namespace +2. **CLI-каркас** — `cmd/epctl/main.go` на cobra с глобальными флагами (`--output`) +3. **Команды plugins** — `build`, `register`, `list` с path-filter и JSON-выводом +4. **Команда config validate** — структурная валидация YAML +5. **Инфраструктура** — обновление Dockerfile, Taskfile, удаление bash-скриптов + +## 2.2 Архитектура + +```mermaid +graph TD + subgraph "cmd/ (entry points)" + EASYP["cmd/easyp/main.go"] + EPCTL["cmd/epctl/main.go"] + end + + subgraph "internal/epctl/ (CLI логика)" + ROOT["root.go — cobra root cmd"] + BUILD["plugins_build.go"] + REGISTER["plugins_register.go"] + LIST["plugins_list.go"] + VALIDATE["config_validate.go"] + PFILTER["path_filter.go"] + BUILDER["builder.go — PluginBuilder"] + end + + subgraph "internal/ (shared packages)" + OUTPUT["output/ — Printer"] + CONFIG["config/ — Config + Validate"] + end + + subgraph "Existing (unchanged)" + SDK["sdk/ — Client"] + API["api/generator/v1/"] + REGISTRY_DIR["registry/ (plugin.yaml)"] + PLUGINS_DIR["plugins/ (binaries)"] + end + + EASYP --> CONFIG + EPCTL --> ROOT + ROOT --> BUILD + ROOT --> REGISTER + ROOT --> LIST + ROOT --> VALIDATE + + BUILD --> BUILDER + BUILD --> PFILTER + BUILD --> OUTPUT + BUILDER --> REGISTRY_DIR + BUILDER -->|docker build| PLUGINS_DIR + + REGISTER --> SDK + REGISTER --> PFILTER + REGISTER --> OUTPUT + REGISTER --> PLUGINS_DIR + LIST --> SDK + LIST --> OUTPUT + + VALIDATE --> CONFIG + VALIDATE --> OUTPUT + + SDK --> API + + style EPCTL fill:#90EE90 + style ROOT fill:#90EE90 + style BUILD fill:#90EE90 + style REGISTER fill:#90EE90 + style LIST fill:#90EE90 + style VALIDATE fill:#90EE90 + style OUTPUT fill:#90EE90 + style PFILTER fill:#90EE90 + style BUILDER fill:#90EE90 + style CONFIG fill:#90EE90 + style EASYP fill:#FFD700 + style SDK fill:#FFD700 +``` + +**Порядок реализации:** +1. `internal/config/` — shared config types + `Validate()` (REQ-5.*) +2. `internal/output/` — shared `Printer` (REQ-6.*) +3. `cmd/easyp/main.go` — переезд + namespace + вызов `cfg.Validate()` при старте (REQ-1.*) +4. `internal/epctl/path_filter.go` + `builder.go` — инфраструктурный слой +5. `cmd/epctl/` + `internal/epctl/root.go` — CLI-каркас +6. `plugins build` → `plugins register` → `plugins list` → `config validate` +7. SDK: `CreatePlugin` метод +8. Dockerfile, Taskfile, удаление скриптов + +## 2.3 Компоненты и интерфейсы + +### Файлы, требующие изменений + +| Файл | Тип | Описание | +|------|-----|----------| +| `internal/config/config.go` | `[NEW]` | Shared-пакет: типы Config, Server, Ports, etc. + метод `Validate()`. Используется и сервером, и `epctl` | +| `internal/output/printer.go` | `[NEW]` | Shared-пакет: `Printer` — абстракция вывода text/JSON. Используется `epctl`, может использоваться сервером | +| `cmd/easyp/main.go` | `[NEW]` | Перенос из `cmd/main.go`. `const serviceNamespace = "easyp"`, импорт `internal/config`, вызов `cfg.Validate()` при старте | +| `cmd/main.go` | `[DELETED]` | Перемещён в `cmd/easyp/main.go` | +| `cmd/epctl/main.go` | `[NEW]` | Entry point CLI: `epctl.Execute()` | +| `internal/epctl/root.go` | `[NEW]` | Cobra root command, глобальные флаги `--output` | +| `internal/epctl/path_filter.go` | `[NEW]` | `PathFilter` — фильтрация по `group` или `group/name` | +| `internal/epctl/builder.go` | `[NEW]` | `PluginBuilder` — сборка плагинов (порт legacy builder) | +| `internal/epctl/plugins_build.go` | `[NEW]` | Cobra command `plugins build` | +| `internal/epctl/plugins_register.go` | `[NEW]` | Cobra command `plugins register` | +| `internal/epctl/plugins_list.go` | `[NEW]` | Cobra command `plugins list` | +| `internal/epctl/config_validate.go` | `[NEW]` | Cobra command `config validate` | +| `sdk/client.go` | `[MODIFIED]` | Добавление метода `CreatePlugin(ctx, group, name, version string, config map[string]any, tags []string)` | +| `Dockerfile` | `[MODIFIED]` | `go build -o easyp ./cmd/easyp/`, путь COPY | +| `Taskfile.yml` | `[MODIFIED]` | Замена `./build-plugins.sh` → `go run ./cmd/epctl plugins build`, `./register-plugins.sh` → `go run ./cmd/epctl plugins register` | +| `build-plugins.sh` | `[DELETED]` | Заменён на `epctl plugins build` | +| `register-plugins.sh` | `[DELETED]` | Заменён на `epctl plugins register` | + +### Файлы, НЕ требующие изменений + +| Файл | Причина | +|------|---------| +| `api/generator/v1/generator.proto` | Protobuf API не изменяется | +| `api/generator/v1/*.pb.go` | Сгенерированный код, не трогаем | +| `internal/core/` | Domain-логика не затрагивается | +| `internal/api/` | gRPC handlers не изменяются | +| `internal/adapters/registry/` | Адаптер не изменяется | +| `cmd/mcp-smoke/main.go` | Остаётся как есть (deferred) | +| `sdk/filter.go`, `sdk/health.go`, `sdk/retry.go` | Существующий SDK код не меняется | +| `registry/` | Plugin.yaml и Dockerfiles не изменяются | +| `config.yml`, `config.local.yml` | Формат конфига не изменяется | + +### Интерфейсы + +```go +// internal/output/printer.go +// Shared-пакет: используется и epctl, и потенциально сервером +// (например, для CLI-вывода health/status информации). + +// Printer абстрагирует вывод результатов в text или JSON формат. +type Printer struct { + format string // "text" | "json" + w io.Writer +} + +func NewPrinter(format string, w io.Writer) *Printer +func (p *Printer) Table(headers []string, rows [][]string) error +func (p *Printer) JSON(v any) error +func (p *Printer) Message(msg string) error +func (p *Printer) Error(err error) error +``` + +```go +// internal/epctl/path_filter.go + +// PathFilter фильтрует плагины по group и опционально name. +type PathFilter struct { + Group string // Обязательное (если фильтр задан) + Name string // Опциональное +} + +// ParsePathFilter парсит "group" или "group/name" в PathFilter. +func ParsePathFilter(arg string) (PathFilter, error) + +// Match проверяет, подходит ли plugin path под фильтр. +func (f PathFilter) Match(group, name string) bool +``` + +```go +// internal/config/config.go +// Shared-пакет: используется cmd/easyp/ (сервер вызывает Validate при старте) +// и internal/epctl/config_validate.go (команда epctl config validate). + +// Validate выполняет структурную валидацию конфигурации. +// Вызывается: +// 1. При старте сервера (cmd/easyp/main.go → start() → cfg.Validate()) +// 2. Из CLI команды (epctl config validate ) +func (c *Config) Validate() error + +// LoadAndValidate загружает YAML из файла и валидирует. +// Используется в epctl config validate. +func LoadAndValidate(path string) (*Config, []string, error) +// Возвращает: конфиг, список warnings (unknown fields), ошибку. +``` + +```go +// internal/epctl/builder.go + +// PluginConfig — конфигурация плагина из plugin.yaml. +type PluginConfig struct { + Binary string `yaml:"binary"` + BuildArgs map[string]string `yaml:"build_args,omitempty"` + Versions []VersionEntry `yaml:"versions"` +} + +// VersionEntry поддерживает строковый и map-формат версий. +type VersionEntry struct { + Version string +} + +// BuildJob представляет одну задачу сборки плагина. +type BuildJob struct { + Group, Name, Version, Binary string + BuildArgs map[string]string + PluginDir, OutputDir string +} + +// BuildResult — результат сборки одного плагина. +type BuildResult struct { + Job BuildJob + Success bool + Cached bool + Size string + Error error + Duration time.Duration +} + +// BuildSummary — итоговый отчёт сборки. +type BuildSummary struct { + Total, Built, Failed, Cached int + Elapsed time.Duration + Results []BuildResult +} + +// PluginBuilder собирает плагины из registry/. +type PluginBuilder struct { + registryDir string + outputDir string + parallelism int + continueOnError bool +} + +func NewPluginBuilder(registryDir, outputDir string, parallelism int, continueOnError bool) *PluginBuilder + +// DiscoverJobs находит все BuildJob из plugin.yaml файлов, применяя фильтр. +func (b *PluginBuilder) DiscoverJobs(filter *PathFilter) ([]BuildJob, error) + +// Build выполняет сборку всех jobs. +func (b *PluginBuilder) Build(ctx context.Context, jobs []BuildJob) (*BuildSummary, error) +``` + +```go +// sdk/client.go (дополнение) + +// CreatePlugin registers a new plugin in the service. +func (c *Client) CreatePlugin( + ctx context.Context, + group, name, version string, + config map[string]any, + tags []string, +) (*generator.PluginInfo, error) +``` + +## 2.4 Ключевые решения (ADR) + +### Decision: CLI-фреймворк — urfave/cli v3 + +- **Context:** Нужен фреймворк для вложенных подкоманд: `epctl plugins build`, `epctl plugins list` +- **Options:** (A) stdlib `flag` + ручной роутинг, (B) cobra, (C) urfave/cli v3 +- **Decision:** urfave/cli v3 (C) +- **Rationale:** Пользователь выбрал urfave/cli v3 во время имплементации. Нативная поддержка вложенных подкоманд, help generation, fluent API. Легче cobra, меньше зависимостей +- **Consequences:** Зависимость `github.com/urfave/cli/v3` в `go.mod` + +### Decision: Отдельный пакет `internal/epctl/` для CLI-логики + +- **Context:** Где размещать логику команд — в `cmd/epctl/` или в отдельном internal-пакете? +- **Options:** (A) Всё в `cmd/epctl/main.go`, (B) `internal/epctl/` с тонким `cmd/epctl/main.go` +- **Decision:** (B) — `internal/epctl/` +- **Rationale:** Тестируемость: логику `PluginBuilder`, `PathFilter`, `Printer` можно юнит-тестировать без запуска cobra. `cmd/epctl/main.go` остаётся 10-строчным entry point +- **Consequences:** Больше файлов, но каждый фокусированный и тестируемый + +### Decision: Параллельная сборка через errgroup с контролируемым прерыванием + +- **Context:** Legacy builder использует `errgroup.SetLimit(3)` и всегда `return nil`. Нужен режим fail-fast и `--continue-on-error` +- **Options:** (A) Всегда fail-fast, (B) Всегда continue, (C) Флаг `--continue-on-error` +- **Decision:** (C) — по умолчанию fail-fast, с флагом для продолжения +- **Rationale:** Fail-fast безопаснее для CI/CD (обнаруживаем проблему быстро). Continue полезен для локальной разработки (собираем что можем) +- **Consequences:** `PluginBuilder` принимает `continueOnError bool`. При fail-fast используется `context.WithCancel` для остановки других горутин + +### Decision: Shared-пакеты для переиспользования между сервером и CLI + +- **Context:** `Printer` и `Config` нужны и серверу, и CLI. Типы конфига (`config`, `server`, `ports`, etc.) сейчас в `cmd/main.go` (package `main`), сервер не валидирует конфиг при старте +- **Options:** (A) Дублировать типы в `internal/epctl/`, (B) Вынести в shared internal-пакеты, (C) Импортировать из `cmd/easyp/` (невозможно — package main) +- **Decision:** (B) — `internal/config/` и `internal/output/` +- **Rationale:** Единый источник правды. Сервер вызывает `cfg.Validate()` при старте (fail-fast при невалидном конфиге). CLI переиспользует тот же `Printer` что доступен серверу. Нет дублирования +- **Consequences:** Рефакторинг `cmd/easyp/main.go` — типы переезжают в `internal/config/config.go`, `Printer` живёт в `internal/output/printer.go`. Сервер добавляет вызов `cfg.Validate()` в `start()` до инициализации компонентов + +### Decision: Backward compatibility — немедленная замена bash-скриптов + +- **Context:** Оставить скрипты для переходного периода или удалить сразу? +- **Options:** (A) Оставить как fallback, (B) Удалить сразу +- **Decision:** (B) — удалить сразу (пользователь подтвердил) +- **Rationale:** Два способа делать одно и то же — source of confusion. Taskfile обновляется одновременно +- **Consequences:** Breaking change для операторов, использующих скрипты напрямую. Mitigation: документация в CHANGELOG + +## 2.5 Модели данных + +```go +// [NEW] internal/config/config.go +// Shared-пакет: используется сервером и CLI. +// Перенос типов из cmd/main.go + добавление Validate() и LoadAndValidate(). + +type Config struct { + Server Server `env:", prefix=SERVER_" yaml:"server"` + DB DBConfig `env:", prefix=DB_" yaml:"db"` + Registry RegistryConfig `env:", prefix=REGISTRY_" yaml:"registry"` + Telemetry TelemetryConfig `env:", prefix=TELEMETRY_" yaml:"telemetry"` + WorkerPool WorkerPoolConfig `env:", prefix=WORKER_POOL_" yaml:"worker_pool"` + License LicenseConfig `env:", prefix=LICENSE_" yaml:"license"` + RateLimit RateLimitConfig `env:", prefix=RATE_LIMIT_" yaml:"rate_limit"` +} + +// Validate выполняет структурную валидацию конфигурации. +// Используется: +// - сервером при старте (cmd/easyp/main.go) +// - CLI командой (epctl config validate) +func (c *Config) Validate() error + +// LoadAndValidate загружает YAML из файла, парсит с KnownFields(true), +// возвращает Config, warnings (unknown fields), error. +// Используется в epctl config validate. +func LoadAndValidate(path string) (*Config, []string, error) +``` + +```go +// [NEW] internal/epctl/builder.go +// PluginConfig, VersionEntry, BuildJob, BuildResult, BuildSummary — +// определены в §2.3 Components. +``` + +## 2.6 Тестирование + +> **Deferred (v2).** Тесты будут добавлены отдельным этапом. Текущий скоуп — только реализация функциональности. + +**Project Commands (для ручной верификации):** + +| Действие | Команда | +|----------|---------| +| Build (server) | `go build -o easyp ./cmd/easyp/` | +| Build (CLI) | `go build -o epctl ./cmd/epctl/` | +| Lint | `golangci-lint run ./...` | + diff --git a/.spec/features/epctl-cli/explore.md b/.spec/features/epctl-cli/explore.md new file mode 100644 index 0000000..e55bbc1 --- /dev/null +++ b/.spec/features/epctl-cli/explore.md @@ -0,0 +1,243 @@ +# Exploration: epctl CLI + +## Намерение + +Создать CLI-утилиту `epctl` — отдельный бинарник для управления сервисом EasyP. Текущее состояние: вся операционная работа (сборка плагинов, регистрация, диагностика) выполняется через разрозненные bash-скрипты (`build-plugins.sh`, `register-plugins.sh`), Taskfile-таски, и требует внешних инструментов (`grpcurl`, `curl`). Это greenfield-разработка нового бинарника рядом с существующим сервером. + +Мотивация: +- Унифицировать операционные инструменты в одном бинарнике +- Убрать зависимость от `grpcurl` и bash-скриптов +- Портировать legacy builder ([.spec/legacy_builder/main.go](file:///Users/zergslaw/Projects/easyp/service/.spec/legacy_builder/main.go)) в продакшн-качество +- Обеспечить кроссплатформенность (bash-скрипты не работают нативно на Windows) + +## Исследование + +### Текущая структура entry points + +Проект имеет один основной бинарник и один smoke-test: + +``` +cmd/ + main.go # Сервер — gRPC/HTTP, 472 строки + mcp-smoke/main.go # MCP smoke test client, 157 строк +``` + +Сервер запускается как `go run ./cmd/main.go -cfg config.yml`. Имя бинарника (`filepath.Base(os.Args[0])`) используется как namespace для метрик Prometheus ([cmd/main.go:121](file:///Users/zergslaw/Projects/easyp/service/cmd/main.go#L121)): + +```go +appName := filepath.Base(os.Args[0]) +``` + +Это означает, что при переименовании бинарника (`easyp` → `epctl`) namespace метрик изменится. Решение обсуждено с пользователем: захардкодить `const serviceNamespace = "easyp"` для метрик, чтобы отвязать от имени бинарника. + +### Bash-скрипты, подлежащие замене + +**[build-plugins.sh](file:///Users/zergslaw/Projects/easyp/service/build-plugins.sh)** (45 строк): +- Итерирует `registry/` → ищет Dockerfiles +- Парсит путь `registry/{group}/{name}/{version}/Dockerfile` +- Запускает `docker build --output` → извлекает бинарник в `plugins/` +- Нет параллелизма, нет кэширования, нет прогресс-трекера + +**[register-plugins.sh](file:///Users/zergslaw/Projects/easyp/service/register-plugins.sh)** (70 строк): +- Итерирует `plugins/` → ищет бинарники +- Вызывает `grpcurl` → gRPC `CreatePlugin` API +- Генерирует JSON config с путём к бинарнику +- Зависит от внешнего `grpcurl` + +### Legacy builder + +[.spec/legacy_builder/main.go](file:///Users/zergslaw/Projects/easyp/service/.spec/legacy_builder/main.go) (303 строки) — Go-реализация сборки плагинов с хорошими идеями: +- Читает `plugin.yaml` (не Dockerfiles напрямую) +- Параллельная сборка через `errgroup` с `SetLimit(3)` +- `Tracker` — прогресс-бар с отслеживанием stalled builds +- Кэширование: `needsBuild()` проверяет наличие бинарника +- Запись `build.log` для диагностики ошибок + +Проблемы legacy builder: +- Жёстко захардкожен на `apple/swift:v1.25.2` (строки 168-176) +- Standalone `main.go`, не интегрирован в проект +- Нет CLI-фреймворка (flag parsing минимальный) +- Нет тестов + +### Формат plugin.yaml + +В `registry/` у каждого плагина есть `plugin.yaml` + `Dockerfile`: + +``` +registry/ + grpc/go/ + plugin.yaml # binary: protoc-gen-go-grpc, versions: [v1.6.2, v1.5.1, ...] + Dockerfile + apple/swift/ + plugin.yaml # binary: protoc-gen-swift, versions: [v1.38.0, ...] + Dockerfile +``` + +Найдено **80 plugin.yaml** файлов и **80 Dockerfiles**. Формат YAML: + +```yaml +binary: protoc-gen-go-grpc # имя бинарника +description: "..." # опционально +source_url: "..." # опционально +build_args: # опционально, доп. docker build args + KEY: value +versions: + - v1.6.2 + - v1.5.1 + # или расширенный формат: + - version: v1.0.0 +``` + +### SDK клиент + +[sdk/](file:///Users/zergslaw/Projects/easyp/service/sdk) уже содержит полноценный Go-клиент для gRPC API: +- `client.go` — `Client` с functional options +- `retry.go` — retry с backoff +- `health.go` — health check +- `filter.go` — client-side plugin filtering +- `interceptors.go` — gRPC interceptors + +SDK можно использовать в `epctl` для `plugins list` и `plugins register`. + +### Конфигурация сервера + +Конфиг валидация ([config.yml](file:///Users/zergslaw/Projects/easyp/service/config.yml), [config.local.yml](file:///Users/zergslaw/Projects/easyp/service/config.local.yml)) — YAML-файл с 30+ параметрами. Сейчас валидация происходит только при запуске сервера (fail-fast). Отдельная `config validate` команда позволит ловить проблемы без запуска. + +### Taskfile.yml + +[Taskfile.yml](file:///Users/zergslaw/Projects/easyp/service/Taskfile.yml) — 96 строк, содержит таски: +- `up` / `up-minimal` / `down` — docker-compose +- `build-plugins` → вызывает `build-plugins.sh` +- `register-plugins` → вызывает `register-plugins.sh` +- `run` / `setup` — полный цикл +- `run-local` — `go run ./cmd/main.go` +- `generate` / `generate-local` — `easyp generate` + +После создания `epctl` Taskfile будет обновлён для вызова `epctl` вместо bash-скриптов. + +## Build Tooling + +- **Оркестратор:** Taskfile v3 ([Taskfile.yml](file:///Users/zergslaw/Projects/easyp/service/Taskfile.yml)) +- **Тесты:** `go test ./...` +- **Сборка:** `go build -o easyp ./cmd/main.go` (сервер), `go build -o epctl ./cmd/epctl/main.go` (CLI — новый) +- **Линтер:** `golangci-lint run ./...` ([.golangci.yml](file:///Users/zergslaw/Projects/easyp/service/.golangci.yml)) +- **Кодоген:** `easyp --cfg easyp.yaml generate` (proto → Go stubs + MCP bindings) +- **Источник:** `Taskfile.yml`, `build-plugins.sh`, `register-plugins.sh` + +## Рассмотренные варианты + +### Вариант A: Единый бинарник с подкомандами + +Текущий `cmd/main.go` превращается в multi-command CLI: `easyp serve`, `easyp plugins build`, и т.д. + +**Плюсы:** +- Один бинарник для сборки и дистрибуции +- Docker image содержит всё + +**Минусы:** +- Namespace метрик ломается (имя бинарника = namespace) +- Docker image раздувается CLI-зависимостями (cobra, docker SDK) +- `plugins build` тянет Docker-зависимости в серверный бинарник +- Нарушает принцип разделения ответственности сервер/клиент + +### Вариант B: Два отдельных бинарника (K8s-стиль) + +``` +cmd/ + easyp/main.go # Сервер (текущий main.go, минимальные изменения) + epctl/main.go # CLI-утилита (новый) +``` + +**Плюсы:** +- Чистое разделение: сервер остаётся лёгким, CLI — отдельно +- Docker image содержит только `easyp`, без CLI overhead +- Namespace метрик не меняется +- `epctl` можно распространять отдельно (оператор ставит на ноутбук) +- Существующий `cmd/main.go` почти не меняется +- Индустриальный стандарт (kubectl/kube-apiserver, docker/dockerd, etcdctl/etcd) + +**Минусы:** +- Два бинарника для сборки/распространения (но GoReleaser уже поддерживает multiple builds) + +### Вариант C: Подкоманды в оригинальном easyp CLI + +Вложить `plugins build/register/list` и `config validate` в оригинальный `easyp` CLI (другой репозиторий). + +**Плюсы:** +- Единая точка входа для разработчика + +**Минусы:** +- `plugins build` нуждается в доступе к `registry/`, Dockerfiles — это серверная инфраструктура +- `plugins register` генерирует `config.command` с серверными путями вида `/plugins/grpc/go/v1.5.1/plugin` +- `config validate` валидирует серверный конфиг, не `easyp.yaml` +- `serve` — физически невозможно перенести +- Разные циклы релиза и зависимости + +Обсуждено с пользователем и **отвергнуто** — команды тесно связаны с серверной инфраструктурой. + +## Ограничения и риски + +- **Docker-зависимость для `plugins build`:** команда вызывает `docker build`, Docker daemon должен быть запущен. Это ожидаемо для серверного оператора. +- **GoReleaser:** нужно обновить [.goreleaser.yaml](file:///Users/zergslaw/Projects/easyp/service/.goreleaser.yaml) для сборки двух бинарников. +- **Dockerfile:** нужно обновить [Dockerfile](file:///Users/zergslaw/Projects/easyp/service/Dockerfile) — переименовать `cmd/main.go` → `cmd/easyp/main.go`. +- **Backward compatibility:** bash-скрипты и Taskfile продолжат работать (пока), но постепенно заменяются на `epctl`. +- **`mcp-smoke/`:** оставляем как есть или переносим в `epctl` подкоманду в будущем. + +## Рекомендуемое направление + +**Вариант B — два бинарника**. Обсуждено и одобрено пользователем. + +Структура: +``` +cmd/ + easyp/main.go # Сервер (переезд из cmd/main.go) + epctl/main.go # CLI-утилита (новый) +``` + +CLI-команды `epctl`: +1. **`epctl plugins build`** — сборка плагинов из `registry/` (порт legacy builder) +2. **`epctl plugins register`** — регистрация плагинов через SDK (замена register-plugins.sh + grpcurl) +3. **`epctl plugins list`** — список плагинов через SDK +4. **`epctl config validate`** — валидация серверного YAML-конфига + +CLI-фреймворк: стандартная библиотека (`flag` + ручной роутинг подкоманд) или `cobra`. [ASSUMPTION: используем cobra — де-факто стандарт для Go CLI, хорошо поддерживает вложенные подкоманды (`plugins build`, `plugins list`)] + +## Границы скоупа + +**Must-have (v1):** +- Перенос `cmd/main.go` → `cmd/easyp/main.go` с захардкоженным namespace +- Новый `cmd/epctl/main.go` с CLI-каркасом +- `epctl plugins build` — порт legacy builder с поддержкой всех `plugin.yaml` +- `epctl plugins register --addr host:port` — регистрация через SDK +- `epctl plugins list --addr host:port` — список плагинов через SDK +- `epctl config validate ` — валидация YAML-конфига +- Обновление Dockerfile +- Обновление Taskfile.yml + +**Deferred (v2):** +- Обновление `.goreleaser.yaml` для двух бинарников +- `epctl plugins inspect ` — детальная информация о плагине +- `epctl serve` — прокси к запуску сервера (удобство) +- Перенос `mcp-smoke` в `epctl mcp smoke` +- `epctl migrate` — управление миграциями (status/up/down) +- `epctl health` — проверка здоровья сервиса +- Удаление bash-скриптов (после стабилизации epctl) + +**Needs spike:** +- Нет + +## Допущения и открытые вопросы + +[ASSUMPTION: CLI-фреймворк — cobra. Альтернатива: stdlib `flag` + ручной роутинг, но для вложенных подкоманд (`plugins build`, `plugins list`) cobra значительно удобнее] + +[ASSUMPTION: `epctl plugins register` использует SDK из `sdk/` для gRPC-вызовов. SDK уже содержит retry, health check, interceptors] + +[ASSUMPTION: `epctl plugins build` вызывает `docker` через `exec.Command`, как legacy builder. Альтернатива — Docker SDK (Go library), но exec проще и достаточен] + +[ASSUMPTION: `cmd/easyp/main.go` — минимальные изменения: переезд файла + `const serviceNamespace = "easyp"` вместо `filepath.Base(os.Args[0])`] + +**Открытые вопросы:** + +1. **Формат вывода `plugins list`:** plain text таблица, JSON, или оба (с флагом `--output json`)? +2. **`plugins register` — UX:** команда регистрирует ВСЕ плагины из `plugins/` или можно указать конкретный (`epctl plugins register grpc/go:v1.5.1`)? +3. **Cobra vs stdlib:** пользователь предпочитает cobra или минималистичный подход без внешних зависимостей? diff --git a/.spec/features/epctl-cli/pipeline.json b/.spec/features/epctl-cli/pipeline.json new file mode 100644 index 0000000..a397189 --- /dev/null +++ b/.spec/features/epctl-cli/pipeline.json @@ -0,0 +1,19 @@ +{ + "feature": "epctl-cli", + "phase": "implementation", + "created_at": "2026-05-28T20:20:22Z", + "current_artifact": null, + "history": [ + {"phase": "explore", "artifact": ".spec/features/epctl-cli/explore.md", "approved_at": "2026-05-29T08:29:20Z"}, + {"phase": "requirements", "artifact": ".spec/features/epctl-cli/requirements.md", "approved_at": "2026-05-29T09:17:04Z"}, + {"phase": "design", "artifact": ".spec/features/epctl-cli/design.md", "approved_at": "2026-05-29T09:45:11Z"}, + {"phase": "task-plan", "artifact": ".spec/features/epctl-cli/task-plan.md", "approved_at": "2026-05-29T09:49:20Z"} + ], + "review_base_commit": "62dc93d97230996d7fcd9770109430ec29b04f7a", + "branch": "feature/epctl-cli", + "worktree": null, + "last_completed_task": null, + "finish_action": null, + "finished_at": null, + "finish_base": null +} diff --git a/.spec/features/epctl-cli/pipeline.kv b/.spec/features/epctl-cli/pipeline.kv new file mode 100644 index 0000000..07dcf61 --- /dev/null +++ b/.spec/features/epctl-cli/pipeline.kv @@ -0,0 +1,19 @@ +feature=epctl-cli +phase=implementation +created_at=2026-05-28T20:20:22Z +current_artifact= +history_count=4 +branch=feature/epctl-cli +history_0_phase=explore +history_0_artifact=.spec/features/epctl-cli/explore.md +history_0_approved_at=2026-05-29T08:29:20Z +history_1_phase=requirements +history_1_artifact=.spec/features/epctl-cli/requirements.md +history_1_approved_at=2026-05-29T09:17:04Z +history_2_phase=design +history_2_artifact=.spec/features/epctl-cli/design.md +history_2_approved_at=2026-05-29T09:45:11Z +review_base_commit=62dc93d97230996d7fcd9770109430ec29b04f7a +history_3_phase=task-plan +history_3_artifact=.spec/features/epctl-cli/task-plan.md +history_3_approved_at=2026-05-29T09:49:20Z diff --git a/.spec/features/epctl-cli/requirements.md b/.spec/features/epctl-cli/requirements.md new file mode 100644 index 0000000..9a344f5 --- /dev/null +++ b/.spec/features/epctl-cli/requirements.md @@ -0,0 +1,146 @@ +# epctl CLI — Requirements + +**Status:** Draft +**Author:** AI agent +**Date:** 2026-05-29 + +## Обзор + +Создание CLI-утилиты `epctl` как отдельного бинарника для управления сервисом EasyP. Перенос серверного entry point из `cmd/main.go` в `cmd/easyp/main.go` с фиксацией namespace метрик. Замена bash-скриптов (`build-plugins.sh`, `register-plugins.sh`) на подкоманды `epctl`. Обновление Taskfile и Dockerfile. + +## Глоссарий + +| Термин | Определение | Code Artifact | +|--------|-------------|---------------| +| `path-filter` | Необязательный аргумент вида `group` или `group/name`, ограничивающий операцию до подмножества плагинов | `cmd/epctl/` | +| `plugin.yaml` | YAML-файл в `registry/{group}/{name}/` с описанием плагина: binary, versions, build_args | `registry/` | +| `PluginConfig` | Конфигурация плагина, передаваемая в gRPC `CreatePlugin` (command, env, timeout) | `internal/adapters/registry/registry.go` | +| `serviceNamespace` | Константа с именем сервиса для метрик Prometheus, не зависящая от имени бинарника | `cmd/easyp/main.go` | + +## User Stories + +- As a **сервисный оператор**, I want собирать плагины одной командой (`epctl plugins build`) so that я не зависел от bash-скриптов и Docker знания. +- As a **сервисный оператор**, I want регистрировать плагины без `grpcurl` (`epctl plugins register`) so that не нужны внешние инструменты. +- As a **сервисный оператор**, I want видеть список плагинов в терминале (`epctl plugins list`) so that я могу быстро проверить состояние сервиса. +- As a **сервисный оператор**, I want валидировать конфиг до запуска сервера (`epctl config validate`) so that ловить ошибки конфигурации заранее. +- As a **CI/CD pipeline**, I want получать JSON-вывод от всех команд (`--output json`) so that парсить результат программно. + +## Требования + +### 1. Структура проекта + +**REQ-1.1** WHEN проект собирается командой `go build ./cmd/easyp/`, the system SHALL создать серверный бинарник из `cmd/easyp/main.go`. + +**REQ-1.2** WHEN проект собирается командой `go build ./cmd/epctl/`, the system SHALL создать CLI-бинарник из `cmd/epctl/main.go`. + +**REQ-1.3** WHEN сервер запускается из `cmd/easyp/main.go`, the system SHALL использовать константу `serviceNamespace = "easyp"` для namespace всех Prometheus-метрик вместо `filepath.Base(os.Args[0])`. + +**REQ-1.4** WHEN `cmd/main.go` (старый entry point) запрашивается в сборке, the system SHALL не компилироваться — файл удалён и перемещён в `cmd/easyp/main.go`. + +### 2. epctl plugins build + +**REQ-2.1** WHEN оператор запускает `epctl plugins build`, the system SHALL обнаружить все `plugin.yaml` файлы в директории `registry/`, прочитать их, и собрать каждую версию каждого плагина через `docker build --output` в директорию `plugins/{group}/{name}/{version}/`. + +**REQ-2.2** WHEN оператор запускает `epctl plugins build `, the system SHALL ограничить сборку плагинами, чей путь `{group}` или `{group}/{name}` совпадает с указанным фильтром. + +**REQ-2.3** WHEN бинарник плагина уже существует в `plugins/{group}/{name}/{version}/` (файл `plugin` или файл с именем из поля `binary`), the system SHALL пропустить сборку этой версии и сообщить о кэш-попадании. + +**REQ-2.4** WHEN Docker build завершается ошибкой и флаг `--continue-on-error` не установлен, the system SHALL прекратить сборку оставшихся плагинов и завершиться с exit code 1. + +**REQ-2.5** WHEN Docker build завершается ошибкой и флаг `--continue-on-error` установлен, the system SHALL продолжить сборку оставшихся плагинов и по завершению вывести суммарный отчёт (сколько собрано, сколько ошибок, сколько из кэша). + +**REQ-2.6** WHEN сборка завершена, the system SHALL вывести суммарный отчёт: количество собранных, ошибочных и кэшированных плагинов, общее время выполнения. + +**REQ-2.7** WHEN `plugin.yaml` содержит поле `build_args`, the system SHALL передать каждую пару ключ-значение как `--build-arg KEY=VALUE` при вызове `docker build`. + +**REQ-2.8** WHEN выходной бинарник не называется `plugin`, the system SHALL переименовать его в `plugin` и установить права `0755`. + +**REQ-2.9** WHEN в директории `registry/` не найдено ни одного `plugin.yaml`, the system SHALL вывести предупреждение и завершиться с exit code 0. + +### 3. epctl plugins register + +**REQ-3.1** WHEN оператор запускает `epctl plugins register --addr `, the system SHALL обнаружить все собранные плагины в директории `plugins/` и зарегистрировать каждый через gRPC `CreatePlugin` API. + +**REQ-3.2** WHEN оператор запускает `epctl plugins register --addr `, the system SHALL ограничить регистрацию плагинами, чей путь `{group}` или `{group}/{name}` совпадает с указанным фильтром. + +**REQ-3.3** WHEN gRPC `CreatePlugin` возвращает ошибку `AlreadyExists`, the system SHALL пропустить этот плагин и вывести предупреждение, не прерывая регистрацию остальных. + +**REQ-3.4** WHEN gRPC `CreatePlugin` возвращает иную ошибку, the system SHALL прекратить регистрацию и завершиться с exit code 1, выводя описание ошибки. + +**REQ-3.5** WHEN регистрация плагина выполняется, the system SHALL сформировать `PluginConfig` с полем `command`, содержащим путь к бинарнику вида `{plugins_prefix}/{group}/{name}/{version}/plugin`. + +**REQ-3.6** WHEN флаг `--addr` не указан, the system SHALL использовать значение по умолчанию `localhost:8080`. + +**REQ-3.7** WHEN флаг `--plugins-prefix` указан, the system SHALL использовать его значение вместо стандартного `/plugins` при формировании `PluginConfig.command`. + +**REQ-3.8** WHEN регистрация завершена, the system SHALL вывести суммарный отчёт: количество зарегистрированных, пропущенных (already exists), и общее количество обработанных плагинов. + +### 4. epctl plugins list + +**REQ-4.1** WHEN оператор запускает `epctl plugins list --addr `, the system SHALL получить список плагинов через gRPC `Plugins` API и вывести таблицу с колонками: group, name, version, tags, created_at. + +**REQ-4.2** WHEN флаг `--group`, `--name`, `--version`, или `--tags` указан, the system SHALL передать соответствующие фильтры в gRPC `Plugins` API. + +**REQ-4.3** WHEN gRPC `Plugins` API возвращает пустой список, the system SHALL вывести сообщение "No plugins found" и завершиться с exit code 0. + +**REQ-4.4** WHEN gRPC-соединение не устанавливается, the system SHALL вывести сообщение об ошибке с адресом сервера и завершиться с exit code 1. + +**REQ-4.5** WHEN флаг `--addr` не указан, the system SHALL использовать значение по умолчанию `localhost:8080`. + +### 5. epctl config validate + +**REQ-5.1** WHEN оператор запускает `epctl config validate `, the system SHALL прочитать YAML-файл, десериализовать его в структуру `config` из `cmd/easyp/main.go`, и сообщить об успехе или ошибках. + +**REQ-5.2** WHEN YAML-файл не существует или не читается, the system SHALL вывести сообщение об ошибке с путём к файлу и завершиться с exit code 1. + +**REQ-5.3** WHEN YAML содержит неизвестные поля, the system SHALL вывести предупреждение с перечислением неизвестных полей. + +**REQ-5.4** WHEN YAML валиден структурно, the system SHALL вывести "Config is valid" и завершиться с exit code 0. + +**REQ-5.5** WHEN аргумент `` не указан, the system SHALL вывести usage-справку и завершиться с exit code 1. + +### 6. Глобальные флаги и вывод + +**REQ-6.1** WHEN флаг `--output json` указан для любой команды, the system SHALL выводить результат в формате JSON вместо human-readable текста. + +**REQ-6.2** WHEN флаг `--output` не указан или равен `text`, the system SHALL выводить результат в human-readable формате (таблицы, текстовые сообщения). + +**REQ-6.3** WHEN команда завершается ошибкой и `--output json` установлен, the system SHALL вывести JSON-объект с полем `error` и завершиться с exit code 1. + +### 7. Инфраструктура + +**REQ-7.1** WHEN `Taskfile.yml` таска `build-plugins` выполняется, the system SHALL вызывать `epctl plugins build` вместо `./build-plugins.sh`. + +**REQ-7.2** WHEN `Taskfile.yml` таска `register-plugins` выполняется, the system SHALL вызывать `epctl plugins register` вместо `./register-plugins.sh`. + +**REQ-7.3** WHEN Docker image собирается из `Dockerfile`, the system SHALL собирать `cmd/easyp/main.go` в бинарник `/easyp` и использовать его как `ENTRYPOINT`. + +**REQ-7.4** WHEN файлы `build-plugins.sh` и `register-plugins.sh` присутствуют в репозитории, the system SHALL их удалить — их функциональность полностью заменена `epctl`. + +## Топологический порядок + +``` +REQ-1.1 → REQ-1.3 → REQ-7.3 +Причина: серверный бинарник должен переехать и собираться (1.1), + namespace должен быть зафиксирован (1.3), + затем Dockerfile обновляется (7.3). + +REQ-1.2 → REQ-2.* → REQ-3.* → REQ-7.1, REQ-7.2 +Причина: CLI-бинарник должен компилироваться (1.2), + затем реализуются команды build (2.*) и register (3.*), + затем Taskfile обновляется (7.1, 7.2). + +REQ-4.* (независимый — может выполняться параллельно с REQ-2-3) +REQ-5.* (независимый — может выполняться параллельно с REQ-2-3) +REQ-6.* (зависит от всех команд — реализуется сквозно) +``` + +## Команды верификации + +| Действие | Команда | Источник | +|----------|---------|----------| +| Test | `go test ./...` | Taskfile.yml | +| Build (server) | `go build -o easyp ./cmd/easyp/` | Taskfile.yml | +| Build (CLI) | `go build -o epctl ./cmd/epctl/` | новый | +| Lint | `golangci-lint run ./...` | .golangci.yml | +| Generate | `easyp --cfg easyp.yaml generate` | Taskfile.yml | diff --git a/.spec/features/epctl-cli/task-plan.md b/.spec/features/epctl-cli/task-plan.md new file mode 100644 index 0000000..387cf9c --- /dev/null +++ b/.spec/features/epctl-cli/task-plan.md @@ -0,0 +1,414 @@ +# epctl CLI — Task Plan + +## Преамбула + +**Work Type:** Pure feature + Migration (переезд сервера + новые команды CLI) + +**Test Style Source:** Deferred (v2) +- Тесты не входят в текущий скоуп по решению пользователя + +**Commands:** + +| Action | Command | Source | +|--------|---------|--------| +| Build (server) | `go build -o easyp ./cmd/easyp/` | design.md §2.6 | +| Build (CLI) | `go build -o epctl ./cmd/epctl/` | design.md §2.6 | +| Lint | `golangci-lint run ./...` | design.md §2.6 | + +## Coverage Matrix + +| Requirement | Task(s) | Описание | +|-------------|---------|----------| +| REQ-1.1 | T-2 | Серверный бинарник из `cmd/easyp/` | +| REQ-1.2 | T-4 | CLI-бинарник из `cmd/epctl/` | +| REQ-1.3 | T-2 | Namespace метрик = "easyp" | +| REQ-1.4 | T-2 | Удаление `cmd/main.go` | +| REQ-2.1 | T-3, T-5 | Сборка плагинов из registry/ | +| REQ-2.2 | T-3, T-5 | Path-filter для build | +| REQ-2.3 | T-3 | Кэш-попадание | +| REQ-2.4 | T-3 | Fail-fast по умолчанию | +| REQ-2.5 | T-3, T-5 | --continue-on-error | +| REQ-2.6 | T-3, T-5 | Суммарный отчёт | +| REQ-2.7 | T-3 | build_args | +| REQ-2.8 | T-3 | Переименование бинарника | +| REQ-2.9 | T-3 | Пустой registry | +| REQ-3.1 | T-6 | Регистрация через gRPC | +| REQ-3.2 | T-6 | Path-filter для register | +| REQ-3.3 | T-6 | AlreadyExists → skip | +| REQ-3.4 | T-6 | Иная ошибка → stop | +| REQ-3.5 | T-6 | Формирование config.command | +| REQ-3.6 | T-6 | Default addr localhost:8080 | +| REQ-3.7 | T-6 | --plugins-prefix | +| REQ-3.8 | T-6 | Суммарный отчёт register | +| REQ-4.1 | T-6 | List через gRPC | +| REQ-4.2 | T-6 | Флаги фильтрации list | +| REQ-4.3 | T-6 | Пустой список → exit 0 | +| REQ-4.4 | T-6 | Connection failure | +| REQ-4.5 | T-6 | Default addr list | +| REQ-5.1 | T-1, T-7 | Валидация YAML | +| REQ-5.2 | T-7 | Файл не существует | +| REQ-5.3 | T-1, T-7 | Unknown fields warning | +| REQ-5.4 | T-1, T-7 | Валидный YAML → exit 0 | +| REQ-5.5 | T-7 | Нет аргумента → usage | +| REQ-6.1 | T-1 | --output json | +| REQ-6.2 | T-1 | --output text (default) | +| REQ-6.3 | T-1 | JSON error | +| REQ-7.1 | T-8 | Taskfile → epctl plugins build | +| REQ-7.2 | T-8 | Taskfile → epctl plugins register | +| REQ-7.3 | T-8 | Dockerfile → cmd/easyp/ | +| REQ-7.4 | T-8 | Удаление bash-скриптов | + +--- + +## T-1: Создать shared-пакеты `internal/config/` и `internal/output/` + +*_Requirements: REQ-5.1, REQ-5.3, REQ-5.4, REQ-6.1, REQ-6.2, REQ-6.3_* +*_Complexity: standard_* + +**GOAL:** Создать переиспользуемые пакеты для конфигурации и вывода, доступные и серверу, и CLI. + +### T-1.1 Создать `internal/config/config.go` + +Создать файл `internal/config/config.go` с package `config`. +- Перенести типы из `cmd/main.go` (строки 51-98): `Config`, `Server`, `Ports`, `DBConfig`, `RegistryConfig`, `TelemetryConfig`, `WorkerPoolConfig`, `LicenseConfig`, `RateLimitConfig`. +- Экспортировать все типы (заглавные буквы уже есть, подтипы переименовать: `config` → `Config`, `server` → `Server`, `ports` → `Ports`, `dbConfig` → `DBConfig`, `registryConfig` → `RegistryConfig`, `telemetryConfig` → `TelemetryConfig`, `workerPoolConfig` → `WorkerPoolConfig`, `licenseConfig` → `LicenseConfig`, `rateLimitConfig` → `RateLimitConfig`). +- Сохранить все `env` и `yaml` теги. +- Добавить метод `Validate() error` — проверяет обязательные поля (GRPC port, DB driver). +- Добавить функцию `LoadAndValidate(path string) (*Config, []string, error)` — читает YAML с `yaml.NewDecoder` + `KnownFields(true)`, возвращает config, warnings (unknown fields), ошибку. +- Импорты: `time`, `gopkg.in/yaml.v3`, `os`. + +### T-1.2 Создать `internal/output/printer.go` + +Создать файл `internal/output/printer.go` с package `output`. +- Определить тип `Printer` с полями `format string` и `w io.Writer`. +- Реализовать `NewPrinter(format string, w io.Writer) *Printer` — валидирует format ("text"/"json"), default "text". +- Реализовать `Table(headers []string, rows [][]string) error`: + - При format "text" — вывод `text/tabwriter` с табуляцией. + - При format "json" — вывод `[]map[string]string` где ключи = headers. +- Реализовать `JSON(v any) error` — `json.NewEncoder(w).Encode(v)`. +- Реализовать `Message(msg string) error`: + - При format "text" — `fmt.Fprintln(w, msg)`. + - При format "json" — `{"message": msg}`. +- Реализовать `Error(err error) error`: + - При format "json" — `{"error": err.Error()}`. + - При format "text" — `fmt.Fprintf(w, "Error: %s\n", err)`. +- Импорты: `encoding/json`, `fmt`, `io`, `text/tabwriter`. + +### T-1.3 Верификация + +Запустить `go build ./internal/config/ && go build ./internal/output/`. +CRITICAL: оба пакета должны компилироваться независимо. + +--- + +## T-2: Перенести серверный entry point в `cmd/easyp/main.go` + +*_Requirements: REQ-1.1, REQ-1.3, REQ-1.4_* +*_Complexity: standard_* + +**GOAL:** Перенести `cmd/main.go` → `cmd/easyp/main.go`, зафиксировать namespace метрик, добавить валидацию конфига при старте. + +### T-2.1 Создать `cmd/easyp/main.go` + +Скопировать `cmd/main.go` в `cmd/easyp/main.go`. +- Заменить все ссылки на локальные типы конфигурации (`config`, `server`, `ports` и т.д.) на импорты из `internal/config`: + - `cfg := config{}` → `cfg := config.Config{}` + - Все поля доступны через `cfg.Server.Port.GRPC` и т.д. +- Добавить `const serviceNamespace = "easyp"`. +- Заменить строку 121 `appName := filepath.Base(os.Args[0])` на `appName := serviceNamespace`. +- Удалить `"path/filepath"` из импортов (если больше нигде не используется). +- Добавить вызов `cfg.Validate()` в функцию `start()` сразу после десериализации конфига (после строки envconfig, ~строка 155), перед инициализацией компонентов: + ``` + if err := cfg.Validate(); err != nil { + return fmt.Errorf("config validation: %w", err) + } + ``` +- Добавить `"github.com/easyp-tech/service/internal/config"` в импорты. + +### T-2.2 Удалить `cmd/main.go` + +Удалить файл `cmd/main.go`. + +### T-2.3 Верификация + +Запустить `go build -o easyp ./cmd/easyp/`. +CRITICAL: бинарник должен собраться без ошибок. + +--- + +## T-3: Реализовать `PluginBuilder` и `PathFilter` + +*_Requirements: REQ-2.1, REQ-2.2, REQ-2.3, REQ-2.4, REQ-2.5, REQ-2.6, REQ-2.7, REQ-2.8, REQ-2.9_* +*_Complexity: complex_* + +**GOAL:** Портировать логику сборки плагинов из legacy builder в переиспользуемый пакет. + +### T-3.1 Создать `internal/epctl/path_filter.go` + +Создать файл с package `epctl`. +- Определить `PathFilter` struct: `Group string`, `Name string`. +- `ParsePathFilter(arg string) (PathFilter, error)`: + - Пустая строка → пустой filter (без ограничений). + - "group" → `PathFilter{Group: "group"}`. + - "group/name" → `PathFilter{Group: "group", Name: "name"}`. + - Более 1 слеша → `fmt.Errorf("invalid filter: %q, expected 'group' or 'group/name'", arg)`. +- `Match(group, name string) bool`: + - Если Group == "" → true (пустой фильтр). + - Если Group != group → false. + - Если Name == "" → true (group match). + - Если Name != name → false. + - Иначе → true. + +### T-3.2 Создать `internal/epctl/builder.go` + +Создать файл с package `epctl`. +- Определить типы: `PluginConfig`, `VersionEntry`, `BuildJob`, `BuildResult`, `BuildSummary` (как в design §2.3). +- Реализовать `UnmarshalYAML` для `VersionEntry` — поддержка строкового ("v1.0.0") и map-формата (`version: "v1.0.0"`). +- `NewPluginBuilder(registryDir, outputDir string, parallelism int, continueOnError bool) *PluginBuilder`. +- `DiscoverJobs(filter *PathFilter) ([]BuildJob, error)`: + - `filepath.WalkDir(registryDir)` ищет `plugin.yaml` файлы. + - Парсит каждый YAML в `PluginConfig`. + - Извлекает group/name из пути (parent dirs). + - Для каждой версии создаёт `BuildJob` с `OutputDir = outputDir/group/name/version/`. + - Применяет `filter.Match(group, name)`. + - Если 0 jobs — возвращает пустой slice, nil (не ошибка). +- `needsBuild(job BuildJob) bool`: + - Проверяет `os.Stat(job.OutputDir + "/plugin")`. + - Если файл существует → false (cached). + - Иначе → true. +- `buildDockerArgs(job BuildJob) []string`: + - Формирует: `["build", "--output", "type=local,dest=" + tmpDir, "--build-arg", "VERSION=" + job.Version, "--build-arg", "BINARY_NAME=" + job.Binary]`. + - Для каждого `key, value` в `job.BuildArgs` добавляет `"--build-arg", key + "=" + value`. + - Добавляет `job.PluginDir` (контекст сборки). +- `Build(ctx context.Context, jobs []BuildJob) (*BuildSummary, error)`: + - Использует `errgroup.Group` с `SetLimit(parallelism)`. + - Для каждого job: если `needsBuild` = false → cached; иначе `exec.CommandContext(ctx, "docker", buildDockerArgs(job)...)`. + - После Docker build: `os.Rename(tmpDir/binaryName, job.OutputDir/plugin)`, `os.Chmod(0o755)`. + - При ошибке: если `continueOnError` = true → записать в results, продолжить; иначе → `cancel()` контекста и вернуть ошибку. + - Собрать `BuildSummary` с подсчётом Total/Built/Failed/Cached. +- Импорты: `context`, `fmt`, `os`, `os/exec`, `path/filepath`, `sync`, `time`, `golang.org/x/sync/errgroup`, `gopkg.in/yaml.v3`. + +### T-3.3 Верификация + +Запустить `go build ./internal/epctl/`. +CRITICAL: пакет должен компилироваться без ошибок. + +--- + +## T-4: Создать CLI-каркас `cmd/epctl/` и cobra root + +*_Requirements: REQ-1.2_* +*_Complexity: mechanical_* + +**GOAL:** Создать entry point для epctl и cobra root command с глобальным `--output` флагом. + +### T-4.1 Создать `internal/epctl/root.go` + +Создать файл с package `epctl`. +- Определить `func NewRootCmd() *cobra.Command`: + - `Use: "epctl"`, `Short: "EasyP Service control utility"`. + - Persistent flag `--output` (string, default "text", valid: "text", "json"). + - Добавить subcommands: `newPluginsCmd()`, `newConfigCmd()` (пока заглушки, будут реализованы в T-5, T-6, T-7). +- Определить `func Execute()`: + - `cmd := NewRootCmd()`, `if err := cmd.Execute(); err != nil { os.Exit(1) }`. +- Определить `func getPrinter(cmd *cobra.Command) *output.Printer`: + - Получить значение `--output` из persistent flags. + - Вернуть `output.NewPrinter(format, os.Stdout)`. +- Импорты: `os`, `github.com/spf13/cobra`, `github.com/easyp-tech/service/internal/output`. + +### T-4.2 Создать `cmd/epctl/main.go` + +Создать файл с package `main`. +- Импортировать `github.com/easyp-tech/service/internal/epctl`. +- `func main() { epctl.Execute() }`. + +### T-4.3 Добавить зависимость cobra + +Запустить `go get github.com/spf13/cobra`. + +### T-4.4 Верификация + +Запустить `go build -o epctl ./cmd/epctl/`. +Запустить `./epctl --help`. +CRITICAL: должен вывести help с подкомандами `plugins` и `config`. + +--- + +## T-5: Реализовать команду `plugins build` + +*_Requirements: REQ-2.1, REQ-2.2, REQ-2.3, REQ-2.4, REQ-2.5, REQ-2.6_* +*_Complexity: standard_* + +**GOAL:** Подключить PluginBuilder к cobra command. + +### T-5.1 Создать `internal/epctl/plugins_build.go` + +Создать файл с package `epctl`. +- `func newPluginsBuildCmd() *cobra.Command`: + - `Use: "build [filter]"`, `Short: "Build plugins from registry"`. + - `Args: cobra.MaximumNArgs(1)`. + - Flags: `--registry-dir` (string, default "registry"), `--output-dir` (string, default "plugins"), `--parallelism` (int, default 3), `--continue-on-error` (bool, default false). + - `RunE`: + 1. Получить `printer` через `getPrinter(cmd)`. + 2. Парсить filter: если args[0] есть → `ParsePathFilter(args[0])`. + 3. Создать `NewPluginBuilder(registryDir, outputDir, parallelism, continueOnError)`. + 4. `jobs, err := builder.DiscoverJobs(&filter)`. + 5. Если 0 jobs → `printer.Message("No plugins found in registry")`, return nil. + 6. `summary, err := builder.Build(cmd.Context(), jobs)`. + 7. Вывести summary через printer: Table или JSON в зависимости от формата. + 8. Если `summary.Failed > 0 && !continueOnError` → return error. + +### T-5.2 Подключить к plugins subcommand в `root.go` + +В `root.go` добавить `newPluginsCmd()` функцию: +- `Use: "plugins"`, `Short: "Plugin management commands"`. +- `AddCommand(newPluginsBuildCmd())`. + +NOTE: `register` и `list` будут добавлены в T-6. + +### T-5.3 Верификация + +Запустить `go build -o epctl ./cmd/epctl/ && ./epctl plugins build --help`. +CRITICAL: должен показать help с флагами `--registry-dir`, `--output-dir`, `--parallelism`, `--continue-on-error`. + +--- + +## T-6: Реализовать команды `plugins register` и `plugins list` + +*_Requirements: REQ-3.1, REQ-3.2, REQ-3.3, REQ-3.4, REQ-3.5, REQ-3.6, REQ-3.7, REQ-3.8, REQ-4.1, REQ-4.2, REQ-4.3, REQ-4.4, REQ-4.5_* +*_Complexity: standard_* + +**GOAL:** Реализовать gRPC-взаимодействие с сервером через SDK. + +### T-6.1 Добавить `CreatePlugin` в SDK + +В `sdk/client.go` добавить метод: +```go +func (c *Client) CreatePlugin( + ctx context.Context, + group, name, version string, + pluginConfig map[string]any, + tags []string, +) (*generator.PluginInfo, error) +``` +- Внутри: конвертировать `pluginConfig` в `*structpb.Struct` через `structpb.NewStruct(pluginConfig)`. +- Вызвать `c.genClient.CreatePlugin(ctx, &generator.CreatePluginRequest{...})`. +- Вернуть `resp.GetPlugin(), nil`. +- Использовать `c.withTimeout(ctx, c.cfg.createPluginTimeout)` — добавить `createPluginTimeout` в SDK config с default 30s. + +### T-6.2 Создать `internal/epctl/plugins_register.go` + +Создать файл с package `epctl`. +- `func newPluginsRegisterCmd() *cobra.Command`: + - `Use: "register [filter]"`, `Short: "Register plugins in EasyP service"`. + - `Args: cobra.MaximumNArgs(1)`. + - Flags: `--addr` (string, default "localhost:8080"), `--plugins-dir` (string, default "plugins"), `--plugins-prefix` (string, default "/plugins"). + - `RunE`: + 1. Парсить filter. + 2. Сканировать `plugins-dir`: найти все директории вида `{group}/{name}/{version}/plugin`. + 3. Применить filter. + 4. Создать `sdk.NewClient(addr, sdk.WithInsecure())`. + 5. Для каждого плагина: + - Сформировать `config = map[string]any{"command": pluginsPrefix + "/" + group + "/" + name + "/" + version + "/plugin"}`. + - Вызвать `client.CreatePlugin(ctx, group, name, version, config, nil)`. + - Если `status.Code(err) == codes.AlreadyExists` → warning, skipped++, continue. + - Если другая ошибка → вернуть ошибку с exit code 1. + - Иначе → registered++. + 6. Вывести отчёт через printer: registered, skipped, total. + +### T-6.3 Создать `internal/epctl/plugins_list.go` + +Создать файл с package `epctl`. +- `func newPluginsListCmd() *cobra.Command`: + - `Use: "list"`, `Short: "List registered plugins"`. + - Flags: `--addr` (string, default "localhost:8080"), `--group`, `--name`, `--version`, `--tags` (string slice). + - `RunE`: + 1. Создать `sdk.NewClient(addr, sdk.WithInsecure())`. + 2. Сформировать `sdk.PluginFilter{Group, Name, Version, Tags}`. + 3. `plugins, err := client.ListPlugins(ctx, filter)`. + 4. Если connection error → `fmt.Errorf("cannot connect to %s: %w", addr, err)`. + 5. Если 0 плагинов → `printer.Message("No plugins found")`, exit 0. + 6. Вывести таблицу через printer: group, name, version, tags, created_at. + +### T-6.4 Подключить к plugins subcommand + +В `root.go` обновить `newPluginsCmd()`: добавить `newPluginsRegisterCmd()` и `newPluginsListCmd()`. + +### T-6.5 Верификация + +Запустить `go build -o epctl ./cmd/epctl/`. +Запустить `./epctl plugins register --help` и `./epctl plugins list --help`. +CRITICAL: обе команды должны показывать корректный help. + +--- + +## T-7: Реализовать команду `config validate` + +*_Requirements: REQ-5.1, REQ-5.2, REQ-5.3, REQ-5.4, REQ-5.5_* +*_Complexity: mechanical_* + +**GOAL:** Подключить валидацию конфига из shared-пакета к cobra command. + +### T-7.1 Создать `internal/epctl/config_validate.go` + +Создать файл с package `epctl`. +- `func newConfigValidateCmd() *cobra.Command`: + - `Use: "validate "`, `Short: "Validate service config YAML"`. + - `Args: cobra.ExactArgs(1)`. + - `RunE`: + 1. Получить printer. + 2. `cfg, warnings, err := config.LoadAndValidate(args[0])`. + 3. Если err != nil → `printer.Error(err)`, return err. + 4. Если len(warnings) > 0 → вывести каждый warning. + 5. Если `cfg.Validate()` != nil → вывести ошибки. + 6. Иначе → `printer.Message("Config is valid")`. + +### T-7.2 Подключить к config subcommand + +В `root.go` добавить `newConfigCmd()`: +- `Use: "config"`, `Short: "Configuration management"`. +- `AddCommand(newConfigValidateCmd())`. + +### T-7.3 Верификация + +Запустить `go build -o epctl ./cmd/epctl/ && ./epctl config validate --help`. +CRITICAL: должен показать help с обязательным аргументом ``. + +--- + +## T-8: Обновить инфраструктуру (Dockerfile, Taskfile, удалить скрипты) + +*_Requirements: REQ-7.1, REQ-7.2, REQ-7.3, REQ-7.4_* +*_Complexity: mechanical_* + +**GOAL:** Обновить сборочную инфраструктуру и удалить замещённые bash-скрипты. + +### T-8.1 Обновить `Dockerfile` + +В `Dockerfile` заменить: +- `go build -o /easyp ./cmd/` → `go build -o /easyp ./cmd/easyp/`. +- Убедиться что `COPY` и `ENTRYPOINT` указывают на `/easyp`. + +### T-8.2 Обновить `Taskfile.yml` + +- Таска `build-plugins`: заменить `./build-plugins.sh` на `go run ./cmd/epctl plugins build`. +- Таска `register-plugins`: заменить `./register-plugins.sh` на `go run ./cmd/epctl plugins register`. + +### T-8.3 Удалить bash-скрипты + +Удалить файлы: +- `build-plugins.sh` +- `register-plugins.sh` + +### T-8.4 Верификация (GATE) + +Запустить: +1. `go build -o easyp ./cmd/easyp/` — серверный бинарник. +2. `go build -o epctl ./cmd/epctl/` — CLI бинарник. +3. `./epctl --help` — help выводится. +4. `./epctl plugins build --help` — help выводится. +5. `./epctl plugins register --help` — help выводится. +6. `./epctl plugins list --help` — help выводится. +7. `./epctl config validate --help` — help выводится. + +CRITICAL: все 7 проверок должны пройти без ошибок. diff --git a/.spec/features/goose-migration/approved/review.md b/.spec/features/goose-migration/approved/review.md new file mode 100644 index 0000000..1e547b6 --- /dev/null +++ b/.spec/features/goose-migration/approved/review.md @@ -0,0 +1,118 @@ +# Code Review: goose-migration + +## Verdict: APPROVED + +Реализация корректно заменяет самописный мигратор на goose v3 с embed.FS и Provider API. Все файлы изменены в соответствии с дизайном, проект компилируется, конфиги очищены. Advisory locking (REQ-2.4) реализован через `lock.NewPostgresSessionLocker()` + `goose.WithSessionLocker()` в Provider API. Все findings из первого раунда ревью исправлены. + +## Change Set + +| Файл | Статус | Notes | +|------|--------|-------| +| `internal/database/goosemigrate/goosemigrate.go` | ✅ Planned (NEW) | Provider API + advisory lock | +| `internal/database/goosemigrate/migrations/00001_init.sql` | ✅ Planned (NEW) | — | +| `cmd/main.go` | ✅ Planned (MODIFIED) | Импорт, dbConfig, вызов миграций | +| `config.yml` | ✅ Planned (MODIFIED) | Удалён `migrate_dir` | +| `config.local.yml` | ✅ Planned (MODIFIED) | Удалён `migrate_dir` | +| `docker-compose.yml` | ✅ Planned (MODIFIED) | Удалён volume mount | +| `internal/database/migrations/` (6 файлов) | ✅ Planned (DELETED) | — | +| `migrate/` (5 файлов) | ✅ Planned (DELETED) | — | +| `go.mod`, `go.sum` | ⚠️ Unexpected | Обоснованно — добавлена зависимость goose v3 | + +## Requirements Traceability + +| Requirement | Test(s) | Code | CP | Verdict | +|-------------|---------|------|----|---------| +| REQ-1.1 | (без тестов) | `goosemigrate.go:16-17` embed.FS | CP-1 | ✅ | +| REQ-1.2 | (без тестов) | `cmd/main.go` нет `MigrateDir` | CP-2 | ✅ | +| REQ-2.1 | (без тестов) | `cmd/main.go` вызов до `NewSQL` | CP-3 | ✅ | +| REQ-2.2 | (без тестов) | `goosemigrate.go:55` provider.Up — идемпотентен | CP-4 | ✅ | +| REQ-2.3 | (без тестов) | `goosemigrate.go:55-58` error propagation | CP-5 | ✅ | +| REQ-2.4 | (без тестов) | `goosemigrate.go:43-49` Provider API + SessionLocker | CP-6 | ✅ | +| REQ-3.1 | (без тестов) | `00001_init.sql:1` goose format | CP-7 | ✅ | +| REQ-3.2 | (без тестов) | goose default behavior — транзакция per migration | CP-8 | ✅ | +| REQ-4.1 | (без тестов) | `internal/database/migrations/` удалён | CP-9 | ✅ | +| REQ-4.2 | (без тестов) | grep подтверждает отсутствие импорта | CP-9 | ✅ | +| REQ-5.1 | (без тестов) | goose создаёт `goose_db_version` автоматически | CP-10 | ✅ | +| REQ-5.2 | (без тестов) | Старая таблица не используется | CP-10 | ✅ | + +NOTE: тесты исключены из scope по решению пользователя. + +## Design Conformance + +### 3.1 Архитектурные границы +✅ Пакет `internal/database/goosemigrate/` создан в правильном слое. Зависимость однонаправленная: `cmd/main.go` → `goosemigrate`. + +### 3.2 Модели данных +✅ SQL-схема в `00001_init.sql` полностью соответствует финальному состоянию из 5 оригинальных миграций. Seed-данные корректно удалены. + +### 3.3 API Contracts +✅ Сигнатура `Up(ctx context.Context, dsn string) error` соответствует дизайну §2.3. + +### 3.4 Error Handling +✅ Ошибки обёрнуты с контекстом через `fmt.Errorf("...: %w", err)`. Все точки ошибок (Open, Ping, fs.Sub, NewPostgresSessionLocker, NewProvider, Up) покрыты. + +### 3.5 Correctness Properties +- CP-1 (embed.FS equivalence): ✅ +- CP-2 (absence MigrateDir): ✅ +- CP-3 (propagation — миграции до pool): ✅ +- CP-4 (idempotent Up): ✅ +- CP-5 (error stops startup): ✅ +- CP-6 (advisory lock): ✅ — реализован через `lock.NewPostgresSessionLocker()` + `goose.WithSessionLocker()` +- CP-7 (goose file format): ✅ +- CP-8 (transaction rollback): ✅ +- CP-9 (no old code): ✅ +- CP-10 (goose_db_version): ✅ + +### 3.6 Документация +✅ Godoc комментарий функции `Up` корректно описывает advisory locking — соответствует реализации. + +## Code Quality + +### 4.1 Naming & Clarity +✅ Именование соответствует конвенциям проекта. Пакет `goosemigrate` — описательный. + +### 4.2 Dead Code +✅ Нет мёртвого кода. Все импорты используются. + +### 4.3 Scope Creep +✅ Нет изменений вне scope. + +### 4.4 Test Quality +N/A — тесты исключены из scope по решению пользователя. + +## Security + +Нет новых эндпоинтов, нет изменений в обработке пользовательского ввода. DSN передаётся из конфигурации — не от пользователя. SQL-миграции статически вкомпилированы через embed.FS — нет риска инъекции. Нет хардкоженных секретов. + +Provider API инкапсулирует всю конфигурацию (диалект, FS, локер) внутри экземпляра — нет мутации глобального состояния пакета goose. Безопасно для параллельных тестов. + +## Verification Evidence + +- **Build:** +``` +$ go build -buildvcs=false ./... +(exit 0, no output) +``` +- **Lint:** +``` +$ go vet ./... +(exit 0, no output) +``` +- **Gate checks:** +``` +$ test ! -d internal/database/migrations → OK: dir absent +$ grep -r "internal/database/migrations" --include="*.go" . → no results +$ grep -r "migrate_dir" config.yml config.local.yml → no results +$ grep "migrate" docker-compose.yml → no results +``` + +## Findings (из первого раунда) + +| ID | Severity | Status | Description | +|----|----------|--------|-------------| +| F-1 | major | ✅ FIXED | Advisory lock реализован через Provider API с `NewPostgresSessionLocker()` + `WithSessionLocker()` | +| F-2 | minor | ✅ FIXED | Глобальное состояние устранено — Provider API инкапсулирует конфигурацию | + +## Recommendations + +Нет открытых рекомендаций. Все findings исправлены. diff --git a/.spec/features/goose-migration/pipeline.json b/.spec/features/goose-migration/pipeline.json index bb82f2f..cfedc16 100644 --- a/.spec/features/goose-migration/pipeline.json +++ b/.spec/features/goose-migration/pipeline.json @@ -1,20 +1,21 @@ { "feature": "goose-migration", - "phase": "review", + "phase": "done", "created_at": "2026-05-25T16:46:49Z", - "current_artifact": ".spec/features/goose-migration/review.md", + "current_artifact": null, "history": [ {"phase": "explore", "artifact": ".spec/features/goose-migration/explore.md", "approved_at": "2026-05-26T14:25:53Z"}, {"phase": "requirements", "artifact": ".spec/features/goose-migration/requirements.md", "approved_at": "2026-05-26T14:34:13Z"}, {"phase": "design", "artifact": ".spec/features/goose-migration/design.md", "approved_at": "2026-05-26T14:39:55Z"}, {"phase": "task-plan", "artifact": ".spec/features/goose-migration/task-plan.md", "approved_at": "2026-05-26T17:33:06Z"}, - {"phase": "implementation", "artifact": ".spec/features/goose-migration/task-plan.md", "approved_at": "2026-05-26T18:21:41Z"} + {"phase": "implementation", "artifact": ".spec/features/goose-migration/task-plan.md", "approved_at": "2026-05-26T18:21:41Z"}, + {"phase": "review", "artifact": ".spec/features/goose-migration/review.md", "approved_at": "2026-05-28T19:46:09Z"} ], "review_base_commit": null, "branch": null, "worktree": null, "last_completed_task": null, - "finish_action": null, - "finished_at": null, + "finish_action": "keep", + "finished_at": "2026-05-28T19:46:32Z", "finish_base": null } diff --git a/.spec/features/goose-migration/pipeline.kv b/.spec/features/goose-migration/pipeline.kv index 9a11ea7..ee6dfec 100644 --- a/.spec/features/goose-migration/pipeline.kv +++ b/.spec/features/goose-migration/pipeline.kv @@ -1,8 +1,8 @@ feature=goose-migration -phase=review +phase=done created_at=2026-05-25T16:46:49Z -current_artifact=.spec/features/goose-migration/review.md -history_count=5 +current_artifact= +history_count=6 revision_count_explore=1 history_0_phase=explore history_0_artifact=.spec/features/goose-migration/explore.md @@ -25,4 +25,9 @@ history_4_phase=implementation history_4_artifact=.spec/features/goose-migration/task-plan.md history_4_approved_at=2026-05-26T18:21:41Z last_completed_task= -revision_count_review=1 +revision_count_review=2 +history_5_phase=review +history_5_artifact=.spec/features/goose-migration/review.md +history_5_approved_at=2026-05-28T19:46:09Z +finish_action=keep +finished_at=2026-05-28T19:46:32Z diff --git a/.spec/features/goose-migration/review.md b/.spec/features/goose-migration/review.md index fa54408..1e547b6 100644 --- a/.spec/features/goose-migration/review.md +++ b/.spec/features/goose-migration/review.md @@ -1,14 +1,14 @@ # Code Review: goose-migration -## Verdict: NEEDS_CHANGES +## Verdict: APPROVED -Реализация корректно заменяет самописный мигратор на goose v3 с embed.FS. Все файлы изменены в соответствии с дизайном, проект компилируется, конфиги очищены. Однако REQ-2.4 (advisory lock для предотвращения параллельного выполнения миграций) **не реализован** — текущий код использует functional API (`goose.UpContext`), который не поддерживает advisory locking. Нужно переключиться на Provider API. Также godoc комментарий функции `Up` утверждает наличие advisory locking, что не соответствует коду. +Реализация корректно заменяет самописный мигратор на goose v3 с embed.FS и Provider API. Все файлы изменены в соответствии с дизайном, проект компилируется, конфиги очищены. Advisory locking (REQ-2.4) реализован через `lock.NewPostgresSessionLocker()` + `goose.WithSessionLocker()` в Provider API. Все findings из первого раунда ревью исправлены. ## Change Set | Файл | Статус | Notes | |------|--------|-------| -| `internal/database/goosemigrate/goosemigrate.go` | ✅ Planned (NEW) | — | +| `internal/database/goosemigrate/goosemigrate.go` | ✅ Planned (NEW) | Provider API + advisory lock | | `internal/database/goosemigrate/migrations/00001_init.sql` | ✅ Planned (NEW) | — | | `cmd/main.go` | ✅ Planned (MODIFIED) | Импорт, dbConfig, вызов миграций | | `config.yml` | ✅ Planned (MODIFIED) | Удалён `migrate_dir` | @@ -22,12 +22,12 @@ | Requirement | Test(s) | Code | CP | Verdict | |-------------|---------|------|----|---------| -| REQ-1.1 | (без тестов) | `goosemigrate.go:13-14` embed.FS | CP-1 | ✅ | -| REQ-1.2 | (без тестов) | `cmd/main.go:71-74` нет `MigrateDir` | CP-2 | ✅ | -| REQ-2.1 | (без тестов) | `cmd/main.go:182` вызов до `NewSQL` | CP-3 | ✅ | -| REQ-2.2 | (без тестов) | `goosemigrate.go:36` goose.UpContext — идемпотентен | CP-4 | ✅ | -| REQ-2.3 | (без тестов) | `goosemigrate.go:36-38` error propagation | CP-5 | ✅ | -| REQ-2.4 | (без тестов) | **Не реализован** — advisory lock отсутствует | CP-6 | ❌ | +| REQ-1.1 | (без тестов) | `goosemigrate.go:16-17` embed.FS | CP-1 | ✅ | +| REQ-1.2 | (без тестов) | `cmd/main.go` нет `MigrateDir` | CP-2 | ✅ | +| REQ-2.1 | (без тестов) | `cmd/main.go` вызов до `NewSQL` | CP-3 | ✅ | +| REQ-2.2 | (без тестов) | `goosemigrate.go:55` provider.Up — идемпотентен | CP-4 | ✅ | +| REQ-2.3 | (без тестов) | `goosemigrate.go:55-58` error propagation | CP-5 | ✅ | +| REQ-2.4 | (без тестов) | `goosemigrate.go:43-49` Provider API + SessionLocker | CP-6 | ✅ | | REQ-3.1 | (без тестов) | `00001_init.sql:1` goose format | CP-7 | ✅ | | REQ-3.2 | (без тестов) | goose default behavior — транзакция per migration | CP-8 | ✅ | | REQ-4.1 | (без тестов) | `internal/database/migrations/` удалён | CP-9 | ✅ | @@ -49,7 +49,7 @@ NOTE: тесты исключены из scope по решению пользо ✅ Сигнатура `Up(ctx context.Context, dsn string) error` соответствует дизайну §2.3. ### 3.4 Error Handling -✅ Ошибки обёрнуты с контекстом через `fmt.Errorf("goosemigrate: ...: %w", err)`. Все 3 точки ошибок (Open, Ping, Up) покрыты. +✅ Ошибки обёрнуты с контекстом через `fmt.Errorf("...: %w", err)`. Все точки ошибок (Open, Ping, fs.Sub, NewPostgresSessionLocker, NewProvider, Up) покрыты. ### 3.5 Correctness Properties - CP-1 (embed.FS equivalence): ✅ @@ -57,14 +57,14 @@ NOTE: тесты исключены из scope по решению пользо - CP-3 (propagation — миграции до pool): ✅ - CP-4 (idempotent Up): ✅ - CP-5 (error stops startup): ✅ -- CP-6 (advisory lock): ❌ **Не реализован** +- CP-6 (advisory lock): ✅ — реализован через `lock.NewPostgresSessionLocker()` + `goose.WithSessionLocker()` - CP-7 (goose file format): ✅ - CP-8 (transaction rollback): ✅ - CP-9 (no old code): ✅ - CP-10 (goose_db_version): ✅ ### 3.6 Документация -Godoc комментарий функции `Up` заявляет "It uses PostgreSQL advisory locking" — это не соответствует реализации (F-1). +✅ Godoc комментарий функции `Up` корректно описывает advisory locking — соответствует реализации. ## Code Quality @@ -72,7 +72,7 @@ Godoc комментарий функции `Up` заявляет "It uses Postg ✅ Именование соответствует конвенциям проекта. Пакет `goosemigrate` — описательный. ### 4.2 Dead Code -✅ Нет мёртвого кода. `connectors` import остался в `cmd/main.go` — он по-прежнему используется для `database.NewSQL`. +✅ Нет мёртвого кода. Все импорты используются. ### 4.3 Scope Creep ✅ Нет изменений вне scope. @@ -84,7 +84,7 @@ N/A — тесты исключены из scope по решению польз Нет новых эндпоинтов, нет изменений в обработке пользовательского ввода. DSN передаётся из конфигурации — не от пользователя. SQL-миграции статически вкомпилированы через embed.FS — нет риска инъекции. Нет хардкоженных секретов. -**Замечание:** `goose.SetBaseFS()` и `goose.SetDialect()` модифицируют глобальное состояние пакета goose. Это безопасно при однократном вызове при старте, но может быть проблемой при параллельных тестах. Для текущего scope это не критично (F-2, minor). +Provider API инкапсулирует всю конфигурацию (диалект, FS, локер) внутри экземпляра — нет мутации глобального состояния пакета goose. Безопасно для параллельных тестов. ## Verification Evidence @@ -98,24 +98,21 @@ $ go build -buildvcs=false ./... $ go vet ./... (exit 0, no output) ``` +- **Gate checks:** +``` +$ test ! -d internal/database/migrations → OK: dir absent +$ grep -r "internal/database/migrations" --include="*.go" . → no results +$ grep -r "migrate_dir" config.yml config.local.yml → no results +$ grep "migrate" docker-compose.yml → no results +``` -## Findings +## Findings (из первого раунда) -| ID | Severity | File | Description | Requirement | -|----|----------|------|-------------|-------------| -| F-1 | major | `goosemigrate.go:17,30-34` | Advisory lock не реализован. Functional API (`goose.UpContext`) не поддерживает advisory locking. Нужно использовать Provider API с `goose.WithSessionLocker(lock.NewPostgresSessionLocker())`. Godoc также вводит в заблуждение. | REQ-2.4 | -| F-2 | minor | `goosemigrate.go:30-34` | `goose.SetBaseFS()` и `goose.SetDialect()` устанавливают глобальное состояние. Переход на Provider API (для F-1) решит эту проблему, т.к. Provider инкапсулирует конфигурацию. | — | +| ID | Severity | Status | Description | +|----|----------|--------|-------------| +| F-1 | major | ✅ FIXED | Advisory lock реализован через Provider API с `NewPostgresSessionLocker()` + `WithSessionLocker()` | +| F-2 | minor | ✅ FIXED | Глобальное состояние устранено — Provider API инкапсулирует конфигурацию | ## Recommendations -1. **F-1 (major):** Переписать `goosemigrate.go` с использованием Provider API: - ```go - locker, err := lock.NewPostgresSessionLocker() - provider, err := goose.NewProvider("postgres", db, migrationsFS, - goose.WithSessionLocker(locker), - ) - _, err = provider.Up(ctx) - ``` - Это решит и F-1 (advisory lock), и F-2 (глобальное состояние). - -2. **F-2 (minor):** Решается автоматически при фиксе F-1. +Нет открытых рекомендаций. Все findings исправлены. diff --git a/.spec/features/goose-migration/revisions/review-rev-2-2026-05-28T19-39-36Z.md b/.spec/features/goose-migration/revisions/review-rev-2-2026-05-28T19-39-36Z.md new file mode 100644 index 0000000..1e547b6 --- /dev/null +++ b/.spec/features/goose-migration/revisions/review-rev-2-2026-05-28T19-39-36Z.md @@ -0,0 +1,118 @@ +# Code Review: goose-migration + +## Verdict: APPROVED + +Реализация корректно заменяет самописный мигратор на goose v3 с embed.FS и Provider API. Все файлы изменены в соответствии с дизайном, проект компилируется, конфиги очищены. Advisory locking (REQ-2.4) реализован через `lock.NewPostgresSessionLocker()` + `goose.WithSessionLocker()` в Provider API. Все findings из первого раунда ревью исправлены. + +## Change Set + +| Файл | Статус | Notes | +|------|--------|-------| +| `internal/database/goosemigrate/goosemigrate.go` | ✅ Planned (NEW) | Provider API + advisory lock | +| `internal/database/goosemigrate/migrations/00001_init.sql` | ✅ Planned (NEW) | — | +| `cmd/main.go` | ✅ Planned (MODIFIED) | Импорт, dbConfig, вызов миграций | +| `config.yml` | ✅ Planned (MODIFIED) | Удалён `migrate_dir` | +| `config.local.yml` | ✅ Planned (MODIFIED) | Удалён `migrate_dir` | +| `docker-compose.yml` | ✅ Planned (MODIFIED) | Удалён volume mount | +| `internal/database/migrations/` (6 файлов) | ✅ Planned (DELETED) | — | +| `migrate/` (5 файлов) | ✅ Planned (DELETED) | — | +| `go.mod`, `go.sum` | ⚠️ Unexpected | Обоснованно — добавлена зависимость goose v3 | + +## Requirements Traceability + +| Requirement | Test(s) | Code | CP | Verdict | +|-------------|---------|------|----|---------| +| REQ-1.1 | (без тестов) | `goosemigrate.go:16-17` embed.FS | CP-1 | ✅ | +| REQ-1.2 | (без тестов) | `cmd/main.go` нет `MigrateDir` | CP-2 | ✅ | +| REQ-2.1 | (без тестов) | `cmd/main.go` вызов до `NewSQL` | CP-3 | ✅ | +| REQ-2.2 | (без тестов) | `goosemigrate.go:55` provider.Up — идемпотентен | CP-4 | ✅ | +| REQ-2.3 | (без тестов) | `goosemigrate.go:55-58` error propagation | CP-5 | ✅ | +| REQ-2.4 | (без тестов) | `goosemigrate.go:43-49` Provider API + SessionLocker | CP-6 | ✅ | +| REQ-3.1 | (без тестов) | `00001_init.sql:1` goose format | CP-7 | ✅ | +| REQ-3.2 | (без тестов) | goose default behavior — транзакция per migration | CP-8 | ✅ | +| REQ-4.1 | (без тестов) | `internal/database/migrations/` удалён | CP-9 | ✅ | +| REQ-4.2 | (без тестов) | grep подтверждает отсутствие импорта | CP-9 | ✅ | +| REQ-5.1 | (без тестов) | goose создаёт `goose_db_version` автоматически | CP-10 | ✅ | +| REQ-5.2 | (без тестов) | Старая таблица не используется | CP-10 | ✅ | + +NOTE: тесты исключены из scope по решению пользователя. + +## Design Conformance + +### 3.1 Архитектурные границы +✅ Пакет `internal/database/goosemigrate/` создан в правильном слое. Зависимость однонаправленная: `cmd/main.go` → `goosemigrate`. + +### 3.2 Модели данных +✅ SQL-схема в `00001_init.sql` полностью соответствует финальному состоянию из 5 оригинальных миграций. Seed-данные корректно удалены. + +### 3.3 API Contracts +✅ Сигнатура `Up(ctx context.Context, dsn string) error` соответствует дизайну §2.3. + +### 3.4 Error Handling +✅ Ошибки обёрнуты с контекстом через `fmt.Errorf("...: %w", err)`. Все точки ошибок (Open, Ping, fs.Sub, NewPostgresSessionLocker, NewProvider, Up) покрыты. + +### 3.5 Correctness Properties +- CP-1 (embed.FS equivalence): ✅ +- CP-2 (absence MigrateDir): ✅ +- CP-3 (propagation — миграции до pool): ✅ +- CP-4 (idempotent Up): ✅ +- CP-5 (error stops startup): ✅ +- CP-6 (advisory lock): ✅ — реализован через `lock.NewPostgresSessionLocker()` + `goose.WithSessionLocker()` +- CP-7 (goose file format): ✅ +- CP-8 (transaction rollback): ✅ +- CP-9 (no old code): ✅ +- CP-10 (goose_db_version): ✅ + +### 3.6 Документация +✅ Godoc комментарий функции `Up` корректно описывает advisory locking — соответствует реализации. + +## Code Quality + +### 4.1 Naming & Clarity +✅ Именование соответствует конвенциям проекта. Пакет `goosemigrate` — описательный. + +### 4.2 Dead Code +✅ Нет мёртвого кода. Все импорты используются. + +### 4.3 Scope Creep +✅ Нет изменений вне scope. + +### 4.4 Test Quality +N/A — тесты исключены из scope по решению пользователя. + +## Security + +Нет новых эндпоинтов, нет изменений в обработке пользовательского ввода. DSN передаётся из конфигурации — не от пользователя. SQL-миграции статически вкомпилированы через embed.FS — нет риска инъекции. Нет хардкоженных секретов. + +Provider API инкапсулирует всю конфигурацию (диалект, FS, локер) внутри экземпляра — нет мутации глобального состояния пакета goose. Безопасно для параллельных тестов. + +## Verification Evidence + +- **Build:** +``` +$ go build -buildvcs=false ./... +(exit 0, no output) +``` +- **Lint:** +``` +$ go vet ./... +(exit 0, no output) +``` +- **Gate checks:** +``` +$ test ! -d internal/database/migrations → OK: dir absent +$ grep -r "internal/database/migrations" --include="*.go" . → no results +$ grep -r "migrate_dir" config.yml config.local.yml → no results +$ grep "migrate" docker-compose.yml → no results +``` + +## Findings (из первого раунда) + +| ID | Severity | Status | Description | +|----|----------|--------|-------------| +| F-1 | major | ✅ FIXED | Advisory lock реализован через Provider API с `NewPostgresSessionLocker()` + `WithSessionLocker()` | +| F-2 | minor | ✅ FIXED | Глобальное состояние устранено — Provider API инкапсулирует конфигурацию | + +## Recommendations + +Нет открытых рекомендаций. Все findings исправлены. diff --git a/.spec/features/local-test-env/approved/design.md b/.spec/features/local-test-env/approved/design.md deleted file mode 100644 index ff779b6..0000000 --- a/.spec/features/local-test-env/approved/design.md +++ /dev/null @@ -1,290 +0,0 @@ -# local-test-env — Design - -**Status:** Draft -**Date:** 2026-05-24 - -## 2.1 Обзор - -Проектирование локального тестового окружения для сервиса EasyP после миграции на disk-plugin-execution. Задача делится на 4 логические части: - -1. **Dockerfile-ы** — переделка 4 файлов в `registry/` для сборки бинарников через `docker build --output`. -2. **Скрипты** — `build-plugins.sh` (сборка) и `register-plugins.sh` (регистрация через gRPC API). -3. **Инфраструктура** — очистка `docker-compose.yml`, обновление конфигов, Taskfile. -4. **Очистка** — удаление `push.sh`. - ---- - -## 2.2 Архитектура - -```mermaid -graph TD - subgraph "Сборка плагинов" - DF1["registry/*/Dockerfile"] - BP["build-plugins.sh"]:::new - PD["./plugins/{group}/{name}/{version}/plugin"]:::new - - DF1 -->|"docker build --output"| BP - BP -->|"создаёт бинарники"| PD - end - - subgraph "Инфраструктура" - DC["docker-compose.yml"]:::modified - CFG["config.yml"]:::modified - CFGL["config.local.yml"]:::modified - TF["Taskfile.yml"]:::modified - end - - subgraph "Регистрация и запуск" - SVC["easyp-api-service"] - RP["register-plugins.sh"]:::new - DB["PostgreSQL"] - - PD -->|"volume mount"| SVC - RP -->|"gRPC CreatePlugin"| SVC - SVC -->|"INSERT plugins"| DB - end - - classDef new fill:#90EE90 - classDef modified fill:#FFD700 -``` - -**Порядок реализации:** -1. Dockerfile-ы (фундамент — без них скрипт сборки не работает) -2. `build-plugins.sh` (зависит от Dockerfile-ов) -3. Инфраструктура (config, docker-compose, Taskfile — параллельно) -4. `register-plugins.sh` (зависит от работающего сервиса) -5. Очистка (удаление `push.sh`) - ---- - -## 2.3 Компоненты и интерфейсы - -### Файлы, требующие изменений - -| File | Change Type | Description | -|------|-------------|-------------| -| `registry/protocolbuffers/go/v1.36.10/Dockerfile` | `[MODIFIED]` | Убрать второй стейдж с `ENTRYPOINT`/`USER`/`passwd`. Финальный стейдж: `FROM scratch` + `COPY --from=0 /go/bin/protoc-gen-go /plugin` | -| `registry/grpc/go/v1.5.1/Dockerfile` | `[MODIFIED]` | Аналогично: `COPY --from=0 /go/bin/protoc-gen-go-grpc /plugin` | -| `registry/grpc-ecosystem/gateway/v2.27.3/Dockerfile` | `[MODIFIED]` | Аналогично: `COPY --from=0 /go/bin/protoc-gen-grpc-gateway /plugin` | -| `registry/grpc-ecosystem/openapiv2/v2.27.3/Dockerfile` | `[MODIFIED]` | Аналогично: `COPY --from=0 /go/bin/protoc-gen-openapiv2 /plugin` | -| `build-plugins.sh` | `[NEW]` | Bash-скрипт: `find registry -name Dockerfile`, для каждого `docker build --output`. `set -e`. | -| `register-plugins.sh` | `[NEW]` | Bash-скрипт: обход `plugins/`, для каждого вызов `grpcurl` → `CreatePlugin`. `ALREADY_EXISTS` → skip. | -| `config.yml` | `[MODIFIED]` | Секция `registry:` — заменить `domain: "localhost:5005"` на `plugins_dir: "/plugins"` и `max_output_size: 67108864` | -| `config.local.yml` | `[MODIFIED]` | Секция `registry:` — заменить `domain: "localhost:5005"` на `plugins_dir: "./plugins"` и `max_output_size: 67108864` | -| `docker-compose.yml` | `[MODIFIED]` | Удалить сервис `registry`, volume `registry-data`. В `service`: убрать `docker.sock`, добавить `./plugins:/plugins:ro`. | -| `Taskfile.yml` | `[MODIFIED]` | Удалить `local-push-registry`, `local-push-required`. Добавить `build-plugins`, `register-plugins`. Обновить deps в `run`. | -| `push.sh` | `[DELETED]` | Устаревший скрипт для пуша образов в Docker Registry. | - -### Файлы, НЕ требующие изменений - -| File | Reason Unchanged | -|------|-----------------| -| `cmd/main.go` | Структура `registryConfig` уже использует `PluginsDir` и `MaxOutputSize` (обновлена в `disk-plugin-execution`) | -| `internal/adapters/registry/registry.go` | Логика `Generate()` уже работает с exec — не затрагивается | -| `internal/core/domain.go` | Доменная модель не меняется | -| `internal/core/pool.go` | WorkerPool не затрагивается | -| `api/generator/v1/generator.proto` | API контракт не меняется | -| `Dockerfile` (корневой) | Сервисный Dockerfile уже переделан в `disk-plugin-execution` (debian, VOLUME /plugins) | -| `migrate/*.sql` | Миграция `5.disk_plugin_config.sql` уже существует | -| `.gitignore` | `plugins/` уже добавлен (строка 9) | - ---- - -## 2.4 Ключевые решения (ADR) - -### Decision: Формат выходного бинарника — `/plugin` - -- **Context:** Каждый Dockerfile собирает плагин с уникальным именем (`protoc-gen-go`, `protoc-gen-go-grpc` и т.д.). Нужен единый путь для бинарника в финальном стейдже. -- **Options considered:** - 1. Оставить оригинальное имя (`/protoc-gen-go`) — разные имена для каждого плагина. - 2. Использовать единое имя `/plugin` — одинаковая структура для всех. -- **Decision:** Единое имя `/plugin`. -- **Rationale:** Скрипт `build-plugins.sh` не нужно учить маппинг "Dockerfile → имя бинарника". Путь всегда `plugins/{group}/{name}/{version}/plugin`. Конфигурация в БД тоже единообразна: `"command": ["/plugins/{group}/{name}/{version}/plugin"]`. -- **Consequences:** Имя файла не несёт семантики, но она закодирована в пути директории. - -### Decision: Разделение скриптов сборки и регистрации - -- **Context:** Сборка плагинов не требует запущенного сервиса. Регистрация требует. -- **Options considered:** - 1. Один скрипт `build-and-register.sh` — удобно, но требует сервиса для сборки. - 2. Два скрипта: `build-plugins.sh` + `register-plugins.sh`. -- **Decision:** Два отдельных скрипта. -- **Rationale:** Разделение ответственности. `build-plugins.sh` можно запускать до `docker compose up`. `register-plugins.sh` — после запуска сервиса. Разные точки отказа, разные зависимости. -- **Consequences:** Две отдельные таски в Taskfile, но workflow `task run` их оркестрирует. - -### Decision: Монтирование `./plugins` как read-only - -- **Context:** Плагины — статичные бинарники. Сервис только читает их. -- **Options considered:** - 1. Read-write mount (`./plugins:/plugins`). - 2. Read-only mount (`./plugins:/plugins:ro`). -- **Decision:** Read-only (`:ro`). -- **Rationale:** Принцип наименьших привилегий. Сервис не должен модифицировать плагины. Защищает от случайной перезаписи. -- **Consequences:** Если понадобится hot-reload плагинов — потребуется убрать `:ro`. - ---- - -## 2.5 Модели данных - -Новых типов данных нет. Существующие типы (`PluginConfig`, `plugin`, `Registry`) были обновлены в `disk-plugin-execution` и не требуют дополнительных изменений. - ---- - -## 2.6 Корректностные свойства - -``` -Property 1: Dockerfile output -Category: Equivalence -Statement: For all Dockerfile-ов в `registry/`, `docker build --output=` создаёт исполняемый файл `/plugin`. -Validates: Requirements 1.1, 1.3 -``` - -``` -Property 2: UPX compression -Category: Propagation -Statement: For all собранных plugin binary, размер файла меньше размера несжатого бинарника (UPX применён). -Validates: Requirements 1.2 -``` - -``` -Property 3: Build script fail-fast -Category: Absence -Statement: For all запусков `build-plugins.sh`, при ошибке `docker build` для любого плагина скрипт завершается немедленно с ненулевым exit-кодом. Частично собранные наборы невозможны. -Validates: Requirements 2.2 -``` - -``` -Property 4: Build script completeness -Category: Equivalence -Statement: For all Dockerfile-ов в `registry/`, `build-plugins.sh` создаёт соответствующий `plugins/{group}/{name}/{version}/plugin`. -Validates: Requirements 2.1, 2.3 -``` - -``` -Property 5: Registration idempotency -Category: Absence -Statement: For all вызовов `register-plugins.sh` для уже зарегистрированных плагинов, ошибка `ALREADY_EXISTS` не приводит к аварийному завершению скрипта. -Validates: Requirements 3.2 -``` - -``` -Property 6: Registration completeness -Category: Propagation -Statement: For all плагинов в `plugins/`, `register-plugins.sh` вызывает `CreatePlugin` с корректными `group`, `name`, `version` и `config.command`. -Validates: Requirements 3.1 -``` - -``` -Property 7: Registration fail-fast -Category: Absence -Statement: For all ошибок gRPC (кроме `ALREADY_EXISTS`), `register-plugins.sh` немедленно завершается с ненулевым exit-кодом. -Validates: Requirements 3.3 -``` - -``` -Property 8: Config correctness -Category: Propagation -Statement: For all конфигурационных файлов (`config.yml`, `config.local.yml`), секция `registry` содержит `plugins_dir` и `max_output_size`, а не `domain`. -Validates: Requirements 4.1, 4.2, 4.3 -``` - -``` -Property 9: Docker compose cleanup -Category: Absence -Statement: For all содержимого `docker-compose.yml`, отсутствуют: сервис `registry`, volume `registry-data`, mount `docker.sock`. -Validates: Requirements 5.1, 5.2 -``` - -``` -Property 10: Plugins volume mount -Category: Propagation -Statement: For all запусков `docker compose up`, сервис `service` имеет read-only volume mount `./plugins:/plugins:ro`. -Validates: Requirements 5.1 -``` - -``` -Property 11: Taskfile cleanup -Category: Absence -Statement: For all содержимого `Taskfile.yml`, отсутствуют таски `local-push-registry` и `local-push-required`. -Validates: Requirements 6.4 -``` - -``` -Property 12: Taskfile new tasks -Category: Equivalence -Statement: For all запусков `task build-plugins` и `task register-plugins`, вызываются `build-plugins.sh` и `register-plugins.sh` соответственно. -Validates: Requirements 6.1, 6.2 -``` - -``` -Property 13: Task run deps -Category: Propagation -Statement: For all запусков `task run`, зависимость `local-push-registry` заменена на `build-plugins`. -Validates: Requirements 6.3 -``` - -``` -Property 14: Push script removal -Category: Absence -Statement: For all файлов в корне репозитория, `push.sh` не существует. -Validates: Requirements 7.1 -``` - ---- - -## 2.7 Обработка ошибок - -| Сценарий | Обнаружение | Действие | -|----------|------------|---------| -| `docker build` падает для одного из Dockerfile-ов | Non-zero exit code от `docker build` | `build-plugins.sh` завершается немедленно (`set -e`) | -| Docker daemon не запущен | `docker build` возвращает ошибку "Cannot connect to the Docker daemon" | Скрипт падает с понятной ошибкой | -| BuildKit не включён | `--output` не поддерживается | Скрипт устанавливает `DOCKER_BUILDKIT=1` перед вызовом | -| gRPC сервис не доступен | `grpcurl` возвращает ошибку connection refused | `register-plugins.sh` падает с ненулевым exit-кодом | -| Плагин уже зарегистрирован | gRPC возвращает `ALREADY_EXISTS` | `register-plugins.sh` логирует warning и продолжает | -| gRPC вызов возвращает другую ошибку | Non-zero exit от `grpcurl` + статус != `ALREADY_EXISTS` | `register-plugins.sh` немедленно завершается | -| Директория `plugins/` пуста при регистрации | Нет поддиректорий с бинарниками | `register-plugins.sh` завершается с warning (ничего не зарегистрировано) | - ---- - -## 2.8 Стратегия тестирования - -**Test Style Source:** Tier 2 -- Evidence: `internal/adapters/registry/registry_test.go` (удалён в `disk-plugin-execution`, но паттерны из него доступны), `internal/core/pool_test.go` (удалён) -- Key patterns: стандартный `go test`, table-driven tests. PBT unavailable — using targeted unit tests as substitute. - -**Project Commands:** - -| Action | Command | -|--------|---------| -| Test | `go test ./...` | -| Build | `go build -o main ./cmd/main.go` | -| Lint | `golangci-lint run ./...` | -| Generate | `easyp --cfg easyp.yaml generate` | - -NOTE: Эта фича в основном затрагивает скрипты и конфиги (bash, YAML, Dockerfile), а не Go-код. Основная верификация — ручная/интеграционная: запуск скриптов, проверка файлов, запуск сервиса. Go-тесты проверяют, что существующий код не сломан. - -### Unit Tests - -| Test | Description | Tags | -|------|-------------|------| -| `TestBuild_GoCompiles` | `go build -o main ./cmd/main.go` компилируется без ошибок после обновления конфигов | `Feature/build` | -| `TestExisting_GoTests` | Все существующие Go-тесты проходят: `go test ./...` | `Feature/regression` | - -### Property-Based Tests (manual verification scripts) - -| Test | Property | Generator description | Tags | -|------|----------|-----------------------|------| -| `verify_dockerfile_output` | CP-1 | Для каждого Dockerfile: `docker build --output=/tmp/test_plugin/ registry/{path}/`, проверить что `/tmp/test_plugin/plugin` существует и исполняем | `Property/1` | -| `verify_upx_compression` | CP-2 | Для собранного бинарника: проверить что `file plugin` показывает "UPX compressed" или размер < порога | `Property/2` | -| `verify_build_failfast` | CP-3 | Создать невалидный Dockerfile, запустить `build-plugins.sh`, проверить что exit code != 0 | `Property/3` | -| `verify_build_completeness` | CP-4 | Запустить `build-plugins.sh`, проверить что для каждого Dockerfile есть `plugins/{...}/plugin` | `Property/4` | -| `verify_register_idempotent` | CP-5 | Запустить `register-plugins.sh` дважды, проверить что второй запуск завершается успешно (exit 0) | `Property/5` | -| `verify_register_completeness` | CP-6 | Запустить `register-plugins.sh`, через `grpcurl` вызвать `Plugins`, проверить наличие всех плагинов | `Property/6` | -| `verify_register_failfast` | CP-7 | Запустить `register-plugins.sh` без работающего сервиса, проверить что exit code != 0 | `Property/7` | -| `verify_config_fields` | CP-8 | Проверить что `config.yml` и `config.local.yml` содержат `plugins_dir` и `max_output_size`, не содержат `domain` | `Property/8` | -| `verify_compose_cleanup` | CP-9 | Проверить что `docker-compose.yml` не содержит `registry`, `registry-data`, `docker.sock` | `Property/9` | -| `verify_compose_volume` | CP-10 | Проверить что `docker-compose.yml` содержит `./plugins:/plugins:ro` | `Property/10` | -| `verify_taskfile_cleanup` | CP-11 | Проверить что `Taskfile.yml` не содержит `local-push-registry`, `local-push-required` | `Property/11` | -| `verify_taskfile_new` | CP-12 | Проверить что `Taskfile.yml` содержит `build-plugins` и `register-plugins` | `Property/12` | -| `verify_run_deps` | CP-13 | Проверить что таска `run` зависит от `build-plugins` а не от `local-push-registry` | `Property/13` | -| `verify_push_deleted` | CP-14 | Проверить что `push.sh` не существует в корне репозитория | `Property/14` | diff --git a/.spec/features/local-test-env/approved/explore.md b/.spec/features/local-test-env/approved/explore.md deleted file mode 100644 index 7c7f364..0000000 --- a/.spec/features/local-test-env/approved/explore.md +++ /dev/null @@ -1,152 +0,0 @@ -# Exploration: local-test-env - -## Введение (Intent) - -После миграции на `disk-plugin-execution` механизм выполнения плагинов изменился с Docker-контейнеров на запуск бинарников с диска. Нужно адаптировать локальное окружение разработчика: конфиги, docker-compose, Taskfile — чтобы можно было легко поставить пару плагинов для тестов и запустить сервис. - -## Исследование (Investigation) - -### Существующие плагины в `registry/` - -4 Dockerfile-а, все с идентичной структурой (multi-stage: `golang:alpine` → `scratch`): - -| Плагин | Dockerfile | Бинарник | -|--------|-----------|----------| -| `protocolbuffers/go:v1.36.10` | `registry/protocolbuffers/go/v1.36.10/Dockerfile` | `/protoc-gen-go` | -| `grpc/go:v1.5.1` | `registry/grpc/go/v1.5.1/Dockerfile` | `/protoc-gen-go-grpc` | -| `grpc-ecosystem/gateway:v2.27.3` | `registry/grpc-ecosystem/gateway/v2.27.3/Dockerfile` | `/protoc-gen-grpc-gateway` | -| `grpc-ecosystem/openapiv2:v2.27.3` | `registry/grpc-ecosystem/openapiv2/v2.27.3/Dockerfile` | `/protoc-gen-openapiv2` | - -### Конфиги — остатки старого подхода - -- `config.yml` (строка 13): `registry.domain: "localhost:5005"` — старое поле, не используемое новым кодом. Нужно заменить на `plugins_dir` + `max_output_size`. -- `config.local.yml` (строка 13): тоже `registry.domain: "localhost:5005"`. -- `cmd/main.go` (строка 76–79): структура `registryConfig` уже использует `PluginsDir` и `MaxOutputSize` — конфиги просто отстали. - -### docker-compose.yml - -- Сервис `registry` (`easyp-registry`, порт 5005) — Docker Registry v3, больше не нужен для plugin execution. -- Сервис `service` всё ещё монтирует `/var/run/docker.sock` (строка 211) — больше не нужен. -- Нет volume для `./plugins:/plugins`. - -### Taskfile.yml - -- `local-push-registry` — запускает `./push.sh` для пуша всех образов в локальный registry. Устарело. -- `local-push-required` — собирает и пушит образы `protoc-gen-go` и `protoc-gen-go-grpc`. Нужно переделать. - -### .gitignore - -- `plugins/` уже в `.gitignore` (строка 9). ✅ - -### Подход: `docker build --output` - -Переделать Dockerfile-ы так, чтобы они **только собирали бинарник** и ничего больше. Финальный стейдж — просто `COPY` в `/plugin`. Затем вызов с `--output` копирует результат прямо на диск: - -**Пример переделанного Dockerfile (`registry/protocolbuffers/go/v1.36.10/Dockerfile`):** -```dockerfile -FROM golang:1.25-alpine3.22 - -ENV CGO_ENABLED=0 GOOS=linux GOARCH=amd64 -RUN apk add upx=5.0.2-r0 --no-cache - -RUN --mount=type=cache,target=/go/pkg/mod \ - go install -ldflags "-s -w" -trimpath google.golang.org/protobuf/cmd/protoc-gen-go@v1.36.10 \ - && mv /go/bin/${GOOS}_${GOARCH}/protoc-gen-go /go/bin/protoc-gen-go || true \ - && upx --best --lzma /go/bin/protoc-gen-go - -FROM scratch -COPY --from=0 /go/bin/protoc-gen-go /plugin -``` - -**Вызов:** -```bash -docker build --output=./plugins/protocolbuffers/go/v1.36.10/ registry/protocolbuffers/go/v1.36.10/ -``` - -Результат: `./plugins/protocolbuffers/go/v1.36.10/plugin` — готовый бинарник. Одна команда, без промежуточных контейнеров. - -### Регистрация плагинов через API - -После сборки бинарников и запуска сервиса, плагины регистрируются через gRPC API `CreatePlugin`: - -```bash -grpcurl -plaintext -d '{ - "group": "protocolbuffers", - "name": "go", - "version": "v1.36.10", - "config": {"command": ["/plugins/protocolbuffers/go/v1.36.10/plugin"]}, - "tags": ["go", "official"] -}' localhost:8080 api.generator.v1.ServiceAPI/CreatePlugin -``` - -Скрипт `register-plugins.sh` (или часть `build-plugins.sh`) будет вызывать `CreatePlugin` для каждого собранного плагина. Это лучше, чем прямой SQL, потому что: -- Проходит через всю цепочку валидации (`ValidateConfig`). -- Не нужен доступ к PostgreSQL напрямую. -- Идемпотентность: если плагин уже зарегистрирован (`ALREADY_EXISTS`), скрипт просто продолжает. - -## Инструменты сборки (Build Tooling) - -- **Orchestrator:** Taskfile v3 -- **Test:** `go test ./...` -- **Build:** `go build -o main ./cmd/main.go` -- **Lint:** `golangci-lint run ./...` -- **Generate:** `easyp --cfg easyp.yaml generate` -- **Source:** `Taskfile.yml` - -## Рассмотренные варианты (Options Considered) - -### Вариант А: Переделать Dockerfile-ы + `docker build --output` (рекомендован) - -- **Описание:** Упростить Dockerfile-ы: убрать `ENTRYPOINT`, `USER`, `/etc/passwd`. Финальный стейдж `FROM scratch` содержит только `COPY --from=0 ... /plugin`. Сборка через `docker build --output=./plugins/{path}/ registry/{path}/` — Docker выплёвывает бинарник прямо на диск. Скрипт `build-plugins.sh` обходит все Dockerfile-ы и вызывает эту команду. -- **Плюсы:** - - Dockerfile-ы становятся чище — одна ответственность (билд бинарника). - - Не нужны промежуточные `docker create` / `docker cp` / `docker rm`. - - Кросс-компиляция из коробки, UPX-сжатие. - - Единый источник правды для версий плагинов. -- **Минусы:** - - Требует BuildKit (`DOCKER_BUILDKIT=1`), но он включён по умолчанию в Docker >= 23.0. -- **Сложность:** Низкая. - -### Вариант Б: `docker build` + `docker create` + `docker cp` - -- **Описание:** Оставить Dockerfile-ы как есть (multi-stage с scratch), собирать образ, создавать контейнер, копировать бинарник, удалять контейнер. -- **Плюсы:** Не меняет Dockerfile-ы. -- **Минусы:** 4 команды вместо 1. Нужно чистить контейнеры. Dockerfile-ы содержат ненужные `ENTRYPOINT`/`USER`. -- **Сложность:** Низкая, но больше boilerplate. - -## Ограничения и риски (Constraints & Risks) - -- **Кросс-платформенность:** `docker compose up` запускает Linux-контейнер. `task run-local` запускает на macOS. Dockerfile-ы сейчас хардкодят `GOOS=linux GOARCH=amd64`. Для `task run-local` нужен будет отдельный режим — но это Deferred (v2). -- **Изменение конфигов:** `config.yml` и `config.local.yml` нужно обновить — но поле `domain` уже не используется кодом, так что ничего не ломается. -- **BuildKit:** `--output` требует BuildKit, который включён по умолчанию в Docker >= 23.0. Для старых версий нужен `DOCKER_BUILDKIT=1`. - -## Рекомендованное направление (Recommended Direction) - -**Вариант А** — переделать Dockerfile-ы + `docker build --output`. Создать скрипт `build-plugins.sh`, который: -1. Обходит все Dockerfile-ы в `registry/`. -2. Для каждого вызывает `docker build --output=./plugins/{group}/{name}/{version}/` . -3. Результат: готовые бинарники в `./plugins/`. - -Плюс обновить конфиги, docker-compose, Taskfile и добавить механизм регистрации плагинов в БД. - -## Границы области (Scope Boundaries) - -- **Must-have (v1):** - - Переделать 4 Dockerfile-а в `registry/` (убрать `ENTRYPOINT`/`USER`/`passwd`, финальный стейдж выдаёт только `/plugin`). - - Скрипт `build-plugins.sh` для сборки всех плагинов через `docker build --output`. - - Обновление `config.yml` и `config.local.yml`: `plugins_dir` + `max_output_size` вместо `domain`. - - Обновление `docker-compose.yml`: убрать `docker.sock` mount, убрать сервис `registry`, убрать volume `registry-data`, добавить volume `./plugins:/plugins`. - - Обновление `Taskfile.yml`: переделать `local-push-required` → `build-plugins`, убрать `local-push-registry`. - - Удаление `push.sh`. - - Скрипт или таска для регистрации плагинов через gRPC API (`CreatePlugin`). -- **Deferred (v2):** - - Режим `--local` для сборки macOS-бинарников (`task run-local`). -- **Needs spike:** - - Нет. - -## Допущения и открытые вопросы (Assumptions & Open Questions) - -- [ASSUMPTION: Docker >= 23.0 установлен у всех разработчиков (BuildKit включён по умолчанию).] -- [ASSUMPTION: Все Dockerfile-ы в `registry/` имеют предсказуемую структуру: один build-стейдж, один бинарник.] -- [ASSUMPTION: Для `task up` достаточно Linux/amd64 бинарников.] -- Открытых вопросов нет — пользователь подтвердил полную очистку docker-compose от registry и docker.sock. diff --git a/.spec/features/local-test-env/approved/implementation.md b/.spec/features/local-test-env/approved/implementation.md deleted file mode 100644 index 09e3073..0000000 --- a/.spec/features/local-test-env/approved/implementation.md +++ /dev/null @@ -1,38 +0,0 @@ -# local-test-env — Implementation Summary - -**Date:** 2026-05-24 - -## Выполненные задачи - -- [x] **T-1** GREEN — Baseline (go build + go test проходят) -- [x] **T-2** CODE — Переделаны 4 Dockerfile-а (убраны ENTRYPOINT/USER/passwd, финальный стейдж `/plugin`) -- [x] **T-3** CODE — Создан `build-plugins.sh` -- [x] **T-4** CODE — Создан `register-plugins.sh` -- [x] **T-5** CODE — Обновлены config.yml, config.local.yml, docker-compose.yml, Taskfile.yml, удалён push.sh -- [x] **T-6** VERIFY — Все проверки пройдены -- [x] **T-7** GATE — Финальная контрольная точка пройдена - -## Изменённые файлы - -| File | Change | -|------|--------| -| `registry/protocolbuffers/go/v1.36.10/Dockerfile` | Упрощён: `FROM scratch` + `COPY /plugin` | -| `registry/grpc/go/v1.5.1/Dockerfile` | Упрощён: `FROM scratch` + `COPY /plugin` | -| `registry/grpc-ecosystem/gateway/v2.27.3/Dockerfile` | Упрощён: `FROM scratch` + `COPY /plugin` | -| `registry/grpc-ecosystem/openapiv2/v2.27.3/Dockerfile` | Упрощён: `FROM scratch` + `COPY /plugin` | -| `build-plugins.sh` | **NEW** — Скрипт сборки бинарников через `docker build --output` | -| `register-plugins.sh` | **NEW** — Скрипт регистрации через gRPC CreatePlugin | -| `config.yml` | `registry.domain` → `registry.plugins_dir` + `max_output_size` | -| `config.local.yml` | `registry.domain` → `registry.plugins_dir` + `max_output_size` | -| `docker-compose.yml` | Убран `registry`, `registry-data`, `docker.sock` → `./plugins:/plugins:ro` | -| `Taskfile.yml` | Убраны `local-push-*`, добавлены `build-plugins` + `register-plugins` | -| `push.sh` | **DELETED** | - -## Результат верификации - -- ✓ `go build` — компилируется -- ✓ `go test ./...` — все тесты проходят -- ✓ Нет ENTRYPOINT в Dockerfile-ах -- ✓ `/plugin` присутствует в 4 Dockerfile-ах -- ✓ `build-plugins.sh` и `register-plugins.sh` исполняемые -- ✓ `push.sh` удалён diff --git a/.spec/features/local-test-env/approved/requirements.md b/.spec/features/local-test-env/approved/requirements.md deleted file mode 100644 index a894e61..0000000 --- a/.spec/features/local-test-env/approved/requirements.md +++ /dev/null @@ -1,98 +0,0 @@ -# local-test-env — Requirements - -**Status:** Draft -**Date:** 2026-05-24 - -## Обзор - -Адаптация локального окружения разработчика к новому способу выполнения плагинов (бинарники с диска вместо Docker-контейнеров). Включает: переделку Dockerfile-ов из `registry/` для сборки бинарников через `docker build --output`, скрипты для сборки и регистрации плагинов, очистку docker-compose от устаревших сервисов (registry, docker.sock), обновление конфигов и Taskfile. - -## Глоссарий - -| Термин | Определение | Code Artifact | -|--------|------------|---------------| -| Plugin binary | Скомпилированный Go-бинарник плагина, принимающий `CodeGeneratorRequest` на stdin и отдающий `CodeGeneratorResponse` на stdout | `./plugins/{group}/{name}/{version}/plugin` | -| `PluginConfig` | JSON-конфигурация плагина с массивом `command`, опциональными `env` и `timeout` | `internal/adapters/registry/registry.go` → `PluginConfig` | -| `build-plugins.sh` | Скрипт сборки plugin binary из Dockerfile-ов через `docker build --output` | `build-plugins.sh` | -| `register-plugins.sh` | Скрипт регистрации собранных плагинов через gRPC API `CreatePlugin` | `register-plugins.sh` | - ---- - -## Требования - -### 1. Dockerfile-ы - -**REQ-1.1** WHEN `docker build --output=./plugins/{group}/{name}/{version}/ registry/{group}/{name}/{version}/` выполняется для любого Dockerfile из `registry/`, the system SHALL создать файл `./plugins/{group}/{name}/{version}/plugin` — исполняемый Linux/amd64 бинарник. - -**REQ-1.2** WHEN Dockerfile собирается, the system SHALL использовать UPX-сжатие для минимизации размера бинарника. - -**REQ-1.3** WHEN финальный стейдж Dockerfile описывается, the system SHALL содержать только `FROM scratch` и `COPY` бинарника в `/plugin` — без `ENTRYPOINT`, `USER`, или `/etc/passwd`. - -### 2. Скрипт сборки (`build-plugins.sh`) - -**REQ-2.1** WHEN `build-plugins.sh` запускается, the system SHALL найти все файлы `Dockerfile` в директории `registry/` рекурсивно и собрать каждый через `docker build --output`. - -**REQ-2.2** WHEN `docker build` для любого плагина завершается с ошибкой, the system SHALL немедленно остановить выполнение скрипта с ненулевым exit-кодом (`set -e`). - -**REQ-2.3** WHEN `build-plugins.sh` завершается успешно, the system SHALL создать для каждого Dockerfile файл `./plugins/{group}/{name}/{version}/plugin` с правами на исполнение. - -### 3. Скрипт регистрации (`register-plugins.sh`) - -**REQ-3.1** WHEN `register-plugins.sh` запускается, the system SHALL для каждого собранного плагина в директории `plugins/` вызвать gRPC метод `CreatePlugin` с полями `group`, `name`, `version` и `config.command = ["/plugins/{group}/{name}/{version}/plugin"]`. - -**REQ-3.2** WHEN gRPC-вызов `CreatePlugin` возвращает ошибку `ALREADY_EXISTS`, the system SHALL пропустить этот плагин и продолжить с остальными (не завершаться с ошибкой). - -**REQ-3.3** WHEN gRPC-вызов `CreatePlugin` возвращает любую другую ошибку (кроме `ALREADY_EXISTS`), the system SHALL немедленно остановить выполнение с ненулевым exit-кодом. - -### 4. Конфигурация - -**REQ-4.1** WHEN сервис запускается с `config.yml`, the system SHALL использовать поле `registry.plugins_dir` (по умолчанию `/plugins`) вместо устаревшего `registry.domain`. - -**REQ-4.2** WHEN сервис запускается с `config.yml`, the system SHALL использовать поле `registry.max_output_size` (по умолчанию `67108864`, 64 МБ). - -**REQ-4.3** WHEN сервис запускается с `config.local.yml`, the system SHALL использовать `registry.plugins_dir` указывающий на локальную директорию `./plugins`. - -### 5. Docker Compose - -**REQ-5.1** WHEN `docker-compose.yml` описывает сервис `service`, the system SHALL монтировать volume `./plugins:/plugins` вместо `/var/run/docker.sock`. - -**REQ-5.2** WHEN `docker-compose.yml` описывает инфраструктуру, the system SHALL не содержать сервис `registry` и volume `registry-data`. - -### 6. Taskfile - -**REQ-6.1** WHEN `task build-plugins` выполняется, the system SHALL вызвать `build-plugins.sh` для сборки всех плагинов. - -**REQ-6.2** WHEN `task register-plugins` выполняется, the system SHALL вызвать `register-plugins.sh` для регистрации всех плагинов через gRPC API. - -**REQ-6.3** WHEN `task run` выполняется, the system SHALL использовать `build-plugins` вместо устаревшей `local-push-registry` в зависимостях. - -**REQ-6.4** WHEN Taskfile описывает таски, the system SHALL не содержать `local-push-registry` и `local-push-required`. - -### 7. Удаление устаревших файлов - -**REQ-7.1** WHEN проект собран, the system SHALL не содержать файл `push.sh` в корне репозитория. - ---- - -## Порядок зависимостей (Topological Order) - -``` -REQ-1.1..1.3 → REQ-2.1..2.3 → REQ-3.1..3.3 -Причина: Dockerfile-ы (1.x) нужны для скрипта сборки (2.x), который создаёт бинарники для скрипта регистрации (3.x). - -REQ-4.1..4.3 (независимы — можно параллельно с 1–3) -REQ-5.1..5.2 (независимы — можно параллельно с 1–3) -REQ-6.1..6.4 → зависят от REQ-2.x и REQ-3.x (таски ссылаются на скрипты) -REQ-7.1 (независим) -``` - ---- - -## Команды верификации - -| Действие | Команда | Источник | -|----------|---------|----------| -| Test | `go test ./...` | Taskfile.yml | -| Build | `go build -o main ./cmd/main.go` | Taskfile.yml | -| Lint | `golangci-lint run ./...` | Taskfile.yml | -| Generate | `easyp --cfg easyp.yaml generate` | Taskfile.yml | diff --git a/.spec/features/local-test-env/approved/review.md b/.spec/features/local-test-env/approved/review.md deleted file mode 100644 index d7a825d..0000000 --- a/.spec/features/local-test-env/approved/review.md +++ /dev/null @@ -1,153 +0,0 @@ -# Code Review: local-test-env - -## Verdict: PASS - -Все 17 требований реализованы. Код соответствует дизайну. Нет security-проблем (изменения затрагивают только скрипты, конфиги и Dockerfile-ы — не Go-код). Сборка и тесты проходят. Один `nit`-уровневый замечание. - ---- - -## Change Set - -| File | Status | Notes | -|------|--------|-------| -| `registry/protocolbuffers/go/v1.36.10/Dockerfile` | ✅ Planned | Упрощён: `FROM scratch` + `COPY /plugin` | -| `registry/grpc/go/v1.5.1/Dockerfile` | ✅ Planned | Упрощён: `FROM scratch` + `COPY /plugin` | -| `registry/grpc-ecosystem/gateway/v2.27.3/Dockerfile` | ✅ Planned | Упрощён: `FROM scratch` + `COPY /plugin` | -| `registry/grpc-ecosystem/openapiv2/v2.27.3/Dockerfile` | ✅ Planned | Упрощён: `FROM scratch` + `COPY /plugin` | -| `build-plugins.sh` | ✅ Planned | NEW — скрипт сборки | -| `register-plugins.sh` | ✅ Planned | NEW — скрипт регистрации | -| `config.yml` | ✅ Planned | `plugins_dir` + `max_output_size` | -| `config.local.yml` | ✅ Planned | `plugins_dir` + `max_output_size` | -| `docker-compose.yml` | ✅ Planned | Удалён registry, docker.sock → plugins:ro | -| `Taskfile.yml` | ✅ Planned | Новые таски, обновлены зависимости | -| `push.sh` | ✅ Planned | DELETED | -| `.spec/features/local-test-env/*` | ⚠️ Unexpected | SDD pipeline artifacts — ожидаемые, не scope creep | - ---- - -## Requirements Traceability - -| Requirement | Test(s) | Code | CP | Verdict | -|-------------|---------|------|----|---------| -| REQ-1.1 | verify_dockerfile_output (manual) | `registry/*/Dockerfile` → `FROM scratch` + `COPY /plugin` | CP-1 | ✅ | -| REQ-1.2 | verify_upx_compression (manual) | `registry/*/Dockerfile` → `upx --best --lzma` в build-стейдже | CP-2 | ✅ | -| REQ-1.3 | verify_dockerfile_output (manual) | Нет `ENTRYPOINT`/`USER`/`passwd` | CP-1 | ✅ | -| REQ-2.1 | verify_build_completeness (manual) | `build-plugins.sh:18-39` — `find` + `docker build --output` | CP-4 | ✅ | -| REQ-2.2 | verify_build_failfast (manual) | `build-plugins.sh:7` — `set -euo pipefail` | CP-3 | ✅ | -| REQ-2.3 | verify_build_completeness (manual) | `build-plugins.sh:35` — `chmod +x` | CP-4 | ✅ | -| REQ-3.1 | verify_register_completeness (manual) | `register-plugins.sh:30-57` — `grpcurl` → `CreatePlugin` | CP-6 | ✅ | -| REQ-3.2 | verify_register_idempotent (manual) | `register-plugins.sh:49-51` — `ALREADY_EXISTS` → continue | CP-5 | ✅ | -| REQ-3.3 | verify_register_failfast (manual) | `register-plugins.sh:53-55` — else → `exit 1` | CP-7 | ✅ | -| REQ-4.1 | — | `config.yml:13` — `plugins_dir: "/plugins"` | CP-8 | ✅ | -| REQ-4.2 | — | `config.yml:14` — `max_output_size: 67108864` | CP-8 | ✅ | -| REQ-4.3 | — | `config.local.yml:13` — `plugins_dir: "./plugins"` | CP-8 | ✅ | -| REQ-5.1 | — | `docker-compose.yml:200` — `./plugins:/plugins:ro` | CP-9, CP-10 | ✅ | -| REQ-5.2 | — | `docker-compose.yml` — нет `registry`, `registry-data` | CP-9 | ✅ | -| REQ-6.1 | — | `Taskfile.yml:27-33` — `build-plugins` → `./build-plugins.sh` | CP-12 | ✅ | -| REQ-6.2 | — | `Taskfile.yml:35-41` — `register-plugins` → `./register-plugins.sh` | CP-12 | ✅ | -| REQ-6.3 | — | `Taskfile.yml:46` — `deps: build-plugins` | CP-13 | ✅ | -| REQ-6.4 | — | Нет `local-push-*` в Taskfile | CP-11 | ✅ | -| REQ-7.1 | — | `push.sh` удалён | CP-14 | ✅ | - ---- - -## Design Conformance - -### 3.1 Architectural Boundaries -Все новые файлы (`build-plugins.sh`, `register-plugins.sh`) расположены в корне проекта — корректно для скриптов оркестрации. Dockerfile-ы остались на своих местах в `registry/`. Конфиги и compose-файл обновлены на месте. ✅ - -### 3.2 Data Models -Новых типов данных нет. Конфигурация `registryConfig` в `cmd/main.go` уже использует `PluginsDir` и `MaxOutputSize` — конфиги теперь соответствуют коду. ✅ - -### 3.3 API Contracts -API контракт (proto) не изменён. `CreatePlugin` RPC используется скриптом регистрации — соответствует дизайну. ✅ - -### 3.4 Error Handling -- `build-plugins.sh`: `set -euo pipefail` + exit 1 для неожиданных путей. ✅ -- `register-plugins.sh`: `ALREADY_EXISTS` → skip, другие ошибки → exit 1. ✅ -- Проверка `grpcurl` installed. ✅ -- Проверка `plugins/` directory exists. ✅ - -### 3.5 Correctness Properties -Все 14 CP из дизайна выполнены (см. Requirements Traceability). ✅ - -### 3.6 Documentation Consistency -Mermaid-диаграмма в design.md соответствует фактической структуре: Dockerfiles → build-plugins.sh → plugins/ → service + register-plugins.sh → gRPC → DB. ✅ - ---- - -## Code Quality - -### 4.1 Naming & Clarity -- Скрипты названы описательно: `build-plugins.sh`, `register-plugins.sh`. ✅ -- Переменные в скриптах (`group`, `name`, `version`, `output_dir`) понятные. ✅ -- Taski в Taskfile имеют `desc:` поля. ✅ - -### 4.2 Dead Code & Debug Artifacts -Нет `TODO`-ов, нет debug-вывода. ✅ - -### 4.3 Scope Creep -Все изменения соответствуют плану. `.spec/features/` — артефакты SDD pipeline, не scope creep. ✅ - -### 4.4 Test Quality -Фича затрагивает скрипты/конфиги — unit-тесты Go не применимы. Верификация через manual/integration тесты (запуск скриптов). Существующие Go-тесты не сломаны (все `[no test files]`). ✅ - ---- - -## Security - -Нет новых публичных API endpoints. Изменения затрагивают только: -- Bash-скрипты (не принимают внешний ввод кроме CLI-аргумента `host:port`) -- Конфиги (YAML) -- Dockerfile-ы (build-only, не runtime) -- Docker Compose (volume mount `:ro`) - -Security-проблем не обнаружено. ✅ - ---- - -## Verification Evidence - -- **Build:** -``` -go build -o main ./cmd/main.go -EXIT: 0 -``` - -- **Tests:** -``` -? github.com/easyp-tech/service/api/generator/v1 [no test files] -? github.com/easyp-tech/service/cmd [no test files] -? github.com/easyp-tech/service/cmd/mcp-smoke [no test files] -? github.com/easyp-tech/service/internal/adapters/audit [no test files] -? github.com/easyp-tech/service/internal/adapters/metrics [no test files] -? github.com/easyp-tech/service/internal/adapters/registry [no test files] -? github.com/easyp-tech/service/internal/api [no test files] -? github.com/easyp-tech/service/internal/core [no test files] -? github.com/easyp-tech/service/internal/database [no test files] -? github.com/easyp-tech/service/internal/database/connectors [no test files] -? github.com/easyp-tech/service/internal/database/internal [no test files] -? github.com/easyp-tech/service/internal/database/migrations [no test files] -? github.com/easyp-tech/service/internal/flags [no test files] -? github.com/easyp-tech/service/internal/grpchelper [no test files] -? github.com/easyp-tech/service/internal/license [no test files] -? github.com/easyp-tech/service/internal/monitor [no test files] -? github.com/easyp-tech/service/internal/ratelimiter [no test files] -? github.com/easyp-tech/service/internal/telemetry [no test files] -? github.com/easyp-tech/service/sdk [no test files] -EXIT: 0 -``` - ---- - -## Findings - -| ID | Severity | File | Description | Requirement | -|----|----------|------|-------------|-------------| -| F-1 | nit | `docker-compose.yml:161` | Пустая строка осталась после удаления `registry:` сервиса (двойной перенос). Визуально не критично. | — | - ---- - -## Recommendations - -1. **(nit) F-1:** Убрать лишнюю пустую строку в `docker-compose.yml:161` после удаления сервиса `registry`. diff --git a/.spec/features/local-test-env/approved/task-plan.md b/.spec/features/local-test-env/approved/task-plan.md deleted file mode 100644 index 347991b..0000000 --- a/.spec/features/local-test-env/approved/task-plan.md +++ /dev/null @@ -1,233 +0,0 @@ -# local-test-env — Task Plan - -**Work Type:** Migration -**Date:** 2026-05-24 - ---- - -**Test Style Source:** Tier 2 -- Evidence: `internal/adapters/registry/registry_test.go` (шаблон удалён, но паттерны известны), `internal/core/pool_test.go` -- Key patterns: стандартный `go test`, table-driven tests, `testing.T`. PBT unavailable — targeted unit tests as substitute. - -**Commands:** - -| Action | Command | Source | -|--------|---------|--------| -| Test | `go test ./...` | Taskfile.yml | -| Build | `go build -o main ./cmd/main.go` | Taskfile.yml | -| Lint | `golangci-lint run ./...` | Taskfile.yml | -| Generate | `easyp --cfg easyp.yaml generate` | Taskfile.yml | - ---- - -## Матрица покрытия - -| Requirement | Task(s) | Correctness Property | -|-------------|---------|----------------------| -| REQ-1.1 | T-2 | CP-1 (Equivalence) | -| REQ-1.2 | T-2 | CP-2 (Propagation) | -| REQ-1.3 | T-2 | CP-1 (Equivalence) | -| REQ-2.1 | T-3 | CP-4 (Equivalence) | -| REQ-2.2 | T-3 | CP-3 (Absence) | -| REQ-2.3 | T-3 | CP-4 (Equivalence) | -| REQ-3.1 | T-4 | CP-6 (Propagation) | -| REQ-3.2 | T-4 | CP-5 (Absence) | -| REQ-3.3 | T-4 | CP-7 (Absence) | -| REQ-4.1 | T-5 | CP-8 (Propagation) | -| REQ-4.2 | T-5 | CP-8 (Propagation) | -| REQ-4.3 | T-5 | CP-8 (Propagation) | -| REQ-5.1 | T-5 | CP-9, CP-10 (Absence, Propagation) | -| REQ-5.2 | T-5 | CP-9 (Absence) | -| REQ-6.1 | T-5 | CP-12 (Equivalence) | -| REQ-6.2 | T-5 | CP-12 (Equivalence) | -| REQ-6.3 | T-5 | CP-13 (Propagation) | -| REQ-6.4 | T-5 | CP-11 (Absence) | -| REQ-7.1 | T-5 | CP-14 (Absence) | - ---- - -## T-1: GREEN — Сохранение текущего поведения (Preservation Tests) - -*_Requirements: REQ-4.1, REQ-4.2_* -*_Complexity: mechanical_* - -GOAL: Убедиться, что существующие Go-тесты проходят ДО внесения изменений. - -1. **Запустить `go build -o main ./cmd/main.go`** — убедиться, что проект компилируется. -2. **Запустить `go test ./...`** — зафиксировать baseline. Все тесты должны проходить. - ---- - -## T-2: CODE — Переделать Dockerfile-ы в `registry/` - -*_Requirements: REQ-1.1, REQ-1.2, REQ-1.3_* -*_Preservation: CP-2_* -*_Complexity: mechanical_* - -GOAL: Упростить все 4 Dockerfile-а: убрать `ENTRYPOINT`, `USER`, `/etc/passwd`. Финальный стейдж — `FROM scratch` + `COPY ... /plugin`. - -1. **Изменить `registry/protocolbuffers/go/v1.36.10/Dockerfile`:** - - Убрать строки: `COPY --from=build --link /etc/passwd /etc/passwd`, `USER nobody`, `ENTRYPOINT [ "/protoc-gen-go" ]`. - - Заменить `COPY --from=build --link --chown=root:root /go/bin/protoc-gen-go /protoc-gen-go` на `COPY --from=build /go/bin/protoc-gen-go /plugin`. - -2. **Изменить `registry/grpc/go/v1.5.1/Dockerfile`:** - - Убрать строки: `COPY --from=build --link /etc/passwd /etc/passwd`, `USER nobody`, `ENTRYPOINT [ "/protoc-gen-go-grpc" ]`. - - Заменить `COPY --from=build --link --chown=root:root /go/bin/protoc-gen-go-grpc /protoc-gen-go-grpc` на `COPY --from=build /go/bin/protoc-gen-go-grpc /plugin`. - -3. **Изменить `registry/grpc-ecosystem/gateway/v2.27.3/Dockerfile`:** - - Убрать строки: `COPY --from=build --link --chown=root:root /etc/passwd /etc/passwd`, `USER nobody`, `ENTRYPOINT [ "/protoc-gen-grpc-gateway" ]`. - - Заменить `COPY --from=build --link --chown=root:root /go/bin/protoc-gen-grpc-gateway /protoc-gen-grpc-gateway` на `COPY --from=build /go/bin/protoc-gen-grpc-gateway /plugin`. - -4. **Изменить `registry/grpc-ecosystem/openapiv2/v2.27.3/Dockerfile`:** - - Убрать строки: `COPY --from=build --link --chown=root:root /etc/passwd /etc/passwd`, `USER nobody`, `ENTRYPOINT [ "/protoc-gen-openapiv2" ]`. - - Заменить `COPY --from=build --link /go/bin/protoc-gen-openapiv2 /protoc-gen-openapiv2` на `COPY --from=build /go/bin/protoc-gen-openapiv2 /plugin`. - ---- - -## T-3: CODE — Создать скрипт `build-plugins.sh` - -*_Requirements: REQ-2.1, REQ-2.2, REQ-2.3_* -*_Preservation: CP-1, CP-2_* -*_Complexity: standard_* - -GOAL: Создать bash-скрипт, который обходит все Dockerfile-ы в `registry/` и собирает бинарники через `docker build --output`. - -1. **Создать файл `build-plugins.sh` в корне проекта:** - - Шебанг: `#!/bin/bash` - - `set -euo pipefail` - - `export DOCKER_BUILDKIT=1` - - Обход: `find registry -name Dockerfile | sort` - - Для каждого Dockerfile: извлечь `{group}/{name}/{version}` из пути `registry/{group}/{name}/{version}/Dockerfile` - - Вызов: `docker build --output="./plugins/${group}/${name}/${version}/" "registry/${group}/${name}/${version}/"` - - Проверка: `chmod +x "./plugins/${group}/${name}/${version}/plugin"` - - Вывод прогресса: `echo "✓ Built {group}/{name}:{version}"` - -2. **Сделать `build-plugins.sh` исполняемым:** `chmod +x build-plugins.sh` - ---- - -## T-4: CODE — Создать скрипт `register-plugins.sh` - -*_Requirements: REQ-3.1, REQ-3.2, REQ-3.3_* -*_Preservation: CP-1, CP-2_* -*_Complexity: standard_* - -GOAL: Создать bash-скрипт, который обходит собранные плагины в `plugins/` и регистрирует их через gRPC API `CreatePlugin`. - -1. **Создать файл `register-plugins.sh` в корне проекта:** - - Шебанг: `#!/bin/bash` - - `set -euo pipefail` - - Переменная: `GRPC_HOST="${1:-localhost:8080}"` (host по умолчанию) - - Обход: `find plugins -name plugin -type f | sort` - - Для каждого найденного `plugins/{group}/{name}/{version}/plugin`: извлечь `group`, `name`, `version` из пути. - - Вызов `grpcurl`: - ``` - grpcurl -plaintext -d "{\"group\":\"${group}\",\"name\":\"${name}\",\"version\":\"${version}\",\"config\":{\"command\":[\"/plugins/${group}/${name}/${version}/plugin\"]}}" "${GRPC_HOST}" api.generator.v1.ServiceAPI/CreatePlugin - ``` - - Обработка ошибок: если `grpcurl` exit-код != 0, проверить stderr на `ALREADY_EXISTS`. Если да — вывести `"⚠ Already exists: {group}/{name}:{version}"` и `continue`. Иначе — `exit 1`. - - Вывод прогресса: `echo "✓ Registered {group}/{name}:{version}"` - -2. **Сделать `register-plugins.sh` исполняемым:** `chmod +x register-plugins.sh` - ---- - -## T-5: CODE — Обновить инфраструктуру (конфиги, compose, Taskfile, очистка) - -*_Requirements: REQ-4.1, REQ-4.2, REQ-4.3, REQ-5.1, REQ-5.2, REQ-6.1, REQ-6.2, REQ-6.3, REQ-6.4, REQ-7.1_* -*_Preservation: CP-1, CP-2, CP-3, CP-4_* -*_Complexity: standard_* - -GOAL: Привести конфиги, docker-compose, Taskfile в соответствие с новой архитектурой и удалить устаревшие файлы. - -1. **Изменить `config.yml` (строка 12–13):** - - Заменить: - ```yaml - registry: - domain: "localhost:5005" - ``` - - На: - ```yaml - registry: - plugins_dir: "/plugins" - max_output_size: 67108864 - ``` - -2. **Изменить `config.local.yml` (строка 12–13):** - - Заменить: - ```yaml - registry: - domain: "localhost:5005" - ``` - - На: - ```yaml - registry: - plugins_dir: "./plugins" - max_output_size: 67108864 - ``` - -3. **Изменить `docker-compose.yml`:** - - Удалить volume `registry-data:` из секции `volumes:` (строка 7). - - Удалить сервис `registry:` целиком (строки 163–172). - - В сервисе `service` → `volumes:` — удалить строку `- "/var/run/docker.sock:/var/run/docker.sock"` (строка 211). Добавить `- "./plugins:/plugins:ro"`. - -4. **Изменить `Taskfile.yml`:** - - Удалить таску `local-push-registry:` (строки 27–32). - - Удалить таску `local-push-required:` (строки 34–43). - - Добавить таску `build-plugins:`: - ```yaml - build-plugins: - dir: "{{.USER_WORKING_DIR}}" - preconditions: - - "test -f build-plugins.sh" - cmds: - - "./build-plugins.sh" - ``` - - Добавить таску `register-plugins:`: - ```yaml - register-plugins: - dir: "{{.USER_WORKING_DIR}}" - preconditions: - - "test -f register-plugins.sh" - cmds: - - "./register-plugins.sh" - ``` - - В таске `run:` → `deps:` — заменить `"local-push-registry"` на `"build-plugins"`. - - В таске `up-minimal:` → `cmds:` — убрать `registry` из аргументов `docker compose up -d postgres registry` → `docker compose up -d postgres`. - -5. **Удалить файл `push.sh`:** `rm push.sh` - ---- - -## T-6: VERIFY — Проверка результата - -*_Requirements: REQ-1.1, REQ-2.1, REQ-4.1, REQ-5.1, REQ-6.1, REQ-7.1_* -*_Complexity: standard_* - -GOAL: Убедиться, что всё работает после всех изменений. - -1. **Запустить `go build -o main ./cmd/main.go`** — проект должен компилироваться. -2. **Запустить `go test ./...`** — все существующие тесты должны проходить. -3. **Проверить `push.sh` удалён:** `test ! -f push.sh` -4. **Проверить что `docker-compose.yml` не содержит `registry`:** `grep -c 'registry' docker-compose.yml` — должно быть 0. -5. **Проверить что `Taskfile.yml` не содержит `local-push`:** `grep -c 'local-push' Taskfile.yml` — должно быть 0. -6. **Проверить что `config.yml` содержит `plugins_dir`:** `grep 'plugins_dir' config.yml` - ---- - -## T-7: GATE — Контрольная точка - -*_Requirements: ALL_* -*_Complexity: mechanical_* - -GOAL: Финальная проверка всех артефактов и зависимостей. - -1. **Запустить `go build -o main ./cmd/main.go`** — компиляция. -2. **Запустить `go test ./...`** — все тесты. -3. **Проверить структуру Dockerfile-ов:** - - `grep -c ENTRYPOINT registry/*/Dockerfile registry/*/*/Dockerfile registry/*/*/*/Dockerfile` — должно быть 0 совпадений. - - `grep -c '/plugin' registry/*/*/*/Dockerfile` — должно быть 4 (по одному на каждый Dockerfile). -4. **Проверить наличие скриптов:** - - `test -x build-plugins.sh` - - `test -x register-plugins.sh` -5. **Проверить отсутствие устаревших файлов:** - - `test ! -f push.sh` diff --git a/.spec/features/local-test-env/design.md b/.spec/features/local-test-env/design.md deleted file mode 100644 index ff779b6..0000000 --- a/.spec/features/local-test-env/design.md +++ /dev/null @@ -1,290 +0,0 @@ -# local-test-env — Design - -**Status:** Draft -**Date:** 2026-05-24 - -## 2.1 Обзор - -Проектирование локального тестового окружения для сервиса EasyP после миграции на disk-plugin-execution. Задача делится на 4 логические части: - -1. **Dockerfile-ы** — переделка 4 файлов в `registry/` для сборки бинарников через `docker build --output`. -2. **Скрипты** — `build-plugins.sh` (сборка) и `register-plugins.sh` (регистрация через gRPC API). -3. **Инфраструктура** — очистка `docker-compose.yml`, обновление конфигов, Taskfile. -4. **Очистка** — удаление `push.sh`. - ---- - -## 2.2 Архитектура - -```mermaid -graph TD - subgraph "Сборка плагинов" - DF1["registry/*/Dockerfile"] - BP["build-plugins.sh"]:::new - PD["./plugins/{group}/{name}/{version}/plugin"]:::new - - DF1 -->|"docker build --output"| BP - BP -->|"создаёт бинарники"| PD - end - - subgraph "Инфраструктура" - DC["docker-compose.yml"]:::modified - CFG["config.yml"]:::modified - CFGL["config.local.yml"]:::modified - TF["Taskfile.yml"]:::modified - end - - subgraph "Регистрация и запуск" - SVC["easyp-api-service"] - RP["register-plugins.sh"]:::new - DB["PostgreSQL"] - - PD -->|"volume mount"| SVC - RP -->|"gRPC CreatePlugin"| SVC - SVC -->|"INSERT plugins"| DB - end - - classDef new fill:#90EE90 - classDef modified fill:#FFD700 -``` - -**Порядок реализации:** -1. Dockerfile-ы (фундамент — без них скрипт сборки не работает) -2. `build-plugins.sh` (зависит от Dockerfile-ов) -3. Инфраструктура (config, docker-compose, Taskfile — параллельно) -4. `register-plugins.sh` (зависит от работающего сервиса) -5. Очистка (удаление `push.sh`) - ---- - -## 2.3 Компоненты и интерфейсы - -### Файлы, требующие изменений - -| File | Change Type | Description | -|------|-------------|-------------| -| `registry/protocolbuffers/go/v1.36.10/Dockerfile` | `[MODIFIED]` | Убрать второй стейдж с `ENTRYPOINT`/`USER`/`passwd`. Финальный стейдж: `FROM scratch` + `COPY --from=0 /go/bin/protoc-gen-go /plugin` | -| `registry/grpc/go/v1.5.1/Dockerfile` | `[MODIFIED]` | Аналогично: `COPY --from=0 /go/bin/protoc-gen-go-grpc /plugin` | -| `registry/grpc-ecosystem/gateway/v2.27.3/Dockerfile` | `[MODIFIED]` | Аналогично: `COPY --from=0 /go/bin/protoc-gen-grpc-gateway /plugin` | -| `registry/grpc-ecosystem/openapiv2/v2.27.3/Dockerfile` | `[MODIFIED]` | Аналогично: `COPY --from=0 /go/bin/protoc-gen-openapiv2 /plugin` | -| `build-plugins.sh` | `[NEW]` | Bash-скрипт: `find registry -name Dockerfile`, для каждого `docker build --output`. `set -e`. | -| `register-plugins.sh` | `[NEW]` | Bash-скрипт: обход `plugins/`, для каждого вызов `grpcurl` → `CreatePlugin`. `ALREADY_EXISTS` → skip. | -| `config.yml` | `[MODIFIED]` | Секция `registry:` — заменить `domain: "localhost:5005"` на `plugins_dir: "/plugins"` и `max_output_size: 67108864` | -| `config.local.yml` | `[MODIFIED]` | Секция `registry:` — заменить `domain: "localhost:5005"` на `plugins_dir: "./plugins"` и `max_output_size: 67108864` | -| `docker-compose.yml` | `[MODIFIED]` | Удалить сервис `registry`, volume `registry-data`. В `service`: убрать `docker.sock`, добавить `./plugins:/plugins:ro`. | -| `Taskfile.yml` | `[MODIFIED]` | Удалить `local-push-registry`, `local-push-required`. Добавить `build-plugins`, `register-plugins`. Обновить deps в `run`. | -| `push.sh` | `[DELETED]` | Устаревший скрипт для пуша образов в Docker Registry. | - -### Файлы, НЕ требующие изменений - -| File | Reason Unchanged | -|------|-----------------| -| `cmd/main.go` | Структура `registryConfig` уже использует `PluginsDir` и `MaxOutputSize` (обновлена в `disk-plugin-execution`) | -| `internal/adapters/registry/registry.go` | Логика `Generate()` уже работает с exec — не затрагивается | -| `internal/core/domain.go` | Доменная модель не меняется | -| `internal/core/pool.go` | WorkerPool не затрагивается | -| `api/generator/v1/generator.proto` | API контракт не меняется | -| `Dockerfile` (корневой) | Сервисный Dockerfile уже переделан в `disk-plugin-execution` (debian, VOLUME /plugins) | -| `migrate/*.sql` | Миграция `5.disk_plugin_config.sql` уже существует | -| `.gitignore` | `plugins/` уже добавлен (строка 9) | - ---- - -## 2.4 Ключевые решения (ADR) - -### Decision: Формат выходного бинарника — `/plugin` - -- **Context:** Каждый Dockerfile собирает плагин с уникальным именем (`protoc-gen-go`, `protoc-gen-go-grpc` и т.д.). Нужен единый путь для бинарника в финальном стейдже. -- **Options considered:** - 1. Оставить оригинальное имя (`/protoc-gen-go`) — разные имена для каждого плагина. - 2. Использовать единое имя `/plugin` — одинаковая структура для всех. -- **Decision:** Единое имя `/plugin`. -- **Rationale:** Скрипт `build-plugins.sh` не нужно учить маппинг "Dockerfile → имя бинарника". Путь всегда `plugins/{group}/{name}/{version}/plugin`. Конфигурация в БД тоже единообразна: `"command": ["/plugins/{group}/{name}/{version}/plugin"]`. -- **Consequences:** Имя файла не несёт семантики, но она закодирована в пути директории. - -### Decision: Разделение скриптов сборки и регистрации - -- **Context:** Сборка плагинов не требует запущенного сервиса. Регистрация требует. -- **Options considered:** - 1. Один скрипт `build-and-register.sh` — удобно, но требует сервиса для сборки. - 2. Два скрипта: `build-plugins.sh` + `register-plugins.sh`. -- **Decision:** Два отдельных скрипта. -- **Rationale:** Разделение ответственности. `build-plugins.sh` можно запускать до `docker compose up`. `register-plugins.sh` — после запуска сервиса. Разные точки отказа, разные зависимости. -- **Consequences:** Две отдельные таски в Taskfile, но workflow `task run` их оркестрирует. - -### Decision: Монтирование `./plugins` как read-only - -- **Context:** Плагины — статичные бинарники. Сервис только читает их. -- **Options considered:** - 1. Read-write mount (`./plugins:/plugins`). - 2. Read-only mount (`./plugins:/plugins:ro`). -- **Decision:** Read-only (`:ro`). -- **Rationale:** Принцип наименьших привилегий. Сервис не должен модифицировать плагины. Защищает от случайной перезаписи. -- **Consequences:** Если понадобится hot-reload плагинов — потребуется убрать `:ro`. - ---- - -## 2.5 Модели данных - -Новых типов данных нет. Существующие типы (`PluginConfig`, `plugin`, `Registry`) были обновлены в `disk-plugin-execution` и не требуют дополнительных изменений. - ---- - -## 2.6 Корректностные свойства - -``` -Property 1: Dockerfile output -Category: Equivalence -Statement: For all Dockerfile-ов в `registry/`, `docker build --output=` создаёт исполняемый файл `/plugin`. -Validates: Requirements 1.1, 1.3 -``` - -``` -Property 2: UPX compression -Category: Propagation -Statement: For all собранных plugin binary, размер файла меньше размера несжатого бинарника (UPX применён). -Validates: Requirements 1.2 -``` - -``` -Property 3: Build script fail-fast -Category: Absence -Statement: For all запусков `build-plugins.sh`, при ошибке `docker build` для любого плагина скрипт завершается немедленно с ненулевым exit-кодом. Частично собранные наборы невозможны. -Validates: Requirements 2.2 -``` - -``` -Property 4: Build script completeness -Category: Equivalence -Statement: For all Dockerfile-ов в `registry/`, `build-plugins.sh` создаёт соответствующий `plugins/{group}/{name}/{version}/plugin`. -Validates: Requirements 2.1, 2.3 -``` - -``` -Property 5: Registration idempotency -Category: Absence -Statement: For all вызовов `register-plugins.sh` для уже зарегистрированных плагинов, ошибка `ALREADY_EXISTS` не приводит к аварийному завершению скрипта. -Validates: Requirements 3.2 -``` - -``` -Property 6: Registration completeness -Category: Propagation -Statement: For all плагинов в `plugins/`, `register-plugins.sh` вызывает `CreatePlugin` с корректными `group`, `name`, `version` и `config.command`. -Validates: Requirements 3.1 -``` - -``` -Property 7: Registration fail-fast -Category: Absence -Statement: For all ошибок gRPC (кроме `ALREADY_EXISTS`), `register-plugins.sh` немедленно завершается с ненулевым exit-кодом. -Validates: Requirements 3.3 -``` - -``` -Property 8: Config correctness -Category: Propagation -Statement: For all конфигурационных файлов (`config.yml`, `config.local.yml`), секция `registry` содержит `plugins_dir` и `max_output_size`, а не `domain`. -Validates: Requirements 4.1, 4.2, 4.3 -``` - -``` -Property 9: Docker compose cleanup -Category: Absence -Statement: For all содержимого `docker-compose.yml`, отсутствуют: сервис `registry`, volume `registry-data`, mount `docker.sock`. -Validates: Requirements 5.1, 5.2 -``` - -``` -Property 10: Plugins volume mount -Category: Propagation -Statement: For all запусков `docker compose up`, сервис `service` имеет read-only volume mount `./plugins:/plugins:ro`. -Validates: Requirements 5.1 -``` - -``` -Property 11: Taskfile cleanup -Category: Absence -Statement: For all содержимого `Taskfile.yml`, отсутствуют таски `local-push-registry` и `local-push-required`. -Validates: Requirements 6.4 -``` - -``` -Property 12: Taskfile new tasks -Category: Equivalence -Statement: For all запусков `task build-plugins` и `task register-plugins`, вызываются `build-plugins.sh` и `register-plugins.sh` соответственно. -Validates: Requirements 6.1, 6.2 -``` - -``` -Property 13: Task run deps -Category: Propagation -Statement: For all запусков `task run`, зависимость `local-push-registry` заменена на `build-plugins`. -Validates: Requirements 6.3 -``` - -``` -Property 14: Push script removal -Category: Absence -Statement: For all файлов в корне репозитория, `push.sh` не существует. -Validates: Requirements 7.1 -``` - ---- - -## 2.7 Обработка ошибок - -| Сценарий | Обнаружение | Действие | -|----------|------------|---------| -| `docker build` падает для одного из Dockerfile-ов | Non-zero exit code от `docker build` | `build-plugins.sh` завершается немедленно (`set -e`) | -| Docker daemon не запущен | `docker build` возвращает ошибку "Cannot connect to the Docker daemon" | Скрипт падает с понятной ошибкой | -| BuildKit не включён | `--output` не поддерживается | Скрипт устанавливает `DOCKER_BUILDKIT=1` перед вызовом | -| gRPC сервис не доступен | `grpcurl` возвращает ошибку connection refused | `register-plugins.sh` падает с ненулевым exit-кодом | -| Плагин уже зарегистрирован | gRPC возвращает `ALREADY_EXISTS` | `register-plugins.sh` логирует warning и продолжает | -| gRPC вызов возвращает другую ошибку | Non-zero exit от `grpcurl` + статус != `ALREADY_EXISTS` | `register-plugins.sh` немедленно завершается | -| Директория `plugins/` пуста при регистрации | Нет поддиректорий с бинарниками | `register-plugins.sh` завершается с warning (ничего не зарегистрировано) | - ---- - -## 2.8 Стратегия тестирования - -**Test Style Source:** Tier 2 -- Evidence: `internal/adapters/registry/registry_test.go` (удалён в `disk-plugin-execution`, но паттерны из него доступны), `internal/core/pool_test.go` (удалён) -- Key patterns: стандартный `go test`, table-driven tests. PBT unavailable — using targeted unit tests as substitute. - -**Project Commands:** - -| Action | Command | -|--------|---------| -| Test | `go test ./...` | -| Build | `go build -o main ./cmd/main.go` | -| Lint | `golangci-lint run ./...` | -| Generate | `easyp --cfg easyp.yaml generate` | - -NOTE: Эта фича в основном затрагивает скрипты и конфиги (bash, YAML, Dockerfile), а не Go-код. Основная верификация — ручная/интеграционная: запуск скриптов, проверка файлов, запуск сервиса. Go-тесты проверяют, что существующий код не сломан. - -### Unit Tests - -| Test | Description | Tags | -|------|-------------|------| -| `TestBuild_GoCompiles` | `go build -o main ./cmd/main.go` компилируется без ошибок после обновления конфигов | `Feature/build` | -| `TestExisting_GoTests` | Все существующие Go-тесты проходят: `go test ./...` | `Feature/regression` | - -### Property-Based Tests (manual verification scripts) - -| Test | Property | Generator description | Tags | -|------|----------|-----------------------|------| -| `verify_dockerfile_output` | CP-1 | Для каждого Dockerfile: `docker build --output=/tmp/test_plugin/ registry/{path}/`, проверить что `/tmp/test_plugin/plugin` существует и исполняем | `Property/1` | -| `verify_upx_compression` | CP-2 | Для собранного бинарника: проверить что `file plugin` показывает "UPX compressed" или размер < порога | `Property/2` | -| `verify_build_failfast` | CP-3 | Создать невалидный Dockerfile, запустить `build-plugins.sh`, проверить что exit code != 0 | `Property/3` | -| `verify_build_completeness` | CP-4 | Запустить `build-plugins.sh`, проверить что для каждого Dockerfile есть `plugins/{...}/plugin` | `Property/4` | -| `verify_register_idempotent` | CP-5 | Запустить `register-plugins.sh` дважды, проверить что второй запуск завершается успешно (exit 0) | `Property/5` | -| `verify_register_completeness` | CP-6 | Запустить `register-plugins.sh`, через `grpcurl` вызвать `Plugins`, проверить наличие всех плагинов | `Property/6` | -| `verify_register_failfast` | CP-7 | Запустить `register-plugins.sh` без работающего сервиса, проверить что exit code != 0 | `Property/7` | -| `verify_config_fields` | CP-8 | Проверить что `config.yml` и `config.local.yml` содержат `plugins_dir` и `max_output_size`, не содержат `domain` | `Property/8` | -| `verify_compose_cleanup` | CP-9 | Проверить что `docker-compose.yml` не содержит `registry`, `registry-data`, `docker.sock` | `Property/9` | -| `verify_compose_volume` | CP-10 | Проверить что `docker-compose.yml` содержит `./plugins:/plugins:ro` | `Property/10` | -| `verify_taskfile_cleanup` | CP-11 | Проверить что `Taskfile.yml` не содержит `local-push-registry`, `local-push-required` | `Property/11` | -| `verify_taskfile_new` | CP-12 | Проверить что `Taskfile.yml` содержит `build-plugins` и `register-plugins` | `Property/12` | -| `verify_run_deps` | CP-13 | Проверить что таска `run` зависит от `build-plugins` а не от `local-push-registry` | `Property/13` | -| `verify_push_deleted` | CP-14 | Проверить что `push.sh` не существует в корне репозитория | `Property/14` | diff --git a/.spec/features/local-test-env/explore.md b/.spec/features/local-test-env/explore.md deleted file mode 100644 index 7c7f364..0000000 --- a/.spec/features/local-test-env/explore.md +++ /dev/null @@ -1,152 +0,0 @@ -# Exploration: local-test-env - -## Введение (Intent) - -После миграции на `disk-plugin-execution` механизм выполнения плагинов изменился с Docker-контейнеров на запуск бинарников с диска. Нужно адаптировать локальное окружение разработчика: конфиги, docker-compose, Taskfile — чтобы можно было легко поставить пару плагинов для тестов и запустить сервис. - -## Исследование (Investigation) - -### Существующие плагины в `registry/` - -4 Dockerfile-а, все с идентичной структурой (multi-stage: `golang:alpine` → `scratch`): - -| Плагин | Dockerfile | Бинарник | -|--------|-----------|----------| -| `protocolbuffers/go:v1.36.10` | `registry/protocolbuffers/go/v1.36.10/Dockerfile` | `/protoc-gen-go` | -| `grpc/go:v1.5.1` | `registry/grpc/go/v1.5.1/Dockerfile` | `/protoc-gen-go-grpc` | -| `grpc-ecosystem/gateway:v2.27.3` | `registry/grpc-ecosystem/gateway/v2.27.3/Dockerfile` | `/protoc-gen-grpc-gateway` | -| `grpc-ecosystem/openapiv2:v2.27.3` | `registry/grpc-ecosystem/openapiv2/v2.27.3/Dockerfile` | `/protoc-gen-openapiv2` | - -### Конфиги — остатки старого подхода - -- `config.yml` (строка 13): `registry.domain: "localhost:5005"` — старое поле, не используемое новым кодом. Нужно заменить на `plugins_dir` + `max_output_size`. -- `config.local.yml` (строка 13): тоже `registry.domain: "localhost:5005"`. -- `cmd/main.go` (строка 76–79): структура `registryConfig` уже использует `PluginsDir` и `MaxOutputSize` — конфиги просто отстали. - -### docker-compose.yml - -- Сервис `registry` (`easyp-registry`, порт 5005) — Docker Registry v3, больше не нужен для plugin execution. -- Сервис `service` всё ещё монтирует `/var/run/docker.sock` (строка 211) — больше не нужен. -- Нет volume для `./plugins:/plugins`. - -### Taskfile.yml - -- `local-push-registry` — запускает `./push.sh` для пуша всех образов в локальный registry. Устарело. -- `local-push-required` — собирает и пушит образы `protoc-gen-go` и `protoc-gen-go-grpc`. Нужно переделать. - -### .gitignore - -- `plugins/` уже в `.gitignore` (строка 9). ✅ - -### Подход: `docker build --output` - -Переделать Dockerfile-ы так, чтобы они **только собирали бинарник** и ничего больше. Финальный стейдж — просто `COPY` в `/plugin`. Затем вызов с `--output` копирует результат прямо на диск: - -**Пример переделанного Dockerfile (`registry/protocolbuffers/go/v1.36.10/Dockerfile`):** -```dockerfile -FROM golang:1.25-alpine3.22 - -ENV CGO_ENABLED=0 GOOS=linux GOARCH=amd64 -RUN apk add upx=5.0.2-r0 --no-cache - -RUN --mount=type=cache,target=/go/pkg/mod \ - go install -ldflags "-s -w" -trimpath google.golang.org/protobuf/cmd/protoc-gen-go@v1.36.10 \ - && mv /go/bin/${GOOS}_${GOARCH}/protoc-gen-go /go/bin/protoc-gen-go || true \ - && upx --best --lzma /go/bin/protoc-gen-go - -FROM scratch -COPY --from=0 /go/bin/protoc-gen-go /plugin -``` - -**Вызов:** -```bash -docker build --output=./plugins/protocolbuffers/go/v1.36.10/ registry/protocolbuffers/go/v1.36.10/ -``` - -Результат: `./plugins/protocolbuffers/go/v1.36.10/plugin` — готовый бинарник. Одна команда, без промежуточных контейнеров. - -### Регистрация плагинов через API - -После сборки бинарников и запуска сервиса, плагины регистрируются через gRPC API `CreatePlugin`: - -```bash -grpcurl -plaintext -d '{ - "group": "protocolbuffers", - "name": "go", - "version": "v1.36.10", - "config": {"command": ["/plugins/protocolbuffers/go/v1.36.10/plugin"]}, - "tags": ["go", "official"] -}' localhost:8080 api.generator.v1.ServiceAPI/CreatePlugin -``` - -Скрипт `register-plugins.sh` (или часть `build-plugins.sh`) будет вызывать `CreatePlugin` для каждого собранного плагина. Это лучше, чем прямой SQL, потому что: -- Проходит через всю цепочку валидации (`ValidateConfig`). -- Не нужен доступ к PostgreSQL напрямую. -- Идемпотентность: если плагин уже зарегистрирован (`ALREADY_EXISTS`), скрипт просто продолжает. - -## Инструменты сборки (Build Tooling) - -- **Orchestrator:** Taskfile v3 -- **Test:** `go test ./...` -- **Build:** `go build -o main ./cmd/main.go` -- **Lint:** `golangci-lint run ./...` -- **Generate:** `easyp --cfg easyp.yaml generate` -- **Source:** `Taskfile.yml` - -## Рассмотренные варианты (Options Considered) - -### Вариант А: Переделать Dockerfile-ы + `docker build --output` (рекомендован) - -- **Описание:** Упростить Dockerfile-ы: убрать `ENTRYPOINT`, `USER`, `/etc/passwd`. Финальный стейдж `FROM scratch` содержит только `COPY --from=0 ... /plugin`. Сборка через `docker build --output=./plugins/{path}/ registry/{path}/` — Docker выплёвывает бинарник прямо на диск. Скрипт `build-plugins.sh` обходит все Dockerfile-ы и вызывает эту команду. -- **Плюсы:** - - Dockerfile-ы становятся чище — одна ответственность (билд бинарника). - - Не нужны промежуточные `docker create` / `docker cp` / `docker rm`. - - Кросс-компиляция из коробки, UPX-сжатие. - - Единый источник правды для версий плагинов. -- **Минусы:** - - Требует BuildKit (`DOCKER_BUILDKIT=1`), но он включён по умолчанию в Docker >= 23.0. -- **Сложность:** Низкая. - -### Вариант Б: `docker build` + `docker create` + `docker cp` - -- **Описание:** Оставить Dockerfile-ы как есть (multi-stage с scratch), собирать образ, создавать контейнер, копировать бинарник, удалять контейнер. -- **Плюсы:** Не меняет Dockerfile-ы. -- **Минусы:** 4 команды вместо 1. Нужно чистить контейнеры. Dockerfile-ы содержат ненужные `ENTRYPOINT`/`USER`. -- **Сложность:** Низкая, но больше boilerplate. - -## Ограничения и риски (Constraints & Risks) - -- **Кросс-платформенность:** `docker compose up` запускает Linux-контейнер. `task run-local` запускает на macOS. Dockerfile-ы сейчас хардкодят `GOOS=linux GOARCH=amd64`. Для `task run-local` нужен будет отдельный режим — но это Deferred (v2). -- **Изменение конфигов:** `config.yml` и `config.local.yml` нужно обновить — но поле `domain` уже не используется кодом, так что ничего не ломается. -- **BuildKit:** `--output` требует BuildKit, который включён по умолчанию в Docker >= 23.0. Для старых версий нужен `DOCKER_BUILDKIT=1`. - -## Рекомендованное направление (Recommended Direction) - -**Вариант А** — переделать Dockerfile-ы + `docker build --output`. Создать скрипт `build-plugins.sh`, который: -1. Обходит все Dockerfile-ы в `registry/`. -2. Для каждого вызывает `docker build --output=./plugins/{group}/{name}/{version}/` . -3. Результат: готовые бинарники в `./plugins/`. - -Плюс обновить конфиги, docker-compose, Taskfile и добавить механизм регистрации плагинов в БД. - -## Границы области (Scope Boundaries) - -- **Must-have (v1):** - - Переделать 4 Dockerfile-а в `registry/` (убрать `ENTRYPOINT`/`USER`/`passwd`, финальный стейдж выдаёт только `/plugin`). - - Скрипт `build-plugins.sh` для сборки всех плагинов через `docker build --output`. - - Обновление `config.yml` и `config.local.yml`: `plugins_dir` + `max_output_size` вместо `domain`. - - Обновление `docker-compose.yml`: убрать `docker.sock` mount, убрать сервис `registry`, убрать volume `registry-data`, добавить volume `./plugins:/plugins`. - - Обновление `Taskfile.yml`: переделать `local-push-required` → `build-plugins`, убрать `local-push-registry`. - - Удаление `push.sh`. - - Скрипт или таска для регистрации плагинов через gRPC API (`CreatePlugin`). -- **Deferred (v2):** - - Режим `--local` для сборки macOS-бинарников (`task run-local`). -- **Needs spike:** - - Нет. - -## Допущения и открытые вопросы (Assumptions & Open Questions) - -- [ASSUMPTION: Docker >= 23.0 установлен у всех разработчиков (BuildKit включён по умолчанию).] -- [ASSUMPTION: Все Dockerfile-ы в `registry/` имеют предсказуемую структуру: один build-стейдж, один бинарник.] -- [ASSUMPTION: Для `task up` достаточно Linux/amd64 бинарников.] -- Открытых вопросов нет — пользователь подтвердил полную очистку docker-compose от registry и docker.sock. diff --git a/.spec/features/local-test-env/implementation.md b/.spec/features/local-test-env/implementation.md deleted file mode 100644 index 09e3073..0000000 --- a/.spec/features/local-test-env/implementation.md +++ /dev/null @@ -1,38 +0,0 @@ -# local-test-env — Implementation Summary - -**Date:** 2026-05-24 - -## Выполненные задачи - -- [x] **T-1** GREEN — Baseline (go build + go test проходят) -- [x] **T-2** CODE — Переделаны 4 Dockerfile-а (убраны ENTRYPOINT/USER/passwd, финальный стейдж `/plugin`) -- [x] **T-3** CODE — Создан `build-plugins.sh` -- [x] **T-4** CODE — Создан `register-plugins.sh` -- [x] **T-5** CODE — Обновлены config.yml, config.local.yml, docker-compose.yml, Taskfile.yml, удалён push.sh -- [x] **T-6** VERIFY — Все проверки пройдены -- [x] **T-7** GATE — Финальная контрольная точка пройдена - -## Изменённые файлы - -| File | Change | -|------|--------| -| `registry/protocolbuffers/go/v1.36.10/Dockerfile` | Упрощён: `FROM scratch` + `COPY /plugin` | -| `registry/grpc/go/v1.5.1/Dockerfile` | Упрощён: `FROM scratch` + `COPY /plugin` | -| `registry/grpc-ecosystem/gateway/v2.27.3/Dockerfile` | Упрощён: `FROM scratch` + `COPY /plugin` | -| `registry/grpc-ecosystem/openapiv2/v2.27.3/Dockerfile` | Упрощён: `FROM scratch` + `COPY /plugin` | -| `build-plugins.sh` | **NEW** — Скрипт сборки бинарников через `docker build --output` | -| `register-plugins.sh` | **NEW** — Скрипт регистрации через gRPC CreatePlugin | -| `config.yml` | `registry.domain` → `registry.plugins_dir` + `max_output_size` | -| `config.local.yml` | `registry.domain` → `registry.plugins_dir` + `max_output_size` | -| `docker-compose.yml` | Убран `registry`, `registry-data`, `docker.sock` → `./plugins:/plugins:ro` | -| `Taskfile.yml` | Убраны `local-push-*`, добавлены `build-plugins` + `register-plugins` | -| `push.sh` | **DELETED** | - -## Результат верификации - -- ✓ `go build` — компилируется -- ✓ `go test ./...` — все тесты проходят -- ✓ Нет ENTRYPOINT в Dockerfile-ах -- ✓ `/plugin` присутствует в 4 Dockerfile-ах -- ✓ `build-plugins.sh` и `register-plugins.sh` исполняемые -- ✓ `push.sh` удалён diff --git a/.spec/features/local-test-env/pipeline.json b/.spec/features/local-test-env/pipeline.json deleted file mode 100644 index cd28e7a..0000000 --- a/.spec/features/local-test-env/pipeline.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "feature": "local-test-env", - "phase": "done", - "created_at": "2026-05-24T15:08:21Z", - "current_artifact": null, - "history": [ - {"phase": "explore", "artifact": ".spec/features/local-test-env/explore.md", "approved_at": "2026-05-24T15:20:40Z"}, - {"phase": "requirements", "artifact": ".spec/features/local-test-env/requirements.md", "approved_at": "2026-05-24T15:24:23Z"}, - {"phase": "design", "artifact": ".spec/features/local-test-env/design.md", "approved_at": "2026-05-24T15:28:59Z"}, - {"phase": "task-plan", "artifact": ".spec/features/local-test-env/task-plan.md", "approved_at": "2026-05-24T15:31:06Z"}, - {"phase": "implementation", "artifact": ".spec/features/local-test-env/implementation.md", "approved_at": "2026-05-24T15:38:11Z"}, - {"phase": "review", "artifact": ".spec/features/local-test-env/review.md", "approved_at": "2026-05-24T15:40:46Z"} - ], - "review_base_commit": "e92d56a76c62afaae4b376b277f0f61ec763f3cf", - "branch": "feature/local-test-env", - "worktree": null, - "last_completed_task": null, - "finish_action": null, - "finished_at": null, - "finish_base": null -} diff --git a/.spec/features/local-test-env/pipeline.kv b/.spec/features/local-test-env/pipeline.kv deleted file mode 100644 index 6295f8b..0000000 --- a/.spec/features/local-test-env/pipeline.kv +++ /dev/null @@ -1,32 +0,0 @@ -feature=local-test-env -phase=done -created_at=2026-05-24T15:08:21Z -current_artifact= -history_count=6 -branch=feature/local-test-env -revision_count_explore=5 -history_0_phase=explore -history_0_artifact=.spec/features/local-test-env/explore.md -history_0_approved_at=2026-05-24T15:20:40Z -revision_count_requirements=1 -revision_count_design=1 -history_1_phase=requirements -history_1_artifact=.spec/features/local-test-env/requirements.md -history_1_approved_at=2026-05-24T15:24:23Z -history_2_phase=design -history_2_artifact=.spec/features/local-test-env/design.md -history_2_approved_at=2026-05-24T15:28:59Z -revision_count_task-plan=1 -review_base_commit=e92d56a76c62afaae4b376b277f0f61ec763f3cf -history_3_phase=task-plan -history_3_artifact=.spec/features/local-test-env/task-plan.md -history_3_approved_at=2026-05-24T15:31:06Z -revision_count_implementation=1 -last_completed_task= -history_4_phase=implementation -history_4_artifact=.spec/features/local-test-env/implementation.md -history_4_approved_at=2026-05-24T15:38:11Z -revision_count_review=1 -history_5_phase=review -history_5_artifact=.spec/features/local-test-env/review.md -history_5_approved_at=2026-05-24T15:40:46Z diff --git a/.spec/features/local-test-env/requirements.md b/.spec/features/local-test-env/requirements.md deleted file mode 100644 index a894e61..0000000 --- a/.spec/features/local-test-env/requirements.md +++ /dev/null @@ -1,98 +0,0 @@ -# local-test-env — Requirements - -**Status:** Draft -**Date:** 2026-05-24 - -## Обзор - -Адаптация локального окружения разработчика к новому способу выполнения плагинов (бинарники с диска вместо Docker-контейнеров). Включает: переделку Dockerfile-ов из `registry/` для сборки бинарников через `docker build --output`, скрипты для сборки и регистрации плагинов, очистку docker-compose от устаревших сервисов (registry, docker.sock), обновление конфигов и Taskfile. - -## Глоссарий - -| Термин | Определение | Code Artifact | -|--------|------------|---------------| -| Plugin binary | Скомпилированный Go-бинарник плагина, принимающий `CodeGeneratorRequest` на stdin и отдающий `CodeGeneratorResponse` на stdout | `./plugins/{group}/{name}/{version}/plugin` | -| `PluginConfig` | JSON-конфигурация плагина с массивом `command`, опциональными `env` и `timeout` | `internal/adapters/registry/registry.go` → `PluginConfig` | -| `build-plugins.sh` | Скрипт сборки plugin binary из Dockerfile-ов через `docker build --output` | `build-plugins.sh` | -| `register-plugins.sh` | Скрипт регистрации собранных плагинов через gRPC API `CreatePlugin` | `register-plugins.sh` | - ---- - -## Требования - -### 1. Dockerfile-ы - -**REQ-1.1** WHEN `docker build --output=./plugins/{group}/{name}/{version}/ registry/{group}/{name}/{version}/` выполняется для любого Dockerfile из `registry/`, the system SHALL создать файл `./plugins/{group}/{name}/{version}/plugin` — исполняемый Linux/amd64 бинарник. - -**REQ-1.2** WHEN Dockerfile собирается, the system SHALL использовать UPX-сжатие для минимизации размера бинарника. - -**REQ-1.3** WHEN финальный стейдж Dockerfile описывается, the system SHALL содержать только `FROM scratch` и `COPY` бинарника в `/plugin` — без `ENTRYPOINT`, `USER`, или `/etc/passwd`. - -### 2. Скрипт сборки (`build-plugins.sh`) - -**REQ-2.1** WHEN `build-plugins.sh` запускается, the system SHALL найти все файлы `Dockerfile` в директории `registry/` рекурсивно и собрать каждый через `docker build --output`. - -**REQ-2.2** WHEN `docker build` для любого плагина завершается с ошибкой, the system SHALL немедленно остановить выполнение скрипта с ненулевым exit-кодом (`set -e`). - -**REQ-2.3** WHEN `build-plugins.sh` завершается успешно, the system SHALL создать для каждого Dockerfile файл `./plugins/{group}/{name}/{version}/plugin` с правами на исполнение. - -### 3. Скрипт регистрации (`register-plugins.sh`) - -**REQ-3.1** WHEN `register-plugins.sh` запускается, the system SHALL для каждого собранного плагина в директории `plugins/` вызвать gRPC метод `CreatePlugin` с полями `group`, `name`, `version` и `config.command = ["/plugins/{group}/{name}/{version}/plugin"]`. - -**REQ-3.2** WHEN gRPC-вызов `CreatePlugin` возвращает ошибку `ALREADY_EXISTS`, the system SHALL пропустить этот плагин и продолжить с остальными (не завершаться с ошибкой). - -**REQ-3.3** WHEN gRPC-вызов `CreatePlugin` возвращает любую другую ошибку (кроме `ALREADY_EXISTS`), the system SHALL немедленно остановить выполнение с ненулевым exit-кодом. - -### 4. Конфигурация - -**REQ-4.1** WHEN сервис запускается с `config.yml`, the system SHALL использовать поле `registry.plugins_dir` (по умолчанию `/plugins`) вместо устаревшего `registry.domain`. - -**REQ-4.2** WHEN сервис запускается с `config.yml`, the system SHALL использовать поле `registry.max_output_size` (по умолчанию `67108864`, 64 МБ). - -**REQ-4.3** WHEN сервис запускается с `config.local.yml`, the system SHALL использовать `registry.plugins_dir` указывающий на локальную директорию `./plugins`. - -### 5. Docker Compose - -**REQ-5.1** WHEN `docker-compose.yml` описывает сервис `service`, the system SHALL монтировать volume `./plugins:/plugins` вместо `/var/run/docker.sock`. - -**REQ-5.2** WHEN `docker-compose.yml` описывает инфраструктуру, the system SHALL не содержать сервис `registry` и volume `registry-data`. - -### 6. Taskfile - -**REQ-6.1** WHEN `task build-plugins` выполняется, the system SHALL вызвать `build-plugins.sh` для сборки всех плагинов. - -**REQ-6.2** WHEN `task register-plugins` выполняется, the system SHALL вызвать `register-plugins.sh` для регистрации всех плагинов через gRPC API. - -**REQ-6.3** WHEN `task run` выполняется, the system SHALL использовать `build-plugins` вместо устаревшей `local-push-registry` в зависимостях. - -**REQ-6.4** WHEN Taskfile описывает таски, the system SHALL не содержать `local-push-registry` и `local-push-required`. - -### 7. Удаление устаревших файлов - -**REQ-7.1** WHEN проект собран, the system SHALL не содержать файл `push.sh` в корне репозитория. - ---- - -## Порядок зависимостей (Topological Order) - -``` -REQ-1.1..1.3 → REQ-2.1..2.3 → REQ-3.1..3.3 -Причина: Dockerfile-ы (1.x) нужны для скрипта сборки (2.x), который создаёт бинарники для скрипта регистрации (3.x). - -REQ-4.1..4.3 (независимы — можно параллельно с 1–3) -REQ-5.1..5.2 (независимы — можно параллельно с 1–3) -REQ-6.1..6.4 → зависят от REQ-2.x и REQ-3.x (таски ссылаются на скрипты) -REQ-7.1 (независим) -``` - ---- - -## Команды верификации - -| Действие | Команда | Источник | -|----------|---------|----------| -| Test | `go test ./...` | Taskfile.yml | -| Build | `go build -o main ./cmd/main.go` | Taskfile.yml | -| Lint | `golangci-lint run ./...` | Taskfile.yml | -| Generate | `easyp --cfg easyp.yaml generate` | Taskfile.yml | diff --git a/.spec/features/local-test-env/review.md b/.spec/features/local-test-env/review.md deleted file mode 100644 index d7a825d..0000000 --- a/.spec/features/local-test-env/review.md +++ /dev/null @@ -1,153 +0,0 @@ -# Code Review: local-test-env - -## Verdict: PASS - -Все 17 требований реализованы. Код соответствует дизайну. Нет security-проблем (изменения затрагивают только скрипты, конфиги и Dockerfile-ы — не Go-код). Сборка и тесты проходят. Один `nit`-уровневый замечание. - ---- - -## Change Set - -| File | Status | Notes | -|------|--------|-------| -| `registry/protocolbuffers/go/v1.36.10/Dockerfile` | ✅ Planned | Упрощён: `FROM scratch` + `COPY /plugin` | -| `registry/grpc/go/v1.5.1/Dockerfile` | ✅ Planned | Упрощён: `FROM scratch` + `COPY /plugin` | -| `registry/grpc-ecosystem/gateway/v2.27.3/Dockerfile` | ✅ Planned | Упрощён: `FROM scratch` + `COPY /plugin` | -| `registry/grpc-ecosystem/openapiv2/v2.27.3/Dockerfile` | ✅ Planned | Упрощён: `FROM scratch` + `COPY /plugin` | -| `build-plugins.sh` | ✅ Planned | NEW — скрипт сборки | -| `register-plugins.sh` | ✅ Planned | NEW — скрипт регистрации | -| `config.yml` | ✅ Planned | `plugins_dir` + `max_output_size` | -| `config.local.yml` | ✅ Planned | `plugins_dir` + `max_output_size` | -| `docker-compose.yml` | ✅ Planned | Удалён registry, docker.sock → plugins:ro | -| `Taskfile.yml` | ✅ Planned | Новые таски, обновлены зависимости | -| `push.sh` | ✅ Planned | DELETED | -| `.spec/features/local-test-env/*` | ⚠️ Unexpected | SDD pipeline artifacts — ожидаемые, не scope creep | - ---- - -## Requirements Traceability - -| Requirement | Test(s) | Code | CP | Verdict | -|-------------|---------|------|----|---------| -| REQ-1.1 | verify_dockerfile_output (manual) | `registry/*/Dockerfile` → `FROM scratch` + `COPY /plugin` | CP-1 | ✅ | -| REQ-1.2 | verify_upx_compression (manual) | `registry/*/Dockerfile` → `upx --best --lzma` в build-стейдже | CP-2 | ✅ | -| REQ-1.3 | verify_dockerfile_output (manual) | Нет `ENTRYPOINT`/`USER`/`passwd` | CP-1 | ✅ | -| REQ-2.1 | verify_build_completeness (manual) | `build-plugins.sh:18-39` — `find` + `docker build --output` | CP-4 | ✅ | -| REQ-2.2 | verify_build_failfast (manual) | `build-plugins.sh:7` — `set -euo pipefail` | CP-3 | ✅ | -| REQ-2.3 | verify_build_completeness (manual) | `build-plugins.sh:35` — `chmod +x` | CP-4 | ✅ | -| REQ-3.1 | verify_register_completeness (manual) | `register-plugins.sh:30-57` — `grpcurl` → `CreatePlugin` | CP-6 | ✅ | -| REQ-3.2 | verify_register_idempotent (manual) | `register-plugins.sh:49-51` — `ALREADY_EXISTS` → continue | CP-5 | ✅ | -| REQ-3.3 | verify_register_failfast (manual) | `register-plugins.sh:53-55` — else → `exit 1` | CP-7 | ✅ | -| REQ-4.1 | — | `config.yml:13` — `plugins_dir: "/plugins"` | CP-8 | ✅ | -| REQ-4.2 | — | `config.yml:14` — `max_output_size: 67108864` | CP-8 | ✅ | -| REQ-4.3 | — | `config.local.yml:13` — `plugins_dir: "./plugins"` | CP-8 | ✅ | -| REQ-5.1 | — | `docker-compose.yml:200` — `./plugins:/plugins:ro` | CP-9, CP-10 | ✅ | -| REQ-5.2 | — | `docker-compose.yml` — нет `registry`, `registry-data` | CP-9 | ✅ | -| REQ-6.1 | — | `Taskfile.yml:27-33` — `build-plugins` → `./build-plugins.sh` | CP-12 | ✅ | -| REQ-6.2 | — | `Taskfile.yml:35-41` — `register-plugins` → `./register-plugins.sh` | CP-12 | ✅ | -| REQ-6.3 | — | `Taskfile.yml:46` — `deps: build-plugins` | CP-13 | ✅ | -| REQ-6.4 | — | Нет `local-push-*` в Taskfile | CP-11 | ✅ | -| REQ-7.1 | — | `push.sh` удалён | CP-14 | ✅ | - ---- - -## Design Conformance - -### 3.1 Architectural Boundaries -Все новые файлы (`build-plugins.sh`, `register-plugins.sh`) расположены в корне проекта — корректно для скриптов оркестрации. Dockerfile-ы остались на своих местах в `registry/`. Конфиги и compose-файл обновлены на месте. ✅ - -### 3.2 Data Models -Новых типов данных нет. Конфигурация `registryConfig` в `cmd/main.go` уже использует `PluginsDir` и `MaxOutputSize` — конфиги теперь соответствуют коду. ✅ - -### 3.3 API Contracts -API контракт (proto) не изменён. `CreatePlugin` RPC используется скриптом регистрации — соответствует дизайну. ✅ - -### 3.4 Error Handling -- `build-plugins.sh`: `set -euo pipefail` + exit 1 для неожиданных путей. ✅ -- `register-plugins.sh`: `ALREADY_EXISTS` → skip, другие ошибки → exit 1. ✅ -- Проверка `grpcurl` installed. ✅ -- Проверка `plugins/` directory exists. ✅ - -### 3.5 Correctness Properties -Все 14 CP из дизайна выполнены (см. Requirements Traceability). ✅ - -### 3.6 Documentation Consistency -Mermaid-диаграмма в design.md соответствует фактической структуре: Dockerfiles → build-plugins.sh → plugins/ → service + register-plugins.sh → gRPC → DB. ✅ - ---- - -## Code Quality - -### 4.1 Naming & Clarity -- Скрипты названы описательно: `build-plugins.sh`, `register-plugins.sh`. ✅ -- Переменные в скриптах (`group`, `name`, `version`, `output_dir`) понятные. ✅ -- Taski в Taskfile имеют `desc:` поля. ✅ - -### 4.2 Dead Code & Debug Artifacts -Нет `TODO`-ов, нет debug-вывода. ✅ - -### 4.3 Scope Creep -Все изменения соответствуют плану. `.spec/features/` — артефакты SDD pipeline, не scope creep. ✅ - -### 4.4 Test Quality -Фича затрагивает скрипты/конфиги — unit-тесты Go не применимы. Верификация через manual/integration тесты (запуск скриптов). Существующие Go-тесты не сломаны (все `[no test files]`). ✅ - ---- - -## Security - -Нет новых публичных API endpoints. Изменения затрагивают только: -- Bash-скрипты (не принимают внешний ввод кроме CLI-аргумента `host:port`) -- Конфиги (YAML) -- Dockerfile-ы (build-only, не runtime) -- Docker Compose (volume mount `:ro`) - -Security-проблем не обнаружено. ✅ - ---- - -## Verification Evidence - -- **Build:** -``` -go build -o main ./cmd/main.go -EXIT: 0 -``` - -- **Tests:** -``` -? github.com/easyp-tech/service/api/generator/v1 [no test files] -? github.com/easyp-tech/service/cmd [no test files] -? github.com/easyp-tech/service/cmd/mcp-smoke [no test files] -? github.com/easyp-tech/service/internal/adapters/audit [no test files] -? github.com/easyp-tech/service/internal/adapters/metrics [no test files] -? github.com/easyp-tech/service/internal/adapters/registry [no test files] -? github.com/easyp-tech/service/internal/api [no test files] -? github.com/easyp-tech/service/internal/core [no test files] -? github.com/easyp-tech/service/internal/database [no test files] -? github.com/easyp-tech/service/internal/database/connectors [no test files] -? github.com/easyp-tech/service/internal/database/internal [no test files] -? github.com/easyp-tech/service/internal/database/migrations [no test files] -? github.com/easyp-tech/service/internal/flags [no test files] -? github.com/easyp-tech/service/internal/grpchelper [no test files] -? github.com/easyp-tech/service/internal/license [no test files] -? github.com/easyp-tech/service/internal/monitor [no test files] -? github.com/easyp-tech/service/internal/ratelimiter [no test files] -? github.com/easyp-tech/service/internal/telemetry [no test files] -? github.com/easyp-tech/service/sdk [no test files] -EXIT: 0 -``` - ---- - -## Findings - -| ID | Severity | File | Description | Requirement | -|----|----------|------|-------------|-------------| -| F-1 | nit | `docker-compose.yml:161` | Пустая строка осталась после удаления `registry:` сервиса (двойной перенос). Визуально не критично. | — | - ---- - -## Recommendations - -1. **(nit) F-1:** Убрать лишнюю пустую строку в `docker-compose.yml:161` после удаления сервиса `registry`. diff --git a/.spec/features/local-test-env/revisions/design-rev-1-2026-05-24T15-26-19Z.md b/.spec/features/local-test-env/revisions/design-rev-1-2026-05-24T15-26-19Z.md deleted file mode 100644 index ff779b6..0000000 --- a/.spec/features/local-test-env/revisions/design-rev-1-2026-05-24T15-26-19Z.md +++ /dev/null @@ -1,290 +0,0 @@ -# local-test-env — Design - -**Status:** Draft -**Date:** 2026-05-24 - -## 2.1 Обзор - -Проектирование локального тестового окружения для сервиса EasyP после миграции на disk-plugin-execution. Задача делится на 4 логические части: - -1. **Dockerfile-ы** — переделка 4 файлов в `registry/` для сборки бинарников через `docker build --output`. -2. **Скрипты** — `build-plugins.sh` (сборка) и `register-plugins.sh` (регистрация через gRPC API). -3. **Инфраструктура** — очистка `docker-compose.yml`, обновление конфигов, Taskfile. -4. **Очистка** — удаление `push.sh`. - ---- - -## 2.2 Архитектура - -```mermaid -graph TD - subgraph "Сборка плагинов" - DF1["registry/*/Dockerfile"] - BP["build-plugins.sh"]:::new - PD["./plugins/{group}/{name}/{version}/plugin"]:::new - - DF1 -->|"docker build --output"| BP - BP -->|"создаёт бинарники"| PD - end - - subgraph "Инфраструктура" - DC["docker-compose.yml"]:::modified - CFG["config.yml"]:::modified - CFGL["config.local.yml"]:::modified - TF["Taskfile.yml"]:::modified - end - - subgraph "Регистрация и запуск" - SVC["easyp-api-service"] - RP["register-plugins.sh"]:::new - DB["PostgreSQL"] - - PD -->|"volume mount"| SVC - RP -->|"gRPC CreatePlugin"| SVC - SVC -->|"INSERT plugins"| DB - end - - classDef new fill:#90EE90 - classDef modified fill:#FFD700 -``` - -**Порядок реализации:** -1. Dockerfile-ы (фундамент — без них скрипт сборки не работает) -2. `build-plugins.sh` (зависит от Dockerfile-ов) -3. Инфраструктура (config, docker-compose, Taskfile — параллельно) -4. `register-plugins.sh` (зависит от работающего сервиса) -5. Очистка (удаление `push.sh`) - ---- - -## 2.3 Компоненты и интерфейсы - -### Файлы, требующие изменений - -| File | Change Type | Description | -|------|-------------|-------------| -| `registry/protocolbuffers/go/v1.36.10/Dockerfile` | `[MODIFIED]` | Убрать второй стейдж с `ENTRYPOINT`/`USER`/`passwd`. Финальный стейдж: `FROM scratch` + `COPY --from=0 /go/bin/protoc-gen-go /plugin` | -| `registry/grpc/go/v1.5.1/Dockerfile` | `[MODIFIED]` | Аналогично: `COPY --from=0 /go/bin/protoc-gen-go-grpc /plugin` | -| `registry/grpc-ecosystem/gateway/v2.27.3/Dockerfile` | `[MODIFIED]` | Аналогично: `COPY --from=0 /go/bin/protoc-gen-grpc-gateway /plugin` | -| `registry/grpc-ecosystem/openapiv2/v2.27.3/Dockerfile` | `[MODIFIED]` | Аналогично: `COPY --from=0 /go/bin/protoc-gen-openapiv2 /plugin` | -| `build-plugins.sh` | `[NEW]` | Bash-скрипт: `find registry -name Dockerfile`, для каждого `docker build --output`. `set -e`. | -| `register-plugins.sh` | `[NEW]` | Bash-скрипт: обход `plugins/`, для каждого вызов `grpcurl` → `CreatePlugin`. `ALREADY_EXISTS` → skip. | -| `config.yml` | `[MODIFIED]` | Секция `registry:` — заменить `domain: "localhost:5005"` на `plugins_dir: "/plugins"` и `max_output_size: 67108864` | -| `config.local.yml` | `[MODIFIED]` | Секция `registry:` — заменить `domain: "localhost:5005"` на `plugins_dir: "./plugins"` и `max_output_size: 67108864` | -| `docker-compose.yml` | `[MODIFIED]` | Удалить сервис `registry`, volume `registry-data`. В `service`: убрать `docker.sock`, добавить `./plugins:/plugins:ro`. | -| `Taskfile.yml` | `[MODIFIED]` | Удалить `local-push-registry`, `local-push-required`. Добавить `build-plugins`, `register-plugins`. Обновить deps в `run`. | -| `push.sh` | `[DELETED]` | Устаревший скрипт для пуша образов в Docker Registry. | - -### Файлы, НЕ требующие изменений - -| File | Reason Unchanged | -|------|-----------------| -| `cmd/main.go` | Структура `registryConfig` уже использует `PluginsDir` и `MaxOutputSize` (обновлена в `disk-plugin-execution`) | -| `internal/adapters/registry/registry.go` | Логика `Generate()` уже работает с exec — не затрагивается | -| `internal/core/domain.go` | Доменная модель не меняется | -| `internal/core/pool.go` | WorkerPool не затрагивается | -| `api/generator/v1/generator.proto` | API контракт не меняется | -| `Dockerfile` (корневой) | Сервисный Dockerfile уже переделан в `disk-plugin-execution` (debian, VOLUME /plugins) | -| `migrate/*.sql` | Миграция `5.disk_plugin_config.sql` уже существует | -| `.gitignore` | `plugins/` уже добавлен (строка 9) | - ---- - -## 2.4 Ключевые решения (ADR) - -### Decision: Формат выходного бинарника — `/plugin` - -- **Context:** Каждый Dockerfile собирает плагин с уникальным именем (`protoc-gen-go`, `protoc-gen-go-grpc` и т.д.). Нужен единый путь для бинарника в финальном стейдже. -- **Options considered:** - 1. Оставить оригинальное имя (`/protoc-gen-go`) — разные имена для каждого плагина. - 2. Использовать единое имя `/plugin` — одинаковая структура для всех. -- **Decision:** Единое имя `/plugin`. -- **Rationale:** Скрипт `build-plugins.sh` не нужно учить маппинг "Dockerfile → имя бинарника". Путь всегда `plugins/{group}/{name}/{version}/plugin`. Конфигурация в БД тоже единообразна: `"command": ["/plugins/{group}/{name}/{version}/plugin"]`. -- **Consequences:** Имя файла не несёт семантики, но она закодирована в пути директории. - -### Decision: Разделение скриптов сборки и регистрации - -- **Context:** Сборка плагинов не требует запущенного сервиса. Регистрация требует. -- **Options considered:** - 1. Один скрипт `build-and-register.sh` — удобно, но требует сервиса для сборки. - 2. Два скрипта: `build-plugins.sh` + `register-plugins.sh`. -- **Decision:** Два отдельных скрипта. -- **Rationale:** Разделение ответственности. `build-plugins.sh` можно запускать до `docker compose up`. `register-plugins.sh` — после запуска сервиса. Разные точки отказа, разные зависимости. -- **Consequences:** Две отдельные таски в Taskfile, но workflow `task run` их оркестрирует. - -### Decision: Монтирование `./plugins` как read-only - -- **Context:** Плагины — статичные бинарники. Сервис только читает их. -- **Options considered:** - 1. Read-write mount (`./plugins:/plugins`). - 2. Read-only mount (`./plugins:/plugins:ro`). -- **Decision:** Read-only (`:ro`). -- **Rationale:** Принцип наименьших привилегий. Сервис не должен модифицировать плагины. Защищает от случайной перезаписи. -- **Consequences:** Если понадобится hot-reload плагинов — потребуется убрать `:ro`. - ---- - -## 2.5 Модели данных - -Новых типов данных нет. Существующие типы (`PluginConfig`, `plugin`, `Registry`) были обновлены в `disk-plugin-execution` и не требуют дополнительных изменений. - ---- - -## 2.6 Корректностные свойства - -``` -Property 1: Dockerfile output -Category: Equivalence -Statement: For all Dockerfile-ов в `registry/`, `docker build --output=` создаёт исполняемый файл `/plugin`. -Validates: Requirements 1.1, 1.3 -``` - -``` -Property 2: UPX compression -Category: Propagation -Statement: For all собранных plugin binary, размер файла меньше размера несжатого бинарника (UPX применён). -Validates: Requirements 1.2 -``` - -``` -Property 3: Build script fail-fast -Category: Absence -Statement: For all запусков `build-plugins.sh`, при ошибке `docker build` для любого плагина скрипт завершается немедленно с ненулевым exit-кодом. Частично собранные наборы невозможны. -Validates: Requirements 2.2 -``` - -``` -Property 4: Build script completeness -Category: Equivalence -Statement: For all Dockerfile-ов в `registry/`, `build-plugins.sh` создаёт соответствующий `plugins/{group}/{name}/{version}/plugin`. -Validates: Requirements 2.1, 2.3 -``` - -``` -Property 5: Registration idempotency -Category: Absence -Statement: For all вызовов `register-plugins.sh` для уже зарегистрированных плагинов, ошибка `ALREADY_EXISTS` не приводит к аварийному завершению скрипта. -Validates: Requirements 3.2 -``` - -``` -Property 6: Registration completeness -Category: Propagation -Statement: For all плагинов в `plugins/`, `register-plugins.sh` вызывает `CreatePlugin` с корректными `group`, `name`, `version` и `config.command`. -Validates: Requirements 3.1 -``` - -``` -Property 7: Registration fail-fast -Category: Absence -Statement: For all ошибок gRPC (кроме `ALREADY_EXISTS`), `register-plugins.sh` немедленно завершается с ненулевым exit-кодом. -Validates: Requirements 3.3 -``` - -``` -Property 8: Config correctness -Category: Propagation -Statement: For all конфигурационных файлов (`config.yml`, `config.local.yml`), секция `registry` содержит `plugins_dir` и `max_output_size`, а не `domain`. -Validates: Requirements 4.1, 4.2, 4.3 -``` - -``` -Property 9: Docker compose cleanup -Category: Absence -Statement: For all содержимого `docker-compose.yml`, отсутствуют: сервис `registry`, volume `registry-data`, mount `docker.sock`. -Validates: Requirements 5.1, 5.2 -``` - -``` -Property 10: Plugins volume mount -Category: Propagation -Statement: For all запусков `docker compose up`, сервис `service` имеет read-only volume mount `./plugins:/plugins:ro`. -Validates: Requirements 5.1 -``` - -``` -Property 11: Taskfile cleanup -Category: Absence -Statement: For all содержимого `Taskfile.yml`, отсутствуют таски `local-push-registry` и `local-push-required`. -Validates: Requirements 6.4 -``` - -``` -Property 12: Taskfile new tasks -Category: Equivalence -Statement: For all запусков `task build-plugins` и `task register-plugins`, вызываются `build-plugins.sh` и `register-plugins.sh` соответственно. -Validates: Requirements 6.1, 6.2 -``` - -``` -Property 13: Task run deps -Category: Propagation -Statement: For all запусков `task run`, зависимость `local-push-registry` заменена на `build-plugins`. -Validates: Requirements 6.3 -``` - -``` -Property 14: Push script removal -Category: Absence -Statement: For all файлов в корне репозитория, `push.sh` не существует. -Validates: Requirements 7.1 -``` - ---- - -## 2.7 Обработка ошибок - -| Сценарий | Обнаружение | Действие | -|----------|------------|---------| -| `docker build` падает для одного из Dockerfile-ов | Non-zero exit code от `docker build` | `build-plugins.sh` завершается немедленно (`set -e`) | -| Docker daemon не запущен | `docker build` возвращает ошибку "Cannot connect to the Docker daemon" | Скрипт падает с понятной ошибкой | -| BuildKit не включён | `--output` не поддерживается | Скрипт устанавливает `DOCKER_BUILDKIT=1` перед вызовом | -| gRPC сервис не доступен | `grpcurl` возвращает ошибку connection refused | `register-plugins.sh` падает с ненулевым exit-кодом | -| Плагин уже зарегистрирован | gRPC возвращает `ALREADY_EXISTS` | `register-plugins.sh` логирует warning и продолжает | -| gRPC вызов возвращает другую ошибку | Non-zero exit от `grpcurl` + статус != `ALREADY_EXISTS` | `register-plugins.sh` немедленно завершается | -| Директория `plugins/` пуста при регистрации | Нет поддиректорий с бинарниками | `register-plugins.sh` завершается с warning (ничего не зарегистрировано) | - ---- - -## 2.8 Стратегия тестирования - -**Test Style Source:** Tier 2 -- Evidence: `internal/adapters/registry/registry_test.go` (удалён в `disk-plugin-execution`, но паттерны из него доступны), `internal/core/pool_test.go` (удалён) -- Key patterns: стандартный `go test`, table-driven tests. PBT unavailable — using targeted unit tests as substitute. - -**Project Commands:** - -| Action | Command | -|--------|---------| -| Test | `go test ./...` | -| Build | `go build -o main ./cmd/main.go` | -| Lint | `golangci-lint run ./...` | -| Generate | `easyp --cfg easyp.yaml generate` | - -NOTE: Эта фича в основном затрагивает скрипты и конфиги (bash, YAML, Dockerfile), а не Go-код. Основная верификация — ручная/интеграционная: запуск скриптов, проверка файлов, запуск сервиса. Go-тесты проверяют, что существующий код не сломан. - -### Unit Tests - -| Test | Description | Tags | -|------|-------------|------| -| `TestBuild_GoCompiles` | `go build -o main ./cmd/main.go` компилируется без ошибок после обновления конфигов | `Feature/build` | -| `TestExisting_GoTests` | Все существующие Go-тесты проходят: `go test ./...` | `Feature/regression` | - -### Property-Based Tests (manual verification scripts) - -| Test | Property | Generator description | Tags | -|------|----------|-----------------------|------| -| `verify_dockerfile_output` | CP-1 | Для каждого Dockerfile: `docker build --output=/tmp/test_plugin/ registry/{path}/`, проверить что `/tmp/test_plugin/plugin` существует и исполняем | `Property/1` | -| `verify_upx_compression` | CP-2 | Для собранного бинарника: проверить что `file plugin` показывает "UPX compressed" или размер < порога | `Property/2` | -| `verify_build_failfast` | CP-3 | Создать невалидный Dockerfile, запустить `build-plugins.sh`, проверить что exit code != 0 | `Property/3` | -| `verify_build_completeness` | CP-4 | Запустить `build-plugins.sh`, проверить что для каждого Dockerfile есть `plugins/{...}/plugin` | `Property/4` | -| `verify_register_idempotent` | CP-5 | Запустить `register-plugins.sh` дважды, проверить что второй запуск завершается успешно (exit 0) | `Property/5` | -| `verify_register_completeness` | CP-6 | Запустить `register-plugins.sh`, через `grpcurl` вызвать `Plugins`, проверить наличие всех плагинов | `Property/6` | -| `verify_register_failfast` | CP-7 | Запустить `register-plugins.sh` без работающего сервиса, проверить что exit code != 0 | `Property/7` | -| `verify_config_fields` | CP-8 | Проверить что `config.yml` и `config.local.yml` содержат `plugins_dir` и `max_output_size`, не содержат `domain` | `Property/8` | -| `verify_compose_cleanup` | CP-9 | Проверить что `docker-compose.yml` не содержит `registry`, `registry-data`, `docker.sock` | `Property/9` | -| `verify_compose_volume` | CP-10 | Проверить что `docker-compose.yml` содержит `./plugins:/plugins:ro` | `Property/10` | -| `verify_taskfile_cleanup` | CP-11 | Проверить что `Taskfile.yml` не содержит `local-push-registry`, `local-push-required` | `Property/11` | -| `verify_taskfile_new` | CP-12 | Проверить что `Taskfile.yml` содержит `build-plugins` и `register-plugins` | `Property/12` | -| `verify_run_deps` | CP-13 | Проверить что таска `run` зависит от `build-plugins` а не от `local-push-registry` | `Property/13` | -| `verify_push_deleted` | CP-14 | Проверить что `push.sh` не существует в корне репозитория | `Property/14` | diff --git a/.spec/features/local-test-env/revisions/explore-rev-1-2026-05-24T15-10-44Z.md b/.spec/features/local-test-env/revisions/explore-rev-1-2026-05-24T15-10-44Z.md deleted file mode 100644 index 58f2d84..0000000 --- a/.spec/features/local-test-env/revisions/explore-rev-1-2026-05-24T15-10-44Z.md +++ /dev/null @@ -1,55 +0,0 @@ -# Exploration: local-test-env - -## Введение (Intent) -Пользователь хочет настроить локальное тестовое окружение, чтобы легко добавлять и тестировать плагины. Так как в предыдущей фиче `disk-plugin-execution` механизм выполнения плагинов был изменён с Docker-контейнеров на бинарные файлы с диска, нам нужно адаптировать инфраструктуру (configs, docker-compose, Taskfile) для локальной разработки. - -## Исследование (Investigation) -- Конфигурации (`config.yml` и `config.local.yml`) все ещё содержат поле `registry.domain` (остаток старого кода), которое нужно заменить на `registry.plugins_dir`. -- `docker-compose.yml` все еще запускает Docker Registry (`easyp-registry`) и прокидывает `/var/run/docker.sock` в `easyp-api-service`. Все это больше не нужно. -- В `Taskfile.yml` остались задачи `local-push-registry` и `local-push-required`, которые собирают Docker-образы (`docker build` и `docker push`). Их нужно переделать на сборку бинарников плагинов (через `go build`) и копирование их в локальную папку `plugins/`. -- Плагины должны регистрироваться в базе данных (`easyp_db`), чтобы сервис мог их использовать. Сейчас это делалось неявно. - -## Инструменты сборки (Build Tooling) -- **Orchestrator:** Taskfile.yml -- **Test:** `go test ./...` -- **Build:** `docker compose build service` или локальный `go run ./cmd/main.go` -- **Lint:** `golangci-lint run ./...` -- **Generate:** `easyp --cfg easyp.yaml generate` -- **Source:** Taskfile.yml - -## Рассмотренные варианты (Options Considered) -### Вариант А: Простая адаптация Taskfile и docker-compose -- **Описание:** Убрать `easyp-registry` и `docker.sock` из `docker-compose.yml`. Добавить volume `./plugins:/plugins`. В `Taskfile.yml` переделать таску `local-push-required`, чтобы она делала `go build` плагинов прямо в `./plugins/group/name/version/plugin`. -- **Плюсы:** Прост в реализации. -- **Минусы:** Для добавления нового плагина разработчику нужно вручную писать SQL-запрос `INSERT INTO plugins`. -- **Сложность:** Низкая. - -### Вариант Б: Адаптация + автоматическая сборка и регистрация (Скрипт) -- **Описание:** То же, что и Вариант А, но дополнительно добавить скрипт или таски в `Taskfile.yml`, которые будут компилировать базовые плагины (например, `protoc-gen-go`, `protoc-gen-go-grpc`), складывать их в нужную директорию `./plugins/...` и автоматически прописывать в PostgreSQL через `docker compose exec postgres psql...`. -- **Плюсы:** Максимально удобный локальный тест. Плагины сами появятся в системе. -- **Минусы:** Чуть больше кода для bash/Taskfile. -- **Сложность:** Средняя. - -## Ограничения и риски (Constraints & Risks) -- **Различия архитектур:** Плагины — это бинарники. При запуске сервиса внутри Docker (`task up`), сервис работает в Linux. Если мы монтируем папку `./plugins` с хоста (macOS), то скомпилированные на macOS плагины (darwin/arm64) **не запустятся** внутри Linux-контейнера. Нужно либо компилировать плагины под linux (`GOOS=linux`), либо выполнять их сборку внутри Docker. -- **Локальный запуск:** При запуске `task run-local` сервис работает на хосте (macOS), и ему нужны бинарники под macOS. - -## Рекомендованное направление (Recommended Direction) -Рекомендуется **Вариант Б**. -Чтобы решить проблему архитектур: мы можем переписать скрипты так, чтобы плагины всегда собирались через многостадийный `Dockerfile` или `go build` с нужными флагами. Проще всего оставить логику сборки кросс-платформенной (для локального запуска собирать под хост, для Docker — под Linux). Но так как мы хотим всё унифицировать: предлагается для `task up` собирать плагины под Linux и класть в `./plugins`, а для `run-local` собирать под хост. - -## Границы области (Scope Boundaries) -- **Must-have (v1):** - - Замена старых конфигов `registry.domain` на `registry.plugins_dir`. - - Очистка `docker-compose.yml` (удаление registry, docker.sock, добавление volume для plugins). - - Настройка Taskfile для сборки необходимых тестовых плагинов (go, grpc) в локальную папку. - - Механизм внедрения тестовых данных (плагинов) в БД (например, скрипт `init-plugins.sql` или таска в `Taskfile`). -- **Deferred (v2):** - - Динамическая загрузка плагинов через API. -- **Needs spike:** - - Отсутствуют. - -## Допущения и открытые вопросы (Assumptions & Open Questions) -- `[ASSUMPTION: We can use a SQL script loaded by postgres initdb.d to register test plugins]`: Можно положить файл инициализации в `docker-compose` чтобы база сразу стартовала с зарегистрированными плагинами. -- **Вопрос 1:** Тебе больше нравится, чтобы таска в `Taskfile` собирала бинарники через обычный `go build` (с `GOOS=linux`) на твоём маке, или мы будем собирать их внутри какого-нибудь временного Docker-контейнера? -- **Вопрос 2:** Достаточно ли для начала базовых плагинов (`protoc-gen-go`, `protoc-gen-go-grpc`), или нужны еще какие-то для проверки? diff --git a/.spec/features/local-test-env/revisions/explore-rev-2-2026-05-24T15-13-55Z.md b/.spec/features/local-test-env/revisions/explore-rev-2-2026-05-24T15-13-55Z.md deleted file mode 100644 index 73c3fbd..0000000 --- a/.spec/features/local-test-env/revisions/explore-rev-2-2026-05-24T15-13-55Z.md +++ /dev/null @@ -1,142 +0,0 @@ -# Exploration: local-test-env - -## Введение (Intent) - -После миграции на `disk-plugin-execution` механизм выполнения плагинов изменился с Docker-контейнеров на запуск бинарников с диска. Нужно адаптировать локальное окружение разработчика: конфиги, docker-compose, Taskfile — чтобы можно было легко поставить пару плагинов для тестов и запустить сервис. - -## Исследование (Investigation) - -### Существующие плагины в `registry/` - -4 Dockerfile-а, все с идентичной структурой (multi-stage: `golang:alpine` → `scratch`): - -| Плагин | Dockerfile | Бинарник | -|--------|-----------|----------| -| `protocolbuffers/go:v1.36.10` | `registry/protocolbuffers/go/v1.36.10/Dockerfile` | `/protoc-gen-go` | -| `grpc/go:v1.5.1` | `registry/grpc/go/v1.5.1/Dockerfile` | `/protoc-gen-go-grpc` | -| `grpc-ecosystem/gateway:v2.27.3` | `registry/grpc-ecosystem/gateway/v2.27.3/Dockerfile` | `/protoc-gen-grpc-gateway` | -| `grpc-ecosystem/openapiv2:v2.27.3` | `registry/grpc-ecosystem/openapiv2/v2.27.3/Dockerfile` | `/protoc-gen-openapiv2` | - -### Конфиги — остатки старого подхода - -- `config.yml` (строка 13): `registry.domain: "localhost:5005"` — старое поле, не используемое новым кодом. Нужно заменить на `plugins_dir` + `max_output_size`. -- `config.local.yml` (строка 13): тоже `registry.domain: "localhost:5005"`. -- `cmd/main.go` (строка 76–79): структура `registryConfig` уже использует `PluginsDir` и `MaxOutputSize` — конфиги просто отстали. - -### docker-compose.yml - -- Сервис `registry` (`easyp-registry`, порт 5005) — Docker Registry v3, больше не нужен для plugin execution. -- Сервис `service` всё ещё монтирует `/var/run/docker.sock` (строка 211) — больше не нужен. -- Нет volume для `./plugins:/plugins`. - -### Taskfile.yml - -- `local-push-registry` — запускает `./push.sh` для пуша всех образов в локальный registry. Устарело. -- `local-push-required` — собирает и пушит образы `protoc-gen-go` и `protoc-gen-go-grpc`. Нужно переделать. - -### .gitignore - -- `plugins/` уже в `.gitignore` (строка 9). ✅ - -### Подход пользователя - -Пользователь предложил: **использовать существующие Dockerfile-ы из `registry/` как билд-среду**, но вместо пуша в Docker Registry — извлекать скомпилированный бинарник на диск в `plugins/`. - -Это реализуемо через: -```bash -# 1. Собрать образ (build stage компилирует бинарник) -docker build --platform linux/amd64 -t plugin-builder registry/protocolbuffers/go/v1.36.10/ - -# 2. Создать временный контейнер -CID=$(docker create plugin-builder) - -# 3. Извлечь бинарник -docker cp "$CID:/protoc-gen-go" ./plugins/protocolbuffers/go/v1.36.10/plugin - -# 4. Удалить временный контейнер -docker rm "$CID" -``` - -### Регистрация плагинов в БД - -После сборки бинарников, плагины нужно зарегистрировать в PostgreSQL. Формат `config` поля в таблице `plugins`: -```json -{ - "command": ["/plugins/protocolbuffers/go/v1.36.10/plugin"] -} -``` - -Можно сделать через SQL-скрипт `init-plugins.sql`, который запускается как часть `psql` в таске. - -## Инструменты сборки (Build Tooling) - -- **Orchestrator:** Taskfile v3 -- **Test:** `go test ./...` -- **Build:** `go build -o main ./cmd/main.go` -- **Lint:** `golangci-lint run ./...` -- **Generate:** `easyp --cfg easyp.yaml generate` -- **Source:** `Taskfile.yml` - -## Рассмотренные варианты (Options Considered) - -### Вариант А: `docker build` + `docker cp` (рекомендован) - -- **Описание:** Переиспользовать существующие Dockerfile-ы из `registry/`. Они уже настроены на кросс-компиляцию (`GOOS=linux GOARCH=amd64`). Мы просто собираем образ, создаём временный контейнер и копируем бинарник на хост. Один bash-скрипт (`build-plugins.sh`) делает это для всех плагинов. -- **Плюсы:** - - Dockerfile-ы уже есть, проверены и рабочие. - - Кросс-компиляция "из коробки" — не нужно настраивать Go-toolchain на хосте. - - UPX-сжатие бинарников (из Dockerfile-ов) — маленький размер. - - Единый источник правды для версий плагинов. -- **Минусы:** - - Требует Docker для сборки (но Docker всё равно нужен для `docker-compose up`). - - Для `task run-local` бинарники будут Linux-овые, а сервис на macOS → не совместимо. -- **Сложность:** Низкая. -- **Решение проблемы macOS:** Для `task run-local` нужен отдельный режим сборки без UPX (`GOOS=darwin GOARCH=arm64`). Можно добавить аргумент `--local` в скрипт. - -### Вариант Б: Чистый `go install` без Docker - -- **Описание:** Собирать плагины напрямую через `go install` на хосте. -- **Плюсы:** Быстрее, не требует Docker для сборки. -- **Минусы:** - - Нет UPX-сжатия. - - Нужен Go на хосте правильной версии. - - Дублирование версий (Dockerfile-ы и скрипт). - - Бинарники будут под хостовую ОС, нужно отдельно собирать под Linux для `docker compose`. -- **Сложность:** Низкая, но больше точек обслуживания. - -## Ограничения и риски (Constraints & Risks) - -- **Кросс-платформенность:** `docker compose up` запускает Linux-контейнер. `task run-local` запускает на macOS. Нужны разные бинарники. Предлагается решить через аргумент скрипта (`--local` для macOS). -- **Изменение конфигов:** `config.yml` и `config.local.yml` нужно обновить — это может сломать существующий workflow у других разработчиков. Но поле `domain` уже не используется кодом. -- **Docker Registry сервис:** Можно оставить для обратной совместимости (например, `push.sh` всё ещё используется в `task local-push-registry`). Но предлагается убрать. - -## Рекомендованное направление (Recommended Direction) - -**Вариант А** — `docker build` + `docker cp`. Создать скрипт `build-plugins.sh`, который: -1. Обходит все Dockerfile-ы в `registry/`. -2. Собирает образ. -3. Извлекает бинарник в `./plugins/{group}/{name}/{version}/plugin`. -4. Чистит временные контейнеры. - -Плюс SQL-скрипт для регистрации плагинов в БД. Всё оркестрируется через `Taskfile.yml`. - -## Границы области (Scope Boundaries) - -- **Must-have (v1):** - - Скрипт `build-plugins.sh` для сборки бинарников из Dockerfile-ов. - - Обновление `config.yml` и `config.local.yml`: `plugins_dir` + `max_output_size` вместо `domain`. - - Обновление `docker-compose.yml`: удалить `docker.sock`, добавить volume `./plugins:/plugins`. - - Обновление `Taskfile.yml`: переделать `local-push-required` на вызов `build-plugins.sh`. - - SQL-скрипт или таска для регистрации плагинов в БД. -- **Deferred (v2):** - - Удаление сервиса `registry` из `docker-compose.yml` (может быть ещё нужен для чего-то). - - Режим `--local` для сборки macOS-бинарников (`task run-local`). -- **Needs spike:** - - Нет. - -## Допущения и открытые вопросы (Assumptions & Open Questions) - -- [ASSUMPTION: Dockerfile-ы в `registry/` всегда имеют предсказуемую структуру: build-стейдж компилирует бинарник, ENTRYPOINT указывает на путь бинарника.] Это верно для всех 4 текущих Dockerfile-ов. -- [ASSUMPTION: Docker доступен на машине разработчика для сборки плагинов.] -- [ASSUMPTION: Для `task up` достаточно Linux/amd64 бинарников, так как сервис запускается в Docker.] -- **Вопрос:** Удалять ли сервис `registry` из `docker-compose.yml` сразу, или оставить на потом? diff --git a/.spec/features/local-test-env/revisions/explore-rev-3-2026-05-24T15-16-54Z.md b/.spec/features/local-test-env/revisions/explore-rev-3-2026-05-24T15-16-54Z.md deleted file mode 100644 index 72d83c1..0000000 --- a/.spec/features/local-test-env/revisions/explore-rev-3-2026-05-24T15-16-54Z.md +++ /dev/null @@ -1,145 +0,0 @@ -# Exploration: local-test-env - -## Введение (Intent) - -После миграции на `disk-plugin-execution` механизм выполнения плагинов изменился с Docker-контейнеров на запуск бинарников с диска. Нужно адаптировать локальное окружение разработчика: конфиги, docker-compose, Taskfile — чтобы можно было легко поставить пару плагинов для тестов и запустить сервис. - -## Исследование (Investigation) - -### Существующие плагины в `registry/` - -4 Dockerfile-а, все с идентичной структурой (multi-stage: `golang:alpine` → `scratch`): - -| Плагин | Dockerfile | Бинарник | -|--------|-----------|----------| -| `protocolbuffers/go:v1.36.10` | `registry/protocolbuffers/go/v1.36.10/Dockerfile` | `/protoc-gen-go` | -| `grpc/go:v1.5.1` | `registry/grpc/go/v1.5.1/Dockerfile` | `/protoc-gen-go-grpc` | -| `grpc-ecosystem/gateway:v2.27.3` | `registry/grpc-ecosystem/gateway/v2.27.3/Dockerfile` | `/protoc-gen-grpc-gateway` | -| `grpc-ecosystem/openapiv2:v2.27.3` | `registry/grpc-ecosystem/openapiv2/v2.27.3/Dockerfile` | `/protoc-gen-openapiv2` | - -### Конфиги — остатки старого подхода - -- `config.yml` (строка 13): `registry.domain: "localhost:5005"` — старое поле, не используемое новым кодом. Нужно заменить на `plugins_dir` + `max_output_size`. -- `config.local.yml` (строка 13): тоже `registry.domain: "localhost:5005"`. -- `cmd/main.go` (строка 76–79): структура `registryConfig` уже использует `PluginsDir` и `MaxOutputSize` — конфиги просто отстали. - -### docker-compose.yml - -- Сервис `registry` (`easyp-registry`, порт 5005) — Docker Registry v3, больше не нужен для plugin execution. -- Сервис `service` всё ещё монтирует `/var/run/docker.sock` (строка 211) — больше не нужен. -- Нет volume для `./plugins:/plugins`. - -### Taskfile.yml - -- `local-push-registry` — запускает `./push.sh` для пуша всех образов в локальный registry. Устарело. -- `local-push-required` — собирает и пушит образы `protoc-gen-go` и `protoc-gen-go-grpc`. Нужно переделать. - -### .gitignore - -- `plugins/` уже в `.gitignore` (строка 9). ✅ - -### Подход: `docker build --output` - -Переделать Dockerfile-ы так, чтобы они **только собирали бинарник** и ничего больше. Финальный стейдж — просто `COPY` в `/plugin`. Затем вызов с `--output` копирует результат прямо на диск: - -**Пример переделанного Dockerfile (`registry/protocolbuffers/go/v1.36.10/Dockerfile`):** -```dockerfile -FROM golang:1.25-alpine3.22 - -ENV CGO_ENABLED=0 GOOS=linux GOARCH=amd64 -RUN apk add upx=5.0.2-r0 --no-cache - -RUN --mount=type=cache,target=/go/pkg/mod \ - go install -ldflags "-s -w" -trimpath google.golang.org/protobuf/cmd/protoc-gen-go@v1.36.10 \ - && mv /go/bin/${GOOS}_${GOARCH}/protoc-gen-go /go/bin/protoc-gen-go || true \ - && upx --best --lzma /go/bin/protoc-gen-go - -FROM scratch -COPY --from=0 /go/bin/protoc-gen-go /plugin -``` - -**Вызов:** -```bash -docker build --output=./plugins/protocolbuffers/go/v1.36.10/ registry/protocolbuffers/go/v1.36.10/ -``` - -Результат: `./plugins/protocolbuffers/go/v1.36.10/plugin` — готовый бинарник. Одна команда, без промежуточных контейнеров. - -### Регистрация плагинов в БД - -После сборки бинарников, плагины нужно зарегистрировать в PostgreSQL. Формат `config` поля в таблице `plugins`: -```json -{ - "command": ["/plugins/protocolbuffers/go/v1.36.10/plugin"] -} -``` - -Можно сделать через SQL-скрипт `init-plugins.sql`, который запускается через `psql` в таске. - -## Инструменты сборки (Build Tooling) - -- **Orchestrator:** Taskfile v3 -- **Test:** `go test ./...` -- **Build:** `go build -o main ./cmd/main.go` -- **Lint:** `golangci-lint run ./...` -- **Generate:** `easyp --cfg easyp.yaml generate` -- **Source:** `Taskfile.yml` - -## Рассмотренные варианты (Options Considered) - -### Вариант А: Переделать Dockerfile-ы + `docker build --output` (рекомендован) - -- **Описание:** Упростить Dockerfile-ы: убрать `ENTRYPOINT`, `USER`, `/etc/passwd`. Финальный стейдж `FROM scratch` содержит только `COPY --from=0 ... /plugin`. Сборка через `docker build --output=./plugins/{path}/ registry/{path}/` — Docker выплёвывает бинарник прямо на диск. Скрипт `build-plugins.sh` обходит все Dockerfile-ы и вызывает эту команду. -- **Плюсы:** - - Dockerfile-ы становятся чище — одна ответственность (билд бинарника). - - Не нужны промежуточные `docker create` / `docker cp` / `docker rm`. - - Кросс-компиляция из коробки, UPX-сжатие. - - Единый источник правды для версий плагинов. -- **Минусы:** - - Требует BuildKit (`DOCKER_BUILDKIT=1`), но он включён по умолчанию в Docker >= 23.0. -- **Сложность:** Низкая. - -### Вариант Б: `docker build` + `docker create` + `docker cp` - -- **Описание:** Оставить Dockerfile-ы как есть (multi-stage с scratch), собирать образ, создавать контейнер, копировать бинарник, удалять контейнер. -- **Плюсы:** Не меняет Dockerfile-ы. -- **Минусы:** 4 команды вместо 1. Нужно чистить контейнеры. Dockerfile-ы содержат ненужные `ENTRYPOINT`/`USER`. -- **Сложность:** Низкая, но больше boilerplate. - -## Ограничения и риски (Constraints & Risks) - -- **Кросс-платформенность:** `docker compose up` запускает Linux-контейнер. `task run-local` запускает на macOS. Dockerfile-ы сейчас хардкодят `GOOS=linux GOARCH=amd64`. Для `task run-local` нужен будет отдельный режим — но это Deferred (v2). -- **Изменение конфигов:** `config.yml` и `config.local.yml` нужно обновить — но поле `domain` уже не используется кодом, так что ничего не ломается. -- **BuildKit:** `--output` требует BuildKit, который включён по умолчанию в Docker >= 23.0. Для старых версий нужен `DOCKER_BUILDKIT=1`. - -## Рекомендованное направление (Recommended Direction) - -**Вариант А** — переделать Dockerfile-ы + `docker build --output`. Создать скрипт `build-plugins.sh`, который: -1. Обходит все Dockerfile-ы в `registry/`. -2. Для каждого вызывает `docker build --output=./plugins/{group}/{name}/{version}/` . -3. Результат: готовые бинарники в `./plugins/`. - -Плюс обновить конфиги, docker-compose, Taskfile и добавить механизм регистрации плагинов в БД. - -## Границы области (Scope Boundaries) - -- **Must-have (v1):** - - Переделать 4 Dockerfile-а в `registry/` (убрать `ENTRYPOINT`/`USER`/`passwd`, финальный стейдж выдаёт только `/plugin`). - - Скрипт `build-plugins.sh` для сборки всех плагинов через `docker build --output`. - - Обновление `config.yml` и `config.local.yml`: `plugins_dir` + `max_output_size` вместо `domain`. - - Обновление `docker-compose.yml`: убрать `docker.sock` mount, добавить volume `./plugins:/plugins`. - - Обновление `Taskfile.yml`: переделать `local-push-required` → `build-plugins` (вызов `build-plugins.sh`). - - Механизм регистрации плагинов в БД (SQL-скрипт или таска). -- **Deferred (v2):** - - Удаление сервиса `registry` из `docker-compose.yml`. - - Режим `--local` для сборки macOS-бинарников (`task run-local`). - - Удаление `push.sh` и `local-push-registry` таски. -- **Needs spike:** - - Нет. - -## Допущения и открытые вопросы (Assumptions & Open Questions) - -- [ASSUMPTION: Docker >= 23.0 установлен у всех разработчиков (BuildKit включён по умолчанию).] -- [ASSUMPTION: Все Dockerfile-ы в `registry/` имеют предсказуемую структуру: один build-стейдж, один бинарник.] -- [ASSUMPTION: Для `task up` достаточно Linux/amd64 бинарников.] -- **Вопрос:** Удалять ли сервис `registry` из `docker-compose.yml` сразу, или оставить на v2? diff --git a/.spec/features/local-test-env/revisions/explore-rev-4-2026-05-24T15-19-19Z.md b/.spec/features/local-test-env/revisions/explore-rev-4-2026-05-24T15-19-19Z.md deleted file mode 100644 index 831379e..0000000 --- a/.spec/features/local-test-env/revisions/explore-rev-4-2026-05-24T15-19-19Z.md +++ /dev/null @@ -1,153 +0,0 @@ -# Exploration: local-test-env - -## Введение (Intent) - -После миграции на `disk-plugin-execution` механизм выполнения плагинов изменился с Docker-контейнеров на запуск бинарников с диска. Нужно адаптировать локальное окружение разработчика: конфиги, docker-compose, Taskfile — чтобы можно было легко поставить пару плагинов для тестов и запустить сервис. - -## Исследование (Investigation) - -### Существующие плагины в `registry/` - -4 Dockerfile-а, все с идентичной структурой (multi-stage: `golang:alpine` → `scratch`): - -| Плагин | Dockerfile | Бинарник | -|--------|-----------|----------| -| `protocolbuffers/go:v1.36.10` | `registry/protocolbuffers/go/v1.36.10/Dockerfile` | `/protoc-gen-go` | -| `grpc/go:v1.5.1` | `registry/grpc/go/v1.5.1/Dockerfile` | `/protoc-gen-go-grpc` | -| `grpc-ecosystem/gateway:v2.27.3` | `registry/grpc-ecosystem/gateway/v2.27.3/Dockerfile` | `/protoc-gen-grpc-gateway` | -| `grpc-ecosystem/openapiv2:v2.27.3` | `registry/grpc-ecosystem/openapiv2/v2.27.3/Dockerfile` | `/protoc-gen-openapiv2` | - -### Конфиги — остатки старого подхода - -- `config.yml` (строка 13): `registry.domain: "localhost:5005"` — старое поле, не используемое новым кодом. Нужно заменить на `plugins_dir` + `max_output_size`. -- `config.local.yml` (строка 13): тоже `registry.domain: "localhost:5005"`. -- `cmd/main.go` (строка 76–79): структура `registryConfig` уже использует `PluginsDir` и `MaxOutputSize` — конфиги просто отстали. - -### docker-compose.yml - -- Сервис `registry` (`easyp-registry`, порт 5005) — Docker Registry v3, больше не нужен для plugin execution. -- Сервис `service` всё ещё монтирует `/var/run/docker.sock` (строка 211) — больше не нужен. -- Нет volume для `./plugins:/plugins`. - -### Taskfile.yml - -- `local-push-registry` — запускает `./push.sh` для пуша всех образов в локальный registry. Устарело. -- `local-push-required` — собирает и пушит образы `protoc-gen-go` и `protoc-gen-go-grpc`. Нужно переделать. - -### .gitignore - -- `plugins/` уже в `.gitignore` (строка 9). ✅ - -### Подход: `docker build --output` - -Переделать Dockerfile-ы так, чтобы они **только собирали бинарник** и ничего больше. Финальный стейдж — просто `COPY` в `/plugin`. Затем вызов с `--output` копирует результат прямо на диск: - -**Пример переделанного Dockerfile (`registry/protocolbuffers/go/v1.36.10/Dockerfile`):** -```dockerfile -FROM golang:1.25-alpine3.22 - -ENV CGO_ENABLED=0 GOOS=linux GOARCH=amd64 -RUN apk add upx=5.0.2-r0 --no-cache - -RUN --mount=type=cache,target=/go/pkg/mod \ - go install -ldflags "-s -w" -trimpath google.golang.org/protobuf/cmd/protoc-gen-go@v1.36.10 \ - && mv /go/bin/${GOOS}_${GOARCH}/protoc-gen-go /go/bin/protoc-gen-go || true \ - && upx --best --lzma /go/bin/protoc-gen-go - -FROM scratch -COPY --from=0 /go/bin/protoc-gen-go /plugin -``` - -**Вызов:** -```bash -docker build --output=./plugins/protocolbuffers/go/v1.36.10/ registry/protocolbuffers/go/v1.36.10/ -``` - -Результат: `./plugins/protocolbuffers/go/v1.36.10/plugin` — готовый бинарник. Одна команда, без промежуточных контейнеров. - -### Регистрация плагинов через API - -После сборки бинарников и запуска сервиса, плагины регистрируются через gRPC API `CreatePlugin`: - -```bash -grpcurl -plaintext -d '{ - "group": "protocolbuffers", - "name": "go", - "version": "v1.36.10", - "config": {"command": ["/plugins/protocolbuffers/go/v1.36.10/plugin"]}, - "tags": ["go", "official"] -}' localhost:8080 api.generator.v1.ServiceAPI/CreatePlugin -``` - -Скрипт `register-plugins.sh` (или часть `build-plugins.sh`) будет вызывать `CreatePlugin` для каждого собранного плагина. Это лучше, чем прямой SQL, потому что: -- Проходит через всю цепочку валидации (`ValidateConfig`). -- Не нужен доступ к PostgreSQL напрямую. -- Идемпотентность: если плагин уже зарегистрирован (`ALREADY_EXISTS`), скрипт просто продолжает. - -## Инструменты сборки (Build Tooling) - -- **Orchestrator:** Taskfile v3 -- **Test:** `go test ./...` -- **Build:** `go build -o main ./cmd/main.go` -- **Lint:** `golangci-lint run ./...` -- **Generate:** `easyp --cfg easyp.yaml generate` -- **Source:** `Taskfile.yml` - -## Рассмотренные варианты (Options Considered) - -### Вариант А: Переделать Dockerfile-ы + `docker build --output` (рекомендован) - -- **Описание:** Упростить Dockerfile-ы: убрать `ENTRYPOINT`, `USER`, `/etc/passwd`. Финальный стейдж `FROM scratch` содержит только `COPY --from=0 ... /plugin`. Сборка через `docker build --output=./plugins/{path}/ registry/{path}/` — Docker выплёвывает бинарник прямо на диск. Скрипт `build-plugins.sh` обходит все Dockerfile-ы и вызывает эту команду. -- **Плюсы:** - - Dockerfile-ы становятся чище — одна ответственность (билд бинарника). - - Не нужны промежуточные `docker create` / `docker cp` / `docker rm`. - - Кросс-компиляция из коробки, UPX-сжатие. - - Единый источник правды для версий плагинов. -- **Минусы:** - - Требует BuildKit (`DOCKER_BUILDKIT=1`), но он включён по умолчанию в Docker >= 23.0. -- **Сложность:** Низкая. - -### Вариант Б: `docker build` + `docker create` + `docker cp` - -- **Описание:** Оставить Dockerfile-ы как есть (multi-stage с scratch), собирать образ, создавать контейнер, копировать бинарник, удалять контейнер. -- **Плюсы:** Не меняет Dockerfile-ы. -- **Минусы:** 4 команды вместо 1. Нужно чистить контейнеры. Dockerfile-ы содержат ненужные `ENTRYPOINT`/`USER`. -- **Сложность:** Низкая, но больше boilerplate. - -## Ограничения и риски (Constraints & Risks) - -- **Кросс-платформенность:** `docker compose up` запускает Linux-контейнер. `task run-local` запускает на macOS. Dockerfile-ы сейчас хардкодят `GOOS=linux GOARCH=amd64`. Для `task run-local` нужен будет отдельный режим — но это Deferred (v2). -- **Изменение конфигов:** `config.yml` и `config.local.yml` нужно обновить — но поле `domain` уже не используется кодом, так что ничего не ломается. -- **BuildKit:** `--output` требует BuildKit, который включён по умолчанию в Docker >= 23.0. Для старых версий нужен `DOCKER_BUILDKIT=1`. - -## Рекомендованное направление (Recommended Direction) - -**Вариант А** — переделать Dockerfile-ы + `docker build --output`. Создать скрипт `build-plugins.sh`, который: -1. Обходит все Dockerfile-ы в `registry/`. -2. Для каждого вызывает `docker build --output=./plugins/{group}/{name}/{version}/` . -3. Результат: готовые бинарники в `./plugins/`. - -Плюс обновить конфиги, docker-compose, Taskfile и добавить механизм регистрации плагинов в БД. - -## Границы области (Scope Boundaries) - -- **Must-have (v1):** - - Переделать 4 Dockerfile-а в `registry/` (убрать `ENTRYPOINT`/`USER`/`passwd`, финальный стейдж выдаёт только `/plugin`). - - Скрипт `build-plugins.sh` для сборки всех плагинов через `docker build --output`. - - Обновление `config.yml` и `config.local.yml`: `plugins_dir` + `max_output_size` вместо `domain`. - - Обновление `docker-compose.yml`: убрать `docker.sock` mount, добавить volume `./plugins:/plugins`. - - Обновление `Taskfile.yml`: переделать `local-push-required` → `build-plugins` (вызов `build-plugins.sh`). - - Скрипт или таска для регистрации плагинов через gRPC API (`CreatePlugin`). -- **Deferred (v2):** - - Удаление сервиса `registry` из `docker-compose.yml`. - - Режим `--local` для сборки macOS-бинарников (`task run-local`). - - Удаление `push.sh` и `local-push-registry` таски. -- **Needs spike:** - - Нет. - -## Допущения и открытые вопросы (Assumptions & Open Questions) - -- [ASSUMPTION: Docker >= 23.0 установлен у всех разработчиков (BuildKit включён по умолчанию).] -- [ASSUMPTION: Все Dockerfile-ы в `registry/` имеют предсказуемую структуру: один build-стейдж, один бинарник.] -- [ASSUMPTION: Для `task up` достаточно Linux/amd64 бинарников.] -- **Вопрос:** Удалять ли сервис `registry` из `docker-compose.yml` сразу, или оставить на v2? diff --git a/.spec/features/local-test-env/revisions/explore-rev-5-2026-05-24T15-20-37Z.md b/.spec/features/local-test-env/revisions/explore-rev-5-2026-05-24T15-20-37Z.md deleted file mode 100644 index 7c7f364..0000000 --- a/.spec/features/local-test-env/revisions/explore-rev-5-2026-05-24T15-20-37Z.md +++ /dev/null @@ -1,152 +0,0 @@ -# Exploration: local-test-env - -## Введение (Intent) - -После миграции на `disk-plugin-execution` механизм выполнения плагинов изменился с Docker-контейнеров на запуск бинарников с диска. Нужно адаптировать локальное окружение разработчика: конфиги, docker-compose, Taskfile — чтобы можно было легко поставить пару плагинов для тестов и запустить сервис. - -## Исследование (Investigation) - -### Существующие плагины в `registry/` - -4 Dockerfile-а, все с идентичной структурой (multi-stage: `golang:alpine` → `scratch`): - -| Плагин | Dockerfile | Бинарник | -|--------|-----------|----------| -| `protocolbuffers/go:v1.36.10` | `registry/protocolbuffers/go/v1.36.10/Dockerfile` | `/protoc-gen-go` | -| `grpc/go:v1.5.1` | `registry/grpc/go/v1.5.1/Dockerfile` | `/protoc-gen-go-grpc` | -| `grpc-ecosystem/gateway:v2.27.3` | `registry/grpc-ecosystem/gateway/v2.27.3/Dockerfile` | `/protoc-gen-grpc-gateway` | -| `grpc-ecosystem/openapiv2:v2.27.3` | `registry/grpc-ecosystem/openapiv2/v2.27.3/Dockerfile` | `/protoc-gen-openapiv2` | - -### Конфиги — остатки старого подхода - -- `config.yml` (строка 13): `registry.domain: "localhost:5005"` — старое поле, не используемое новым кодом. Нужно заменить на `plugins_dir` + `max_output_size`. -- `config.local.yml` (строка 13): тоже `registry.domain: "localhost:5005"`. -- `cmd/main.go` (строка 76–79): структура `registryConfig` уже использует `PluginsDir` и `MaxOutputSize` — конфиги просто отстали. - -### docker-compose.yml - -- Сервис `registry` (`easyp-registry`, порт 5005) — Docker Registry v3, больше не нужен для plugin execution. -- Сервис `service` всё ещё монтирует `/var/run/docker.sock` (строка 211) — больше не нужен. -- Нет volume для `./plugins:/plugins`. - -### Taskfile.yml - -- `local-push-registry` — запускает `./push.sh` для пуша всех образов в локальный registry. Устарело. -- `local-push-required` — собирает и пушит образы `protoc-gen-go` и `protoc-gen-go-grpc`. Нужно переделать. - -### .gitignore - -- `plugins/` уже в `.gitignore` (строка 9). ✅ - -### Подход: `docker build --output` - -Переделать Dockerfile-ы так, чтобы они **только собирали бинарник** и ничего больше. Финальный стейдж — просто `COPY` в `/plugin`. Затем вызов с `--output` копирует результат прямо на диск: - -**Пример переделанного Dockerfile (`registry/protocolbuffers/go/v1.36.10/Dockerfile`):** -```dockerfile -FROM golang:1.25-alpine3.22 - -ENV CGO_ENABLED=0 GOOS=linux GOARCH=amd64 -RUN apk add upx=5.0.2-r0 --no-cache - -RUN --mount=type=cache,target=/go/pkg/mod \ - go install -ldflags "-s -w" -trimpath google.golang.org/protobuf/cmd/protoc-gen-go@v1.36.10 \ - && mv /go/bin/${GOOS}_${GOARCH}/protoc-gen-go /go/bin/protoc-gen-go || true \ - && upx --best --lzma /go/bin/protoc-gen-go - -FROM scratch -COPY --from=0 /go/bin/protoc-gen-go /plugin -``` - -**Вызов:** -```bash -docker build --output=./plugins/protocolbuffers/go/v1.36.10/ registry/protocolbuffers/go/v1.36.10/ -``` - -Результат: `./plugins/protocolbuffers/go/v1.36.10/plugin` — готовый бинарник. Одна команда, без промежуточных контейнеров. - -### Регистрация плагинов через API - -После сборки бинарников и запуска сервиса, плагины регистрируются через gRPC API `CreatePlugin`: - -```bash -grpcurl -plaintext -d '{ - "group": "protocolbuffers", - "name": "go", - "version": "v1.36.10", - "config": {"command": ["/plugins/protocolbuffers/go/v1.36.10/plugin"]}, - "tags": ["go", "official"] -}' localhost:8080 api.generator.v1.ServiceAPI/CreatePlugin -``` - -Скрипт `register-plugins.sh` (или часть `build-plugins.sh`) будет вызывать `CreatePlugin` для каждого собранного плагина. Это лучше, чем прямой SQL, потому что: -- Проходит через всю цепочку валидации (`ValidateConfig`). -- Не нужен доступ к PostgreSQL напрямую. -- Идемпотентность: если плагин уже зарегистрирован (`ALREADY_EXISTS`), скрипт просто продолжает. - -## Инструменты сборки (Build Tooling) - -- **Orchestrator:** Taskfile v3 -- **Test:** `go test ./...` -- **Build:** `go build -o main ./cmd/main.go` -- **Lint:** `golangci-lint run ./...` -- **Generate:** `easyp --cfg easyp.yaml generate` -- **Source:** `Taskfile.yml` - -## Рассмотренные варианты (Options Considered) - -### Вариант А: Переделать Dockerfile-ы + `docker build --output` (рекомендован) - -- **Описание:** Упростить Dockerfile-ы: убрать `ENTRYPOINT`, `USER`, `/etc/passwd`. Финальный стейдж `FROM scratch` содержит только `COPY --from=0 ... /plugin`. Сборка через `docker build --output=./plugins/{path}/ registry/{path}/` — Docker выплёвывает бинарник прямо на диск. Скрипт `build-plugins.sh` обходит все Dockerfile-ы и вызывает эту команду. -- **Плюсы:** - - Dockerfile-ы становятся чище — одна ответственность (билд бинарника). - - Не нужны промежуточные `docker create` / `docker cp` / `docker rm`. - - Кросс-компиляция из коробки, UPX-сжатие. - - Единый источник правды для версий плагинов. -- **Минусы:** - - Требует BuildKit (`DOCKER_BUILDKIT=1`), но он включён по умолчанию в Docker >= 23.0. -- **Сложность:** Низкая. - -### Вариант Б: `docker build` + `docker create` + `docker cp` - -- **Описание:** Оставить Dockerfile-ы как есть (multi-stage с scratch), собирать образ, создавать контейнер, копировать бинарник, удалять контейнер. -- **Плюсы:** Не меняет Dockerfile-ы. -- **Минусы:** 4 команды вместо 1. Нужно чистить контейнеры. Dockerfile-ы содержат ненужные `ENTRYPOINT`/`USER`. -- **Сложность:** Низкая, но больше boilerplate. - -## Ограничения и риски (Constraints & Risks) - -- **Кросс-платформенность:** `docker compose up` запускает Linux-контейнер. `task run-local` запускает на macOS. Dockerfile-ы сейчас хардкодят `GOOS=linux GOARCH=amd64`. Для `task run-local` нужен будет отдельный режим — но это Deferred (v2). -- **Изменение конфигов:** `config.yml` и `config.local.yml` нужно обновить — но поле `domain` уже не используется кодом, так что ничего не ломается. -- **BuildKit:** `--output` требует BuildKit, который включён по умолчанию в Docker >= 23.0. Для старых версий нужен `DOCKER_BUILDKIT=1`. - -## Рекомендованное направление (Recommended Direction) - -**Вариант А** — переделать Dockerfile-ы + `docker build --output`. Создать скрипт `build-plugins.sh`, который: -1. Обходит все Dockerfile-ы в `registry/`. -2. Для каждого вызывает `docker build --output=./plugins/{group}/{name}/{version}/` . -3. Результат: готовые бинарники в `./plugins/`. - -Плюс обновить конфиги, docker-compose, Taskfile и добавить механизм регистрации плагинов в БД. - -## Границы области (Scope Boundaries) - -- **Must-have (v1):** - - Переделать 4 Dockerfile-а в `registry/` (убрать `ENTRYPOINT`/`USER`/`passwd`, финальный стейдж выдаёт только `/plugin`). - - Скрипт `build-plugins.sh` для сборки всех плагинов через `docker build --output`. - - Обновление `config.yml` и `config.local.yml`: `plugins_dir` + `max_output_size` вместо `domain`. - - Обновление `docker-compose.yml`: убрать `docker.sock` mount, убрать сервис `registry`, убрать volume `registry-data`, добавить volume `./plugins:/plugins`. - - Обновление `Taskfile.yml`: переделать `local-push-required` → `build-plugins`, убрать `local-push-registry`. - - Удаление `push.sh`. - - Скрипт или таска для регистрации плагинов через gRPC API (`CreatePlugin`). -- **Deferred (v2):** - - Режим `--local` для сборки macOS-бинарников (`task run-local`). -- **Needs spike:** - - Нет. - -## Допущения и открытые вопросы (Assumptions & Open Questions) - -- [ASSUMPTION: Docker >= 23.0 установлен у всех разработчиков (BuildKit включён по умолчанию).] -- [ASSUMPTION: Все Dockerfile-ы в `registry/` имеют предсказуемую структуру: один build-стейдж, один бинарник.] -- [ASSUMPTION: Для `task up` достаточно Linux/amd64 бинарников.] -- Открытых вопросов нет — пользователь подтвердил полную очистку docker-compose от registry и docker.sock. diff --git a/.spec/features/local-test-env/revisions/implementation-rev-1-2026-05-24T15-35-11Z.md b/.spec/features/local-test-env/revisions/implementation-rev-1-2026-05-24T15-35-11Z.md deleted file mode 100644 index 09e3073..0000000 --- a/.spec/features/local-test-env/revisions/implementation-rev-1-2026-05-24T15-35-11Z.md +++ /dev/null @@ -1,38 +0,0 @@ -# local-test-env — Implementation Summary - -**Date:** 2026-05-24 - -## Выполненные задачи - -- [x] **T-1** GREEN — Baseline (go build + go test проходят) -- [x] **T-2** CODE — Переделаны 4 Dockerfile-а (убраны ENTRYPOINT/USER/passwd, финальный стейдж `/plugin`) -- [x] **T-3** CODE — Создан `build-plugins.sh` -- [x] **T-4** CODE — Создан `register-plugins.sh` -- [x] **T-5** CODE — Обновлены config.yml, config.local.yml, docker-compose.yml, Taskfile.yml, удалён push.sh -- [x] **T-6** VERIFY — Все проверки пройдены -- [x] **T-7** GATE — Финальная контрольная точка пройдена - -## Изменённые файлы - -| File | Change | -|------|--------| -| `registry/protocolbuffers/go/v1.36.10/Dockerfile` | Упрощён: `FROM scratch` + `COPY /plugin` | -| `registry/grpc/go/v1.5.1/Dockerfile` | Упрощён: `FROM scratch` + `COPY /plugin` | -| `registry/grpc-ecosystem/gateway/v2.27.3/Dockerfile` | Упрощён: `FROM scratch` + `COPY /plugin` | -| `registry/grpc-ecosystem/openapiv2/v2.27.3/Dockerfile` | Упрощён: `FROM scratch` + `COPY /plugin` | -| `build-plugins.sh` | **NEW** — Скрипт сборки бинарников через `docker build --output` | -| `register-plugins.sh` | **NEW** — Скрипт регистрации через gRPC CreatePlugin | -| `config.yml` | `registry.domain` → `registry.plugins_dir` + `max_output_size` | -| `config.local.yml` | `registry.domain` → `registry.plugins_dir` + `max_output_size` | -| `docker-compose.yml` | Убран `registry`, `registry-data`, `docker.sock` → `./plugins:/plugins:ro` | -| `Taskfile.yml` | Убраны `local-push-*`, добавлены `build-plugins` + `register-plugins` | -| `push.sh` | **DELETED** | - -## Результат верификации - -- ✓ `go build` — компилируется -- ✓ `go test ./...` — все тесты проходят -- ✓ Нет ENTRYPOINT в Dockerfile-ах -- ✓ `/plugin` присутствует в 4 Dockerfile-ах -- ✓ `build-plugins.sh` и `register-plugins.sh` исполняемые -- ✓ `push.sh` удалён diff --git a/.spec/features/local-test-env/revisions/requirements-rev-1-2026-05-24T15-23-10Z.md b/.spec/features/local-test-env/revisions/requirements-rev-1-2026-05-24T15-23-10Z.md deleted file mode 100644 index a894e61..0000000 --- a/.spec/features/local-test-env/revisions/requirements-rev-1-2026-05-24T15-23-10Z.md +++ /dev/null @@ -1,98 +0,0 @@ -# local-test-env — Requirements - -**Status:** Draft -**Date:** 2026-05-24 - -## Обзор - -Адаптация локального окружения разработчика к новому способу выполнения плагинов (бинарники с диска вместо Docker-контейнеров). Включает: переделку Dockerfile-ов из `registry/` для сборки бинарников через `docker build --output`, скрипты для сборки и регистрации плагинов, очистку docker-compose от устаревших сервисов (registry, docker.sock), обновление конфигов и Taskfile. - -## Глоссарий - -| Термин | Определение | Code Artifact | -|--------|------------|---------------| -| Plugin binary | Скомпилированный Go-бинарник плагина, принимающий `CodeGeneratorRequest` на stdin и отдающий `CodeGeneratorResponse` на stdout | `./plugins/{group}/{name}/{version}/plugin` | -| `PluginConfig` | JSON-конфигурация плагина с массивом `command`, опциональными `env` и `timeout` | `internal/adapters/registry/registry.go` → `PluginConfig` | -| `build-plugins.sh` | Скрипт сборки plugin binary из Dockerfile-ов через `docker build --output` | `build-plugins.sh` | -| `register-plugins.sh` | Скрипт регистрации собранных плагинов через gRPC API `CreatePlugin` | `register-plugins.sh` | - ---- - -## Требования - -### 1. Dockerfile-ы - -**REQ-1.1** WHEN `docker build --output=./plugins/{group}/{name}/{version}/ registry/{group}/{name}/{version}/` выполняется для любого Dockerfile из `registry/`, the system SHALL создать файл `./plugins/{group}/{name}/{version}/plugin` — исполняемый Linux/amd64 бинарник. - -**REQ-1.2** WHEN Dockerfile собирается, the system SHALL использовать UPX-сжатие для минимизации размера бинарника. - -**REQ-1.3** WHEN финальный стейдж Dockerfile описывается, the system SHALL содержать только `FROM scratch` и `COPY` бинарника в `/plugin` — без `ENTRYPOINT`, `USER`, или `/etc/passwd`. - -### 2. Скрипт сборки (`build-plugins.sh`) - -**REQ-2.1** WHEN `build-plugins.sh` запускается, the system SHALL найти все файлы `Dockerfile` в директории `registry/` рекурсивно и собрать каждый через `docker build --output`. - -**REQ-2.2** WHEN `docker build` для любого плагина завершается с ошибкой, the system SHALL немедленно остановить выполнение скрипта с ненулевым exit-кодом (`set -e`). - -**REQ-2.3** WHEN `build-plugins.sh` завершается успешно, the system SHALL создать для каждого Dockerfile файл `./plugins/{group}/{name}/{version}/plugin` с правами на исполнение. - -### 3. Скрипт регистрации (`register-plugins.sh`) - -**REQ-3.1** WHEN `register-plugins.sh` запускается, the system SHALL для каждого собранного плагина в директории `plugins/` вызвать gRPC метод `CreatePlugin` с полями `group`, `name`, `version` и `config.command = ["/plugins/{group}/{name}/{version}/plugin"]`. - -**REQ-3.2** WHEN gRPC-вызов `CreatePlugin` возвращает ошибку `ALREADY_EXISTS`, the system SHALL пропустить этот плагин и продолжить с остальными (не завершаться с ошибкой). - -**REQ-3.3** WHEN gRPC-вызов `CreatePlugin` возвращает любую другую ошибку (кроме `ALREADY_EXISTS`), the system SHALL немедленно остановить выполнение с ненулевым exit-кодом. - -### 4. Конфигурация - -**REQ-4.1** WHEN сервис запускается с `config.yml`, the system SHALL использовать поле `registry.plugins_dir` (по умолчанию `/plugins`) вместо устаревшего `registry.domain`. - -**REQ-4.2** WHEN сервис запускается с `config.yml`, the system SHALL использовать поле `registry.max_output_size` (по умолчанию `67108864`, 64 МБ). - -**REQ-4.3** WHEN сервис запускается с `config.local.yml`, the system SHALL использовать `registry.plugins_dir` указывающий на локальную директорию `./plugins`. - -### 5. Docker Compose - -**REQ-5.1** WHEN `docker-compose.yml` описывает сервис `service`, the system SHALL монтировать volume `./plugins:/plugins` вместо `/var/run/docker.sock`. - -**REQ-5.2** WHEN `docker-compose.yml` описывает инфраструктуру, the system SHALL не содержать сервис `registry` и volume `registry-data`. - -### 6. Taskfile - -**REQ-6.1** WHEN `task build-plugins` выполняется, the system SHALL вызвать `build-plugins.sh` для сборки всех плагинов. - -**REQ-6.2** WHEN `task register-plugins` выполняется, the system SHALL вызвать `register-plugins.sh` для регистрации всех плагинов через gRPC API. - -**REQ-6.3** WHEN `task run` выполняется, the system SHALL использовать `build-plugins` вместо устаревшей `local-push-registry` в зависимостях. - -**REQ-6.4** WHEN Taskfile описывает таски, the system SHALL не содержать `local-push-registry` и `local-push-required`. - -### 7. Удаление устаревших файлов - -**REQ-7.1** WHEN проект собран, the system SHALL не содержать файл `push.sh` в корне репозитория. - ---- - -## Порядок зависимостей (Topological Order) - -``` -REQ-1.1..1.3 → REQ-2.1..2.3 → REQ-3.1..3.3 -Причина: Dockerfile-ы (1.x) нужны для скрипта сборки (2.x), который создаёт бинарники для скрипта регистрации (3.x). - -REQ-4.1..4.3 (независимы — можно параллельно с 1–3) -REQ-5.1..5.2 (независимы — можно параллельно с 1–3) -REQ-6.1..6.4 → зависят от REQ-2.x и REQ-3.x (таски ссылаются на скрипты) -REQ-7.1 (независим) -``` - ---- - -## Команды верификации - -| Действие | Команда | Источник | -|----------|---------|----------| -| Test | `go test ./...` | Taskfile.yml | -| Build | `go build -o main ./cmd/main.go` | Taskfile.yml | -| Lint | `golangci-lint run ./...` | Taskfile.yml | -| Generate | `easyp --cfg easyp.yaml generate` | Taskfile.yml | diff --git a/.spec/features/local-test-env/revisions/review-rev-1-2026-05-24T15-40-11Z.md b/.spec/features/local-test-env/revisions/review-rev-1-2026-05-24T15-40-11Z.md deleted file mode 100644 index d7a825d..0000000 --- a/.spec/features/local-test-env/revisions/review-rev-1-2026-05-24T15-40-11Z.md +++ /dev/null @@ -1,153 +0,0 @@ -# Code Review: local-test-env - -## Verdict: PASS - -Все 17 требований реализованы. Код соответствует дизайну. Нет security-проблем (изменения затрагивают только скрипты, конфиги и Dockerfile-ы — не Go-код). Сборка и тесты проходят. Один `nit`-уровневый замечание. - ---- - -## Change Set - -| File | Status | Notes | -|------|--------|-------| -| `registry/protocolbuffers/go/v1.36.10/Dockerfile` | ✅ Planned | Упрощён: `FROM scratch` + `COPY /plugin` | -| `registry/grpc/go/v1.5.1/Dockerfile` | ✅ Planned | Упрощён: `FROM scratch` + `COPY /plugin` | -| `registry/grpc-ecosystem/gateway/v2.27.3/Dockerfile` | ✅ Planned | Упрощён: `FROM scratch` + `COPY /plugin` | -| `registry/grpc-ecosystem/openapiv2/v2.27.3/Dockerfile` | ✅ Planned | Упрощён: `FROM scratch` + `COPY /plugin` | -| `build-plugins.sh` | ✅ Planned | NEW — скрипт сборки | -| `register-plugins.sh` | ✅ Planned | NEW — скрипт регистрации | -| `config.yml` | ✅ Planned | `plugins_dir` + `max_output_size` | -| `config.local.yml` | ✅ Planned | `plugins_dir` + `max_output_size` | -| `docker-compose.yml` | ✅ Planned | Удалён registry, docker.sock → plugins:ro | -| `Taskfile.yml` | ✅ Planned | Новые таски, обновлены зависимости | -| `push.sh` | ✅ Planned | DELETED | -| `.spec/features/local-test-env/*` | ⚠️ Unexpected | SDD pipeline artifacts — ожидаемые, не scope creep | - ---- - -## Requirements Traceability - -| Requirement | Test(s) | Code | CP | Verdict | -|-------------|---------|------|----|---------| -| REQ-1.1 | verify_dockerfile_output (manual) | `registry/*/Dockerfile` → `FROM scratch` + `COPY /plugin` | CP-1 | ✅ | -| REQ-1.2 | verify_upx_compression (manual) | `registry/*/Dockerfile` → `upx --best --lzma` в build-стейдже | CP-2 | ✅ | -| REQ-1.3 | verify_dockerfile_output (manual) | Нет `ENTRYPOINT`/`USER`/`passwd` | CP-1 | ✅ | -| REQ-2.1 | verify_build_completeness (manual) | `build-plugins.sh:18-39` — `find` + `docker build --output` | CP-4 | ✅ | -| REQ-2.2 | verify_build_failfast (manual) | `build-plugins.sh:7` — `set -euo pipefail` | CP-3 | ✅ | -| REQ-2.3 | verify_build_completeness (manual) | `build-plugins.sh:35` — `chmod +x` | CP-4 | ✅ | -| REQ-3.1 | verify_register_completeness (manual) | `register-plugins.sh:30-57` — `grpcurl` → `CreatePlugin` | CP-6 | ✅ | -| REQ-3.2 | verify_register_idempotent (manual) | `register-plugins.sh:49-51` — `ALREADY_EXISTS` → continue | CP-5 | ✅ | -| REQ-3.3 | verify_register_failfast (manual) | `register-plugins.sh:53-55` — else → `exit 1` | CP-7 | ✅ | -| REQ-4.1 | — | `config.yml:13` — `plugins_dir: "/plugins"` | CP-8 | ✅ | -| REQ-4.2 | — | `config.yml:14` — `max_output_size: 67108864` | CP-8 | ✅ | -| REQ-4.3 | — | `config.local.yml:13` — `plugins_dir: "./plugins"` | CP-8 | ✅ | -| REQ-5.1 | — | `docker-compose.yml:200` — `./plugins:/plugins:ro` | CP-9, CP-10 | ✅ | -| REQ-5.2 | — | `docker-compose.yml` — нет `registry`, `registry-data` | CP-9 | ✅ | -| REQ-6.1 | — | `Taskfile.yml:27-33` — `build-plugins` → `./build-plugins.sh` | CP-12 | ✅ | -| REQ-6.2 | — | `Taskfile.yml:35-41` — `register-plugins` → `./register-plugins.sh` | CP-12 | ✅ | -| REQ-6.3 | — | `Taskfile.yml:46` — `deps: build-plugins` | CP-13 | ✅ | -| REQ-6.4 | — | Нет `local-push-*` в Taskfile | CP-11 | ✅ | -| REQ-7.1 | — | `push.sh` удалён | CP-14 | ✅ | - ---- - -## Design Conformance - -### 3.1 Architectural Boundaries -Все новые файлы (`build-plugins.sh`, `register-plugins.sh`) расположены в корне проекта — корректно для скриптов оркестрации. Dockerfile-ы остались на своих местах в `registry/`. Конфиги и compose-файл обновлены на месте. ✅ - -### 3.2 Data Models -Новых типов данных нет. Конфигурация `registryConfig` в `cmd/main.go` уже использует `PluginsDir` и `MaxOutputSize` — конфиги теперь соответствуют коду. ✅ - -### 3.3 API Contracts -API контракт (proto) не изменён. `CreatePlugin` RPC используется скриптом регистрации — соответствует дизайну. ✅ - -### 3.4 Error Handling -- `build-plugins.sh`: `set -euo pipefail` + exit 1 для неожиданных путей. ✅ -- `register-plugins.sh`: `ALREADY_EXISTS` → skip, другие ошибки → exit 1. ✅ -- Проверка `grpcurl` installed. ✅ -- Проверка `plugins/` directory exists. ✅ - -### 3.5 Correctness Properties -Все 14 CP из дизайна выполнены (см. Requirements Traceability). ✅ - -### 3.6 Documentation Consistency -Mermaid-диаграмма в design.md соответствует фактической структуре: Dockerfiles → build-plugins.sh → plugins/ → service + register-plugins.sh → gRPC → DB. ✅ - ---- - -## Code Quality - -### 4.1 Naming & Clarity -- Скрипты названы описательно: `build-plugins.sh`, `register-plugins.sh`. ✅ -- Переменные в скриптах (`group`, `name`, `version`, `output_dir`) понятные. ✅ -- Taski в Taskfile имеют `desc:` поля. ✅ - -### 4.2 Dead Code & Debug Artifacts -Нет `TODO`-ов, нет debug-вывода. ✅ - -### 4.3 Scope Creep -Все изменения соответствуют плану. `.spec/features/` — артефакты SDD pipeline, не scope creep. ✅ - -### 4.4 Test Quality -Фича затрагивает скрипты/конфиги — unit-тесты Go не применимы. Верификация через manual/integration тесты (запуск скриптов). Существующие Go-тесты не сломаны (все `[no test files]`). ✅ - ---- - -## Security - -Нет новых публичных API endpoints. Изменения затрагивают только: -- Bash-скрипты (не принимают внешний ввод кроме CLI-аргумента `host:port`) -- Конфиги (YAML) -- Dockerfile-ы (build-only, не runtime) -- Docker Compose (volume mount `:ro`) - -Security-проблем не обнаружено. ✅ - ---- - -## Verification Evidence - -- **Build:** -``` -go build -o main ./cmd/main.go -EXIT: 0 -``` - -- **Tests:** -``` -? github.com/easyp-tech/service/api/generator/v1 [no test files] -? github.com/easyp-tech/service/cmd [no test files] -? github.com/easyp-tech/service/cmd/mcp-smoke [no test files] -? github.com/easyp-tech/service/internal/adapters/audit [no test files] -? github.com/easyp-tech/service/internal/adapters/metrics [no test files] -? github.com/easyp-tech/service/internal/adapters/registry [no test files] -? github.com/easyp-tech/service/internal/api [no test files] -? github.com/easyp-tech/service/internal/core [no test files] -? github.com/easyp-tech/service/internal/database [no test files] -? github.com/easyp-tech/service/internal/database/connectors [no test files] -? github.com/easyp-tech/service/internal/database/internal [no test files] -? github.com/easyp-tech/service/internal/database/migrations [no test files] -? github.com/easyp-tech/service/internal/flags [no test files] -? github.com/easyp-tech/service/internal/grpchelper [no test files] -? github.com/easyp-tech/service/internal/license [no test files] -? github.com/easyp-tech/service/internal/monitor [no test files] -? github.com/easyp-tech/service/internal/ratelimiter [no test files] -? github.com/easyp-tech/service/internal/telemetry [no test files] -? github.com/easyp-tech/service/sdk [no test files] -EXIT: 0 -``` - ---- - -## Findings - -| ID | Severity | File | Description | Requirement | -|----|----------|------|-------------|-------------| -| F-1 | nit | `docker-compose.yml:161` | Пустая строка осталась после удаления `registry:` сервиса (двойной перенос). Визуально не критично. | — | - ---- - -## Recommendations - -1. **(nit) F-1:** Убрать лишнюю пустую строку в `docker-compose.yml:161` после удаления сервиса `registry`. diff --git a/.spec/features/local-test-env/revisions/task-plan-rev-1-2026-05-24T15-30-48Z.md b/.spec/features/local-test-env/revisions/task-plan-rev-1-2026-05-24T15-30-48Z.md deleted file mode 100644 index 347991b..0000000 --- a/.spec/features/local-test-env/revisions/task-plan-rev-1-2026-05-24T15-30-48Z.md +++ /dev/null @@ -1,233 +0,0 @@ -# local-test-env — Task Plan - -**Work Type:** Migration -**Date:** 2026-05-24 - ---- - -**Test Style Source:** Tier 2 -- Evidence: `internal/adapters/registry/registry_test.go` (шаблон удалён, но паттерны известны), `internal/core/pool_test.go` -- Key patterns: стандартный `go test`, table-driven tests, `testing.T`. PBT unavailable — targeted unit tests as substitute. - -**Commands:** - -| Action | Command | Source | -|--------|---------|--------| -| Test | `go test ./...` | Taskfile.yml | -| Build | `go build -o main ./cmd/main.go` | Taskfile.yml | -| Lint | `golangci-lint run ./...` | Taskfile.yml | -| Generate | `easyp --cfg easyp.yaml generate` | Taskfile.yml | - ---- - -## Матрица покрытия - -| Requirement | Task(s) | Correctness Property | -|-------------|---------|----------------------| -| REQ-1.1 | T-2 | CP-1 (Equivalence) | -| REQ-1.2 | T-2 | CP-2 (Propagation) | -| REQ-1.3 | T-2 | CP-1 (Equivalence) | -| REQ-2.1 | T-3 | CP-4 (Equivalence) | -| REQ-2.2 | T-3 | CP-3 (Absence) | -| REQ-2.3 | T-3 | CP-4 (Equivalence) | -| REQ-3.1 | T-4 | CP-6 (Propagation) | -| REQ-3.2 | T-4 | CP-5 (Absence) | -| REQ-3.3 | T-4 | CP-7 (Absence) | -| REQ-4.1 | T-5 | CP-8 (Propagation) | -| REQ-4.2 | T-5 | CP-8 (Propagation) | -| REQ-4.3 | T-5 | CP-8 (Propagation) | -| REQ-5.1 | T-5 | CP-9, CP-10 (Absence, Propagation) | -| REQ-5.2 | T-5 | CP-9 (Absence) | -| REQ-6.1 | T-5 | CP-12 (Equivalence) | -| REQ-6.2 | T-5 | CP-12 (Equivalence) | -| REQ-6.3 | T-5 | CP-13 (Propagation) | -| REQ-6.4 | T-5 | CP-11 (Absence) | -| REQ-7.1 | T-5 | CP-14 (Absence) | - ---- - -## T-1: GREEN — Сохранение текущего поведения (Preservation Tests) - -*_Requirements: REQ-4.1, REQ-4.2_* -*_Complexity: mechanical_* - -GOAL: Убедиться, что существующие Go-тесты проходят ДО внесения изменений. - -1. **Запустить `go build -o main ./cmd/main.go`** — убедиться, что проект компилируется. -2. **Запустить `go test ./...`** — зафиксировать baseline. Все тесты должны проходить. - ---- - -## T-2: CODE — Переделать Dockerfile-ы в `registry/` - -*_Requirements: REQ-1.1, REQ-1.2, REQ-1.3_* -*_Preservation: CP-2_* -*_Complexity: mechanical_* - -GOAL: Упростить все 4 Dockerfile-а: убрать `ENTRYPOINT`, `USER`, `/etc/passwd`. Финальный стейдж — `FROM scratch` + `COPY ... /plugin`. - -1. **Изменить `registry/protocolbuffers/go/v1.36.10/Dockerfile`:** - - Убрать строки: `COPY --from=build --link /etc/passwd /etc/passwd`, `USER nobody`, `ENTRYPOINT [ "/protoc-gen-go" ]`. - - Заменить `COPY --from=build --link --chown=root:root /go/bin/protoc-gen-go /protoc-gen-go` на `COPY --from=build /go/bin/protoc-gen-go /plugin`. - -2. **Изменить `registry/grpc/go/v1.5.1/Dockerfile`:** - - Убрать строки: `COPY --from=build --link /etc/passwd /etc/passwd`, `USER nobody`, `ENTRYPOINT [ "/protoc-gen-go-grpc" ]`. - - Заменить `COPY --from=build --link --chown=root:root /go/bin/protoc-gen-go-grpc /protoc-gen-go-grpc` на `COPY --from=build /go/bin/protoc-gen-go-grpc /plugin`. - -3. **Изменить `registry/grpc-ecosystem/gateway/v2.27.3/Dockerfile`:** - - Убрать строки: `COPY --from=build --link --chown=root:root /etc/passwd /etc/passwd`, `USER nobody`, `ENTRYPOINT [ "/protoc-gen-grpc-gateway" ]`. - - Заменить `COPY --from=build --link --chown=root:root /go/bin/protoc-gen-grpc-gateway /protoc-gen-grpc-gateway` на `COPY --from=build /go/bin/protoc-gen-grpc-gateway /plugin`. - -4. **Изменить `registry/grpc-ecosystem/openapiv2/v2.27.3/Dockerfile`:** - - Убрать строки: `COPY --from=build --link --chown=root:root /etc/passwd /etc/passwd`, `USER nobody`, `ENTRYPOINT [ "/protoc-gen-openapiv2" ]`. - - Заменить `COPY --from=build --link /go/bin/protoc-gen-openapiv2 /protoc-gen-openapiv2` на `COPY --from=build /go/bin/protoc-gen-openapiv2 /plugin`. - ---- - -## T-3: CODE — Создать скрипт `build-plugins.sh` - -*_Requirements: REQ-2.1, REQ-2.2, REQ-2.3_* -*_Preservation: CP-1, CP-2_* -*_Complexity: standard_* - -GOAL: Создать bash-скрипт, который обходит все Dockerfile-ы в `registry/` и собирает бинарники через `docker build --output`. - -1. **Создать файл `build-plugins.sh` в корне проекта:** - - Шебанг: `#!/bin/bash` - - `set -euo pipefail` - - `export DOCKER_BUILDKIT=1` - - Обход: `find registry -name Dockerfile | sort` - - Для каждого Dockerfile: извлечь `{group}/{name}/{version}` из пути `registry/{group}/{name}/{version}/Dockerfile` - - Вызов: `docker build --output="./plugins/${group}/${name}/${version}/" "registry/${group}/${name}/${version}/"` - - Проверка: `chmod +x "./plugins/${group}/${name}/${version}/plugin"` - - Вывод прогресса: `echo "✓ Built {group}/{name}:{version}"` - -2. **Сделать `build-plugins.sh` исполняемым:** `chmod +x build-plugins.sh` - ---- - -## T-4: CODE — Создать скрипт `register-plugins.sh` - -*_Requirements: REQ-3.1, REQ-3.2, REQ-3.3_* -*_Preservation: CP-1, CP-2_* -*_Complexity: standard_* - -GOAL: Создать bash-скрипт, который обходит собранные плагины в `plugins/` и регистрирует их через gRPC API `CreatePlugin`. - -1. **Создать файл `register-plugins.sh` в корне проекта:** - - Шебанг: `#!/bin/bash` - - `set -euo pipefail` - - Переменная: `GRPC_HOST="${1:-localhost:8080}"` (host по умолчанию) - - Обход: `find plugins -name plugin -type f | sort` - - Для каждого найденного `plugins/{group}/{name}/{version}/plugin`: извлечь `group`, `name`, `version` из пути. - - Вызов `grpcurl`: - ``` - grpcurl -plaintext -d "{\"group\":\"${group}\",\"name\":\"${name}\",\"version\":\"${version}\",\"config\":{\"command\":[\"/plugins/${group}/${name}/${version}/plugin\"]}}" "${GRPC_HOST}" api.generator.v1.ServiceAPI/CreatePlugin - ``` - - Обработка ошибок: если `grpcurl` exit-код != 0, проверить stderr на `ALREADY_EXISTS`. Если да — вывести `"⚠ Already exists: {group}/{name}:{version}"` и `continue`. Иначе — `exit 1`. - - Вывод прогресса: `echo "✓ Registered {group}/{name}:{version}"` - -2. **Сделать `register-plugins.sh` исполняемым:** `chmod +x register-plugins.sh` - ---- - -## T-5: CODE — Обновить инфраструктуру (конфиги, compose, Taskfile, очистка) - -*_Requirements: REQ-4.1, REQ-4.2, REQ-4.3, REQ-5.1, REQ-5.2, REQ-6.1, REQ-6.2, REQ-6.3, REQ-6.4, REQ-7.1_* -*_Preservation: CP-1, CP-2, CP-3, CP-4_* -*_Complexity: standard_* - -GOAL: Привести конфиги, docker-compose, Taskfile в соответствие с новой архитектурой и удалить устаревшие файлы. - -1. **Изменить `config.yml` (строка 12–13):** - - Заменить: - ```yaml - registry: - domain: "localhost:5005" - ``` - - На: - ```yaml - registry: - plugins_dir: "/plugins" - max_output_size: 67108864 - ``` - -2. **Изменить `config.local.yml` (строка 12–13):** - - Заменить: - ```yaml - registry: - domain: "localhost:5005" - ``` - - На: - ```yaml - registry: - plugins_dir: "./plugins" - max_output_size: 67108864 - ``` - -3. **Изменить `docker-compose.yml`:** - - Удалить volume `registry-data:` из секции `volumes:` (строка 7). - - Удалить сервис `registry:` целиком (строки 163–172). - - В сервисе `service` → `volumes:` — удалить строку `- "/var/run/docker.sock:/var/run/docker.sock"` (строка 211). Добавить `- "./plugins:/plugins:ro"`. - -4. **Изменить `Taskfile.yml`:** - - Удалить таску `local-push-registry:` (строки 27–32). - - Удалить таску `local-push-required:` (строки 34–43). - - Добавить таску `build-plugins:`: - ```yaml - build-plugins: - dir: "{{.USER_WORKING_DIR}}" - preconditions: - - "test -f build-plugins.sh" - cmds: - - "./build-plugins.sh" - ``` - - Добавить таску `register-plugins:`: - ```yaml - register-plugins: - dir: "{{.USER_WORKING_DIR}}" - preconditions: - - "test -f register-plugins.sh" - cmds: - - "./register-plugins.sh" - ``` - - В таске `run:` → `deps:` — заменить `"local-push-registry"` на `"build-plugins"`. - - В таске `up-minimal:` → `cmds:` — убрать `registry` из аргументов `docker compose up -d postgres registry` → `docker compose up -d postgres`. - -5. **Удалить файл `push.sh`:** `rm push.sh` - ---- - -## T-6: VERIFY — Проверка результата - -*_Requirements: REQ-1.1, REQ-2.1, REQ-4.1, REQ-5.1, REQ-6.1, REQ-7.1_* -*_Complexity: standard_* - -GOAL: Убедиться, что всё работает после всех изменений. - -1. **Запустить `go build -o main ./cmd/main.go`** — проект должен компилироваться. -2. **Запустить `go test ./...`** — все существующие тесты должны проходить. -3. **Проверить `push.sh` удалён:** `test ! -f push.sh` -4. **Проверить что `docker-compose.yml` не содержит `registry`:** `grep -c 'registry' docker-compose.yml` — должно быть 0. -5. **Проверить что `Taskfile.yml` не содержит `local-push`:** `grep -c 'local-push' Taskfile.yml` — должно быть 0. -6. **Проверить что `config.yml` содержит `plugins_dir`:** `grep 'plugins_dir' config.yml` - ---- - -## T-7: GATE — Контрольная точка - -*_Requirements: ALL_* -*_Complexity: mechanical_* - -GOAL: Финальная проверка всех артефактов и зависимостей. - -1. **Запустить `go build -o main ./cmd/main.go`** — компиляция. -2. **Запустить `go test ./...`** — все тесты. -3. **Проверить структуру Dockerfile-ов:** - - `grep -c ENTRYPOINT registry/*/Dockerfile registry/*/*/Dockerfile registry/*/*/*/Dockerfile` — должно быть 0 совпадений. - - `grep -c '/plugin' registry/*/*/*/Dockerfile` — должно быть 4 (по одному на каждый Dockerfile). -4. **Проверить наличие скриптов:** - - `test -x build-plugins.sh` - - `test -x register-plugins.sh` -5. **Проверить отсутствие устаревших файлов:** - - `test ! -f push.sh` diff --git a/.spec/features/local-test-env/task-plan.md b/.spec/features/local-test-env/task-plan.md deleted file mode 100644 index 347991b..0000000 --- a/.spec/features/local-test-env/task-plan.md +++ /dev/null @@ -1,233 +0,0 @@ -# local-test-env — Task Plan - -**Work Type:** Migration -**Date:** 2026-05-24 - ---- - -**Test Style Source:** Tier 2 -- Evidence: `internal/adapters/registry/registry_test.go` (шаблон удалён, но паттерны известны), `internal/core/pool_test.go` -- Key patterns: стандартный `go test`, table-driven tests, `testing.T`. PBT unavailable — targeted unit tests as substitute. - -**Commands:** - -| Action | Command | Source | -|--------|---------|--------| -| Test | `go test ./...` | Taskfile.yml | -| Build | `go build -o main ./cmd/main.go` | Taskfile.yml | -| Lint | `golangci-lint run ./...` | Taskfile.yml | -| Generate | `easyp --cfg easyp.yaml generate` | Taskfile.yml | - ---- - -## Матрица покрытия - -| Requirement | Task(s) | Correctness Property | -|-------------|---------|----------------------| -| REQ-1.1 | T-2 | CP-1 (Equivalence) | -| REQ-1.2 | T-2 | CP-2 (Propagation) | -| REQ-1.3 | T-2 | CP-1 (Equivalence) | -| REQ-2.1 | T-3 | CP-4 (Equivalence) | -| REQ-2.2 | T-3 | CP-3 (Absence) | -| REQ-2.3 | T-3 | CP-4 (Equivalence) | -| REQ-3.1 | T-4 | CP-6 (Propagation) | -| REQ-3.2 | T-4 | CP-5 (Absence) | -| REQ-3.3 | T-4 | CP-7 (Absence) | -| REQ-4.1 | T-5 | CP-8 (Propagation) | -| REQ-4.2 | T-5 | CP-8 (Propagation) | -| REQ-4.3 | T-5 | CP-8 (Propagation) | -| REQ-5.1 | T-5 | CP-9, CP-10 (Absence, Propagation) | -| REQ-5.2 | T-5 | CP-9 (Absence) | -| REQ-6.1 | T-5 | CP-12 (Equivalence) | -| REQ-6.2 | T-5 | CP-12 (Equivalence) | -| REQ-6.3 | T-5 | CP-13 (Propagation) | -| REQ-6.4 | T-5 | CP-11 (Absence) | -| REQ-7.1 | T-5 | CP-14 (Absence) | - ---- - -## T-1: GREEN — Сохранение текущего поведения (Preservation Tests) - -*_Requirements: REQ-4.1, REQ-4.2_* -*_Complexity: mechanical_* - -GOAL: Убедиться, что существующие Go-тесты проходят ДО внесения изменений. - -1. **Запустить `go build -o main ./cmd/main.go`** — убедиться, что проект компилируется. -2. **Запустить `go test ./...`** — зафиксировать baseline. Все тесты должны проходить. - ---- - -## T-2: CODE — Переделать Dockerfile-ы в `registry/` - -*_Requirements: REQ-1.1, REQ-1.2, REQ-1.3_* -*_Preservation: CP-2_* -*_Complexity: mechanical_* - -GOAL: Упростить все 4 Dockerfile-а: убрать `ENTRYPOINT`, `USER`, `/etc/passwd`. Финальный стейдж — `FROM scratch` + `COPY ... /plugin`. - -1. **Изменить `registry/protocolbuffers/go/v1.36.10/Dockerfile`:** - - Убрать строки: `COPY --from=build --link /etc/passwd /etc/passwd`, `USER nobody`, `ENTRYPOINT [ "/protoc-gen-go" ]`. - - Заменить `COPY --from=build --link --chown=root:root /go/bin/protoc-gen-go /protoc-gen-go` на `COPY --from=build /go/bin/protoc-gen-go /plugin`. - -2. **Изменить `registry/grpc/go/v1.5.1/Dockerfile`:** - - Убрать строки: `COPY --from=build --link /etc/passwd /etc/passwd`, `USER nobody`, `ENTRYPOINT [ "/protoc-gen-go-grpc" ]`. - - Заменить `COPY --from=build --link --chown=root:root /go/bin/protoc-gen-go-grpc /protoc-gen-go-grpc` на `COPY --from=build /go/bin/protoc-gen-go-grpc /plugin`. - -3. **Изменить `registry/grpc-ecosystem/gateway/v2.27.3/Dockerfile`:** - - Убрать строки: `COPY --from=build --link --chown=root:root /etc/passwd /etc/passwd`, `USER nobody`, `ENTRYPOINT [ "/protoc-gen-grpc-gateway" ]`. - - Заменить `COPY --from=build --link --chown=root:root /go/bin/protoc-gen-grpc-gateway /protoc-gen-grpc-gateway` на `COPY --from=build /go/bin/protoc-gen-grpc-gateway /plugin`. - -4. **Изменить `registry/grpc-ecosystem/openapiv2/v2.27.3/Dockerfile`:** - - Убрать строки: `COPY --from=build --link --chown=root:root /etc/passwd /etc/passwd`, `USER nobody`, `ENTRYPOINT [ "/protoc-gen-openapiv2" ]`. - - Заменить `COPY --from=build --link /go/bin/protoc-gen-openapiv2 /protoc-gen-openapiv2` на `COPY --from=build /go/bin/protoc-gen-openapiv2 /plugin`. - ---- - -## T-3: CODE — Создать скрипт `build-plugins.sh` - -*_Requirements: REQ-2.1, REQ-2.2, REQ-2.3_* -*_Preservation: CP-1, CP-2_* -*_Complexity: standard_* - -GOAL: Создать bash-скрипт, который обходит все Dockerfile-ы в `registry/` и собирает бинарники через `docker build --output`. - -1. **Создать файл `build-plugins.sh` в корне проекта:** - - Шебанг: `#!/bin/bash` - - `set -euo pipefail` - - `export DOCKER_BUILDKIT=1` - - Обход: `find registry -name Dockerfile | sort` - - Для каждого Dockerfile: извлечь `{group}/{name}/{version}` из пути `registry/{group}/{name}/{version}/Dockerfile` - - Вызов: `docker build --output="./plugins/${group}/${name}/${version}/" "registry/${group}/${name}/${version}/"` - - Проверка: `chmod +x "./plugins/${group}/${name}/${version}/plugin"` - - Вывод прогресса: `echo "✓ Built {group}/{name}:{version}"` - -2. **Сделать `build-plugins.sh` исполняемым:** `chmod +x build-plugins.sh` - ---- - -## T-4: CODE — Создать скрипт `register-plugins.sh` - -*_Requirements: REQ-3.1, REQ-3.2, REQ-3.3_* -*_Preservation: CP-1, CP-2_* -*_Complexity: standard_* - -GOAL: Создать bash-скрипт, который обходит собранные плагины в `plugins/` и регистрирует их через gRPC API `CreatePlugin`. - -1. **Создать файл `register-plugins.sh` в корне проекта:** - - Шебанг: `#!/bin/bash` - - `set -euo pipefail` - - Переменная: `GRPC_HOST="${1:-localhost:8080}"` (host по умолчанию) - - Обход: `find plugins -name plugin -type f | sort` - - Для каждого найденного `plugins/{group}/{name}/{version}/plugin`: извлечь `group`, `name`, `version` из пути. - - Вызов `grpcurl`: - ``` - grpcurl -plaintext -d "{\"group\":\"${group}\",\"name\":\"${name}\",\"version\":\"${version}\",\"config\":{\"command\":[\"/plugins/${group}/${name}/${version}/plugin\"]}}" "${GRPC_HOST}" api.generator.v1.ServiceAPI/CreatePlugin - ``` - - Обработка ошибок: если `grpcurl` exit-код != 0, проверить stderr на `ALREADY_EXISTS`. Если да — вывести `"⚠ Already exists: {group}/{name}:{version}"` и `continue`. Иначе — `exit 1`. - - Вывод прогресса: `echo "✓ Registered {group}/{name}:{version}"` - -2. **Сделать `register-plugins.sh` исполняемым:** `chmod +x register-plugins.sh` - ---- - -## T-5: CODE — Обновить инфраструктуру (конфиги, compose, Taskfile, очистка) - -*_Requirements: REQ-4.1, REQ-4.2, REQ-4.3, REQ-5.1, REQ-5.2, REQ-6.1, REQ-6.2, REQ-6.3, REQ-6.4, REQ-7.1_* -*_Preservation: CP-1, CP-2, CP-3, CP-4_* -*_Complexity: standard_* - -GOAL: Привести конфиги, docker-compose, Taskfile в соответствие с новой архитектурой и удалить устаревшие файлы. - -1. **Изменить `config.yml` (строка 12–13):** - - Заменить: - ```yaml - registry: - domain: "localhost:5005" - ``` - - На: - ```yaml - registry: - plugins_dir: "/plugins" - max_output_size: 67108864 - ``` - -2. **Изменить `config.local.yml` (строка 12–13):** - - Заменить: - ```yaml - registry: - domain: "localhost:5005" - ``` - - На: - ```yaml - registry: - plugins_dir: "./plugins" - max_output_size: 67108864 - ``` - -3. **Изменить `docker-compose.yml`:** - - Удалить volume `registry-data:` из секции `volumes:` (строка 7). - - Удалить сервис `registry:` целиком (строки 163–172). - - В сервисе `service` → `volumes:` — удалить строку `- "/var/run/docker.sock:/var/run/docker.sock"` (строка 211). Добавить `- "./plugins:/plugins:ro"`. - -4. **Изменить `Taskfile.yml`:** - - Удалить таску `local-push-registry:` (строки 27–32). - - Удалить таску `local-push-required:` (строки 34–43). - - Добавить таску `build-plugins:`: - ```yaml - build-plugins: - dir: "{{.USER_WORKING_DIR}}" - preconditions: - - "test -f build-plugins.sh" - cmds: - - "./build-plugins.sh" - ``` - - Добавить таску `register-plugins:`: - ```yaml - register-plugins: - dir: "{{.USER_WORKING_DIR}}" - preconditions: - - "test -f register-plugins.sh" - cmds: - - "./register-plugins.sh" - ``` - - В таске `run:` → `deps:` — заменить `"local-push-registry"` на `"build-plugins"`. - - В таске `up-minimal:` → `cmds:` — убрать `registry` из аргументов `docker compose up -d postgres registry` → `docker compose up -d postgres`. - -5. **Удалить файл `push.sh`:** `rm push.sh` - ---- - -## T-6: VERIFY — Проверка результата - -*_Requirements: REQ-1.1, REQ-2.1, REQ-4.1, REQ-5.1, REQ-6.1, REQ-7.1_* -*_Complexity: standard_* - -GOAL: Убедиться, что всё работает после всех изменений. - -1. **Запустить `go build -o main ./cmd/main.go`** — проект должен компилироваться. -2. **Запустить `go test ./...`** — все существующие тесты должны проходить. -3. **Проверить `push.sh` удалён:** `test ! -f push.sh` -4. **Проверить что `docker-compose.yml` не содержит `registry`:** `grep -c 'registry' docker-compose.yml` — должно быть 0. -5. **Проверить что `Taskfile.yml` не содержит `local-push`:** `grep -c 'local-push' Taskfile.yml` — должно быть 0. -6. **Проверить что `config.yml` содержит `plugins_dir`:** `grep 'plugins_dir' config.yml` - ---- - -## T-7: GATE — Контрольная точка - -*_Requirements: ALL_* -*_Complexity: mechanical_* - -GOAL: Финальная проверка всех артефактов и зависимостей. - -1. **Запустить `go build -o main ./cmd/main.go`** — компиляция. -2. **Запустить `go test ./...`** — все тесты. -3. **Проверить структуру Dockerfile-ов:** - - `grep -c ENTRYPOINT registry/*/Dockerfile registry/*/*/Dockerfile registry/*/*/*/Dockerfile` — должно быть 0 совпадений. - - `grep -c '/plugin' registry/*/*/*/Dockerfile` — должно быть 4 (по одному на каждый Dockerfile). -4. **Проверить наличие скриптов:** - - `test -x build-plugins.sh` - - `test -x register-plugins.sh` -5. **Проверить отсутствие устаревших файлов:** - - `test ! -f push.sh` diff --git a/.spec/legacy_builder/main.go b/.spec/legacy_builder/main.go new file mode 100644 index 0000000..9244699 --- /dev/null +++ b/.spec/legacy_builder/main.go @@ -0,0 +1,302 @@ +package main + +import ( + "bytes" + "fmt" + "log" + "os" + "os/exec" + "path/filepath" + "sort" + "sync" + "time" + + "golang.org/x/sync/errgroup" + "gopkg.in/yaml.v3" +) + +type PluginConfig struct { + Binary string `yaml:"binary"` + Description string `yaml:"description,omitempty"` + SourceURL string `yaml:"source_url,omitempty"` + BuildArgs map[string]string `yaml:"build_args,omitempty"` + Versions []interface{} `yaml:"versions"` +} + +type BuildJob struct { + Group, Name, Version, Binary string + BuildArgs map[string]string + PluginDir, OutputDir string +} + +func (j BuildJob) Key() string { return fmt.Sprintf("%s/%s:%s", j.Group, j.Name, j.Version) } + +// needsBuild returns true if the plugin version has not been built yet +func needsBuild(job BuildJob) bool { + for _, path := range []string{ + filepath.Join(job.OutputDir, "plugin"), + filepath.Join(job.OutputDir, job.Binary), + } { + if _, err := os.Stat(path); err == nil { + return false + } + } + if stat, err := os.Stat(filepath.Join(job.OutputDir, "app")); err == nil && stat.IsDir() { + return false + } + return true +} + +// Tracker keeps state of active builds for display +type Tracker struct { + mu sync.Mutex + active map[string]time.Time + completed int + succeeded int + failed int + total int + startTime time.Time + lastReport time.Time +} + +func NewTracker(total int) *Tracker { + now := time.Now() + return &Tracker{ + active: make(map[string]time.Time), + total: total, + startTime: now, + lastReport: now, + } +} + +func (t *Tracker) Start(key string) { + t.mu.Lock() + defer t.mu.Unlock() + t.active[key] = time.Now() +} + +func (t *Tracker) Finish(key string, success bool, details string, dur time.Duration) { + t.mu.Lock() + defer t.mu.Unlock() + + delete(t.active, key) + t.completed++ + if success { + t.succeeded++ + } else { + t.failed++ + } + t.lastReport = time.Now() + + icon := "✅" + if !success { + icon = "❌" + } + + fmt.Printf("[%4d/%4d] %s %s (%s, %s) | active: %d\n", + t.completed, t.total, icon, key, details, dur, len(t.active)) +} + +// PrintStalled prints active builds if nothing completed recently +func (t *Tracker) PrintStalled() { + t.mu.Lock() + defer t.mu.Unlock() + + if len(t.active) == 0 { + return + } + if time.Since(t.lastReport) < 30*time.Second { + return + } + + elapsed := time.Since(t.startTime).Round(time.Second) + + type entry struct { + name string + dur time.Duration + } + var entries []entry + for name, started := range t.active { + entries = append(entries, entry{name, time.Since(started).Round(time.Second)}) + } + sort.Slice(entries, func(i, j int) bool { return entries[i].dur > entries[j].dur }) + + fmt.Printf(" ⏳ [%s] %d/%d done | active: %d\n", elapsed, t.completed, t.total, len(entries)) + for _, e := range entries { + fmt.Printf(" %-50s (%s)\n", e.name, e.dur) + } + t.lastReport = time.Now() +} + +func main() { + registryDir := "registry" + outputDir := "plugins" + + var configs []string + filepath.Walk(registryDir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !info.IsDir() && info.Name() == "plugin.yaml" { + configs = append(configs, path) + } + return nil + }) + + sort.Strings(configs) + + var allJobs []BuildJob + for _, configPath := range configs { + data, _ := os.ReadFile(configPath) + var cfg PluginConfig + if err := yaml.Unmarshal(data, &cfg); err != nil { + log.Fatalf("Failed to parse %s: %v", configPath, err) + } + + pluginDir := filepath.Dir(configPath) + name := filepath.Base(pluginDir) + group := filepath.Base(filepath.Dir(pluginDir)) + + for _, v := range cfg.Versions { + var ver string + switch val := v.(type) { + case string: + ver = val + case map[string]interface{}: + ver = val["version"].(string) + } + if group == "apple" && name == "swift" && ver == "v1.25.2" { + allJobs = append(allJobs, BuildJob{ + Group: group, Name: name, Version: ver, + Binary: cfg.Binary, BuildArgs: cfg.BuildArgs, + PluginDir: pluginDir, + OutputDir: filepath.Join(outputDir, group, name, ver), + }) + } + } + } + + // Pre-scan: separate cached from uncached + var toBuild []BuildJob + cached := 0 + for _, job := range allJobs { + if needsBuild(job) { + toBuild = append(toBuild, job) + } else { + cached++ + } + } + + fmt.Printf("Found %d total: %d cached ⏭, %d to build (parallel=%d)\n\n", len(allJobs), cached, len(toBuild), 10) + + if len(toBuild) == 0 { + fmt.Println("Nothing to build, all cached!") + return + } + + tracker := NewTracker(len(toBuild)) + + // Safety ticker: prints active builds if nothing completed for 30s + ticker := time.NewTicker(10 * time.Second) + done := make(chan struct{}) + go func() { + for { + select { + case <-ticker.C: + tracker.PrintStalled() + case <-done: + return + } + } + }() + + var g errgroup.Group + g.SetLimit(3) + + for _, j := range toBuild { + job := j + g.Go(func() error { + key := job.Key() + start := time.Now() + + os.MkdirAll(job.OutputDir, 0755) + tracker.Start(key) + + var outBuf bytes.Buffer + args := []string{ + "build", "--progress=plain", + "--build-arg", fmt.Sprintf("VERSION=%s", job.Version), + "--build-arg", fmt.Sprintf("BINARY_NAME=%s", job.Binary), + } + for k, v := range job.BuildArgs { + args = append(args, "--build-arg", fmt.Sprintf("%s=%s", k, v)) + } + args = append(args, "--output", job.OutputDir+"/", "-f", + filepath.Join(job.PluginDir, "Dockerfile"), job.PluginDir) + + cmd := exec.Command("docker", args...) + cmd.Stdout = &outBuf + cmd.Stderr = &outBuf + + err := cmd.Run() + + logPath := filepath.Join(job.OutputDir, "build.log") + os.WriteFile(logPath, outBuf.Bytes(), 0644) + + dur := time.Since(start).Round(time.Second) + + pluginFile := filepath.Join(job.OutputDir, "plugin") + binaryFile := filepath.Join(job.OutputDir, job.Binary) + appDir := filepath.Join(job.OutputDir, "app") + + if err != nil { + os.WriteFile(logPath, []byte(fmt.Sprintf("\n%s\nExec Error: %v\n", outBuf.Bytes(), err)), 0644) + tracker.Finish(key, false, fmt.Sprintf("see %s", logPath), dur) + return nil + } + + sizeStr := "" + if stat, err := os.Stat(pluginFile); err == nil { + os.Chmod(pluginFile, 0755) + sizeStr = formatSize(stat.Size()) + } else if stat, err := os.Stat(binaryFile); err == nil { + os.Rename(binaryFile, pluginFile) + os.Chmod(pluginFile, 0755) + sizeStr = formatSize(stat.Size()) + } else if stat, err := os.Stat(appDir); err == nil && stat.IsDir() { + sizeStr = "app dir" + } else { + tracker.Finish(key, false, "no output binary", dur) + return nil + } + + tracker.Finish(key, true, sizeStr, dur) + return nil + }) + } + + g.Wait() + ticker.Stop() + close(done) + + elapsed := time.Since(tracker.startTime).Round(time.Second) + fmt.Printf("\n══════════════════════════════════════════\n") + fmt.Printf(" DONE in %s\n", elapsed) + fmt.Printf(" Built: %d ✅\n", tracker.succeeded) + fmt.Printf(" Failed: %d ❌\n", tracker.failed) + fmt.Printf(" Cached: %d ⏭\n", cached) + fmt.Printf("══════════════════════════════════════════\n") +} + +func formatSize(b int64) string { + const unit = 1024 + if b < unit { + return fmt.Sprintf("%d B", b) + } + div, exp := int64(unit), 0 + for n := b / unit; n >= unit; n /= unit { + div *= unit + exp++ + } + return fmt.Sprintf("%.1f%cB", float64(b)/float64(div), "KMGTPE"[exp]) +} diff --git a/AGENTS.md b/AGENTS.md index 37780d9..65a1c88 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -170,7 +170,9 @@ This project includes agent skills in `.agents/skills/`. Each skill provides dom | **protobuf-expert-skill** | `.agents/skills/protobuf-expert-skill/SKILL.md` | Writing/reviewing `.proto` files, configuring `easyp.yaml`, lint rules, code generation plugins, proto dependencies, breaking changes, debugging easyp errors. | | **protoc-gen-mcp-skill** | `.agents/skills/protoc-gen-mcp-skill/SKILL.md` | Building MCP servers from protobuf definitions, generating MCP tools from proto files, adding MCP annotations, `protoc-gen-mcp` code generation. | | **go-code-style** | `.agents/skills/go-code-style/SKILL.md` | Writing or reviewing Go code. Error wrapping, defer patterns, assignment style, naming conventions, import ordering. | +| **go-testing** | `.agents/skills/go-testing/SKILL.md` | Writing or reviewing Go tests. Table-driven tests, t.Parallel, inline mocks, testify assertions, naming conventions. | | **goose-migration** | `.agents/skills/goose-migration/SKILL.md` | Adding database tables/columns, creating migration files, modifying schema, debugging goose migration errors. | +| **epctl-commands** | `.agents/skills/epctl-commands/SKILL.md` | Adding CLI commands to epctl, creating subcommands, working with `cmd/epctl/` files, urfave/cli v3 patterns, output formatting. | ### Spec-Driven Development diff --git a/Dockerfile b/Dockerfile index 13091ee..5380fea 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,7 @@ COPY . /app WORKDIR /app -RUN go build -o easyp ./cmd/main.go +RUN go build -o easyp ./cmd/easyp/ FROM debian:bookworm-slim diff --git a/Taskfile.yml b/Taskfile.yml index 0c3eec3..74977d7 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -26,18 +26,14 @@ tasks: build-plugins: desc: "Build all plugin binaries from registry/ Dockerfiles" dir: "{{.USER_WORKING_DIR}}" - preconditions: - - "test -f build-plugins.sh" cmds: - - "./build-plugins.sh" + - "go run ./cmd/epctl plugins build" register-plugins: desc: "Register all built plugins via gRPC CreatePlugin API" dir: "{{.USER_WORKING_DIR}}" - preconditions: - - "test -f register-plugins.sh" cmds: - - "./register-plugins.sh" + - "go run ./cmd/epctl plugins register" run: desc: "Full cycle: build plugins → restart services → register → follow logs" @@ -64,7 +60,7 @@ tasks: preconditions: - "test -f config.local.yml" cmds: - - "go run ./cmd/main.go -cfg config.local.yml -log_level debug" + - "go run ./cmd/easyp/ -cfg config.local.yml -log_level debug" test-mcp: dir: "{{.USER_WORKING_DIR}}" diff --git a/build-plugins.sh b/build-plugins.sh deleted file mode 100755 index 38c5f98..0000000 --- a/build-plugins.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/bash - -# build-plugins.sh — Build all plugin binaries from registry/ Dockerfiles. -# Uses docker build --output to extract binaries directly to ./plugins/. -# -# Usage: ./build-plugins.sh - -set -euo pipefail - -export DOCKER_BUILDKIT=1 - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -REGISTRY_DIR="${SCRIPT_DIR}/registry" -PLUGINS_DIR="${SCRIPT_DIR}/plugins" - -echo "Building plugins from ${REGISTRY_DIR}..." - -for dockerfile in $(find "${REGISTRY_DIR}" -name Dockerfile | sort); do - dir=$(dirname "${dockerfile}") - # Extract relative path: registry/{group}/{name}/{version} - rel_path="${dir#"${REGISTRY_DIR}/"}" - - # Parse group/name/version from path - if [[ "${rel_path}" =~ ^([^/]+)/([^/]+)/(.+)$ ]]; then - group="${BASH_REMATCH[1]}" - name="${BASH_REMATCH[2]}" - version="${BASH_REMATCH[3]}" - - output_dir="${PLUGINS_DIR}/${group}/${name}/${version}" - mkdir -p "${output_dir}" - - echo "Building ${group}/${name}:${version}..." - docker build --output="${output_dir}/" "${dir}" - - chmod +x "${output_dir}/plugin" - echo "✓ Built ${group}/${name}:${version} → ${output_dir}/plugin" - else - echo "✗ Skipping unexpected path: ${rel_path}" >&2 - exit 1 - fi -done - -echo "" -echo "Done! All plugins built in ${PLUGINS_DIR}/" diff --git a/cmd/main.go b/cmd/main.go deleted file mode 100644 index 4abd32f..0000000 --- a/cmd/main.go +++ /dev/null @@ -1,471 +0,0 @@ -package main - -import ( - "context" - "errors" - "flag" - "fmt" - "log/slog" - "net" - "net/http" - "os" - "os/signal" - "path/filepath" - "syscall" - "time" - - "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/ratelimit" - "github.com/hellofresh/health-go/v5" - _ "github.com/lib/pq" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/collectors" - "github.com/prometheus/client_golang/prometheus/promhttp" - "github.com/sethvargo/go-envconfig" - "golang.org/x/sync/errgroup" - "google.golang.org/grpc" - "gopkg.in/yaml.v3" - - adapter_audit "github.com/easyp-tech/service/internal/adapters/audit" - adapter_metrics "github.com/easyp-tech/service/internal/adapters/metrics" - "github.com/easyp-tech/service/internal/adapters/registry" - "github.com/easyp-tech/service/internal/api" - "github.com/easyp-tech/service/internal/core" - "github.com/easyp-tech/service/internal/database" - "github.com/easyp-tech/service/internal/database/connectors" - "github.com/easyp-tech/service/internal/database/goosemigrate" - "github.com/easyp-tech/service/internal/flags" - "github.com/easyp-tech/service/internal/grpchelper" - "github.com/easyp-tech/service/internal/license" - "github.com/easyp-tech/service/internal/monitor" - "github.com/easyp-tech/service/internal/ratelimiter" - "github.com/easyp-tech/service/internal/telemetry" -) - -const ( - exitCode = 2 - configFileSize = 1024 * 1024 - auditChannelCapacity = 1000 - componentShutdownTimeout = 5 * time.Second -) - -type ( - config struct { - Server server `env:", prefix=SERVER_" yaml:"server"` - DB dbConfig `env:", prefix=DB_" yaml:"db"` - Registry registryConfig `env:", prefix=REGISTRY_" yaml:"registry"` - Telemetry telemetryConfig `env:", prefix=TELEMETRY_" yaml:"telemetry"` - WorkerPool workerPoolConfig `env:", prefix=WORKER_POOL_" yaml:"worker_pool"` - License licenseConfig `env:", prefix=LICENSE_" yaml:"license"` - RateLimit rateLimitConfig `env:", prefix=RATE_LIMIT_" yaml:"rate_limit"` - } - server struct { - Host string `env:"HOST, default=0.0.0.0" yaml:"host"` - Port ports `env:", prefix=PORT_" yaml:"port"` - } - ports struct { - GRPC string `env:"GRPC, default=23410" yaml:"grpc"` - Metric string `env:"METRIC, default=23411" yaml:"metric"` - Health string `env:"HEALTH, default=23412" yaml:"health"` - MCP string `env:"MCP, default=23413" yaml:"mcp"` - } - dbConfig struct { - Driver string `env:"DRIVER, default=postgres" yaml:"driver"` - Postgres string `env:"POSTGRES_DSN" yaml:"postgres"` - } - registryConfig struct { - PluginsDir string `env:"PLUGINS_DIR, default=/plugins" yaml:"plugins_dir"` - MaxOutputSize int64 `env:"MAX_OUTPUT_SIZE, default=67108864" yaml:"max_output_size"` - } - telemetryConfig struct { - OTLPEndpoint string `env:"OTEL_EXPORTER_OTLP_ENDPOINT, default=localhost:4317" yaml:"otlp_endpoint"` - PyroscopeEndpoint string `env:"PYROSCOPE_ENDPOINT, default=http://localhost:4040" yaml:"pyroscope_endpoint"` - } - workerPoolConfig struct { - Workers int `env:"WORKERS,default=4" yaml:"workers"` - QueueSize int `env:"QUEUE_SIZE,default=16" yaml:"queue_size"` - GenerationTimeout time.Duration `env:"GENERATION_TIMEOUT,default=120s" yaml:"generation_timeout"` - MaxRetries int `env:"MAX_RETRIES,default=2" yaml:"max_retries"` - ShutdownTimeout time.Duration `env:"SHUTDOWN_TIMEOUT,default=30s" yaml:"shutdown_timeout"` - } - licenseConfig struct { - CacheTTL time.Duration `env:"LICENSE_CACHE_TTL" yaml:"cache_ttl"` - } - rateLimitConfig struct { - RequestsPerSecond float64 `env:"REQUESTS_PER_SECOND,default=10.0" yaml:"requests_per_second"` - Burst int `env:"BURST,default=20" yaml:"burst"` - CleanupInterval time.Duration `env:"CLEANUP_INTERVAL,default=10m" yaml:"cleanup_interval"` - } -) - -var ( - cfgFile = &flags.File{DefaultPath: "", MaxSize: configFileSize} - logLevel = &flags.Level{Level: slog.LevelDebug} -) - -// grpcMetrics implements grpchelper.Metrics for the recovery interceptor. -type grpcMetrics struct { - panics prometheus.Counter -} - -func (m *grpcMetrics) PanicsTotal() prometheus.Counter { //nolint:ireturn // interface requires prometheus.Counter - return m.panics -} - -func main() { - flag.Var(cfgFile, "cfg", "path to config file") - flag.Var(logLevel, "log_level", "log level") - flag.Parse() - - log := buildLogger(logLevel.Level) - - appName := filepath.Base(os.Args[0]) - ctxParent := monitor.WithContext(context.Background(), log.With( - slog.String("version", "dev"), // Replace with actual version logic if needed - slog.String("app", appName), - )) - - ctx, cancel := signal.NotifyContext(ctxParent, syscall.SIGHUP, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGABRT, syscall.SIGTERM) - defer cancel() - - go forceShutdown(ctx) - - err := start(ctx, cfgFile, appName) - if err != nil { - log.Error("shutdown", "error", err) - os.Exit(exitCode) //nolint:gocritic // forceShutdown is running and defer cancel() is intentionally bypassed - } -} - -func start(ctx context.Context, cfgFile *flags.File, appName string) error { - cfg := config{} - - if !cfgFile.IsNil() { - err := yaml.NewDecoder(cfgFile).Decode(&cfg) - if err != nil { - return fmt.Errorf("yaml.NewDecoder.Decode: %w", err) - } - } else { - err := envconfig.Process(ctx, &cfg) - if err != nil { - return fmt.Errorf("envconfig.Process: %w", err) - } - } - - reg := prometheus.NewRegistry() // Use standard registry - // Add Go metrics - reg.MustRegister(collectors.NewProcessCollector(collectors.ProcessCollectorOpts{})) - reg.MustRegister(collectors.NewGoCollector()) - - return run(ctx, cfg, reg, appName) -} - -func run(ctx context.Context, cfg config, reg *prometheus.Registry, namespace string) error { - log := monitor.FromContext(ctx) - - // Initialize telemetry (before anything else) - telCfg := telemetry.Config{ - OTLPEndpoint: cfg.Telemetry.OTLPEndpoint, - ServiceName: namespace, - PyroscopeEndpoint: cfg.Telemetry.PyroscopeEndpoint, - } - baseHandler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{AddSource: true, Level: slog.LevelDebug}) - shutdownTelemetry, telLog, err := telemetry.Init(ctx, telCfg, baseHandler) - if err != nil { - return fmt.Errorf("telemetry.Init: %w", err) - } - // Use telemetry-enriched logger - log = telLog - // Update context with new logger - ctx = monitor.WithContext(ctx, log) - - // Apply database migrations before creating the connection pool - err = goosemigrate.Up(ctx, cfg.DB.Postgres) - if err != nil { - return fmt.Errorf("goosemigrate.Up: %w", err) - } - - // Create DAL metrics for database operations - dalMetrics := database.NewMetrics(reg, namespace, "repo", new(core.Registry)) - - // Create database connection via database.NewSQL - sqlCfg := database.SQLConfig{Metrics: dalMetrics} - db, err := database.NewSQL(ctx, cfg.DB.Driver, sqlCfg, &connectors.Raw{Query: cfg.DB.Postgres}) - if err != nil { - return fmt.Errorf("database.NewSQL: %w", err) - } - - repo, err := registry.New(ctx, db, cfg.Registry.PluginsDir, cfg.Registry.MaxOutputSize) - if err != nil { - return fmt.Errorf("registry.New: %w", err) - } - - defer func() { - err := repo.Close() - if err != nil { - log.Error("close database connection", "error", err) - } - }() - - // Register DB connection pool metrics collector - dbCollector := adapter_metrics.NewDBCollector(repo.DB().UnderlyingDB(), namespace) - reg.MustRegister(dbCollector) - - // Register business metrics collector - businessCollector := adapter_metrics.NewBusinessMetricsCollector(repo.DB().UnderlyingDB(), namespace, log) - reg.MustRegister(businessCollector) - - auditStore := adapter_audit.New(repo.DB(), log) - auditWorker, auditCh := adapter_audit.NewWorker(auditStore, auditChannelCapacity, log, reg, namespace) - - go auditWorker.Run(ctx) - - defer func() { - lost := auditWorker.Shutdown(componentShutdownTimeout) - if lost > 0 { - log.Warn("audit events lost on shutdown", "count", lost) - } - }() - - // Create LicenseManager - licenseClient := license.NewMockLicenseClient() - lm, err := license.NewManager(ctx, licenseClient, license.Config{ - CacheTTL: cfg.License.CacheTTL, - }, log, reg, namespace) - if err != nil { - log.Warn("license initialization error, continuing in community mode", "error", err) - } - lm.StartRefreshWatcher(ctx) - - // Create FeatureGate - gate := license.NewFeatureGate(lm) - - // Create RateLimiter - rl := ratelimiter.New(ratelimiter.Config{ - RequestsPerSecond: cfg.RateLimit.RequestsPerSecond, - Burst: cfg.RateLimit.Burst, - CleanupInterval: cfg.RateLimit.CleanupInterval, - }, gate, nil, log, reg) // nil keyExtractor → PeerIPExtractor - rl.StartCleanup(ctx) - - // Wrap Registry in tracing decorator - tracedRegistry := telemetry.NewTracingRegistry(repo) - - // Override WorkerPool workers from license limits - wpWorkers := cfg.WorkerPool.Workers - if licenseWorkers := gate.MaxWorkers(); licenseWorkers > 0 { - wpWorkers = licenseWorkers - } - - // Wrap TracingRegistry in WorkerPool (limit plugin execution parallelism) - metricsAdapter := adapter_metrics.New(reg, namespace) - pool := core.NewWorkerPool(tracedRegistry, core.WorkerPoolConfig{ - Workers: wpWorkers, - QueueSize: cfg.WorkerPool.QueueSize, - GenerationTimeout: cfg.WorkerPool.GenerationTimeout, - MaxRetries: cfg.WorkerPool.MaxRetries, - ShutdownTimeout: cfg.WorkerPool.ShutdownTimeout, - }, log, metricsAdapter, reg, namespace) - pool.Start(ctx) - - defer func() { - lost := pool.Shutdown(cfg.WorkerPool.ShutdownTimeout) - if lost > 0 { - log.Warn("generation jobs lost on shutdown", "count", lost) - } - }() - - // Core gets pool as Registry - module := core.New(metricsAdapter, pool, gate, auditCh, log) - - // Wrap Core in tracing decorator, pass to API - tracedCore := telemetry.NewTracingCore(module) - - // Create gRPC server metrics - serverMetrics := grpchelper.NewServerMetrics(reg, "easyp", "api") - - // Create panics counter - panicsCounter := prometheus.NewCounter(prometheus.CounterOpts{ - Namespace: "easyp", - Name: "panics_total", - Help: "Total number of panics recovered in gRPC handlers.", - }) - reg.MustRegister(panicsCounter) - - // Create license interceptor - licenseInterceptor := api.NewLicenseInterceptor(gate, log) - - // Create gRPC server with full middleware stack - grpcSrv, healthSrv := grpchelper.NewServer( - &grpcMetrics{panics: panicsCounter}, - log, - serverMetrics, - api.ErrorToStatus, - []grpc.UnaryServerInterceptor{ - ratelimit.UnaryServerInterceptor(rl), - licenseInterceptor.UnaryServerInterceptor(), - }, - []grpc.StreamServerInterceptor{ - ratelimit.StreamServerInterceptor(rl), - licenseInterceptor.StreamServerInterceptor(), - }, - ) - serverMetrics.InitializeMetrics(grpcSrv) - - // Register API handlers - apiSrv := api.New(grpcSrv, healthSrv, tracedCore, log) - - eg, ctx := errgroup.WithContext(ctx) - - // Run gRPC Server - eg.Go(func() error { - lis, err := (&net.ListenConfig{}).Listen(ctx, "tcp", fmt.Sprintf("%s:%s", cfg.Server.Host, cfg.Server.Port.GRPC)) - if err != nil { - return fmt.Errorf("net.Listen gRPC: %w", err) - } - log.Info("starting gRPC server", "addr", lis.Addr().String()) - - go func() { - <-ctx.Done() - // Shutdown order: 1. Stop accepting new gRPC requests - grpcSrv.GracefulStop() - // 2. Shutdown telemetry (flush spans/metrics) - shutdownCtx, cancel := context.WithTimeout(context.Background(), componentShutdownTimeout) - defer cancel() - err := shutdownTelemetry(shutdownCtx) - if err != nil { - log.Error("telemetry shutdown error", "error", err) - } - }() - - err = grpcSrv.Serve(lis) - if err != nil { - return fmt.Errorf("grpcSrv.Serve: %w", err) - } - - return nil - }) - - // Run Metrics Server - eg.Go(func() error { - mux := http.NewServeMux() - mux.Handle("/metrics", promhttp.HandlerFor(reg, promhttp.HandlerOpts{})) - - srv := &http.Server{ - Addr: fmt.Sprintf("%s:%s", cfg.Server.Host, cfg.Server.Port.Metric), - Handler: mux, - ReadHeaderTimeout: componentShutdownTimeout, - } - - log.Info("starting metrics server", "addr", srv.Addr) - - go func() { - <-ctx.Done() - ctxShutdown, cancel := context.WithTimeout(context.Background(), componentShutdownTimeout) - defer cancel() - _ = srv.Shutdown(ctxShutdown) - }() - - err := srv.ListenAndServe() - if err != nil && !errors.Is(err, http.ErrServerClosed) { - return fmt.Errorf("metrics server error: %w", err) - } - - return nil - }) - - // Run Health Server - eg.Go(func() error { - // Simple health check handler - healthSrv, err := health.New(health.WithChecks(health.Config{ - Name: "postgres", - Timeout: time.Second, - Check: repo.Health, - })) - if err != nil { - return fmt.Errorf("health.New: %w", err) - } - - srv := &http.Server{ - Addr: fmt.Sprintf("%s:%s", cfg.Server.Host, cfg.Server.Port.Health), - Handler: healthSrv.Handler(), - ReadHeaderTimeout: componentShutdownTimeout, - } - - log.Info("starting health server", "addr", srv.Addr) - - go func() { - <-ctx.Done() - ctxShutdown, cancel := context.WithTimeout(context.Background(), componentShutdownTimeout) - defer cancel() - _ = srv.Shutdown(ctxShutdown) - }() - - err = srv.ListenAndServe() - if err != nil && !errors.Is(err, http.ErrServerClosed) { - return fmt.Errorf("health server error: %w", err) - } - - return nil - }) - - // Run MCP Server over streamable HTTP transport. - eg.Go(func() error { - mux := http.NewServeMux() - mux.Handle("/mcp", apiSrv.MCPHandler()) - - srv := &http.Server{ - Addr: fmt.Sprintf("%s:%s", cfg.Server.Host, cfg.Server.Port.MCP), - Handler: mux, - ReadHeaderTimeout: componentShutdownTimeout, - } - - log.Info("starting mcp server", "addr", srv.Addr, "path", "/mcp") - - go func() { - <-ctx.Done() - ctxShutdown, cancel := context.WithTimeout(context.Background(), componentShutdownTimeout) - defer cancel() - _ = srv.Shutdown(ctxShutdown) - }() - - err := srv.ListenAndServe() - if err != nil && !errors.Is(err, http.ErrServerClosed) { - return fmt.Errorf("mcp server error: %w", err) - } - - return nil - }) - - err = eg.Wait() - if err != nil { - return fmt.Errorf("errgroup: %w", err) - } - - return nil -} - -func buildLogger(level slog.Level) *slog.Logger { - return slog.New( - slog.NewJSONHandler( - os.Stdout, - &slog.HandlerOptions{ - AddSource: true, - Level: level, - }, - ), - ) -} - -func forceShutdown(ctx context.Context) { - log := monitor.FromContext(ctx) - const shutdownDelay = 15 * time.Second - - <-ctx.Done() - - // Create a new context for the shutdown delay/logging since the parent ctx is done - // But actually we just want to sleep. - - <-time.After(shutdownDelay) - log.Error("failed to graceful shutdown") - os.Exit(exitCode) -} diff --git a/go.mod b/go.mod index b839914..d21a0e0 100644 --- a/go.mod +++ b/go.mod @@ -17,6 +17,7 @@ require ( github.com/pressly/goose/v3 v3.27.1 github.com/prometheus/client_golang v1.23.2 github.com/sethvargo/go-envconfig v1.3.0 + github.com/urfave/cli/v3 v3.9.0 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0 go.opentelemetry.io/otel v1.43.0 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0 diff --git a/go.sum b/go.sum index 211f1fe..5c79a1a 100644 --- a/go.sum +++ b/go.sum @@ -124,6 +124,8 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/tetratelabs/wazero v1.11.0 h1:+gKemEuKCTevU4d7ZTzlsvgd1uaToIDtlQlmNbwqYhA= github.com/tetratelabs/wazero v1.11.0/go.mod h1:eV28rsN8Q+xwjogd7f4/Pp4xFxO7uOGbLcD/LzB1wiU= +github.com/urfave/cli/v3 v3.9.0 h1:AV9lIiPv3ukYnxunaCUsHnEozptYmDN2F0+yWqLMn/c= +github.com/urfave/cli/v3 v3.9.0/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso= github.com/wasilibs/wazero-helpers v0.0.0-20250123031827-cd30c44769bb h1:gQ+ZV4wJke/EBKYciZ2MshEouEHFuinB85dY3f5s1q8= github.com/wasilibs/wazero-helpers v0.0.0-20250123031827-cd30c44769bb/go.mod h1:jMeV4Vpbi8osrE/pKUxRZkVaA0EX7NZN0A9/oRzgpgY= github.com/yoheimuta/go-protoparser/v4 v4.14.2 h1:/P/LlX1CF9NaTWEltGcIZVvNlPbhABuAnBtAWpb3+74= diff --git a/internal/config/config.go b/internal/config/config.go new file mode 100644 index 0000000..e7d17b4 --- /dev/null +++ b/internal/config/config.go @@ -0,0 +1,142 @@ +// Package config provides shared configuration types and validation +// for both the EasyP server and the epctl CLI utility. +package config + +import ( + "bytes" + "fmt" + "os" + "time" + + "gopkg.in/yaml.v3" +) + +// Config is the root configuration for the EasyP service. +type Config struct { + Server Server `env:", prefix=SERVER_" yaml:"server"` + DB DBConfig `env:", prefix=DB_" yaml:"db"` + Registry RegistryConfig `env:", prefix=REGISTRY_" yaml:"registry"` + Telemetry TelemetryConfig `env:", prefix=TELEMETRY_" yaml:"telemetry"` + WorkerPool WorkerPoolConfig `env:", prefix=WORKER_POOL_" yaml:"worker_pool"` + License LicenseConfig `env:", prefix=LICENSE_" yaml:"license"` + RateLimit RateLimitConfig `env:", prefix=RATE_LIMIT_" yaml:"rate_limit"` +} + +// Server holds HTTP/gRPC server settings. +type Server struct { + Host string `env:"HOST, default=0.0.0.0" yaml:"host"` + Port Ports `env:", prefix=PORT_" yaml:"port"` +} + +// Ports defines listening ports for each protocol. +type Ports struct { + GRPC string `env:"GRPC, default=23410" yaml:"grpc"` + Metric string `env:"METRIC, default=23411" yaml:"metric"` + Health string `env:"HEALTH, default=23412" yaml:"health"` + MCP string `env:"MCP, default=23413" yaml:"mcp"` +} + +// DBConfig holds database connection settings. +type DBConfig struct { + Driver string `env:"DRIVER, default=postgres" yaml:"driver"` + Postgres string `env:"POSTGRES_DSN" yaml:"postgres"` +} + +// RegistryConfig configures plugin execution. +type RegistryConfig struct { + PluginsDir string `env:"PLUGINS_DIR, default=/plugins" yaml:"plugins_dir"` + MaxOutputSize int64 `env:"MAX_OUTPUT_SIZE, default=67108864" yaml:"max_output_size"` +} + +// TelemetryConfig configures observability endpoints. +type TelemetryConfig struct { + OTLPEndpoint string `env:"OTEL_EXPORTER_OTLP_ENDPOINT, default=localhost:4317" yaml:"otlp_endpoint"` + PyroscopeEndpoint string `env:"PYROSCOPE_ENDPOINT, default=http://localhost:4040" yaml:"pyroscope_endpoint"` +} + +// WorkerPoolConfig configures bounded concurrency for plugin execution. +type WorkerPoolConfig struct { + Workers int `env:"WORKERS,default=4" yaml:"workers"` + QueueSize int `env:"QUEUE_SIZE,default=16" yaml:"queue_size"` + GenerationTimeout time.Duration `env:"GENERATION_TIMEOUT,default=120s" yaml:"generation_timeout"` + MaxRetries int `env:"MAX_RETRIES,default=2" yaml:"max_retries"` + ShutdownTimeout time.Duration `env:"SHUTDOWN_TIMEOUT,default=30s" yaml:"shutdown_timeout"` +} + +// LicenseConfig configures the license cache. +type LicenseConfig struct { + CacheTTL time.Duration `env:"LICENSE_CACHE_TTL" yaml:"cache_ttl"` +} + +// RateLimitConfig configures per-IP rate limiting. +type RateLimitConfig struct { + RequestsPerSecond float64 `env:"REQUESTS_PER_SECOND,default=10.0" yaml:"requests_per_second"` + Burst int `env:"BURST,default=20" yaml:"burst"` + CleanupInterval time.Duration `env:"CLEANUP_INTERVAL,default=10m" yaml:"cleanup_interval"` +} + +// Validate performs structural validation of the configuration. +// Called by the server at startup and by "epctl config validate". +func (c *Config) Validate() error { + if c.Server.Port.GRPC == "" { + return fmt.Errorf("server.port.grpc is required") + } + + if c.DB.Driver == "" { + return fmt.Errorf("db.driver is required") + } + + if c.WorkerPool.Workers <= 0 { + return fmt.Errorf("worker_pool.workers must be positive, got %d", c.WorkerPool.Workers) + } + + if c.WorkerPool.QueueSize <= 0 { + return fmt.Errorf("worker_pool.queue_size must be positive, got %d", c.WorkerPool.QueueSize) + } + + if c.RateLimit.RequestsPerSecond <= 0 { + return fmt.Errorf("rate_limit.requests_per_second must be positive, got %f", c.RateLimit.RequestsPerSecond) + } + + if c.RateLimit.Burst <= 0 { + return fmt.Errorf("rate_limit.burst must be positive, got %d", c.RateLimit.Burst) + } + + return nil +} + +// LoadAndValidate reads a YAML file, parses it with strict field checking, +// and returns the config, a list of warnings (for unknown fields), and any error. +func LoadAndValidate(path string) (*Config, []string, error) { + data, err := os.ReadFile(path) + if err != nil { + return nil, nil, fmt.Errorf("reading config file %s: %w", path, err) + } + + // First pass: strict parsing to detect unknown fields. + var warnings []string + + strictDec := yaml.NewDecoder(bytes.NewReader(data)) + strictDec.KnownFields(true) + + var strictCfg Config + + if strictErr := strictDec.Decode(&strictCfg); strictErr != nil { + // If it's a type error about unknown fields, collect as warnings and re-parse leniently. + warnings = append(warnings, strictErr.Error()) + } + + // Second pass: lenient parsing to get the actual config. + lenientDec := yaml.NewDecoder(bytes.NewReader(data)) + + var cfg Config + if err = lenientDec.Decode(&cfg); err != nil { + return nil, warnings, fmt.Errorf("parsing config YAML: %w", err) + } + + if err = cfg.Validate(); err != nil { + return &cfg, warnings, fmt.Errorf("config validation: %w", err) + } + + return &cfg, warnings, nil +} diff --git a/register-plugins.sh b/register-plugins.sh deleted file mode 100755 index cdf2369..0000000 --- a/register-plugins.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/bin/bash - -# register-plugins.sh — Register all built plugins via gRPC CreatePlugin API. -# Requires a running easyp service and grpcurl installed. -# -# Usage: ./register-plugins.sh [host:port] -# host:port defaults to localhost:8080 - -set -euo pipefail - -GRPC_HOST="${1:-localhost:8080}" -PLUGINS_PREFIX="${2:-/plugins}" -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -PLUGINS_DIR="${SCRIPT_DIR}/plugins" - -if ! command -v grpcurl &>/dev/null; then - echo "Error: grpcurl is not installed. Install it: https://github.com/fullstorydev/grpcurl" >&2 - exit 1 -fi - -if [ ! -d "${PLUGINS_DIR}" ]; then - echo "Warning: plugins directory does not exist: ${PLUGINS_DIR}" >&2 - echo "Run ./build-plugins.sh first." - exit 0 -fi - -plugin_count=0 - -for plugin_bin in $(find "${PLUGINS_DIR}" -name plugin -type f | sort); do - # Extract relative path: plugins/{group}/{name}/{version}/plugin - rel_path="${plugin_bin#"${PLUGINS_DIR}/"}" - dir_path=$(dirname "${rel_path}") - - # Parse group/name/version - if [[ "${dir_path}" =~ ^([^/]+)/([^/]+)/(.+)$ ]]; then - group="${BASH_REMATCH[1]}" - name="${BASH_REMATCH[2]}" - version="${BASH_REMATCH[3]}" - - echo "Registering ${group}/${name}:${version}..." - - # Call CreatePlugin via grpcurl, capture output and exit code - output=$(grpcurl -plaintext \ - -d "{\"group\":\"${group}\",\"name\":\"${name}\",\"version\":\"${version}\",\"config\":{\"command\":[\"${PLUGINS_PREFIX}/${group}/${name}/${version}/plugin\"]}}" \ - "${GRPC_HOST}" \ - api.generator.v1.ServiceAPI/CreatePlugin 2>&1) && rc=0 || rc=$? - - if [ "${rc}" -ne 0 ]; then - if echo "${output}" | grep -qi "AlreadyExists\|ALREADY_EXISTS\|already exists"; then - echo "⚠ Already exists: ${group}/${name}:${version}" - else - echo "✗ Failed to register ${group}/${name}:${version}" >&2 - echo "${output}" >&2 - exit 1 - fi - else - echo "✓ Registered ${group}/${name}:${version}" - fi - - plugin_count=$((plugin_count + 1)) - fi -done - -if [ "${plugin_count}" -eq 0 ]; then - echo "Warning: no plugins found in ${PLUGINS_DIR}" >&2 -fi - -echo "" -echo "Done! Processed ${plugin_count} plugin(s)." diff --git a/registry/anthropics/buffa/.dockerignore b/registry/anthropics/buffa/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/anthropics/buffa/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/anthropics/buffa/Dockerfile b/registry/anthropics/buffa/Dockerfile new file mode 100644 index 0000000..3bbac77 --- /dev/null +++ b/registry/anthropics/buffa/Dockerfile @@ -0,0 +1,13 @@ +# syntax=docker/dockerfile:1.19 +FROM rust:1.91-alpine3.22 AS build + +ARG VERSION +ARG CRATE_NAME +ARG BINARY_NAME + +RUN apk add musl-dev upx --no-cache +RUN cargo install ${CRATE_NAME} --version ${VERSION#v} --root /out \ + && upx --best --lzma /out/bin/${BINARY_NAME} + +FROM scratch +COPY --from=build /out/bin/${BINARY_NAME} /plugin diff --git a/registry/anthropics/buffa/plugin.yaml b/registry/anthropics/buffa/plugin.yaml new file mode 100644 index 0000000..3dd29ca --- /dev/null +++ b/registry/anthropics/buffa/plugin.yaml @@ -0,0 +1,6 @@ +binary: protoc-gen-buffa +build_args: + CRATE_NAME: protoc-gen-buffa +versions: + - v0.6.0 + - v0.5.2 diff --git a/registry/anthropics/connect-rust/.dockerignore b/registry/anthropics/connect-rust/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/anthropics/connect-rust/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/anthropics/connect-rust/Dockerfile b/registry/anthropics/connect-rust/Dockerfile new file mode 100644 index 0000000..3bbac77 --- /dev/null +++ b/registry/anthropics/connect-rust/Dockerfile @@ -0,0 +1,13 @@ +# syntax=docker/dockerfile:1.19 +FROM rust:1.91-alpine3.22 AS build + +ARG VERSION +ARG CRATE_NAME +ARG BINARY_NAME + +RUN apk add musl-dev upx --no-cache +RUN cargo install ${CRATE_NAME} --version ${VERSION#v} --root /out \ + && upx --best --lzma /out/bin/${BINARY_NAME} + +FROM scratch +COPY --from=build /out/bin/${BINARY_NAME} /plugin diff --git a/registry/anthropics/connect-rust/plugin.yaml b/registry/anthropics/connect-rust/plugin.yaml new file mode 100644 index 0000000..da8cedf --- /dev/null +++ b/registry/anthropics/connect-rust/plugin.yaml @@ -0,0 +1,7 @@ +binary: protoc-gen-connect-rust +build_args: + CRATE_NAME: connectrpc-codegen +versions: + - v0.6.0 + - v0.5.0 + - v0.4.2 diff --git a/registry/apple/servicetalk/.dockerignore b/registry/apple/servicetalk/.dockerignore new file mode 100644 index 0000000..7ff6309 --- /dev/null +++ b/registry/apple/servicetalk/.dockerignore @@ -0,0 +1,3 @@ +* +!Dockerfile +!pom.xml diff --git a/registry/apple/servicetalk/Dockerfile b/registry/apple/servicetalk/Dockerfile new file mode 100644 index 0000000..8cc3a1f --- /dev/null +++ b/registry/apple/servicetalk/Dockerfile @@ -0,0 +1,24 @@ +# syntax=docker/dockerfile:1.23 +FROM debian:trixie-20260505@sha256:e2d08da6f42ef4b09b165d55528a12727aeed8240dc9edf888e3ec07e10ef9da AS build +ARG VERSION + +WORKDIR /app +RUN apt-get update \ + && apt-get install -y curl +RUN curl -fsSL -o servicetalk-grpc-protoc.jar https://repo1.maven.org/maven2/io/servicetalk/servicetalk-grpc-protoc/${VERSION#v}/servicetalk-grpc-protoc-${VERSION#v}-all.jar + +FROM gcr.io/distroless/java25-debian13:latest@sha256:3e0a1496b365a18d2c01ccfe27c8bc93b1a6b8ca7460c02b8badb791bf296fce AS base +ARG VERSION + +FROM maven:3.9.11-eclipse-temurin-21 AS maven-deps +ARG VERSION +COPY pom.xml /tmp/pom.xml +RUN cd /tmp && mvn -f pom.xml dependency:go-offline + +FROM scratch +ARG VERSION +COPY --from=base --link / / +COPY --from=build --link --chmod=0755 --chown=root:root /app/servicetalk-grpc-protoc.jar . +COPY --from=maven-deps /root/.m2/repository /maven-repository +USER nobody +ENTRYPOINT [ "/usr/bin/java", "-jar", "/servicetalk-grpc-protoc.jar"] diff --git a/registry/apple/servicetalk/plugin.yaml b/registry/apple/servicetalk/plugin.yaml new file mode 100644 index 0000000..8397847 --- /dev/null +++ b/registry/apple/servicetalk/plugin.yaml @@ -0,0 +1,10 @@ +binary: servicetalk-grpc-protoc.jar +versions: + - v0.42.64 + - v0.42.63 + - v0.42.62 + - v0.42.61 + - v0.42.60 + - v0.42.59 + - v0.42.58 + - v0.42.57 diff --git a/registry/apple/servicetalk/pom.xml b/registry/apple/servicetalk/pom.xml new file mode 100644 index 0000000..a511d46 --- /dev/null +++ b/registry/apple/servicetalk/pom.xml @@ -0,0 +1,39 @@ + + 4.0.0 + temp + temp + 1.0 + + + io.servicetalk + servicetalk-data-protobuf + 0.42.64 + + + io.servicetalk + servicetalk-grpc-api + 0.42.64 + + + io.servicetalk + servicetalk-grpc-protobuf + 0.42.64 + + + com.google.protobuf + protobuf-java + 4.34.1 + + + + com.google.protobuf + protobuf-javalite + 4.34.1 + + + build.buf + protobuf-javalite + 4.34.1 + + + diff --git a/registry/apple/swift/.dockerignore b/registry/apple/swift/.dockerignore new file mode 100644 index 0000000..ff0a69e --- /dev/null +++ b/registry/apple/swift/.dockerignore @@ -0,0 +1,3 @@ +* +!Dockerfile +!extramoduleimports.patch diff --git a/registry/apple/swift/Dockerfile b/registry/apple/swift/Dockerfile new file mode 100644 index 0000000..06fc0dc --- /dev/null +++ b/registry/apple/swift/Dockerfile @@ -0,0 +1,22 @@ +# syntax=docker/dockerfile:1.23 +FROM swift:6.3.2-bookworm@sha256:40918972df286d15aa47f4686c98a211f0a6152ca1d48b3dbccd6f56a3c34815 AS build +ARG VERSION + +RUN apt-get update \ + && apt-get install -y libstdc++-12-dev unzip + +WORKDIR /app +RUN git clone --depth 1 --branch ${VERSION#v} https://github.com/apple/swift-protobuf --recursive +WORKDIR /app/swift-protobuf + +RUN swift build -c release --static-swift-stdlib -Xlinker -s + +FROM gcr.io/distroless/cc-debian13:latest@sha256:56aaf20ab2523a346a67c8e8f8e8dabe447447d0788b82284d14ad79cd5f93cc AS base +ARG VERSION + +FROM scratch +ARG VERSION +COPY --link --from=base / / +COPY --link --from=build /app/swift-protobuf/.build/release/protoc-gen-swift . +USER nobody +ENTRYPOINT [ "/protoc-gen-swift" ] diff --git a/registry/apple/swift/extramoduleimports.patch b/registry/apple/swift/extramoduleimports.patch new file mode 100644 index 0000000..e3b9e6c --- /dev/null +++ b/registry/apple/swift/extramoduleimports.patch @@ -0,0 +1,63 @@ +diff --git a/Sources/protoc-gen-swift/FileGenerator.swift b/Sources/protoc-gen-swift/FileGenerator.swift +index d370e25..cea7bd2 100644 +--- a/Sources/protoc-gen-swift/FileGenerator.swift ++++ b/Sources/protoc-gen-swift/FileGenerator.swift +@@ -161,6 +161,14 @@ class FileGenerator { + return + } + ++ let neededCustomImports = generatorOptions.extraModuleImports ++ if !neededCustomImports.isEmpty { ++ p.print() ++ for i in neededCustomImports { ++ p.print("import \(i)\n") ++ } ++ } ++ + p.print() + generateVersionCheck(printer: &p) + +diff --git a/Sources/protoc-gen-swift/GeneratorOptions.swift b/Sources/protoc-gen-swift/GeneratorOptions.swift +index 159e621..a8f631e 100644 +--- a/Sources/protoc-gen-swift/GeneratorOptions.swift ++++ b/Sources/protoc-gen-swift/GeneratorOptions.swift +@@ -115,6 +115,7 @@ package class GeneratorOptions { + } + } + ++ let extraModuleImports: [String] + let outputNaming: OutputNaming + let enumGeneration: EnumGeneration + let protoToModuleMappings: ProtoFileToModuleMappings +@@ -146,6 +147,7 @@ package class GeneratorOptions { + } + + package init(parameter: any CodeGeneratorParameter) throws { ++ var externalModuleImports: [String] = [] + var outputNaming: OutputNaming = .fullPath + var enumGeneration: EnumGeneration = .none + var moduleMapPath: String? +@@ -238,6 +240,15 @@ package class GeneratorOptions { + value: pair.value + ) + } ++ case "ExtraModuleImports": ++ if !pair.value.isEmpty { ++ externalModuleImports.append(pair.value) ++ } else { ++ throw GenerationError.invalidParameterValue( ++ name: pair.key, ++ value: pair.value ++ ) ++ } + default: + throw GenerationError.unknownParameter(name: pair.key) + } +@@ -272,6 +283,7 @@ package class GeneratorOptions { + visibilitySourceSnippet = "package " + } + ++ self.extraModuleImports = externalModuleImports + self.experimentalStripNonfunctionalCodegen = experimentalStripNonfunctionalCodegen + self.experimentalHiddenNames = experimentalHiddenNames + diff --git a/registry/apple/swift/plugin.yaml b/registry/apple/swift/plugin.yaml new file mode 100644 index 0000000..89cf774 --- /dev/null +++ b/registry/apple/swift/plugin.yaml @@ -0,0 +1,31 @@ +binary: protoc-gen-swift +versions: + - v1.38.0 + - v1.37.0 + - v1.36.1 + - v1.36.0 + - v1.35.1 + - v1.35.0 + - v1.34.1 + - v1.33.3 + - v1.33.1 + - v1.32.0 + - v1.31.1 + - v1.31.0 + - v1.30.0 + - v1.29.0 + - v1.28.2 + - v1.28.1 + - v1.28.0 + - v1.27.1 + - v1.27.0 + - v1.26.0 + - v1.25.2 + - v1.25.1 + - v1.24.0 + - v1.23.0 + - v1.22.1 + - v1.22.0 + - v1.21.0 + - v1.20.3 + - v1.20.2 diff --git a/registry/bufbuild/connect-es/.dockerignore b/registry/bufbuild/connect-es/.dockerignore new file mode 100644 index 0000000..771bbba --- /dev/null +++ b/registry/bufbuild/connect-es/.dockerignore @@ -0,0 +1,3 @@ +* +!Dockerfile +!package*.json diff --git a/registry/bufbuild/connect-es/Dockerfile b/registry/bufbuild/connect-es/Dockerfile new file mode 100644 index 0000000..83a1cbf --- /dev/null +++ b/registry/bufbuild/connect-es/Dockerfile @@ -0,0 +1,13 @@ +# syntax=docker/dockerfile:1.4 +FROM node:18.16.0-alpine3.17 AS build +ARG VERSION +WORKDIR /app +COPY --link package*.json . +RUN npm ci +RUN sed -i -e 's|/usr/bin/env node|/nodejs/bin/node|g' /app/node_modules/@bufbuild/protoc-gen-connect-es/bin/protoc-gen-connect-es + +FROM gcr.io/distroless/nodejs18-debian11 +ARG VERSION +COPY --link --from=build /app /app +USER nobody +ENTRYPOINT [ "/app/node_modules/.bin/protoc-gen-connect-es" ] diff --git a/registry/bufbuild/connect-es/package-lock.json b/registry/bufbuild/connect-es/package-lock.json new file mode 100644 index 0000000..4e7f765 --- /dev/null +++ b/registry/bufbuild/connect-es/package-lock.json @@ -0,0 +1,98 @@ +{ + "name": "plugins-bufbuild-connect-es", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "plugins-bufbuild-connect-es", + "version": "1.0.0", + "dependencies": { + "@bufbuild/protoc-gen-connect-es": "0.9.1" + } + }, + "node_modules/@bufbuild/protobuf": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-1.2.1.tgz", + "integrity": "sha512-cwwGvLGqvoaOZmoP5+i4v/rbW+rHkguvTehuZyM2p/xpmaNSdT2h3B7kHw33aiffv35t1XrYHIkdJSEkSEMJuA==" + }, + "node_modules/@bufbuild/protoc-gen-connect-es": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@bufbuild/protoc-gen-connect-es/-/protoc-gen-connect-es-0.9.1.tgz", + "integrity": "sha512-Wtgtw4RpGAlEma7Z7hVUPra3BC/otWewFie1bgxejJr2BykqejzFN63iP4KF5fUUY38LBbtZE9IfnBfxQaNV/Q==", + "dependencies": { + "@bufbuild/protobuf": "^1.2.0", + "@bufbuild/protoplugin": "^1.2.0" + }, + "bin": { + "protoc-gen-connect-es": "bin/protoc-gen-connect-es" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@bufbuild/connect": "0.9.1", + "@bufbuild/protoc-gen-es": "^1.2.0" + }, + "peerDependenciesMeta": { + "@bufbuild/connect": { + "optional": true + }, + "@bufbuild/protoc-gen-es": { + "optional": true + } + } + }, + "node_modules/@bufbuild/protoplugin": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@bufbuild/protoplugin/-/protoplugin-1.2.1.tgz", + "integrity": "sha512-7VtPgJGXcOszidMpQRQK9wm8QlKqC80Uc5XR9+Ej+DN4Ur64dAW1djZ7kLZ3ij6Z9IY4FteGQT+ubUNxzcW2NA==", + "dependencies": { + "@bufbuild/protobuf": "1.2.1", + "@typescript/vfs": "^1.4.0", + "typescript": "4.5.2" + } + }, + "node_modules/@typescript/vfs": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.4.0.tgz", + "integrity": "sha512-Pood7yv5YWMIX+yCHo176OnF8WUlKGImFG7XlsuH14Zb1YN5+dYD3uUtS7lqZtsH7tAveNUi2NzdpQCN0yRbaw==", + "dependencies": { + "debug": "^4.1.1" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/typescript": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz", + "integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + } + } +} diff --git a/registry/bufbuild/connect-es/package.json b/registry/bufbuild/connect-es/package.json new file mode 100644 index 0000000..8272e8c --- /dev/null +++ b/registry/bufbuild/connect-es/package.json @@ -0,0 +1,7 @@ +{ + "name": "plugins-bufbuild-connect-es", + "version": "1.0.0", + "dependencies": { + "@bufbuild/protoc-gen-connect-es": "0.9.1" + } +} diff --git a/registry/bufbuild/connect-es/plugin.yaml b/registry/bufbuild/connect-es/plugin.yaml new file mode 100644 index 0000000..5a768d2 --- /dev/null +++ b/registry/bufbuild/connect-es/plugin.yaml @@ -0,0 +1,17 @@ +binary: app/node_modules/.bin/protoc-gen-connect-es +versions: + - v0.9.1 + - v0.9.0 + - v0.8.6 + - v0.8.5 + - v0.8.4 + - v0.8.3 + - v0.8.2 + - v0.8.1 + - v0.8.0 + - v0.7.0 + - v0.13.0 + - v0.12.0 + - v0.11.0 + - v0.10.1 + - v0.10.0 diff --git a/registry/bufbuild/connect-go/.dockerignore b/registry/bufbuild/connect-go/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/bufbuild/connect-go/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/bufbuild/connect-go/Dockerfile b/registry/bufbuild/connect-go/Dockerfile new file mode 100644 index 0000000..e85c930 --- /dev/null +++ b/registry/bufbuild/connect-go/Dockerfile @@ -0,0 +1,16 @@ +# syntax=docker/dockerfile:1.19 +FROM golang:1.26-alpine3.22 AS build + +ARG VERSION +ARG GO_MODULE +ARG BINARY_NAME + +RUN apk add upx --no-cache +RUN --mount=type=cache,target=/go/pkg/mod \ + CGO_ENABLED=0 go install -ldflags "-s -w" -trimpath ${GO_MODULE}@${VERSION} +RUN cp /go/bin/${BINARY_NAME} /plugin 2>/dev/null \ + || cp /go/bin/linux_*/${BINARY_NAME} /plugin \ + && upx --best --lzma /plugin + +FROM scratch +COPY --from=build /plugin /plugin diff --git a/registry/bufbuild/connect-go/plugin.yaml b/registry/bufbuild/connect-go/plugin.yaml new file mode 100644 index 0000000..4eacd5a --- /dev/null +++ b/registry/bufbuild/connect-go/plugin.yaml @@ -0,0 +1,20 @@ +binary: protoc-gen-connect-go +build_args: + GO_MODULE: github.com/bufbuild/connect-go/cmd/protoc-gen-connect-go +versions: + - v1.9.0 + - v1.8.0 + - v1.7.0 + - v1.6.0 + - v1.5.2 + - v1.5.1 + - v1.5.0 + - v1.4.1 + - v1.4.0 + - v1.3.2 + - v1.3.1 + - v1.3.0 + - v1.2.0 + - v1.10.0 + - v1.1.0 + - v1.0.0 diff --git a/registry/bufbuild/connect-kotlin/.dockerignore b/registry/bufbuild/connect-kotlin/.dockerignore new file mode 100644 index 0000000..7ff6309 --- /dev/null +++ b/registry/bufbuild/connect-kotlin/.dockerignore @@ -0,0 +1,3 @@ +* +!Dockerfile +!pom.xml diff --git a/registry/bufbuild/connect-kotlin/Dockerfile b/registry/bufbuild/connect-kotlin/Dockerfile new file mode 100644 index 0000000..a396f4d --- /dev/null +++ b/registry/bufbuild/connect-kotlin/Dockerfile @@ -0,0 +1,19 @@ +# syntax=docker/dockerfile:1.4 +FROM debian:bullseye-20230814 AS build +ARG VERSION +RUN apt-get update \ + && apt-get install -y curl +WORKDIR /app +RUN curl -fsSL -o /app/protoc-gen-connect-kotlin.jar https://repo1.maven.org/maven2/build/buf/protoc-gen-connect-kotlin/${VERSION#v}/protoc-gen-connect-kotlin-${VERSION#v}.jar + +FROM maven:3.9.11-eclipse-temurin-21 AS maven-deps +ARG VERSION +COPY pom.xml /tmp/pom.xml +RUN cd /tmp && mvn -f pom.xml dependency:go-offline + +FROM gcr.io/distroless/java17-debian11 +ARG VERSION +WORKDIR /app +COPY --from=build /app/protoc-gen-connect-kotlin.jar /app +COPY --from=maven-deps /root/.m2/repository /maven-repository +CMD ["/app/protoc-gen-connect-kotlin.jar"] diff --git a/registry/bufbuild/connect-kotlin/plugin.yaml b/registry/bufbuild/connect-kotlin/plugin.yaml new file mode 100644 index 0000000..e242d74 --- /dev/null +++ b/registry/bufbuild/connect-kotlin/plugin.yaml @@ -0,0 +1,11 @@ +binary: protoc-gen-connect-kotlin +versions: + - v0.1.9 + - v0.1.8 + - v0.1.7 + - v0.1.6 + - v0.1.5 + - v0.1.4 + - v0.1.2 + - v0.1.10 + - v0.1.1 diff --git a/registry/bufbuild/connect-kotlin/pom.xml b/registry/bufbuild/connect-kotlin/pom.xml new file mode 100644 index 0000000..a01cb0a --- /dev/null +++ b/registry/bufbuild/connect-kotlin/pom.xml @@ -0,0 +1,75 @@ + + 4.0.0 + temp + temp + 1.0 + + + build.buf + connect-kotlin + 0.1.9 + + + build.buf + connect-kotlin-google-java-ext + 0.1.9 + + + build.buf + connect-kotlin-okhttp + 0.1.9 + + + com.google.protobuf + protobuf-kotlin + 3.24.0 + + + org.jetbrains.kotlin + kotlin-stdlib + 1.8.22 + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + 1.8.22 + + + com.google.protobuf + protobuf-java + 3.24.0 + + + + build.buf + connect-kotlin-google-javalite-ext + 0.1.9 + + + com.google.protobuf + protobuf-kotlin-lite + 3.24.0 + + + com.google.protobuf + protobuf-javalite + 3.24.0 + + + build.buf + protobuf-javalite + 3.24.0 + + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + 1.8.22 + + + + + + diff --git a/registry/bufbuild/connect-query/.dockerignore b/registry/bufbuild/connect-query/.dockerignore new file mode 100644 index 0000000..771bbba --- /dev/null +++ b/registry/bufbuild/connect-query/.dockerignore @@ -0,0 +1,3 @@ +* +!Dockerfile +!package*.json diff --git a/registry/bufbuild/connect-query/Dockerfile b/registry/bufbuild/connect-query/Dockerfile new file mode 100644 index 0000000..dfea3e2 --- /dev/null +++ b/registry/bufbuild/connect-query/Dockerfile @@ -0,0 +1,13 @@ +# syntax=docker/dockerfile:1.4 +FROM node:18.17.1-alpine3.18 AS build +ARG VERSION +WORKDIR /app +COPY --link package*.json . +RUN npm ci +RUN sed -i -e 's|/usr/bin/env node|/nodejs/bin/node|g' /app/node_modules/@bufbuild/protoc-gen-connect-query/bin/protoc-gen-connect-query + +FROM gcr.io/distroless/nodejs18-debian11 +ARG VERSION +COPY --link --from=build /app /app +USER nobody +ENTRYPOINT [ "/app/node_modules/.bin/protoc-gen-connect-query" ] diff --git a/registry/bufbuild/connect-query/package-lock.json b/registry/bufbuild/connect-query/package-lock.json new file mode 100644 index 0000000..58af583 --- /dev/null +++ b/registry/bufbuild/connect-query/package-lock.json @@ -0,0 +1,94 @@ +{ + "name": "plugins-bufbuild-connect-query", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "plugins-bufbuild-connect-query", + "version": "1.0.0", + "dependencies": { + "@bufbuild/protoc-gen-connect-query": "0.4.1" + } + }, + "node_modules/@bufbuild/protobuf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-1.3.0.tgz", + "integrity": "sha512-G372ods0pLt46yxVRsnP/e2btVPuuzArcMPFpIDeIwiGPuuglEs9y75iG0HMvZgncsj5TvbYRWqbVyOe3PLCWQ==" + }, + "node_modules/@bufbuild/protoc-gen-connect-query": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@bufbuild/protoc-gen-connect-query/-/protoc-gen-connect-query-0.4.1.tgz", + "integrity": "sha512-tSKaFODEnT9j+Nz1BswbYEbRzE4JGHIdYksOPVKtUPDa+NN0sbxDIFVvBkVAr4mKi7Ekrde3sT2BeEcbtdOBSQ==", + "dependencies": { + "@bufbuild/protobuf": "^1.3.0", + "@bufbuild/protoplugin": "^1.3.0" + }, + "bin": { + "protoc-gen-connect-query": "bin/protoc-gen-connect-query" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@bufbuild/protoc-gen-es": "1.x" + }, + "peerDependenciesMeta": { + "@bufbuild/protoc-gen-es": { + "optional": true + } + } + }, + "node_modules/@bufbuild/protoplugin": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@bufbuild/protoplugin/-/protoplugin-1.3.0.tgz", + "integrity": "sha512-zye8CfJb9VWzaHR/f1qcEkddaRh9De+u6fORsj92Ten8EJUcyhiY5BivET+RMTissAKXKrp/f2zSBCV0dlFxPw==", + "dependencies": { + "@bufbuild/protobuf": "1.3.0", + "@typescript/vfs": "^1.4.0", + "typescript": "4.5.2" + } + }, + "node_modules/@typescript/vfs": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.5.0.tgz", + "integrity": "sha512-AJS307bPgbsZZ9ggCT3wwpg3VbTKMFNHfaY/uF0ahSkYYrPF2dSSKDNIDIQAHm9qJqbLvCsSJH7yN4Vs/CsMMg==", + "dependencies": { + "debug": "^4.1.1" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/typescript": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz", + "integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + } + } +} diff --git a/registry/bufbuild/connect-query/package.json b/registry/bufbuild/connect-query/package.json new file mode 100644 index 0000000..385d293 --- /dev/null +++ b/registry/bufbuild/connect-query/package.json @@ -0,0 +1,7 @@ +{ + "name": "plugins-bufbuild-connect-query", + "version": "1.0.0", + "dependencies": { + "@bufbuild/protoc-gen-connect-query": "0.4.1" + } +} diff --git a/registry/bufbuild/connect-query/plugin.yaml b/registry/bufbuild/connect-query/plugin.yaml new file mode 100644 index 0000000..e3a5e59 --- /dev/null +++ b/registry/bufbuild/connect-query/plugin.yaml @@ -0,0 +1,11 @@ +binary: app/node_modules/.bin/protoc-gen-connect-query +versions: + - v0.4.1 + - v0.4.0 + - v0.3.0 + - v0.2.3 + - v0.2.2 + - v0.2.1 + - v0.2.0 + - v0.1.1 + - v0.1.0 diff --git a/registry/bufbuild/connect-swift-mocks/.dockerignore b/registry/bufbuild/connect-swift-mocks/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/bufbuild/connect-swift-mocks/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/bufbuild/connect-swift-mocks/Dockerfile b/registry/bufbuild/connect-swift-mocks/Dockerfile new file mode 100644 index 0000000..fd83671 --- /dev/null +++ b/registry/bufbuild/connect-swift-mocks/Dockerfile @@ -0,0 +1,14 @@ +# syntax=docker/dockerfile:1.6 +FROM swift:5.8.1-focal AS build +ARG VERSION + +WORKDIR /app +RUN git clone --depth 1 --branch ${VERSION#v} https://github.com/bufbuild/connect-swift +WORKDIR /app/connect-swift +RUN swift build -c release --product protoc-gen-connect-swift-mocks --static-swift-stdlib -Xlinker -s + +FROM gcr.io/distroless/cc-debian11 +ARG VERSION +COPY --from=build --link /app/connect-swift/.build/release/protoc-gen-connect-swift-mocks . +USER nobody +ENTRYPOINT [ "/protoc-gen-connect-swift-mocks" ] diff --git a/registry/bufbuild/connect-swift-mocks/plugin.yaml b/registry/bufbuild/connect-swift-mocks/plugin.yaml new file mode 100644 index 0000000..013074c --- /dev/null +++ b/registry/bufbuild/connect-swift-mocks/plugin.yaml @@ -0,0 +1,9 @@ +binary: protoc-gen-connect-swift-mocks +versions: + - v0.7.0 + - v0.6.0 + - v0.5.0 + - v0.4.0 + - v0.3.0 + - v0.2.0 + - v0.1.0 diff --git a/registry/bufbuild/connect-swift/.dockerignore b/registry/bufbuild/connect-swift/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/bufbuild/connect-swift/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/bufbuild/connect-swift/Dockerfile b/registry/bufbuild/connect-swift/Dockerfile new file mode 100644 index 0000000..f337f15 --- /dev/null +++ b/registry/bufbuild/connect-swift/Dockerfile @@ -0,0 +1,14 @@ +# syntax=docker/dockerfile:1.6 +FROM swift:5.8.1-focal AS build +ARG VERSION + +WORKDIR /app +RUN git clone --depth 1 --branch ${VERSION#v} https://github.com/bufbuild/connect-swift +WORKDIR /app/connect-swift +RUN swift build -c release --product protoc-gen-connect-swift --static-swift-stdlib -Xlinker -s + +FROM gcr.io/distroless/cc-debian11 +ARG VERSION +COPY --from=build --link /app/connect-swift/.build/release/protoc-gen-connect-swift . +USER nobody +ENTRYPOINT [ "/protoc-gen-connect-swift" ] diff --git a/registry/bufbuild/connect-swift/plugin.yaml b/registry/bufbuild/connect-swift/plugin.yaml new file mode 100644 index 0000000..e47ac7c --- /dev/null +++ b/registry/bufbuild/connect-swift/plugin.yaml @@ -0,0 +1,9 @@ +binary: protoc-gen-connect-swift +versions: + - v0.7.0 + - v0.6.0 + - v0.5.0 + - v0.4.0 + - v0.3.0 + - v0.2.0 + - v0.1.0 diff --git a/registry/bufbuild/connect-web/.dockerignore b/registry/bufbuild/connect-web/.dockerignore new file mode 100644 index 0000000..771bbba --- /dev/null +++ b/registry/bufbuild/connect-web/.dockerignore @@ -0,0 +1,3 @@ +* +!Dockerfile +!package*.json diff --git a/registry/bufbuild/connect-web/Dockerfile b/registry/bufbuild/connect-web/Dockerfile new file mode 100644 index 0000000..8120aac --- /dev/null +++ b/registry/bufbuild/connect-web/Dockerfile @@ -0,0 +1,13 @@ +# syntax=docker/dockerfile:1.4 +FROM node:18.15.0-alpine3.17 AS build +ARG VERSION +WORKDIR /app +COPY --link package*.json . +RUN npm ci +RUN sed -i -e 's|/usr/bin/env node|/nodejs/bin/node|g' /app/node_modules/@bufbuild/protoc-gen-connect-web/bin/protoc-gen-connect-web + +FROM gcr.io/distroless/nodejs18-debian11 +ARG VERSION +COPY --link --from=build /app /app +USER nobody +ENTRYPOINT [ "/app/node_modules/.bin/protoc-gen-connect-web" ] diff --git a/registry/bufbuild/connect-web/package-lock.json b/registry/bufbuild/connect-web/package-lock.json new file mode 100644 index 0000000..e272e88 --- /dev/null +++ b/registry/bufbuild/connect-web/package-lock.json @@ -0,0 +1,98 @@ +{ + "name": "plugins-bufbuild-connect-web", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "plugins-bufbuild-connect-web", + "version": "1.0.0", + "dependencies": { + "@bufbuild/protoc-gen-connect-web": "0.8.6" + } + }, + "node_modules/@bufbuild/protobuf": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-1.2.0.tgz", + "integrity": "sha512-MBVuQMOBHxgGnZ9XCUIi8WOy5O/T4ma3TduCRhRvndv3UDbG9cHgd8h6nOYSGyBYPEvXf1z9nTwhp8mVIDbq2g==" + }, + "node_modules/@bufbuild/protoc-gen-connect-web": { + "version": "0.8.6", + "resolved": "https://registry.npmjs.org/@bufbuild/protoc-gen-connect-web/-/protoc-gen-connect-web-0.8.6.tgz", + "integrity": "sha512-BltKOzGONR8uGlGdjgZnMBqudNEcRgF75XcttfyyZJsKPsknivFrMSoSlI6ZUyR+TQ567vJDxKKGO4bukQiBWA==", + "dependencies": { + "@bufbuild/protobuf": "^1.2.0", + "@bufbuild/protoplugin": "^1.2.0" + }, + "bin": { + "protoc-gen-connect-web": "bin/protoc-gen-connect-web" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@bufbuild/connect": "0.8.6", + "@bufbuild/protoc-gen-es": "^1.2.0" + }, + "peerDependenciesMeta": { + "@bufbuild/connect": { + "optional": true + }, + "@bufbuild/protoc-gen-es": { + "optional": true + } + } + }, + "node_modules/@bufbuild/protoplugin": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@bufbuild/protoplugin/-/protoplugin-1.2.0.tgz", + "integrity": "sha512-TX0mEk+LdIbpK2xr5RqeUswR8jGZs6uCX6Cq8azADj8hhiUr7Xty8agEOU/zR+J71D4dV5SnyEPYyw0nGJ6dGQ==", + "dependencies": { + "@bufbuild/protobuf": "1.2.0", + "@typescript/vfs": "^1.4.0", + "typescript": "4.5.2" + } + }, + "node_modules/@typescript/vfs": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.4.0.tgz", + "integrity": "sha512-Pood7yv5YWMIX+yCHo176OnF8WUlKGImFG7XlsuH14Zb1YN5+dYD3uUtS7lqZtsH7tAveNUi2NzdpQCN0yRbaw==", + "dependencies": { + "debug": "^4.1.1" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/typescript": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz", + "integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + } + } +} diff --git a/registry/bufbuild/connect-web/package.json b/registry/bufbuild/connect-web/package.json new file mode 100644 index 0000000..1012c40 --- /dev/null +++ b/registry/bufbuild/connect-web/package.json @@ -0,0 +1,7 @@ +{ + "name": "plugins-bufbuild-connect-web", + "version": "1.0.0", + "dependencies": { + "@bufbuild/protoc-gen-connect-web": "0.8.6" + } +} diff --git a/registry/bufbuild/connect-web/plugin.yaml b/registry/bufbuild/connect-web/plugin.yaml new file mode 100644 index 0000000..d356f82 --- /dev/null +++ b/registry/bufbuild/connect-web/plugin.yaml @@ -0,0 +1,18 @@ +binary: app/node_modules/.bin/protoc-gen-connect-web +versions: + - v0.8.6 + - v0.8.5 + - v0.8.4 + - v0.8.3 + - v0.8.2 + - v0.8.1 + - v0.8.0 + - v0.7.0 + - v0.6.0 + - v0.5.0 + - v0.4.0 + - v0.3.3 + - v0.3.2 + - v0.3.1 + - v0.3.0 + - v0.2.1 diff --git a/registry/bufbuild/es/.dockerignore b/registry/bufbuild/es/.dockerignore new file mode 100644 index 0000000..771bbba --- /dev/null +++ b/registry/bufbuild/es/.dockerignore @@ -0,0 +1,3 @@ +* +!Dockerfile +!package*.json diff --git a/registry/bufbuild/es/Dockerfile b/registry/bufbuild/es/Dockerfile new file mode 100644 index 0000000..a30bd39 --- /dev/null +++ b/registry/bufbuild/es/Dockerfile @@ -0,0 +1,25 @@ +# syntax=docker/dockerfile:1.18 +FROM node:22.19.0-bookworm AS build +ARG VERSION +WORKDIR /app +COPY --link package*.json . +RUN npm ci \ + && find node_modules/typescript ! -name 'typescript.js' ! -name 'package.json' -type f -exec rm -f {} + \ + && find node_modules/typescript -depth -type d -empty -delete \ + && ./node_modules/.bin/esbuild ./node_modules/.bin/protoc-gen-es --bundle --external:typescript --platform=node --outfile=protoc-gen-es.js + +FROM gcr.io/distroless/nodejs22-debian12:latest@sha256:3732180ba4a39101bd95b7105ef0c47526c197d6c29c6d48f7059a647a4064a5 AS node +ARG VERSION + +FROM gcr.io/distroless/cc-debian12:latest@sha256:620d8b11ae800f0dbd7995f89ddc5344ad603269ea98770588b1b07a4a0a6872 AS base +ARG VERSION + +FROM scratch +ARG VERSION +COPY --link --from=base / / +COPY --link --from=node --chmod=0755 /nodejs/bin/node /nodejs/bin/node +COPY --link --from=build --chmod=0755 /app/protoc-gen-es.js /app/protoc-gen-es.js +COPY --link --from=build /app/node_modules/typescript /app/node_modules/typescript +USER nobody +ENTRYPOINT ["/nodejs/bin/node"] +CMD [ "/app/protoc-gen-es.js" ] diff --git a/registry/bufbuild/es/package-lock.json b/registry/bufbuild/es/package-lock.json new file mode 100644 index 0000000..ede7ae1 --- /dev/null +++ b/registry/bufbuild/es/package-lock.json @@ -0,0 +1,591 @@ +{ + "name": "plugins-bufbuild-protobuf-es", + "version": "2.9.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "plugins-bufbuild-protobuf-es", + "version": "2.9.0", + "dependencies": { + "@bufbuild/protoc-gen-es": "2.9.0" + }, + "devDependencies": { + "esbuild": "^0.25.1" + } + }, + "node_modules/@bufbuild/protobuf": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.9.0.tgz", + "integrity": "sha512-rnJenoStJ8nvmt9Gzye8nkYd6V22xUAnu4086ER7h1zJ508vStko4pMvDeQ446ilDTFpV5wnoc5YS7XvMwwMqA==", + "license": "(Apache-2.0 AND BSD-3-Clause)" + }, + "node_modules/@bufbuild/protoc-gen-es": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@bufbuild/protoc-gen-es/-/protoc-gen-es-2.9.0.tgz", + "integrity": "sha512-g54rrHLKc7fnxN25ikynstRxR19M5G5l/hyqut2JoypJ9iU9QgUE63zEm8PNvVfBd4r5PEzWPfbWy5MNOeuMKQ==", + "license": "Apache-2.0", + "dependencies": { + "@bufbuild/protobuf": "2.9.0", + "@bufbuild/protoplugin": "2.9.0" + }, + "bin": { + "protoc-gen-es": "bin/protoc-gen-es" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@bufbuild/protobuf": "2.9.0" + }, + "peerDependenciesMeta": { + "@bufbuild/protobuf": { + "optional": true + } + } + }, + "node_modules/@bufbuild/protoplugin": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@bufbuild/protoplugin/-/protoplugin-2.9.0.tgz", + "integrity": "sha512-uoiwNVYoTq+AyqaV1L6pBazGx5fXOO89L0NSR9/7hEfo0Y8n9T1jsKGu4mkitLmP3z+8gJREaule1mMuKBPyYw==", + "license": "Apache-2.0", + "dependencies": { + "@bufbuild/protobuf": "2.9.0", + "@typescript/vfs": "^1.5.2", + "typescript": "5.4.5" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.10.tgz", + "integrity": "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.10.tgz", + "integrity": "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.10.tgz", + "integrity": "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.10.tgz", + "integrity": "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.10.tgz", + "integrity": "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.10.tgz", + "integrity": "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.10.tgz", + "integrity": "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.10.tgz", + "integrity": "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.10.tgz", + "integrity": "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.10.tgz", + "integrity": "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.10.tgz", + "integrity": "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.10.tgz", + "integrity": "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.10.tgz", + "integrity": "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.10.tgz", + "integrity": "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.10.tgz", + "integrity": "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.10.tgz", + "integrity": "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.10.tgz", + "integrity": "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.10.tgz", + "integrity": "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.10.tgz", + "integrity": "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.10.tgz", + "integrity": "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.10.tgz", + "integrity": "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.10.tgz", + "integrity": "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.10.tgz", + "integrity": "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.10.tgz", + "integrity": "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.10.tgz", + "integrity": "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.10.tgz", + "integrity": "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@typescript/vfs": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.6.1.tgz", + "integrity": "sha512-JwoxboBh7Oz1v38tPbkrZ62ZXNHAk9bJ7c9x0eI5zBfBnBYGhURdbnh7Z4smN/MV48Y5OCcZb58n972UtbazsA==", + "license": "MIT", + "dependencies": { + "debug": "^4.1.1" + }, + "peerDependencies": { + "typescript": "*" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/esbuild": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.10.tgz", + "integrity": "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.10", + "@esbuild/android-arm": "0.25.10", + "@esbuild/android-arm64": "0.25.10", + "@esbuild/android-x64": "0.25.10", + "@esbuild/darwin-arm64": "0.25.10", + "@esbuild/darwin-x64": "0.25.10", + "@esbuild/freebsd-arm64": "0.25.10", + "@esbuild/freebsd-x64": "0.25.10", + "@esbuild/linux-arm": "0.25.10", + "@esbuild/linux-arm64": "0.25.10", + "@esbuild/linux-ia32": "0.25.10", + "@esbuild/linux-loong64": "0.25.10", + "@esbuild/linux-mips64el": "0.25.10", + "@esbuild/linux-ppc64": "0.25.10", + "@esbuild/linux-riscv64": "0.25.10", + "@esbuild/linux-s390x": "0.25.10", + "@esbuild/linux-x64": "0.25.10", + "@esbuild/netbsd-arm64": "0.25.10", + "@esbuild/netbsd-x64": "0.25.10", + "@esbuild/openbsd-arm64": "0.25.10", + "@esbuild/openbsd-x64": "0.25.10", + "@esbuild/openharmony-arm64": "0.25.10", + "@esbuild/sunos-x64": "0.25.10", + "@esbuild/win32-arm64": "0.25.10", + "@esbuild/win32-ia32": "0.25.10", + "@esbuild/win32-x64": "0.25.10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/typescript": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + } + } +} diff --git a/registry/bufbuild/es/package.json b/registry/bufbuild/es/package.json new file mode 100644 index 0000000..9dc391c --- /dev/null +++ b/registry/bufbuild/es/package.json @@ -0,0 +1,10 @@ +{ + "name": "plugins-bufbuild-protobuf-es", + "version": "2.9.0", + "dependencies": { + "@bufbuild/protoc-gen-es": "2.9.0" + }, + "devDependencies": { + "esbuild": "^0.25.1" + } +} diff --git a/registry/bufbuild/es/plugin.yaml b/registry/bufbuild/es/plugin.yaml new file mode 100644 index 0000000..16534a5 --- /dev/null +++ b/registry/bufbuild/es/plugin.yaml @@ -0,0 +1,53 @@ +binary: nodejs/bin/node +versions: + - v2.9.0 + - v2.8.0 + - v2.7.0 + - v2.6.3 + - v2.6.2 + - v2.6.1 + - v2.6.0 + - v2.5.2 + - v2.5.1 + - v2.5.0 + - v2.4.0 + - v2.3.0 + - v2.2.5 + - v2.2.4 + - v2.2.3 + - v2.2.2 + - v2.2.1 + - v2.2.0 + - v2.12.0 + - v2.11.0 + - v2.10.2 + - v2.10.1 + - v2.10.0 + - v2.1.0 + - v2.0.0 + - v1.9.0 + - v1.8.0 + - v1.7.2 + - v1.7.1 + - v1.7.0 + - v1.6.0 + - v1.5.1 + - v1.5.0 + - v1.4.2 + - v1.4.1 + - v1.4.0 + - v1.3.3 + - v1.3.1 + - v1.3.0 + - v1.2.1 + - v1.2.0 + - v1.10.0 + - v1.1.1 + - v1.1.0 + - v1.0.0 + - v0.5.0 + - v0.4.0 + - v0.3.0 + - v0.2.1 + - v0.2.0 + - v0.1.1 diff --git a/registry/bufbuild/knit-ts/.dockerignore b/registry/bufbuild/knit-ts/.dockerignore new file mode 100644 index 0000000..771bbba --- /dev/null +++ b/registry/bufbuild/knit-ts/.dockerignore @@ -0,0 +1,3 @@ +* +!Dockerfile +!package*.json diff --git a/registry/bufbuild/knit-ts/Dockerfile b/registry/bufbuild/knit-ts/Dockerfile new file mode 100644 index 0000000..00d28bd --- /dev/null +++ b/registry/bufbuild/knit-ts/Dockerfile @@ -0,0 +1,19 @@ +# syntax=docker/dockerfile:1.7 +FROM node:20.11.1-alpine3.19 AS build +ARG VERSION +WORKDIR /app +COPY --link package*.json . +RUN npm ci \ + && ./node_modules/.bin/esbuild ./node_modules/.bin/protoc-gen-knit-ts --bundle --external:typescript --platform=node --outfile=protoc-gen-knit-ts.js + +FROM gcr.io/distroless/nodejs20-debian12:latest@sha256:e0edb89411e3f93863e9bd97c15cddcbac968d7a16b7a82af6ae6b264da65de9 AS node +ARG VERSION + +FROM gcr.io/distroless/cc-debian12:latest@sha256:e6ae66a5a343d7112167f9117c4e630cfffcd80db44e44302759ec13ddd2d22b +ARG VERSION +COPY --link --from=node --chmod=0755 /nodejs/bin/node /nodejs/bin/node +COPY --link --from=build --chmod=0755 /app/protoc-gen-knit-ts.js /app/protoc-gen-knit-ts.js +COPY --link --from=build /app/node_modules/typescript /app/node_modules/typescript +USER nobody +ENTRYPOINT ["/nodejs/bin/node"] +CMD [ "/app/protoc-gen-knit-ts.js" ] diff --git a/registry/bufbuild/knit-ts/package-lock.json b/registry/bufbuild/knit-ts/package-lock.json new file mode 100644 index 0000000..337dcdd --- /dev/null +++ b/registry/bufbuild/knit-ts/package-lock.json @@ -0,0 +1,502 @@ +{ + "name": "plugins-bufbuild-knit-ts", + "version": "0.0.7", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "plugins-bufbuild-knit-ts", + "version": "0.0.7", + "dependencies": { + "@bufbuild/protoc-gen-knit-ts": "0.0.7" + }, + "devDependencies": { + "esbuild": "0.20.2" + } + }, + "node_modules/@bufbuild/protobuf": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-1.7.2.tgz", + "integrity": "sha512-i5GE2Dk5ekdlK1TR7SugY4LWRrKSfb5T1Qn4unpIMbfxoeGKERKQ59HG3iYewacGD10SR7UzevfPnh6my4tNmQ==" + }, + "node_modules/@bufbuild/protoc-gen-knit-ts": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/@bufbuild/protoc-gen-knit-ts/-/protoc-gen-knit-ts-0.0.7.tgz", + "integrity": "sha512-zLD3E4UZ+ibmAdBBiFboz79i+2EU6FETl46yWW6Uh/7iF7bD4868D37lyTY+1NKb34i5ZMTekLL+z8iAqeYPiQ==", + "dependencies": { + "@bufbuild/protoplugin": "^1.5.0" + }, + "bin": { + "protoc-gen-knit-ts": "bin/protoc-gen-knit-ts" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@bufbuild/protobuf": "^1.5.0" + }, + "peerDependenciesMeta": { + "@bufbuild/protobuf": { + "optional": true + } + } + }, + "node_modules/@bufbuild/protoplugin": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@bufbuild/protoplugin/-/protoplugin-1.7.2.tgz", + "integrity": "sha512-N3QtO8XWD4F4alMtASWtxBw6BWXp4aLz7rPBXH4KTULdjpUHnq46g15TsrG0/8szZw6pIklTO3lFe14dl6ZYdA==", + "dependencies": { + "@bufbuild/protobuf": "1.7.2", + "@typescript/vfs": "^1.4.0", + "typescript": "4.5.2" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", + "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", + "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", + "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", + "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", + "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", + "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", + "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", + "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", + "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", + "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", + "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", + "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", + "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", + "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", + "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", + "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", + "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", + "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", + "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", + "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", + "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", + "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", + "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@typescript/vfs": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.5.0.tgz", + "integrity": "sha512-AJS307bPgbsZZ9ggCT3wwpg3VbTKMFNHfaY/uF0ahSkYYrPF2dSSKDNIDIQAHm9qJqbLvCsSJH7yN4Vs/CsMMg==", + "dependencies": { + "debug": "^4.1.1" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/esbuild": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", + "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.20.2", + "@esbuild/android-arm": "0.20.2", + "@esbuild/android-arm64": "0.20.2", + "@esbuild/android-x64": "0.20.2", + "@esbuild/darwin-arm64": "0.20.2", + "@esbuild/darwin-x64": "0.20.2", + "@esbuild/freebsd-arm64": "0.20.2", + "@esbuild/freebsd-x64": "0.20.2", + "@esbuild/linux-arm": "0.20.2", + "@esbuild/linux-arm64": "0.20.2", + "@esbuild/linux-ia32": "0.20.2", + "@esbuild/linux-loong64": "0.20.2", + "@esbuild/linux-mips64el": "0.20.2", + "@esbuild/linux-ppc64": "0.20.2", + "@esbuild/linux-riscv64": "0.20.2", + "@esbuild/linux-s390x": "0.20.2", + "@esbuild/linux-x64": "0.20.2", + "@esbuild/netbsd-x64": "0.20.2", + "@esbuild/openbsd-x64": "0.20.2", + "@esbuild/sunos-x64": "0.20.2", + "@esbuild/win32-arm64": "0.20.2", + "@esbuild/win32-ia32": "0.20.2", + "@esbuild/win32-x64": "0.20.2" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/typescript": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz", + "integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + } + } +} diff --git a/registry/bufbuild/knit-ts/package.json b/registry/bufbuild/knit-ts/package.json new file mode 100644 index 0000000..72bc6cd --- /dev/null +++ b/registry/bufbuild/knit-ts/package.json @@ -0,0 +1,10 @@ +{ + "name": "plugins-bufbuild-knit-ts", + "version": "0.0.7", + "dependencies": { + "@bufbuild/protoc-gen-knit-ts": "0.0.7" + }, + "devDependencies": { + "esbuild": "0.20.2" + } +} diff --git a/registry/bufbuild/knit-ts/plugin.yaml b/registry/bufbuild/knit-ts/plugin.yaml new file mode 100644 index 0000000..6feac2a --- /dev/null +++ b/registry/bufbuild/knit-ts/plugin.yaml @@ -0,0 +1,9 @@ +binary: nodejs/bin/node +versions: + - v0.0.7 + - v0.0.6 + - v0.0.5 + - v0.0.4 + - v0.0.3 + - v0.0.2 + - v0.0.1 diff --git a/registry/bufbuild/protoschema-bigquery/.dockerignore b/registry/bufbuild/protoschema-bigquery/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/bufbuild/protoschema-bigquery/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/bufbuild/protoschema-bigquery/Dockerfile b/registry/bufbuild/protoschema-bigquery/Dockerfile new file mode 100644 index 0000000..e85c930 --- /dev/null +++ b/registry/bufbuild/protoschema-bigquery/Dockerfile @@ -0,0 +1,16 @@ +# syntax=docker/dockerfile:1.19 +FROM golang:1.26-alpine3.22 AS build + +ARG VERSION +ARG GO_MODULE +ARG BINARY_NAME + +RUN apk add upx --no-cache +RUN --mount=type=cache,target=/go/pkg/mod \ + CGO_ENABLED=0 go install -ldflags "-s -w" -trimpath ${GO_MODULE}@${VERSION} +RUN cp /go/bin/${BINARY_NAME} /plugin 2>/dev/null \ + || cp /go/bin/linux_*/${BINARY_NAME} /plugin \ + && upx --best --lzma /plugin + +FROM scratch +COPY --from=build /plugin /plugin diff --git a/registry/bufbuild/protoschema-bigquery/plugin.yaml b/registry/bufbuild/protoschema-bigquery/plugin.yaml new file mode 100644 index 0000000..2b031cd --- /dev/null +++ b/registry/bufbuild/protoschema-bigquery/plugin.yaml @@ -0,0 +1,8 @@ +binary: protoc-gen-bigquery +build_args: + GO_MODULE: github.com/bufbuild/protoschema-plugins/cmd/protoc-gen-bigquery +versions: + - v0.1.3 + - v0.1.2 + - v0.1.1 + - v0.1.0 diff --git a/registry/bufbuild/protoschema-jsonschema/.dockerignore b/registry/bufbuild/protoschema-jsonschema/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/bufbuild/protoschema-jsonschema/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/bufbuild/protoschema-jsonschema/Dockerfile b/registry/bufbuild/protoschema-jsonschema/Dockerfile new file mode 100644 index 0000000..e85c930 --- /dev/null +++ b/registry/bufbuild/protoschema-jsonschema/Dockerfile @@ -0,0 +1,16 @@ +# syntax=docker/dockerfile:1.19 +FROM golang:1.26-alpine3.22 AS build + +ARG VERSION +ARG GO_MODULE +ARG BINARY_NAME + +RUN apk add upx --no-cache +RUN --mount=type=cache,target=/go/pkg/mod \ + CGO_ENABLED=0 go install -ldflags "-s -w" -trimpath ${GO_MODULE}@${VERSION} +RUN cp /go/bin/${BINARY_NAME} /plugin 2>/dev/null \ + || cp /go/bin/linux_*/${BINARY_NAME} /plugin \ + && upx --best --lzma /plugin + +FROM scratch +COPY --from=build /plugin /plugin diff --git a/registry/bufbuild/protoschema-jsonschema/plugin.yaml b/registry/bufbuild/protoschema-jsonschema/plugin.yaml new file mode 100644 index 0000000..8325e60 --- /dev/null +++ b/registry/bufbuild/protoschema-jsonschema/plugin.yaml @@ -0,0 +1,18 @@ +binary: protoc-gen-jsonschema +build_args: + GO_MODULE: github.com/bufbuild/protoschema-plugins/cmd/protoc-gen-jsonschema +versions: + - v0.6.0 + - v0.5.2 + - v0.5.1 + - v0.5.0 + - v0.4.0 + - v0.3.1 + - v0.3.0 + - v0.2.0 + - v0.1.5 + - v0.1.4 + - v0.1.3 + - v0.1.2 + - v0.1.1 + - v0.1.0 diff --git a/registry/bufbuild/protoschema-pubsub/.dockerignore b/registry/bufbuild/protoschema-pubsub/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/bufbuild/protoschema-pubsub/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/bufbuild/protoschema-pubsub/Dockerfile b/registry/bufbuild/protoschema-pubsub/Dockerfile new file mode 100644 index 0000000..e85c930 --- /dev/null +++ b/registry/bufbuild/protoschema-pubsub/Dockerfile @@ -0,0 +1,16 @@ +# syntax=docker/dockerfile:1.19 +FROM golang:1.26-alpine3.22 AS build + +ARG VERSION +ARG GO_MODULE +ARG BINARY_NAME + +RUN apk add upx --no-cache +RUN --mount=type=cache,target=/go/pkg/mod \ + CGO_ENABLED=0 go install -ldflags "-s -w" -trimpath ${GO_MODULE}@${VERSION} +RUN cp /go/bin/${BINARY_NAME} /plugin 2>/dev/null \ + || cp /go/bin/linux_*/${BINARY_NAME} /plugin \ + && upx --best --lzma /plugin + +FROM scratch +COPY --from=build /plugin /plugin diff --git a/registry/bufbuild/protoschema-pubsub/plugin.yaml b/registry/bufbuild/protoschema-pubsub/plugin.yaml new file mode 100644 index 0000000..d5bf963 --- /dev/null +++ b/registry/bufbuild/protoschema-pubsub/plugin.yaml @@ -0,0 +1,18 @@ +binary: protoc-gen-pubsub +build_args: + GO_MODULE: github.com/bufbuild/protoschema-plugins/cmd/protoc-gen-pubsub +versions: + - v0.6.0 + - v0.5.2 + - v0.5.1 + - v0.5.0 + - v0.4.0 + - v0.3.1 + - v0.3.0 + - v0.2.0 + - v0.1.5 + - v0.1.4 + - v0.1.3 + - v0.1.2 + - v0.1.1 + - v0.1.0 diff --git a/registry/bufbuild/validate-cpp/.dockerignore b/registry/bufbuild/validate-cpp/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/bufbuild/validate-cpp/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/bufbuild/validate-cpp/Dockerfile b/registry/bufbuild/validate-cpp/Dockerfile new file mode 100644 index 0000000..e85c930 --- /dev/null +++ b/registry/bufbuild/validate-cpp/Dockerfile @@ -0,0 +1,16 @@ +# syntax=docker/dockerfile:1.19 +FROM golang:1.26-alpine3.22 AS build + +ARG VERSION +ARG GO_MODULE +ARG BINARY_NAME + +RUN apk add upx --no-cache +RUN --mount=type=cache,target=/go/pkg/mod \ + CGO_ENABLED=0 go install -ldflags "-s -w" -trimpath ${GO_MODULE}@${VERSION} +RUN cp /go/bin/${BINARY_NAME} /plugin 2>/dev/null \ + || cp /go/bin/linux_*/${BINARY_NAME} /plugin \ + && upx --best --lzma /plugin + +FROM scratch +COPY --from=build /plugin /plugin diff --git a/registry/bufbuild/validate-cpp/plugin.yaml b/registry/bufbuild/validate-cpp/plugin.yaml new file mode 100644 index 0000000..ab93ffb --- /dev/null +++ b/registry/bufbuild/validate-cpp/plugin.yaml @@ -0,0 +1,17 @@ +binary: protoc-gen-validate-cpp +build_args: + GO_MODULE: github.com/envoyproxy/protoc-gen-validate/cmd/protoc-gen-validate-cpp +versions: + - v1.3.3 + - v1.3.0 + - v1.2.1 + - v1.1.0 + - v1.0.4 + - v1.0.3 + - v1.0.2 + - v1.0.1 + - v1.0.0 + - v0.9.1 + - v0.9.0 + - v0.10.1 + - v0.10.0 diff --git a/registry/bufbuild/validate-go/.dockerignore b/registry/bufbuild/validate-go/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/bufbuild/validate-go/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/bufbuild/validate-go/Dockerfile b/registry/bufbuild/validate-go/Dockerfile new file mode 100644 index 0000000..e85c930 --- /dev/null +++ b/registry/bufbuild/validate-go/Dockerfile @@ -0,0 +1,16 @@ +# syntax=docker/dockerfile:1.19 +FROM golang:1.26-alpine3.22 AS build + +ARG VERSION +ARG GO_MODULE +ARG BINARY_NAME + +RUN apk add upx --no-cache +RUN --mount=type=cache,target=/go/pkg/mod \ + CGO_ENABLED=0 go install -ldflags "-s -w" -trimpath ${GO_MODULE}@${VERSION} +RUN cp /go/bin/${BINARY_NAME} /plugin 2>/dev/null \ + || cp /go/bin/linux_*/${BINARY_NAME} /plugin \ + && upx --best --lzma /plugin + +FROM scratch +COPY --from=build /plugin /plugin diff --git a/registry/bufbuild/validate-go/plugin.yaml b/registry/bufbuild/validate-go/plugin.yaml new file mode 100644 index 0000000..e853b5a --- /dev/null +++ b/registry/bufbuild/validate-go/plugin.yaml @@ -0,0 +1,17 @@ +binary: protoc-gen-validate-go +build_args: + GO_MODULE: github.com/envoyproxy/protoc-gen-validate/cmd/protoc-gen-validate-go +versions: + - v1.3.3 + - v1.3.0 + - v1.2.1 + - v1.1.0 + - v1.0.4 + - v1.0.3 + - v1.0.2 + - v1.0.1 + - v1.0.0 + - v0.9.1 + - v0.9.0 + - v0.10.1 + - v0.10.0 diff --git a/registry/bufbuild/validate-java/.dockerignore b/registry/bufbuild/validate-java/.dockerignore new file mode 100644 index 0000000..7ff6309 --- /dev/null +++ b/registry/bufbuild/validate-java/.dockerignore @@ -0,0 +1,3 @@ +* +!Dockerfile +!pom.xml diff --git a/registry/bufbuild/validate-java/Dockerfile b/registry/bufbuild/validate-java/Dockerfile new file mode 100644 index 0000000..e85c930 --- /dev/null +++ b/registry/bufbuild/validate-java/Dockerfile @@ -0,0 +1,16 @@ +# syntax=docker/dockerfile:1.19 +FROM golang:1.26-alpine3.22 AS build + +ARG VERSION +ARG GO_MODULE +ARG BINARY_NAME + +RUN apk add upx --no-cache +RUN --mount=type=cache,target=/go/pkg/mod \ + CGO_ENABLED=0 go install -ldflags "-s -w" -trimpath ${GO_MODULE}@${VERSION} +RUN cp /go/bin/${BINARY_NAME} /plugin 2>/dev/null \ + || cp /go/bin/linux_*/${BINARY_NAME} /plugin \ + && upx --best --lzma /plugin + +FROM scratch +COPY --from=build /plugin /plugin diff --git a/registry/bufbuild/validate-java/plugin.yaml b/registry/bufbuild/validate-java/plugin.yaml new file mode 100644 index 0000000..7539c02 --- /dev/null +++ b/registry/bufbuild/validate-java/plugin.yaml @@ -0,0 +1,17 @@ +binary: protoc-gen-validate-java +build_args: + GO_MODULE: github.com/envoyproxy/protoc-gen-validate/cmd/protoc-gen-validate-java +versions: + - v1.3.3 + - v1.3.0 + - v1.2.1 + - v1.1.0 + - v1.0.4 + - v1.0.3 + - v1.0.2 + - v1.0.1 + - v1.0.0 + - v0.9.1 + - v0.9.0 + - v0.10.1 + - v0.10.0 diff --git a/registry/bufbuild/validate-java/pom.xml b/registry/bufbuild/validate-java/pom.xml new file mode 100644 index 0000000..288dddd --- /dev/null +++ b/registry/bufbuild/validate-java/pom.xml @@ -0,0 +1,29 @@ + + 4.0.0 + temp + temp + 1.0 + + + build.buf.protoc-gen-validate + pgv-java-stub + 1.3.3 + + + com.google.protobuf + protobuf-java + 4.33.5 + + + + com.google.protobuf + protobuf-javalite + 4.33.5 + + + build.buf + protobuf-javalite + 4.33.5 + + + diff --git a/registry/community/chrusty-jsonschema/.dockerignore b/registry/community/chrusty-jsonschema/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/community/chrusty-jsonschema/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/community/chrusty-jsonschema/Dockerfile b/registry/community/chrusty-jsonschema/Dockerfile new file mode 100644 index 0000000..1f89773 --- /dev/null +++ b/registry/community/chrusty-jsonschema/Dockerfile @@ -0,0 +1,13 @@ +# syntax=docker/dockerfile:1.4 +FROM golang:1.20.3-bullseye AS build +ARG VERSION +RUN --mount=type=cache,target=/go/pkg/mod \ + CGO_ENABLED=0 \ + go install -ldflags "-s -w" -trimpath github.com/chrusty/protoc-gen-jsonschema/cmd/protoc-gen-jsonschema@${VERSION#v} + +FROM scratch +ARG VERSION +COPY --from=build --link --chown=root:root /etc/passwd /etc/passwd +COPY --from=build --link --chown=root:root /go/bin/protoc-gen-jsonschema / +USER nobody +ENTRYPOINT [ "/protoc-gen-jsonschema" ] diff --git a/registry/community/chrusty-jsonschema/plugin.yaml b/registry/community/chrusty-jsonschema/plugin.yaml new file mode 100644 index 0000000..920a749 --- /dev/null +++ b/registry/community/chrusty-jsonschema/plugin.yaml @@ -0,0 +1,6 @@ +binary: protoc-gen-jsonschema +versions: + - v1.4.1 + - v1.4.0 + - v1.3.9 + - v1.3.10 diff --git a/registry/community/danielgtaylor-betterproto/.dockerignore b/registry/community/danielgtaylor-betterproto/.dockerignore new file mode 100644 index 0000000..c8a679b --- /dev/null +++ b/registry/community/danielgtaylor-betterproto/.dockerignore @@ -0,0 +1,3 @@ +* +!Dockerfile +!requirements.txt diff --git a/registry/community/danielgtaylor-betterproto/Dockerfile b/registry/community/danielgtaylor-betterproto/Dockerfile new file mode 100644 index 0000000..74c82bc --- /dev/null +++ b/registry/community/danielgtaylor-betterproto/Dockerfile @@ -0,0 +1,18 @@ +# syntax=docker/dockerfile:1.4 +FROM python:3.11.4-alpine3.18 AS build +ARG VERSION +WORKDIR /app +RUN python -mvenv /app +ADD /requirements.txt requirements.txt +RUN source ./bin/activate \ + && pip install --no-cache-dir -r requirements.txt \ + && pip uninstall --yes pip setuptools \ + && rm -f requirements.txt bin/activate.fish bin/activate.csh bin/Activate.ps1 + +FROM python:3.11.4-alpine3.18 +ARG VERSION +COPY --from=build --link /app /app +USER nobody +# Plugin uses os.makedirs - needs to run in a directory it can write to +WORKDIR /tmp +ENTRYPOINT [ "/app/bin/protoc-gen-python_betterproto" ] diff --git a/registry/community/danielgtaylor-betterproto/plugin.yaml b/registry/community/danielgtaylor-betterproto/plugin.yaml new file mode 100644 index 0000000..7be2213 --- /dev/null +++ b/registry/community/danielgtaylor-betterproto/plugin.yaml @@ -0,0 +1,3 @@ +binary: app/bin/protoc-gen-python_betterproto +versions: + - v1.2.5 diff --git a/registry/community/danielgtaylor-betterproto/requirements.txt b/registry/community/danielgtaylor-betterproto/requirements.txt new file mode 100644 index 0000000..fde8e3f --- /dev/null +++ b/registry/community/danielgtaylor-betterproto/requirements.txt @@ -0,0 +1,16 @@ +betterproto==1.2.5 +black==24.3.0 +click==8.1.3 +grpclib==0.4.4 +h2==4.1.0 +hpack==4.0.0 +hyperframe==6.0.1 +Jinja2==3.1.6 +MarkupSafe==2.1.3 +multidict==6.0.4 +mypy-extensions==1.0.0 +packaging==23.1 +pathspec==0.11.1 +platformdirs==3.5.3 +protobuf==4.23.2 +stringcase==1.2.0 diff --git a/registry/community/google-gnostic-openapi/.dockerignore b/registry/community/google-gnostic-openapi/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/community/google-gnostic-openapi/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/community/google-gnostic-openapi/Dockerfile b/registry/community/google-gnostic-openapi/Dockerfile new file mode 100644 index 0000000..e85c930 --- /dev/null +++ b/registry/community/google-gnostic-openapi/Dockerfile @@ -0,0 +1,16 @@ +# syntax=docker/dockerfile:1.19 +FROM golang:1.26-alpine3.22 AS build + +ARG VERSION +ARG GO_MODULE +ARG BINARY_NAME + +RUN apk add upx --no-cache +RUN --mount=type=cache,target=/go/pkg/mod \ + CGO_ENABLED=0 go install -ldflags "-s -w" -trimpath ${GO_MODULE}@${VERSION} +RUN cp /go/bin/${BINARY_NAME} /plugin 2>/dev/null \ + || cp /go/bin/linux_*/${BINARY_NAME} /plugin \ + && upx --best --lzma /plugin + +FROM scratch +COPY --from=build /plugin /plugin diff --git a/registry/community/google-gnostic-openapi/plugin.yaml b/registry/community/google-gnostic-openapi/plugin.yaml new file mode 100644 index 0000000..95c3b28 --- /dev/null +++ b/registry/community/google-gnostic-openapi/plugin.yaml @@ -0,0 +1,6 @@ +binary: protoc-gen-openapi +build_args: + GO_MODULE: github.com/google/gnostic/cmd/protoc-gen-openapi +versions: + - v0.7.1 + - v0.7.0 diff --git a/registry/community/mercari-grpc-federation/.dockerignore b/registry/community/mercari-grpc-federation/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/community/mercari-grpc-federation/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/community/mercari-grpc-federation/Dockerfile b/registry/community/mercari-grpc-federation/Dockerfile new file mode 100644 index 0000000..e85c930 --- /dev/null +++ b/registry/community/mercari-grpc-federation/Dockerfile @@ -0,0 +1,16 @@ +# syntax=docker/dockerfile:1.19 +FROM golang:1.26-alpine3.22 AS build + +ARG VERSION +ARG GO_MODULE +ARG BINARY_NAME + +RUN apk add upx --no-cache +RUN --mount=type=cache,target=/go/pkg/mod \ + CGO_ENABLED=0 go install -ldflags "-s -w" -trimpath ${GO_MODULE}@${VERSION} +RUN cp /go/bin/${BINARY_NAME} /plugin 2>/dev/null \ + || cp /go/bin/linux_*/${BINARY_NAME} /plugin \ + && upx --best --lzma /plugin + +FROM scratch +COPY --from=build /plugin /plugin diff --git a/registry/community/mercari-grpc-federation/plugin.yaml b/registry/community/mercari-grpc-federation/plugin.yaml new file mode 100644 index 0000000..266cbc9 --- /dev/null +++ b/registry/community/mercari-grpc-federation/plugin.yaml @@ -0,0 +1,59 @@ +binary: protoc-gen-grpc-federation +build_args: + GO_MODULE: github.com/mercari/grpc-federation/cmd/protoc-gen-grpc-federation +versions: + - v1.9.9 + - v1.9.8 + - v1.9.7 + - v1.9.5 + - v1.9.3 + - v1.9.1 + - v1.8.3 + - v1.8.1 + - v1.8.0 + - v1.7.3 + - v1.7.1 + - v1.7.0 + - v1.6.0 + - v1.5.1 + - v1.5.0 + - v1.4.2 + - v1.4.1 + - v1.3.1 + - v1.26.0 + - v1.24.2 + - v1.24.1 + - v1.2.1 + - v1.1.4 + - v1.1.2 + - v1.1.0 + - v1.0.4 + - v1.0.3 + - v1.0.2 + - v1.0.1 + - v1.0.0 + - v0.9.2 + - v0.19.1 + - v0.19.0 + - v0.18.0 + - v0.17.6 + - v0.17.3 + - v0.17.2 + - v0.16.0 + - v0.15.2 + - v0.15.0 + - v0.13.8 + - v0.13.7 + - v0.13.6 + - v0.13.5 + - v0.13.4 + - v0.13.3 + - v0.13.10 + - v0.13.1 + - v0.13.0 + - v0.12.4 + - v0.12.2 + - v0.12.1 + - v0.12.0 + - v0.11.0 + - v0.10.0 diff --git a/registry/community/mfridman-go-json/.dockerignore b/registry/community/mfridman-go-json/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/community/mfridman-go-json/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/community/mfridman-go-json/Dockerfile b/registry/community/mfridman-go-json/Dockerfile new file mode 100644 index 0000000..e85c930 --- /dev/null +++ b/registry/community/mfridman-go-json/Dockerfile @@ -0,0 +1,16 @@ +# syntax=docker/dockerfile:1.19 +FROM golang:1.26-alpine3.22 AS build + +ARG VERSION +ARG GO_MODULE +ARG BINARY_NAME + +RUN apk add upx --no-cache +RUN --mount=type=cache,target=/go/pkg/mod \ + CGO_ENABLED=0 go install -ldflags "-s -w" -trimpath ${GO_MODULE}@${VERSION} +RUN cp /go/bin/${BINARY_NAME} /plugin 2>/dev/null \ + || cp /go/bin/linux_*/${BINARY_NAME} /plugin \ + && upx --best --lzma /plugin + +FROM scratch +COPY --from=build /plugin /plugin diff --git a/registry/community/mfridman-go-json/plugin.yaml b/registry/community/mfridman-go-json/plugin.yaml new file mode 100644 index 0000000..4514025 --- /dev/null +++ b/registry/community/mfridman-go-json/plugin.yaml @@ -0,0 +1,11 @@ +binary: protoc-gen-go-json +build_args: + GO_MODULE: github.com/mfridman/protoc-gen-go-json +versions: + - v1.6.0 + - v1.5.0 + - v1.4.1 + - v1.4.0 + - v1.3.1 + - v1.3.0 + - v1.2.0 diff --git a/registry/community/mitchellh-go-json/.dockerignore b/registry/community/mitchellh-go-json/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/community/mitchellh-go-json/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/community/mitchellh-go-json/Dockerfile b/registry/community/mitchellh-go-json/Dockerfile new file mode 100644 index 0000000..e85c930 --- /dev/null +++ b/registry/community/mitchellh-go-json/Dockerfile @@ -0,0 +1,16 @@ +# syntax=docker/dockerfile:1.19 +FROM golang:1.26-alpine3.22 AS build + +ARG VERSION +ARG GO_MODULE +ARG BINARY_NAME + +RUN apk add upx --no-cache +RUN --mount=type=cache,target=/go/pkg/mod \ + CGO_ENABLED=0 go install -ldflags "-s -w" -trimpath ${GO_MODULE}@${VERSION} +RUN cp /go/bin/${BINARY_NAME} /plugin 2>/dev/null \ + || cp /go/bin/linux_*/${BINARY_NAME} /plugin \ + && upx --best --lzma /plugin + +FROM scratch +COPY --from=build /plugin /plugin diff --git a/registry/community/mitchellh-go-json/plugin.yaml b/registry/community/mitchellh-go-json/plugin.yaml new file mode 100644 index 0000000..7e505be --- /dev/null +++ b/registry/community/mitchellh-go-json/plugin.yaml @@ -0,0 +1,5 @@ +binary: protoc-gen-go-json +build_args: + GO_MODULE: github.com/mitchellh/protoc-gen-go-json +versions: + - v1.1.0 diff --git a/registry/community/nanopb/.dockerignore b/registry/community/nanopb/.dockerignore new file mode 100644 index 0000000..c8a679b --- /dev/null +++ b/registry/community/nanopb/.dockerignore @@ -0,0 +1,3 @@ +* +!Dockerfile +!requirements.txt diff --git a/registry/community/nanopb/Dockerfile b/registry/community/nanopb/Dockerfile new file mode 100644 index 0000000..e862fe9 --- /dev/null +++ b/registry/community/nanopb/Dockerfile @@ -0,0 +1,21 @@ +# syntax=docker/dockerfile:1.10 +FROM python:3.11.10-alpine3.20 AS build +ARG VERSION +WORKDIR /app +RUN python -mvenv /app +ADD /requirements.txt requirements.txt +RUN source ./bin/activate \ + && pip install --no-cache-dir -r requirements.txt \ + && pip uninstall --yes pip \ + && rm -f requirements.txt bin/activate.fish bin/activate.csh bin/Activate.ps1 \ + && ln -sf /usr/bin/python /app/bin/python + +FROM gcr.io/distroless/python3-debian12:latest@sha256:55283e240bb5bd506e711a395092b61acf1fb14e63977ee24336285dd9e68fb8 AS base +ARG VERSION + +FROM scratch +ARG VERSION +COPY --link --from=base / / +COPY --link --from=build --chmod=0755 /app /app +USER nobody +ENTRYPOINT ["/app/bin/protoc-gen-nanopb"] diff --git a/registry/community/nanopb/plugin.yaml b/registry/community/nanopb/plugin.yaml new file mode 100644 index 0000000..f91ce56 --- /dev/null +++ b/registry/community/nanopb/plugin.yaml @@ -0,0 +1,4 @@ +binary: app/bin/protoc-gen-nanopb +versions: + - v0.4.9 + - v0.4.8 diff --git a/registry/community/nanopb/requirements.txt b/registry/community/nanopb/requirements.txt new file mode 100644 index 0000000..34f5468 --- /dev/null +++ b/registry/community/nanopb/requirements.txt @@ -0,0 +1 @@ +nanopb==0.4.9 diff --git a/registry/community/neoeinstein-prost-crate/.dockerignore b/registry/community/neoeinstein-prost-crate/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/community/neoeinstein-prost-crate/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/community/neoeinstein-prost-crate/Dockerfile b/registry/community/neoeinstein-prost-crate/Dockerfile new file mode 100644 index 0000000..3bbac77 --- /dev/null +++ b/registry/community/neoeinstein-prost-crate/Dockerfile @@ -0,0 +1,13 @@ +# syntax=docker/dockerfile:1.19 +FROM rust:1.91-alpine3.22 AS build + +ARG VERSION +ARG CRATE_NAME +ARG BINARY_NAME + +RUN apk add musl-dev upx --no-cache +RUN cargo install ${CRATE_NAME} --version ${VERSION#v} --root /out \ + && upx --best --lzma /out/bin/${BINARY_NAME} + +FROM scratch +COPY --from=build /out/bin/${BINARY_NAME} /plugin diff --git a/registry/community/neoeinstein-prost-crate/plugin.yaml b/registry/community/neoeinstein-prost-crate/plugin.yaml new file mode 100644 index 0000000..95d12fa --- /dev/null +++ b/registry/community/neoeinstein-prost-crate/plugin.yaml @@ -0,0 +1,8 @@ +binary: protoc-gen-prost-crate +build_args: + CRATE_NAME: protoc-gen-prost-crate +versions: + - v0.5.0 + - v0.4.1 + - v0.4.0 + - v0.3.1 diff --git a/registry/community/neoeinstein-prost-serde/.dockerignore b/registry/community/neoeinstein-prost-serde/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/community/neoeinstein-prost-serde/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/community/neoeinstein-prost-serde/Dockerfile b/registry/community/neoeinstein-prost-serde/Dockerfile new file mode 100644 index 0000000..3bbac77 --- /dev/null +++ b/registry/community/neoeinstein-prost-serde/Dockerfile @@ -0,0 +1,13 @@ +# syntax=docker/dockerfile:1.19 +FROM rust:1.91-alpine3.22 AS build + +ARG VERSION +ARG CRATE_NAME +ARG BINARY_NAME + +RUN apk add musl-dev upx --no-cache +RUN cargo install ${CRATE_NAME} --version ${VERSION#v} --root /out \ + && upx --best --lzma /out/bin/${BINARY_NAME} + +FROM scratch +COPY --from=build /out/bin/${BINARY_NAME} /plugin diff --git a/registry/community/neoeinstein-prost-serde/plugin.yaml b/registry/community/neoeinstein-prost-serde/plugin.yaml new file mode 100644 index 0000000..1193136 --- /dev/null +++ b/registry/community/neoeinstein-prost-serde/plugin.yaml @@ -0,0 +1,9 @@ +binary: protoc-gen-prost-serde +build_args: + CRATE_NAME: protoc-gen-prost-serde +versions: + - v0.4.0 + - v0.3.1 + - v0.3.0 + - v0.2.3 + - v0.2.2 diff --git a/registry/community/neoeinstein-prost/.dockerignore b/registry/community/neoeinstein-prost/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/community/neoeinstein-prost/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/community/neoeinstein-prost/Dockerfile b/registry/community/neoeinstein-prost/Dockerfile new file mode 100644 index 0000000..3bbac77 --- /dev/null +++ b/registry/community/neoeinstein-prost/Dockerfile @@ -0,0 +1,13 @@ +# syntax=docker/dockerfile:1.19 +FROM rust:1.91-alpine3.22 AS build + +ARG VERSION +ARG CRATE_NAME +ARG BINARY_NAME + +RUN apk add musl-dev upx --no-cache +RUN cargo install ${CRATE_NAME} --version ${VERSION#v} --root /out \ + && upx --best --lzma /out/bin/${BINARY_NAME} + +FROM scratch +COPY --from=build /out/bin/${BINARY_NAME} /plugin diff --git a/registry/community/neoeinstein-prost/plugin.yaml b/registry/community/neoeinstein-prost/plugin.yaml new file mode 100644 index 0000000..3c69e1c --- /dev/null +++ b/registry/community/neoeinstein-prost/plugin.yaml @@ -0,0 +1,11 @@ +binary: protoc-gen-prost +build_args: + CRATE_NAME: protoc-gen-prost +versions: + - v0.5.0 + - v0.4.0 + - v0.3.1 + - v0.3.0 + - v0.2.3 + - v0.2.2 + - v0.2.1 diff --git a/registry/community/neoeinstein-tonic/.dockerignore b/registry/community/neoeinstein-tonic/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/community/neoeinstein-tonic/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/community/neoeinstein-tonic/Dockerfile b/registry/community/neoeinstein-tonic/Dockerfile new file mode 100644 index 0000000..3bbac77 --- /dev/null +++ b/registry/community/neoeinstein-tonic/Dockerfile @@ -0,0 +1,13 @@ +# syntax=docker/dockerfile:1.19 +FROM rust:1.91-alpine3.22 AS build + +ARG VERSION +ARG CRATE_NAME +ARG BINARY_NAME + +RUN apk add musl-dev upx --no-cache +RUN cargo install ${CRATE_NAME} --version ${VERSION#v} --root /out \ + && upx --best --lzma /out/bin/${BINARY_NAME} + +FROM scratch +COPY --from=build /out/bin/${BINARY_NAME} /plugin diff --git a/registry/community/neoeinstein-tonic/plugin.yaml b/registry/community/neoeinstein-tonic/plugin.yaml new file mode 100644 index 0000000..95843f1 --- /dev/null +++ b/registry/community/neoeinstein-tonic/plugin.yaml @@ -0,0 +1,9 @@ +binary: protoc-gen-tonic +build_args: + CRATE_NAME: protoc-gen-tonic +versions: + - v0.5.0 + - v0.4.1 + - v0.4.0 + - v0.3.0 + - v0.2.2 diff --git a/registry/community/nipunn1313-mypy-grpc/.dockerignore b/registry/community/nipunn1313-mypy-grpc/.dockerignore new file mode 100644 index 0000000..c8a679b --- /dev/null +++ b/registry/community/nipunn1313-mypy-grpc/.dockerignore @@ -0,0 +1,3 @@ +* +!Dockerfile +!requirements.txt diff --git a/registry/community/nipunn1313-mypy-grpc/Dockerfile b/registry/community/nipunn1313-mypy-grpc/Dockerfile new file mode 100644 index 0000000..55a82db --- /dev/null +++ b/registry/community/nipunn1313-mypy-grpc/Dockerfile @@ -0,0 +1,22 @@ +# syntax=docker/dockerfile:1.23 +FROM python:3.13.13-trixie@sha256:d52f7ef162003e17330f0dd9c3e0b4ac6775b39ea988be0c23de6d1461191569 AS build +ARG VERSION +WORKDIR /app +RUN python -mvenv /app +ADD /requirements.txt requirements.txt +RUN . ./bin/activate \ + && pip install --no-cache-dir -r requirements.txt \ + && pip uninstall --yes pip setuptools \ + && rm -f requirements.txt bin/activate.fish bin/activate.csh bin/Activate.ps1 \ + && rm -f bin/protoc-gen-mypy \ + && ln -sf /usr/bin/python /app/bin/python + +FROM gcr.io/distroless/python3-debian13:latest@sha256:ed3a4beb46f8f8baac068743ba1b1f95ea3f793422129cf6dd23967f779b6018 AS base +ARG VERSION + +FROM scratch +ARG VERSION +COPY --link --from=base / / +COPY --link --from=build /app /app +USER nobody +ENTRYPOINT [ "/app/bin/protoc-gen-mypy_grpc" ] diff --git a/registry/community/nipunn1313-mypy-grpc/plugin.yaml b/registry/community/nipunn1313-mypy-grpc/plugin.yaml new file mode 100644 index 0000000..b10dfdf --- /dev/null +++ b/registry/community/nipunn1313-mypy-grpc/plugin.yaml @@ -0,0 +1,9 @@ +binary: app/bin/protoc-gen-mypy_grpc +versions: + - v5.1.0 + - v5.0.0 + - v4.0.0 + - v3.7.0 + - v3.6.0 + - v3.5.0 + - v3.4.0 diff --git a/registry/community/nipunn1313-mypy-grpc/requirements.txt b/registry/community/nipunn1313-mypy-grpc/requirements.txt new file mode 100644 index 0000000..2cb7ece --- /dev/null +++ b/registry/community/nipunn1313-mypy-grpc/requirements.txt @@ -0,0 +1,3 @@ +mypy-protobuf==5.1.0 +protobuf==6.33.1 +types-protobuf==6.32.1.20251105 diff --git a/registry/community/nipunn1313-mypy/.dockerignore b/registry/community/nipunn1313-mypy/.dockerignore new file mode 100644 index 0000000..c8a679b --- /dev/null +++ b/registry/community/nipunn1313-mypy/.dockerignore @@ -0,0 +1,3 @@ +* +!Dockerfile +!requirements.txt diff --git a/registry/community/nipunn1313-mypy/Dockerfile b/registry/community/nipunn1313-mypy/Dockerfile new file mode 100644 index 0000000..dc9afd3 --- /dev/null +++ b/registry/community/nipunn1313-mypy/Dockerfile @@ -0,0 +1,22 @@ +# syntax=docker/dockerfile:1.23 +FROM python:3.13.13-trixie@sha256:d52f7ef162003e17330f0dd9c3e0b4ac6775b39ea988be0c23de6d1461191569 AS build +ARG VERSION +WORKDIR /app +RUN python -mvenv /app +ADD /requirements.txt requirements.txt +RUN . ./bin/activate \ + && pip install --no-cache-dir -r requirements.txt \ + && pip uninstall --yes pip setuptools \ + && rm -f requirements.txt bin/activate.fish bin/activate.csh bin/Activate.ps1 \ + && rm -f bin/protoc-gen-mypy_grpc \ + && ln -sf /usr/bin/python /app/bin/python + +FROM gcr.io/distroless/python3-debian13:latest@sha256:ed3a4beb46f8f8baac068743ba1b1f95ea3f793422129cf6dd23967f779b6018 AS base +ARG VERSION + +FROM scratch +ARG VERSION +COPY --link --from=base / / +COPY --link --from=build /app /app +USER nobody +ENTRYPOINT [ "/app/bin/protoc-gen-mypy" ] diff --git a/registry/community/nipunn1313-mypy/plugin.yaml b/registry/community/nipunn1313-mypy/plugin.yaml new file mode 100644 index 0000000..626f33a --- /dev/null +++ b/registry/community/nipunn1313-mypy/plugin.yaml @@ -0,0 +1,9 @@ +binary: app/bin/protoc-gen-mypy +versions: + - v5.1.0 + - v5.0.0 + - v4.0.0 + - v3.7.0 + - v3.6.0 + - v3.5.0 + - v3.4.0 diff --git a/registry/community/nipunn1313-mypy/requirements.txt b/registry/community/nipunn1313-mypy/requirements.txt new file mode 100644 index 0000000..2cb7ece --- /dev/null +++ b/registry/community/nipunn1313-mypy/requirements.txt @@ -0,0 +1,3 @@ +mypy-protobuf==5.1.0 +protobuf==6.33.1 +types-protobuf==6.32.1.20251105 diff --git a/registry/community/planetscale-vtprotobuf/.dockerignore b/registry/community/planetscale-vtprotobuf/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/community/planetscale-vtprotobuf/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/community/planetscale-vtprotobuf/Dockerfile b/registry/community/planetscale-vtprotobuf/Dockerfile new file mode 100644 index 0000000..e85c930 --- /dev/null +++ b/registry/community/planetscale-vtprotobuf/Dockerfile @@ -0,0 +1,16 @@ +# syntax=docker/dockerfile:1.19 +FROM golang:1.26-alpine3.22 AS build + +ARG VERSION +ARG GO_MODULE +ARG BINARY_NAME + +RUN apk add upx --no-cache +RUN --mount=type=cache,target=/go/pkg/mod \ + CGO_ENABLED=0 go install -ldflags "-s -w" -trimpath ${GO_MODULE}@${VERSION} +RUN cp /go/bin/${BINARY_NAME} /plugin 2>/dev/null \ + || cp /go/bin/linux_*/${BINARY_NAME} /plugin \ + && upx --best --lzma /plugin + +FROM scratch +COPY --from=build /plugin /plugin diff --git a/registry/community/planetscale-vtprotobuf/plugin.yaml b/registry/community/planetscale-vtprotobuf/plugin.yaml new file mode 100644 index 0000000..fa995ac --- /dev/null +++ b/registry/community/planetscale-vtprotobuf/plugin.yaml @@ -0,0 +1,7 @@ +binary: protoc-gen-go-vtproto +build_args: + GO_MODULE: github.com/planetscale/vtprotobuf/cmd/protoc-gen-go-vtproto +versions: + - v0.6.0 + - v0.5.0 + - v0.4.0 diff --git a/registry/community/protobuf-c/.dockerignore b/registry/community/protobuf-c/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/community/protobuf-c/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/community/protobuf-c/Dockerfile b/registry/community/protobuf-c/Dockerfile new file mode 100644 index 0000000..5e963bb --- /dev/null +++ b/registry/community/protobuf-c/Dockerfile @@ -0,0 +1,26 @@ +# syntax=docker/dockerfile:1.12 + +FROM debian:bookworm-20250317 AS build +ARG VERSION + +RUN apt-get update \ + && apt-get install -y build-essential curl libprotobuf-dev libprotoc-dev pkg-config protobuf-compiler + +WORKDIR /build +RUN curl -fsSL -o /tmp/protobuf-c.tar.gz https://github.com/protobuf-c/protobuf-c/releases/download/${VERSION}/protobuf-c-${VERSION#v}.tar.gz \ + && tar zxf /tmp/protobuf-c.tar.gz \ + && cd protobuf-c-${VERSION#v} \ + && ./configure \ + && make LDFLAGS=-all-static -j$(nproc) \ + && make install \ + && strip /usr/local/bin/protoc-gen-c + +FROM gcr.io/distroless/cc-debian12:latest@sha256:c1cbcec08d39c81adbefb80cabc51cba285465866f7b5ab15ddb2fcae51a1aed AS base +ARG VERSION + +FROM scratch +ARG VERSION +COPY --from=base --link / / +COPY --from=build --link --chmod=0755 --chown=root:root /usr/local/bin/protoc-gen-c . +USER nobody +ENTRYPOINT ["/protoc-gen-c"] diff --git a/registry/community/protobuf-c/plugin.yaml b/registry/community/protobuf-c/plugin.yaml new file mode 100644 index 0000000..b1c0633 --- /dev/null +++ b/registry/community/protobuf-c/plugin.yaml @@ -0,0 +1,5 @@ +binary: protoc-gen-c +versions: + - v1.5.2 + - v1.5.1 + - v1.5.0 diff --git a/registry/community/pseudomuto-doc/.dockerignore b/registry/community/pseudomuto-doc/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/community/pseudomuto-doc/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/community/pseudomuto-doc/Dockerfile b/registry/community/pseudomuto-doc/Dockerfile new file mode 100644 index 0000000..e85c930 --- /dev/null +++ b/registry/community/pseudomuto-doc/Dockerfile @@ -0,0 +1,16 @@ +# syntax=docker/dockerfile:1.19 +FROM golang:1.26-alpine3.22 AS build + +ARG VERSION +ARG GO_MODULE +ARG BINARY_NAME + +RUN apk add upx --no-cache +RUN --mount=type=cache,target=/go/pkg/mod \ + CGO_ENABLED=0 go install -ldflags "-s -w" -trimpath ${GO_MODULE}@${VERSION} +RUN cp /go/bin/${BINARY_NAME} /plugin 2>/dev/null \ + || cp /go/bin/linux_*/${BINARY_NAME} /plugin \ + && upx --best --lzma /plugin + +FROM scratch +COPY --from=build /plugin /plugin diff --git a/registry/community/pseudomuto-doc/plugin.yaml b/registry/community/pseudomuto-doc/plugin.yaml new file mode 100644 index 0000000..68b466c --- /dev/null +++ b/registry/community/pseudomuto-doc/plugin.yaml @@ -0,0 +1,5 @@ +binary: protoc-gen-doc +build_args: + GO_MODULE: github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc +versions: + - v1.5.1 diff --git a/registry/community/roadrunner-server-php-grpc/.dockerignore b/registry/community/roadrunner-server-php-grpc/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/community/roadrunner-server-php-grpc/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/community/roadrunner-server-php-grpc/Dockerfile b/registry/community/roadrunner-server-php-grpc/Dockerfile new file mode 100644 index 0000000..21e8c0e --- /dev/null +++ b/registry/community/roadrunner-server-php-grpc/Dockerfile @@ -0,0 +1,19 @@ +# syntax=docker/dockerfile:1.22 +FROM --platform=$BUILDPLATFORM golang:1.26.1-trixie@sha256:ce3f1c8d3718a306811d8d5e547073b466b15e85bfa7e1b4f0dc45516c95b72d AS build +ARG VERSION + +ARG TARGETOS TARGETARCH +ENV CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH + +RUN git clone --depth=1 --branch ${VERSION} https://github.com/roadrunner-server/grpc.git +RUN --mount=type=cache,target=/go/pkg/mod \ + cd grpc/protoc_plugins/protoc-gen-php-grpc \ + && go install -ldflags="-s -w" -trimpath \ + && mv /go/bin/${GOOS}_${GOARCH}/protoc-gen-php-grpc /go/bin/protoc-gen-php-grpc || true + +FROM scratch +ARG VERSION +COPY --from=build --link --chown=root:root /etc/passwd /etc/passwd +COPY --from=build --link --chown=root:root /go/bin/protoc-gen-php-grpc / +USER nobody +ENTRYPOINT [ "/protoc-gen-php-grpc" ] diff --git a/registry/community/roadrunner-server-php-grpc/plugin.yaml b/registry/community/roadrunner-server-php-grpc/plugin.yaml new file mode 100644 index 0000000..41c6885 --- /dev/null +++ b/registry/community/roadrunner-server-php-grpc/plugin.yaml @@ -0,0 +1,31 @@ +binary: protoc-gen-php-grpc +versions: + - v5.3.0 + - v5.2.3 + - v5.0.2 + - v4.8.6 + - v4.8.5 + - v4.8.4 + - v4.8.3 + - v4.8.2 + - v4.8.1 + - v4.8.0 + - v4.6.7 + - v4.6.6 + - v4.6.5 + - v4.6.4 + - v4.6.3 + - v4.6.2 + - v4.6.1 + - v4.6.0 + - v4.5.8 + - v4.5.7 + - v4.5.6 + - v4.5.5 + - v4.5.4 + - v4.5.3 + - v4.5.2 + - v4.5.1 + - v4.4.1 + - v4.4.0 + - v4.3.0 diff --git a/registry/community/salesforce-reactive-grpc/.dockerignore b/registry/community/salesforce-reactive-grpc/.dockerignore new file mode 100644 index 0000000..7ff6309 --- /dev/null +++ b/registry/community/salesforce-reactive-grpc/.dockerignore @@ -0,0 +1,3 @@ +* +!Dockerfile +!pom.xml diff --git a/registry/community/salesforce-reactive-grpc/Dockerfile b/registry/community/salesforce-reactive-grpc/Dockerfile new file mode 100644 index 0000000..9188708 --- /dev/null +++ b/registry/community/salesforce-reactive-grpc/Dockerfile @@ -0,0 +1,23 @@ +# syntax=docker/dockerfile:1.15 +FROM debian:bookworm-20250610 AS build +ARG VERSION + +WORKDIR /app +RUN apt-get update && apt-get install -y curl +RUN curl -fsSL -o reactor-grpc-protoc.jar https://repo1.maven.org/maven2/com/salesforce/servicelibs/reactor-grpc/${VERSION#v}/reactor-grpc-${VERSION#v}.jar + +FROM gcr.io/distroless/java21-debian12:latest@sha256:7c9a9a362eadadb308d29b9c7fec2b39e5d5aa21d58837176a2cca50bdd06609 AS base +ARG VERSION + +FROM maven:3.9.11-eclipse-temurin-21 AS maven-deps +ARG VERSION +COPY pom.xml /tmp/pom.xml +RUN cd /tmp && mvn -f pom.xml dependency:go-offline + +FROM scratch +ARG VERSION +COPY --from=base --link / / +COPY --from=build --link --chmod=0755 --chown=root:root /app/reactor-grpc-protoc.jar . +COPY --from=maven-deps /root/.m2/repository /maven-repository +USER nobody +ENTRYPOINT [ "/usr/bin/java", "-jar", "/reactor-grpc-protoc.jar"] diff --git a/registry/community/salesforce-reactive-grpc/plugin.yaml b/registry/community/salesforce-reactive-grpc/plugin.yaml new file mode 100644 index 0000000..668906c --- /dev/null +++ b/registry/community/salesforce-reactive-grpc/plugin.yaml @@ -0,0 +1,3 @@ +binary: protoc-gen-salesforce-reactive-grpc +versions: + - v1.2.4 diff --git a/registry/community/salesforce-reactive-grpc/pom.xml b/registry/community/salesforce-reactive-grpc/pom.xml new file mode 100644 index 0000000..4ad7585 --- /dev/null +++ b/registry/community/salesforce-reactive-grpc/pom.xml @@ -0,0 +1,54 @@ + + 4.0.0 + temp + temp + 1.0 + + + com.salesforce.servicelibs + reactor-grpc-stub + 1.2.4 + + + io.projectreactor + reactor-core + 3.5.4 + + + com.google.protobuf + protobuf-java + 4.31.0 + + + io.grpc + grpc-core + 1.73.0 + + + io.grpc + grpc-protobuf + 1.73.0 + + + io.grpc + grpc-stub + 1.73.0 + + + + io.grpc + grpc-protobuf-lite + 1.73.0 + + + com.google.protobuf + protobuf-javalite + 4.31.0 + + + build.buf + protobuf-javalite + 4.31.0 + + + diff --git a/registry/community/scalapb-scala/.dockerignore b/registry/community/scalapb-scala/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/community/scalapb-scala/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/community/scalapb-scala/Dockerfile b/registry/community/scalapb-scala/Dockerfile new file mode 100644 index 0000000..e8d3d2e --- /dev/null +++ b/registry/community/scalapb-scala/Dockerfile @@ -0,0 +1,21 @@ +# syntax=docker/dockerfile:1.17 +FROM debian:bookworm-20250811 AS build +ARG VERSION + +ARG TARGETARCH + +RUN apt-get update \ + && apt-get install -y curl + +#This script embeds the the .class files and is a self contained jvm protoc plugin. See https://scalapb.github.io/docs/scalapbc/#using-scalapb-as-a-proper-protoc-plugin for more details +RUN curl -fsSL -o protoc-gen-scala.jar https://repo1.maven.org/maven2/com/thesamet/scalapb/protoc-gen-scala/${VERSION#v}/protoc-gen-scala-${VERSION#v}-unix.sh + +FROM gcr.io/distroless/java21-debian12:latest@sha256:914d2e4d0aef6afe6167a11de8d87a4bfcd9325f36d1b45c03c04e6f16ba94d8 AS base +ARG VERSION + +FROM scratch +ARG VERSION +COPY --link --from=base / / +COPY --link --from=build /protoc-gen-scala.jar . +USER nobody +ENTRYPOINT ["/usr/bin/java", "-jar", "/protoc-gen-scala.jar"] diff --git a/registry/community/scalapb-scala/plugin.yaml b/registry/community/scalapb-scala/plugin.yaml new file mode 100644 index 0000000..d6b7ddf --- /dev/null +++ b/registry/community/scalapb-scala/plugin.yaml @@ -0,0 +1,10 @@ +binary: protoc-gen-scalapb-scala +versions: + - v0.11.20 + - v0.11.19 + - v0.11.18 + - v0.11.17 + - v0.11.15 + - v0.11.14 + - v0.11.13 + - v0.11.12 diff --git a/registry/community/scalapb-zio-grpc/.dockerignore b/registry/community/scalapb-zio-grpc/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/community/scalapb-zio-grpc/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/community/scalapb-zio-grpc/Dockerfile b/registry/community/scalapb-zio-grpc/Dockerfile new file mode 100644 index 0000000..236977e --- /dev/null +++ b/registry/community/scalapb-zio-grpc/Dockerfile @@ -0,0 +1,21 @@ +# syntax=docker/dockerfile:1.10 +FROM debian:bookworm-20241016 AS build +ARG VERSION + +ARG TARGETARCH + +RUN apt-get update \ + && apt-get install -y curl + +#This script embeds the the .class files and is a self contained jvm protoc plugin. See https://scalapb.github.io/docs/scalapbc/#using-scalapb-as-a-proper-protoc-plugin for more details +RUN curl -fsSL -o protoc-gen-zio.jar https://repo1.maven.org/maven2/com/thesamet/scalapb/zio-grpc/protoc-gen-zio/${VERSION#v}/protoc-gen-zio-${VERSION#v}-unix.sh + +FROM gcr.io/distroless/java17-debian12:latest@sha256:26054428ef0fa1b71d28018e35823060c9e89d4b2f120d8efe1964669f44fccc AS base +ARG VERSION + +FROM scratch +ARG VERSION +COPY --from=base --link / / +COPY --from=build --link /protoc-gen-zio.jar . +USER nobody +ENTRYPOINT ["/usr/bin/java", "-jar", "/protoc-gen-zio.jar"] diff --git a/registry/community/scalapb-zio-grpc/plugin.yaml b/registry/community/scalapb-zio-grpc/plugin.yaml new file mode 100644 index 0000000..8b545e9 --- /dev/null +++ b/registry/community/scalapb-zio-grpc/plugin.yaml @@ -0,0 +1,7 @@ +binary: protoc-gen-scalapb-zio-grpc +versions: + - v0.6.3 + - v0.6.2 + - v0.6.1 + - v0.6.0 + - v0.5.3 diff --git a/registry/community/stephenh-ts-proto/.dockerignore b/registry/community/stephenh-ts-proto/.dockerignore new file mode 100644 index 0000000..771bbba --- /dev/null +++ b/registry/community/stephenh-ts-proto/.dockerignore @@ -0,0 +1,3 @@ +* +!Dockerfile +!package*.json diff --git a/registry/community/stephenh-ts-proto/Dockerfile b/registry/community/stephenh-ts-proto/Dockerfile new file mode 100644 index 0000000..df2bb95 --- /dev/null +++ b/registry/community/stephenh-ts-proto/Dockerfile @@ -0,0 +1,23 @@ +# syntax=docker/dockerfile:1.12 +FROM node:22.14.0-bookworm AS build +ARG VERSION +WORKDIR /app +COPY --link package*.json . +RUN npm ci +RUN sed -i -e 's|/usr/bin/env node|/nodejs/bin/node|g' /app/node_modules/ts-proto/protoc-gen-ts_proto \ + && rm -f /app/node_modules/dprint-node/*darwin*.node /app/node_modules/dprint-node/*win32*.node + +FROM gcr.io/distroless/nodejs22-debian12:latest@sha256:881157f8399d3ab71c54068f148c25296f7f9bee6d36279febad5a6f46f41c2b AS node +ARG VERSION + +FROM gcr.io/distroless/cc-debian12:latest@sha256:b7550f0b15838de14c564337eef2b804ba593ae55d81ca855421bd52f19bb480 AS base +ARG VERSION + +FROM scratch +ARG VERSION +COPY --link --from=base / / +COPY --link --from=node --chmod=0755 /nodejs/bin/node /nodejs/bin/node +COPY --link --from=build /app /app +USER nobody +ENTRYPOINT ["/nodejs/bin/node"] +CMD [ "/app/node_modules/.bin/protoc-gen-ts_proto" ] diff --git a/registry/community/stephenh-ts-proto/package-lock.json b/registry/community/stephenh-ts-proto/package-lock.json new file mode 100644 index 0000000..b80bb73 --- /dev/null +++ b/registry/community/stephenh-ts-proto/package-lock.json @@ -0,0 +1,87 @@ +{ + "name": "plugins-stephenh-ts-proto", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "plugins-stephenh-ts-proto", + "version": "1.0.0", + "dependencies": { + "ts-proto": "2.6.1" + } + }, + "node_modules/@bufbuild/protobuf": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.2.3.tgz", + "integrity": "sha512-tFQoXHJdkEOSwj5tRIZSPNUuXK3RaR7T1nUrPgbYX1pUbvqqaaZAsfo+NXBPsz5rZMSKVFrgK1WL8Q/MSLvprg==", + "license": "(Apache-2.0 AND BSD-3-Clause)" + }, + "node_modules/case-anything": { + "version": "2.1.13", + "resolved": "https://registry.npmjs.org/case-anything/-/case-anything-2.1.13.tgz", + "integrity": "sha512-zlOQ80VrQ2Ue+ymH5OuM/DlDq64mEm+B9UTdHULv5osUMD6HalNTblf2b1u/m6QecjsnOkBpqVZ+XPwIVsy7Ng==", + "license": "MIT", + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "license": "Apache-2.0", + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/dprint-node": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/dprint-node/-/dprint-node-1.0.8.tgz", + "integrity": "sha512-iVKnUtYfGrYcW1ZAlfR/F59cUVL8QIhWoBJoSjkkdua/dkWIgjZfiLMeTjiB06X0ZLkQ0M2C1VbUj/CxkIf1zg==", + "license": "MIT", + "dependencies": { + "detect-libc": "^1.0.3" + } + }, + "node_modules/ts-poet": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/ts-poet/-/ts-poet-6.11.0.tgz", + "integrity": "sha512-r5AGF8vvb+GjBsnqiTqbLhN1/U2FJt6BI+k0dfCrkKzWvUhNlwMmq9nDHuucHs45LomgHjZPvYj96dD3JawjJA==", + "license": "Apache-2.0", + "dependencies": { + "dprint-node": "^1.0.8" + } + }, + "node_modules/ts-proto": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/ts-proto/-/ts-proto-2.6.1.tgz", + "integrity": "sha512-4LTT99MkwkF1+fIA0b2mZu/58Qlpq3Q1g53TwEMZZgR1w/uX00PoVT4Z8aKJxMw0LeKQD4s9NrJYsF27Clckrg==", + "license": "ISC", + "dependencies": { + "@bufbuild/protobuf": "^2.0.0", + "case-anything": "^2.1.13", + "ts-poet": "^6.7.0", + "ts-proto-descriptors": "2.0.0" + }, + "bin": { + "protoc-gen-ts_proto": "protoc-gen-ts_proto" + } + }, + "node_modules/ts-proto-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-proto-descriptors/-/ts-proto-descriptors-2.0.0.tgz", + "integrity": "sha512-wHcTH3xIv11jxgkX5OyCSFfw27agpInAd6yh89hKG6zqIXnjW9SYqSER2CVQxdPj4czeOhGagNvZBEbJPy7qkw==", + "license": "ISC", + "dependencies": { + "@bufbuild/protobuf": "^2.0.0" + } + } + } +} diff --git a/registry/community/stephenh-ts-proto/package.json b/registry/community/stephenh-ts-proto/package.json new file mode 100644 index 0000000..b2f34e2 --- /dev/null +++ b/registry/community/stephenh-ts-proto/package.json @@ -0,0 +1,7 @@ +{ + "name": "plugins-stephenh-ts-proto", + "version": "1.0.0", + "dependencies": { + "ts-proto": "2.6.1" + } +} diff --git a/registry/community/stephenh-ts-proto/plugin.yaml b/registry/community/stephenh-ts-proto/plugin.yaml new file mode 100644 index 0000000..d1b9a4f --- /dev/null +++ b/registry/community/stephenh-ts-proto/plugin.yaml @@ -0,0 +1,67 @@ +binary: nodejs/bin/node +versions: + - v2.6.1 + - v2.3.0 + - v2.2.0 + - v2.11.7 + - v2.11.6 + - v2.11.4 + - v2.11.2 + - v2.11.1 + - v2.11.0 + - v1.181.2 + - v1.178.0 + - v1.176.0 + - v1.167.9 + - v1.167.1 + - v1.167.0 + - v1.166.4 + - v1.166.3 + - v1.166.2 + - v1.165.2 + - v1.165.1 + - v1.165.0 + - v1.164.1 + - v1.164.0 + - v1.163.0 + - v1.162.2 + - v1.162.1 + - v1.160.0 + - v1.159.1 + - v1.158.0 + - v1.157.1 + - v1.157.0 + - v1.156.8 + - v1.156.7 + - v1.156.6 + - v1.156.5 + - v1.156.2 + - v1.156.1 + - v1.156.0 + - v1.155.1 + - v1.153.3 + - v1.153.2 + - v1.153.0 + - v1.152.1 + - v1.151.1 + - v1.151.0 + - v1.150.1 + - v1.150.0 + - v1.149.0 + - v1.148.2 + - v1.148.1 + - v1.148.0 + - v1.147.3 + - v1.147.2 + - v1.147.1 + - v1.146.0 + - v1.145.0 + - v1.144.1 + - v1.143.0 + - v1.141.1 + - v1.141.0 + - v1.140.0 + - v1.139.0 + - v1.138.0 + - v1.137.2 + - v1.115.0 diff --git a/registry/community/sudorandom-connect-openapi/.dockerignore b/registry/community/sudorandom-connect-openapi/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/community/sudorandom-connect-openapi/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/community/sudorandom-connect-openapi/Dockerfile b/registry/community/sudorandom-connect-openapi/Dockerfile new file mode 100644 index 0000000..e85c930 --- /dev/null +++ b/registry/community/sudorandom-connect-openapi/Dockerfile @@ -0,0 +1,16 @@ +# syntax=docker/dockerfile:1.19 +FROM golang:1.26-alpine3.22 AS build + +ARG VERSION +ARG GO_MODULE +ARG BINARY_NAME + +RUN apk add upx --no-cache +RUN --mount=type=cache,target=/go/pkg/mod \ + CGO_ENABLED=0 go install -ldflags "-s -w" -trimpath ${GO_MODULE}@${VERSION} +RUN cp /go/bin/${BINARY_NAME} /plugin 2>/dev/null \ + || cp /go/bin/linux_*/${BINARY_NAME} /plugin \ + && upx --best --lzma /plugin + +FROM scratch +COPY --from=build /plugin /plugin diff --git a/registry/community/sudorandom-connect-openapi/plugin.yaml b/registry/community/sudorandom-connect-openapi/plugin.yaml new file mode 100644 index 0000000..8247ed8 --- /dev/null +++ b/registry/community/sudorandom-connect-openapi/plugin.yaml @@ -0,0 +1,20 @@ +binary: protoc-gen-connect-openapi +build_args: + GO_MODULE: github.com/sudorandom/protoc-gen-connect-openapi +versions: + - v0.25.6 + - v0.25.5 + - v0.25.4 + - v0.25.3 + - v0.25.2 + - v0.25.1 + - v0.25.0 + - v0.24.0 + - v0.23.1 + - v0.22.1 + - v0.22.0 + - v0.21.3 + - v0.21.2 + - v0.21.1 + - v0.20.3 + - v0.19.1 diff --git a/registry/community/timostamm-protobuf-ts/.dockerignore b/registry/community/timostamm-protobuf-ts/.dockerignore new file mode 100644 index 0000000..771bbba --- /dev/null +++ b/registry/community/timostamm-protobuf-ts/.dockerignore @@ -0,0 +1,3 @@ +* +!Dockerfile +!package*.json diff --git a/registry/community/timostamm-protobuf-ts/Dockerfile b/registry/community/timostamm-protobuf-ts/Dockerfile new file mode 100644 index 0000000..631b765 --- /dev/null +++ b/registry/community/timostamm-protobuf-ts/Dockerfile @@ -0,0 +1,23 @@ +# syntax=docker/dockerfile:1.12 +FROM node:22.14.0-bookworm AS build +ARG VERSION +WORKDIR /app +COPY --link package*.json . +RUN npm ci \ + && ./node_modules/.bin/esbuild ./node_modules/.bin/protoc-gen-ts --bundle --external:typescript --platform=node --outfile=protoc-gen-ts.js + +FROM gcr.io/distroless/nodejs22-debian12:latest@sha256:176a1a417bd00cf01952c2854a3ff0b11bfb118ff91a7ab0b7307899df239d4e AS node +ARG VERSION + +FROM gcr.io/distroless/cc-debian12:latest@sha256:85dac24dd2f03e841d986d5ed967385d3a721dcd9dbd21b602ddd82437f364c9 AS base +ARG VERSION + +FROM scratch +ARG VERSION +COPY --link --from=base / / +COPY --link --from=node --chmod=0755 /nodejs/bin/node /nodejs/bin/node +COPY --link --from=build --chmod=0755 /app/protoc-gen-ts.js /app/protoc-gen-ts.js +COPY --link --from=build /app/node_modules/typescript /app/node_modules/typescript +USER nobody +ENTRYPOINT ["/nodejs/bin/node"] +CMD [ "/app/protoc-gen-ts.js" ] diff --git a/registry/community/timostamm-protobuf-ts/package-lock.json b/registry/community/timostamm-protobuf-ts/package-lock.json new file mode 100644 index 0000000..e59be3e --- /dev/null +++ b/registry/community/timostamm-protobuf-ts/package-lock.json @@ -0,0 +1,548 @@ +{ + "name": "plugins-timostamm-protobuf-ts", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "plugins-timostamm-protobuf-ts", + "version": "1.0.0", + "dependencies": { + "@protobuf-ts/plugin": "2.9.6" + }, + "devDependencies": { + "esbuild": "^0.25.1" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.1.tgz", + "integrity": "sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.1.tgz", + "integrity": "sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.1.tgz", + "integrity": "sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.1.tgz", + "integrity": "sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.1.tgz", + "integrity": "sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.1.tgz", + "integrity": "sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.1.tgz", + "integrity": "sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.1.tgz", + "integrity": "sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.1.tgz", + "integrity": "sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.1.tgz", + "integrity": "sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.1.tgz", + "integrity": "sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.1.tgz", + "integrity": "sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.1.tgz", + "integrity": "sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.1.tgz", + "integrity": "sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.1.tgz", + "integrity": "sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.1.tgz", + "integrity": "sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.1.tgz", + "integrity": "sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.1.tgz", + "integrity": "sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.1.tgz", + "integrity": "sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.1.tgz", + "integrity": "sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.1.tgz", + "integrity": "sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.1.tgz", + "integrity": "sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.1.tgz", + "integrity": "sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.1.tgz", + "integrity": "sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.1.tgz", + "integrity": "sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@protobuf-ts/plugin": { + "version": "2.9.6", + "resolved": "https://registry.npmjs.org/@protobuf-ts/plugin/-/plugin-2.9.6.tgz", + "integrity": "sha512-Wpv5rkXeu6E5Y4r0TjWE0bzRGddiTYl/RM+tLgVlS0r8CqOBqNAmlWv+s8ltf/F75rVrahUal0cpyhFwha9GRA==", + "license": "Apache-2.0", + "dependencies": { + "@protobuf-ts/plugin-framework": "^2.9.6", + "@protobuf-ts/protoc": "^2.9.6", + "@protobuf-ts/runtime": "^2.9.6", + "@protobuf-ts/runtime-rpc": "^2.9.6", + "typescript": "^3.9" + }, + "bin": { + "protoc-gen-dump": "bin/protoc-gen-dump", + "protoc-gen-ts": "bin/protoc-gen-ts" + } + }, + "node_modules/@protobuf-ts/plugin-framework": { + "version": "2.9.6", + "resolved": "https://registry.npmjs.org/@protobuf-ts/plugin-framework/-/plugin-framework-2.9.6.tgz", + "integrity": "sha512-w7A1RXrDCiVzcaRE6YJP7FCARuAFe/Vc4SNQnHAi4CF0V6mEtyjAYEIC5BNYgIRaJEqB26zzsBQjIem3R02SCA==", + "license": "(Apache-2.0 AND BSD-3-Clause)", + "dependencies": { + "@protobuf-ts/runtime": "^2.9.6", + "typescript": "^3.9" + } + }, + "node_modules/@protobuf-ts/protoc": { + "version": "2.9.6", + "resolved": "https://registry.npmjs.org/@protobuf-ts/protoc/-/protoc-2.9.6.tgz", + "integrity": "sha512-c0XvAPDIBAovH9HxV8gBv8gzOTreQIqibcusrB1+DxvFiSvy+2V1tjbUmG5gJEbjk3aAOaoj0a3+QuE+P28xUw==", + "license": "Apache-2.0", + "bin": { + "protoc": "protoc.js" + } + }, + "node_modules/@protobuf-ts/runtime": { + "version": "2.9.6", + "resolved": "https://registry.npmjs.org/@protobuf-ts/runtime/-/runtime-2.9.6.tgz", + "integrity": "sha512-C0CfpKx4n4LBbUrajOdRj2BTbd3qBoK0SiKWLq7RgCoU6xiN4wesBMFHUOBp3fFzKeZwgU8Q2KtzaqzIvPLRXg==", + "license": "(Apache-2.0 AND BSD-3-Clause)" + }, + "node_modules/@protobuf-ts/runtime-rpc": { + "version": "2.9.6", + "resolved": "https://registry.npmjs.org/@protobuf-ts/runtime-rpc/-/runtime-rpc-2.9.6.tgz", + "integrity": "sha512-0UeqDRzNxgsh08lY5eWzFJNfD3gZ8Xf+WG1HzbIAbVAigzigwjwsYNNhTeas5H3gco1U5owTzCg177aambKOOw==", + "license": "Apache-2.0", + "dependencies": { + "@protobuf-ts/runtime": "^2.9.6" + } + }, + "node_modules/esbuild": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.1.tgz", + "integrity": "sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.1", + "@esbuild/android-arm": "0.25.1", + "@esbuild/android-arm64": "0.25.1", + "@esbuild/android-x64": "0.25.1", + "@esbuild/darwin-arm64": "0.25.1", + "@esbuild/darwin-x64": "0.25.1", + "@esbuild/freebsd-arm64": "0.25.1", + "@esbuild/freebsd-x64": "0.25.1", + "@esbuild/linux-arm": "0.25.1", + "@esbuild/linux-arm64": "0.25.1", + "@esbuild/linux-ia32": "0.25.1", + "@esbuild/linux-loong64": "0.25.1", + "@esbuild/linux-mips64el": "0.25.1", + "@esbuild/linux-ppc64": "0.25.1", + "@esbuild/linux-riscv64": "0.25.1", + "@esbuild/linux-s390x": "0.25.1", + "@esbuild/linux-x64": "0.25.1", + "@esbuild/netbsd-arm64": "0.25.1", + "@esbuild/netbsd-x64": "0.25.1", + "@esbuild/openbsd-arm64": "0.25.1", + "@esbuild/openbsd-x64": "0.25.1", + "@esbuild/sunos-x64": "0.25.1", + "@esbuild/win32-arm64": "0.25.1", + "@esbuild/win32-ia32": "0.25.1", + "@esbuild/win32-x64": "0.25.1" + } + }, + "node_modules/typescript": { + "version": "3.9.10", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz", + "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + } + } +} diff --git a/registry/community/timostamm-protobuf-ts/package.json b/registry/community/timostamm-protobuf-ts/package.json new file mode 100644 index 0000000..eaf43ed --- /dev/null +++ b/registry/community/timostamm-protobuf-ts/package.json @@ -0,0 +1,10 @@ +{ + "name": "plugins-timostamm-protobuf-ts", + "version": "1.0.0", + "dependencies": { + "@protobuf-ts/plugin": "2.9.6" + }, + "devDependencies": { + "esbuild": "^0.25.1" + } +} diff --git a/registry/community/timostamm-protobuf-ts/plugin.yaml b/registry/community/timostamm-protobuf-ts/plugin.yaml new file mode 100644 index 0000000..3c5388d --- /dev/null +++ b/registry/community/timostamm-protobuf-ts/plugin.yaml @@ -0,0 +1,14 @@ +binary: nodejs/bin/node +versions: + - v2.9.6 + - v2.9.5 + - v2.9.4 + - v2.9.3 + - v2.9.2 + - v2.9.1 + - v2.9.0 + - v2.8.3 + - v2.8.2 + - v2.11.1 + - v2.11.0 + - v2.10.0 diff --git a/registry/connectrpc/dart/.dockerignore b/registry/connectrpc/dart/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/connectrpc/dart/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/connectrpc/dart/Dockerfile b/registry/connectrpc/dart/Dockerfile new file mode 100644 index 0000000..b45a89d --- /dev/null +++ b/registry/connectrpc/dart/Dockerfile @@ -0,0 +1,17 @@ +# syntax=docker/dockerfile:1.15 +FROM dart:3.8.1-sdk AS build +ARG VERSION + +WORKDIR /build +RUN git clone --depth=1 --branch ${VERSION} https://github.com/connectrpc/connect-dart.git +RUN cd connect-dart/packages/connect \ + && dart pub get \ + && dart compile exe bin/protoc-gen-connect-dart.dart -o /build/protoc-gen-connect-dart + +FROM scratch +ARG VERSION +COPY --from=build --link /etc/passwd /etc/passwd +COPY --from=build --link /runtime/ / +COPY --from=build --link /build/protoc-gen-connect-dart . +USER nobody +ENTRYPOINT [ "/protoc-gen-connect-dart" ] diff --git a/registry/connectrpc/dart/plugin.yaml b/registry/connectrpc/dart/plugin.yaml new file mode 100644 index 0000000..3efc1e7 --- /dev/null +++ b/registry/connectrpc/dart/plugin.yaml @@ -0,0 +1,8 @@ +binary: protoc-gen-connect-dart +versions: + - v1.0.0 + - v0.4.1 + - v0.4.0 + - v0.3.0 + - v0.2.1 + - v0.1.0 diff --git a/registry/connectrpc/es/.dockerignore b/registry/connectrpc/es/.dockerignore new file mode 100644 index 0000000..771bbba --- /dev/null +++ b/registry/connectrpc/es/.dockerignore @@ -0,0 +1,3 @@ +* +!Dockerfile +!package*.json diff --git a/registry/connectrpc/es/Dockerfile b/registry/connectrpc/es/Dockerfile new file mode 100644 index 0000000..ca0b821 --- /dev/null +++ b/registry/connectrpc/es/Dockerfile @@ -0,0 +1,25 @@ +# syntax=docker/dockerfile:1.10 +FROM node:22.9.0-bookworm AS build +ARG VERSION +WORKDIR /app +COPY --link package*.json . +RUN npm ci \ + && find node_modules/typescript ! -name 'typescript.js' ! -name 'package.json' -type f -exec rm -f {} + \ + && find node_modules/typescript -depth -type d -empty -delete \ + && ./node_modules/.bin/esbuild ./node_modules/.bin/protoc-gen-connect-es --bundle --external:typescript --platform=node --outfile=protoc-gen-connect-es.js + +FROM gcr.io/distroless/nodejs22-debian12:latest@sha256:d7ccebdf7617f225aa511c4a0e9c3bff2a8a65b22f8032ca982193d5a52c8ee9 AS node +ARG VERSION + +FROM gcr.io/distroless/cc-debian12:latest@sha256:3310655aac0d85eb9d579792387af1ff3eb7a1667823478be58020ab0e0d97a8 AS base +ARG VERSION + +FROM scratch +ARG VERSION +COPY --link --from=base / / +COPY --link --from=node --chmod=0755 /nodejs/bin/node /nodejs/bin/node +COPY --link --from=build --chmod=0755 /app/protoc-gen-connect-es.js /app/protoc-gen-connect-es.js +COPY --link --from=build /app/node_modules/typescript /app/node_modules/typescript +USER nobody +ENTRYPOINT ["/nodejs/bin/node"] +CMD [ "/app/protoc-gen-connect-es.js" ] diff --git a/registry/connectrpc/es/package-lock.json b/registry/connectrpc/es/package-lock.json new file mode 100644 index 0000000..0bf28dd --- /dev/null +++ b/registry/connectrpc/es/package-lock.json @@ -0,0 +1,552 @@ +{ + "name": "plugins-connectrpc-es", + "version": "1.6.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "plugins-connectrpc-es", + "version": "1.6.1", + "dependencies": { + "@connectrpc/protoc-gen-connect-es": "1.6.1" + }, + "devDependencies": { + "esbuild": "^0.24.0" + } + }, + "node_modules/@bufbuild/protobuf": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-1.10.0.tgz", + "integrity": "sha512-QDdVFLoN93Zjg36NoQPZfsVH9tZew7wKDKyV5qRdj8ntT4wQCOradQjRaTdwMhWUYsgKsvCINKKm87FdEk96Ag==" + }, + "node_modules/@bufbuild/protoplugin": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@bufbuild/protoplugin/-/protoplugin-1.10.0.tgz", + "integrity": "sha512-u6NE4vL0lw1+EK4/PiE/SQB7fKO4LRJNTEScIXVOi2x88K/c8WKc/k0KyEaA0asVBMpwekJQZGnRyj04ZtN5Gg==", + "dependencies": { + "@bufbuild/protobuf": "1.10.0", + "@typescript/vfs": "^1.4.0", + "typescript": "4.5.2" + } + }, + "node_modules/@connectrpc/protoc-gen-connect-es": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@connectrpc/protoc-gen-connect-es/-/protoc-gen-connect-es-1.6.1.tgz", + "integrity": "sha512-0fHcaADd+GKM0I/koIQpmKg7b+QL18bXlggTUYEAlMFzsd4zN/ApG3235hdUcRyhrAOAItTXxh8ZAV/nNd43Gg==", + "dependencies": { + "@bufbuild/protobuf": "^1.10.0", + "@bufbuild/protoplugin": "^1.10.0" + }, + "bin": { + "protoc-gen-connect-es": "bin/protoc-gen-connect-es" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@bufbuild/protoc-gen-es": "^1.10.0", + "@connectrpc/connect": "1.6.1" + }, + "peerDependenciesMeta": { + "@bufbuild/protoc-gen-es": { + "optional": true + }, + "@connectrpc/connect": { + "optional": true + } + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz", + "integrity": "sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.0.tgz", + "integrity": "sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz", + "integrity": "sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.0.tgz", + "integrity": "sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz", + "integrity": "sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz", + "integrity": "sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz", + "integrity": "sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz", + "integrity": "sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz", + "integrity": "sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz", + "integrity": "sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz", + "integrity": "sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz", + "integrity": "sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz", + "integrity": "sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz", + "integrity": "sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz", + "integrity": "sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz", + "integrity": "sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz", + "integrity": "sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz", + "integrity": "sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz", + "integrity": "sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz", + "integrity": "sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz", + "integrity": "sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz", + "integrity": "sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz", + "integrity": "sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz", + "integrity": "sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@typescript/vfs": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.6.0.tgz", + "integrity": "sha512-hvJUjNVeBMp77qPINuUvYXj4FyWeeMMKZkxEATEU3hqBAQ7qdTBCUFT7Sp0Zu0faeEtFf+ldXxMEDr/bk73ISg==", + "dependencies": { + "debug": "^4.1.1" + }, + "peerDependencies": { + "typescript": "*" + } + }, + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/esbuild": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz", + "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.24.0", + "@esbuild/android-arm": "0.24.0", + "@esbuild/android-arm64": "0.24.0", + "@esbuild/android-x64": "0.24.0", + "@esbuild/darwin-arm64": "0.24.0", + "@esbuild/darwin-x64": "0.24.0", + "@esbuild/freebsd-arm64": "0.24.0", + "@esbuild/freebsd-x64": "0.24.0", + "@esbuild/linux-arm": "0.24.0", + "@esbuild/linux-arm64": "0.24.0", + "@esbuild/linux-ia32": "0.24.0", + "@esbuild/linux-loong64": "0.24.0", + "@esbuild/linux-mips64el": "0.24.0", + "@esbuild/linux-ppc64": "0.24.0", + "@esbuild/linux-riscv64": "0.24.0", + "@esbuild/linux-s390x": "0.24.0", + "@esbuild/linux-x64": "0.24.0", + "@esbuild/netbsd-x64": "0.24.0", + "@esbuild/openbsd-arm64": "0.24.0", + "@esbuild/openbsd-x64": "0.24.0", + "@esbuild/sunos-x64": "0.24.0", + "@esbuild/win32-arm64": "0.24.0", + "@esbuild/win32-ia32": "0.24.0", + "@esbuild/win32-x64": "0.24.0" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/typescript": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz", + "integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + } + } +} diff --git a/registry/connectrpc/es/package.json b/registry/connectrpc/es/package.json new file mode 100644 index 0000000..95cb243 --- /dev/null +++ b/registry/connectrpc/es/package.json @@ -0,0 +1,10 @@ +{ + "name": "plugins-connectrpc-es", + "version": "1.6.1", + "dependencies": { + "@connectrpc/protoc-gen-connect-es": "1.6.1" + }, + "devDependencies": { + "esbuild": "^0.24.0" + } +} diff --git a/registry/connectrpc/es/plugin.yaml b/registry/connectrpc/es/plugin.yaml new file mode 100644 index 0000000..c7e6d87 --- /dev/null +++ b/registry/connectrpc/es/plugin.yaml @@ -0,0 +1,16 @@ +binary: nodejs/bin/node +versions: + - v1.6.1 + - v1.6.0 + - v1.5.0 + - v1.4.0 + - v1.3.0 + - v1.2.1 + - v1.2.0 + - v1.1.4 + - v1.1.3 + - v1.1.2 + - v1.1.0 + - v1.0.0 + - v0.13.2 + - v0.13.1 diff --git a/registry/connectrpc/go/.dockerignore b/registry/connectrpc/go/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/connectrpc/go/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/connectrpc/go/Dockerfile b/registry/connectrpc/go/Dockerfile new file mode 100644 index 0000000..e85c930 --- /dev/null +++ b/registry/connectrpc/go/Dockerfile @@ -0,0 +1,16 @@ +# syntax=docker/dockerfile:1.19 +FROM golang:1.26-alpine3.22 AS build + +ARG VERSION +ARG GO_MODULE +ARG BINARY_NAME + +RUN apk add upx --no-cache +RUN --mount=type=cache,target=/go/pkg/mod \ + CGO_ENABLED=0 go install -ldflags "-s -w" -trimpath ${GO_MODULE}@${VERSION} +RUN cp /go/bin/${BINARY_NAME} /plugin 2>/dev/null \ + || cp /go/bin/linux_*/${BINARY_NAME} /plugin \ + && upx --best --lzma /plugin + +FROM scratch +COPY --from=build /plugin /plugin diff --git a/registry/connectrpc/go/plugin.yaml b/registry/connectrpc/go/plugin.yaml new file mode 100644 index 0000000..4d32d60 --- /dev/null +++ b/registry/connectrpc/go/plugin.yaml @@ -0,0 +1,20 @@ +binary: protoc-gen-connect-go +build_args: + GO_MODULE: connectrpc.com/connect/cmd/protoc-gen-connect-go +versions: + - v1.20.0 + - v1.19.2 + - v1.19.1 + - v1.19.0 + - v1.18.1 + - v1.18.0 + - v1.17.0 + - v1.16.2 + - v1.16.1 + - v1.16.0 + - v1.15.0 + - v1.14.0 + - v1.13.0 + - v1.12.0 + - v1.11.1 + - v1.11.0 diff --git a/registry/connectrpc/gosimple/.dockerignore b/registry/connectrpc/gosimple/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/connectrpc/gosimple/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/connectrpc/gosimple/Dockerfile b/registry/connectrpc/gosimple/Dockerfile new file mode 100644 index 0000000..e85c930 --- /dev/null +++ b/registry/connectrpc/gosimple/Dockerfile @@ -0,0 +1,16 @@ +# syntax=docker/dockerfile:1.19 +FROM golang:1.26-alpine3.22 AS build + +ARG VERSION +ARG GO_MODULE +ARG BINARY_NAME + +RUN apk add upx --no-cache +RUN --mount=type=cache,target=/go/pkg/mod \ + CGO_ENABLED=0 go install -ldflags "-s -w" -trimpath ${GO_MODULE}@${VERSION} +RUN cp /go/bin/${BINARY_NAME} /plugin 2>/dev/null \ + || cp /go/bin/linux_*/${BINARY_NAME} /plugin \ + && upx --best --lzma /plugin + +FROM scratch +COPY --from=build /plugin /plugin diff --git a/registry/connectrpc/gosimple/plugin.yaml b/registry/connectrpc/gosimple/plugin.yaml new file mode 100644 index 0000000..7fe77c2 --- /dev/null +++ b/registry/connectrpc/gosimple/plugin.yaml @@ -0,0 +1,8 @@ +binary: protoc-gen-connect-go +build_args: + GO_MODULE: connectrpc.com/connect/cmd/protoc-gen-connect-go +versions: + - v1.20.0 + - v1.19.2 + - v1.19.1 + - v1.19.0 diff --git a/registry/connectrpc/kotlin/.dockerignore b/registry/connectrpc/kotlin/.dockerignore new file mode 100644 index 0000000..7ff6309 --- /dev/null +++ b/registry/connectrpc/kotlin/.dockerignore @@ -0,0 +1,3 @@ +* +!Dockerfile +!pom.xml diff --git a/registry/connectrpc/kotlin/Dockerfile b/registry/connectrpc/kotlin/Dockerfile new file mode 100644 index 0000000..f72b454 --- /dev/null +++ b/registry/connectrpc/kotlin/Dockerfile @@ -0,0 +1,24 @@ +# syntax=docker/dockerfile:1.23 +FROM debian:trixie-20260505@sha256:e2d08da6f42ef4b09b165d55528a12727aeed8240dc9edf888e3ec07e10ef9da AS build +ARG VERSION + +RUN apt-get update \ + && apt-get install -y curl +WORKDIR /app +RUN curl -fsSL -o /app/protoc-gen-connect-kotlin.jar https://repo1.maven.org/maven2/com/connectrpc/protoc-gen-connect-kotlin/${VERSION#v}/protoc-gen-connect-kotlin-${VERSION#v}.jar + +FROM gcr.io/distroless/java25-debian13:latest@sha256:583ba2e08558063002bd1b5874a81b33b7204a0ad46727d4b6cbeff5a25935ba as base +ARG VERSION + +FROM maven:3.9.11-eclipse-temurin-21 AS maven-deps +ARG VERSION +COPY pom.xml /tmp/pom.xml +RUN cd /tmp && mvn -f pom.xml dependency:go-offline + +FROM scratch +ARG VERSION +COPY --from=base --link / / +COPY --from=build --link --chmod=0755 --chown=root:root /app/protoc-gen-connect-kotlin.jar . +COPY --from=maven-deps /root/.m2/repository /maven-repository +USER nobody +ENTRYPOINT [ "/usr/bin/java", "-jar", "/protoc-gen-connect-kotlin.jar"] diff --git a/registry/connectrpc/kotlin/plugin.yaml b/registry/connectrpc/kotlin/plugin.yaml new file mode 100644 index 0000000..2bc5807 --- /dev/null +++ b/registry/connectrpc/kotlin/plugin.yaml @@ -0,0 +1,18 @@ +binary: protoc-gen-kotlin +versions: + - v0.8.2 + - v0.8.0 + - v0.7.4 + - v0.7.3 + - v0.7.2 + - v0.7.1 + - v0.7.0 + - v0.6.1 + - v0.6.0 + - v0.5.1 + - v0.5.0 + - v0.4.0 + - v0.3.1 + - v0.3.0 + - v0.2.0 + - v0.1.11 diff --git a/registry/connectrpc/kotlin/pom.xml b/registry/connectrpc/kotlin/pom.xml new file mode 100644 index 0000000..c1f9b50 --- /dev/null +++ b/registry/connectrpc/kotlin/pom.xml @@ -0,0 +1,76 @@ + + 4.0.0 + temp + temp + 1.0 + + + com.connectrpc + connect-kotlin + 0.8.2 + + + com.connectrpc + connect-kotlin-google-java-ext + 0.8.2 + + + com.connectrpc + connect-kotlin-okhttp + 0.8.2 + + + com.google.protobuf + protobuf-kotlin + 4.35.0 + + + org.jetbrains.kotlin + kotlin-stdlib + 2.2.21 + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + 2.2.21 + + + com.google.protobuf + protobuf-java + 4.35.0 + + + + com.connectrpc + connect-kotlin-google-javalite-ext + 0.8.2 + + + com.google.protobuf + protobuf-kotlin-lite + 4.35.0 + + + com.google.protobuf + protobuf-javalite + 4.35.0 + + + build.buf + protobuf-javalite + 4.35.0 + + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + 2.2.21 + + 2.1 + + + + + diff --git a/registry/connectrpc/python/.dockerignore b/registry/connectrpc/python/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/connectrpc/python/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/connectrpc/python/Dockerfile b/registry/connectrpc/python/Dockerfile new file mode 100644 index 0000000..adae3ca --- /dev/null +++ b/registry/connectrpc/python/Dockerfile @@ -0,0 +1,12 @@ +# syntax=docker/dockerfile:1.19 +FROM python:3.13-alpine AS build + +ARG VERSION +ARG PIP_PACKAGE + +WORKDIR /app +RUN python -m venv venv && \ + ./venv/bin/pip install --no-cache-dir "${PIP_PACKAGE}==${VERSION#v}" + +FROM scratch +COPY --from=build /app /app diff --git a/registry/connectrpc/python/plugin.yaml b/registry/connectrpc/python/plugin.yaml new file mode 100644 index 0000000..ce09f50 --- /dev/null +++ b/registry/connectrpc/python/plugin.yaml @@ -0,0 +1,13 @@ +binary: app/bin/protoc-gen-connect-python +build_args: + PIP_PACKAGE: protoc-gen-connect-python +versions: + - v0.9.0 + - v0.8.1 + - v0.8.0 + - v0.7.1 + - v0.7.0 + - v0.6.0 + - v0.5.0 + - v0.4.2 + - v0.10.0 diff --git a/registry/connectrpc/query-es/.dockerignore b/registry/connectrpc/query-es/.dockerignore new file mode 100644 index 0000000..771bbba --- /dev/null +++ b/registry/connectrpc/query-es/.dockerignore @@ -0,0 +1,3 @@ +* +!Dockerfile +!package*.json diff --git a/registry/connectrpc/query-es/Dockerfile b/registry/connectrpc/query-es/Dockerfile new file mode 100644 index 0000000..1114b0f --- /dev/null +++ b/registry/connectrpc/query-es/Dockerfile @@ -0,0 +1,25 @@ +# syntax=docker/dockerfile:1.17 +FROM node:22.19.0-bookworm AS build +ARG VERSION +WORKDIR /app +COPY --link package*.json . +RUN npm ci \ + && find node_modules/typescript ! -name 'typescript.js' ! -name 'package.json' -type f -exec rm -f {} + \ + && find node_modules/typescript -depth -type d -empty -delete \ + && ./node_modules/.bin/esbuild ./node_modules/.bin/protoc-gen-connect-query --bundle --external:typescript --platform=node --outfile=protoc-gen-connect-query.js + +FROM gcr.io/distroless/nodejs22-debian12:latest@sha256:3732180ba4a39101bd95b7105ef0c47526c197d6c29c6d48f7059a647a4064a5 AS node +ARG VERSION + +FROM gcr.io/distroless/cc-debian12:latest@sha256:620d8b11ae800f0dbd7995f89ddc5344ad603269ea98770588b1b07a4a0a6872 AS base +ARG VERSION + +FROM scratch +ARG VERSION +COPY --link --from=base / / +COPY --link --from=node --chmod=0755 /nodejs/bin/node /nodejs/bin/node +COPY --link --from=build --chmod=0755 /app/protoc-gen-connect-query.js /app/protoc-gen-connect-query.js +COPY --link --from=build /app/node_modules/typescript /app/node_modules/typescript +USER nobody +ENTRYPOINT ["/nodejs/bin/node"] +CMD [ "/app/protoc-gen-connect-query.js" ] diff --git a/registry/connectrpc/query-es/package-lock.json b/registry/connectrpc/query-es/package-lock.json new file mode 100644 index 0000000..51477b4 --- /dev/null +++ b/registry/connectrpc/query-es/package-lock.json @@ -0,0 +1,591 @@ +{ + "name": "plugins-connectrpc-query-es", + "version": "2.2.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "plugins-connectrpc-query-es", + "version": "2.2.0", + "dependencies": { + "@connectrpc/protoc-gen-connect-query": "2.2.0" + }, + "devDependencies": { + "esbuild": "^0.25.6" + } + }, + "node_modules/@bufbuild/protobuf": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.7.0.tgz", + "integrity": "sha512-qn6tAIZEw5i/wiESBF4nQxZkl86aY4KoO0IkUa2Lh+rya64oTOdJQFlZuMwI1Qz9VBJQrQC4QlSA2DNek5gCOA==", + "license": "(Apache-2.0 AND BSD-3-Clause)" + }, + "node_modules/@bufbuild/protoplugin": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@bufbuild/protoplugin/-/protoplugin-2.7.0.tgz", + "integrity": "sha512-yUdg8hXzFGR6K8ren7aXly2hT9BxClId814VB142YeZPatY0wqD3c0D8KfIz5nIeMdoPt0/Pm/RycFJCNGMD6w==", + "license": "Apache-2.0", + "dependencies": { + "@bufbuild/protobuf": "2.7.0", + "@typescript/vfs": "^1.5.2", + "typescript": "5.4.5" + } + }, + "node_modules/@connectrpc/protoc-gen-connect-query": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@connectrpc/protoc-gen-connect-query/-/protoc-gen-connect-query-2.2.0.tgz", + "integrity": "sha512-MC+Yn3GjqfmdXnmzixylbnAF6viX5y30PTj9gBXTPwyvA5s9iarWV3FKdjT4IcfQYQIJv11e7LrJ2eFZ6vGAUA==", + "license": "Apache-2.0", + "dependencies": { + "@bufbuild/protobuf": "^2.5.1", + "@bufbuild/protoplugin": "^2.2.1" + }, + "bin": { + "protoc-gen-connect-query": "bin/protoc-gen-connect-query" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@bufbuild/protoc-gen-es": "2.x" + }, + "peerDependenciesMeta": { + "@bufbuild/protoc-gen-es": { + "optional": true + } + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", + "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", + "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", + "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", + "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", + "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", + "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", + "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", + "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", + "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", + "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", + "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", + "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", + "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", + "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", + "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", + "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", + "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", + "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", + "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", + "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", + "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", + "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", + "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", + "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", + "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", + "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@typescript/vfs": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.6.1.tgz", + "integrity": "sha512-JwoxboBh7Oz1v38tPbkrZ62ZXNHAk9bJ7c9x0eI5zBfBnBYGhURdbnh7Z4smN/MV48Y5OCcZb58n972UtbazsA==", + "license": "MIT", + "dependencies": { + "debug": "^4.1.1" + }, + "peerDependencies": { + "typescript": "*" + } + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/esbuild": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", + "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.9", + "@esbuild/android-arm": "0.25.9", + "@esbuild/android-arm64": "0.25.9", + "@esbuild/android-x64": "0.25.9", + "@esbuild/darwin-arm64": "0.25.9", + "@esbuild/darwin-x64": "0.25.9", + "@esbuild/freebsd-arm64": "0.25.9", + "@esbuild/freebsd-x64": "0.25.9", + "@esbuild/linux-arm": "0.25.9", + "@esbuild/linux-arm64": "0.25.9", + "@esbuild/linux-ia32": "0.25.9", + "@esbuild/linux-loong64": "0.25.9", + "@esbuild/linux-mips64el": "0.25.9", + "@esbuild/linux-ppc64": "0.25.9", + "@esbuild/linux-riscv64": "0.25.9", + "@esbuild/linux-s390x": "0.25.9", + "@esbuild/linux-x64": "0.25.9", + "@esbuild/netbsd-arm64": "0.25.9", + "@esbuild/netbsd-x64": "0.25.9", + "@esbuild/openbsd-arm64": "0.25.9", + "@esbuild/openbsd-x64": "0.25.9", + "@esbuild/openharmony-arm64": "0.25.9", + "@esbuild/sunos-x64": "0.25.9", + "@esbuild/win32-arm64": "0.25.9", + "@esbuild/win32-ia32": "0.25.9", + "@esbuild/win32-x64": "0.25.9" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/typescript": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + } + } +} diff --git a/registry/connectrpc/query-es/package.json b/registry/connectrpc/query-es/package.json new file mode 100644 index 0000000..854811e --- /dev/null +++ b/registry/connectrpc/query-es/package.json @@ -0,0 +1,10 @@ +{ + "name": "plugins-connectrpc-query-es", + "version": "2.2.0", + "dependencies": { + "@connectrpc/protoc-gen-connect-query": "2.2.0" + }, + "devDependencies": { + "esbuild": "^0.25.6" + } +} diff --git a/registry/connectrpc/query-es/plugin.yaml b/registry/connectrpc/query-es/plugin.yaml new file mode 100644 index 0000000..3b23342 --- /dev/null +++ b/registry/connectrpc/query-es/plugin.yaml @@ -0,0 +1,25 @@ +binary: nodejs/bin/node +versions: + - v2.2.0 + - v2.1.1 + - v2.1.0 + - v2.0.1 + - v2.0.0 + - v1.4.2 + - v1.4.1 + - v1.4.0 + - v1.3.1 + - v1.3.0 + - v1.2.0 + - v1.1.3 + - v1.1.2 + - v1.1.1 + - v1.1.0 + - v1.0.0 + - v0.6.0 + - v0.5.3 + - v0.5.2 + - v0.5.1 + - v0.4.4 + - v0.4.3 + - v0.4.2 diff --git a/registry/connectrpc/swift-mocks/.dockerignore b/registry/connectrpc/swift-mocks/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/connectrpc/swift-mocks/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/connectrpc/swift-mocks/Dockerfile b/registry/connectrpc/swift-mocks/Dockerfile new file mode 100644 index 0000000..36519f3 --- /dev/null +++ b/registry/connectrpc/swift-mocks/Dockerfile @@ -0,0 +1,20 @@ +# syntax=docker/dockerfile:1.23 +FROM swift:6.3.2-bookworm@sha256:de037fec606b26cee2bf7f93dba8eab7a617f067f94cf78669f929e5a0c6146a AS build +ARG VERSION + +WORKDIR /app +RUN apt-get update \ + && apt-get install -y libstdc++-12-dev +RUN git clone --depth 1 --branch ${VERSION#v} https://github.com/connectrpc/connect-swift +WORKDIR /app/connect-swift +RUN swift build -c release --product protoc-gen-connect-swift-mocks --static-swift-stdlib -Xlinker -s + +FROM gcr.io/distroless/cc-debian13:latest@sha256:8b5d1db6d2253036a53cb8362d3e3fa82a7caf84c247772c46a023166c64e977 AS base +ARG VERSION + +FROM scratch +ARG VERSION +COPY --link --from=base / / +COPY --link --from=build /app/connect-swift/.build/release/protoc-gen-connect-swift-mocks . +USER nobody +ENTRYPOINT [ "/protoc-gen-connect-swift-mocks" ] diff --git a/registry/connectrpc/swift-mocks/plugin.yaml b/registry/connectrpc/swift-mocks/plugin.yaml new file mode 100644 index 0000000..dd3a147 --- /dev/null +++ b/registry/connectrpc/swift-mocks/plugin.yaml @@ -0,0 +1,19 @@ +binary: protoc-gen-connect-swift-mocks +versions: + - v1.2.3 + - v1.2.2 + - v1.2.1 + - v1.2.0 + - v1.1.0 + - v1.0.4 + - v1.0.3 + - v1.0.2 + - v1.0.0 + - v0.9.0 + - v0.8.0 + - v0.14.0 + - v0.13.0 + - v0.12.0 + - v0.11.0 + - v0.10.1 + - v0.10.0 diff --git a/registry/connectrpc/swift/.dockerignore b/registry/connectrpc/swift/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/connectrpc/swift/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/connectrpc/swift/Dockerfile b/registry/connectrpc/swift/Dockerfile new file mode 100644 index 0000000..00a3174 --- /dev/null +++ b/registry/connectrpc/swift/Dockerfile @@ -0,0 +1,20 @@ +# syntax=docker/dockerfile:1.23 +FROM swift:6.3.2-bookworm@sha256:de037fec606b26cee2bf7f93dba8eab7a617f067f94cf78669f929e5a0c6146a AS build +ARG VERSION + +WORKDIR /app +RUN apt-get update \ + && apt-get install -y libstdc++-12-dev +RUN git clone --depth 1 --branch ${VERSION#v} https://github.com/connectrpc/connect-swift +WORKDIR /app/connect-swift +RUN swift build -c release --product protoc-gen-connect-swift --static-swift-stdlib -Xlinker -s + +FROM gcr.io/distroless/cc-debian13:latest@sha256:8b5d1db6d2253036a53cb8362d3e3fa82a7caf84c247772c46a023166c64e977 AS base +ARG VERSION + +FROM scratch +ARG VERSION +COPY --link --from=base / / +COPY --link --from=build /app/connect-swift/.build/release/protoc-gen-connect-swift . +USER nobody +ENTRYPOINT [ "/protoc-gen-connect-swift" ] diff --git a/registry/connectrpc/swift/plugin.yaml b/registry/connectrpc/swift/plugin.yaml new file mode 100644 index 0000000..ef2033f --- /dev/null +++ b/registry/connectrpc/swift/plugin.yaml @@ -0,0 +1,19 @@ +binary: protoc-gen-connect-swift +versions: + - v1.2.3 + - v1.2.2 + - v1.2.1 + - v1.2.0 + - v1.1.0 + - v1.0.4 + - v1.0.3 + - v1.0.2 + - v1.0.0 + - v0.9.0 + - v0.8.0 + - v0.14.0 + - v0.13.0 + - v0.12.0 + - v0.11.0 + - v0.10.1 + - v0.10.0 diff --git a/registry/googlecloudplatform/bq-schema/.dockerignore b/registry/googlecloudplatform/bq-schema/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/googlecloudplatform/bq-schema/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/googlecloudplatform/bq-schema/Dockerfile b/registry/googlecloudplatform/bq-schema/Dockerfile new file mode 100644 index 0000000..e85c930 --- /dev/null +++ b/registry/googlecloudplatform/bq-schema/Dockerfile @@ -0,0 +1,16 @@ +# syntax=docker/dockerfile:1.19 +FROM golang:1.26-alpine3.22 AS build + +ARG VERSION +ARG GO_MODULE +ARG BINARY_NAME + +RUN apk add upx --no-cache +RUN --mount=type=cache,target=/go/pkg/mod \ + CGO_ENABLED=0 go install -ldflags "-s -w" -trimpath ${GO_MODULE}@${VERSION} +RUN cp /go/bin/${BINARY_NAME} /plugin 2>/dev/null \ + || cp /go/bin/linux_*/${BINARY_NAME} /plugin \ + && upx --best --lzma /plugin + +FROM scratch +COPY --from=build /plugin /plugin diff --git a/registry/googlecloudplatform/bq-schema/plugin.yaml b/registry/googlecloudplatform/bq-schema/plugin.yaml new file mode 100644 index 0000000..e9edb08 --- /dev/null +++ b/registry/googlecloudplatform/bq-schema/plugin.yaml @@ -0,0 +1,7 @@ +binary: protoc-gen-bq-schema +build_args: + GO_MODULE: github.com/GoogleCloudPlatform/protoc-gen-bq-schema/v3 +versions: + - v3.1.0 + - v2.0.1 + - v1.1.0 diff --git a/registry/grpc-ecosystem/gateway/.dockerignore b/registry/grpc-ecosystem/gateway/.dockerignore new file mode 100644 index 0000000..1245899 --- /dev/null +++ b/registry/grpc-ecosystem/gateway/.dockerignore @@ -0,0 +1,3 @@ +* +!Dockerfile +!separate_pkg_additional_imports.patch diff --git a/registry/grpc-ecosystem/gateway/Dockerfile b/registry/grpc-ecosystem/gateway/Dockerfile new file mode 100644 index 0000000..02cb5e5 --- /dev/null +++ b/registry/grpc-ecosystem/gateway/Dockerfile @@ -0,0 +1,25 @@ +# syntax=docker/dockerfile:1.23 +FROM --platform=$BUILDPLATFORM golang:1.26.2-trixie@sha256:c0074c718b473f3827043f86532c4c0ff537e3fe7a81b8219b0d1ccfcc2c9a09 AS build +ARG VERSION + +ARG TARGETOS TARGETARCH +ENV CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH + +WORKDIR /tmp +RUN git clone --depth 1 --branch ${VERSION} https://github.com/grpc-ecosystem/grpc-gateway.git +COPY --link separate_pkg_additional_imports.patch /tmp/separate_pkg_additional_imports.patch +WORKDIR /tmp/grpc-gateway +RUN git apply /tmp/separate_pkg_additional_imports.patch +RUN --mount=type=cache,target=/go/pkg/mod \ + GOOS= GOARCH= go test -run 'TestGenerator_GenerateSeparatePackage' ./protoc-gen-grpc-gateway/internal/gengateway/ +WORKDIR /tmp/grpc-gateway/protoc-gen-grpc-gateway +RUN --mount=type=cache,target=/go/pkg/mod \ + go install -ldflags="-s -w" -trimpath \ + && mv /go/bin/${GOOS}_${GOARCH}/protoc-gen-grpc-gateway /go/bin/protoc-gen-grpc-gateway || true + +FROM scratch +ARG VERSION +COPY --from=build --link --chown=root:root /etc/passwd /etc/passwd +COPY --from=build --link --chown=root:root /go/bin/protoc-gen-grpc-gateway / +USER nobody +ENTRYPOINT [ "/protoc-gen-grpc-gateway" ] diff --git a/registry/grpc-ecosystem/gateway/plugin.yaml b/registry/grpc-ecosystem/gateway/plugin.yaml new file mode 100644 index 0000000..3d23ed9 --- /dev/null +++ b/registry/grpc-ecosystem/gateway/plugin.yaml @@ -0,0 +1,33 @@ +binary: protoc-gen-grpc-gateway +versions: + - v2.29.0 + - v2.28.0 + - v2.27.7 + - v2.27.6 + - v2.27.5 + - v2.27.4 + - v2.27.3 + - v2.27.2 + - v2.27.1 + - v2.27.0 + - v2.26.3 + - v2.26.1 + - v2.26.0 + - v2.25.1 + - v2.24.0 + - v2.23.0 + - v2.22.0 + - v2.21.0 + - v2.20.0 + - v2.19.1 + - v2.19.0 + - v2.18.1 + - v2.18.0 + - v2.17.1 + - v2.17.0 + - v2.16.2 + - v2.16.1 + - v2.16.0 + - v2.15.2 + - v2.15.1 + - v2.15.0 diff --git a/registry/grpc-ecosystem/gateway/separate_pkg_additional_imports.patch b/registry/grpc-ecosystem/gateway/separate_pkg_additional_imports.patch new file mode 100644 index 0000000..468c56c --- /dev/null +++ b/registry/grpc-ecosystem/gateway/separate_pkg_additional_imports.patch @@ -0,0 +1,766 @@ +diff --git a/internal/descriptor/buf_build.go b/internal/descriptor/buf_build.go +new file mode 100644 +index 0000000..1c6725f +--- /dev/null ++++ b/internal/descriptor/buf_build.go +@@ -0,0 +1,58 @@ ++package descriptor ++ ++import ( ++ "path/filepath" ++ "strings" ++) ++ ++const ( ++ BaseTypePackageSubPath = "protocolbuffers/go" ++ grpcPackageSubPath = "grpc/go" ++ // TODO: change "v2" to "v3" when v3 of grpc gateway is released, ++ // or, even better, stop generating at the extra location. ++ GatewayPackageSubPath = "grpc-ecosystem/gateway/v2" ++) ++ ++// SetSeparatePackage sets separatePackage ++func (r *Registry) SetSeparatePackage(use bool) { ++ r.separatePackage = use ++} ++ ++// IncludeAdditionalImports adds additionalImports to the registry on a per-package basis ++func (r *Registry) IncludeAdditionalImports(svc *Service, goPkg GoPackage) { ++ if !r.separatePackage { ++ return ++ } ++ if r.additionalImports == nil { ++ r.additionalImports = make(map[string][]string) ++ } ++ // when generating a separate package for the gateway, we need to generate an import statement ++ // for the gRPC stubs that are no longer in the same package. This is done by adding the grpc ++ // package to the additionalImports list. In order to prepare a valid import statement, we'll replace ++ // the source package name, something like: ../pet/v1/v1petgateway with ../pet/v1/v1petgrpc ++ ++ packageName := strings.TrimSuffix(goPkg.Name, "gateway") + "grpc" ++ svc.GRPCFile = &File{ ++ GoPkg: GoPackage{ ++ // additionally, as the `go_package` option is passed through from the generator, and can only be ++ // set the one time, without making major changes, we'll use the package name sent through the ++ // options as a basis, and replace the source package name with the grpc package name. ++ Path: strings.Replace( ++ filepath.Join(goPkg.Path, packageName), ++ BaseTypePackageSubPath, ++ grpcPackageSubPath, ++ 1, ++ ), ++ Name: strings.Replace(packageName, BaseTypePackageSubPath, grpcPackageSubPath, 1), ++ }, ++ } ++ r.additionalImports[goPkg.Path] = append(r.additionalImports[goPkg.Path], svc.GRPCFile.GoPkg.Path) ++} ++ ++// GetAdditionalImports returns additionalImports ++func (r *Registry) GetAdditionalImports(goPkg GoPackage) []string { ++ if !r.separatePackage || r.additionalImports == nil { ++ return nil ++ } ++ return r.additionalImports[goPkg.Path] ++} +diff --git a/internal/descriptor/registry.go b/internal/descriptor/registry.go +index b057980..7ef4f4a 100644 +--- a/internal/descriptor/registry.go ++++ b/internal/descriptor/registry.go +@@ -164,6 +164,13 @@ type Registry struct { + // allowPatchFeature determines whether to use PATCH feature involving update masks (using google.protobuf.FieldMask). + allowPatchFeature bool + ++ // separatePackage determines whether to output the generated code into a separate package. ++ separatePackage bool ++ ++ // additionalImports is a list of additional imports to be added to the generated code. ++ // N.B. additional imports is not a flag option ++ additionalImports map[string][]string ++ + // preserveRPCOrder, if true, will ensure the order of paths emitted in openapi swagger files mirror + // the order of RPC methods found in proto files. If false, emitted paths will be ordered alphabetically. + preserveRPCOrder bool +@@ -280,6 +287,9 @@ func (r *Registry) loadFile(filePath string, file *protogen.File) { + pkg.Alias = "ext" + cases.Title(language.AmericanEnglish).String(pkg.Name) + } + ++ if r.separatePackage { ++ pkg.Name += "gateway" ++ } + if err := r.ReserveGoPackageAlias(pkg.Name, pkg.Path); err != nil { + for i := 0; ; i++ { + alias := fmt.Sprintf("%s_%d", pkg.Name, i) +diff --git a/internal/descriptor/services.go b/internal/descriptor/services.go +index ad1764c..ae58f7e 100644 +--- a/internal/descriptor/services.go ++++ b/internal/descriptor/services.go +@@ -29,6 +29,7 @@ func (r *Registry) loadServices(file *File) error { + ServiceDescriptorProto: sd, + ForcePrefixedName: r.standalone, + } ++ r.IncludeAdditionalImports(svc, file.GoPkg) + for _, md := range sd.GetMethod() { + if grpclog.V(2) { + grpclog.Infof("Processing %s.%s", sd.GetName(), md.GetName()) +diff --git a/internal/descriptor/types.go b/internal/descriptor/types.go +index 46a4a17..e6fcc5d 100644 +--- a/internal/descriptor/types.go ++++ b/internal/descriptor/types.go +@@ -166,6 +166,9 @@ type Service struct { + *descriptorpb.ServiceDescriptorProto + // File is the file where this service is defined. + File *File ++ // GRPCFile is the file where this service's gRPC stubs are defined. ++ // This is nil if the service's gRPC stubs are defined alongside the messages. ++ GRPCFile *File + // Methods is the list of methods defined in this service. + Methods []*Method + // ForcePrefixedName when set to true, prefixes a type with a package prefix. +@@ -175,7 +178,9 @@ type Service struct { + // FQSN returns the fully qualified service name of this service. + func (s *Service) FQSN() string { + components := []string{""} +- if s.File.Package != nil { ++ if s.GRPCFile != nil && s.GRPCFile.GetPackage() != "" { ++ components = append(components, s.GRPCFile.GetPackage()) ++ } else if s.File.Package != nil { + components = append(components, s.File.GetPackage()) + } + components = append(components, s.GetName()) +@@ -187,7 +192,11 @@ func (s *Service) InstanceName() string { + if !s.ForcePrefixedName { + return s.GetName() + } +- return fmt.Sprintf("%s.%s", s.File.Pkg(), s.GetName()) ++ pkg := s.File.Pkg() ++ if s.GRPCFile != nil { ++ pkg = s.GRPCFile.Pkg() ++ } ++ return fmt.Sprintf("%s.%s", pkg, s.GetName()) + } + + // ClientConstructorName returns name of the Client constructor with package prefix if needed +@@ -196,7 +205,11 @@ func (s *Service) ClientConstructorName() string { + if !s.ForcePrefixedName { + return constructor + } +- return fmt.Sprintf("%s.%s", s.File.Pkg(), constructor) ++ pkg := s.File.Pkg() ++ if s.GRPCFile != nil { ++ pkg = s.GRPCFile.Pkg() ++ } ++ return fmt.Sprintf("%s.%s", pkg, constructor) + } + + // Method wraps descriptorpb.MethodDescriptorProto for richer features. +diff --git a/protoc-gen-grpc-gateway/internal/gengateway/generator.go b/protoc-gen-grpc-gateway/internal/gengateway/generator.go +index d252936..173d3f9 100644 +--- a/protoc-gen-grpc-gateway/internal/gengateway/generator.go ++++ b/protoc-gen-grpc-gateway/internal/gengateway/generator.go +@@ -5,6 +5,7 @@ import ( + "fmt" + "go/format" + "path" ++ "strings" + + "github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor" + gen "github.com/grpc-ecosystem/grpc-gateway/v2/internal/generator" +@@ -23,11 +24,12 @@ type generator struct { + allowPatchFeature bool + standalone bool + useOpaqueAPI bool ++ separatePackage bool + } + + // New returns a new generator which generates grpc gateway files. + func New(reg *descriptor.Registry, useRequestContext bool, registerFuncSuffix string, +- allowPatchFeature, standalone bool, useOpaqueAPI bool) gen.Generator { ++ allowPatchFeature, standalone bool, useOpaqueAPI bool, separatePackage bool) gen.Generator { + var imports []descriptor.GoPackage + for _, pkgpath := range []string{ + "context", +@@ -68,6 +70,7 @@ func New(reg *descriptor.Registry, useRequestContext bool, registerFuncSuffix st + allowPatchFeature: allowPatchFeature, + standalone: standalone, + useOpaqueAPI: useOpaqueAPI, ++ separatePackage: separatePackage, + } + } + +@@ -78,7 +81,7 @@ func (g *generator) Generate(targets []*descriptor.File) ([]*descriptor.Response + grpclog.Infof("Processing %s", file.GetName()) + } + +- code, err := g.generate(file) ++ code, err := g.generate(file, nil) + if errors.Is(err, errNoTargetService) { + if grpclog.V(1) { + grpclog.Infof("%s: %v", file.GetName(), err) +@@ -93,10 +96,64 @@ func (g *generator) Generate(targets []*descriptor.File) ([]*descriptor.Response + grpclog.Errorf("%v: %s", err, code) + return nil, err + } ++ if !g.separatePackage { ++ files = append(files, &descriptor.ResponseFile{ ++ GoPkg: file.GoPkg, ++ CodeGeneratorResponse_File: &pluginpb.CodeGeneratorResponse_File{ ++ Name: proto.String(file.GeneratedFilenamePrefix + ".pb.gw.go"), ++ Content: proto.String(string(formatted)), ++ }, ++ }) ++ continue ++ } ++ goPkg := descriptor.GoPackage{ ++ Path: path.Join(file.GoPkg.Path, file.GoPkg.Name), ++ Name: file.GoPkg.Name, ++ } ++ fileNamePrefix := path.Join(path.Dir(file.GeneratedFilenamePrefix), file.GoPkg.Name, path.Base(file.GeneratedFilenamePrefix)) + files = append(files, &descriptor.ResponseFile{ +- GoPkg: file.GoPkg, ++ GoPkg: goPkg, + CodeGeneratorResponse_File: &pluginpb.CodeGeneratorResponse_File{ +- Name: proto.String(file.GeneratedFilenamePrefix + ".pb.gw.go"), ++ Name: proto.String(fileNamePrefix + ".pb.gw.go"), ++ Content: proto.String(string(formatted)), ++ }, ++ }) ++ // There was a bug where we include an extra path element (the filename), resulting ++ // in a stuttering import path. Fixing this bug cannot involve removing the Go file ++ // generated at the wrong path, because that would be a breaking change. ++ // ++ // Instead, we generate the same file both at the right path and at the wrong path, ++ // marking the file (its package) at the wrong path as deprecated. ++ // ++ // If gateway has a new major version, we should then stop generating at the wrong path. ++ aliasedPackage := &descriptor.GoPackage{ ++ // When generating for generated SDK, the original goPkg points to code generated by "protocolbuffers/go", ++ // but we are aliasing to a package generated by "grpc-ecosystem/gateway". ++ Path: strings.Replace(goPkg.Path, "/"+descriptor.BaseTypePackageSubPath, "/"+descriptor.GatewayPackageSubPath, 1), ++ Name: goPkg.Name, ++ Alias: "gateway", ++ } ++ code, err = g.generate(file, aliasedPackage) ++ if errors.Is(err, errNoTargetService) { ++ if grpclog.V(1) { ++ grpclog.Infof("%s: %v", file.GetName(), err) ++ } ++ continue ++ } ++ if err != nil { ++ return nil, err ++ } ++ formatted, err = format.Source([]byte(code)) ++ if err != nil { ++ grpclog.Errorf("%v: %s", err, code) ++ return nil, err ++ } ++ // The prefix is incorrect, but we are still generating it for backwards compatibility. ++ fileNamePrefix = path.Join(file.GeneratedFilenamePrefix, file.GoPkg.Name, path.Base(file.GeneratedFilenamePrefix)) ++ files = append(files, &descriptor.ResponseFile{ ++ GoPkg: goPkg, ++ CodeGeneratorResponse_File: &pluginpb.CodeGeneratorResponse_File{ ++ Name: proto.String(fileNamePrefix + ".pb.gw.go"), + Content: proto.String(string(formatted)), + }, + }) +@@ -104,7 +161,7 @@ func (g *generator) Generate(targets []*descriptor.File) ([]*descriptor.Response + return files, nil + } + +-func (g *generator) generate(file *descriptor.File) (string, error) { ++func (g *generator) generate(file *descriptor.File, aliasedPkg *descriptor.GoPackage) (string, error) { + pkgSeen := make(map[string]bool) + var imports []descriptor.GoPackage + for _, pkg := range g.baseImports { +@@ -112,6 +169,14 @@ func (g *generator) generate(file *descriptor.File) (string, error) { + imports = append(imports, pkg) + } + ++ for _, additionalImport := range g.reg.GetAdditionalImports(file.GoPkg) { ++ elems := strings.Split(additionalImport, "/") ++ imports = append(imports, descriptor.GoPackage{ ++ Path: additionalImport, ++ Name: elems[len(elems)-1], ++ }) ++ } ++ + if g.standalone { + imports = append(imports, file.GoPkg) + } +@@ -130,6 +195,7 @@ func (g *generator) generate(file *descriptor.File) (string, error) { + } + } + params := param{ ++ AliasedPkg: aliasedPkg, + File: file, + Imports: imports, + UseRequestContext: g.useRequestContext, +diff --git a/protoc-gen-grpc-gateway/internal/gengateway/generator_test.go b/protoc-gen-grpc-gateway/internal/gengateway/generator_test.go +index 0a1cd48..039af20 100644 +--- a/protoc-gen-grpc-gateway/internal/gengateway/generator_test.go ++++ b/protoc-gen-grpc-gateway/internal/gengateway/generator_test.go +@@ -1,6 +1,9 @@ + package gengateway + + import ( ++ "fmt" ++ "path/filepath" ++ "strings" + "testing" + + "github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor" +@@ -76,6 +79,18 @@ func newExampleFileDescriptorWithGoPkg(gp *descriptor.GoPackage, filenamePrefix + } + } + ++func newExampleFileDescriptorWithGoPkgWithoutBinding(gp *descriptor.GoPackage, filenamePrefix string) *descriptor.File { ++ file := newExampleFileDescriptorWithGoPkg(gp, filenamePrefix) ++ for _, service := range file.Services { ++ for _, method := range service.Methods { ++ if method != nil { ++ method.Bindings = nil ++ } ++ } ++ } ++ return file ++} ++ + func TestGenerator_Generate(t *testing.T) { + + // Test with Open Struct API (default) +@@ -234,3 +249,232 @@ func buildBodyImportTestFile(t *testing.T) (*descriptor.Registry, *descriptor.Fi + } + return reg, crossLinkFixture(file) + } ++ ++func TestGenerator_GenerateSeparatePackage(t *testing.T) { ++ reg := descriptor.NewRegistry() ++ reg.SetSeparatePackage(true) ++ reg.SetStandalone(true) ++ g := New(reg, true, "Handler", true, true, true, true) ++ targets := []*descriptor.File{ ++ crossLinkFixture(newExampleFileDescriptorWithGoPkg(&descriptor.GoPackage{ ++ Path: "example.com/mymodule/foo/bar/v1", ++ Name: "v1" + "gateway", // Name is appended with "gateway" with standalone set to true. ++ Alias: "extalias", ++ }, "foo/bar/v1/example")), ++ } ++ // Set ForcePrefixedName (usually set when standalone=true). ++ for _, f := range targets { ++ for _, msg := range f.Messages { ++ msg.ForcePrefixedName = true ++ for _, field := range msg.Fields { ++ field.ForcePrefixedName = true ++ } ++ } ++ for _, enum := range f.Enums { ++ enum.ForcePrefixedName = true ++ } ++ for _, svc := range f.Services { ++ packageName := strings.TrimSuffix(svc.File.GoPkg.Name, "gateway") + "grpc" ++ svc.ForcePrefixedName = true ++ // replicates behavior in internal/descriptor/services.go (loadServices) ++ svc.GRPCFile = &descriptor.File{ ++ GoPkg: descriptor.GoPackage{ ++ Path: strings.Replace( ++ filepath.Join(svc.File.GoPkg.Path, packageName), ++ "protocolbuffers/go", ++ "grpc/go", ++ 1, ++ ), ++ Name: strings.Replace(packageName, "protocolbuffers/go", "grpc/go", 1), ++ }, ++ } ++ reg.IncludeAdditionalImports(svc, f.GoPkg) ++ } ++ } ++ result, err := g.Generate(targets) ++ if err != nil { ++ t.Fatalf("failed to generate stubs: %v", err) ++ } ++ if len(result) != 2 { ++ t.Fatalf("expected to generate 2 files, got: %d", len(result)) ++ } ++ expectedName := "foo/bar/v1/v1gateway/example.pb.gw.go" ++ expectedGoPkgPath := "example.com/mymodule/foo/bar/v1/v1gateway" ++ expectedGoPkgName := "v1gateway" ++ correctFile := result[0] ++ if correctFile == nil { ++ t.Fatal("result is nil") ++ } ++ if correctFile.GetName() != expectedName { ++ t.Errorf("invalid name %q, expected %q", correctFile.GetName(), expectedName) ++ } ++ if correctFile.GoPkg.Path != expectedGoPkgPath { ++ t.Errorf("invalid path %q, expected %q", result[0].GoPkg.Path, expectedGoPkgPath) ++ } ++ if correctFile.GoPkg.Name != expectedGoPkgName { ++ t.Errorf("invalid name %q, expected %q", result[0].GoPkg.Name, expectedGoPkgName) ++ } ++ // Require the two dependencies to be declared as imported packages ++ correctFileContent := correctFile.GetContent() ++ for _, expectedImport := range []string{ ++ `extalias "example.com/mymodule/foo/bar/v1"`, ++ `"example.com/mymodule/foo/bar/v1/v1grpc"`, ++ } { ++ if !strings.Contains(correctFileContent, expectedImport) { ++ t.Errorf("expected to find import %q in the generated file: %s", expectedImport, correctFileContent[:400]) ++ } ++ } ++ ++ expectedName = "foo/bar/v1/example/v1gateway/example.pb.gw.go" ++ // wrong path but correct go package ++ aliasFile := result[1] ++ if aliasFile == nil { ++ t.Fatal("result is nil") ++ } ++ if aliasFile.GetName() != expectedName { ++ t.Errorf("invalid name %q, expected %q", aliasFile.GetName(), expectedName) ++ } ++ if aliasFile.GoPkg.Path != expectedGoPkgPath { ++ t.Errorf("invalid path %q, expected %q", aliasFile.GoPkg.Path, expectedGoPkgPath) ++ } ++ if aliasFile.GoPkg.Name != expectedGoPkgName { ++ t.Errorf("invalid name %q, expected %q", aliasFile.GoPkg.Name, expectedGoPkgName) ++ } ++ aliasFileContent := aliasFile.GetContent() ++ // Require the two dependencies to be declared as imported packages ++ expectedImport := `gateway "example.com/mymodule/foo/bar/v1/v1gateway"` ++ if !strings.Contains(aliasFileContent, expectedImport) { ++ t.Errorf("expected to find import %q in the generated file: %s...", expectedImport, aliasFileContent[:500]) ++ } ++ aliasedFunctions := []string{ ++ "RegisterExampleServiceHandlerServer", ++ "RegisterExampleServiceHandlerClient", ++ "RegisterExampleServiceHandlerFromEndpoint", ++ "RegisterExampleServiceHandler", ++ } ++ for _, aliasedFunction := range aliasedFunctions { ++ aliasDefinition := fmt.Sprintf("%[1]s = gateway.%[1]s", aliasedFunction) ++ if !strings.Contains(aliasFileContent, aliasDefinition) { ++ t.Fatalf("expected %q in the alias file: %s", aliasDefinition, aliasFileContent) ++ } ++ if strings.Contains(correctFileContent, aliasDefinition) { ++ t.Fatalf("unexpected alias %q in the correct file: %s", aliasDefinition, correctFileContent) ++ } ++ } ++} ++ ++func TestGenerator_GenerateSeparatePackage_WithoutBinding(t *testing.T) { ++ reg := descriptor.NewRegistry() ++ reg.SetSeparatePackage(true) ++ reg.SetStandalone(true) ++ g := New(reg, true, "Handler", true, true, true, true) ++ targets := []*descriptor.File{ ++ crossLinkFixture(newExampleFileDescriptorWithGoPkgWithoutBinding(&descriptor.GoPackage{ ++ Path: "example.com/mymodule/foo/bar/v1", ++ Name: "v1" + "gateway", ++ Alias: "extalias", ++ }, "foo/bar/v1/example")), ++ } ++ result, err := g.Generate(targets) ++ if err != nil { ++ t.Fatalf("failed to generate stubs: %v", err) ++ } ++ if len(result) != 0 { ++ t.Fatalf("expected to generate 0 file, got: %d", len(result)) ++ } ++} ++ ++func TestGenerator_GenerateSeparatePackage_WithOmitPackageDoc_Local(t *testing.T) { ++ reg := descriptor.NewRegistry() ++ reg.SetSeparatePackage(true) ++ reg.SetStandalone(true) ++ reg.SetOmitPackageDoc(true) ++ g := New(reg, true, "Handler", true, true, true, true) ++ targets := []*descriptor.File{ ++ crossLinkFixture(newExampleFileDescriptorWithGoPkg(&descriptor.GoPackage{ ++ Path: "example.com/mymodule/foo/bar/v1", ++ Name: "v1" + "gateway", ++ Alias: "extalias", ++ }, "foo/bar/v1/example")), ++ } ++ result, err := g.Generate(targets) ++ if err != nil { ++ t.Fatalf("failed to generate stubs: %v", err) ++ } ++ if len(result) != 2 { ++ t.Fatalf("expected to generate 2 files, got: %d", len(result)) ++ } ++ correctFileContent := result[0].GetContent() ++ if strings.Contains(correctFileContent, "Deprecated:") { ++ t.Errorf("the correct file should not be deprecated: %s...", correctFileContent[:500]) ++ } ++ deprecationDoc := `/* ++Deprecated: This package has moved to "example.com/mymodule/foo/bar/v1/v1gateway". Use that import path instead. ++*/` ++ aliasFileContent := result[1].GetContent() ++ // Even though omit_package_doc is set, we still need to deprecate the package. ++ if !strings.Contains(aliasFileContent, deprecationDoc) { ++ t.Errorf("expected to find deprecation doc in the alias file: %s...", aliasFileContent[:500]) ++ } ++} ++ ++func TestGenerator_GenerateSeparatePackage_WithOmitPackageDoc_Generate_SDK(t *testing.T) { ++ reg := descriptor.NewRegistry() ++ reg.SetSeparatePackage(true) ++ reg.SetStandalone(true) ++ reg.SetOmitPackageDoc(true) ++ g := New(reg, true, "Handler", true, true, true, true) ++ targets := []*descriptor.File{ ++ crossLinkFixture(newExampleFileDescriptorWithGoPkg(&descriptor.GoPackage{ ++ Path: "example.com/gen/go/owner/module/protocolbuffers/go/foo/bar/v1", ++ Name: "v1" + "gateway", ++ Alias: "extalias", ++ }, "foo/bar/v1/example")), ++ } ++ result, err := g.Generate(targets) ++ if err != nil { ++ t.Fatalf("failed to generate stubs: %v", err) ++ } ++ if len(result) != 2 { ++ t.Fatalf("expected to generate 2 files, got: %d", len(result)) ++ } ++ correctFileContent := result[0].GetContent() ++ if strings.Contains(correctFileContent, "Deprecated:") { ++ t.Errorf("the correct file should not be deprecated: %s...", correctFileContent[:500]) ++ } ++ deprecationDoc := `/* ++Deprecated: This package has moved to "example.com/gen/go/owner/module/grpc-ecosystem/gateway/v2/foo/bar/v1/v1gateway". Use that import path instead. ++*/` ++ aliasFileContent := result[1].GetContent() ++ // Even though omit_package_doc is set, we still need to deprecate the package. ++ if !strings.Contains(aliasFileContent, deprecationDoc) { ++ t.Errorf("expected to find deprecation doc in the alias file: %s...", aliasFileContent[:500]) ++ } ++} ++ ++func TestGenerator_GenerateSeparatePackage_WithoutService(t *testing.T) { ++ reg := descriptor.NewRegistry() ++ reg.SetSeparatePackage(true) ++ reg.SetStandalone(true) ++ g := New(reg, true, "Handler", true, true, true, true) ++ targets := []*descriptor.File{ ++ { ++ FileDescriptorProto: &descriptorpb.FileDescriptorProto{ ++ Name: proto.String("example.proto"), ++ Package: proto.String("example"), ++ }, ++ GoPkg: descriptor.GoPackage{ ++ Path: "foo/bar/baz/gen/v1", ++ Name: "v1", ++ }, ++ GeneratedFilenamePrefix: "gen/v1/example", ++ }, ++ } ++ result, err := g.Generate(targets) ++ if err != nil { ++ t.Fatalf("failed to generate stubs: %v", err) ++ } ++ if len(result) != 0 { ++ t.Fatalf("expected to generate 0 file, got: %d", len(result)) ++ } ++} +diff --git a/protoc-gen-grpc-gateway/internal/gengateway/template.go b/protoc-gen-grpc-gateway/internal/gengateway/template.go +index a08e0b4..e4a5d30 100644 +--- a/protoc-gen-grpc-gateway/internal/gengateway/template.go ++++ b/protoc-gen-grpc-gateway/internal/gengateway/template.go +@@ -17,6 +17,7 @@ import ( + + type param struct { + *descriptor.File ++ AliasedPkg *descriptor.GoPackage + Imports []descriptor.GoPackage + UseRequestContext bool + RegisterFuncSuffix string +@@ -170,6 +171,7 @@ func (f queryParamFilter) String() string { + } + + type trailerParams struct { ++ AliasedPkg *descriptor.GoPackage + Services []*descriptor.Service + UseRequestContext bool + RegisterFuncSuffix string +@@ -200,11 +202,14 @@ func applyTemplate(p param, reg *descriptor.Registry) (string, error) { + methName := casing.Camel(*meth.Name) + meth.Name = &methName + for _, b := range meth.Bindings { ++ methodWithBindingsSeen = true ++ if p.AliasedPkg != nil { ++ break ++ } + if err := reg.CheckDuplicateAnnotation(b.HTTPMethod, b.PathTmpl.Template, svc); err != nil { + return "", err + } + +- methodWithBindingsSeen = true + if err := handlerTemplate.Execute(w, binding{ + Binding: b, + Registry: reg, +@@ -234,6 +239,7 @@ func applyTemplate(p param, reg *descriptor.Registry) (string, error) { + } + + tp := trailerParams{ ++ AliasedPkg: p.AliasedPkg, + Services: targetServices, + UseRequestContext: p.UseRequestContext, + RegisterFuncSuffix: p.RegisterFuncSuffix, +@@ -270,8 +276,18 @@ var ( + Package {{ .GoPkg.Name }} is a reverse proxy. + + It translates gRPC into RESTful JSON APIs. ++{{if $.AliasedPkg}} ++Deprecated: This package has moved to "{{$.AliasedPkg.Path}}". Use that import path instead. ++{{- end}} ++*/ ++{{- else if $.AliasedPkg}} ++/* ++Deprecated: This package has moved to "{{$.AliasedPkg.Path}}". Use that import path instead. + */{{ end }} + package {{ .GoPkg.Name }} ++{{- if $.AliasedPkg}} ++import {{$.AliasedPkg}} ++{{- else}} + import ( + {{ range $i := .Imports }}{{ if $i.Standard }}{{ $i | printf "%s\n" }}{{ end }}{{ end }} + +@@ -288,6 +304,7 @@ var ( + _ = utilities.NewDoubleArray + _ = metadata.Join + ) ++{{- end}} + `)) + + handlerTemplate = template.Must(template.New("handler").Parse(` +@@ -781,6 +798,9 @@ func local_request_{{ .Method.Service.GetName }}_{{ .Method.GetName }}_{{ .Index + }`)) + + localTrailerTemplate = template.Must(template.New("local-trailer").Funcs(funcMap).Parse(` ++{{- if $.AliasedPkg }} ++var ( ++{{- end }} + {{ $UseRequestContext := .UseRequestContext }} + {{ range $svc := .Services }} + // Register{{ $svc.GetName }}{{ $.RegisterFuncSuffix }}Server registers the http handlers for service {{ $svc.GetName }} to "mux". +@@ -788,6 +808,9 @@ func local_request_{{ .Method.Service.GetName }}_{{ .Method.GetName }}_{{ .Index + // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. + // Note that using this registration option will cause many gRPC library features to stop working. Consider using Register{{ $svc.GetName }}{{ $.RegisterFuncSuffix }}FromEndpoint instead. + // GRPC interceptors will not work for this type of registration. To use interceptors, you must use the "runtime.WithMiddlewares" option in the "runtime.NewServeMux" call. ++{{- if $.AliasedPkg}} ++ Register{{$svc.GetName}}{{$.RegisterFuncSuffix}}Server = {{$.AliasedPkg.Alias}}.Register{{$svc.GetName}}{{$.RegisterFuncSuffix}}Server ++{{- else}} + func Register{{ $svc.GetName }}{{ $.RegisterFuncSuffix }}Server(ctx context.Context, mux *runtime.ServeMux, server {{ $svc.InstanceName }}Server) error { + {{- range $m := $svc.Methods }} + {{- range $b := $m.Bindings }} +@@ -836,6 +859,7 @@ func Register{{ $svc.GetName }}{{ $.RegisterFuncSuffix }}Server(ctx context.Cont + {{- end }} + return nil + } ++{{end}} + {{ end }}`)) + + trailerTemplate = template.Must(template.New("trailer").Funcs(funcMap).Parse(` +@@ -844,6 +868,9 @@ func Register{{ $svc.GetName }}{{ $.RegisterFuncSuffix }}Server(ctx context.Cont + {{range $svc := .Services}} + // Register{{ $svc.GetName }}{{ $.RegisterFuncSuffix }}FromEndpoint is same as Register{{ $svc.GetName }}{{ $.RegisterFuncSuffix }} but + // automatically dials to "endpoint" and closes the connection when "ctx" gets done. ++{{- if $.AliasedPkg}} ++ Register{{$svc.GetName}}{{$.RegisterFuncSuffix}}FromEndpoint = {{$.AliasedPkg.Alias}}.Register{{$svc.GetName}}{{$.RegisterFuncSuffix}}FromEndpoint ++{{- else}} + func Register{{ $svc.GetName }}{{ $.RegisterFuncSuffix }}FromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.NewClient(endpoint, opts...) + if err != nil { +@@ -865,18 +892,26 @@ func Register{{ $svc.GetName }}{{ $.RegisterFuncSuffix }}FromEndpoint(ctx contex + }() + return Register{{ $svc.GetName }}{{ $.RegisterFuncSuffix }}(ctx, mux, conn) + } ++{{- end}} + + // Register{{ $svc.GetName}}{{ $.RegisterFuncSuffix}} registers the http handlers for service {{ $svc.GetName }} to "mux". + // The handlers forward requests to the grpc endpoint over "conn". ++{{- if $.AliasedPkg}} ++ Register{{$svc.GetName}}{{$.RegisterFuncSuffix}} = {{$.AliasedPkg.Alias}}.Register{{$svc.GetName}}{{$.RegisterFuncSuffix}} ++{{- else}} + func Register{{ $svc.GetName }}{{ $.RegisterFuncSuffix }}(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return Register{{ $svc.GetName }}{{ $.RegisterFuncSuffix }}Client(ctx, mux, {{ $svc.ClientConstructorName }}(conn)) + } ++{{- end}} + + // Register{{ $svc.GetName }}{{ $.RegisterFuncSuffix }}Client registers the http handlers for service {{ $svc.GetName }} + // to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "{{ $svc.InstanceName }}Client". + // Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "{{ $svc.InstanceName }}Client" + // doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in + // "{{ $svc.InstanceName }}Client" to call the correct interceptors. This client ignores the HTTP middlewares. ++{{- if $.AliasedPkg}} ++ Register{{$svc.GetName}}{{$.RegisterFuncSuffix}}Client = {{$.AliasedPkg.Alias}}.Register{{$svc.GetName}}{{$.RegisterFuncSuffix}}Client ++{{- else}} + func Register{{ $svc.GetName }}{{ $.RegisterFuncSuffix }}Client(ctx context.Context, mux *runtime.ServeMux, client {{ $svc.InstanceName }}Client) error { + {{- range $m := $svc.Methods }} + {{- range $b := $m.Bindings }} +@@ -963,5 +998,9 @@ var ( + {{- end }} + {{- end }} + ) +-{{ end }}`)) ++{{end}} ++{{end}} ++{{- if $.AliasedPkg}} ++) ++{{- end}}`)) + ) +diff --git a/protoc-gen-grpc-gateway/main.go b/protoc-gen-grpc-gateway/main.go +index 862a8fc..c17e4c3 100644 +--- a/protoc-gen-grpc-gateway/main.go ++++ b/protoc-gen-grpc-gateway/main.go +@@ -10,6 +10,7 @@ + package main + + import ( ++ "errors" + "flag" + "fmt" + "os" +@@ -37,6 +38,7 @@ var ( + warnOnUnboundMethods = flag.Bool("warn_on_unbound_methods", false, "emit a warning message if an RPC method has no HttpRule annotation") + generateUnboundMethods = flag.Bool("generate_unbound_methods", false, "generate proxy methods even for RPC methods that have no HttpRule annotation") + useOpaqueAPI = flag.Bool("use_opaque_api", false, "generate code compatible with the new Opaque API instead of the older Open Struct API") ++ separatePackage = flag.Bool("separate_package", false, "generate gateway code to v1gateway package (requires standalone=true).") + + _ = flag.Bool("logtostderr", false, "Legacy glog compatibility. This flag is a no-op, you can safely remove it") + ) +@@ -74,15 +76,23 @@ func main() { + ParamFunc: flag.CommandLine.Set, + }.Run(func(gen *protogen.Plugin) error { + reg := descriptor.NewRegistry() +- + if err := applyFlags(reg); err != nil { + return err + } +- ++ if *separatePackage && !*standalone { ++ return errors.New("option separate_package=true must be specified with standalone=true") ++ } ++ generator := gengateway.New( ++ reg, ++ *useRequestContext, ++ *registerFuncSuffix, ++ *allowPatchFeature, ++ *standalone, ++ *useOpaqueAPI, ++ *separatePackage, ++ ) + codegenerator.SetSupportedFeaturesOnPluginGen(gen) + +- generator := gengateway.New(reg, *useRequestContext, *registerFuncSuffix, *allowPatchFeature, *standalone, *useOpaqueAPI) +- + if grpclog.V(1) { + grpclog.Infof("Parsing code generator request") + } +@@ -136,6 +146,7 @@ func applyFlags(reg *descriptor.Registry) error { + } + reg.SetStandalone(*standalone) + reg.SetAllowDeleteBody(*allowDeleteBody) ++ reg.SetSeparatePackage(*separatePackage) + + flag.Visit(func(f *flag.Flag) { + if f.Name == "allow_repeated_fields_in_body" { diff --git a/registry/grpc-ecosystem/openapiv2/.dockerignore b/registry/grpc-ecosystem/openapiv2/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/grpc-ecosystem/openapiv2/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/grpc-ecosystem/openapiv2/Dockerfile b/registry/grpc-ecosystem/openapiv2/Dockerfile new file mode 100644 index 0000000..e85c930 --- /dev/null +++ b/registry/grpc-ecosystem/openapiv2/Dockerfile @@ -0,0 +1,16 @@ +# syntax=docker/dockerfile:1.19 +FROM golang:1.26-alpine3.22 AS build + +ARG VERSION +ARG GO_MODULE +ARG BINARY_NAME + +RUN apk add upx --no-cache +RUN --mount=type=cache,target=/go/pkg/mod \ + CGO_ENABLED=0 go install -ldflags "-s -w" -trimpath ${GO_MODULE}@${VERSION} +RUN cp /go/bin/${BINARY_NAME} /plugin 2>/dev/null \ + || cp /go/bin/linux_*/${BINARY_NAME} /plugin \ + && upx --best --lzma /plugin + +FROM scratch +COPY --from=build /plugin /plugin diff --git a/registry/grpc-ecosystem/openapiv2/plugin.yaml b/registry/grpc-ecosystem/openapiv2/plugin.yaml new file mode 100644 index 0000000..b1cf798 --- /dev/null +++ b/registry/grpc-ecosystem/openapiv2/plugin.yaml @@ -0,0 +1,35 @@ +binary: protoc-gen-openapiv2 +build_args: + GO_MODULE: github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2 +versions: + - v2.29.0 + - v2.28.0 + - v2.27.7 + - v2.27.6 + - v2.27.5 + - v2.27.4 + - v2.27.3 + - v2.27.2 + - v2.27.1 + - v2.27.0 + - v2.26.3 + - v2.26.1 + - v2.26.0 + - v2.25.1 + - v2.24.0 + - v2.23.0 + - v2.22.0 + - v2.21.0 + - v2.20.0 + - v2.19.1 + - v2.19.0 + - v2.18.1 + - v2.18.0 + - v2.17.1 + - v2.17.0 + - v2.16.2 + - v2.16.1 + - v2.16.0 + - v2.15.2 + - v2.15.1 + - v2.15.0 diff --git a/registry/grpc/cpp/.dockerignore b/registry/grpc/cpp/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/grpc/cpp/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/grpc/cpp/Dockerfile b/registry/grpc/cpp/Dockerfile new file mode 100644 index 0000000..a48bb08 --- /dev/null +++ b/registry/grpc/cpp/Dockerfile @@ -0,0 +1,32 @@ +# syntax=docker/dockerfile:1.22 +FROM debian:trixie-20260316@sha256:55a15a112b42be10bfc8092fcc40b6748dc236f7ef46a358d9392b339e9d60e8 AS build +ARG VERSION + +ARG TARGETARCH +ARG BAZEL_OPTS="--host_jvm_args=-Djava.net.preferIPv4Stack=true" + +RUN apt-get update \ + && apt-get install -y curl git cmake build-essential autoconf clang libc++-dev libtool pkg-config unzip zip +RUN curl -fsSL -o /usr/local/bin/bazelisk https://github.com/bazelbuild/bazelisk/releases/download/v1.26.0/bazelisk-linux-${TARGETARCH} \ + && chmod +x /usr/local/bin/bazelisk \ + && mkdir /build \ + && chown nobody:nogroup /build \ + && usermod --home /build nobody + +USER nobody +WORKDIR /build + +RUN git clone --depth 1 --branch ${VERSION} https://github.com/grpc/grpc +WORKDIR /build/grpc +RUN bazelisk ${BAZEL_OPTS} build //src/compiler:grpc_plugin_support +RUN bazelisk ${BAZEL_OPTS} build //src/compiler:grpc_cpp_plugin + +FROM gcr.io/distroless/cc-debian13:latest@sha256:e1cc90d06703f5dc30ae869fbfce78fce688f21a97efecd226375233a882e62f AS base +ARG VERSION + +FROM scratch +ARG VERSION +COPY --link --from=base / / +COPY --link --from=build --chmod=0755 /build/grpc/bazel-bin/src/compiler/grpc_cpp_plugin_binary . +USER nobody +ENTRYPOINT ["/grpc_cpp_plugin_binary"] diff --git a/registry/grpc/cpp/plugin.yaml b/registry/grpc/cpp/plugin.yaml new file mode 100644 index 0000000..c91071e --- /dev/null +++ b/registry/grpc/cpp/plugin.yaml @@ -0,0 +1,63 @@ +binary: grpc_cpp_plugin_binary +versions: + - v1.80.0 + - v1.78.1 + - v1.78.0 + - v1.76.0 + - v1.75.1 + - v1.75.0 + - v1.74.1 + - v1.74.0 + - v1.73.1 + - v1.73.0 + - v1.72.1 + - v1.72.0 + - v1.71.0 + - v1.70.1 + - v1.70.0 + - v1.69.0 + - v1.68.2 + - v1.68.1 + - v1.68.0 + - v1.67.1 + - v1.67.0 + - v1.66.2 + - v1.66.1 + - v1.66.0 + - v1.65.5 + - v1.65.4 + - v1.65.2 + - v1.65.1 + - v1.65.0 + - v1.64.2 + - v1.64.1 + - v1.64.0 + - v1.63.0 + - v1.62.2 + - v1.62.1 + - v1.62.0 + - v1.61.1 + - v1.61.0 + - v1.60.0 + - v1.59.3 + - v1.59.2 + - v1.59.1 + - v1.59.0 + - v1.58.1 + - v1.58.0 + - v1.57.0 + - v1.56.2 + - v1.56.1 + - v1.56.0 + - v1.55.1 + - v1.55.0 + - v1.54.2 + - v1.54.1 + - v1.54.0 + - v1.53.0 + - v1.52.1 + - v1.52.0 + - v1.51.1 + - v1.51.0 + - v1.50.1 + - v1.50.0 diff --git a/registry/grpc/csharp/.dockerignore b/registry/grpc/csharp/.dockerignore new file mode 100644 index 0000000..b2a42ae --- /dev/null +++ b/registry/grpc/csharp/.dockerignore @@ -0,0 +1,3 @@ +* +!Dockerfile +!build.csproj diff --git a/registry/grpc/csharp/Dockerfile b/registry/grpc/csharp/Dockerfile new file mode 100644 index 0000000..8f5d1ab --- /dev/null +++ b/registry/grpc/csharp/Dockerfile @@ -0,0 +1,39 @@ +# syntax=docker/dockerfile:1.22 +FROM debian:trixie-20260316@sha256:55a15a112b42be10bfc8092fcc40b6748dc236f7ef46a358d9392b339e9d60e8 AS build +ARG VERSION + +ARG TARGETARCH +ARG BAZEL_OPTS="--host_jvm_args=-Djava.net.preferIPv4Stack=true" + +RUN apt-get update \ + && apt-get install -y curl git cmake build-essential autoconf clang libc++-dev libtool pkg-config unzip zip +RUN curl -fsSL -o /usr/local/bin/bazelisk https://github.com/bazelbuild/bazelisk/releases/download/v1.26.0/bazelisk-linux-${TARGETARCH} \ + && chmod +x /usr/local/bin/bazelisk \ + && mkdir /build \ + && chown nobody:nogroup /build \ + && usermod --home /build nobody + +USER nobody +WORKDIR /build + +RUN git clone --depth 1 --branch ${VERSION} https://github.com/grpc/grpc +WORKDIR /build/grpc +RUN bazelisk ${BAZEL_OPTS} build //src/compiler:grpc_plugin_support +RUN bazelisk ${BAZEL_OPTS} build //src/compiler:grpc_csharp_plugin + +FROM mcr.microsoft.com/dotnet/sdk:8.0.419-bookworm-slim@sha256:a9330090b730c2abde8f3f43b3d1e24e4b8cba028de7bab1a7fdcd50cb7a3b7e AS dotnetrestore +ARG VERSION +WORKDIR /build +COPY --link ./build.csproj /build/build.csproj +RUN mkdir /nuget && dotnet restore --packages /nuget + +FROM gcr.io/distroless/cc-debian13:latest@sha256:e1cc90d06703f5dc30ae869fbfce78fce688f21a97efecd226375233a882e62f AS base +ARG VERSION + +FROM scratch +ARG VERSION +COPY --link --from=base / / +COPY --link --from=dotnetrestore /nuget /nuget +COPY --link --from=build --chmod=0755 /build/grpc/bazel-bin/src/compiler/grpc_csharp_plugin_binary . +USER nobody +ENTRYPOINT ["/grpc_csharp_plugin_binary"] diff --git a/registry/grpc/csharp/build.csproj b/registry/grpc/csharp/build.csproj new file mode 100644 index 0000000..914018c --- /dev/null +++ b/registry/grpc/csharp/build.csproj @@ -0,0 +1,9 @@ + + + netstandard2.0 + + + + + + diff --git a/registry/grpc/csharp/plugin.yaml b/registry/grpc/csharp/plugin.yaml new file mode 100644 index 0000000..614d75f --- /dev/null +++ b/registry/grpc/csharp/plugin.yaml @@ -0,0 +1,63 @@ +binary: grpc_csharp_plugin_binary +versions: + - v1.80.0 + - v1.78.1 + - v1.78.0 + - v1.76.0 + - v1.75.1 + - v1.75.0 + - v1.74.1 + - v1.74.0 + - v1.73.1 + - v1.73.0 + - v1.72.1 + - v1.72.0 + - v1.71.0 + - v1.70.1 + - v1.70.0 + - v1.69.0 + - v1.68.2 + - v1.68.1 + - v1.68.0 + - v1.67.1 + - v1.67.0 + - v1.66.2 + - v1.66.1 + - v1.66.0 + - v1.65.5 + - v1.65.4 + - v1.65.2 + - v1.65.1 + - v1.65.0 + - v1.64.2 + - v1.64.1 + - v1.64.0 + - v1.63.0 + - v1.62.2 + - v1.62.1 + - v1.62.0 + - v1.61.1 + - v1.61.0 + - v1.60.0 + - v1.59.3 + - v1.59.2 + - v1.59.1 + - v1.59.0 + - v1.58.1 + - v1.58.0 + - v1.57.0 + - v1.56.2 + - v1.56.1 + - v1.56.0 + - v1.55.1 + - v1.55.0 + - v1.54.2 + - v1.54.1 + - v1.54.0 + - v1.53.0 + - v1.52.1 + - v1.52.0 + - v1.51.1 + - v1.51.0 + - v1.50.1 + - v1.50.0 diff --git a/registry/grpc/go/.dockerignore b/registry/grpc/go/.dockerignore new file mode 100644 index 0000000..7feaa9b --- /dev/null +++ b/registry/grpc/go/.dockerignore @@ -0,0 +1,3 @@ +* +!Dockerfile +!separate-package.patch diff --git a/registry/grpc/go/Dockerfile b/registry/grpc/go/Dockerfile new file mode 100644 index 0000000..562222d --- /dev/null +++ b/registry/grpc/go/Dockerfile @@ -0,0 +1,22 @@ +# syntax=docker/dockerfile:1.23 +FROM --platform=$BUILDPLATFORM golang:1.26.3-trixie@sha256:d08bf3ed2bd263088ca8e23fefaf10f1b71769f6932f0a4017ba28d2a5baf001 AS build +ARG VERSION + +ARG TARGETOS TARGETARCH +ENV CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH + +WORKDIR /tmp +RUN git clone --depth 1 --branch cmd/protoc-gen-go-grpc/${VERSION} https://github.com/grpc/grpc-go.git +WORKDIR /tmp/grpc-go +COPY separate-package.patch /tmp/grpc-go +RUN git apply separate-package.patch +WORKDIR /tmp/grpc-go/cmd/protoc-gen-go-grpc +RUN --mount=type=cache,target=/go/pkg/mod \ + go build -o protoc-gen-go-grpc -ldflags "-s -w" -trimpath + +FROM scratch +ARG VERSION +COPY --from=build --link /etc/passwd /etc/passwd +COPY --from=build --link --chown=root:root /tmp/grpc-go/cmd/protoc-gen-go-grpc/protoc-gen-go-grpc . +USER nobody +ENTRYPOINT [ "/protoc-gen-go-grpc" ] diff --git a/registry/grpc/go/plugin.yaml b/registry/grpc/go/plugin.yaml new file mode 100644 index 0000000..ab32e21 --- /dev/null +++ b/registry/grpc/go/plugin.yaml @@ -0,0 +1,9 @@ +binary: protoc-gen-go-grpc +versions: + - v1.6.2 + - v1.6.1 + - v1.6.0 + - v1.5.1 + - v1.4.0 + - v1.3.0 + - v1.2.0 diff --git a/registry/grpc/go/separate-package.patch b/registry/grpc/go/separate-package.patch new file mode 100644 index 0000000..a84f5d7 --- /dev/null +++ b/registry/grpc/go/separate-package.patch @@ -0,0 +1,62 @@ +diff --git a/cmd/protoc-gen-go-grpc/grpc.go b/cmd/protoc-gen-go-grpc/grpc.go +index abc21602..7d9dd638 100644 +--- a/cmd/protoc-gen-go-grpc/grpc.go ++++ b/cmd/protoc-gen-go-grpc/grpc.go +@@ -20,6 +20,7 @@ package main + + import ( + "fmt" ++ "path" + "strconv" + "strings" + +@@ -135,8 +136,27 @@ func generateFile(gen *protogen.Plugin, file *protogen.File) *protogen.Generated + if len(file.Services) == 0 { + return nil + } +- filename := file.GeneratedFilenamePrefix + "_grpc.pb.go" +- g := gen.NewGeneratedFile(filename, file.GoImportPath) ++ var g *protogen.GeneratedFile ++ if !*separatePackage { ++ filename := file.GeneratedFilenamePrefix + "_grpc.pb.go" ++ g = gen.NewGeneratedFile(filename, file.GoImportPath) ++ } else { ++ file.GoPackageName += "grpc" ++ dir := path.Dir(file.GeneratedFilenamePrefix) ++ base := path.Base(file.GeneratedFilenamePrefix) ++ file.GeneratedFilenamePrefix = path.Join( ++ dir, ++ string(file.GoPackageName), ++ base, ++ ) ++ g = gen.NewGeneratedFile( ++ file.GeneratedFilenamePrefix+"_grpc.pb.go", ++ protogen.GoImportPath(path.Join( ++ string(file.GoImportPath), ++ string(file.GoPackageName), ++ )), ++ ) ++ } + // Attach all comments associated with the syntax field. + genLeadingComments(g, file.Desc.SourceLocations().ByPath(protoreflect.SourcePath{fileDescriptorProtoSyntaxFieldNumber})) + g.P("// Code generated by protoc-gen-go-grpc. DO NOT EDIT.") +diff --git a/cmd/protoc-gen-go-grpc/main.go b/cmd/protoc-gen-go-grpc/main.go +index 183ba697..d0e1be60 100644 +--- a/cmd/protoc-gen-go-grpc/main.go ++++ b/cmd/protoc-gen-go-grpc/main.go +@@ -45,6 +45,7 @@ const version = "1.6.2" + const version = "1.6.2" + + var requireUnimplemented *bool ++var separatePackage *bool + + func main() { + showVersion := flag.Bool("version", false, "print the version and exit") +@@ -56,6 +57,7 @@ func main() { + + var flags flag.FlagSet + requireUnimplemented = flags.Bool("require_unimplemented_servers", true, "set to false to match legacy behavior") ++ separatePackage = flags.Bool("separate_package", false, "set to true to write generated files to a separate grpc package") + + protogen.Options{ + ParamFunc: flags.Set, diff --git a/registry/grpc/java/.dockerignore b/registry/grpc/java/.dockerignore new file mode 100644 index 0000000..7ff6309 --- /dev/null +++ b/registry/grpc/java/.dockerignore @@ -0,0 +1,3 @@ +* +!Dockerfile +!pom.xml diff --git a/registry/grpc/java/Dockerfile b/registry/grpc/java/Dockerfile new file mode 100644 index 0000000..b26c930 --- /dev/null +++ b/registry/grpc/java/Dockerfile @@ -0,0 +1,33 @@ +# syntax=docker/dockerfile:1.23 +FROM debian:trixie-20260421@sha256:35b8ff74ead4880f22090b617372daff0ccae742eb5674455d542bef71ef1999 AS build +ARG VERSION + +ARG TARGETARCH + +WORKDIR /build +RUN apt-get update \ + && apt-get install -y curl +RUN arch=${TARGETARCH}; \ + if [ "${arch}" = "arm64" ]; then\ + arch="aarch_64"; \ + elif [ "${arch}" = "amd64" ]; then\ + arch="x86_64"; \ + fi; \ + echo "${arch}"; \ + curl -fsSL -o protoc-gen-grpc-java https://repo1.maven.org/maven2/io/grpc/protoc-gen-grpc-java/${VERSION#v}/protoc-gen-grpc-java-${VERSION#v}-linux-${arch}.exe + +FROM gcr.io/distroless/cc-debian13:latest@sha256:56aaf20ab2523a346a67c8e8f8e8dabe447447d0788b82284d14ad79cd5f93cc AS base +ARG VERSION + +FROM maven:3.9.11-eclipse-temurin-21 AS maven-deps +ARG VERSION +COPY pom.xml /tmp/pom.xml +RUN cd /tmp && mvn -f pom.xml dependency:go-offline + +FROM scratch +ARG VERSION +COPY --link --from=base / / +COPY --link --from=build --chmod=0755 --chown=root:root /build/protoc-gen-grpc-java . +COPY --from=maven-deps /root/.m2/repository /maven-repository +USER nobody +ENTRYPOINT [ "/protoc-gen-grpc-java" ] diff --git a/registry/grpc/java/plugin.yaml b/registry/grpc/java/plugin.yaml new file mode 100644 index 0000000..0a3aab7 --- /dev/null +++ b/registry/grpc/java/plugin.yaml @@ -0,0 +1,47 @@ +binary: protoc-gen-grpc-java +versions: + - v1.81.0 + - v1.80.0 + - v1.79.0 + - v1.78.0 + - v1.77.0 + - v1.76.0 + - v1.75.0 + - v1.74.0 + - v1.73.0 + - v1.72.0 + - v1.71.0 + - v1.70.0 + - v1.69.1 + - v1.69.0 + - v1.68.2 + - v1.68.1 + - v1.67.1 + - v1.66.0 + - v1.65.1 + - v1.65.0 + - v1.64.0 + - v1.63.0 + - v1.62.2 + - v1.61.1 + - v1.61.0 + - v1.60.1 + - v1.60.0 + - v1.59.1 + - v1.59.0 + - v1.58.0 + - v1.57.2 + - v1.57.1 + - v1.57.0 + - v1.56.1 + - v1.56.0 + - v1.55.1 + - v1.54.1 + - v1.54.0 + - v1.53.0 + - v1.52.1 + - v1.52.0 + - v1.51.1 + - v1.51.0 + - v1.50.2 + - v1.50.0 diff --git a/registry/grpc/java/pom.xml b/registry/grpc/java/pom.xml new file mode 100644 index 0000000..5065525 --- /dev/null +++ b/registry/grpc/java/pom.xml @@ -0,0 +1,44 @@ + + 4.0.0 + temp + temp + 1.0 + + + io.grpc + grpc-core + 1.81.0 + + + io.grpc + grpc-protobuf + 1.81.0 + + + io.grpc + grpc-stub + 1.81.0 + + + com.google.protobuf + protobuf-java + 4.34.1 + + + + io.grpc + grpc-protobuf-lite + 1.81.0 + + + com.google.protobuf + protobuf-javalite + 4.34.1 + + + build.buf + protobuf-javalite + 4.34.1 + + + diff --git a/registry/grpc/kotlin/.dockerignore b/registry/grpc/kotlin/.dockerignore new file mode 100644 index 0000000..7ff6309 --- /dev/null +++ b/registry/grpc/kotlin/.dockerignore @@ -0,0 +1,3 @@ +* +!Dockerfile +!pom.xml diff --git a/registry/grpc/kotlin/Dockerfile b/registry/grpc/kotlin/Dockerfile new file mode 100644 index 0000000..dbbcc03 --- /dev/null +++ b/registry/grpc/kotlin/Dockerfile @@ -0,0 +1,26 @@ +# syntax=docker/dockerfile:1.18 +FROM debian:bookworm-20250908 AS build +ARG VERSION + +ARG TARGETARCH + +WORKDIR /build +RUN apt-get update \ + && apt-get install -y curl +RUN curl -fsSL -o protoc-gen-grpc-kotlin.jar https://repo1.maven.org/maven2/io/grpc/protoc-gen-grpc-kotlin/${VERSION#v}/protoc-gen-grpc-kotlin-${VERSION#v}-jdk8.jar + +FROM gcr.io/distroless/java21-debian12:latest@sha256:418b2e2a9e452aa9299511427f2ae404dfc910ecfa78feb53b1c60c22c3b640c AS base +ARG VERSION + +FROM maven:3.9.11-eclipse-temurin-21 AS maven-deps +ARG VERSION +COPY pom.xml /tmp/pom.xml +RUN cd /tmp && mvn -f pom.xml dependency:go-offline + +FROM scratch +ARG VERSION +COPY --link --from=base / / +COPY --link --from=build --chmod=0644 --chown=root:root /build/protoc-gen-grpc-kotlin.jar . +COPY --from=maven-deps /root/.m2/repository /maven-repository +USER nobody +ENTRYPOINT [ "/usr/bin/java", "-jar", "/protoc-gen-grpc-kotlin.jar" ] diff --git a/registry/grpc/kotlin/plugin.yaml b/registry/grpc/kotlin/plugin.yaml new file mode 100644 index 0000000..0c5d4c9 --- /dev/null +++ b/registry/grpc/kotlin/plugin.yaml @@ -0,0 +1,8 @@ +binary: protoc-gen-kotlin +versions: + - v1.5.0 + - v1.4.3 + - v1.4.1 + - v1.4.0 + - v1.3.1 + - v1.3.0 diff --git a/registry/grpc/kotlin/pom.xml b/registry/grpc/kotlin/pom.xml new file mode 100644 index 0000000..6e18f97 --- /dev/null +++ b/registry/grpc/kotlin/pom.xml @@ -0,0 +1,85 @@ + + 4.0.0 + temp + temp + 1.0 + + + io.grpc + grpc-kotlin-stub + 1.5.0 + + + org.jetbrains.kotlinx + kotlinx-coroutines-core-jvm + 1.10.2 + + + io.grpc + grpc-core + 1.75.0 + + + io.grpc + grpc-protobuf + 1.75.0 + + + io.grpc + grpc-stub + 1.75.0 + + + com.google.protobuf + protobuf-java + 4.32.0 + + + com.google.protobuf + protobuf-kotlin + 4.32.0 + + + org.jetbrains.kotlin + kotlin-stdlib + 1.8.22 + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + 1.8.22 + + + + io.grpc + grpc-protobuf-lite + 1.75.0 + + + com.google.protobuf + protobuf-javalite + 4.32.0 + + + build.buf + protobuf-javalite + 4.32.0 + + + com.google.protobuf + protobuf-kotlin-lite + 4.32.0 + + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + 2.2.20 + + + + + + diff --git a/registry/grpc/node/.dockerignore b/registry/grpc/node/.dockerignore new file mode 100644 index 0000000..771bbba --- /dev/null +++ b/registry/grpc/node/.dockerignore @@ -0,0 +1,3 @@ +* +!Dockerfile +!package*.json diff --git a/registry/grpc/node/Dockerfile b/registry/grpc/node/Dockerfile new file mode 100644 index 0000000..f97790b --- /dev/null +++ b/registry/grpc/node/Dockerfile @@ -0,0 +1,20 @@ +# syntax=docker/dockerfile:1.19 +FROM node:24.11.1-bookworm AS build +ARG VERSION + +ARG TARGETARCH + +WORKDIR /build +COPY --link package* . +RUN npm ci \ + && cp /build/node_modules/grpc-tools/bin/grpc_node_plugin /build + +FROM gcr.io/distroless/cc-debian12:latest@sha256:0000f9dc0290f8eaf0ecceafbc35e803649087ea7879570fbc78372df7ac649b AS base +ARG VERSION + +FROM scratch +ARG VERSION +COPY --link --from=base / / +COPY --link --from=build --chmod=0755 /build/grpc_node_plugin . +USER nobody +ENTRYPOINT [ "/grpc_node_plugin" ] diff --git a/registry/grpc/node/package-lock.json b/registry/grpc/node/package-lock.json new file mode 100644 index 0000000..09479a6 --- /dev/null +++ b/registry/grpc/node/package-lock.json @@ -0,0 +1,257 @@ +{ + "name": "plugins-grpc-node", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "plugins-grpc-node", + "version": "1.0.0", + "dependencies": { + "grpc-tools": "1.13.1" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-2.0.3.tgz", + "integrity": "sha512-uwPAhccfFJlsfCxMYTwOdVfOz3xqyj8xYL3zJj8f0pb30tLohnnFPhLuqp4/qoEz8sNxe4SESZedcBojRefIzg==", + "license": "BSD-3-Clause", + "dependencies": { + "consola": "^3.2.3", + "detect-libc": "^2.0.0", + "https-proxy-agent": "^7.0.5", + "node-fetch": "^2.6.7", + "nopt": "^8.0.0", + "semver": "^7.5.3", + "tar": "^7.4.0" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/abbrev": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-3.0.1.tgz", + "integrity": "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==", + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/grpc-tools": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/grpc-tools/-/grpc-tools-1.13.1.tgz", + "integrity": "sha512-0sttMUxThNIkCTJq5qI0xXMz5zWqV2u3yG1kR3Sj9OokGIoyRBFjoInK9NyW7x5fH7knj48Roh1gq5xbl0VoDQ==", + "hasInstallScript": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^2.0.0" + }, + "bin": { + "grpc_tools_node_protoc": "bin/protoc.js", + "grpc_tools_node_protoc_plugin": "bin/protoc_plugin.js" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/nopt": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-8.1.0.tgz", + "integrity": "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==", + "license": "ISC", + "dependencies": { + "abbrev": "^3.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar": { + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.2.tgz", + "integrity": "sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + } + } +} diff --git a/registry/grpc/node/package.json b/registry/grpc/node/package.json new file mode 100644 index 0000000..1c433b1 --- /dev/null +++ b/registry/grpc/node/package.json @@ -0,0 +1,7 @@ +{ + "name": "plugins-grpc-node", + "version": "1.0.0", + "dependencies": { + "grpc-tools": "1.13.1" + } +} diff --git a/registry/grpc/node/plugin.yaml b/registry/grpc/node/plugin.yaml new file mode 100644 index 0000000..3dec320 --- /dev/null +++ b/registry/grpc/node/plugin.yaml @@ -0,0 +1,8 @@ +binary: grpc_node_plugin +versions: + - v1.13.1 + - v1.13.0 + - v1.12.4 + - v1.12.3 + - v1.12.0 + - v1.11.3 diff --git a/registry/grpc/objc/.dockerignore b/registry/grpc/objc/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/grpc/objc/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/grpc/objc/Dockerfile b/registry/grpc/objc/Dockerfile new file mode 100644 index 0000000..df02a90 --- /dev/null +++ b/registry/grpc/objc/Dockerfile @@ -0,0 +1,32 @@ +# syntax=docker/dockerfile:1.22 +FROM debian:trixie-20260316@sha256:55a15a112b42be10bfc8092fcc40b6748dc236f7ef46a358d9392b339e9d60e8 AS build +ARG VERSION + +ARG TARGETARCH +ARG BAZEL_OPTS="--host_jvm_args=-Djava.net.preferIPv4Stack=true" + +RUN apt-get update \ + && apt-get install -y curl git cmake build-essential autoconf clang libc++-dev libtool pkg-config unzip zip +RUN curl -fsSL -o /usr/local/bin/bazelisk https://github.com/bazelbuild/bazelisk/releases/download/v1.26.0/bazelisk-linux-${TARGETARCH} \ + && chmod +x /usr/local/bin/bazelisk \ + && mkdir /build \ + && chown nobody:nogroup /build \ + && usermod --home /build nobody + +USER nobody +WORKDIR /build + +RUN git clone --depth 1 --branch ${VERSION} https://github.com/grpc/grpc +WORKDIR /build/grpc +RUN bazelisk ${BAZEL_OPTS} build //src/compiler:grpc_plugin_support +RUN bazelisk ${BAZEL_OPTS} build //src/compiler:grpc_objective_c_plugin + +FROM gcr.io/distroless/cc-debian13:latest@sha256:e1cc90d06703f5dc30ae869fbfce78fce688f21a97efecd226375233a882e62f AS base +ARG VERSION + +FROM scratch +ARG VERSION +COPY --link --from=base / / +COPY --link --from=build --chmod=0755 /build/grpc/bazel-bin/src/compiler/grpc_objective_c_plugin_binary . +USER nobody +ENTRYPOINT ["/grpc_objective_c_plugin_binary"] diff --git a/registry/grpc/objc/plugin.yaml b/registry/grpc/objc/plugin.yaml new file mode 100644 index 0000000..9fd1bdc --- /dev/null +++ b/registry/grpc/objc/plugin.yaml @@ -0,0 +1,63 @@ +binary: grpc_objective_c_plugin_binary +versions: + - v1.80.0 + - v1.78.1 + - v1.78.0 + - v1.76.0 + - v1.75.1 + - v1.75.0 + - v1.74.1 + - v1.74.0 + - v1.73.1 + - v1.73.0 + - v1.72.1 + - v1.72.0 + - v1.71.0 + - v1.70.1 + - v1.70.0 + - v1.69.0 + - v1.68.2 + - v1.68.1 + - v1.68.0 + - v1.67.1 + - v1.67.0 + - v1.66.2 + - v1.66.1 + - v1.66.0 + - v1.65.5 + - v1.65.4 + - v1.65.2 + - v1.65.1 + - v1.65.0 + - v1.64.2 + - v1.64.1 + - v1.64.0 + - v1.63.0 + - v1.62.2 + - v1.62.1 + - v1.62.0 + - v1.61.1 + - v1.61.0 + - v1.60.0 + - v1.59.3 + - v1.59.2 + - v1.59.1 + - v1.59.0 + - v1.58.1 + - v1.58.0 + - v1.57.0 + - v1.56.2 + - v1.56.1 + - v1.56.0 + - v1.55.1 + - v1.55.0 + - v1.54.2 + - v1.54.1 + - v1.54.0 + - v1.53.0 + - v1.52.1 + - v1.52.0 + - v1.51.1 + - v1.51.0 + - v1.50.1 + - v1.50.0 diff --git a/registry/grpc/php/.dockerignore b/registry/grpc/php/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/grpc/php/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/grpc/php/Dockerfile b/registry/grpc/php/Dockerfile new file mode 100644 index 0000000..1f4275c --- /dev/null +++ b/registry/grpc/php/Dockerfile @@ -0,0 +1,32 @@ +# syntax=docker/dockerfile:1.22 +FROM debian:trixie-20260316@sha256:55a15a112b42be10bfc8092fcc40b6748dc236f7ef46a358d9392b339e9d60e8 AS build +ARG VERSION + +ARG TARGETARCH +ARG BAZEL_OPTS="--host_jvm_args=-Djava.net.preferIPv4Stack=true" + +RUN apt-get update \ + && apt-get install -y curl git cmake build-essential autoconf clang libc++-dev libtool pkg-config unzip zip +RUN curl -fsSL -o /usr/local/bin/bazelisk https://github.com/bazelbuild/bazelisk/releases/download/v1.26.0/bazelisk-linux-${TARGETARCH} \ + && chmod +x /usr/local/bin/bazelisk \ + && mkdir /build \ + && chown nobody:nogroup /build \ + && usermod --home /build nobody + +USER nobody +WORKDIR /build + +RUN git clone --depth 1 --branch ${VERSION} https://github.com/grpc/grpc +WORKDIR /build/grpc +RUN bazelisk ${BAZEL_OPTS} build //src/compiler:grpc_plugin_support +RUN bazelisk ${BAZEL_OPTS} build //src/compiler:grpc_php_plugin + +FROM gcr.io/distroless/cc-debian13:latest@sha256:e1cc90d06703f5dc30ae869fbfce78fce688f21a97efecd226375233a882e62f AS base +ARG VERSION + +FROM scratch +ARG VERSION +COPY --link --from=base / / +COPY --link --from=build --chmod=0755 /build/grpc/bazel-bin/src/compiler/grpc_php_plugin_binary . +USER nobody +ENTRYPOINT ["/grpc_php_plugin_binary"] diff --git a/registry/grpc/php/plugin.yaml b/registry/grpc/php/plugin.yaml new file mode 100644 index 0000000..764b4e8 --- /dev/null +++ b/registry/grpc/php/plugin.yaml @@ -0,0 +1,63 @@ +binary: grpc_php_plugin_binary +versions: + - v1.80.0 + - v1.78.1 + - v1.78.0 + - v1.76.0 + - v1.75.1 + - v1.75.0 + - v1.74.1 + - v1.74.0 + - v1.73.1 + - v1.73.0 + - v1.72.1 + - v1.72.0 + - v1.71.0 + - v1.70.1 + - v1.70.0 + - v1.69.0 + - v1.68.2 + - v1.68.1 + - v1.68.0 + - v1.67.1 + - v1.67.0 + - v1.66.2 + - v1.66.1 + - v1.66.0 + - v1.65.5 + - v1.65.4 + - v1.65.2 + - v1.65.1 + - v1.65.0 + - v1.64.2 + - v1.64.1 + - v1.64.0 + - v1.63.0 + - v1.62.2 + - v1.62.1 + - v1.62.0 + - v1.61.1 + - v1.61.0 + - v1.60.0 + - v1.59.3 + - v1.59.2 + - v1.59.1 + - v1.59.0 + - v1.58.1 + - v1.58.0 + - v1.57.0 + - v1.56.2 + - v1.56.1 + - v1.56.0 + - v1.55.1 + - v1.55.0 + - v1.54.2 + - v1.54.1 + - v1.54.0 + - v1.53.0 + - v1.52.1 + - v1.52.0 + - v1.51.1 + - v1.51.0 + - v1.50.1 + - v1.50.0 diff --git a/registry/grpc/python/.dockerignore b/registry/grpc/python/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/grpc/python/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/grpc/python/Dockerfile b/registry/grpc/python/Dockerfile new file mode 100644 index 0000000..e6ec3d8 --- /dev/null +++ b/registry/grpc/python/Dockerfile @@ -0,0 +1,32 @@ +# syntax=docker/dockerfile:1.22 +FROM debian:trixie-20260316@sha256:55a15a112b42be10bfc8092fcc40b6748dc236f7ef46a358d9392b339e9d60e8 AS build +ARG VERSION + +ARG TARGETARCH +ARG BAZEL_OPTS="--host_jvm_args=-Djava.net.preferIPv4Stack=true" + +RUN apt-get update \ + && apt-get install -y curl git cmake build-essential autoconf clang libc++-dev libtool pkg-config unzip zip +RUN curl -fsSL -o /usr/local/bin/bazelisk https://github.com/bazelbuild/bazelisk/releases/download/v1.26.0/bazelisk-linux-${TARGETARCH} \ + && chmod +x /usr/local/bin/bazelisk \ + && mkdir /build \ + && chown nobody:nogroup /build \ + && usermod --home /build nobody + +USER nobody +WORKDIR /build + +RUN git clone --depth 1 --branch ${VERSION} https://github.com/grpc/grpc +WORKDIR /build/grpc +RUN bazelisk ${BAZEL_OPTS} build //src/compiler:grpc_plugin_support +RUN bazelisk ${BAZEL_OPTS} build //src/compiler:grpc_python_plugin + +FROM gcr.io/distroless/cc-debian13:latest@sha256:e1cc90d06703f5dc30ae869fbfce78fce688f21a97efecd226375233a882e62f AS base +ARG VERSION + +FROM scratch +ARG VERSION +COPY --link --from=base / / +COPY --link --from=build --chmod=0755 /build/grpc/bazel-bin/src/compiler/grpc_python_plugin_binary . +USER nobody +ENTRYPOINT ["/grpc_python_plugin_binary"] diff --git a/registry/grpc/python/plugin.yaml b/registry/grpc/python/plugin.yaml new file mode 100644 index 0000000..5748a50 --- /dev/null +++ b/registry/grpc/python/plugin.yaml @@ -0,0 +1,63 @@ +binary: grpc_python_plugin_binary +versions: + - v1.80.0 + - v1.78.1 + - v1.78.0 + - v1.76.0 + - v1.75.1 + - v1.75.0 + - v1.74.1 + - v1.74.0 + - v1.73.1 + - v1.73.0 + - v1.72.1 + - v1.72.0 + - v1.71.0 + - v1.70.1 + - v1.70.0 + - v1.69.0 + - v1.68.2 + - v1.68.1 + - v1.68.0 + - v1.67.1 + - v1.67.0 + - v1.66.2 + - v1.66.1 + - v1.66.0 + - v1.65.5 + - v1.65.4 + - v1.65.2 + - v1.65.1 + - v1.65.0 + - v1.64.2 + - v1.64.1 + - v1.64.0 + - v1.63.0 + - v1.62.2 + - v1.62.1 + - v1.62.0 + - v1.61.1 + - v1.61.0 + - v1.60.0 + - v1.59.3 + - v1.59.2 + - v1.59.1 + - v1.59.0 + - v1.58.1 + - v1.58.0 + - v1.57.0 + - v1.56.2 + - v1.56.1 + - v1.56.0 + - v1.55.1 + - v1.55.0 + - v1.54.2 + - v1.54.1 + - v1.54.0 + - v1.53.0 + - v1.52.1 + - v1.52.0 + - v1.51.1 + - v1.51.0 + - v1.50.1 + - v1.50.0 diff --git a/registry/grpc/ruby/.dockerignore b/registry/grpc/ruby/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/grpc/ruby/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/grpc/ruby/Dockerfile b/registry/grpc/ruby/Dockerfile new file mode 100644 index 0000000..9e88771 --- /dev/null +++ b/registry/grpc/ruby/Dockerfile @@ -0,0 +1,32 @@ +# syntax=docker/dockerfile:1.22 +FROM debian:trixie-20260316@sha256:55a15a112b42be10bfc8092fcc40b6748dc236f7ef46a358d9392b339e9d60e8 AS build +ARG VERSION + +ARG TARGETARCH +ARG BAZEL_OPTS="--host_jvm_args=-Djava.net.preferIPv4Stack=true" + +RUN apt-get update \ + && apt-get install -y curl git cmake build-essential autoconf clang libc++-dev libtool pkg-config unzip zip +RUN curl -fsSL -o /usr/local/bin/bazelisk https://github.com/bazelbuild/bazelisk/releases/download/v1.26.0/bazelisk-linux-${TARGETARCH} \ + && chmod +x /usr/local/bin/bazelisk \ + && mkdir /build \ + && chown nobody:nogroup /build \ + && usermod --home /build nobody + +USER nobody +WORKDIR /build + +RUN git clone --depth 1 --branch ${VERSION} https://github.com/grpc/grpc +WORKDIR /build/grpc +RUN bazelisk ${BAZEL_OPTS} build //src/compiler:grpc_plugin_support +RUN bazelisk ${BAZEL_OPTS} build //src/compiler:grpc_ruby_plugin + +FROM gcr.io/distroless/cc-debian13:latest@sha256:e1cc90d06703f5dc30ae869fbfce78fce688f21a97efecd226375233a882e62f AS base +ARG VERSION + +FROM scratch +ARG VERSION +COPY --link --from=base / / +COPY --link --from=build --chmod=0755 /build/grpc/bazel-bin/src/compiler/grpc_ruby_plugin_binary . +USER nobody +ENTRYPOINT ["/grpc_ruby_plugin_binary"] diff --git a/registry/grpc/ruby/plugin.yaml b/registry/grpc/ruby/plugin.yaml new file mode 100644 index 0000000..c7e77bf --- /dev/null +++ b/registry/grpc/ruby/plugin.yaml @@ -0,0 +1,63 @@ +binary: grpc_ruby_plugin_binary +versions: + - v1.80.0 + - v1.78.1 + - v1.78.0 + - v1.76.0 + - v1.75.1 + - v1.75.0 + - v1.74.1 + - v1.74.0 + - v1.73.1 + - v1.73.0 + - v1.72.1 + - v1.72.0 + - v1.71.0 + - v1.70.1 + - v1.70.0 + - v1.69.0 + - v1.68.2 + - v1.68.1 + - v1.68.0 + - v1.67.1 + - v1.67.0 + - v1.66.2 + - v1.66.1 + - v1.66.0 + - v1.65.5 + - v1.65.4 + - v1.65.2 + - v1.65.1 + - v1.65.0 + - v1.64.2 + - v1.64.1 + - v1.64.0 + - v1.63.0 + - v1.62.2 + - v1.62.1 + - v1.62.0 + - v1.61.1 + - v1.61.0 + - v1.60.0 + - v1.59.3 + - v1.59.2 + - v1.59.1 + - v1.59.0 + - v1.58.1 + - v1.58.0 + - v1.57.0 + - v1.56.2 + - v1.56.1 + - v1.56.0 + - v1.55.1 + - v1.55.0 + - v1.54.2 + - v1.54.1 + - v1.54.0 + - v1.53.0 + - v1.52.1 + - v1.52.0 + - v1.51.1 + - v1.51.0 + - v1.50.1 + - v1.50.0 diff --git a/registry/grpc/swift-protobuf/.dockerignore b/registry/grpc/swift-protobuf/.dockerignore new file mode 100644 index 0000000..2b3157b --- /dev/null +++ b/registry/grpc/swift-protobuf/.dockerignore @@ -0,0 +1,3 @@ +* +!Dockerfile +!Package.resolved diff --git a/registry/grpc/swift-protobuf/Dockerfile b/registry/grpc/swift-protobuf/Dockerfile new file mode 100644 index 0000000..eb5d88a --- /dev/null +++ b/registry/grpc/swift-protobuf/Dockerfile @@ -0,0 +1,21 @@ +# syntax=docker/dockerfile:1.23 +FROM swift:6.3.2-bookworm@sha256:40918972df286d15aa47f4686c98a211f0a6152ca1d48b3dbccd6f56a3c34815 AS build +ARG VERSION + +RUN apt-get update \ + && apt-get install -y unzip +WORKDIR /app +RUN git clone --depth 1 --branch ${VERSION#v} https://github.com/grpc/grpc-swift-protobuf --recursive +WORKDIR /app/grpc-swift-protobuf +COPY --link Package.resolved . +RUN swift build -c release --static-swift-stdlib --product protoc-gen-grpc-swift-2 -Xlinker -s --force-resolved-versions + +FROM gcr.io/distroless/cc-debian13:latest@sha256:8b5d1db6d2253036a53cb8362d3e3fa82a7caf84c247772c46a023166c64e977 AS base +ARG VERSION + +FROM scratch +ARG VERSION +COPY --link --from=base / / +COPY --link --from=build /app/grpc-swift-protobuf/.build/release/protoc-gen-grpc-swift-2 . +USER nobody +ENTRYPOINT [ "/protoc-gen-grpc-swift-2" ] diff --git a/registry/grpc/swift-protobuf/Package.resolved b/registry/grpc/swift-protobuf/Package.resolved new file mode 100644 index 0000000..e09425e --- /dev/null +++ b/registry/grpc/swift-protobuf/Package.resolved @@ -0,0 +1,33 @@ +{ + "originHash" : "6191d898f7c18a107e7714f7960c012906126eb95f0770b4bf0335224ec96f68", + "pins" : [ + { + "identity" : "grpc-swift-2", + "kind" : "remoteSourceControl", + "location" : "https://github.com/grpc/grpc-swift-2.git", + "state" : { + "revision" : "21fe69ab7ce0e87ac089534733c52f037e74a3eb", + "version" : "2.4.1" + } + }, + { + "identity" : "swift-collections", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-collections.git", + "state" : { + "revision" : "fea17c02d767f46b23070fdfdacc28a03a39232a", + "version" : "1.5.1" + } + }, + { + "identity" : "swift-protobuf", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-protobuf.git", + "state" : { + "revision" : "f6506eaa86ed2e01cb0ae14a75035b7fdbf0918f", + "version" : "1.38.0" + } + } + ], + "version" : 3 +} diff --git a/registry/grpc/swift-protobuf/plugin.yaml b/registry/grpc/swift-protobuf/plugin.yaml new file mode 100644 index 0000000..9ea5555 --- /dev/null +++ b/registry/grpc/swift-protobuf/plugin.yaml @@ -0,0 +1,14 @@ +binary: protoc-gen-grpc-swift-2 +versions: + - v2.4.0 + - v2.3.0 + - v2.2.1 + - v2.2.0 + - v2.1.2 + - v2.1.1 + - v2.1.0 + - v2.0.0 + - v1.3.0 + - v1.2.0 + - v1.1.0 + - v1.0.0 diff --git a/registry/grpc/swift/.dockerignore b/registry/grpc/swift/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/grpc/swift/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/grpc/swift/Dockerfile b/registry/grpc/swift/Dockerfile new file mode 100644 index 0000000..6377e61 --- /dev/null +++ b/registry/grpc/swift/Dockerfile @@ -0,0 +1,20 @@ +# syntax=docker/dockerfile:1.22 +FROM swift:6.3.0-bookworm@sha256:943bb41624442cef8ccca256f1ff7830ce25209cbad88ebfede0aaef268a8a80 AS build +ARG VERSION + +RUN apt-get update \ + && apt-get install -y libstdc++-12-dev +WORKDIR /app +RUN git clone --depth 1 --branch ${VERSION#v} https://github.com/grpc/grpc-swift --recursive +WORKDIR /app/grpc-swift +RUN swift build -c release --static-swift-stdlib --product protoc-gen-grpc-swift -Xlinker -s + +FROM gcr.io/distroless/cc-debian13:latest@sha256:e1cc90d06703f5dc30ae869fbfce78fce688f21a97efecd226375233a882e62f AS base +ARG VERSION + +FROM scratch +ARG VERSION +COPY --link --from=base / / +COPY --link --from=build /app/grpc-swift/.build/release/protoc-gen-grpc-swift . +USER nobody +ENTRYPOINT [ "/protoc-gen-grpc-swift" ] diff --git a/registry/grpc/swift/plugin.yaml b/registry/grpc/swift/plugin.yaml new file mode 100644 index 0000000..d7708ce --- /dev/null +++ b/registry/grpc/swift/plugin.yaml @@ -0,0 +1,29 @@ +binary: protoc-gen-grpc-swift +versions: + - v1.27.5 + - v1.27.4 + - v1.27.3 + - v1.27.2 + - v1.27.1 + - v1.24.2 + - v1.24.1 + - v1.23.1 + - v1.23.0 + - v1.22.0 + - v1.21.1 + - v1.21.0 + - v1.20.0 + - v1.19.1 + - v1.19.0 + - v1.18.0 + - v1.17.0 + - v1.16.0 + - v1.15.0 + - v1.14.2 + - v1.14.1 + - v1.14.0 + - v1.13.2 + - v1.13.1 + - v1.13.0 + - v1.12.0 + - v1.11.0 diff --git a/registry/grpc/web/.dockerignore b/registry/grpc/web/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/grpc/web/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/grpc/web/Dockerfile b/registry/grpc/web/Dockerfile new file mode 100644 index 0000000..7572f6d --- /dev/null +++ b/registry/grpc/web/Dockerfile @@ -0,0 +1,32 @@ +# syntax=docker/dockerfile:1.18 +FROM debian:bookworm-20250908 AS build +ARG VERSION + +ARG TARGETARCH +ARG BAZEL_OPTS="--host_jvm_args=-Djava.net.preferIPv4Stack=true" + +RUN apt-get update \ + && apt-get install -y curl git cmake build-essential autoconf clang libc++-dev libtool pkg-config unzip zip +RUN curl -fsSL -o /usr/local/bin/bazelisk https://github.com/bazelbuild/bazelisk/releases/download/v1.27.0/bazelisk-linux-${TARGETARCH} \ + && chmod +x /usr/local/bin/bazelisk \ + && mkdir /build \ + && chown nobody:nogroup /build \ + && usermod --home /build nobody + +USER nobody +WORKDIR /build +RUN curl -fsSL -o grpc-web-src.tar.gz https://github.com/grpc/grpc-web/releases/download/${VERSION#v}/grpc-web-source-${VERSION#v}.tar.gz \ + && tar -zxf grpc-web-src.tar.gz \ + && rm grpc-web-src.tar.gz +WORKDIR /build/grpc-web-${VERSION#v} +RUN bazelisk ${BAZEL_OPTS} build '//javascript/net/grpc/web/generator:protoc-gen-grpc-web' + +FROM gcr.io/distroless/cc-debian12:latest@sha256:620d8b11ae800f0dbd7995f89ddc5344ad603269ea98770588b1b07a4a0a6872 AS base +ARG VERSION + +FROM scratch +ARG VERSION +COPY --from=base --link / / +COPY --from=build --link --chmod=0755 /build/grpc-web-${VERSION#v}/bazel-bin/javascript/net/grpc/web/generator/protoc-gen-grpc-web . +USER nobody +ENTRYPOINT [ "/protoc-gen-grpc-web" ] diff --git a/registry/grpc/web/plugin.yaml b/registry/grpc/web/plugin.yaml new file mode 100644 index 0000000..3f236b4 --- /dev/null +++ b/registry/grpc/web/plugin.yaml @@ -0,0 +1,7 @@ +binary: protoc-gen-grpc-web +versions: + - v2.0.2 + - v2.0.1 + - v2.0.0 + - v1.5.0 + - v1.4.2 diff --git a/registry/pluginrpc/go/.dockerignore b/registry/pluginrpc/go/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/pluginrpc/go/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/pluginrpc/go/Dockerfile b/registry/pluginrpc/go/Dockerfile new file mode 100644 index 0000000..e85c930 --- /dev/null +++ b/registry/pluginrpc/go/Dockerfile @@ -0,0 +1,16 @@ +# syntax=docker/dockerfile:1.19 +FROM golang:1.26-alpine3.22 AS build + +ARG VERSION +ARG GO_MODULE +ARG BINARY_NAME + +RUN apk add upx --no-cache +RUN --mount=type=cache,target=/go/pkg/mod \ + CGO_ENABLED=0 go install -ldflags "-s -w" -trimpath ${GO_MODULE}@${VERSION} +RUN cp /go/bin/${BINARY_NAME} /plugin 2>/dev/null \ + || cp /go/bin/linux_*/${BINARY_NAME} /plugin \ + && upx --best --lzma /plugin + +FROM scratch +COPY --from=build /plugin /plugin diff --git a/registry/pluginrpc/go/plugin.yaml b/registry/pluginrpc/go/plugin.yaml new file mode 100644 index 0000000..c2f2766 --- /dev/null +++ b/registry/pluginrpc/go/plugin.yaml @@ -0,0 +1,6 @@ +binary: protoc-gen-pluginrpc-go +build_args: + GO_MODULE: pluginrpc.com/pluginrpc/cmd/protoc-gen-pluginrpc-go +versions: + - v0.5.0 + - v0.3.0 diff --git a/registry/protocolbuffers/cpp/.dockerignore b/registry/protocolbuffers/cpp/.dockerignore new file mode 100644 index 0000000..c804556 --- /dev/null +++ b/registry/protocolbuffers/cpp/.dockerignore @@ -0,0 +1,4 @@ +* +!BUILD +!cpp.cc +!Dockerfile diff --git a/registry/protocolbuffers/cpp/BUILD b/registry/protocolbuffers/cpp/BUILD new file mode 100644 index 0000000..3cacc9e --- /dev/null +++ b/registry/protocolbuffers/cpp/BUILD @@ -0,0 +1,7 @@ +cc_binary( + name = "protoc-gen-cpp", + srcs = ["cpp.cc"], + deps = [ + "//:protoc_lib", + ], +) diff --git a/registry/protocolbuffers/cpp/Dockerfile b/registry/protocolbuffers/cpp/Dockerfile new file mode 100644 index 0000000..51a18b4 --- /dev/null +++ b/registry/protocolbuffers/cpp/Dockerfile @@ -0,0 +1,33 @@ +# syntax=docker/dockerfile:1.23 +FROM debian:trixie-20260505@sha256:e2d08da6f42ef4b09b165d55528a12727aeed8240dc9edf888e3ec07e10ef9da AS build +ARG VERSION + +ARG TARGETARCH +ARG BAZEL_OPTS="--host_jvm_args=-Djava.net.preferIPv4Stack=true" + +RUN apt-get update \ + && apt-get install -y curl git cmake build-essential autoconf clang libc++-dev libtool pkg-config unzip zip +RUN curl -fsSL -o /usr/local/bin/bazelisk https://github.com/bazelbuild/bazelisk/releases/download/v1.28.1/bazelisk-linux-${TARGETARCH} \ + && chmod +x /usr/local/bin/bazelisk \ + && mkdir /build \ + && chown nobody:nogroup /build \ + && usermod --home /build nobody + +USER nobody +WORKDIR /build +RUN curl -fsSL -o protoc.tar.gz https://github.com/protocolbuffers/protobuf/releases/download/${VERSION}/protobuf-${VERSION#v}.tar.gz \ + && tar --strip-components=1 -zxf protoc.tar.gz \ + && rm protoc.tar.gz +RUN bazelisk ${BAZEL_OPTS} build '//:protoc_lib' +COPY --link BUILD cpp.cc plugins/ +RUN bazelisk ${BAZEL_OPTS} build '//plugins:protoc-gen-cpp.stripped' + +FROM gcr.io/distroless/cc-debian13:latest@sha256:8b5d1db6d2253036a53cb8362d3e3fa82a7caf84c247772c46a023166c64e977 AS base +ARG VERSION + +FROM scratch +ARG VERSION +COPY --from=base --link / / +COPY --from=build --link --chmod=0755 /build/bazel-bin/plugins/protoc-gen-cpp . +USER nobody +ENTRYPOINT ["/protoc-gen-cpp"] diff --git a/registry/protocolbuffers/cpp/cpp.cc b/registry/protocolbuffers/cpp/cpp.cc new file mode 100644 index 0000000..a3c5da5 --- /dev/null +++ b/registry/protocolbuffers/cpp/cpp.cc @@ -0,0 +1,7 @@ +#include +#include + +int main(int argc, char *argv[]) { + google::protobuf::compiler::cpp::CppGenerator generator; + return google::protobuf::compiler::PluginMain(argc, argv, &generator); +} diff --git a/registry/protocolbuffers/cpp/plugin.yaml b/registry/protocolbuffers/cpp/plugin.yaml new file mode 100644 index 0000000..bd1c7f8 --- /dev/null +++ b/registry/protocolbuffers/cpp/plugin.yaml @@ -0,0 +1,62 @@ +binary: protoc-gen-cpp +versions: + - v35.0 + - v34.1 + - v34.0 + - v33.5 + - v33.4 + - v33.3 + - v33.2 + - v33.1 + - v33.0 + - v32.1 + - v32.0 + - v31.1 + - v31.0 + - v30.2 + - v30.1 + - v30.0 + - v3.14.0 + - v29.5 + - v29.3 + - v29.2 + - v29.1 + - v29.0 + - v28.3 + - v28.2 + - v28.1 + - v28.0 + - v27.4 + - v27.3 + - v27.2 + - v27.1 + - v27.0 + - v26.1 + - v26.0 + - v25.8 + - v25.6 + - v25.3 + - v25.2 + - v25.1 + - v25.0 + - v24.4 + - v24.3 + - v24.2 + - v24.1 + - v24.0 + - v23.4 + - v23.3 + - v23.2 + - v23.1 + - v23.0 + - v22.4 + - v22.3 + - v22.2 + - v22.1 + - v22.0 + - v21.9 + - v21.8 + - v21.7 + - v21.12 + - v21.11 + - v21.10 diff --git a/registry/protocolbuffers/csharp/.dockerignore b/registry/protocolbuffers/csharp/.dockerignore new file mode 100644 index 0000000..71e50a4 --- /dev/null +++ b/registry/protocolbuffers/csharp/.dockerignore @@ -0,0 +1,5 @@ +* +!BUILD +!csharp.cc +!Dockerfile +!build.csproj diff --git a/registry/protocolbuffers/csharp/BUILD b/registry/protocolbuffers/csharp/BUILD new file mode 100644 index 0000000..c68d051 --- /dev/null +++ b/registry/protocolbuffers/csharp/BUILD @@ -0,0 +1,7 @@ +cc_binary( + name = "protoc-gen-csharp", + srcs = ["csharp.cc"], + deps = [ + "//:protoc_lib", + ], +) diff --git a/registry/protocolbuffers/csharp/Dockerfile b/registry/protocolbuffers/csharp/Dockerfile new file mode 100644 index 0000000..d344c24 --- /dev/null +++ b/registry/protocolbuffers/csharp/Dockerfile @@ -0,0 +1,40 @@ +# syntax=docker/dockerfile:1.23 +FROM debian:trixie-20260505@sha256:e2d08da6f42ef4b09b165d55528a12727aeed8240dc9edf888e3ec07e10ef9da AS build +ARG VERSION + +ARG TARGETARCH +ARG BAZEL_OPTS="--host_jvm_args=-Djava.net.preferIPv4Stack=true" + +RUN apt-get update \ + && apt-get install -y curl git cmake build-essential autoconf clang libc++-dev libtool pkg-config unzip zip +RUN curl -fsSL -o /usr/local/bin/bazelisk https://github.com/bazelbuild/bazelisk/releases/download/v1.28.1/bazelisk-linux-${TARGETARCH} \ + && chmod +x /usr/local/bin/bazelisk \ + && mkdir /build \ + && chown nobody:nogroup /build \ + && usermod --home /build nobody + +USER nobody +WORKDIR /build +RUN curl -fsSL -o protoc.tar.gz https://github.com/protocolbuffers/protobuf/releases/download/${VERSION}/protobuf-${VERSION#v}.tar.gz \ + && tar --strip-components=1 -zxf protoc.tar.gz \ + && rm protoc.tar.gz +RUN bazelisk ${BAZEL_OPTS} build '//:protoc_lib' +COPY --link BUILD csharp.cc plugins/ +RUN bazelisk ${BAZEL_OPTS} build '//plugins:protoc-gen-csharp.stripped' + +FROM mcr.microsoft.com/dotnet/sdk:8.0.421-bookworm-slim@sha256:ebecce75fb42c4c14db108f88054d20a93b0b9e6dfbacad56c8f744c342cf9ef AS dotnetrestore +ARG VERSION +WORKDIR /build +COPY --link ./build.csproj /build/build.csproj +RUN mkdir /nuget && dotnet restore --packages /nuget + +FROM gcr.io/distroless/cc-debian13:latest@sha256:8b5d1db6d2253036a53cb8362d3e3fa82a7caf84c247772c46a023166c64e977 AS base +ARG VERSION + +FROM scratch +ARG VERSION +COPY --from=base --link / / +COPY --link --from=dotnetrestore /nuget /nuget +COPY --from=build --link --chmod=0755 /build/bazel-bin/plugins/protoc-gen-csharp . +USER nobody +ENTRYPOINT ["/protoc-gen-csharp"] diff --git a/registry/protocolbuffers/csharp/build.csproj b/registry/protocolbuffers/csharp/build.csproj new file mode 100644 index 0000000..33fe3b3 --- /dev/null +++ b/registry/protocolbuffers/csharp/build.csproj @@ -0,0 +1,8 @@ + + + netstandard2.0 + + + + + diff --git a/registry/protocolbuffers/csharp/csharp.cc b/registry/protocolbuffers/csharp/csharp.cc new file mode 100644 index 0000000..a823660 --- /dev/null +++ b/registry/protocolbuffers/csharp/csharp.cc @@ -0,0 +1,7 @@ +#include +#include + +int main(int argc, char *argv[]) { + google::protobuf::compiler::csharp::Generator generator; + return google::protobuf::compiler::PluginMain(argc, argv, &generator); +} diff --git a/registry/protocolbuffers/csharp/plugin.yaml b/registry/protocolbuffers/csharp/plugin.yaml new file mode 100644 index 0000000..b3b1753 --- /dev/null +++ b/registry/protocolbuffers/csharp/plugin.yaml @@ -0,0 +1,62 @@ +binary: protoc-gen-csharp +versions: + - v35.0 + - v34.1 + - v34.0 + - v33.5 + - v33.4 + - v33.3 + - v33.2 + - v33.1 + - v33.0 + - v32.1 + - v32.0 + - v31.1 + - v31.0 + - v30.2 + - v30.1 + - v30.0 + - v3.14.0 + - v29.5 + - v29.3 + - v29.2 + - v29.1 + - v29.0 + - v28.3 + - v28.2 + - v28.1 + - v28.0 + - v27.4 + - v27.3 + - v27.2 + - v27.1 + - v27.0 + - v26.1 + - v26.0 + - v25.8 + - v25.6 + - v25.3 + - v25.2 + - v25.1 + - v25.0 + - v24.4 + - v24.3 + - v24.2 + - v24.1 + - v24.0 + - v23.4 + - v23.3 + - v23.2 + - v23.1 + - v23.0 + - v22.4 + - v22.3 + - v22.2 + - v22.1 + - v22.0 + - v21.9 + - v21.8 + - v21.7 + - v21.12 + - v21.11 + - v21.10 diff --git a/registry/protocolbuffers/dart/.dockerignore b/registry/protocolbuffers/dart/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/protocolbuffers/dart/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/protocolbuffers/dart/Dockerfile b/registry/protocolbuffers/dart/Dockerfile new file mode 100644 index 0000000..55c9062 --- /dev/null +++ b/registry/protocolbuffers/dart/Dockerfile @@ -0,0 +1,17 @@ +# syntax=docker/dockerfile:1.19 +FROM dart:3.10.1-sdk AS build +ARG VERSION + +WORKDIR /build +RUN git clone --depth 1 --branch protoc_plugin-${VERSION} https://github.com/google/protobuf.dart.git \ + && cd protobuf.dart/protoc_plugin \ + && dart pub get \ + && dart compile exe bin/protoc_plugin.dart -o /build/protoc-gen-dart + +FROM scratch +ARG VERSION +COPY --from=build --link /etc/passwd /etc/passwd +COPY --from=build --link /runtime/ / +COPY --from=build --link /build/protoc-gen-dart . +USER nobody +ENTRYPOINT [ "/protoc-gen-dart" ] diff --git a/registry/protocolbuffers/dart/plugin.yaml b/registry/protocolbuffers/dart/plugin.yaml new file mode 100644 index 0000000..72590d4 --- /dev/null +++ b/registry/protocolbuffers/dart/plugin.yaml @@ -0,0 +1,15 @@ +binary: protoc-gen-dart +versions: + - v25.0.0 + - v24.0.0 + - v23.0.0 + - v22.5.0 + - v22.4.0 + - v22.3.0 + - v22.2.0 + - v22.1.0 + - v22.0.1 + - v21.1.2 + - v21.1.1 + - v21.0.2 + - v20.0.1 diff --git a/registry/protocolbuffers/go/.dockerignore b/registry/protocolbuffers/go/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/protocolbuffers/go/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/protocolbuffers/go/Dockerfile b/registry/protocolbuffers/go/Dockerfile new file mode 100644 index 0000000..e85c930 --- /dev/null +++ b/registry/protocolbuffers/go/Dockerfile @@ -0,0 +1,16 @@ +# syntax=docker/dockerfile:1.19 +FROM golang:1.26-alpine3.22 AS build + +ARG VERSION +ARG GO_MODULE +ARG BINARY_NAME + +RUN apk add upx --no-cache +RUN --mount=type=cache,target=/go/pkg/mod \ + CGO_ENABLED=0 go install -ldflags "-s -w" -trimpath ${GO_MODULE}@${VERSION} +RUN cp /go/bin/${BINARY_NAME} /plugin 2>/dev/null \ + || cp /go/bin/linux_*/${BINARY_NAME} /plugin \ + && upx --best --lzma /plugin + +FROM scratch +COPY --from=build /plugin /plugin diff --git a/registry/protocolbuffers/go/plugin.yaml b/registry/protocolbuffers/go/plugin.yaml new file mode 100644 index 0000000..4e8a073 --- /dev/null +++ b/registry/protocolbuffers/go/plugin.yaml @@ -0,0 +1,28 @@ +binary: protoc-gen-go +build_args: + GO_MODULE: google.golang.org/protobuf/cmd/protoc-gen-go +versions: + - v1.36.9 + - v1.36.8 + - v1.36.7 + - v1.36.6 + - v1.36.5 + - v1.36.4 + - v1.36.3 + - v1.36.2 + - v1.36.11 + - v1.36.10 + - v1.36.1 + - v1.36.0 + - v1.35.2 + - v1.35.1 + - v1.34.2 + - v1.34.1 + - v1.34.0 + - v1.33.0 + - v1.32.0 + - v1.31.0 + - v1.30.0 + - v1.29.1 + - v1.29.0 + - v1.28.1 diff --git a/registry/protocolbuffers/java/.dockerignore b/registry/protocolbuffers/java/.dockerignore new file mode 100644 index 0000000..f93a3ef --- /dev/null +++ b/registry/protocolbuffers/java/.dockerignore @@ -0,0 +1,5 @@ +* +!BUILD +!Dockerfile +!java.cc +!pom.xml diff --git a/registry/protocolbuffers/java/BUILD b/registry/protocolbuffers/java/BUILD new file mode 100644 index 0000000..34a738a --- /dev/null +++ b/registry/protocolbuffers/java/BUILD @@ -0,0 +1,7 @@ +cc_binary( + name = "protoc-gen-java", + srcs = ["java.cc"], + deps = [ + "//:protoc_lib", + ], +) diff --git a/registry/protocolbuffers/java/Dockerfile b/registry/protocolbuffers/java/Dockerfile new file mode 100644 index 0000000..0b4a34b --- /dev/null +++ b/registry/protocolbuffers/java/Dockerfile @@ -0,0 +1,39 @@ +# syntax=docker/dockerfile:1.23 +FROM debian:trixie-20260505@sha256:e2d08da6f42ef4b09b165d55528a12727aeed8240dc9edf888e3ec07e10ef9da AS build +ARG VERSION + +ARG TARGETARCH +ARG BAZEL_OPTS="--host_jvm_args=-Djava.net.preferIPv4Stack=true" + +RUN apt-get update \ + && apt-get install -y curl git cmake build-essential autoconf clang libc++-dev libtool pkg-config unzip zip +RUN curl -fsSL -o /usr/local/bin/bazelisk https://github.com/bazelbuild/bazelisk/releases/download/v1.28.1/bazelisk-linux-${TARGETARCH} \ + && chmod +x /usr/local/bin/bazelisk \ + && mkdir /build \ + && chown nobody:nogroup /build \ + && usermod --home /build nobody + +USER nobody +WORKDIR /build +RUN curl -fsSL -o protoc.tar.gz https://github.com/protocolbuffers/protobuf/releases/download/${VERSION}/protobuf-${VERSION#v}.tar.gz \ + && tar --strip-components=1 -zxf protoc.tar.gz \ + && rm protoc.tar.gz +RUN bazelisk ${BAZEL_OPTS} build '//:protoc_lib' +COPY --link BUILD java.cc plugins/ +RUN bazelisk ${BAZEL_OPTS} build '//plugins:protoc-gen-java.stripped' + +FROM gcr.io/distroless/cc-debian13:latest@sha256:8b5d1db6d2253036a53cb8362d3e3fa82a7caf84c247772c46a023166c64e977 AS base +ARG VERSION + +FROM maven:3.9.11-eclipse-temurin-21 AS maven-deps +ARG VERSION +COPY pom.xml /tmp/pom.xml +RUN cd /tmp && mvn -f pom.xml dependency:go-offline + +FROM scratch +ARG VERSION +COPY --from=base --link / / +COPY --from=build --link --chmod=0755 /build/bazel-bin/plugins/protoc-gen-java . +COPY --from=maven-deps /root/.m2/repository /maven-repository +USER nobody +ENTRYPOINT ["/protoc-gen-java"] diff --git a/registry/protocolbuffers/java/java.cc b/registry/protocolbuffers/java/java.cc new file mode 100644 index 0000000..bd5cb13 --- /dev/null +++ b/registry/protocolbuffers/java/java.cc @@ -0,0 +1,7 @@ +#include +#include + +int main(int argc, char *argv[]) { + google::protobuf::compiler::java::JavaGenerator generator; + return google::protobuf::compiler::PluginMain(argc, argv, &generator); +} diff --git a/registry/protocolbuffers/java/plugin.yaml b/registry/protocolbuffers/java/plugin.yaml new file mode 100644 index 0000000..b43f69d --- /dev/null +++ b/registry/protocolbuffers/java/plugin.yaml @@ -0,0 +1,62 @@ +binary: protoc-gen-java +versions: + - v35.0 + - v34.1 + - v34.0 + - v33.5 + - v33.4 + - v33.3 + - v33.2 + - v33.1 + - v33.0 + - v32.1 + - v32.0 + - v31.1 + - v31.0 + - v30.2 + - v30.1 + - v30.0 + - v3.14.0 + - v29.5 + - v29.3 + - v29.2 + - v29.1 + - v29.0 + - v28.3 + - v28.2 + - v28.1 + - v28.0 + - v27.4 + - v27.3 + - v27.2 + - v27.1 + - v27.0 + - v26.1 + - v26.0 + - v25.8 + - v25.6 + - v25.3 + - v25.2 + - v25.1 + - v25.0 + - v24.4 + - v24.3 + - v24.2 + - v24.1 + - v24.0 + - v23.4 + - v23.3 + - v23.2 + - v23.1 + - v23.0 + - v22.4 + - v22.3 + - v22.2 + - v22.1 + - v22.0 + - v21.9 + - v21.8 + - v21.7 + - v21.12 + - v21.11 + - v21.10 diff --git a/registry/protocolbuffers/java/pom.xml b/registry/protocolbuffers/java/pom.xml new file mode 100644 index 0000000..85f31a1 --- /dev/null +++ b/registry/protocolbuffers/java/pom.xml @@ -0,0 +1,24 @@ + + 4.0.0 + temp + temp + 1.0 + + + com.google.protobuf + protobuf-java + 4.35.0 + + + + com.google.protobuf + protobuf-javalite + 4.35.0 + + + build.buf + protobuf-javalite + 4.35.0 + + + diff --git a/registry/protocolbuffers/js/.dockerignore b/registry/protocolbuffers/js/.dockerignore new file mode 100644 index 0000000..5d0f124 --- /dev/null +++ b/registry/protocolbuffers/js/.dockerignore @@ -0,0 +1,2 @@ +* +!Dockerfile diff --git a/registry/protocolbuffers/js/Dockerfile b/registry/protocolbuffers/js/Dockerfile new file mode 100644 index 0000000..53332de --- /dev/null +++ b/registry/protocolbuffers/js/Dockerfile @@ -0,0 +1,24 @@ +# syntax=docker/dockerfile:1.19 +FROM debian:bookworm-20260223 AS build +ARG VERSION + +ARG TARGETARCH + +RUN apt-get update \ + && apt-get install -y curl +RUN arch=${TARGETARCH}; \ + if [ "${arch}" = "amd64" ]; then arch="x86_64"; elif [ "${arch}" = "arm64" ]; then arch="aarch_64"; fi; \ + curl -fsSL -o /tmp/protobuf-javascript.tar.gz https://github.com/protocolbuffers/protobuf-javascript/releases/download/${VERSION}/protobuf-javascript-${VERSION#v}-linux-${arch}.tar.gz \ + && cd /tmp \ + && tar zxf protobuf-javascript.tar.gz bin/protoc-gen-js \ + && rm -f protobuf-javascript.tar.gz + +FROM gcr.io/distroless/cc-debian12:latest@sha256:329e54034ce498f9c6b345044e8f530c6691f99e94a92446f68c0adf9baa8464 AS base +ARG VERSION + +FROM scratch +ARG VERSION +COPY --from=base --link / / +COPY --from=build --link --chmod=0755 /tmp/bin/protoc-gen-js . +USER nobody +ENTRYPOINT ["/protoc-gen-js"] diff --git a/registry/protocolbuffers/js/plugin.yaml b/registry/protocolbuffers/js/plugin.yaml new file mode 100644 index 0000000..0e404c8 --- /dev/null +++ b/registry/protocolbuffers/js/plugin.yaml @@ -0,0 +1,8 @@ +binary: protoc-gen-js +versions: + - v4.0.2 + - v4.0.1 + - v4.0.0 + - v3.21.4 + - v3.21.3 + - v3.21.2 diff --git a/registry/protocolbuffers/kotlin/.dockerignore b/registry/protocolbuffers/kotlin/.dockerignore new file mode 100644 index 0000000..c881939 --- /dev/null +++ b/registry/protocolbuffers/kotlin/.dockerignore @@ -0,0 +1,5 @@ +* +!BUILD +!Dockerfile +!kotlin.cc +!pom.xml diff --git a/registry/protocolbuffers/kotlin/BUILD b/registry/protocolbuffers/kotlin/BUILD new file mode 100644 index 0000000..340328b --- /dev/null +++ b/registry/protocolbuffers/kotlin/BUILD @@ -0,0 +1,7 @@ +cc_binary( + name = "protoc-gen-kotlin", + srcs = ["kotlin.cc"], + deps = [ + "//:protoc_lib", + ], +) diff --git a/registry/protocolbuffers/kotlin/Dockerfile b/registry/protocolbuffers/kotlin/Dockerfile new file mode 100644 index 0000000..87e14f7 --- /dev/null +++ b/registry/protocolbuffers/kotlin/Dockerfile @@ -0,0 +1,39 @@ +# syntax=docker/dockerfile:1.23 +FROM debian:trixie-20260505@sha256:e2d08da6f42ef4b09b165d55528a12727aeed8240dc9edf888e3ec07e10ef9da AS build +ARG VERSION + +ARG TARGETARCH +ARG BAZEL_OPTS="--host_jvm_args=-Djava.net.preferIPv4Stack=true" + +RUN apt-get update \ + && apt-get install -y curl git cmake build-essential autoconf clang libc++-dev libtool pkg-config unzip zip +RUN curl -fsSL -o /usr/local/bin/bazelisk https://github.com/bazelbuild/bazelisk/releases/download/v1.28.1/bazelisk-linux-${TARGETARCH} \ + && chmod +x /usr/local/bin/bazelisk \ + && mkdir /build \ + && chown nobody:nogroup /build \ + && usermod --home /build nobody + +USER nobody +WORKDIR /build +RUN curl -fsSL -o protoc.tar.gz https://github.com/protocolbuffers/protobuf/releases/download/${VERSION}/protobuf-${VERSION#v}.tar.gz \ + && tar --strip-components=1 -zxf protoc.tar.gz \ + && rm protoc.tar.gz +RUN bazelisk ${BAZEL_OPTS} build '//:protoc_lib' +COPY --link BUILD kotlin.cc plugins/ +RUN bazelisk ${BAZEL_OPTS} build '//plugins:protoc-gen-kotlin.stripped' + +FROM gcr.io/distroless/cc-debian13:latest@sha256:8b5d1db6d2253036a53cb8362d3e3fa82a7caf84c247772c46a023166c64e977 AS base +ARG VERSION + +FROM maven:3.9.11-eclipse-temurin-21 AS maven-deps +ARG VERSION +COPY pom.xml /tmp/pom.xml +RUN cd /tmp && mvn -f pom.xml dependency:go-offline + +FROM scratch +ARG VERSION +COPY --from=base --link / / +COPY --from=build --link --chmod=0755 /build/bazel-bin/plugins/protoc-gen-kotlin . +COPY --from=maven-deps /root/.m2/repository /maven-repository +USER nobody +ENTRYPOINT ["/protoc-gen-kotlin"] diff --git a/registry/protocolbuffers/kotlin/kotlin.cc b/registry/protocolbuffers/kotlin/kotlin.cc new file mode 100644 index 0000000..b438517 --- /dev/null +++ b/registry/protocolbuffers/kotlin/kotlin.cc @@ -0,0 +1,7 @@ +#include +#include + +int main(int argc, char *argv[]) { + google::protobuf::compiler::kotlin::KotlinGenerator generator; + return google::protobuf::compiler::PluginMain(argc, argv, &generator); +} diff --git a/registry/protocolbuffers/kotlin/plugin.yaml b/registry/protocolbuffers/kotlin/plugin.yaml new file mode 100644 index 0000000..63857b0 --- /dev/null +++ b/registry/protocolbuffers/kotlin/plugin.yaml @@ -0,0 +1,61 @@ +binary: protoc-gen-kotlin +versions: + - v35.0 + - v34.1 + - v34.0 + - v33.5 + - v33.4 + - v33.3 + - v33.2 + - v33.1 + - v33.0 + - v32.1 + - v32.0 + - v31.1 + - v31.0 + - v30.2 + - v30.1 + - v30.0 + - v29.5 + - v29.3 + - v29.2 + - v29.1 + - v29.0 + - v28.3 + - v28.2 + - v28.1 + - v28.0 + - v27.4 + - v27.3 + - v27.2 + - v27.1 + - v27.0 + - v26.1 + - v26.0 + - v25.8 + - v25.6 + - v25.3 + - v25.2 + - v25.1 + - v25.0 + - v24.4 + - v24.3 + - v24.2 + - v24.1 + - v24.0 + - v23.4 + - v23.3 + - v23.2 + - v23.1 + - v23.0 + - v22.4 + - v22.3 + - v22.2 + - v22.1 + - v22.0 + - v21.9 + - v21.8 + - v21.7 + - v21.12 + - v21.11 + - v21.10 diff --git a/registry/protocolbuffers/kotlin/pom.xml b/registry/protocolbuffers/kotlin/pom.xml new file mode 100644 index 0000000..b45e70a --- /dev/null +++ b/registry/protocolbuffers/kotlin/pom.xml @@ -0,0 +1,56 @@ + + 4.0.0 + temp + temp + 1.0 + + + com.google.protobuf + protobuf-kotlin + 4.35.0 + + + org.jetbrains.kotlin + kotlin-stdlib + 2.2.21 + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + 2.2.21 + + + com.google.protobuf + protobuf-java + 4.35.0 + + + + com.google.protobuf + protobuf-kotlin-lite + 4.35.0 + + + com.google.protobuf + protobuf-javalite + 4.35.0 + + + build.buf + protobuf-javalite + 4.35.0 + + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + 2.2.21 + + 2.1 + + + + + diff --git a/registry/protocolbuffers/objc/.dockerignore b/registry/protocolbuffers/objc/.dockerignore new file mode 100644 index 0000000..28438c5 --- /dev/null +++ b/registry/protocolbuffers/objc/.dockerignore @@ -0,0 +1,4 @@ +* +!BUILD +!Dockerfile +!objectivec.cc diff --git a/registry/protocolbuffers/objc/BUILD b/registry/protocolbuffers/objc/BUILD new file mode 100644 index 0000000..cc22935 --- /dev/null +++ b/registry/protocolbuffers/objc/BUILD @@ -0,0 +1,7 @@ +cc_binary( + name = "protoc-gen-objectivec", + srcs = ["objectivec.cc"], + deps = [ + "//:protoc_lib", + ], +) diff --git a/registry/protocolbuffers/objc/Dockerfile b/registry/protocolbuffers/objc/Dockerfile new file mode 100644 index 0000000..1fa1e3f --- /dev/null +++ b/registry/protocolbuffers/objc/Dockerfile @@ -0,0 +1,33 @@ +# syntax=docker/dockerfile:1.23 +FROM debian:trixie-20260505@sha256:e2d08da6f42ef4b09b165d55528a12727aeed8240dc9edf888e3ec07e10ef9da AS build +ARG VERSION + +ARG TARGETARCH +ARG BAZEL_OPTS="--host_jvm_args=-Djava.net.preferIPv4Stack=true" + +RUN apt-get update \ + && apt-get install -y curl git cmake build-essential autoconf clang libc++-dev libtool pkg-config unzip zip +RUN curl -fsSL -o /usr/local/bin/bazelisk https://github.com/bazelbuild/bazelisk/releases/download/v1.28.1/bazelisk-linux-${TARGETARCH} \ + && chmod +x /usr/local/bin/bazelisk \ + && mkdir /build \ + && chown nobody:nogroup /build \ + && usermod --home /build nobody + +USER nobody +WORKDIR /build +RUN curl -fsSL -o protoc.tar.gz https://github.com/protocolbuffers/protobuf/releases/download/${VERSION}/protobuf-${VERSION#v}.tar.gz \ + && tar --strip-components=1 -zxf protoc.tar.gz \ + && rm protoc.tar.gz +RUN bazelisk ${BAZEL_OPTS} build '//:protoc_lib' +COPY --link BUILD objectivec.cc plugins/ +RUN bazelisk ${BAZEL_OPTS} build '//plugins:protoc-gen-objectivec.stripped' + +FROM gcr.io/distroless/cc-debian13:latest@sha256:8b5d1db6d2253036a53cb8362d3e3fa82a7caf84c247772c46a023166c64e977 AS base +ARG VERSION + +FROM scratch +ARG VERSION +COPY --from=base --link / / +COPY --from=build --link --chmod=0755 /build/bazel-bin/plugins/protoc-gen-objectivec . +USER nobody +ENTRYPOINT ["/protoc-gen-objectivec"] diff --git a/registry/protocolbuffers/objc/objectivec.cc b/registry/protocolbuffers/objc/objectivec.cc new file mode 100644 index 0000000..1fda208 --- /dev/null +++ b/registry/protocolbuffers/objc/objectivec.cc @@ -0,0 +1,7 @@ +#include +#include + +int main(int argc, char *argv[]) { + google::protobuf::compiler::objectivec::ObjectiveCGenerator generator; + return google::protobuf::compiler::PluginMain(argc, argv, &generator); +} diff --git a/registry/protocolbuffers/objc/plugin.yaml b/registry/protocolbuffers/objc/plugin.yaml new file mode 100644 index 0000000..6e063b0 --- /dev/null +++ b/registry/protocolbuffers/objc/plugin.yaml @@ -0,0 +1,62 @@ +binary: protoc-gen-objectivec +versions: + - v35.0 + - v34.1 + - v34.0 + - v33.5 + - v33.4 + - v33.3 + - v33.2 + - v33.1 + - v33.0 + - v32.1 + - v32.0 + - v31.1 + - v31.0 + - v30.2 + - v30.1 + - v30.0 + - v3.14.0 + - v29.5 + - v29.3 + - v29.2 + - v29.1 + - v29.0 + - v28.3 + - v28.2 + - v28.1 + - v28.0 + - v27.4 + - v27.3 + - v27.2 + - v27.1 + - v27.0 + - v26.1 + - v26.0 + - v25.8 + - v25.6 + - v25.3 + - v25.2 + - v25.1 + - v25.0 + - v24.4 + - v24.3 + - v24.2 + - v24.1 + - v24.0 + - v23.4 + - v23.3 + - v23.2 + - v23.1 + - v23.0 + - v22.4 + - v22.3 + - v22.2 + - v22.1 + - v22.0 + - v21.9 + - v21.8 + - v21.7 + - v21.12 + - v21.11 + - v21.10 diff --git a/registry/protocolbuffers/php/.dockerignore b/registry/protocolbuffers/php/.dockerignore new file mode 100644 index 0000000..1cc4590 --- /dev/null +++ b/registry/protocolbuffers/php/.dockerignore @@ -0,0 +1,4 @@ +* +!BUILD +!Dockerfile +!php.cc diff --git a/registry/protocolbuffers/php/BUILD b/registry/protocolbuffers/php/BUILD new file mode 100644 index 0000000..a813f91 --- /dev/null +++ b/registry/protocolbuffers/php/BUILD @@ -0,0 +1,7 @@ +cc_binary( + name = "protoc-gen-php", + srcs = ["php.cc"], + deps = [ + "//:protoc_lib", + ], +) diff --git a/registry/protocolbuffers/php/Dockerfile b/registry/protocolbuffers/php/Dockerfile new file mode 100644 index 0000000..b208dc1 --- /dev/null +++ b/registry/protocolbuffers/php/Dockerfile @@ -0,0 +1,33 @@ +# syntax=docker/dockerfile:1.23 +FROM debian:trixie-20260505@sha256:e2d08da6f42ef4b09b165d55528a12727aeed8240dc9edf888e3ec07e10ef9da AS build +ARG VERSION + +ARG TARGETARCH +ARG BAZEL_OPTS="--host_jvm_args=-Djava.net.preferIPv4Stack=true" + +RUN apt-get update \ + && apt-get install -y curl git cmake build-essential autoconf clang libc++-dev libtool pkg-config unzip zip +RUN curl -fsSL -o /usr/local/bin/bazelisk https://github.com/bazelbuild/bazelisk/releases/download/v1.28.1/bazelisk-linux-${TARGETARCH} \ + && chmod +x /usr/local/bin/bazelisk \ + && mkdir /build \ + && chown nobody:nogroup /build \ + && usermod --home /build nobody + +USER nobody +WORKDIR /build +RUN curl -fsSL -o protoc.tar.gz https://github.com/protocolbuffers/protobuf/releases/download/${VERSION}/protobuf-${VERSION#v}.tar.gz \ + && tar --strip-components=1 -zxf protoc.tar.gz \ + && rm protoc.tar.gz +RUN bazelisk ${BAZEL_OPTS} build '//:protoc_lib' +COPY --link BUILD php.cc plugins/ +RUN bazelisk ${BAZEL_OPTS} build '//plugins:protoc-gen-php.stripped' + +FROM gcr.io/distroless/cc-debian13:latest@sha256:8b5d1db6d2253036a53cb8362d3e3fa82a7caf84c247772c46a023166c64e977 AS base +ARG VERSION + +FROM scratch +ARG VERSION +COPY --from=base / / +COPY --from=build --link --chmod=0755 /build/bazel-bin/plugins/protoc-gen-php . +USER nobody +ENTRYPOINT ["/protoc-gen-php"] diff --git a/registry/protocolbuffers/php/php.cc b/registry/protocolbuffers/php/php.cc new file mode 100644 index 0000000..107efe8 --- /dev/null +++ b/registry/protocolbuffers/php/php.cc @@ -0,0 +1,7 @@ +#include +#include + +int main(int argc, char *argv[]) { + google::protobuf::compiler::php::Generator generator; + return google::protobuf::compiler::PluginMain(argc, argv, &generator); +} diff --git a/registry/protocolbuffers/php/plugin.yaml b/registry/protocolbuffers/php/plugin.yaml new file mode 100644 index 0000000..253146e --- /dev/null +++ b/registry/protocolbuffers/php/plugin.yaml @@ -0,0 +1,62 @@ +binary: protoc-gen-php +versions: + - v35.0 + - v34.1 + - v34.0 + - v33.5 + - v33.4 + - v33.3 + - v33.2 + - v33.1 + - v33.0 + - v32.1 + - v32.0 + - v31.1 + - v31.0 + - v30.2 + - v30.1 + - v30.0 + - v3.14.0 + - v29.5 + - v29.3 + - v29.2 + - v29.1 + - v29.0 + - v28.3 + - v28.2 + - v28.1 + - v28.0 + - v27.4 + - v27.3 + - v27.2 + - v27.1 + - v27.0 + - v26.1 + - v26.0 + - v25.8 + - v25.6 + - v25.3 + - v25.2 + - v25.1 + - v25.0 + - v24.4 + - v24.3 + - v24.2 + - v24.1 + - v24.0 + - v23.4 + - v23.3 + - v23.2 + - v23.1 + - v23.0 + - v22.4 + - v22.3 + - v22.2 + - v22.1 + - v22.0 + - v21.9 + - v21.8 + - v21.7 + - v21.12 + - v21.11 + - v21.10 diff --git a/registry/protocolbuffers/pyi/.dockerignore b/registry/protocolbuffers/pyi/.dockerignore new file mode 100644 index 0000000..54ca5b1 --- /dev/null +++ b/registry/protocolbuffers/pyi/.dockerignore @@ -0,0 +1,4 @@ +* +!BUILD +!Dockerfile +!pyi.cc diff --git a/registry/protocolbuffers/pyi/BUILD b/registry/protocolbuffers/pyi/BUILD new file mode 100644 index 0000000..89eebd0 --- /dev/null +++ b/registry/protocolbuffers/pyi/BUILD @@ -0,0 +1,8 @@ +# Create a standalone binary to generate Python .pyi files +cc_binary( + name = "protoc-gen-pyi", + srcs = ["pyi.cc"], + deps = [ + "//:protoc_lib", + ], +) diff --git a/registry/protocolbuffers/pyi/Dockerfile b/registry/protocolbuffers/pyi/Dockerfile new file mode 100644 index 0000000..7c2801d --- /dev/null +++ b/registry/protocolbuffers/pyi/Dockerfile @@ -0,0 +1,33 @@ +# syntax=docker/dockerfile:1.23 +FROM debian:trixie-20260505@sha256:e2d08da6f42ef4b09b165d55528a12727aeed8240dc9edf888e3ec07e10ef9da AS build +ARG VERSION + +ARG TARGETARCH +ARG BAZEL_OPTS="--host_jvm_args=-Djava.net.preferIPv4Stack=true" + +RUN apt-get update \ + && apt-get install -y curl git cmake build-essential autoconf clang libc++-dev libtool pkg-config unzip zip +RUN curl -fsSL -o /usr/local/bin/bazelisk https://github.com/bazelbuild/bazelisk/releases/download/v1.28.1/bazelisk-linux-${TARGETARCH} \ + && chmod +x /usr/local/bin/bazelisk \ + && mkdir /build \ + && chown nobody:nogroup /build \ + && usermod --home /build nobody + +USER nobody +WORKDIR /build +RUN curl -fsSL -o protoc.tar.gz https://github.com/protocolbuffers/protobuf/releases/download/${VERSION}/protobuf-${VERSION#v}.tar.gz \ + && tar --strip-components=1 -zxf protoc.tar.gz \ + && rm protoc.tar.gz +RUN bazelisk ${BAZEL_OPTS} build '//:protoc_lib' +COPY BUILD pyi.cc plugins/ +RUN bazelisk ${BAZEL_OPTS} build '//plugins:protoc-gen-pyi.stripped' + +FROM gcr.io/distroless/cc-debian13:latest@sha256:8b5d1db6d2253036a53cb8362d3e3fa82a7caf84c247772c46a023166c64e977 AS base +ARG VERSION + +FROM scratch +ARG VERSION +COPY --from=base / / +COPY --from=build --link --chmod=0755 /build/bazel-bin/plugins/protoc-gen-pyi . +USER nobody +ENTRYPOINT ["/protoc-gen-pyi"] diff --git a/registry/protocolbuffers/pyi/plugin.yaml b/registry/protocolbuffers/pyi/plugin.yaml new file mode 100644 index 0000000..ad1de86 --- /dev/null +++ b/registry/protocolbuffers/pyi/plugin.yaml @@ -0,0 +1,61 @@ +binary: protoc-gen-pyi +versions: + - v35.0 + - v34.1 + - v34.0 + - v33.5 + - v33.4 + - v33.3 + - v33.2 + - v33.1 + - v33.0 + - v32.1 + - v32.0 + - v31.1 + - v31.0 + - v30.2 + - v30.1 + - v30.0 + - v29.5 + - v29.3 + - v29.2 + - v29.1 + - v29.0 + - v28.3 + - v28.2 + - v28.1 + - v28.0 + - v27.4 + - v27.3 + - v27.2 + - v27.1 + - v27.0 + - v26.1 + - v26.0 + - v25.8 + - v25.6 + - v25.3 + - v25.2 + - v25.1 + - v25.0 + - v24.4 + - v24.3 + - v24.2 + - v24.1 + - v24.0 + - v23.4 + - v23.3 + - v23.2 + - v23.1 + - v23.0 + - v22.4 + - v22.3 + - v22.2 + - v22.1 + - v22.0 + - v21.9 + - v21.8 + - v21.7 + - v21.12 + - v21.11 + - v21.10 diff --git a/registry/protocolbuffers/pyi/pyi.cc b/registry/protocolbuffers/pyi/pyi.cc new file mode 100644 index 0000000..4d32abe --- /dev/null +++ b/registry/protocolbuffers/pyi/pyi.cc @@ -0,0 +1,8 @@ +#include +#include + +// Standalone binary to generate Python .pyi files +int main(int argc, char *argv[]) { + google::protobuf::compiler::python::PyiGenerator generator; + return google::protobuf::compiler::PluginMain(argc, argv, &generator); +} diff --git a/registry/protocolbuffers/python/.dockerignore b/registry/protocolbuffers/python/.dockerignore new file mode 100644 index 0000000..ee7c3ba --- /dev/null +++ b/registry/protocolbuffers/python/.dockerignore @@ -0,0 +1,4 @@ +* +!BUILD +!Dockerfile +!python.cc diff --git a/registry/protocolbuffers/python/BUILD b/registry/protocolbuffers/python/BUILD new file mode 100644 index 0000000..013f431 --- /dev/null +++ b/registry/protocolbuffers/python/BUILD @@ -0,0 +1,7 @@ +cc_binary( + name = "protoc-gen-python", + srcs = ["python.cc"], + deps = [ + "//:protoc_lib", + ], +) diff --git a/registry/protocolbuffers/python/Dockerfile b/registry/protocolbuffers/python/Dockerfile new file mode 100644 index 0000000..88b3920 --- /dev/null +++ b/registry/protocolbuffers/python/Dockerfile @@ -0,0 +1,33 @@ +# syntax=docker/dockerfile:1.23 +FROM debian:trixie-20260505@sha256:e2d08da6f42ef4b09b165d55528a12727aeed8240dc9edf888e3ec07e10ef9da AS build +ARG VERSION + +ARG TARGETARCH +ARG BAZEL_OPTS="--host_jvm_args=-Djava.net.preferIPv4Stack=true" + +RUN apt-get update \ + && apt-get install -y curl git cmake build-essential autoconf clang libc++-dev libtool pkg-config unzip zip +RUN curl -fsSL -o /usr/local/bin/bazelisk https://github.com/bazelbuild/bazelisk/releases/download/v1.28.1/bazelisk-linux-${TARGETARCH} \ + && chmod +x /usr/local/bin/bazelisk \ + && mkdir /build \ + && chown nobody:nogroup /build \ + && usermod --home /build nobody + +USER nobody +WORKDIR /build +RUN curl -fsSL -o protoc.tar.gz https://github.com/protocolbuffers/protobuf/releases/download/${VERSION}/protobuf-${VERSION#v}.tar.gz \ + && tar --strip-components=1 -zxf protoc.tar.gz \ + && rm protoc.tar.gz +RUN bazelisk ${BAZEL_OPTS} build '//:protoc_lib' +COPY --link BUILD python.cc plugins/ +RUN bazelisk ${BAZEL_OPTS} build '//plugins:protoc-gen-python.stripped' + +FROM gcr.io/distroless/cc-debian13:latest@sha256:8b5d1db6d2253036a53cb8362d3e3fa82a7caf84c247772c46a023166c64e977 AS base +ARG VERSION + +FROM scratch +ARG VERSION +COPY --from=base --link / / +COPY --from=build --link --chmod=0755 /build/bazel-bin/plugins/protoc-gen-python . +USER nobody +ENTRYPOINT ["/protoc-gen-python"] diff --git a/registry/protocolbuffers/python/plugin.yaml b/registry/protocolbuffers/python/plugin.yaml new file mode 100644 index 0000000..bac134b --- /dev/null +++ b/registry/protocolbuffers/python/plugin.yaml @@ -0,0 +1,62 @@ +binary: protoc-gen-python +versions: + - v35.0 + - v34.1 + - v34.0 + - v33.5 + - v33.4 + - v33.3 + - v33.2 + - v33.1 + - v33.0 + - v32.1 + - v32.0 + - v31.1 + - v31.0 + - v30.2 + - v30.1 + - v30.0 + - v3.14.0 + - v29.5 + - v29.3 + - v29.2 + - v29.1 + - v29.0 + - v28.3 + - v28.2 + - v28.1 + - v28.0 + - v27.4 + - v27.3 + - v27.2 + - v27.1 + - v27.0 + - v26.1 + - v26.0 + - v25.8 + - v25.6 + - v25.3 + - v25.2 + - v25.1 + - v25.0 + - v24.4 + - v24.3 + - v24.2 + - v24.1 + - v24.0 + - v23.4 + - v23.3 + - v23.2 + - v23.1 + - v23.0 + - v22.4 + - v22.3 + - v22.2 + - v22.1 + - v22.0 + - v21.9 + - v21.8 + - v21.7 + - v21.12 + - v21.11 + - v21.10 diff --git a/registry/protocolbuffers/python/python.cc b/registry/protocolbuffers/python/python.cc new file mode 100644 index 0000000..f8a9f63 --- /dev/null +++ b/registry/protocolbuffers/python/python.cc @@ -0,0 +1,7 @@ +#include +#include + +int main(int argc, char *argv[]) { + google::protobuf::compiler::python::Generator generator; + return google::protobuf::compiler::PluginMain(argc, argv, &generator); +} diff --git a/registry/protocolbuffers/ruby/.dockerignore b/registry/protocolbuffers/ruby/.dockerignore new file mode 100644 index 0000000..2bdb552 --- /dev/null +++ b/registry/protocolbuffers/ruby/.dockerignore @@ -0,0 +1,4 @@ +* +!BUILD +!Dockerfile +!ruby.cc diff --git a/registry/protocolbuffers/ruby/BUILD b/registry/protocolbuffers/ruby/BUILD new file mode 100644 index 0000000..8bd93e1 --- /dev/null +++ b/registry/protocolbuffers/ruby/BUILD @@ -0,0 +1,7 @@ +cc_binary( + name = "protoc-gen-ruby", + srcs = ["ruby.cc"], + deps = [ + "//:protoc_lib", + ], +) diff --git a/registry/protocolbuffers/ruby/Dockerfile b/registry/protocolbuffers/ruby/Dockerfile new file mode 100644 index 0000000..25fb305 --- /dev/null +++ b/registry/protocolbuffers/ruby/Dockerfile @@ -0,0 +1,33 @@ +# syntax=docker/dockerfile:1.23 +FROM debian:trixie-20260505@sha256:e2d08da6f42ef4b09b165d55528a12727aeed8240dc9edf888e3ec07e10ef9da AS build +ARG VERSION + +ARG TARGETARCH +ARG BAZEL_OPTS="--host_jvm_args=-Djava.net.preferIPv4Stack=true" + +RUN apt-get update \ + && apt-get install -y curl git cmake build-essential autoconf clang libc++-dev libtool pkg-config unzip zip +RUN curl -fsSL -o /usr/local/bin/bazelisk https://github.com/bazelbuild/bazelisk/releases/download/v1.28.1/bazelisk-linux-${TARGETARCH} \ + && chmod +x /usr/local/bin/bazelisk \ + && mkdir /build \ + && chown nobody:nogroup /build \ + && usermod --home /build nobody + +USER nobody +WORKDIR /build +RUN curl -fsSL -o protoc.tar.gz https://github.com/protocolbuffers/protobuf/releases/download/${VERSION}/protobuf-${VERSION#v}.tar.gz \ + && tar --strip-components=1 -zxf protoc.tar.gz \ + && rm protoc.tar.gz +RUN bazelisk ${BAZEL_OPTS} build '//:protoc_lib' +COPY --link BUILD ruby.cc plugins/ +RUN bazelisk ${BAZEL_OPTS} build '//plugins:protoc-gen-ruby.stripped' + +FROM gcr.io/distroless/cc-debian13:latest@sha256:8b5d1db6d2253036a53cb8362d3e3fa82a7caf84c247772c46a023166c64e977 AS base +ARG VERSION + +FROM scratch +ARG VERSION +COPY --from=base --link / / +COPY --from=build --link --chmod=0755 /build/bazel-bin/plugins/protoc-gen-ruby . +USER nobody +ENTRYPOINT ["/protoc-gen-ruby"] diff --git a/registry/protocolbuffers/ruby/plugin.yaml b/registry/protocolbuffers/ruby/plugin.yaml new file mode 100644 index 0000000..ef337d2 --- /dev/null +++ b/registry/protocolbuffers/ruby/plugin.yaml @@ -0,0 +1,62 @@ +binary: protoc-gen-ruby +versions: + - v35.0 + - v34.1 + - v34.0 + - v33.5 + - v33.4 + - v33.3 + - v33.2 + - v33.1 + - v33.0 + - v32.1 + - v32.0 + - v31.1 + - v31.0 + - v30.2 + - v30.1 + - v30.0 + - v3.14.0 + - v29.5 + - v29.3 + - v29.2 + - v29.1 + - v29.0 + - v28.3 + - v28.2 + - v28.1 + - v28.0 + - v27.4 + - v27.3 + - v27.2 + - v27.1 + - v27.0 + - v26.1 + - v26.0 + - v25.8 + - v25.6 + - v25.3 + - v25.2 + - v25.1 + - v25.0 + - v24.4 + - v24.3 + - v24.2 + - v24.1 + - v24.0 + - v23.4 + - v23.3 + - v23.2 + - v23.1 + - v23.0 + - v22.4 + - v22.3 + - v22.2 + - v22.1 + - v22.0 + - v21.9 + - v21.8 + - v21.7 + - v21.12 + - v21.11 + - v21.10 diff --git a/registry/protocolbuffers/ruby/ruby.cc b/registry/protocolbuffers/ruby/ruby.cc new file mode 100644 index 0000000..73ba6a6 --- /dev/null +++ b/registry/protocolbuffers/ruby/ruby.cc @@ -0,0 +1,7 @@ +#include +#include + +int main(int argc, char *argv[]) { + google::protobuf::compiler::ruby::Generator generator; + return google::protobuf::compiler::PluginMain(argc, argv, &generator); +} diff --git a/sdk/client.go b/sdk/client.go index da829f0..374fa7b 100644 --- a/sdk/client.go +++ b/sdk/client.go @@ -6,6 +6,7 @@ import ( "time" "google.golang.org/grpc" + "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/pluginpb" generator "github.com/easyp-tech/service/api/generator/v1" @@ -141,3 +142,32 @@ func (c *Client) ListPlugins(ctx context.Context, filter ...PluginFilter) ([]*ge return applyFilter(resp.GetPlugins(), filter[0]), nil } + +// CreatePlugin registers a new plugin in the service. +func (c *Client) CreatePlugin( + ctx context.Context, + group, name, version string, + pluginConfig map[string]any, + tags []string, +) (*generator.PluginInfo, error) { + ctx, cancel := c.withTimeout(ctx, c.cfg.createPluginTimeout) + defer cancel() + + cfgStruct, err := structpb.NewStruct(pluginConfig) + if err != nil { + return nil, fmt.Errorf("structpb.NewStruct: %w", err) + } + + resp, err := c.genClient.CreatePlugin(ctx, &generator.CreatePluginRequest{ + Group: group, + Name: name, + Version: version, + Config: cfgStruct, + Tags: tags, + }) + if err != nil { + return nil, fmt.Errorf("c.genClient.CreatePlugin: %w", err) + } + + return resp.GetPlugin(), nil +} diff --git a/sdk/config.go b/sdk/config.go index 7333ea9..cc3e477 100644 --- a/sdk/config.go +++ b/sdk/config.go @@ -21,8 +21,9 @@ type config struct { retryMaxDelay time.Duration // Timeouts - generateCodeTimeout time.Duration - listPluginsTimeout time.Duration + generateCodeTimeout time.Duration + listPluginsTimeout time.Duration + createPluginTimeout time.Duration // Interceptors unaryInterceptors []grpc.UnaryClientInterceptor @@ -41,8 +42,9 @@ func defaultConfig() *config { maxRetries: 3, retryBaseDelay: 100 * time.Millisecond, retryMaxDelay: 5 * time.Second, - generateCodeTimeout: 30 * time.Second, - listPluginsTimeout: 10 * time.Second, + generateCodeTimeout: 30 * time.Second, + listPluginsTimeout: 10 * time.Second, + createPluginTimeout: 30 * time.Second, healthCheckInterval: 30 * time.Second, } } diff --git a/registry/grpc-ecosystem/gateway/v2.27.3/Dockerfile b/test_registry/grpc-ecosystem/gateway/v2.27.3/Dockerfile similarity index 100% rename from registry/grpc-ecosystem/gateway/v2.27.3/Dockerfile rename to test_registry/grpc-ecosystem/gateway/v2.27.3/Dockerfile diff --git a/registry/grpc-ecosystem/openapiv2/v2.27.3/Dockerfile b/test_registry/grpc-ecosystem/openapiv2/v2.27.3/Dockerfile similarity index 100% rename from registry/grpc-ecosystem/openapiv2/v2.27.3/Dockerfile rename to test_registry/grpc-ecosystem/openapiv2/v2.27.3/Dockerfile diff --git a/registry/grpc/go/v1.5.1/Dockerfile b/test_registry/grpc/go/v1.5.1/Dockerfile similarity index 100% rename from registry/grpc/go/v1.5.1/Dockerfile rename to test_registry/grpc/go/v1.5.1/Dockerfile diff --git a/registry/protocolbuffers/go/v1.36.10/Dockerfile b/test_registry/protocolbuffers/go/v1.36.10/Dockerfile similarity index 100% rename from registry/protocolbuffers/go/v1.36.10/Dockerfile rename to test_registry/protocolbuffers/go/v1.36.10/Dockerfile