Repository: goadesign/goa (go)

Issue:
v3: Debug middleware for http does not implement Hijacker interface
The default debug middleware used when `--debug` is active does not implement the http.Hijacker interface to work with streaming endpoints using websockets which leads to an `Internal Server Error 500` on those endpoints.

Simple design:
```
package design

import . "goa.design/goa/v3/dsl"

var _ = API("api", func() {
	Server("apisvr", func() {
		Host("default", func() {
			URI("http://0.0.0.0:8000")
			URI("grpc://0.0.0.0:8080")
		})
	})
})

var _ = Service("ws", func() {
	HTTP(func() {
		Path("/")
	})

	Method("Retrieve", func() {
		Payload(func() {
			Field(1, "id", String, "Payload required to avoid issue #2242")
		})

		StreamingResult(String)

		HTTP(func() {
			GET("/{id}")
			Response(StatusOK)
		})
	})
})
```
```
// Retrieve implements Retrieve.
func (s *wssrvc) Retrieve(ctx context.Context, p *ws.RetrievePayload, stream ws.RetrieveServerStream) (err error) {
	s.logger.Println(stream.Send("test"))
	return stream.Close()
}
```
Using with `--debug` gives:
```
[api] 11:27:12 HTTP "Retrieve" mounted on GET /{id}
[api] 11:27:12 HTTP server listening on "0.0.0.0:8000"
[api] 11:27:13  id=1prCzFmU req=GET /test from=127.0.0.1
[api] 11:27:13 websocket: response does not implement http.Hijacker
> [1prCzFmU] GET /test
> [1prCzFmU] Connection: Upgrade
> [1prCzFmU] Sec-Websocket-Extensions: permessage-deflate; client_max_window_bits
> [1prCzFmU] Sec-Websocket-Key: XUU617jy94qxuy3TUy0KTA==
> [1prCzFmU] Sec-Websocket-Version: 13
> [1prCzFmU] Upgrade: websocket
< [1prCzFmU] Internal Server Error
< [1prCzFmU] Content-Type: text/plain; charset=utf-8
< [1prCzFmU] Sec-Websocket-Version: 13
< [1prCzFmU] X-Content-Type-Options: nosniff
[1prCzFmU] Internal Server Error
[1prCzFmU] 

[api] 11:27:13  id=1prCzFmU status=500 bytes=22 time=238.644µs
```

Pull request description:
Hijack function to support http.Hijacker in debug middleware
This fixes #2243 by adding a Hijack function, same as in xray to debug middleware to support http.Hijacker interface on streaming endpoints.

Patch:
diff --git a/.github/stale.yml b/.github/stale.yml
new file mode 100644
index 00000000..c9714211
--- /dev/null
+++ b/.github/stale.yml
@@ -0,0 +1,18 @@
+# Number of days of inactivity before an issue becomes stale
+daysUntilStale: 60
+# Number of days of inactivity before a stale issue is closed
+daysUntilClose: 7
+# Issues with these labels will never be considered stale
+exemptLabels:
+  - pinned
+  - security
+  - bug
+# Label to use when marking an issue as stale
+staleLabel: wontfix
+# Comment to post when marking an issue as stale. Set to `false` to disable
+markComment: >
+  This issue has been automatically marked as stale because it has not had
+  recent activity. It will be closed if no further activity occurs. Thank you
+  for your contributions.
+# Comment to post when closing a stale issue. Set to `false` to disable
+closeComment: false
diff --git a/.travis.yml b/.travis.yml
index 43a6c3fe..29fa900e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,7 @@
 language: go
 dist: xenial
 go:
-- 1.12.x
+- 1.13.x
 install:
 - export PATH=${PATH}:${HOME}/gopath/bin
 script:
diff --git a/Makefile b/Makefile
index fc6f13ef..f95855e4 100644
--- a/Makefile
+++ b/Makefile
@@ -28,15 +28,13 @@ endif
 DEPEND=\
 	golang.org/x/lint/golint \
 	golang.org/x/tools/cmd/goimports \
-	github.com/cheggaaa/pb \
-	github.com/hashicorp/go-getter \
 	github.com/golang/protobuf/protoc-gen-go \
 	github.com/golang/protobuf/proto \
 	honnef.co/go/tools/cmd/staticcheck
 
 all: lint test
 
-travis: depend all test-examples test-plugins
+travis: depend all #test-examples test-plugins
 
 # Install protoc
 PROTOC_VERSION=3.6.1
@@ -56,12 +54,11 @@ PROTOC_EXEC="$(PROTOC)\bin\protoc.exe"
 endif
 depend:
 	@go get -v $(DEPEND)
-	@go install github.com/hashicorp/go-getter/cmd/go-getter && \
+	@env GO111MODULE=off go get github.com/hashicorp/go-getter/cmd/go-getter && \
 		go-getter https://github.com/google/protobuf/releases/download/v$(PROTOC_VERSION)/$(PROTOC).zip $(PROTOC) && \
 		cp $(PROTOC_EXEC) $(GOBIN) && \
 		rm -r $(PROTOC) && \
 		echo "`protoc --version`"
-	@go install github.com/golang/protobuf/protoc-gen-go
 	@go get -t -v ./...
 
 lint:
