Repository: bufbuild/buf (Go)

Issue:
plugin not found in $PATH
buf 1.14.0 is not finding plugins added to the PATH variable:

```
> PATH="$(pwd)/tools:${PATH}" buf generate
Failure: plugin go-grpc: exec: "": executable file not found in $PATH

> ls tools
buf			protoc-gen-go		protoc-gen-go-grpc
```

We use this approach to pin plugin versions. Downgrading to to 1.13.1 is our workaround for now.


Discussion:
Can you provide some more information for us to reproduce?

- Confirm that protoc-gen-go-grpc is executable
- OS, Arch, go env
I've tried extensively and I am unable to reproduce this. I have to close for now, but please re-open if you can provide a specific, reproducible example, including os/arch information, the buf.gen.yaml you are using, and a test `.proto` file that fails.
Hi,

I have similar problem, downgrade to buf v1.13.1 helped, v1.14 and 1.15 do not work...

OS = Mac OS 13.1(22C65) Ventura
arch =  arm64

protoc-gen-go-grpc works well:
```
$ protoc-gen-go-grpc --version
protoc-gen-go-grpc 1.3.0
```

go env:
```
GO111MODULE=""
GOARCH="arm64"
GOBIN=""
GOCACHE="/Users/user/Library/Caches/go-build"
GOENV="/Users/user/Library/Application Support/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="arm64"
GOHOSTOS="darwin"
GOINSECURE=""
GOMODCACHE="/Users/user/go/pkg/mod"
GONOPROXY="github.com/myorgname"
GONOSUMDB="go.myorgname.com"
GOOS="darwin"
GOPATH="/Users/user/go"
GOPRIVATE="github.com/myorgname"
GOPROXY="https://go.myorgname.com,https://proxy.golang.org,direct"
GOROOT="/Users/user/sdk/go1.18.3"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/Users/user/sdk/go1.18.3/pkg/tool/darwin_arm64"
GOVCS=""
GOVERSION="go1.18.3"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD="/dev/null"
GOWORK=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/h7/7hcd2nns3qjf_k3blr_65fvc0wy1jm/T/go-build956519854=/tmp/go-build -gno-record-gcc-switches -fno-common"
```

buf.gen.yaml
```yaml
version: v1beta1
plugins:
  - name: go
    out: gen/go
    opt: paths=source_relative
  - name: go-grpc
    out: gen/go
    opt: paths=source_relative
  - name: grpc-gateway
    out: gen/go
    opt:
      - paths=source_relative
      - generate_unbound_methods=true
  - name: openapiv2
    out: docs/openapiv2
```

buf.yaml
```yaml
version: v1beta1
name: buf.build/example/test
deps:
  - buf.build/googleapis/googleapis
  - buf.build/grpc-ecosystem/grpc-gateway
build:
  roots:
    - .
lint:
  use:
    - MINIMAL
    - STYLE_BASIC
  except:
    - PACKAGE_DIRECTORY_MATCH
breaking:
  use:
    - FILE
```

test.proto

```proto
syntax = "proto3";

package text.v1;
option go_package="github.com/myorg/test/v1";

import "google/api/annotations.proto";
import "google/protobuf/struct.proto";
import "protoc-gen-openapiv2/options/annotations.proto";

message Request {
  string data = 1;
}

message Response {
  string data = 1;
}

service TestService {
  rpc Test (Request) returns (Response) {
    option (google.api.http) = {
      get: "/v1/test"
    };

    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
      parameters: {
        headers: {
          name: "header";
          description: "header";
          type: STRING,
        };
      };
    };
  }
}
```

(protoc-gen-go-grpc       protoc-gen-grpc-gateway  protoc-gen-openapiv2 are all installed and can be executed)

I've also experienced this under very similar conditions mentioned by @ohouse-andy. buf v1.15.0, MacOS Ventura 13.1, arm64 arch, confirmed `protoc-gen-go-grpc` and `protoc-gen-grpc-gateway` are on my $PATH and are callable. Downgrading to 1.13.0 resolved the issue. 
This is fixed in #1893, we will cut a v1.15.1 release with this patch. Apologies for the inconvenience.
This is fixed on v1.15.1.



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0c1407cdfa..ee2b912d47 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,7 +2,7 @@
 
 ## [Unreleased]
 
