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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/format/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (

"github.com/bufbuild/protocompile/parser"
"github.com/bufbuild/protocompile/reporter"
"github.com/pubgo/funk/assert"
"github.com/pubgo/funk/v2/assert"
)

// Format formats and writes the target module files into a read bucket.
Expand Down
2 changes: 1 addition & 1 deletion cmd/formatcmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"path/filepath"
"strings"

"github.com/pubgo/funk/errors"
"github.com/pubgo/funk/v2/errors"
"github.com/pubgo/redant"

"github.com/pubgo/protobuild/cmd/format"
Expand Down
166 changes: 145 additions & 21 deletions cmd/protobuild/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,21 @@ package protobuild
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"io/fs"
"log/slog"
"os"
"os/exec"
"path/filepath"
"strings"

"github.com/pubgo/funk/assert"
"github.com/pubgo/funk/generic"
"github.com/pubgo/funk/log"
"github.com/pubgo/funk/recovery"
"github.com/pubgo/funk/running"
"github.com/pubgo/funk/v2/assert"
"github.com/pubgo/funk/v2/cmds/upgradecmd"
"github.com/pubgo/funk/v2/log"
"github.com/pubgo/funk/v2/recovery"
"github.com/pubgo/funk/v2/running"
"github.com/pubgo/protobuild/cmd/formatcmd"
"github.com/pubgo/protobuild/cmd/linters"
"github.com/pubgo/protobuild/cmd/webcmd"
Expand All @@ -29,12 +32,62 @@ import (
)

var (
globalCfg Config

globalCfg Config
protoCfg = "protobuf.yaml"
protoPluginCfg = "protobuf.plugin.yaml"
pwd = assert.Exit1(os.Getwd())
logger = log.GetLogger("protobuild")

defaultSkillContent = `---
name: protobuild
description: "Manage protobuf projects: vendor dependencies, generate code, lint/format. Use only when protobuf.yaml is present in the project root."
license: MIT
metadata:
author: pubgo
version: "1.0.0"
compatibility: "Requires protobuild CLI, protoc, plugins; run in repo root with protobuf.yaml."
allowed-tools: "Bash(protobuild:*)"
---

## When to use
- 项目存在 ` + "`protobuf.yaml`" + `,需要下载依赖、生成代码、lint/format proto。
- 先 vendor 再 gen;只检查格式/规范,使用 lint 或 format --exit-code。

## Safety / Constraints
- 会写盘的命令:gen、format -w、install(需告知用户)。
- 必须在包含 protobuf.yaml 的项目根目录执行。
- 如未 vendor 过依赖,先运行 protobuild vendor。

## Inputs
- command: one of [gen, vendor, lint, format, clean, deps, install, doctor, web]
- args: optional string list, e.g. ["-w", "--diff"]
- working_dir: project root (defaults to current CWD if已在项目根)

## Steps
1) 确认 working_dir 内存在 protobuf.yaml,否则提示用户补全。
2) 如 command=gen 且依赖未就绪,先运行 protobuild vendor。
3) 执行:protobuild <command> <args...>(在 working_dir)。
4) 收集 stdout/stderr/exit_code,若命令会写盘(gen/format -w/install),提示用户查看变更。
5) 失败时给出 stderr 摘要与下一步建议(如检查路径/依赖/插件)。

## Examples
- protobuild vendor
- protobuild gen
- protobuild format --exit-code
- protobuild lint
`

defaultOpenAIContent = `interface:
display_name: "Protobuild"
short_description: "Proto build/lint/format tool"
icon_small: "./assets/protobuild-16.png"
icon_large: "./assets/protobuild-64.png"
brand_color: "#0F9D58"
default_prompt: "Use protobuild to vendor deps and generate proto code."

dependencies:
tools: []
`
)

const (
Expand All @@ -54,14 +107,14 @@ func withParseConfig() redant.MiddlewareFunc {
}
}