@@ -87,7 +84,7 @@ test-examples:
 	fi
 	@cd $(EXAMPLES_DIR) && git checkout $(GOA_BRANCH) || echo "Using master branch in examples repo" && \
 	make -k travis || (echo "Tests in examples repo (https://github.com/goadesign/examples) failed" \
-                  "due to changes in goa repo (branch: $(GOA_BRANCH))!" \
+                  "due to changes in Goa repo (branch: $(GOA_BRANCH))!" \
                   "Create a branch with name '$(GOA_BRANCH)' in the examples repo and fix these errors." && exit 1)
 
 test-plugins:
diff --git a/README.md b/README.md
index 24184dad..cb893d7b 100644
--- a/README.md
+++ b/README.md
@@ -108,7 +108,7 @@ without having to make changes.
 Releases are tagged with the corresponding version number. There is also a
 branch for each major version (`v1`, `v2` and `v3`).
 
-Current Release: `v3.0.4`
+Current Release: `v3.0.6`
 
 ## Teaser
 
diff --git a/RELEASE.md b/RELEASE.md
index a515a257..06939ab7 100644
--- a/RELEASE.md
+++ b/RELEASE.md
@@ -5,8 +5,12 @@ This document is intended to help Goa maintainers release new versions of Goa.
 ## Release Procedure
 
 1. Update `pkg/version.go` and `README.md` to reflect the new version.
-2. Generate and push the Goa examples from the `goadesign/examples` repo.
-3. Generate and push the plugin examples from the `goadesign/plugins` repo.
-4. Create git tags in the Goa repo for both the `v2` and `v3` branches.
+2. Create git tags in the Goa repo for both the `v2` and `v3` branches.
+3. Update `go.mod` in the examples repo `master` branch.
+4. Generate the Goa examples from the examples repo `master` and `v2` branches.
+5. Push the examples repo `master` and `v2` branches.
+6. Update the plugins repo `v3` branch `go.mod` file.
+6. Generate the plugin examples from the plugins repo `v3` and `v2` branches.
+5. Create git tags for plugin repo (both `v2` and `v3`).
 5. Push the tags.
 6. Write and publish blog to goa.design.
diff --git a/cmd/goa/main.go b/cmd/goa/main.go
index 9a8d2ec4..fcd2d0cd 100644
--- a/cmd/goa/main.go
+++ b/cmd/goa/main.go
@@ -24,7 +24,7 @@ func main() {
 
 		switch os.Args[1] {
 		case "version":
-			fmt.Println("goa version " + goa.Version())
+			fmt.Println("Goa version " + goa.Version())
 			os.Exit(0)
 		case "gen", "example":
 			if len(os.Args) == 2 {
diff --git a/codegen/example/example_cli.go b/codegen/example/example_cli.go
index e86428a9..308ecf22 100644
--- a/codegen/example/example_cli.go
+++ b/codegen/example/example_cli.go
@@ -132,7 +132,7 @@ const (
 						}
 					}
 					if !{{ .VarName }}Seen {
-						fmt.Fprintf(os.Stderr, "invalid value for URL '{{ .Name }}' variable: %q (valid values: {{ join .Values "," }})", *{{ .VarName }}F)
+						fmt.Fprintf(os.Stderr, "invalid value for URL '{{ .Name }}' variable: %q (valid values: {{ join .Values "," }})\n", *{{ .VarName }}F)
 						os.Exit(1)
 					}
 				{{- end }}
@@ -140,7 +140,7 @@ const (
 			{{- end }}
 		{{- end }}
 			default:
-				fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: {{ join .Server.AvailableHosts "|" }})", *hostF)
+				fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: {{ join .Server.AvailableHosts "|" }})\n", *hostF)
 				os.Exit(1)
 			}
 		}
@@ -155,7 +155,7 @@ const (
 	{
 		u, err := url.Parse(addr)
 		if err != nil {
-			fmt.Fprintf(os.Stderr, "invalid URL %#v: %s", addr, err)
+			fmt.Fprintf(os.Stderr, "invalid URL %#v: %s\n", addr, err)
 			os.Exit(1)
 		}
 		scheme = u.Scheme
@@ -176,7 +176,7 @@ const (
 			endpoint, payload, err = do{{ toUpper $t.Name }}(scheme, host, timeout, debug)
 	{{- end }}
 		default:
-			fmt.Fprintf(os.Stderr, "invalid scheme: %q (valid schemes: {{ join .Server.Schemes "|" }})", scheme)
+			fmt.Fprintf(os.Stderr, "invalid scheme: %q (valid schemes: {{ join .Server.Schemes "|" }})\n", scheme)
 			os.Exit(1)
 		}
 	}
diff --git a/codegen/example/example_server.go b/codegen/example/example_server.go
index e47ab193..4463b436 100644
--- a/codegen/example/example_server.go
+++ b/codegen/example/example_server.go
@@ -255,7 +255,7 @@ func main() {
 						}
 					}
 					if !{{ .VarName }}Seen {
-						fmt.Fprintf(os.Stderr, "invalid value for URL '{{ .Name }}' variable: %q (valid values: {{ join .Values "," }})", *{{ .VarName }}F)
+						fmt.Fprintf(os.Stderr, "invalid value for URL '{{ .Name }}' variable: %q (valid values: {{ join .Values "," }})\n", *{{ .VarName }}F)
 						os.Exit(1)
 					}
 				{{- end }}
