Repository: googleapis/gapic-generator-go
Pull request #1313 (2023-04-20 20:02:30)

fix(internal/gengapic): write snippet output to cloud.google.com/go
* Write snippets at the top level of the google-cloud-go namespace.
* With the following change to **each entry**  in `deep-copy-regex` in `.OwlBot.yaml`, both client files and snippets are copied to the correct destinations in google-cloud-go. (`deep-remove-regex` will also need to be updated to remove deleted snippets.)

```diff
-  - source: /google/cloud/asset/v1/cloud.google.com/go/asset/apiv1
-    dest: /asset/apiv1
+  - source: /google/cloud/asset/v1/cloud.google.com/go/
+    dest: /
```

<details>
<summary>Results from `bazelisk build //google/cloud/asset/v1:gapi-cloud-asset-v1-go`</summary>
<pre>
chrisdsmith@chrisdsmith:~/oss/googleapis$ tar -tzf bazel-bin/google/cloud/asset/v1/gapi-cloud-asset-v1-go.tar.gz
./cloud.google.com/
./cloud.google.com/go/
./cloud.google.com/go/asset/
./cloud.google.com/go/asset/apiv1/
./cloud.google.com/go/asset/apiv1/asset_client_example_test.go
./cloud.google.com/go/asset/apiv1/assetpb/
./cloud.google.com/go/asset/apiv1/assetpb/assets.pb.go
./cloud.google.com/go/asset/apiv1/assetpb/asset_service.pb.go
./cloud.google.com/go/asset/apiv1/doc.go
./cloud.google.com/go/asset/apiv1/asset_client.go
./cloud.google.com/go/internal/
./cloud.google.com/go/internal/generated/
./cloud.google.com/go/internal/generated/snippets/
./cloud.google.com/go/internal/generated/snippets/asset/
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/snippet_metadata.google.cloud.asset.v1.json
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/AnalyzeIamPolicyLongrunning/
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/AnalyzeIamPolicyLongrunning/main.go
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/AnalyzeOrgPolicyGovernedContainers/
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/AnalyzeOrgPolicyGovernedContainers/main.go
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/SearchAllIamPolicies/
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/SearchAllIamPolicies/main.go
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/AnalyzeIamPolicy/
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/AnalyzeIamPolicy/main.go
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/ListAssets/
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/ListAssets/main.go
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/ListSavedQueries/
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/ListSavedQueries/main.go
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/CreateFeed/
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/CreateFeed/main.go
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/DeleteSavedQuery/
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/DeleteSavedQuery/main.go
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/BatchGetEffectiveIamPolicies/
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/BatchGetEffectiveIamPolicies/main.go
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/QueryAssets/
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/QueryAssets/main.go
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/DeleteFeed/
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/DeleteFeed/main.go
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/GetSavedQuery/
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/GetSavedQuery/main.go
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/GetFeed/
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/GetFeed/main.go
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/GetOperation/
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/GetOperation/main.go
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/UpdateSavedQuery/
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/UpdateSavedQuery/main.go
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/ListFeeds/
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/ListFeeds/main.go
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/AnalyzeOrgPolicyGovernedAssets/
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/AnalyzeOrgPolicyGovernedAssets/main.go
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/UpdateFeed/
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/UpdateFeed/main.go
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/BatchGetAssetsHistory/
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/BatchGetAssetsHistory/main.go
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/AnalyzeOrgPolicies/
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/AnalyzeOrgPolicies/main.go
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/ExportAssets/
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/ExportAssets/main.go
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/CreateSavedQuery/
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/CreateSavedQuery/main.go
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/SearchAllResources/
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/SearchAllResources/main.go
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/AnalyzeMove/
./cloud.google.com/go/internal/generated/snippets/asset/apiv1/Client/AnalyzeMove/main.go
</pre>
</details>


