Repository: uber/prototool (go)

Issue:
The generate descriptor_set functionality does not handle relative paths properly
`prototool version`
```                                                                                                                                        
Version:                 1.4.0-dev
Default protoc version:  3.7.0
Go version:              go1.12
OS/Arch:                 darwin/amd64
```
`prototool generate proto/activity --debug `
```
returning ProtoSets     {"workDirPath": "/Users/cory.t/go/src/github.com/xyz/service-home", "dirPath": "proto/activity", "protoSets": [{"WorkDirPath":"/Users/cory.t/go/src/github.com/xyz/service-home","DirPath":"/Users/cory.t/go/src/github.com/xyz/service-home/proto/activity","DirPathToFiles":{"/Users/cory.t/go/src/github.com/xyz/service-home/proto/activity":[{"Path":"/Users/cory.t/go/src/github.com/xyz/service-home/proto/activity/activity_api.proto","DisplayPath":"proto/activity/activity_api.proto"}]},"Config":{"DirPath":"/Users/cory.t/go/src/github.com/xyz/service-home/proto/activity","ExcludePrefixes":[],"Compile":{"ProtobufVersion":"3.6.1","IncludePaths":["/Users/cory.t/go/src"],"IncludeWellKnownTypes":true,"AllowUnusedImports":false},"Create":{"DirPathToBasePackage":null},"Lint":{"Group":"google","NoDefault":false,"IncludeIDs":[],"ExcludeIDs":[],"IgnoreIDToFilePaths":{},"FileHeader":"","JavaPackagePrefix":"","AllowSuppression":false},"Break":{"IncludeBeta":true,"AllowBetaDeps":true},"Gen":{"GoPluginOptions":{"ImportPath":"github.com/xyz/service-home/","ExtraModifiers":{"google/api/annotations.proto":"google.golang.org/genproto/googleapis/api/annotations","google/api/http.proto":"google.golang.org/genproto/googleapis/api/annotations"}},"Plugins":[{"Name":"descriptor_set","Type":1,"Flags":"","OutputPath":{"RelPath":"../../pkg/activity/api_descriptors/","AbsPath":"/Users/cory.t/go/src/github.com/xyz/service-home/pkg/activity/api_descriptors"},"FileSuffix":"pb","IncludeImports":true,"IncludeSourceInfo":true}]}}}]}
using file      {"file": "proto/activity/activity_api.proto"}
output from protoc --version    {"output": "libprotoc 3.6.1"}
protobuf already downloaded     {"path": "/Users/cory.t/Library/Caches/prototool/Darwin/x86_64/protobuf/3.6.1"}
running protoc  {"command": "/Users/cory.t/Library/Caches/prototool/Darwin/x86_64/protobuf/3.6.1/bin/protoc -I /Users/cory.t/go/src -I /Users/cory.t/Library/Caches/prototool/Darwin/x86_64/protobuf/3.6.1/include --descriptor_set_out=/Users/cory.t/go/src/github.com/xyz/service-home/pkg/activity/api_descriptors/..pb --include_imports --include_source_info /Users/cory.t/go/src/github.com/xyz/service-home/proto/activity/activity_api.proto"}
```

the file that gets written is `--descriptor_set_out=/Users/cory.t/go/src/github.com/xyz/service-home/pkg/activity/api_descriptors/..pb`

here is the generate prototool config:
```
generate:
  go_options:
    import_path: github.com/xyz/service-home/
    # Extra modifiers to include with Mfile=package.
    extra_modifiers:
        google/api/annotations.proto: google.golang.org/genproto/googleapis/api/annotations
        google/api/http.proto: google.golang.org/genproto/googleapis/api/annotations
  # The list of plugins.
  plugins:
    - name: go
      type: go
      flags: plugins=grpc
      output: ../../pkg/activity/gen/
    - name: descriptor_set
      output: ../../pkg/activity/api_descriptors/
      file_suffix: pb
      include_imports: true
      include_source_info: true
```

Pull request description:
Fix issue where generated descriptor set file is in the same directory as the config file
Fixes #389 and adds a test.

