diff --git a/.gitignore b/.gitignore index eee4026a7..21a203f78 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ # Temp directories tmp/ dist/ +**/packrd/* +**/*-packr.go diff --git a/README.md b/README.md index 7f87534a3..4f8df7b93 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ hold using this installation method._ ## Schema The schema of GAPIC Showcase API can be found in [schema/google/showcase/v1beta1](schema/google/showcase/v1beta1) -It's dependencies can be found in the [googleapis/api-common-protos](https://github.com/googleapis/api-common-protos) +Its dependencies can be found in the [googleapis/api-common-protos](https://github.com/googleapis/api-common-protos) submodule. ## Quick Start diff --git a/cmd/gapic-showcase/qualifier/acceptance_suite/basic-check/README.txt b/cmd/gapic-showcase/qualifier/acceptance_suite/basic-check/README.txt new file mode 100644 index 000000000..9fd290ee3 --- /dev/null +++ b/cmd/gapic-showcase/qualifier/acceptance_suite/basic-check/README.txt @@ -0,0 +1,14 @@ +This " basic-check" scenario also showcases some features of the +"qualifer" acceptance harness. In particular, it shows how files and +directories can be included at various points in the sandbox hierarchy +by means of include files: + +* basic-check/include.google includes the subdirectory of api-common + protos needed by the echo service + +* basic-check/include.example.just_a_file is just an example showing + how to include just a file instead of a directory + +* basic-chec/foo/bar/include.example.an_arbitrarily_nested_file is + just an example showing how to include a file or directory at any + point in a sandbox directory tree. diff --git a/cmd/gapic-showcase/qualifier/acceptance_suite/basic-check/echo.proto b/cmd/gapic-showcase/qualifier/acceptance_suite/basic-check/echo.proto new file mode 100644 index 000000000..06b82b432 --- /dev/null +++ b/cmd/gapic-showcase/qualifier/acceptance_suite/basic-check/echo.proto @@ -0,0 +1,210 @@ +// Copyright 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +import "google/api/annotations.proto"; +import "google/api/client.proto"; +import "google/api/field_behavior.proto"; +import "google/longrunning/operations.proto"; +import "google/protobuf/duration.proto"; +import "google/protobuf/timestamp.proto"; +import "google/rpc/status.proto"; + +package google.showcase.v1beta1; + +option go_package = "github.com/googleapis/gapic-showcase/server/genproto"; +option java_package = "com.google.showcase.v1beta1"; +option java_multiple_files = true; + +// This service is used showcase the four main types of rpcs - unary, server +// side streaming, client side streaming, and bidirectional streaming. This +// service also exposes methods that explicitly implement server delay, and +// paginated calls. Set the 'showcase-trailer' metadata key on any method +// to have the values echoed in the response trailers. +service Echo { + // This service is meant to only run locally on the port 7469 (keypad digits + // for "show"). + option (google.api.default_host) = "localhost:7469"; + + // This method simply echos the request. This method is showcases unary rpcs. + rpc Echo(EchoRequest) returns (EchoResponse) { + option (google.api.http) = { + post: "/v1beta1/echo:echo" + body: "*" + }; + } + + // This method split the given content into words and will pass each word back + // through the stream. This method showcases server-side streaming rpcs. + rpc Expand(ExpandRequest) returns (stream EchoResponse) { + option (google.api.http) = { + post: "/v1beta1/echo:expand" + body: "*" + }; + // TODO(landrito): change this to be `fields: ["content", "error"]` once + // github.com/dcodeIO/protobuf.js/issues/1094 has been resolved. + option (google.api.method_signature) = "content,error"; + } + + // This method will collect the words given to it. When the stream is closed + // by the client, this method will return the a concatenation of the strings + // passed to it. This method showcases client-side streaming rpcs. + rpc Collect(stream EchoRequest) returns (EchoResponse) { + option (google.api.http) = { + post: "/v1beta1/echo:collect" + body: "*" + }; + } + + // This method, upon receiving a request on the stream, will pass + // the same content back on the stream. This method showcases + // bidirectional streaming rpcs. + rpc Chat(stream EchoRequest) returns (stream EchoResponse); + + // This is similar to the Expand method but instead of returning a stream of + // expanded words, this method returns a paged list of expanded words. + rpc PagedExpand(PagedExpandRequest) returns (PagedExpandResponse) { + option (google.api.http) = { + post: "/v1beta1/echo:pagedExpand" + body: "*" + }; + } + + // This method will wait the requested amount of and then return. + // This method showcases how a client handles a request timing out. + rpc Wait(WaitRequest) returns (google.longrunning.Operation) { + option (google.api.http) = { + post: "/v1beta1/echo:wait" + body: "*" + }; + option (google.longrunning.operation_info) = { + response_type: "WaitResponse" + metadata_type: "WaitMetadata" + }; + } + + // This method will block (wait) for the requested amount of time + // and then return the response or error. + // This method showcases how a client handles delays or retries. + rpc Block(BlockRequest) returns (BlockResponse) { + option (google.api.http) = { + post: "/v1beta1/echo:block" + body: "*" + }; + }; +} + +// The request message used for the Echo, Collect and Chat methods. If content +// is set in this message then the request will succeed. If status is set in +// this message then the status will be returned as an error. +message EchoRequest { + oneof response { + // The content to be echoed by the server. + string content = 1; + + // The error to be thrown by the server. + google.rpc.Status error = 2; + } +} + +// The response message for the Echo methods. +message EchoResponse { + // The content specified in the request. + string content = 1; +} + +// The request message for the Expand method. +message ExpandRequest { + // The content that will be split into words and returned on the stream. + string content = 1; + + // The error that is thrown after all words are sent on the stream. + google.rpc.Status error = 2; +} + +// The request for the PagedExpand method. +message PagedExpandRequest { + // The string to expand. + string content = 1 [(google.api.field_behavior) = REQUIRED]; + + // The amount of words to returned in each page. + int32 page_size = 2; + + // The position of the page to be returned. + string page_token = 3; +} + +// The response for the PagedExpand method. +message PagedExpandResponse { + // The words that were expanded. + repeated EchoResponse responses = 1; + + // The next page token. + string next_page_token = 2; +} + +// The request for Wait method. +message WaitRequest { + oneof end { + // The time that this operation will complete. + google.protobuf.Timestamp end_time = 1; + + // The duration of this operation. + google.protobuf.Duration ttl = 4; + } + + oneof response { + // The error that will be returned by the server. If this code is specified + // to be the OK rpc code, an empty response will be returned. + google.rpc.Status error = 2; + + // The response to be returned on operation completion. + WaitResponse success = 3; + } +} + +// The result of the Wait operation. +message WaitResponse { + // This content of the result. + string content = 1; +} + +// The metadata for Wait operation. +message WaitMetadata { + // The time that this operation will complete. + google.protobuf.Timestamp end_time =1; +} + +// The request for Block method. +message BlockRequest { + // The amount of time to block before returning a response. + google.protobuf.Duration response_delay = 1; + + oneof response { + // The error that will be returned by the server. If this code is specified + // to be the OK rpc code, an empty response will be returned. + google.rpc.Status error = 2; + + // The response to be returned that will signify successful method call. + BlockResponse success = 3; + } +} + +// The response for Block method. +message BlockResponse { + // This content can contain anything, the server will not depend on a value + // here. + string content = 1; +} diff --git a/cmd/gapic-showcase/qualifier/acceptance_suite/basic-check/foo/bar/include.example.nested_included_file b/cmd/gapic-showcase/qualifier/acceptance_suite/basic-check/foo/bar/include.example.nested_included_file new file mode 100644 index 000000000..be3faf98a --- /dev/null +++ b/cmd/gapic-showcase/qualifier/acceptance_suite/basic-check/foo/bar/include.example.nested_included_file @@ -0,0 +1 @@ +api-common-protos/CONTRIBUTING.md diff --git a/cmd/gapic-showcase/qualifier/acceptance_suite/basic-check/include.example.included_file b/cmd/gapic-showcase/qualifier/acceptance_suite/basic-check/include.example.included_file new file mode 100644 index 000000000..332a237b8 --- /dev/null +++ b/cmd/gapic-showcase/qualifier/acceptance_suite/basic-check/include.example.included_file @@ -0,0 +1 @@ +api-common-protos/README.md \ No newline at end of file diff --git a/cmd/gapic-showcase/qualifier/acceptance_suite/basic-check/include.google b/cmd/gapic-showcase/qualifier/acceptance_suite/basic-check/include.google new file mode 100644 index 000000000..e2f99f6b0 --- /dev/null +++ b/cmd/gapic-showcase/qualifier/acceptance_suite/basic-check/include.google @@ -0,0 +1 @@ +api-common-protos/google/ \ No newline at end of file diff --git a/cmd/gapic-showcase/qualifier/acceptance_suite/basic-check/sample.yaml b/cmd/gapic-showcase/qualifier/acceptance_suite/basic-check/sample.yaml new file mode 100644 index 000000000..d1b08fca5 --- /dev/null +++ b/cmd/gapic-showcase/qualifier/acceptance_suite/basic-check/sample.yaml @@ -0,0 +1,31 @@ +--- +type: com.google.api.codegen.SampleConfigProto +schema_version: 1.2.0 +samples: +- service: google.showcase.v1beta1.Echo + rpc: Echo + description: A sample of a simple echo request and response + request: + - field: content + comment: The content to be echoed back + value: a resounding echo...echo...echo + input_parameter: message + response: + - comment: + - "We simply print the response" + - print: + - 'Heard back: "%s"' + - $resp.content +--- +type: test/sample +schema_version: 1 +--- +type: inventory +widget: "$expensive" +--- +elements: + - hydrogen + - oxygen +--- +type: sample/other +tool: my_yaml diff --git a/cmd/gapic-showcase/qualifier/dependencies.go b/cmd/gapic-showcase/qualifier/dependencies.go new file mode 100644 index 000000000..87dd50e56 --- /dev/null +++ b/cmd/gapic-showcase/qualifier/dependencies.go @@ -0,0 +1,167 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package qualifier + +import ( + "fmt" + "log" + "os" + "os/exec" + "strings" + + packr "github.com/gobuffalo/packr/v2" + trace "github.com/google/go-trace" +) + +var ( + acceptanceSuite *packr.Box // check suite and their files: protos, configs, tests + schemaSuite *packr.Box // the schema files that may be needed by various suite checks +) + +const ( + // various types that classify file contents, based on the file extension and/or YAML + // document types. + fileTypeSampleConfig = "com.google.api.codegen.SampleConfigProto" + fileTypeSampleTest = "test/samples" + fileTypeProtobuf = "proto" + fileTypeUnknown = "(UNKNOWN)" + + // each file in a scenario directory is either a scenario file that helps define a + // scenario, or an include file needed to support a scenario (eg commonly used types) + fileTypeScenario = "scenario" + fileTypeInclude = "include" + + // external command to run + cmdSampleTester = "sample-tester" +) + +// getAssets loads the assets needed by the qualifier suite. These are Packr boxes with the files +// obtained from the source tree. +func getAssets() error { + acceptanceSuite = packr.New("acceptance suite", "acceptance_suite") + schemaSuite = packr.New("schema", "../../../schema") + + if len(acceptanceSuite.List()) == 0 || len(schemaSuite.List()) == 0 { + return fmt.Errorf("release error: some of the asset boxes are empty") + } + + traceBox(acceptanceSuite) + traceBox(schemaSuite) + return nil +} + +// checkDependencies checks that any external dependencies (typically programs that the qualifier +// suite calls) are accessible in this invocation. +func checkDependencies() error { + notFound := []string{} + trace.Trace("") + + sampleTesterPath, err := exec.LookPath(cmdSampleTester) + if err != nil { + notFound = append(notFound, cmdSampleTester) + } + + if len(notFound) > 0 { + msg := fmt.Sprintf("could not find dependencies in $PATH: %q", notFound) + log.Printf(msg) + return fmt.Errorf(msg) + } + trace.Trace("found %q: %s", cmdSampleTester, sampleTesterPath) + return nil +} + +func traceBox(box *packr.Box) { + trace.Trace("suite %q has %d entries", box.Name, len(box.List())) +} + +// filesByDir contains a list of all the files under the given directory. Each entry in `Files` +// includes the path components for `Directory`. +type filesByDir struct { + directory string + files []string // filenames are paths relative to `Directory` +} + +// getFilesByDir returns a list of `filesByDir` objects, one per top-level directory in `box`. +func getFilesByDir(box *packr.Box) []*filesByDir { + allDirs := []*filesByDir{} + var currDir *filesByDir + commitDir := func() { + if currDir != nil { + allDirs = append(allDirs, currDir) + currDir = nil + } + } + + // We assume allFiles is returned in a top-down order, so that + // all files under each first level directory appear + // contiguously. + allFiles := box.List() + prevName := "" + for _, file := range allFiles { + name := strings.Split(file, string(os.PathSeparator))[0] + if name != prevName { + commitDir() + currDir = &filesByDir{directory: name} + } + prevName = name + currDir.files = append(currDir.files, file[len(name)+len(string(os.PathSeparator)):]) + } + commitDir() + + return allDirs +} + +// GetMatchingFiles returns the a list of files in `box` matching the +// paths `src`. `src` should end with `os.PathSeparator` iff it refers +// to a directory. +// +// As a convenience, this also returns an additional value, useful +// for copying files within matched directories: +// +// * `newDst`: a copy of `dst` (which is assumed to have no +// trailing separator), with `os.PathSeparator` appended if +// `src` refers to a directory +func GetMatchingFiles(box *packr.Box, dst, src string) (files []string, newDst string, err error) { + trace.Trace("reading %q", src) + + newDst = dst + + // If `src` specifies a single file, match just that. + if !strings.HasSuffix(src, string(os.PathSeparator)) { + if !box.Has(src) { + err = fmt.Errorf("file box %q has no file %q", box.Name, src) + return + } + files = []string{src} + return + } + + // Let the caller replace the directory part of the path with + // the separator included. + newDst = dst + string(os.PathSeparator) + + for _, entry := range box.List() { + if strings.HasPrefix(entry, src) { + files = append(files, entry) + } + } + if len(files) == 0 { + err = fmt.Errorf("file box %q has no files matching %q", box.Name, src) + return + } + + trace.Trace("obtained %d files", len(files)) + return +} diff --git a/cmd/gapic-showcase/qualifier/generator.go b/cmd/gapic-showcase/qualifier/generator.go new file mode 100644 index 000000000..da0aa0201 --- /dev/null +++ b/cmd/gapic-showcase/qualifier/generator.go @@ -0,0 +1,90 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package qualifier + +import ( + "errors" + "fmt" + "os" + "os/exec" + "path" + "strings" + + trace "github.com/google/go-trace" +) + +// Generator has the information needed to run a generator in a given language. The generator +// will be run as a protoc plugin unless `isMonolith` is set, in which case gapic-generator will be +// invoked directly. +type Generator struct { + Language string // the language in which to generate GAPICs and samples + Directory string // the directory in which to find the generator + Options string // any options + isMonolith bool // TODO: implement this functionality and export this field +} + +// Run runs the generator `gen` from `work_dir`, creating it if necessary and obtaining the required +// files from `filesByType`. The generated output is placed in a `generated/` sub-directory. +func (gen *Generator) Run(workDir string, filesByType map[string][]string) ([]byte, *os.ProcessState, error) { + const generationDir = "generated" + + if gen.isMonolith { + return nil, nil, fmt.Errorf("monolith not implemented yet") + } + + if err := os.MkdirAll(path.Join(workDir, generationDir), os.ModePerm); err != nil { + return nil, nil, err + } + + // Construct the various arguments to invoke the generator as a protoc plugin. + + pluginOpt := fmt.Sprintf("--%s_gapic_opt", gen.Language) + opts := []string{} + if len(gen.Options) > 0 { + opts = append(opts, pluginOpt, gen.Options) + } + + sampleConfigs := filesByType[fileTypeSampleConfig] + if len(sampleConfigs) > 0 { + opts = append(opts, pluginOpt, + fmt.Sprintf("samples=%s", strings.Join(sampleConfigs, ",samples="))) + } + + cmdParts := []string{ + "protoc", + fmt.Sprintf("--%s_gapic_out", gen.Language), fmt.Sprintf("./%s", generationDir), + } + cmdParts = append(cmdParts, opts...) + if len(gen.Directory) > 0 { + cmdParts = append(cmdParts, fmt.Sprintf("--plugin=%s/protoc-gen-%s_gapic", gen.Directory, gen.Language)) + + } + cmdParts = append(cmdParts, filesByType[fileTypeProtobuf]...) + + // Execute the command, clear all but internal errors, return. + + trace.Trace("running: %s", strings.Join(cmdParts, " ")) + + cmd := exec.Command(cmdParts[0], cmdParts[1:]...) + cmd.Dir = workDir + output, err := cmd.CombinedOutput() + + var exitError *exec.ExitError + if errors.As(err, &exitError) { + err = nil + trace.Trace("clearing exit error: %v", exitError) + } + return output, cmd.ProcessState, err +} diff --git a/cmd/gapic-showcase/qualifier/prepare-to-qualify b/cmd/gapic-showcase/qualifier/prepare-to-qualify new file mode 100644 index 000000000..b2bb9fc54 --- /dev/null +++ b/cmd/gapic-showcase/qualifier/prepare-to-qualify @@ -0,0 +1,57 @@ +#!/bin/bash +# Copyright 2019 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This script is a tool for developers to invoke to prepare for local +# development. It sets up the environment needed to run +# `gapic-showcase qualify`. + +VENV_NAME=venv/qualify +DATE=$(date +'%Y-%m-%d.%H-%M') + +# Check external dependencies + +verify() { + name=$1 + msg=$2 + which $name || { echo "Please install $name to continue. $2" ; return 2 ; } +} + +verify python3 || return $? +verify protoc "See https://github.com/protocolbuffers/protobuf/releases" || return $? +verify pip || return $? +verify curl || return $? +verify protoc-gen-python_gapic || return $? + + +# Set up environment + +export GAPIC_QUALIFY_DIR=${1:-$(mktemp -d -t qualify.${DATE}.XXXXXX)} +pushd ${GAPIC_QUALIFY_DIR} >& /dev/null + +VENV_CREATE="python3 -m venv $VENV_NAME" + +echo '=== creating venv' && \ + ${VENV_CREATE} || { python3 -m pip install virtualenv && ${VENV_ACTIVATE} ; } && \ + echo "=== activating venv" && \ + . ${VENV_NAME}/bin/activate && \ + echo "=== installing pip" && \ + pip install sample-tester && \ + echo "=== installing showcase" && \ + export PATH=$(pwd):${PATH} + + +popd >& /dev/null +echo -e "\nInstalled in: ${GAPIC_QUALIFY_DIR}" + diff --git a/cmd/gapic-showcase/qualifier/qualify.go b/cmd/gapic-showcase/qualifier/qualify.go new file mode 100644 index 000000000..624708f4f --- /dev/null +++ b/cmd/gapic-showcase/qualifier/qualify.go @@ -0,0 +1,90 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package qualifier + +import ( + "fmt" + "os" + + trace "github.com/google/go-trace" +) + +// Settings contains the settings for a run of the qualification suite. +type Settings struct { + Generator + ShowcasePort int + Timestamp string // for tracing and diagnostics + Verbose bool +} + +// Run runs the qualification suite using the values in `settings`. +func Run(settings *Settings) { + // TODO: Return an error rather than exiting. We can return an error type ErrorCode that + // wraps the current errors and includes the appropriate return code, and change main() so + // that if the error it gets is of type ErrorCode, it exits with that code. + const ( + retCodeSuccess = iota + retCodeInternalError + retCodeFailedDependencies + retCodeUsageError + retCodeScenarioFailure + ) + + if err := getAssets(); err != nil { + os.Exit(retCodeInternalError) + } + + if err := checkDependencies(); err != nil { + os.Exit(retCodeFailedDependencies) + } + + allScenarios := getTestScenarios(settings) + + success := true + for _, scenario := range allScenarios { + if err := scenario.Run(); err != nil { + os.Exit(retCodeInternalError) + } + status := "SUCCESS" + if !scenario.Success() { + success = false + status = "FAILURE" + } + fmt.Printf("scenario %q: %s %s\n", scenario.name, status, scenario.sandbox) + } + if !success { + os.Exit(retCodeScenarioFailure) + } +} + +// getTestScenarios returns a list of Scenario as found in the acceptanceSuite. +func getTestScenarios(settings *Settings) []*Scenario { + scenarios := []*Scenario{} + configDirs := getFilesByDir(acceptanceSuite) + for _, config := range configDirs { + scenarios = append(scenarios, &Scenario{ + name: config.directory, + timestamp: settings.Timestamp, + showcasePort: settings.ShowcasePort, + generator: &settings.Generator, + files: config.files, + fileBox: acceptanceSuite, + schemaBox: schemaSuite, + }) + + } + trace.Trace("adding scenarios %#v", scenarios) + return scenarios +} diff --git a/cmd/gapic-showcase/qualifier/scenario.go b/cmd/gapic-showcase/qualifier/scenario.go new file mode 100644 index 000000000..0762cc583 --- /dev/null +++ b/cmd/gapic-showcase/qualifier/scenario.go @@ -0,0 +1,341 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package qualifier + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + + packr "github.com/gobuffalo/packr/v2" + trace "github.com/google/go-trace" + yaml "gopkg.in/yaml.v2" +) + +// Constants pertaining to the syntax of include files. Include files are akin to symbolic links: +// they reference content that will be copied at run-time from the indicated location to the include +// file's location under the indicated names. For example, an include file called "include.boren" +// with contents "api-common-protos/google/" will be replaced at run-time with a directory called +// "boren" whose contents will be a copy of everything under "api-common-protos/google/" +// +// We need some mechanism for including common protos because the protos under test may reference +// them. This particular scheme is also useful in that it allows us to explicitly pass to the +// generator only the protos explicitly listed in the acceptance check, rather than also having to +// include all the protos that are transitively included. +const ( + // In a qualifier suite directory, any file that begins with this prefix is considered an + // include file. The rest of the filename after the prefix is the destination file or + // directory name to which the files referenced by the content of the include file will be + // copied. + includeFilePrefix = "include." + + // In the contents of an include file, this symbol will be treated as the directory + // separator. At run time, it will be replaced by the appropriate os.PathSeparator. + includeFileDirectorySeparator = '/' +) + +type Scenario struct { + // Every element in `files` corresponds to a file in `fileBox` under + // a directory called `name`, which is NOT part of the string + // element itself. + name string // name of the scenario + timestamp string // timestamp for debug and trace messages + files []string // a flat list of files in the scenario + fileBox *packr.Box // the files explicitly specified in the scenario + schemaBox *packr.Box // additional files that may be referenced by include files in a scenario + sandbox string // the sandbox created to run this scenario + filesByType map[string][]string // scenario files partitioned by file type + + generator *Generator + showcasePort int // the port of the running showcase server against which samples will run + + generationOutput []byte // combined output of the generation process + generationProcessState *os.ProcessState // info on the completed generation process + generationPassed bool // whether all generation checks passed + sampleTestsPassed bool // whether all sample tests specified passed +} + +// Success returns true iff all the expectations for this scenario were met. +func (scenario *Scenario) Success() bool { + return scenario.generationPassed && // the generation checks passed and... + (!scenario.generationProcessState.Success() || // ...either generation failed, or it succeeded with ... + (len(scenario.filesByType[fileTypeSampleTest]) == 0 || scenario.sampleTestsPassed)) // all sample tests, if any, passing. +} + +// Run runs this particular acceptance scenario by creating a sandbox in which it generates +// libraries and samples and then tests that the generation and any sample tests performed as +// expected. +func (scenario *Scenario) Run() error { + if err := scenario.classifyConfigs(); err != nil { + return fmt.Errorf("could not classify config files for scenario %q: %w", scenario.name, err) + } + + if err := scenario.createSandbox(); err != nil { + return fmt.Errorf("could not create sandbox for scenario %q: %w", scenario.name, err) + } + trace.Trace("created sandbox: %q", scenario.sandbox) + + if err := scenario.Generate(); err != nil { + return err + } + scenario.CheckGeneration() + scenario.RunTests() + return nil +} + +// Generate generates the client libraries and samples for this scenario. +func (scenario *Scenario) Generate() error { + var err error + scenario.generationOutput, scenario.generationProcessState, err = scenario.generator.Run(scenario.sandbox, scenario.filesByType) + trace.Trace("run error: %v", err) + trace.Trace("run scenario.generationOutput: %s", scenario.generationOutput) + return err +} + +// CheckGeneration verifies that generation matched configured expectations when it succeeded or +// failed. +func (scenario *Scenario) CheckGeneration() { + // TODO: Fill in in order to handle expected, configured failures + trace.Trace("CheckGeneration") + scenario.generationPassed = scenario.generationProcessState.Success() +} + +// RunTests runs tests on the generated samples. +func (scenario *Scenario) RunTests() { + // TODO: Fill in + trace.Trace("RunTests (TODO)") +} + +// sandboxPath returns `relativePath` re-rooted at `scenario.sandbox`, +// which must have been previously set. +func (scenario *Scenario) sandboxPath(relativePath string) string { + return filepath.Join(scenario.sandbox, relativePath) +} + +// fileBoxPath returns relativePath re-rooted at scenario.name, so that it references an object in +// `scenario.fileBox`. +func (scenario *Scenario) fileBoxPath(relativePath string) string { + return filepath.Join(scenario.name, relativePath) +} + +// fromFileBox returns the file named by `relativePath` from `scenario.fileBox`. +func (scenario *Scenario) fromFileBox(relativePath string) ([]byte, error) { + return scenario.fileBox.Find(scenario.fileBoxPath(relativePath)) +} + +// classifyConfigs classifies all the files in the scenario, storing the results in +// `scenario.filesByType`. Each file is classified as either an "include" file or a "scenario" +// file. Scenario files are also classified by the type of data they contain. Thus, "scenario" files +// have at least two labels (ie multiple entries in `scenario.filesByType`). +func (scenario *Scenario) classifyConfigs() (err error) { + scenario.filesByType = make(map[string][]string) + // TODO: process `include.*` files first + for _, file := range scenario.files { + types, err := scenario.getFileTypes(file) + if err != nil { + return err + } + for _, fType := range types { + similarFiles := scenario.filesByType[fType] + scenario.filesByType[fType] = append(similarFiles, file) + } + trace.Trace("%s: type %q", file, types) + } + return nil +} + +// getFileTypes gets the various type labels for `file`. The type labels are either the +// singleton list {"include"}, or a list {"scenario", ...} that includes the various types of data +// included in that file. +func (scenario *Scenario) getFileTypes(file string) (types []string, err error) { + + // Identify include files in the scenario. Include files always begin with `includeFilePrefix` + pathParts := strings.Split(file, string(os.PathSeparator)) + if len(pathParts) > 0 && strings.HasPrefix(pathParts[len(pathParts)-1], includeFilePrefix) { + return []string{"include"}, nil + } + + extension := filepath.Ext(file) + switch extension { + case ".proto": + types = []string{"proto"} + case ".yaml", ".yml": + trace.Trace("reading %s", file) + content, err := scenario.fromFileBox(file) + if err != nil { + err := fmt.Errorf("error reading %q: %w", file, err) + trace.Trace(err) + return types, err + } + types, err = yamlDocTypes(content) + if err != nil { + return types, err + } + } + types = append(types, "scenario") + return types, err +} + +// yamlDocTypes returns a list of the document types encountered in the (possibly multi-document) +// YAML payload `content`. +func yamlDocTypes(content []byte) (types []string, err error) { + type docSchema struct { + Type string + } + + decoder := yaml.NewDecoder(bytes.NewReader(content)) + for { + doc := &docSchema{Type: fileTypeUnknown} + err = decoder.Decode(doc) + if err == io.EOF { + break + } + if err != nil { + err = fmt.Errorf("error decoding file: %w", err) + trace.Trace(err) + return + } + types = append(types, doc.Type) + } + return types, nil +} + +// createSandbox creates a sandbox directory within which all the generation and checks for this +// scenario will be run. +func (scenario *Scenario) createSandbox() (err error) { + if scenario.sandbox, err = ioutil.TempDir("", fmt.Sprintf("showcase-qualify.%s.%s.", scenario.timestamp, scenario.name)); err != nil { + err := fmt.Errorf("could not create sandbox: %w", err) + trace.Trace(err) + return err + } + + err = scenario.copyFiles(scenario.filesByType[fileTypeScenario]) + if err != nil { + err = fmt.Errorf("could not copy scenario files: %w", err) + trace.Trace(err) + return err + } + + if err = scenario.getIncludes(scenario.filesByType[fileTypeInclude]); err != nil { + err = fmt.Errorf("could not copy included schema files: %w", err) + trace.Trace(err) + return err + } + + trace.Trace("created sandbox") + return nil +} + +// getIncludes processes include files (whose names start with `includeFilePrefix`) by replacing +// them with the file or directory referenced in the file's contents. The only currently supported +// type of reference is to either an entry in `scenario.schemaBox`, or a directory that resolves to +// one or more entries in `scenario.schemaBox`. If an include file does not resolve to any entries +// in `scenario.schemaBox`, this function returns an error. +// +// If there's need in the future, we could potentially grow to support fetching files or directories +// from public repositories on the web. +func (scenario *Scenario) getIncludes(includeFiles []string) error { + for _, inclusion := range includeFiles { + + // The following guards against includeFilePrefix being present elsewhere in + // inclusion. Otherwise, we could simply use + // + // `dstPath := strings.Replace(inclusion, includeFilePrefix, "")` + prefixStartIdx := strings.LastIndex(inclusion, includeFilePrefix) + if prefixStartIdx < 0 { + msg := fmt.Sprintf("logic error: did not find prefix %q in %q", includeFilePrefix, inclusion) + trace.Trace(msg) + return fmt.Errorf(msg) + } + prefixEndIdx := prefixStartIdx + len(includeFilePrefix) + dstPath := inclusion[:prefixStartIdx] + inclusion[prefixEndIdx:] + + content, err := scenario.fromFileBox(inclusion) + if err != nil { + err = fmt.Errorf("could not read %q: %w", inclusion, err) + trace.Trace(err) + return err + } + + srcPath := strings.TrimSpace(string(content)) + srcPath = strings.ReplaceAll(srcPath, string(inclusion), string(os.PathSeparator)) + files, replacePath, err := GetMatchingFiles(scenario.schemaBox, dstPath, srcPath) + if err != nil { + err = fmt.Errorf("could not process %q: %w", inclusion, err) + trace.Trace(err) + return err + } + + scenario.copyFilesTo(files, scenario.schemaBox.Find, srcPath, replacePath) + } + return nil +} + +// copyFiles copies the files listed in `files` from `scenario.fromFileBox` to `scenario.sandbox`. +func (scenario *Scenario) copyFiles(files []string) error { + return scenario.copyFilesTo(files, scenario.fromFileBox, "", "") +} + +// copyFilesTo copies the files listed in `files` from the source in `fromBox` to +// `scenario.sandbox`. In so doing, it replaces a leading `prefix` in each filename with +// `newPrefix`. +func (scenario *Scenario) copyFilesTo(files []string, fromBox func(string) ([]byte, error), prefix, newPrefix string) (err error) { + const filePermissions = 0555 + replace := len(prefix) > 0 + + trace.Trace("prefix:%q newPrefix:%q len(files):%d", prefix, newPrefix, len(files)) + + for _, srcFile := range files { + renamedFile := srcFile + if replace { + if !strings.HasPrefix(renamedFile, prefix) { + err := fmt.Errorf("%q does not begin with %q", srcFile, prefix) + trace.Trace(err) + return err + } + renamedFile = strings.Replace(renamedFile, prefix, newPrefix, 1) + } + + renamedDir := filepath.Dir(renamedFile) + + dstDir := scenario.sandboxPath(renamedDir) + if _, err = os.Stat(dstDir); os.IsNotExist(err) { + if err = os.MkdirAll(dstDir, os.ModePerm); err != nil { + err = fmt.Errorf("could not make directory %q: %w", dstDir, err) + trace.Trace(err) + return err + } + } + + var contents []byte + if contents, err = fromBox(srcFile); err != nil { + err = fmt.Errorf("could not find file %q: %w", srcFile, err) + trace.Trace(err) + return err + } + + dstFile := scenario.sandboxPath(renamedFile) + if err = ioutil.WriteFile(dstFile, contents, filePermissions); err != nil { + err = fmt.Errorf("could not write file %q: %w", dstFile, err) + trace.Trace(err) + return err + } + } + return nil +} diff --git a/cmd/gapic-showcase/qualify.go b/cmd/gapic-showcase/qualify.go new file mode 100644 index 000000000..04e6d2d20 --- /dev/null +++ b/cmd/gapic-showcase/qualify.go @@ -0,0 +1,74 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "strconv" + "time" + + trace "github.com/google/go-trace" + "github.com/spf13/cobra" + + "github.com/googleapis/gapic-showcase/cmd/gapic-showcase/qualifier" +) + +func init() { + var timestamp string + timestamp = time.Now().Format("20060102.150405") + trace.Trace("timestamp = %q", timestamp) + + settings := &qualifier.Settings{ + Timestamp: timestamp, + Verbose: Verbose, + ShowcasePort: 7469, + } + qualifyCmd := &cobra.Command{ + Use: "qualify [language]", + Short: "Tests a provided GAPIC generator against an acceptance suite", + Long: `qualify will execute a suite of acceptance checks against the GAPIC generator for the specified language. +This confirms that the generator behaves and emits artifacts as specified in generator requirements under + a variety of inputs for various types of RPCs. Each acceptance check typically attempts to generate client +libraries and corresponding standalone samples for the Showcase "Echo" service. The generator is invoked + as a protoc plugin; its location may be specified via --dir, and additional generator options + that are needed to successfully generate the GAPIC for this API may be specified via --options,`, + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + // TODO: Consider moving this to a more central place for debugging all of + // showcase. + trace.On(false) // set to true for debugging + + servers := RunShowcase(strconv.Itoa(settings.ShowcasePort), "") + defer servers.Shutdown() + + settings.Language = args[0] + trace.Trace("settings: %v", settings) + qualifier.Run(settings) + }, + } + rootCmd.AddCommand(qualifyCmd) + qualifyCmd.Flags().StringVarP( + &settings.Directory, + "dir", + "d", + "", + "The directory in which to find the protoc plugin implementing the given GAPIC generator") + qualifyCmd.Flags().StringVarP( + &settings.Options, + "options", + "o", + "", + "The options to pass to the generator in order to generate a GAPIC for the Showcase \"Echo\" service") + +} diff --git a/cmd/gapic-showcase/run.go b/cmd/gapic-showcase/run.go index c8452ab9e..019bec266 100644 --- a/cmd/gapic-showcase/run.go +++ b/cmd/gapic-showcase/run.go @@ -18,6 +18,7 @@ import ( "log" "net" "strings" + "sync" "github.com/googleapis/gapic-showcase/server" pb "github.com/googleapis/gapic-showcase/server/genproto" @@ -37,49 +38,7 @@ func init() { Use: "run", Short: "Runs the showcase server", Run: func(cmd *cobra.Command, args []string) { - // Ensure port is of the right form. - if !strings.HasPrefix(port, ":") { - port = ":" + port - } - - // Start listening. - lis, err := net.Listen("tcp", port) - if err != nil { - log.Fatalf("Showcase failed to listen on port '%s': %v", port, err) - } - stdLog.Printf("Showcase listening on port: %s", port) - - // Setup Server. - logger := &loggerObserver{} - observerRegistry := server.ShowcaseObserverRegistry() - observerRegistry.RegisterUnaryObserver(logger) - observerRegistry.RegisterStreamRequestObserver(logger) - observerRegistry.RegisterStreamResponseObserver(logger) - - opts := []grpc.ServerOption{ - grpc.StreamInterceptor(observerRegistry.StreamInterceptor), - grpc.UnaryInterceptor(observerRegistry.UnaryInterceptor), - } - s := grpc.NewServer(opts...) - defer s.GracefulStop() - - // Register Services to the server. - pb.RegisterEchoServer(s, services.NewEchoServer()) - identityServer := services.NewIdentityServer() - pb.RegisterIdentityServer(s, identityServer) - messagingServer := services.NewMessagingServer(identityServer) - pb.RegisterMessagingServer(s, messagingServer) - operationsServer := services.NewOperationsServer(messagingServer) - pb.RegisterTestingServer(s, services.NewTestingServer(observerRegistry)) - lropb.RegisterOperationsServer(s, operationsServer) - - fb := fallback.NewServer(fallbackPort, "localhost"+port) - fb.StartBackground() - defer fb.Shutdown() - - // Register reflection service on gRPC server. - reflection.Register(s) - s.Serve(lis) + RunShowcase(port, fallbackPort).Wait() }, } rootCmd.AddCommand(runCmd) @@ -96,3 +55,86 @@ func init() { ":1337", "The port that the fallback-proxy will be served on.") } + +// RunShowcase sets up and starts the showcase and fallback servers and returns pointers to +// them. They can be shutdown by showcaseServers.Shutdown() or showcaseServers.Wait(). +func RunShowcase(port string, fallbackPort string) (showcaseServers *ShowcaseServers) { + // Ensure port is of the right form. + if !strings.HasPrefix(port, ":") { + port = ":" + port + } + + // Start listening. + lis, err := net.Listen("tcp", port) + if err != nil { + log.Fatalf("Showcase failed to listen on port '%s': %v", port, err) + } + stdLog.Printf("Showcase listening on port: %s", port) + + // Setup Server. + logger := &loggerObserver{} + observerRegistry := server.ShowcaseObserverRegistry() + observerRegistry.RegisterUnaryObserver(logger) + observerRegistry.RegisterStreamRequestObserver(logger) + observerRegistry.RegisterStreamResponseObserver(logger) + + opts := []grpc.ServerOption{ + grpc.StreamInterceptor(observerRegistry.StreamInterceptor), + grpc.UnaryInterceptor(observerRegistry.UnaryInterceptor), + } + s := grpc.NewServer(opts...) + + // Register Services to the server. + pb.RegisterEchoServer(s, services.NewEchoServer()) + identityServer := services.NewIdentityServer() + pb.RegisterIdentityServer(s, identityServer) + messagingServer := services.NewMessagingServer(identityServer) + pb.RegisterMessagingServer(s, messagingServer) + operationsServer := services.NewOperationsServer(messagingServer) + pb.RegisterTestingServer(s, services.NewTestingServer(observerRegistry)) + lropb.RegisterOperationsServer(s, operationsServer) + + var fb *fallback.FallbackServer + if len(fallbackPort) > 0 { + fb = fallback.NewServer(fallbackPort, "localhost"+port) + fb.StartBackground() + } + + // Register reflection service on gRPC server. + reflection.Register(s) + + var wait sync.WaitGroup + wait.Add(1) + go func() { + s.Serve(lis) + wait.Done() + }() + return &ShowcaseServers{s: s, fb: fb, wait: &wait} +} + +// ShowcaseServers encapsulates information on running showcase servers, allowing for them to be +// shutdown immediately or when they stop serving. +type ShowcaseServers struct { + s *grpc.Server + fb *fallback.FallbackServer + wait *sync.WaitGroup +} + +// Shutdown will immediately start a graceful shutdown of the servers. +func (srv *ShowcaseServers) Shutdown() { + if srv.fb != nil { + srv.fb.Shutdown() // unfortunately, this always results in an en error log + } + if srv.s != nil { + srv.s.GracefulStop() + } +} + +// Wait will wait until the servers stop serving and then call Shutdown() to shut them down +// gracefully. +func (srv *ShowcaseServers) Wait() { + if srv.wait != nil { + srv.wait.Wait() + } + srv.Shutdown() +} diff --git a/go.mod b/go.mod index 077d74964..2c6db6eea 100644 --- a/go.mod +++ b/go.mod @@ -2,15 +2,24 @@ module github.com/googleapis/gapic-showcase require ( cloud.google.com/go v0.47.0 + github.com/gobuffalo/packr/v2 v2.7.1 github.com/golang/protobuf v1.3.2 + github.com/google/go-trace v0.0.0-20180203010202-d282251f7009 github.com/googleapis/gax-go/v2 v2.0.5 github.com/googleapis/grpc-fallback-go v0.1.3 + github.com/hashicorp/go-version v1.2.0 // indirect + github.com/karrick/godirwalk v1.12.0 // indirect + github.com/mitchellh/gox v1.0.1 // indirect + github.com/rogpeppe/go-internal v1.5.0 // indirect github.com/spf13/cobra v0.0.5 + github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/viper v1.4.0 + golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 // indirect golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 google.golang.org/api v0.11.0 google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03 google.golang.org/grpc v1.24.0 + gopkg.in/yaml.v2 v2.2.2 ) go 1.13 diff --git a/go.sum b/go.sum index 126777816..682161e87 100644 --- a/go.sum +++ b/go.sum @@ -33,6 +33,7 @@ github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= @@ -45,6 +46,18 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/envy v1.7.1 h1:OQl5ys5MBea7OGCdvPbBJWRgnhC/fGona6QKfvFeau8= +github.com/gobuffalo/envy v1.7.1/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w= +github.com/gobuffalo/logger v1.0.1 h1:ZEgyRGgAm4ZAhAO45YXMs5Fp+bzGLESFewzAVBMKuTg= +github.com/gobuffalo/logger v1.0.1/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs= +github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4= +github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q= +github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg= +github.com/gobuffalo/packr/v2 v2.7.0 h1:snQei95nVOAtgY8rMiccTIPPYUXkuQws34FM/9/+UVg= +github.com/gobuffalo/packr/v2 v2.7.0/go.mod h1:I+ICQFYFNPqTT7CUw7/RfMhluz683BO8FLH0wZgh/pQ= +github.com/gobuffalo/packr/v2 v2.7.1 h1:n3CIW5T17T8v4GGK5sWXLVWJhCz7b5aNLSxW6gYim4o= +github.com/gobuffalo/packr/v2 v2.7.1/go.mod h1:qYEvAazPaVxy7Y7KR0W8qYEE+RymX74kETFqjFoFlOc= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= @@ -64,6 +77,8 @@ github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-trace v0.0.0-20180203010202-d282251f7009 h1:fzuC1Phituau4YOSDoX9ZPi9DnnuReeXoONKMOMDojI= +github.com/google/go-trace v0.0.0-20180203010202-d282251f7009/go.mod h1:Z8aRH4v1BFRrLDTZF2jr96KpzD7RHBRMAzVnnKhgKzM= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -80,6 +95,10 @@ github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoA github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/go-version v1.0.0 h1:21MVWPKDphxa7ineQQTrCU5brh7OuVVAzGOCnnCPtE8= +github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= @@ -88,12 +107,19 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/karrick/godirwalk v1.11.3 h1:ZrtYOzzHRzItdU1MvkK3CLlhC4m3YTWFgGyiBuSCQSY= +github.com/karrick/godirwalk v1.11.3/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= +github.com/karrick/godirwalk v1.12.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -104,6 +130,10 @@ github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDe github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/gox v1.0.1 h1:x0jD3dcHk9a9xPSDN6YEL4xL6Qz0dvNYm8yZqui5chI= +github.com/mitchellh/gox v1.0.1/go.mod h1:ED6BioOGXMswlXa2zxfh/xdd5QhwYliBFn9V18Ap4z4= +github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -123,9 +153,17 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.4.0 h1:LUa41nrWTQNGhzdsZ5lTnkwbNjj6rXTdazA1cSdjkOY= +github.com/rogpeppe/go-internal v1.4.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.5.0 h1:Usqs0/lDK/NqTkvrmKSwA/3XkZAs7ZAW/eLeQ2MVBTw= +github.com/rogpeppe/go-internal v1.5.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= @@ -138,13 +176,18 @@ github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= @@ -162,6 +205,10 @@ golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4 h1:ydJNl0ENAG67pFbB+9tfhiL2pYqLhfoaZFw/cjLhY4A= +golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -203,6 +250,8 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -212,11 +261,15 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191010194322-b09406accb47 h1:/XfQ9z7ib8eEJX2hdgFTZJ/ntt0swNk5oYBziWeTCvY= +golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -240,6 +293,8 @@ golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191010171213-8abd42400456 h1:LR16zMCx87X52rsLOtnByklL2K/xWUKAo1Nm7AA4HA0= golang.org/x/tools v0.0.0-20191010171213-8abd42400456/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= diff --git a/schema/google/showcase/v1beta1/echo.proto b/schema/google/showcase/v1beta1/echo.proto index 6e9d78a37..06b82b432 100644 --- a/schema/google/showcase/v1beta1/echo.proto +++ b/schema/google/showcase/v1beta1/echo.proto @@ -68,9 +68,9 @@ service Echo { }; } - // This method, upon receiving a request on the stream, the same content will - // be passed back on the stream. This method showcases bidirectional - // streaming rpcs. + // This method, upon receiving a request on the stream, will pass + // the same content back on the stream. This method showcases + // bidirectional streaming rpcs. rpc Chat(stream EchoRequest) returns (stream EchoResponse); // This is similar to the Expand method but instead of returning a stream of diff --git a/util/cmd/compile_protos/main.go b/util/cmd/compile_protos/main.go index a44579621..3308c8891 100644 --- a/util/cmd/compile_protos/main.go +++ b/util/cmd/compile_protos/main.go @@ -20,7 +20,7 @@ import ( // This script regenerates all of the generated source code for the Showcase // API including the generated messages, gRPC services, go gapic clients, -// and the generated CLI. This script must be ran from the root directory +// and the generated CLI. This script must be run from the root directory // of the gapic-showcase repository. // // This script should be used whenever any changes are made to any of diff --git a/util/cmd/release/main.go b/util/cmd/release/main.go index 3ff99a617..0c7010494 100644 --- a/util/cmd/release/main.go +++ b/util/cmd/release/main.go @@ -24,12 +24,12 @@ import ( "github.com/googleapis/gapic-showcase/util" ) -// This script is ran in CI when a new version tag is pushed to master. This script -// places the compiled proto descriptor set, a tarball of showcase-protos alongside it's +// This script is run in CI when a new version tag is pushed to master. This script +// places the compiled proto descriptor set, a tarball of showcase-protos alongside its // dependencies, and the compiled executables of the gapic-showcase cli tool inside the // directory "dist" // -// This script must be ran from the root directory of the gapic-showcase repository. +// This script must be run from the root directory of the gapic-showcase repository. // // Usage: go run ./util/cmd/release func main() { @@ -103,10 +103,15 @@ func main() { } util.Execute(append(command, files...)...) - // Get cross compiler - // Mousetrap is a windows dependency that is not implicitly got since - // we only get the linux dependencies. - util.Execute("go", "get", "github.com/mitchellh/gox", "github.com/inconshreveable/mousetrap") + // Get Packr to generate the boxes with local files before compiling. The caller needs to + // make sure the installation directory is in the $PATH. + util.Execute("go", "get", "github.com/gobuffalo/packr/v2/packr2") + util.Execute("packr2") + + // Get cross compiler. Mousetrap is a windows dependency that is not implicitly got since we + // only get the linux dependencies. The caller needs to make sure the installation + // directory is in the $PATH. + util.Execute("go", "get", "-u", "github.com/mitchellh/gox", "github.com/inconshreveable/mousetrap") // Compile binaries stagingDir := filepath.Join("tmp", "binaries") @@ -138,4 +143,8 @@ func main() { filepath.Dir(files[0]), filepath.Base(files[0])) } + + // Remove the generated Packr files from the source tree to avoid confusion. + util.Execute("packr2", "clean") + } diff --git a/util/compile_protos.go b/util/compile_protos.go index 83b43f47b..992eaf428 100644 --- a/util/compile_protos.go +++ b/util/compile_protos.go @@ -25,7 +25,7 @@ import ( // CompileProtos regenerates all of the generated source code for the Showcase // API including the generated messages, gRPC services, go gapic clients, -// and the generated CLI. This must be ran from the root directory +// and the generated CLI. This must be run from the root directory // of the gapic-showcase repository. func CompileProtos(version string) { // Check if protoc is installed. diff --git a/util/execute.go b/util/execute.go index bbf660925..fcf076803 100644 --- a/util/execute.go +++ b/util/execute.go @@ -22,6 +22,6 @@ import ( // Execute runs the given strings as a command. func Execute(args ...string) { if output, err := exec.Command(args[0], args[1:]...).CombinedOutput(); err != nil { - log.Fatalf("%s", output) + log.Fatalf("%s running %s", output, args) } }