-- No changes yet.
+- Fixes a bug in `buf generate` with `v1beta1` config files.
 
 ## [v1.15.0] - 2023-02-28
 
diff --git a/private/buf/bufgen/config.go b/private/buf/bufgen/config.go
index e92af3c6cf..194826cff5 100644
--- a/private/buf/bufgen/config.go
+++ b/private/buf/bufgen/config.go
@@ -580,16 +580,16 @@ func newConfigV1Beta1(externalConfig ExternalConfigV1Beta1, id string) (*Config,
 		if err != nil {
 			return nil, err
 		}
-		pluginConfigs = append(
-			pluginConfigs,
-			&PluginConfig{
-				Name:     plugin.Name,
-				Out:      plugin.Out,
-				Opt:      opt,
-				Path:     []string{plugin.Path},
-				Strategy: strategy,
-			},
-		)
+		pluginConfig := &PluginConfig{
+			Name:     plugin.Name,
+			Out:      plugin.Out,
+			Opt:      opt,
+			Strategy: strategy,
+		}
+		if plugin.Path != "" {
+			pluginConfig.Path = []string{plugin.Path}
+		}
+		pluginConfigs = append(pluginConfigs, pluginConfig)
 	}
 	return &Config{
 		PluginConfigs: pluginConfigs,


Test patch:
diff --git a/private/buf/bufgen/config_test.go b/private/buf/bufgen/config_test.go
index fdc50dc086..561e68e17d 100644
--- a/private/buf/bufgen/config_test.go
+++ b/private/buf/bufgen/config_test.go
@@ -24,6 +24,7 @@ import (
 	"github.com/bufbuild/buf/private/bufpkg/bufmodule/bufmoduleref"
 	"github.com/bufbuild/buf/private/pkg/storage"
 	"github.com/bufbuild/buf/private/pkg/storage/storagemem"
+	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 	"go.uber.org/zap"
 	"google.golang.org/protobuf/types/descriptorpb"
@@ -87,68 +88,48 @@ func TestReadConfigV1Beta1(t *testing.T) {
 			},
 		},
 	}
+	successConfig4 := &Config{
+		ManagedConfig: &ManagedConfig{
+			OptimizeForConfig: &OptimizeForConfig{
+				Default:  descriptorpb.FileOptions_LITE_RUNTIME,
+				Except:   make([]bufmoduleref.ModuleIdentity, 0),
+				Override: make(map[bufmoduleref.ModuleIdentity]descriptorpb.FileOptions_OptimizeMode),
+			},
+		},
+		PluginConfigs: []*PluginConfig{
+			{
+				Name:     "go",
+				Out:      "gen/go",
+				Strategy: StrategyAll,
+			},
+		},
+	}
 	ctx := context.Background()
 	nopLogger := zap.NewNop()
 	provider := NewProvider(zap.NewNop())
 	readBucket, err := storagemem.NewReadBucket(nil)
 	require.NoError(t, err)
-	config, err := ReadConfig(ctx, nopLogger, provider, readBucket, ReadConfigWithOverride(filepath.Join("testdata", "v1beta1", "gen_success1.yaml")))
-	require.NoError(t, err)
-	require.Equal(t, successConfig, config)
-	data, err := os.ReadFile(filepath.Join("testdata", "v1beta1", "gen_success1.yaml"))
-	require.NoError(t, err)
-	config, err = ReadConfig(ctx, nopLogger, provider, readBucket, ReadConfigWithOverride(string(data)))
-	require.NoError(t, err)
-	require.Equal(t, successConfig, config)
-	config, err = ReadConfig(ctx, nopLogger, provider, readBucket, ReadConfigWithOverride(filepath.Join("testdata", "v1beta1", "gen_success1.json")))
-	require.NoError(t, err)
-	require.Equal(t, successConfig, config)
-	data, err = os.ReadFile(filepath.Join("testdata", "v1beta1", "gen_success1.json"))
-	require.NoError(t, err)
-	config, err = ReadConfig(ctx, nopLogger, provider, readBucket, ReadConfigWithOverride(string(data)))
-	require.NoError(t, err)
-	require.Equal(t, successConfig, config)
 