@@ -263,7 +263,7 @@ func main() {
 			{{- end }}
 			u, err := url.Parse(addr)
 			if err != nil {
-				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s", addr, err)
+				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s\n", addr, err)
 				os.Exit(1)
 			}
 			if *secureF {
@@ -284,7 +284,7 @@ func main() {
 	{{ end }}
 {{- end }}
 	default:
-		fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: {{ join .Server.AvailableHosts "|" }})", *hostF)
+		fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: {{ join .Server.AvailableHosts "|" }})\n", *hostF)
 	}
 `
 
diff --git a/codegen/funcs.go b/codegen/funcs.go
index 7d6b8cc2..173a53ad 100644
--- a/codegen/funcs.go
+++ b/codegen/funcs.go
@@ -148,10 +148,18 @@ func CamelCase(name string, firstUpper bool, acronym bool) string {
 }
 
 // SnakeCase produces the snake_case version of the given CamelCase string.
+// News    => news
+// OldNews => old_news
+// CNNNews => cnn_news
 func SnakeCase(name string) string {
+	// Special handling for single "words" starting with multiple upper case letters
 	for u, l := range toLower {
 		name = strings.Replace(name, u, l, -1)
 	}
+
+	// Special handling for dashes to convert them into underscores
+	name = strings.Replace(name, "-", "_", -1)
+
 	var b bytes.Buffer
 	ln := len(name)
 	if ln == 0 {
diff --git a/codegen/generator/generate.go b/codegen/generator/generate.go
index 01e7a073..6fc44a7a 100644
--- a/codegen/generator/generate.go
+++ b/codegen/generator/generate.go
@@ -1,6 +1,7 @@
 package generator
 
 import (
+	"io/ioutil"
 	"os"
 	"path/filepath"
 	"sort"
@@ -33,7 +34,18 @@ func Generate(dir, cmd string) ([]string, error) {
 		if err := os.MkdirAll(path, 0777); err != nil {
 			return nil, err
 		}
-		pkgs, err := packages.Load(nil, path)
+
+		// We create a temporary Go file to make sure the directory is a valid Go package
+		dummy, err := ioutil.TempFile(path, "temp.*.go")
+		if err != nil {
+			return nil, err
+		}
+		defer os.Remove(dummy.Name())
+		if err = ioutil.WriteFile(dummy.Name(), []byte("package gen"), 0644); err != nil {
+			return nil, err
+		}
+
+		pkgs, err := packages.Load(&packages.Config{Mode: packages.NeedName}, path)
 		if err != nil {
 			return nil, err
 		}
diff --git a/codegen/service/service_data.go b/codegen/service/service_data.go
index 94bb33aa..fa19cd7b 100644
--- a/codegen/service/service_data.go
+++ b/codegen/service/service_data.go
@@ -763,7 +763,7 @@ func buildMethodData(m *expr.MethodExpr, svcPkgName string, service *expr.Servic
 	if m.IsStreaming() {
 		svrStream = &StreamData{
 			Interface:      vname + "ServerStream",
-			VarName:        m.Name + "ServerStream",
+			VarName:        scope.Unique(codegen.Goify(m.Name, true), "ServerStream"),
 			EndpointStruct: vname + "EndpointInput",
 			Kind:           m.Stream,
 			SendName:       "Send",
@@ -774,7 +774,7 @@ func buildMethodData(m *expr.MethodExpr, svcPkgName string, service *expr.Servic
 		}
 		cliStream = &StreamData{
 			Interface:    vname + "ClientStream",
-			VarName:      m.Name + "ClientStream",
+			VarName:      scope.Unique(codegen.Goify(m.Name, true), "ClientStream"),
 			Kind:         m.Stream,
 			RecvName:     "Recv",
 			RecvDesc:     fmt.Sprintf("Recv reads instances of %q from the stream.", rname),
diff --git a/http/codegen/example_server.go b/http/codegen/example_server.go
index ea25fb2e..c403754c 100644
--- a/http/codegen/example_server.go
+++ b/http/codegen/example_server.go
@@ -206,7 +206,7 @@ func handleHTTPServer(ctx context.Context, u *url.URL{{ range $.Services }}{{ if
 	// Provide the transport specific request decoder and response encoder.
 	// The goa http package has built-in support for JSON, XML and gob.
 	// Other encodings can be used by providing the corresponding functions,
-	// see goa.design/encoding.
+	// see goa.design/implement/encoding.
 	var (
 		dec = goahttp.RequestDecoder
 		enc = goahttp.ResponseEncoder
diff --git a/http/encoding.go b/http/encoding.go
index b964ec6a..8d05cb52 100644
--- a/http/encoding.go
+++ b/http/encoding.go
@@ -294,7 +294,7 @@ func (e *textDecoder) Decode(v interface{}) error {
 	case *string:
 		*c = string(b)
 	case *[]byte:
-		*c = []byte(string(b))
+		*c = b
 	default:
 		err = fmt.Errorf("can't decode %s to %T", e.ct, c)
 	}
diff --git a/http/middleware/debug.go b/http/middleware/debug.go
index de32ff61..53208f68 100644
--- a/http/middleware/debug.go
+++ b/http/middleware/debug.go
@@ -1,12 +1,14 @@
 package middleware
 
 import (
+	"bufio"
 	"bytes"
 	"crypto/rand"
 	"encoding/base64"
 	"fmt"
 	"io"
 	"io/ioutil"
+	"net"
 	"net/http"
 	"sort"
 	"strings"
@@ -116,6 +118,14 @@ func (r *responseDupper) WriteHeader(s int) {
 	r.ResponseWriter.WriteHeader(s)
 }
 
+// Hijack supports the http.Hijacker interface.
+func (r *responseDupper) Hijack() (net.Conn, *bufio.ReadWriter, error) {
+	if hijacker, ok := r.ResponseWriter.(http.Hijacker); ok {
+		return hijacker.Hijack()
+	}
+	return nil, nil, fmt.Errorf("debug middleware: inner ResponseWriter cannot be hijacked: %T", r.ResponseWriter)
+}
+
 // shortID produces a " unique" 6 bytes long string.
 // Do not use as a reliable way to get unique IDs, instead use for things like logging.
 func shortID() string {
diff --git a/pkg/version.go b/pkg/version.go
index 07b95f8a..fc0228e9 100644
--- a/pkg/version.go
+++ b/pkg/version.go
@@ -12,7 +12,7 @@ const (
 	// Minor version number
 	Minor = 0
 	// Build number
-	Build = 4
+	Build = 6
 	// Suffix - set to empty string in release tag commits.
 	Suffix = ""
 )


Test patch:
diff --git a/codegen/example/testdata/example_cli_code.go b/codegen/example/testdata/example_cli_code.go
index c4699011..b32a18c6 100644
--- a/codegen/example/testdata/example_cli_code.go
+++ b/codegen/example/testdata/example_cli_code.go
@@ -24,7 +24,7 @@ const (
 			case "localhost":
 				addr = "http://localhost:80"
 			default:
-				fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: localhost)", *hostF)
+				fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: localhost)\n", *hostF)
 				os.Exit(1)
 			}
 		}
@@ -39,7 +39,7 @@ const (
 	{
 		u, err := url.Parse(addr)
 		if err != nil {
-			fmt.Fprintf(os.Stderr, "invalid URL %#v: %s", addr, err)
+			fmt.Fprintf(os.Stderr, "invalid URL %#v: %s\n", addr, err)
 			os.Exit(1)
 		}
 		scheme = u.Scheme
@@ -57,7 +57,7 @@ const (
 		case "grpc", "grpcs":
 			endpoint, payload, err = doGRPC(scheme, host, timeout, debug)
 		default:
-			fmt.Fprintf(os.Stderr, "invalid scheme: %q (valid schemes: grpc|http)", scheme)
+			fmt.Fprintf(os.Stderr, "invalid scheme: %q (valid schemes: grpc|http)\n", scheme)
 			os.Exit(1)
 		}
 	}
@@ -134,7 +134,7 @@ func indent(s string) string {
 			case "dev":
 				addr = "http://example:8090"
 			default:
-				fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: dev)", *hostF)
+				fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: dev)\n", *hostF)
 				os.Exit(1)
 			}
 		}
@@ -149,7 +149,7 @@ func indent(s string) string {
 	{
 		u, err := url.Parse(addr)
 		if err != nil {
-			fmt.Fprintf(os.Stderr, "invalid URL %#v: %s", addr, err)
+			fmt.Fprintf(os.Stderr, "invalid URL %#v: %s\n", addr, err)
 			os.Exit(1)
 		}
 		scheme = u.Scheme
@@ -167,7 +167,7 @@ func indent(s string) string {
 		case "grpc", "grpcs":
 			endpoint, payload, err = doGRPC(scheme, host, timeout, debug)
 		default:
-			fmt.Fprintf(os.Stderr, "invalid scheme: %q (valid schemes: grpc|http|https)", scheme)
+			fmt.Fprintf(os.Stderr, "invalid scheme: %q (valid schemes: grpc|http|https)\n", scheme)
 			os.Exit(1)
 		}
 	}
@@ -262,7 +262,7 @@ func indent(s string) string {
 				addr = strings.Replace(addr, "{float64}", *float64_F, -1)
 				addr = strings.Replace(addr, "{bool}", *bool_F, -1)
 			default:
-				fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: dev)", *hostF)
+				fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: dev)\n", *hostF)
 				os.Exit(1)
 			}
 		}
@@ -277,7 +277,7 @@ func indent(s string) string {
 	{
 		u, err := url.Parse(addr)
 		if err != nil {
-			fmt.Fprintf(os.Stderr, "invalid URL %#v: %s", addr, err)
+			fmt.Fprintf(os.Stderr, "invalid URL %#v: %s\n", addr, err)
 			os.Exit(1)
 		}
 		scheme = u.Scheme
@@ -293,7 +293,7 @@ func indent(s string) string {
 		case "http", "https":
 			endpoint, payload, err = doHTTP(scheme, host, timeout, debug)
 		default:
-			fmt.Fprintf(os.Stderr, "invalid scheme: %q (valid schemes: http|https)", scheme)
+			fmt.Fprintf(os.Stderr, "invalid scheme: %q (valid schemes: http|https)\n", scheme)
 			os.Exit(1)
 		}
 	}
@@ -381,7 +381,7 @@ func indent(s string) string {
 			case "stage":
 				addr = "https://example"
 			default:
-				fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: dev|stage)", *hostF)
+				fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: dev|stage)\n", *hostF)
 				os.Exit(1)
 			}
 		}
@@ -396,7 +396,7 @@ func indent(s string) string {
 	{
 		u, err := url.Parse(addr)
 		if err != nil {
-			fmt.Fprintf(os.Stderr, "invalid URL %#v: %s", addr, err)
+			fmt.Fprintf(os.Stderr, "invalid URL %#v: %s\n", addr, err)
 			os.Exit(1)
 		}
 		scheme = u.Scheme
@@ -412,7 +412,7 @@ func indent(s string) string {
 		case "http", "https":
 			endpoint, payload, err = doHTTP(scheme, host, timeout, debug)
 		default:
-			fmt.Fprintf(os.Stderr, "invalid scheme: %q (valid schemes: http|https)", scheme)
+			fmt.Fprintf(os.Stderr, "invalid scheme: %q (valid schemes: http|https)\n", scheme)
 			os.Exit(1)
 		}
 	}
@@ -501,7 +501,7 @@ func indent(s string) string {
 					}
 				}
 				if !versionSeen {
-					fmt.Fprintf(os.Stderr, "invalid value for URL 'version' variable: %q (valid values: v1,v2)", *versionF)
+					fmt.Fprintf(os.Stderr, "invalid value for URL 'version' variable: %q (valid values: v1,v2)\n", *versionF)
 					os.Exit(1)
 				}
 				addr = strings.Replace(addr, "{version}", *versionF, -1)
@@ -510,7 +510,7 @@ func indent(s string) string {
 				addr = strings.Replace(addr, "{domain}", *domainF, -1)
 				addr = strings.Replace(addr, "{port}", *portF, -1)
 			default:
-				fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: dev|stage)", *hostF)
+				fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: dev|stage)\n", *hostF)
 				os.Exit(1)
 			}
 		}
@@ -525,7 +525,7 @@ func indent(s string) string {
 	{
 		u, err := url.Parse(addr)
 		if err != nil {
-			fmt.Fprintf(os.Stderr, "invalid URL %#v: %s", addr, err)
+			fmt.Fprintf(os.Stderr, "invalid URL %#v: %s\n", addr, err)
 			os.Exit(1)
 		}
 		scheme = u.Scheme
@@ -541,7 +541,7 @@ func indent(s string) string {
 		case "http", "https":
 			endpoint, payload, err = doHTTP(scheme, host, timeout, debug)
 		default:
-			fmt.Fprintf(os.Stderr, "invalid scheme: %q (valid schemes: http|https)", scheme)
+			fmt.Fprintf(os.Stderr, "invalid scheme: %q (valid schemes: http|https)\n", scheme)
 			os.Exit(1)
 		}
 	}
diff --git a/codegen/example/testdata/example_server_code.go b/codegen/example/testdata/example_server_code.go
index 6e3a172a..d19bfa18 100644
--- a/codegen/example/testdata/example_server_code.go
+++ b/codegen/example/testdata/example_server_code.go
@@ -61,7 +61,7 @@ const (
 			addr := "http://localhost:80"
 			u, err := url.Parse(addr)
 			if err != nil {
-				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s", addr, err)
+				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s\n", addr, err)
 				os.Exit(1)
 			}
 			if *secureF {
@@ -83,7 +83,7 @@ const (
 			addr := "grpc://localhost:8080"
 			u, err := url.Parse(addr)
 			if err != nil {
-				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s", addr, err)
+				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s\n", addr, err)
 				os.Exit(1)
 			}
 			if *secureF {
@@ -102,7 +102,7 @@ const (
 		}
 
 	default:
-		fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: localhost)", *hostF)
+		fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: localhost)\n", *hostF)
 	}
 
 	// Wait for signal.
@@ -176,7 +176,7 @@ const (
 			addr := "http://localhost:80"
 			u, err := url.Parse(addr)
 			if err != nil {
-				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s", addr, err)
+				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s\n", addr, err)
 				os.Exit(1)
 			}
 			if *secureF {
@@ -198,7 +198,7 @@ const (
 			addr := "grpc://localhost:8080"
 			u, err := url.Parse(addr)
 			if err != nil {
-				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s", addr, err)
+				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s\n", addr, err)
 				os.Exit(1)
 			}
 			if *secureF {
@@ -217,7 +217,7 @@ const (
 		}
 
 	default:
-		fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: localhost)", *hostF)
+		fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: localhost)\n", *hostF)
 	}
 
 	// Wait for signal.
@@ -291,7 +291,7 @@ const (
 			addr := "http://example:8090"
 			u, err := url.Parse(addr)
 			if err != nil {
-				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s", addr, err)
+				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s\n", addr, err)
 				os.Exit(1)
 			}
 			if *secureF {
@@ -313,7 +313,7 @@ const (
 			addr := "https://example:80"
 			u, err := url.Parse(addr)
 			if err != nil {
-				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s", addr, err)
+				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s\n", addr, err)
 				os.Exit(1)
 			}
 			if *secureF {
@@ -335,7 +335,7 @@ const (
 			addr := "grpc://example:8080"
 			u, err := url.Parse(addr)
 			if err != nil {
-				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s", addr, err)
+				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s\n", addr, err)
 				os.Exit(1)
 			}
 			if *secureF {
@@ -354,7 +354,7 @@ const (
 		}
 
 	default:
-		fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: dev)", *hostF)
+		fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: dev)\n", *hostF)
 	}
 
 	// Wait for signal.
@@ -445,7 +445,7 @@ const (
 			addr = strings.Replace(addr, "{bool}", *bool_F, -1)
 			u, err := url.Parse(addr)
 			if err != nil {
-				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s", addr, err)
+				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s\n", addr, err)
 				os.Exit(1)
 			}
 			if *secureF {
@@ -476,7 +476,7 @@ const (
 			addr = strings.Replace(addr, "{bool}", *bool_F, -1)
 			u, err := url.Parse(addr)
 			if err != nil {
-				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s", addr, err)
+				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s\n", addr, err)
 				os.Exit(1)
 			}
 			if *secureF {
@@ -495,7 +495,7 @@ const (
 		}
 
 	default:
-		fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: dev)", *hostF)
+		fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: dev)\n", *hostF)
 	}
 
 	// Wait for signal.
@@ -551,7 +551,7 @@ const (
 			addr := "http://localhost:80"
 			u, err := url.Parse(addr)
 			if err != nil {
-				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s", addr, err)
+				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s\n", addr, err)
 				os.Exit(1)
 			}
 			if *secureF {
@@ -570,7 +570,7 @@ const (
 		}
 
 	default:
-		fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: svc)", *hostF)
+		fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: svc)\n", *hostF)
 	}
 
 	// Wait for signal.
@@ -644,7 +644,7 @@ const (
 			addr := "http://example:8090"
 			u, err := url.Parse(addr)
 			if err != nil {
-				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s", addr, err)
+				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s\n", addr, err)
 				os.Exit(1)
 			}
 			if *secureF {
@@ -666,7 +666,7 @@ const (
 			addr := "grpc://localhost:8080"
 			u, err := url.Parse(addr)
 			if err != nil {
-				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s", addr, err)
+				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s\n", addr, err)
 				os.Exit(1)
 			}
 			if *secureF {
@@ -685,7 +685,7 @@ const (
 		}
 
 	default:
-		fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: dev)", *hostF)
+		fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: dev)\n", *hostF)
 	}
 
 	// Wait for signal.
@@ -763,7 +763,7 @@ const (
 			addr := "http://example:8090"
 			u, err := url.Parse(addr)
 			if err != nil {
-				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s", addr, err)
+				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s\n", addr, err)
 				os.Exit(1)
 			}
 			if *secureF {
@@ -785,7 +785,7 @@ const (
 			addr := "grpc://localhost:8080"
 			u, err := url.Parse(addr)
 			if err != nil {
-				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s", addr, err)
+				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s\n", addr, err)
 				os.Exit(1)
 			}
 			if *secureF {
@@ -804,7 +804,7 @@ const (
 		}
 
 	default:
-		fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: dev)", *hostF)
+		fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: dev)\n", *hostF)
 	}
 
 	// Wait for signal.
@@ -877,7 +877,7 @@ const (
 			addr := "http://example:8090"
 			u, err := url.Parse(addr)
 			if err != nil {
-				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s", addr, err)
+				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s\n", addr, err)
 				os.Exit(1)
 			}
 			if *secureF {
@@ -900,7 +900,7 @@ const (
 			addr := "https://example"
 			u, err := url.Parse(addr)
 			if err != nil {
-				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s", addr, err)
+				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s\n", addr, err)
 				os.Exit(1)
 			}
 			if *secureF {
@@ -919,7 +919,7 @@ const (
 		}
 
 	default:
-		fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: dev|stage)", *hostF)
+		fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: dev|stage)\n", *hostF)
 	}
 
 	// Wait for signal.
@@ -1003,13 +1003,13 @@ const (
 				}
 			}
 			if !versionSeen {
-				fmt.Fprintf(os.Stderr, "invalid value for URL 'version' variable: %q (valid values: v1,v2)", *versionF)
+				fmt.Fprintf(os.Stderr, "invalid value for URL 'version' variable: %q (valid values: v1,v2)\n", *versionF)
 				os.Exit(1)
 			}
 			addr = strings.Replace(addr, "{version}", *versionF, -1)
 			u, err := url.Parse(addr)
 			if err != nil {
-				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s", addr, err)
+				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s\n", addr, err)
 				os.Exit(1)
 			}
 			if *secureF {
@@ -1034,7 +1034,7 @@ const (
 			addr = strings.Replace(addr, "{port}", *portF, -1)
 			u, err := url.Parse(addr)
 			if err != nil {
-				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s", addr, err)
+				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s\n", addr, err)
 				os.Exit(1)
 			}
 			if *secureF {
@@ -1053,7 +1053,7 @@ const (
 		}
 
 	default:
-		fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: dev|stage)", *hostF)
+		fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: dev|stage)\n", *hostF)
 	}
 
 	// Wait for signal.
@@ -1126,7 +1126,7 @@ const (
 			addr := "http://localhost:80"
 			u, err := url.Parse(addr)
 			if err != nil {
-				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s", addr, err)
+				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s\n", addr, err)
 				os.Exit(1)
 			}
 			if *secureF {
@@ -1148,7 +1148,7 @@ const (
 			addr := "grpc://localhost:8080"
 			u, err := url.Parse(addr)
 			if err != nil {
-				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s", addr, err)
+				fmt.Fprintf(os.Stderr, "invalid URL %#v: %s\n", addr, err)
 				os.Exit(1)
 			}
 			if *secureF {
@@ -1167,7 +1167,7 @@ const (
 		}
 
 	default:
-		fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: svc)", *hostF)
+		fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: svc)\n", *hostF)
 	}
 
 	// Wait for signal.
diff --git a/codegen/funcs_test.go b/codegen/funcs_test.go
index deba1e8b..7b8f5bbc 100644
--- a/codegen/funcs_test.go
+++ b/codegen/funcs_test.go
@@ -18,6 +18,8 @@ func TestSnakeCase(t *testing.T) {
 		"sequential uppers 2":   {"aaAAAa", "aa_aa_aa"},
 		"end sequential uppers": {"aaAA", "aa_aa"},
 		"multiple_uppers":       {"aaAaaAaa", "aa_aaa_aaa"},
+		"dashes":                {"aa-Aaa-Aaa", "aa_aaa_aaa"},
+		"dashes 2":              {"aa-AAaaAA-Aaa", "aa_a_aaa_aa_aaa"},
 		"underscores":           {"aa_Aaa_Aaa", "aa_aaa_aaa"},
 		"underscores 2":         {"aa_AAaaAA_Aaa", "aa_a_aaa_aa_aaa"},
 		"numbers":               {"aa1", "aa1"},
@@ -61,6 +63,36 @@ func TestCamelCase(t *testing.T) {
 	}
 }
 
+func TestKebabCase(t *testing.T) {
+	cases := map[string]struct {
+		str      string
+		expected string
+	}{
+		"all lower":             {"aaa", "aaa"},
+		"start upper":           {"Aaa", "aaa"},
+		"start upper 2":         {"AAAaa", "aa-aaa"},
+		"mid upper":             {"aAa", "a-aa"},
+		"end upper":             {"aaA", "aa-a"},
+		"sequential uppers":     {"aaAAaa", "aa-a-aaa"},
+		"sequential uppers 2":   {"aaAAAa", "aa-aa-aa"},
+		"end sequential uppers": {"aaAA", "aa-aa"},
+		"multiple_uppers":       {"aaAaaAaa", "aa-aaa-aaa"},
+		"underscores":           {"aa_Aaa_Aaa", "aa-aaa-aaa"},
+		"underscores 2":         {"aa_AAaaAA_Aaa", "aa-a-aaa-aa-aaa"},
+		"dashes":                {"aa-Aaa-Aaa", "aa-aaa-aaa"},
+		"dashes 2":              {"aa-AAaaAA-Aaa", "aa-a-aaa-aa-aaa"},
+		"numbers":               {"aa1", "aa1"},
+	}
+	for k, tc := range cases {
+		t.Run(k, func(t *testing.T) {
+			actual := KebabCase(tc.str)
+			if actual != tc.expected {
+				t.Errorf("got %q, expected %q", actual, tc.expected)
+			}
+		})
+	}
+}
+
 func TestWrapText(t *testing.T) {
 	cases := map[string]struct {
 		maxChars int
diff --git a/http/codegen/testdata/example_code.go b/http/codegen/testdata/example_code.go
index 2d98cd01..6d0f999e 100644
--- a/http/codegen/testdata/example_code.go
+++ b/http/codegen/testdata/example_code.go
@@ -16,7 +16,7 @@ func handleHTTPServer(ctx context.Context, u *url.URL, serviceEndpoints *service
 	// Provide the transport specific request decoder and response encoder.
 	// The goa http package has built-in support for JSON, XML and gob.
 	// Other encodings can be used by providing the corresponding functions,
-	// see goa.design/encoding.
+	// see goa.design/implement/encoding.
 	var (
 		dec = goahttp.RequestDecoder
 		enc = goahttp.ResponseEncoder
@@ -109,7 +109,7 @@ func handleHTTPServer(ctx context.Context, u *url.URL, wg *sync.WaitGroup, errc
 	// Provide the transport specific request decoder and response encoder.
 	// The goa http package has built-in support for JSON, XML and gob.
 	// Other encodings can be used by providing the corresponding functions,
-	// see goa.design/encoding.
+	// see goa.design/implement/encoding.
 	var (
 		dec = goahttp.RequestDecoder
 		enc = goahttp.ResponseEncoder
@@ -202,7 +202,7 @@ func handleHTTPServer(ctx context.Context, u *url.URL, serviceEndpoints *service
 	// Provide the transport specific request decoder and response encoder.
 	// The goa http package has built-in support for JSON, XML and gob.
 	// Other encodings can be used by providing the corresponding functions,
-	// see goa.design/encoding.
+	// see goa.design/implement/encoding.
 	var (
 		dec = goahttp.RequestDecoder
 		enc = goahttp.ResponseEncoder
@@ -295,7 +295,7 @@ func handleHTTPServer(ctx context.Context, u *url.URL, serviceEndpoints *service
 	// Provide the transport specific request decoder and response encoder.
 	// The goa http package has built-in support for JSON, XML and gob.
 	// Other encodings can be used by providing the corresponding functions,
-	// see goa.design/encoding.
+	// see goa.design/implement/encoding.
 	var (
 		dec = goahttp.RequestDecoder
 		enc = goahttp.ResponseEncoder
@@ -394,7 +394,7 @@ func handleHTTPServer(ctx context.Context, u *url.URL, streamingServiceAEndpoint
 	// Provide the transport specific request decoder and response encoder.
 	// The goa http package has built-in support for JSON, XML and gob.
 	// Other encodings can be used by providing the corresponding functions,
-	// see goa.design/encoding.
+	// see goa.design/implement/encoding.
 	var (
 		dec = goahttp.RequestDecoder
 		enc = goahttp.ResponseEncoder
diff --git a/pkg/validation_test.go b/pkg/validation_test.go
index 8970c94b..60fab7b1 100644
--- a/pkg/validation_test.go
+++ b/pkg/validation_test.go
@@ -19,7 +19,10 @@ func TestValidateFormat(t *testing.T) {
 		validUUID       = "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
 		invalidUUID     = "96054a62-a9e45ed26688389b"
 		validEmail      = "raphael@goa.design"
-		invalidEmail    = "foo"
+
+		// Re-enable once CircleCI uses Go 1.13
+		// invalidEmail    = "foo"
+
 		validHostname   = "goa.design"
 		invalidHostname = "_hi_"
 		validIPv4       = "192.168.0.1"
@@ -45,14 +48,17 @@ func TestValidateFormat(t *testing.T) {
 		format   Format
 		expected error
 	}{
-		"valid date":         {"validDate", validDate, FormatDate, nil},
-		"invalid date":       {"invalidDate", invalidDate, FormatDate, InvalidFormatError("invalidDate", invalidDate, FormatDate, &time.ParseError{Layout: "2006-01-02", Value: invalidDate, LayoutElem: "-", ValueElem: invalidDate[4:]})},
-		"valid date-time":    {"validDateTime", validDateTime, FormatDateTime, nil},
-		"invalid date-time":  {"invalidDateTime", invalidDateTime, FormatDateTime, InvalidFormatError("invalidDateTime", invalidDateTime, FormatDateTime, &time.ParseError{Layout: time.RFC3339, Value: invalidDateTime, LayoutElem: "-", ValueElem: invalidDateTime[4:]})},
-		"valid uuid":         {"validUUID", validUUID, FormatUUID, nil},
-		"invalid uuid":       {"invalidUUID", invalidUUID, FormatUUID, InvalidFormatError("invalidUUID", invalidUUID, FormatUUID, fmt.Errorf("uuid: UUID string too short: %s", invalidUUID))},
-		"valid email":        {"validEmail", validEmail, FormatEmail, nil},
-		"invalid email":      {"invalidEmail", invalidEmail, FormatEmail, InvalidFormatError("invalidEmail", invalidEmail, FormatEmail, errors.New("mail: no angle-addr"))},
+		"valid date":        {"validDate", validDate, FormatDate, nil},
+		"invalid date":      {"invalidDate", invalidDate, FormatDate, InvalidFormatError("invalidDate", invalidDate, FormatDate, &time.ParseError{Layout: "2006-01-02", Value: invalidDate, LayoutElem: "-", ValueElem: invalidDate[4:]})},
+		"valid date-time":   {"validDateTime", validDateTime, FormatDateTime, nil},
+		"invalid date-time": {"invalidDateTime", invalidDateTime, FormatDateTime, InvalidFormatError("invalidDateTime", invalidDateTime, FormatDateTime, &time.ParseError{Layout: time.RFC3339, Value: invalidDateTime, LayoutElem: "-", ValueElem: invalidDateTime[4:]})},
+		"valid uuid":        {"validUUID", validUUID, FormatUUID, nil},
+		"invalid uuid":      {"invalidUUID", invalidUUID, FormatUUID, InvalidFormatError("invalidUUID", invalidUUID, FormatUUID, fmt.Errorf("uuid: UUID string too short: %s", invalidUUID))},
+		"valid email":       {"validEmail", validEmail, FormatEmail, nil},
+
+		// Re-enable once CircleCI uses Go 1.13
+		// "invalid email":      {"invalidEmail", invalidEmail, FormatEmail, InvalidFormatError("invalidEmail", invalidEmail, FormatEmail, errors.New("mail: missing '@' or angle-addr"))},
+
 		"valid hostname":     {"validHostname", validHostname, FormatHostname, nil},
 		"invalid hostname":   {"invalidHostname", invalidHostname, FormatHostname, InvalidFormatError("invalidHostname", invalidHostname, FormatHostname, fmt.Errorf("hostname value '%s' does not match %s", invalidHostname, `^[[:alnum:]][[:alnum:]\-]{0,61}[[:alnum:]]|[[:alpha:]]$`))},
 		"valid ipv4":         {"validIPv4", validIPv4, FormatIPv4, nil},