<details>
<summary>Results from `docker run --rm --user $(id -u):$(id -g)   -v $GOOGLE_CLOUD_GO:/repo    -v $GOOGLEAPIS/bazel-bin:/bazel-bin   gcr.io/cloud-devrel-public-resources/owlbot-cli:latest   copy-bazel-bin   --config-file=.github/.OwlBot.yaml   --source-dir /bazel-bin   --dest /repo`</summary>
<pre>
chrisdsmith@chrisdsmith:~/oss/google-cloud-go$ git status | grep asset/apiv1/
	modified:   asset/apiv1/asset_client.go
	modified:   asset/apiv1/assetpb/asset_service.pb.go
	modified:   asset/apiv1/assetpb/assets.pb.go
	modified:   asset/apiv1/doc.go
	modified:   internal/generated/snippets/asset/apiv1/Client/AnalyzeIamPolicy/main.go
	modified:   internal/generated/snippets/asset/apiv1/Client/AnalyzeIamPolicyLongrunning/main.go
	modified:   internal/generated/snippets/asset/apiv1/Client/AnalyzeMove/main.go
	modified:   internal/generated/snippets/asset/apiv1/Client/AnalyzeOrgPolicies/main.go
	modified:   internal/generated/snippets/asset/apiv1/Client/AnalyzeOrgPolicyGovernedAssets/main.go
	modified:   internal/generated/snippets/asset/apiv1/Client/AnalyzeOrgPolicyGovernedContainers/main.go
	modified:   internal/generated/snippets/asset/apiv1/Client/BatchGetAssetsHistory/main.go
	modified:   internal/generated/snippets/asset/apiv1/Client/BatchGetEffectiveIamPolicies/main.go
	modified:   internal/generated/snippets/asset/apiv1/Client/CreateFeed/main.go
	modified:   internal/generated/snippets/asset/apiv1/Client/CreateSavedQuery/main.go
	modified:   internal/generated/snippets/asset/apiv1/Client/DeleteFeed/main.go
	modified:   internal/generated/snippets/asset/apiv1/Client/DeleteSavedQuery/main.go
	modified:   internal/generated/snippets/asset/apiv1/Client/ExportAssets/main.go
	modified:   internal/generated/snippets/asset/apiv1/Client/GetFeed/main.go
	modified:   internal/generated/snippets/asset/apiv1/Client/GetOperation/main.go
	modified:   internal/generated/snippets/asset/apiv1/Client/GetSavedQuery/main.go
	modified:   internal/generated/snippets/asset/apiv1/Client/ListAssets/main.go
	modified:   internal/generated/snippets/asset/apiv1/Client/ListFeeds/main.go
	modified:   internal/generated/snippets/asset/apiv1/Client/ListSavedQueries/main.go
	modified:   internal/generated/snippets/asset/apiv1/Client/QueryAssets/main.go
	modified:   internal/generated/snippets/asset/apiv1/Client/SearchAllIamPolicies/main.go
	modified:   internal/generated/snippets/asset/apiv1/Client/SearchAllResources/main.go
	modified:   internal/generated/snippets/asset/apiv1/Client/UpdateFeed/main.go
	modified:   internal/generated/snippets/asset/apiv1/Client/UpdateSavedQuery/main.go
	modified:   internal/generated/snippets/asset/apiv1/snippet_metadata.google.cloud.asset.v1.json
</pre>
</details>

quartzmo: Draft, pending manual testing with OwlBot `copy-bazel-bin`.

Patch:
diff --git a/internal/gengapic/snippets.go b/internal/gengapic/snippets.go
index 280481f..7415020 100644
--- a/internal/gengapic/snippets.go
+++ b/internal/gengapic/snippets.go
@@ -17,6 +17,7 @@ package gengapic
 import (
 	"fmt"
 	"path/filepath"
+	"strings"
 
 	"github.com/golang/protobuf/protoc-gen-go/descriptor"
 	plugin "github.com/golang/protobuf/protoc-gen-go/plugin"
@@ -94,8 +95,7 @@ func (g *generator) genAndCommitSnippets(s *descriptor.ServiceDescriptorProto) e
 		f := g.descInfo.ParentFile[m]
 		// Get the original proto service for the method (different from `s` only for mixins).
 		methodServ := (g.descInfo.ParentElement[m]).(*descriptor.ServiceDescriptorProto)
-		lineCount := g.commit(filepath.Join(g.opts.outDir, "internal",
-			"snippets", clientName, m.GetName(), "main.go"), "main")
+		lineCount := g.commit(filepath.Join(g.snippetsOutDir(), clientName, m.GetName(), "main.go"), "main")
 		g.snippetMetadata.AddMethod(s.GetName(), m.GetName(), f.GetPackage(), methodServ.GetName(), lineCount-1)
 	}
 	return nil