Patch:
diff --git a/.gitignore b/.gitignore
index e98f46b..c128c93 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,6 +24,7 @@
 /internal/cmd/testdata/create/version2three/baz.proto
 /internal/cmd/testdata/create/version2four/baz.proto
 /internal/cmd/testdata/create/version2five/baz.proto
+/internal/cmd/testdata/generate/descriptorset/descriptorset.bin
 /main
 /prototool.iml
 /release
diff --git a/BUILD.bazel b/BUILD.bazel
index a6b99d1..93272fc 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -13,6 +13,7 @@ load("@bazel_gazelle//:def.bzl", "gazelle")
 # gazelle:exclude internal/cmd/testdata/format-fix
 # gazelle:exclude internal/cmd/testdata/bar
 # gazelle:exclude internal/cmd/testdata/create
+# gazelle:exclude internal/cmd/testdata/generate
 # gazelle:exclude internal/file/testdata
 # gazelle:exclude internal/reflect/proto
 # gazelle:exclude internal/reflect/testdata
diff --git a/Makefile b/Makefile
index 5ae46ae..4210c12 100644
--- a/Makefile
+++ b/Makefile
@@ -213,7 +213,7 @@ bazelbuild: $(BAZEL)
 	bazel build //...:all
 
 .PHONY: releasegen
-releasegen: internalgen
+releasegen: all
 	docker run \
 		--volume "$(CURDIR):/app" \
 		--workdir "/app" \
diff --git a/etc/bin/releasegen.sh b/etc/bin/releasegen.sh
index c139740..f698004 100644
--- a/etc/bin/releasegen.sh
+++ b/etc/bin/releasegen.sh
@@ -20,29 +20,50 @@ goarch() {
   esac
 }
 
-BASE_DIR="release"
+sha256() {
+  if ! type sha256sum >/dev/null 2>/dev/null; then
+    if ! type shasum >/dev/null 2>/dev/null; then
+      echo "sha256sum and shasum are not installed" >&2
+      return 1
+    else
+      shasum -a 256 "$@"
+    fi
+  else
+    sha256sum "$@"
+  fi
+}
 
+BASE_DIR="release"
 rm -rf "${BASE_DIR}"
+mkdir -p "${BASE_DIR}"
+cd "${BASE_DIR}"
+
 for os in Darwin Linux; do
   for arch in x86_64; do
-    dir="${BASE_DIR}/${os}/${arch}/prototool"
+    dir="${os}/${arch}/prototool"
     tar_context_dir="$(dirname "${dir}")"
     tar_dir="prototool"
+    binary="prototool-${os}-${arch}"
+    tarball="prototool-${os}-${arch}.tar.gz"
     mkdir -p "${dir}/bin"
     mkdir -p "${dir}/etc/bash_completion.d"
     mkdir -p "${dir}/etc/zsh/site-functions"
     mkdir -p "${dir}/share/man/man1"
-    go run internal/cmd/gen-prototool-bash-completion/main.go > "${dir}/etc/bash_completion.d/prototool"
-    go run internal/cmd/gen-prototool-zsh-completion/main.go > "${dir}/etc/zsh/site-functions/_prototool"
-    go run internal/cmd/gen-prototool-manpages/main.go "${dir}/share/man/man1"
+    go run "${DIR}/internal/cmd/gen-prototool-bash-completion/main.go" > "${dir}/etc/bash_completion.d/prototool"
+    go run "${DIR}/internal/cmd/gen-prototool-zsh-completion/main.go" > "${dir}/etc/zsh/site-functions/_prototool"
+    go run "${DIR}/internal/cmd/gen-prototool-manpages/main.go" "${dir}/share/man/man1"
     CGO_ENABLED=0 GOOS=$(goos "${os}") GOARCH=$(goarch "${arch}") \
       go build \
       -a \
       -installsuffix cgo \
       -o "${dir}/bin/prototool" \