-	config, err = ReadConfig(ctx, nopLogger, provider, readBucket, ReadConfigWithOverride(filepath.Join("testdata", "v1beta1", "gen_success2.yaml")))
-	require.NoError(t, err)
-	require.Equal(t, successConfig2, config)
-	data, err = os.ReadFile(filepath.Join("testdata", "v1beta1", "gen_success2.yaml"))
-	require.NoError(t, err)
-	config, err = ReadConfig(ctx, nopLogger, provider, readBucket, ReadConfigWithOverride(string(data)))
-	require.NoError(t, err)
-	require.Equal(t, successConfig2, config)
-	config, err = ReadConfig(ctx, nopLogger, provider, readBucket, ReadConfigWithOverride(filepath.Join("testdata", "v1beta1", "gen_success2.json")))
-	require.NoError(t, err)
-	require.Equal(t, successConfig2, config)
-	data, err = os.ReadFile(filepath.Join("testdata", "v1beta1", "gen_success2.json"))
-	require.NoError(t, err)
-	config, err = ReadConfig(ctx, nopLogger, provider, readBucket, ReadConfigWithOverride((string(data))))
-	require.NoError(t, err)
-	require.Equal(t, successConfig2, config)
-	config, err = ReadConfig(ctx, nopLogger, provider, readBucket, ReadConfigWithOverride(filepath.Join("testdata", "v1beta1", "gen_success3.yaml")))
-	require.NoError(t, err)
-	require.Equal(t, successConfig3, config)
-	data, err = os.ReadFile(filepath.Join("testdata", "v1beta1", "gen_success3.yaml"))
-	require.NoError(t, err)
-	config, err = ReadConfig(ctx, nopLogger, provider, readBucket, ReadConfigWithOverride((string(data))))
-	require.NoError(t, err)
-	require.Equal(t, successConfig3, config)
-	config, err = ReadConfig(ctx, nopLogger, provider, readBucket, ReadConfigWithOverride(filepath.Join("testdata", "v1beta1", "gen_success3.json")))
-	require.NoError(t, err)
-	require.Equal(t, successConfig3, config)
-	data, err = os.ReadFile(filepath.Join("testdata", "v1beta1", "gen_success3.json"))
-	require.NoError(t, err)
-	config, err = ReadConfig(ctx, nopLogger, provider, readBucket, ReadConfigWithOverride(string(data)))
-	require.NoError(t, err)
-	require.Equal(t, successConfig3, config)
-	config, err = ReadConfig(ctx, nopLogger, provider, readBucket, ReadConfigWithOverride(filepath.Join("testdata", "v1beta1", "gen_success3.yml")))
-	require.NoError(t, err)
-	require.Equal(t, successConfig3, config)
-	data, err = os.ReadFile(filepath.Join("testdata", "v1beta1", "gen_success3.yml"))
-	require.NoError(t, err)
-	config, err = ReadConfig(ctx, nopLogger, provider, readBucket, ReadConfigWithOverride(string(data)))
-	require.NoError(t, err)
-	require.Equal(t, successConfig3, config)
+	testReadConfigSuccess := func(t *testing.T, configPath string, expected *Config) {
+		t.Helper()
+		config, err := ReadConfig(ctx, nopLogger, provider, readBucket, ReadConfigWithOverride(configPath))
+		require.NoError(t, err)
+		assert.Equal(t, expected, config)
+		data, err := os.ReadFile(configPath)
+		require.NoError(t, err)
+		config, err = ReadConfig(ctx, nopLogger, provider, readBucket, ReadConfigWithOverride(string(data)))
+		require.NoError(t, err)
+		assert.Equal(t, expected, config)
+	}
+
+	testReadConfigSuccess(t, filepath.Join("testdata", "v1beta1", "gen_success1.yaml"), successConfig)
+	testReadConfigSuccess(t, filepath.Join("testdata", "v1beta1", "gen_success1.json"), successConfig)
+	testReadConfigSuccess(t, filepath.Join("testdata", "v1beta1", "gen_success2.yaml"), successConfig2)
+	testReadConfigSuccess(t, filepath.Join("testdata", "v1beta1", "gen_success2.json"), successConfig2)
+	testReadConfigSuccess(t, filepath.Join("testdata", "v1beta1", "gen_success3.yaml"), successConfig3)
+	testReadConfigSuccess(t, filepath.Join("testdata", "v1beta1", "gen_success3.json"), successConfig3)
+	testReadConfigSuccess(t, filepath.Join("testdata", "v1beta1", "gen_success3.yml"), successConfig3)
+	testReadConfigSuccess(t, filepath.Join("testdata", "v1beta1", "gen_success4_nopath.yaml"), successConfig4)
 
 	testReadConfigError(t, nopLogger, provider, readBucket, filepath.Join("testdata", "v1beta1", "gen_error1.yaml"))
 	testReadConfigError(t, nopLogger, provider, readBucket, filepath.Join("testdata", "v1beta1", "gen_error2.yaml"))