@@ -130,11 +130,20 @@ func (g *generator) genAndCommitSnippetMetadata(protoPkg string) error {
 	if err != nil {
 		return err
 	}
-	file := filepath.Join(g.opts.outDir, "internal", "snippets",
-		fmt.Sprintf("snippet_metadata.%s.json", protoPkg))
+	file := filepath.Join(g.snippetsOutDir(), fmt.Sprintf("snippet_metadata.%s.json", protoPkg))
 	g.resp.File = append(g.resp.File, &plugin.CodeGeneratorResponse_File{
 		Name:    proto.String(file),
 		Content: proto.String(string(json[:])),
 	})
 	return nil
 }
+
+func (g *generator) snippetsOutDir() string {
+	if strings.Contains(g.opts.pkgPath, "cloud.google.com/go/") {
+		// Write snippet metadata at the top level of the google-cloud-go namespace, not at the client package.
+		// This matches the destination directory structure in google-cloud-go.
+		pkg := strings.TrimPrefix(g.opts.pkgPath, "cloud.google.com/go/")
+		return filepath.Join("cloud.google.com/go", "internal", "generated", "snippets", filepath.FromSlash(pkg))
+	}
+	return filepath.Join(g.opts.outDir, "internal", "snippets")
+}


Test patch:
diff --git a/internal/gengapic/example_test.go b/internal/gengapic/example_test.go
index 60eff34..05a48c8 100644
--- a/internal/gengapic/example_test.go
+++ b/internal/gengapic/example_test.go
@@ -350,7 +350,7 @@ func TestGenSnippetFile(t *testing.T) {
 		if err != nil {
 			t.Fatal(err)
 		}
-		g.commit(filepath.Join(g.opts.outDir, "temp", "snippets", "main.go"), "main")
+		g.commit(filepath.Join("cloud.google.com/go", "internal", "generated", "snippets", "bigquery", "main.go"), "main")
 		if diff := cmp.Diff(g.imports, tst.imports); diff != "" {
 			t.Errorf("TestExample(%s): imports got(-),want(+):\n%s", tst.tstName, diff)
 		}
diff --git a/internal/gengapic/snippets_test.go b/internal/gengapic/snippets_test.go
index c1975cc..d39009d 100644
--- a/internal/gengapic/snippets_test.go
+++ b/internal/gengapic/snippets_test.go
@@ -27,6 +27,34 @@ import (
 	"google.golang.org/protobuf/proto"
 )
 
+func TestSnippetsOutDir(t *testing.T) {
+	for _, tst := range []struct {
+		opts options
+		want string
+	}{
+		{
+			opts: options{
+				outDir:  "cloud.google.com/go/video/stitcher/apiv1",
+				pkgPath: "cloud.google.com/go/video/stitcher/apiv1",
+			},
+			want: "cloud.google.com/go/internal/generated/snippets/video/stitcher/apiv1",
+		},
+		{
+			opts: options{
+				outDir:  "example.com/my/package",
+				pkgPath: "example.com/my/package",
+			},
+			want: "example.com/my/package/internal/snippets",
+		},
+	} {
+		var g generator
+		g.opts = &tst.opts
+		if s := g.snippetsOutDir(); s != tst.want {
+			t.Errorf("TestGenAndCommitSnippets(g.opts.pkgPath = %s): got %s, want %s", g.opts.pkgPath, s, tst.want)
+		}
+	}
+}
+
 func TestGenAndCommitSnippets(t *testing.T) {
 	inputType := &descriptor.DescriptorProto{
 		Name: proto.String("InputType"),