-      cmd/prototool/main.go
-    tar -C "${tar_context_dir}" -cvzf "${BASE_DIR}/prototool-${os}-${arch}.tar.gz" "${tar_dir}"
-    cp "${dir}/bin/prototool" "${BASE_DIR}/prototool-${os}-${arch}"
+      "${DIR}/cmd/prototool/main.go"
+    tar -C "${tar_context_dir}" -cvzf "${tarball}" "${tar_dir}"
+    cp "${dir}/bin/prototool" "${binary}"
+    sha256 "${binary}" > "${binary}.sha256sum"
+    sha256 -c "${binary}.sha256sum"
+    sha256 "${tarball}" > "${tarball}.sha256sum"
+    sha256 -c "${tarball}.sha256sum"
   done
-  rm -rf "${BASE_DIR:?/tmp}/${os}"
+  rm -rf "${os}"
 done
diff --git a/internal/protoc/compiler.go b/internal/protoc/compiler.go
index e994fed..13552ad 100644
--- a/internal/protoc/compiler.go
+++ b/internal/protoc/compiler.go
@@ -502,7 +502,15 @@ func getRelOutputFilePath(protoSet *file.ProtoSet, dirPath string, fileSuffix st
 		// this should never happen, but could in a bad case with links
 		return "", fmt.Errorf("could not find relative path for %q to %q, this is a system error, please file a bug at github.com/uber/prototool/issues/new: %v", dirPath, protoSet.Config.DirPath, err)
 	}
-	return filepath.Join(relPath, filepath.Base(relPath)+"."+fileSuffix), nil
+	base := filepath.Base(relPath)
+	if relPath == "" || relPath == "." {
+		if protoSet.Config.DirPath == string(os.PathSeparator) {
+			base = "default"
+		} else {
+			base = filepath.Base(protoSet.Config.DirPath)
+		}
+	}
+	return filepath.Join(relPath, base+"."+fileSuffix), nil
 }
 
 // the return value corresponds to CodeGeneratorRequest.Parameter


Test patch:
diff --git a/internal/cmd/cmd_test.go b/internal/cmd/cmd_test.go
index 85f5331..c38a067 100644
--- a/internal/cmd/cmd_test.go
+++ b/internal/cmd/cmd_test.go
@@ -1579,6 +1579,18 @@ func TestDiffLintGroups(t *testing.T) {
 	)
 }
 
+func TestGenerateDescriptorSetSameDirAsConfigFile(t *testing.T) {
+	// https://github.com/uber/prototool/issues/389
+	generatedFilePath := "testdata/generate/descriptorset/descriptorset.bin"
+	if _, err := os.Stat(generatedFilePath); err == nil {
+		assert.NoError(t, os.Remove(generatedFilePath))
+	}
+	_, exitCode := testDo(t, false, "generate", filepath.Dir(generatedFilePath))
+	assert.Equal(t, 0, exitCode)
+	_, err := os.Stat(generatedFilePath)
+	assert.NoError(t, err)
+}
+
 func assertLinters(t *testing.T, linters []lint.Linter, args ...string) {
 	linterIDs := make([]string, 0, len(linters))
 	for _, linter := range linters {
diff --git a/internal/cmd/testdata/generate/descriptorset/foo.proto b/internal/cmd/testdata/generate/descriptorset/foo.proto
new file mode 100644
index 0000000..ee13e27
--- /dev/null
+++ b/internal/cmd/testdata/generate/descriptorset/foo.proto
@@ -0,0 +1,14 @@
+syntax = "proto3";
+
+package foo.v1;
+
+option csharp_namespace = "Foo.V1";
+option go_package = "foov1";
+option java_multiple_files = true;
+option java_outer_classname = "FooProto";
+option java_package = "com.foo.v1";
+option objc_class_prefix = "FXX";
+option php_namespace = "Foo\\V1";
+
+// A bar.
+message Bar {}
diff --git a/internal/cmd/testdata/generate/descriptorset/prototool.yaml b/internal/cmd/testdata/generate/descriptorset/prototool.yaml
new file mode 100644
index 0000000..1996f6b
--- /dev/null
+++ b/internal/cmd/testdata/generate/descriptorset/prototool.yaml
@@ -0,0 +1,7 @@
+lint:
+  group: uber2
+generate:
+  plugins:
+    - name: descriptor_set
+      file_suffix: bin
+      output: .