// Main creates the main CLI application.
func Main(ver string) *redant.Command {
// Main creates and returns the root CLI command with all subcommands.
func Main() *redant.Command {
var force, update, dryRun bool
cliArgs, options := linters.NewCli()

app := &redant.Command{
Use: "protobuf",
Short: "protobuf generation, configuration and management",
Use: "protobuild",
Short: "Protobuf generation, configuration and management tool",
Options: typex.Options{
redant.Option{
Flag: "conf",
Expand All @@ -82,28 +135,99 @@ func Main(ver string) *redant.Command {
newFormatCommand(),
newDepsCommand(),
newCleanCommand(&dryRun),
newSkillsCommand(),
webcmd.New(&protoCfg),
newVersionCommand(),
upgradecmd.New("pubgo", "protobuild"),
},
}

return app
}

// handleStdinPlugin handles protoc plugin mode when invoked via stdin.
func handleStdinPlugin(ctx context.Context, inv *redant.Invocation) error {
if shutil.IsHelp() {
return nil
// newSkillsCommand installs the Agent Skills template into the current repository.
func newSkillsCommand() *redant.Command {
var outDir = ".agents/skills/protobuild"
var withOpenAI bool
var force bool

return &redant.Command{
Use: "skills",
Short: "install Agent Skills template (.agents/skills/protobuild)",
Options: typex.Options{
redant.Option{
Flag: "path",
Shorthand: "p",
Description: "skills output directory",
Value: redant.StringOf(&outDir),
},
redant.Option{
Flag: "openai",
Description: "also generate agents/openai.yaml",
Value: redant.BoolOf(&withOpenAI),
},
redant.Option{
Flag: "force",
Shorthand: "f",
Description: "overwrite existing files",
Value: redant.BoolOf(&force),
},
},
Handler: func(ctx context.Context, inv *redant.Invocation) error {
defer recovery.Exit()

skillPath := filepath.Join(outDir, "SKILL.md")
if err := os.MkdirAll(outDir, 0o755); err != nil {
return err
}

if err := writeFileIfNeeded(skillPath, []byte(defaultSkillContent), force); err != nil {
return err
}

if withOpenAI {
openaiDir := filepath.Join(outDir, "agents")
if err := os.MkdirAll(openaiDir, 0o755); err != nil {
return err
}
openaiPath := filepath.Join(openaiDir, "openai.yaml")
if err := writeFileIfNeeded(openaiPath, []byte(defaultOpenAIContent), force); err != nil {
return err
}
}

fmt.Printf("Skill installed at %s\n", skillPath)
if withOpenAI {
fmt.Printf("OpenAI metadata at %s\n", filepath.Join(outDir, "agents", "openai.yaml"))
}
fmt.Println("You can validate with: skills-ref validate", outDir)
return nil
},
}
}

func writeFileIfNeeded(path string, data []byte, force bool) error {
if _, err := os.Stat(path); err == nil {
if !force {
return fmt.Errorf("file already exists: %s (use --force to overwrite)", path)
}
} else if !errors.Is(err, fs.ErrNotExist) {
return err
}

return os.WriteFile(path, data, 0o644)
}

// handleStdinPlugin handles protoc plugin mode when invoked via stdin.
func handleStdinPlugin(ctx context.Context, inv *redant.Invocation) error {
file := os.Stdin
if term.IsTerminal(int(file.Fd())) {
return nil
return redant.DefaultHelpFn()(ctx, inv)
}

fi := assert.Exit1(file.Stat())
if fi.Size() == 0 {
return nil
return redant.DefaultHelpFn()(ctx, inv)
}

in := assert.Must1(io.ReadAll(file))
Expand All @@ -121,7 +245,7 @@ func handleStdinPlugin(ctx context.Context, inv *redant.Invocation) error {

plgName, params := parsePluginParams(req.GetParameter())
if len(params) > 0 {
req.Parameter = generic.Ptr(strings.Join(params, ","))
req.Parameter = lo.ToPtr(strings.Join(params, ","))
}

return executeWrapperPlugin(plgName, req)
Expand Down Expand Up @@ -221,9 +345,9 @@ func newVersionCommand() *redant.Command {
Short: "version info",
Handler: func(ctx context.Context, inv *redant.Invocation) error {
defer recovery.Exit()
fmt.Printf("Project: %s\n", running.Project)
fmt.Printf("Version: %s\n", running.Version)
fmt.Printf("GitCommit: %s\n", running.CommitID)
fmt.Printf("Project: %s\n", running.Project())
fmt.Printf("Version: %s\n", running.Version())
fmt.Printf("GitCommit: %s\n", running.CommitID())
return nil
},
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/protobuild/cmd_doctor.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"runtime"
"strings"

"github.com/pubgo/funk/recovery"
"github.com/pubgo/funk/v2/recovery"
"github.com/pubgo/protobuild/internal/typex"
"github.com/pubgo/redant"
)
Expand Down
2 changes: 1 addition & 1 deletion cmd/protobuild/cmd_init.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"path/filepath"
"strings"

"github.com/pubgo/funk/recovery"
"github.com/pubgo/funk/v2/recovery"
"github.com/pubgo/protobuild/internal/config"
"github.com/pubgo/protobuild/internal/typex"
"github.com/pubgo/redant"
Expand Down
4 changes: 2 additions & 2 deletions cmd/protobuild/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import (
"os"
"path/filepath"

"github.com/pubgo/funk/pathutil"
"github.com/pubgo/funk/recovery"
"github.com/pubgo/funk/v2/pathutil"
"github.com/pubgo/funk/v2/recovery"
"github.com/pubgo/protobuild/internal/depresolver"
"github.com/pubgo/redant"
"gopkg.in/yaml.v3"
Expand Down
4 changes: 2 additions & 2 deletions cmd/protobuild/proto_walker.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import (
"path/filepath"
"strings"

"github.com/pubgo/funk/assert"
"github.com/pubgo/funk/pathutil"
"github.com/pubgo/funk/v2/assert"
"github.com/pubgo/funk/v2/pathutil"
"github.com/samber/lo"
)

Expand Down
6 changes: 3 additions & 3 deletions cmd/protobuild/protoc_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import (
"path/filepath"
"strings"

"github.com/pubgo/funk/assert"
"github.com/pubgo/funk/log"
"github.com/pubgo/funk/pathutil"
"github.com/pubgo/funk/v2/assert"
"github.com/pubgo/funk/v2/log"
"github.com/pubgo/funk/v2/pathutil"
"github.com/pubgo/protobuild/internal/shutil"
"github.com/samber/lo"
)
Expand Down
12 changes: 6 additions & 6 deletions cmd/protobuild/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import (
"github.com/cnf/structhash"
"github.com/googleapis/api-linter/v2/lint"
"github.com/huandu/go-clone"
"github.com/pubgo/funk/assert"
"github.com/pubgo/funk/errors"
"github.com/pubgo/funk/pathutil"
"github.com/pubgo/funk/strutil"
"github.com/pubgo/funk/v2/assert"
"github.com/pubgo/funk/v2/errors"
"github.com/pubgo/funk/v2/pathutil"
"github.com/pubgo/funk/v2/strutil"
"github.com/pubgo/protobuild/cmd/linters"
"github.com/pubgo/protobuild/internal/config"
"gopkg.in/yaml.v3"
Expand Down Expand Up @@ -135,11 +135,11 @@ var checkSumPath = func(vendorPath string) string {
func getChecksumData(vendorPath string) (string, error) {
var path = checkSumPath(vendorPath)
if pathutil.IsNotExist(vendorPath) {
return "", errors.NewFmt("file not found")
return "", errors.New("file not found")
}

if pathutil.IsNotExist(path) {
return "", errors.NewFmt("file not found")
return "", errors.New("file not found")
}

data, err := os.ReadFile(path)
Expand Down
16 changes: 8 additions & 8 deletions cmd/protobuild/vendor_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import (
"path/filepath"
"strings"

"github.com/pubgo/funk/assert"
"github.com/pubgo/funk/errors"
"github.com/pubgo/funk/pathutil"
"github.com/pubgo/funk/recovery"
"github.com/pubgo/funk/v2/assert"
"github.com/pubgo/funk/v2/errors"
"github.com/pubgo/funk/v2/pathutil"
"github.com/pubgo/funk/v2/recovery"
"github.com/pubgo/protobuild/internal/depresolver"
"github.com/schollz/progressbar/v3"
)
Expand Down Expand Up @@ -128,10 +128,10 @@ func (s *VendorService) CopyToVendor(resolvedPaths map[string]string) (int, erro
}

defer recovery.Err(&gErr, func(err error) error {
return errors.WrapTag(err,
errors.T("path", path),
errors.T("name", info.Name()),
)
return errors.WrapTags(err, errors.Tags{
"path": path,
"name": info.Name(),
})
})

if info.IsDir() || !strings.HasSuffix(info.Name(), ".proto") {
Expand Down
8 changes: 4 additions & 4 deletions cmd/protobuild/yaml_types.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package protobuild

import (
"github.com/pubgo/funk/assert"
"github.com/pubgo/funk/errors"
"github.com/pubgo/funk/v2/assert"
"github.com/pubgo/funk/v2/errors"
yaml "gopkg.in/yaml.v3"
)

Expand Down Expand Up @@ -34,7 +34,7 @@ func (p *YamlListType[T]) UnmarshalYAML(value *yaml.Node) error {
default:
var val any
assert.Exit(value.Decode(&val))
return errors.Format("yaml kind type error, kind=%v data=%v", value.Kind, val)
return errors.Errorf("yaml kind type error, kind=%v data=%v", value.Kind, val)
}
}

Expand Down Expand Up @@ -65,6 +65,6 @@ func (p *strOrObject) UnmarshalYAML(value *yaml.Node) error {
default:
var val any
assert.Exit(value.Decode(&val))
return errors.Format("yaml kind type error,kind=%v data=%v", value.Kind, val)
return errors.Errorf("yaml kind type error,kind=%v data=%v", value.Kind, val)
}
}
Loading