@@ -422,7 +403,7 @@ func TestReadConfigV1(t *testing.T) {
 	require.Equal(t, successConfig2, config)
 	data, err = os.ReadFile(filepath.Join("testdata", "v1", "gen_success2.json"))
 	require.NoError(t, err)
-	config, err = ReadConfig(ctx, nopLogger, provider, readBucket, ReadConfigWithOverride((string(data))))
+	config, err = ReadConfig(ctx, nopLogger, provider, readBucket, ReadConfigWithOverride(string(data)))
 	require.NoError(t, err)
 	require.Equal(t, successConfig2, config)
 	config, err = ReadConfig(ctx, nopLogger, provider, readBucket, ReadConfigWithOverride(filepath.Join("testdata", "v1", "gen_success3.yaml")))
@@ -430,7 +411,7 @@ func TestReadConfigV1(t *testing.T) {
 	require.Equal(t, successConfig3, config)
 	data, err = os.ReadFile(filepath.Join("testdata", "v1", "gen_success3.yaml"))
 	require.NoError(t, err)
-	config, err = ReadConfig(ctx, nopLogger, provider, readBucket, ReadConfigWithOverride((string(data))))
+	config, err = ReadConfig(ctx, nopLogger, provider, readBucket, ReadConfigWithOverride(string(data)))
 	require.NoError(t, err)
 	require.Equal(t, successConfig3, config)
 	config, err = ReadConfig(ctx, nopLogger, provider, readBucket, ReadConfigWithOverride(filepath.Join("testdata", "v1", "gen_success3.json")))
@@ -454,7 +435,7 @@ func TestReadConfigV1(t *testing.T) {
 	require.Equal(t, successConfig4, config)
 	data, err = os.ReadFile(filepath.Join("testdata", "v1", "gen_success4.yaml"))
 	require.NoError(t, err)
-	config, err = ReadConfig(ctx, nopLogger, provider, readBucket, ReadConfigWithOverride((string(data))))
+	config, err = ReadConfig(ctx, nopLogger, provider, readBucket, ReadConfigWithOverride(string(data)))
 	require.NoError(t, err)
 	require.Equal(t, successConfig4, config)
 	config, err = ReadConfig(ctx, nopLogger, provider, readBucket, ReadConfigWithOverride(filepath.Join("testdata", "v1", "gen_success4.json")))
@@ -478,7 +459,7 @@ func TestReadConfigV1(t *testing.T) {
 	require.Equal(t, successConfig5, config)
 	data, err = os.ReadFile(filepath.Join("testdata", "v1", "gen_success5.yaml"))
 	require.NoError(t, err)
-	config, err = ReadConfig(ctx, nopLogger, provider, readBucket, ReadConfigWithOverride((string(data))))
+	config, err = ReadConfig(ctx, nopLogger, provider, readBucket, ReadConfigWithOverride(string(data)))
 	require.NoError(t, err)
 	require.Equal(t, successConfig5, config)
 	config, err = ReadConfig(ctx, nopLogger, provider, readBucket, ReadConfigWithOverride(filepath.Join("testdata", "v1", "gen_success5.json")))
@@ -502,7 +483,7 @@ func TestReadConfigV1(t *testing.T) {
 	require.Equal(t, successConfig6, config)
 	data, err = os.ReadFile(filepath.Join("testdata", "v1", "gen_success6.yaml"))
 	require.NoError(t, err)
-	config, err = ReadConfig(ctx, nopLogger, provider, readBucket, ReadConfigWithOverride((string(data))))
+	config, err = ReadConfig(ctx, nopLogger, provider, readBucket, ReadConfigWithOverride(string(data)))
 	require.NoError(t, err)
 	require.Equal(t, successConfig6, config)
 	config, err = ReadConfig(ctx, nopLogger, provider, readBucket, ReadConfigWithOverride(filepath.Join("testdata", "v1", "gen_success6.json")))
@@ -526,7 +507,7 @@ func TestReadConfigV1(t *testing.T) {
 	require.Equal(t, successConfig7, config)
 	data, err = os.ReadFile(filepath.Join("testdata", "v1", "gen_success7.yaml"))
 	require.NoError(t, err)
-	config, err = ReadConfig(ctx, nopLogger, provider, readBucket, ReadConfigWithOverride((string(data))))
+	config, err = ReadConfig(ctx, nopLogger, provider, readBucket, ReadConfigWithOverride(string(data)))
 	require.NoError(t, err)
 	require.Equal(t, successConfig7, config)
 	config, err = ReadConfig(ctx, nopLogger, provider, readBucket, ReadConfigWithOverride(filepath.Join("testdata", "v1", "gen_success7.json")))
@@ -552,7 +533,7 @@ func TestReadConfigV1(t *testing.T) {
 	assertConfigsWithEqualRubyPackage(t, successConfig8, config)
 	data, err = os.ReadFile(filepath.Join("testdata", "v1", "gen_success8.yaml"))
 	require.NoError(t, err)
-	config, err = ReadConfig(ctx, nopLogger, provider, readBucket, ReadConfigWithOverride((string(data))))
+	config, err = ReadConfig(ctx, nopLogger, provider, readBucket, ReadConfigWithOverride(string(data)))
 	require.NoError(t, err)
 	assertConfigsWithEqualCsharpnamespace(t, successConfig8, config)
 	assertConfigsWithEqualObjcPrefix(t, successConfig8, config)
@@ -586,7 +567,7 @@ func TestReadConfigV1(t *testing.T) {
 	assertConfigsWithEqualOptimizeFor(t, successConfig9, config)
 	data, err = os.ReadFile(filepath.Join("testdata", "v1", "gen_success9.yaml"))
 	require.NoError(t, err)
-	config, err = ReadConfig(ctx, nopLogger, provider, readBucket, ReadConfigWithOverride((string(data))))
+	config, err = ReadConfig(ctx, nopLogger, provider, readBucket, ReadConfigWithOverride(string(data)))
 	require.NoError(t, err)
 	assertConfigsWithEqualOptimizeFor(t, successConfig9, config)
 	config, err = ReadConfig(ctx, nopLogger, provider, readBucket, ReadConfigWithOverride(filepath.Join("testdata", "v1", "gen_success9.json")))
diff --git a/private/buf/bufgen/testdata/v1beta1/gen_success4_nopath.yaml b/private/buf/bufgen/testdata/v1beta1/gen_success4_nopath.yaml
new file mode 100644
index 0000000000..50621a4b2c
--- /dev/null
+++ b/private/buf/bufgen/testdata/v1beta1/gen_success4_nopath.yaml
@@ -0,0 +1,8 @@
+version: v1beta1
+managed: true
+options:
+  optimize_for: LITE_RUNTIME
+plugins:
+  - name: go
+    out: gen/go
+    strategy: all