Gunk is a modern frontend and syntax for Protocol Buffers.
Quickstart | Installing | Syntax | Configuring | About | Releases
Gunk provides a modern project-based workflow along with a Go-derived
syntax for defining types and services for use with Protocol Buffers.
Gunk is designed to integrate cleanly with existing protoc based
build pipelines, while standardizing workflows in a way that is
familiar/accessible to Go developers.
Create a working directory for a project:
$ mkdir -p ~/src/example && cd ~/src/exampleInstall gunk and place the following Gunk definitions
in example/util.gunk:
package util
// Util is a utility service.
type Util interface {
// Echo returns the passed message.
Echo(Message) Message
}
// Message contains an echo message.
type Message struct {
// Msg is a message from a client.
Msg string `pb:"1"`
}Create the corresponding project configuration in example/.gunkconfig:
[generate go]
[generate js]
import_style=commonjs
binaryThen, generate protocol buffer definitions/code:
$ ls -A
.gunkconfig util.gunk
$ gunk generate
$ ls -A
all.pb.go all_pb.js .gunkconfig util.gunkAs seen above, gunk generated the corresponding Go and JavaScript protobuf
code using the options defined in the .gunkconfig.
A end-to-end example gRPC server implementation, using Gunk definitions is available for review.
Underlying commands executed by gunk can be viewed with the following:
$ gunk generate -x
protoc-gen-go
protoc --js_out=import_style=commonjs,binary:/home/user/example --descriptor_set_in=/dev/stdin all.protoThe gunk command-line tool can be installed via Release, via Homebrew, via Scoop or via Go:
- Download a release for your platform
- Extract the
gunkorgunk.exefile from the.tar.bz2or.zipfile - Move the extracted executable to somewhere on your
$PATH(Linux/macOS) or%PATH%(Windows)
gunk is available in the gunk/gunk tap, and can be installed in
the usual way with the brew command:
# add tap
$ brew tap gunk/gunk
# install gunk
$ brew install gunkgunk can be installed using Scoop:
# install scoop if not already installed
iex (new-object net.webclient).downloadstring('https://get.scoop.sh')
scoop install gunkgunk can be installed in the usual Go fashion:
# install gunk
$ go get -u github.com/gunk/gunkThe gunk command-line tool uses the protoc command-line tool. gunk can be configured
to use protoc at a specified path. If it isn't available, gunk will
download the latest protobuf release to the user's cache,
for use. It's also possible to pin a specific version, see the section on protoc configuration.
Gunk provides an alternate, Go-derived syntax for defining protocol
buffers. As such, Gunk definitions are a subset of the Go
programming language. Additionally, a special +gunk annotation is recognized
by gunk, to allow the declaration of protocol buffer options:
package message
import "github.com/gunk/opt/http"
// Message is a Echo message.
type Message struct {
// Msg holds a message.
Msg string `pb:"1" json:"msg"`
Code int `pb:"2" json:"code"`
}
// Util is a utility service.
type Util interface {
// Echo echoes a message.
//
// +gunk http.Match{
// Method: "POST",
// Path: "/v1/echo",
// Body: "*",
// }
Echo(Message) Message
}Gunk's Go-derived syntax uses the canonical Go scalar types
of the proto3 syntax, defined by the protocol buffer project:
| Proto3 Type | Gunk Type |
|---|---|
double |
float64 |
float |
float32 |
int32 |
int |
int32 |
int32 |
int64 |
int64 |
uint32 |
uint |
uint32 |
uint32 |
uint64 |
uint64 |
bool |
bool |
string |
string |
bytes |
[]byte |
Note: Variable-length scalars will be enabled in the future using a tag parameter.
Gunk's Go-derived syntax uses Go's struct type declarations for declaring
messages, and require a pb:"<field_number>" tag to indicate the field number:
type Message struct {
FieldA string `pb:"1"`
}
type Envelope struct {
Message Message `pb:"1" json:"msg"`
}There are additional tags (for example, the json: tag above), that will be
recognized by gunk format, and passed on to generators, where possible.
Note: When using gunk format, a valid pb:"<field_number>" tag will be automatically
inserted if not declared.
Gunk's Go-derived syntax uses Go's interface syntax for declaring services:
type SearchService interface {
Search(SearchRequest) SearchResponse
}The above is equivalent to the following protobuf syntax:
service SearchService {
rpc Search (SearchRequest) returns (SearchResponse);
}
Gunk's Go-derived syntax uses Go const's for declaring enums:
type MyEnum int
const (
MYENUM MyEnum = iota
MYENUM2
)Note: values can also be fixed numeric values or a calculated value (using
iota).
Gunk's Go-derived syntax uses Go map's for declaring map fields:
type Project struct {
ProjectID string `pb:"1" json:"project_id"`
}
type GetProjectResponse struct {
Projects map[string]Project `pb:"1"`
}Gunk's Go-derived syntax uses Go's slice syntax ([]) for declaring a
repeated field:
type MyMessage struct {
FieldA []string `pb:"1"`
}Gunk's Go-derived syntax uses Go chan syntax for declaring streams:
type MessageService interface {
List(chan Message) chan Message
}The above is equivalent to the following protobuf syntax:
service MessageService {
rpc List(stream Message) returns (stream Message);
}
Protocol buffer options are standard messages (ie, a
struct), and can be attached to any service, message, enum, or other other
type declaration in a Gunk file via the doccomment preceding the type, field,
or service:
// MyOption is an option.
type MyOption struct {
Name string `pb:"1"`
}
// +gunk MyOption {
// Name: "test",
// }
type MyMessage struct {
/* ... */
}Gunk uses a top-level .gunkconfig configuration file for managing the Gunk
protocol definitons for a project:
# Example .gunkconfig for Go, grpc-gateway, Python and JS
[generate go]
out=v1/go
plugins=grpc
[generate]
out=v1/go
command=protoc-gen-grpc-gateway
logtostderr=true
[generate python]
out=v1/python
[generate js]
out=v1/js
import_style=commonjs
binaryWhen gunk is invoked from the command-line, it searches the passed package
spec (or current working directory) for a .gunkconfig file, and walks up the
directory hierarchy until a .gunkconfig is found, or the project's root is
encountered. The project root is defined as the top-most directory containing a
.git subdirectory, or where a go.mod file is located.
The .gunkconfig file format is compatible with Git config syntax,
and in turn is compatible with the INI file format:
[generate]
command=protoc-gen-go
[generate]
out=v1/js
protoc=jsThe path where to check for (or where to download) the protoc binary can be configured.
The version can also be pinned.
-
version- the version of protoc to use. If unspecified, defaults to the latest release available. Otherwise, gunk will either download the specified version, or check that the version ofprotocat the specified path matches what was configured. -
path- the path to check for theprotocbinary. If unspecified, defaults appropriate user cache directory for the user's OS. If no file exists at the path,gunkwill attempt to download protoc.
Each [generate] or [generate <type>] section in a .gunkconfig corresponds
to a invocation of the protoc-gen-<type> tool.
Each name[=value] parameter defined within a [generate] section will be
passed as a parameter to the protoc-gen-<type> tool, with the exception of
the following special parameters that override the behavior of the gunk generate tool:
-
command- overrides theprotoc-gen-*command executable used bygunk generate. The executable must be findable on$PATH(Linux/macOS) or%PATH%(Windows), or may be the full path to the executable. If not defined, thencommandwill beprotoc-gen-<type>, when<type>is the value in[generate <type>]. -
protoc- overrides the<type>value, causinggunk generateto use theprotocvalue in place of<type>. -
out- overrides the output path ofprotoc. If not defined, output will be the same directory as the location of the.gunkfiles.
All other name[=value] pairs specified within the generate section will be
passed as plugin parameters to protoc and the protoc-gen-<type> generators.
The following .gunkconfig:
[generate go]
[generate js]
out=v1/jsis equivalent to:
[generate]
command=protoc-gen-go
[generate]
out=v1/js
protoc=jsGunk provides the +gunk annotation syntax for declaring protobuf
options, and specially recognizes some third-party API
annotations, such as Google HTTP options, including all builtin/standard
protoc options for code generation:
// +gunk java.Package("com.example.message")
// +gunk java.MultipleFiles(true)
package message
import (
"github.com/gunk/opt/http"
"github.com/gunk/opt/file/java"
)
type Util interface {
// +gunk http.Match{
// Method: "POST",
// Path: "/v1/echo",
// Body: "*",
// }
Echo()
}Further documentation on available options can be found at the Gunk options project.
Gunk provides the gunk format command to format .gunk files (akin to gofmt):
$ gunk format /path/to/file.gunk
$ gunk format <pathspec>Gunk provides the gunk convert command that will converting existing .proto
files (or a directory) to the Go-derived Gunk syntax:
$ gunk convert /path/to/file.proto
$ gunk convert /path/to/protobuf/directoryIf your .proto is referencing another .proto from another directory,
you can add import_path in the global section of your .gunkconfig.
If you don't provide import_path it will only search in the root directory.
import_path=relative/path/to/protobuf/directoryThe path to provide is relative from the
.gunkconfiglocation.
Furthermore, the referenced files must contain:
option go_package="path/of/go/package";The resulting .gunk file will contain the import path as defined in go_package:
import (
name "path/of/go/package"
)Gunk is developed by the team at Brankas, and was designed to streamline API design and development.
From the beginning of the company, the Brankas team defined API types and
services in .proto files, leveraging ad-hoc Makefile's, shell scripts, and
other non-standardized mechanisms for generating Protocol Buffer code.
As development exploded in 2017 (and beyond) with continued addition of backend microservices/APIs, more code repositories and projects, and team members, it became necessary to standardize tooling for the organization as well as reduce the cognitive load of developers (who for the most part were working almost exclusively with Go) when declaring gRPC and REST services.
The Gunk name has a cheeky, backronym "Gunk Unified N-terface Kompiler",
however the name was chosen because it was possible to secure the GitHub gunk
project name, was short, concise, and not used by other projects.
Additionally, "gunk" is an apt description for the "gunk" surrounding protocol definition, generation, compilation, and delivery.
Issues, Pull Requests, and other contributions are greatly welcomed and
appreciated! Get started with building and running gunk:
# clone source repository
$ git clone https://github.com/gunk/gunk.git && cd gunk
# force GO111MODULES
$ export GO111MODULE=on
# build and run
$ go build && ./gunkGunk uses Go modules for dependency management, and as such
requires Go 1.11+. Please run go mod tidy before submitting any PRs:
$ export GO111MODULE=on
$ cd gunk && go mod tidy