Repository: knative/pkg (go)

Issue:
update-codegen.sh produces inconsistent output
<!--
Pro-tip: You can leave this block commented, and it still works!

Select the appropriate areas for your issue:

/area API

Classify what kind of issue this is:

/kind bug

-->

## Expected Behavior
Checking out master and running ./hack/update-codegen.sh should produce no changes.

## Actual Behavior
Running ./hack/update-codegen.sh produces changes to the generated controllers.

## Steps to Reproduce the Problem

1. Checkout the master branch.
2. Run ./hack/update-codegen.sh

## Additional Info
Using go 1.14.2

Pull request description:
Use go run to run codegen
Fixes #1294
cc @grantr @chizhg 

Patch:
diff --git a/apis/duck/v1/kresource_type.go b/apis/duck/v1/kresource_type.go
index 22a3b33ec..b64977fcb 100644
--- a/apis/duck/v1/kresource_type.go
+++ b/apis/duck/v1/kresource_type.go
@@ -28,11 +28,13 @@ import (
 
 // KRShaped is an interface for retrieving the duck elements of an arbitraty resource.
 type KRShaped interface {
-	GetTypeMeta() *metav1.TypeMeta
+	metav1.ObjectMetaAccessor
 
-	GetObjectMeta() *metav1.ObjectMeta
+	GetTypeMeta() *metav1.TypeMeta
 
 	GetStatus() *Status
+
+	GetTopLevelConditionType() apis.ConditionType
 }
 
 // Asserts KResource conformance with KRShaped
@@ -84,12 +86,17 @@ func (t *KResource) GetTypeMeta() *metav1.TypeMeta {
 	return &t.TypeMeta
 }
 
-// GetObjectMeta retrieves the ObjectMeta of the KResource. Implements the KRShaped interface.
-func (t *KResource) GetObjectMeta() *metav1.ObjectMeta {
-	return &t.ObjectMeta
-}
-
 // GetStatus retrieves the status of the KResource. Implements the KRShaped interface.
 func (t *KResource) GetStatus() *Status {
 	return &t.Status
 }
+
+// GetTopLevelConditionType retrieves the happy condition of this resource. Implements the KRShaped interface.
+func (t *KResource) GetTopLevelConditionType() apis.ConditionType {
+	// Note: KResources are unmarshalled from existing resources. This will only work properly for resources that
+	// have already been initialized to their type.
+	if cond := t.Status.GetCondition(apis.ConditionSucceeded); cond != nil {
+		return apis.ConditionSucceeded
+	}
+	return apis.ConditionReady
+}
diff --git a/client/injection/apiextensions/reconciler/apiextensions/v1beta1/customresourcedefinition/controller.go b/client/injection/apiextensions/reconciler/apiextensions/v1beta1/customresourcedefinition/controller.go
index 26824e6e2..4f20eeb03 100644
--- a/client/injection/apiextensions/reconciler/apiextensions/v1beta1/customresourcedefinition/controller.go
+++ b/client/injection/apiextensions/reconciler/apiextensions/v1beta1/customresourcedefinition/controller.go
@@ -30,9 +30,9 @@ import (
 	scheme "k8s.io/client-go/kubernetes/scheme"
 	v1 "k8s.io/client-go/kubernetes/typed/core/v1"
 	record "k8s.io/client-go/tools/record"
-	apiextensionsclient "knative.dev/pkg/client/injection/apiextensions/client"
+	client "knative.dev/pkg/client/injection/apiextensions/client"
 	customresourcedefinition "knative.dev/pkg/client/injection/apiextensions/informers/apiextensions/v1beta1/customresourcedefinition"
-	client "knative.dev/pkg/client/injection/kube/client"
+	kubeclient "knative.dev/pkg/client/injection/kube/client"
 	controller "knative.dev/pkg/controller"
 	logging "knative.dev/pkg/logging"
 )
@@ -56,29 +56,9 @@ func NewImpl(ctx context.Context, r Interface, optionsFns ...controller.OptionsF
 
 	customresourcedefinitionInformer := customresourcedefinition.Get(ctx)
 
-	recorder := controller.GetEventRecorder(ctx)
-	if recorder == nil {
-		// Create event broadcaster
-		logger.Debug("Creating event broadcaster")
-		eventBroadcaster := record.NewBroadcaster()
-		watches := []watch.Interface{
-			eventBroadcaster.StartLogging(logger.Named("event-broadcaster").Infof),
-			eventBroadcaster.StartRecordingToSink(
-				&v1.EventSinkImpl{Interface: client.Get(ctx).CoreV1().Events("")}),
-		}
-		recorder = eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: defaultControllerAgentName})
-		go func() {
-			<-ctx.Done()
-			for _, w := range watches {
-				w.Stop()
-			}
-		}()
-	}
-
 	rec := &reconcilerImpl{
-		Client:        apiextensionsclient.Get(ctx),
+		Client:        client.Get(ctx),
 		Lister:        customresourcedefinitionInformer.Lister(),
-		Recorder:      recorder,
 		reconciler:    r,
 		finalizerName: defaultFinalizerName,
 	}
@@ -87,6 +67,7 @@ func NewImpl(ctx context.Context, r Interface, optionsFns ...controller.OptionsF
 	queueName := fmt.Sprintf("%s.%s", strings.ReplaceAll(t.PkgPath(), "/", "-"), t.Name())
 
 	impl := controller.NewImpl(rec, logger, queueName)
+	agentName := defaultControllerAgentName
 
 	// Pass impl to the options. Save any optional results.
 	for _, fn := range optionsFns {
@@ -97,11 +78,41 @@ func NewImpl(ctx context.Context, r Interface, optionsFns ...controller.OptionsF
 		if opts.FinalizerName != "" {
 			rec.finalizerName = opts.FinalizerName
 		}
+		if opts.AgentName != "" {
+			agentName = opts.AgentName
+		}
 	}
 
+	rec.Recorder = createRecorder(ctx, agentName)
+
 	return impl
 }
 
+func createRecorder(ctx context.Context, agentName string) record.EventRecorder {
+	logger := logging.FromContext(ctx)
+
+	recorder := controller.GetEventRecorder(ctx)
+	if recorder == nil {
+		// Create event broadcaster
+		logger.Debug("Creating event broadcaster")
+		eventBroadcaster := record.NewBroadcaster()
+		watches := []watch.Interface{
+			eventBroadcaster.StartLogging(logger.Named("event-broadcaster").Infof),
+			eventBroadcaster.StartRecordingToSink(
+				&v1.EventSinkImpl{Interface: kubeclient.Get(ctx).CoreV1().Events("")}),
+		}
+		recorder = eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: agentName})
+		go func() {
+			<-ctx.Done()
+			for _, w := range watches {
+				w.Stop()
+			}
+		}()
+	}
+
+	return recorder
+}
+
 func init() {
 	clientsetscheme.AddToScheme(scheme.Scheme)
 }
diff --git a/client/injection/kube/reconciler/core/v1/namespace/controller.go b/client/injection/kube/reconciler/core/v1/namespace/controller.go
index 9639b3b02..4cee36b43 100644
--- a/client/injection/kube/reconciler/core/v1/namespace/controller.go
+++ b/client/injection/kube/reconciler/core/v1/namespace/controller.go
@@ -54,29 +54,9 @@ func NewImpl(ctx context.Context, r Interface, optionsFns ...controller.OptionsF
 
 	namespaceInformer := namespace.Get(ctx)
 
-	recorder := controller.GetEventRecorder(ctx)
-	if recorder == nil {
-		// Create event broadcaster
-		logger.Debug("Creating event broadcaster")
-		eventBroadcaster := record.NewBroadcaster()
-		watches := []watch.Interface{
-			eventBroadcaster.StartLogging(logger.Named("event-broadcaster").Infof),
-			eventBroadcaster.StartRecordingToSink(
-				&v1.EventSinkImpl{Interface: client.Get(ctx).CoreV1().Events("")}),
-		}
-		recorder = eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: defaultControllerAgentName})
-		go func() {
-			<-ctx.Done()
-			for _, w := range watches {
-				w.Stop()
-			}
-		}()
-	}
-
 	rec := &reconcilerImpl{
 		Client:        client.Get(ctx),
 		Lister:        namespaceInformer.Lister(),
-		Recorder:      recorder,
 		reconciler:    r,
 		finalizerName: defaultFinalizerName,
 	}
@@ -85,6 +65,7 @@ func NewImpl(ctx context.Context, r Interface, optionsFns ...controller.OptionsF
 	queueName := fmt.Sprintf("%s.%s", strings.ReplaceAll(t.PkgPath(), "/", "-"), t.Name())
 
 	impl := controller.NewImpl(rec, logger, queueName)
+	agentName := defaultControllerAgentName
 
 	// Pass impl to the options. Save any optional results.
 	for _, fn := range optionsFns {
@@ -95,11 +76,41 @@ func NewImpl(ctx context.Context, r Interface, optionsFns ...controller.OptionsF
 		if opts.FinalizerName != "" {
 			rec.finalizerName = opts.FinalizerName
 		}
+		if opts.AgentName != "" {
+			agentName = opts.AgentName
+		}
 	}
 
+	rec.Recorder = createRecorder(ctx, agentName)
+
 	return impl
 }
 
+func createRecorder(ctx context.Context, agentName string) record.EventRecorder {
+	logger := logging.FromContext(ctx)
+
+	recorder := controller.GetEventRecorder(ctx)
+	if recorder == nil {
+		// Create event broadcaster
+		logger.Debug("Creating event broadcaster")
+		eventBroadcaster := record.NewBroadcaster()
+		watches := []watch.Interface{
+			eventBroadcaster.StartLogging(logger.Named("event-broadcaster").Infof),
+			eventBroadcaster.StartRecordingToSink(
+				&v1.EventSinkImpl{Interface: client.Get(ctx).CoreV1().Events("")}),
+		}
+		recorder = eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: agentName})
+		go func() {
+			<-ctx.Done()
+			for _, w := range watches {
+				w.Stop()
+			}
+		}()
+	}
+
+	return recorder
+}
+
 func init() {
 	scheme.AddToScheme(scheme.Scheme)
 }
diff --git a/codegen/cmd/injection-gen/generators/packages.go b/codegen/cmd/injection-gen/generators/packages.go
index 012e6c0b6..a53dc8b02 100644
--- a/codegen/cmd/injection-gen/generators/packages.go
+++ b/codegen/cmd/injection-gen/generators/packages.go
@@ -193,13 +193,13 @@ func extractCommentTags(t *types.Type) map[string]map[string]string {
 	return ExtractCommentTags("+", comments)
 }
 
-func extractReconcilerClassTag(tags map[string]map[string]string) (classname string, has bool) {
-	vals, has := tags["genreconciler"]
-	if !has {
-		return
+func extractReconcilerClassTag(tags map[string]map[string]string) (string, bool) {
+	vals, ok := tags["genreconciler"]
+	if !ok {
+		return "", false
 	}
-	classname, _ = vals["class"]
-	return
+	classname, has := vals["class"]
+	return classname, has
 }
 
 func isNonNamespaced(tags map[string]map[string]string) bool {
diff --git a/go.mod b/go.mod
index 72d87d047..4cbe57269 100644
--- a/go.mod
+++ b/go.mod
@@ -48,7 +48,7 @@ require (
 	k8s.io/klog v1.0.0
 	k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a
 	k8s.io/test-infra v0.0.0-20200407001919-bc7f71ef65b8
-	knative.dev/test-infra v0.0.0-20200505052144-5ea2f705bb55
+	knative.dev/test-infra v0.0.0-20200505192244-75864c82db21
 )
 
 replace (
diff --git a/go.sum b/go.sum
index a0cbdc195..c65606075 100644
--- a/go.sum
+++ b/go.sum
@@ -1346,10 +1346,10 @@ knative.dev/caching v0.0.0-20190719140829-2032732871ff/go.mod h1:dHXFU6CGlLlbzaW
 knative.dev/eventing-contrib v0.6.1-0.20190723221543-5ce18048c08b/go.mod h1:SnXZgSGgMSMLNFTwTnpaOH7hXDzTFtw0J8OmHflNx3g=
 knative.dev/pkg v0.0.0-20191101194912-56c2594e4f11/go.mod h1:pgODObA1dTyhNoFxPZTTjNWfx6F0aKsKzn+vaT9XO/Q=
 knative.dev/pkg v0.0.0-20191111150521-6d806b998379/go.mod h1:pgODObA1dTyhNoFxPZTTjNWfx6F0aKsKzn+vaT9XO/Q=
-knative.dev/pkg v0.0.0-20200428194351-90fc61bae7f7/go.mod h1:o+e8OVEJKIuvXPsGVPIautjXgs05xbos7G+QMRjuUps=
-knative.dev/test-infra v0.0.0-20200407185800-1b88cb3b45a5/go.mod h1:xcdUkMJrLlBswIZqL5zCuBFOC22WIPMQoVX1L35i0vQ=
-knative.dev/test-infra v0.0.0-20200505052144-5ea2f705bb55 h1:Ajn44+eHHjPQL/BQicj8LMy8VTD2ypMCfHJuZVGEtew=
-knative.dev/test-infra v0.0.0-20200505052144-5ea2f705bb55/go.mod h1:WqF1Azka+FxPZ20keR2zCNtiQA1MP9ZB4BH4HuI+SIU=
+knative.dev/pkg v0.0.0-20200504180943-4a2ba059b008/go.mod h1:1RvwKBbKqKYt5rgI4lfYdWCdtXgMxJY73QxPb3jZPC4=
+knative.dev/test-infra v0.0.0-20200430225942-f7c1fafc1cde/go.mod h1:xcdUkMJrLlBswIZqL5zCuBFOC22WIPMQoVX1L35i0vQ=
+knative.dev/test-infra v0.0.0-20200505192244-75864c82db21 h1:SsvqMKpvrn7cl7UqRUIT90SXDowHzpzHwHaTu+wN70s=
+knative.dev/test-infra v0.0.0-20200505192244-75864c82db21/go.mod h1:AqweEMgaMbb2xmYq9ZOPsH/lQ61qNx2XGr5tGltj5QU=
 modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=
 modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
 modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
diff --git a/hack/generate-knative.sh b/hack/generate-knative.sh
index cbd957077..1c87ed378 100755
--- a/hack/generate-knative.sh
+++ b/hack/generate-knative.sh
@@ -46,12 +46,8 @@ APIS_PKG="$3"
 GROUPS_WITH_VERSIONS="$4"
 shift 4
 
-(
-  # To support running this script from anywhere, we have to first cd into this directory
-  # so we can install the tools.
-  cd $(dirname "${0}")
-  go install ../codegen/cmd/injection-gen
-)
+# To support running this script from anywhere, we have to first cd into this directory.
+cd $(dirname "${0}")/..
 
 function codegen::join() { local IFS="$1"; shift; echo "$*"; }
 
@@ -89,7 +85,7 @@ if grep -qw "injection" <<<"${GENS}"; then
   # Clear old injection
   rm -rf ${OUTPUT_PKG}
 
-  ${GOPATH}/bin/injection-gen \
+  go run knative.dev/pkg/codegen/cmd/injection-gen \
     --input-dirs $(codegen::join , "${FQ_APIS[@]}") \
     --versioned-clientset-package ${VERSIONED_CLIENTSET_PKG} \
     --external-versions-informers-package ${EXTERNAL_INFORMER_PKG} \
diff --git a/hack/update-codegen.sh b/hack/update-codegen.sh
index 108d8e646..495de15b3 100755
--- a/hack/update-codegen.sh
+++ b/hack/update-codegen.sh
@@ -24,7 +24,7 @@ source $(dirname $0)/../vendor/knative.dev/test-infra/scripts/library.sh
 
 CODEGEN_PKG=${CODEGEN_PKG:-$(cd ${REPO_ROOT_DIR}; ls -d -1 $(dirname $0)/../vendor/k8s.io/code-generator 2>/dev/null || echo ../code-generator)}
 
-go install $(dirname $0)/../vendor/k8s.io/code-generator/cmd/deepcopy-gen
+cd $(dirname $0)/..
 
 # generate the code with:
 # --output-base    because this script should also be able to run inside the vendor dir of
@@ -64,7 +64,7 @@ ${CODEGEN_PKG}/generate-groups.sh "deepcopy" \
   --go-header-file ${REPO_ROOT_DIR}/hack/boilerplate/boilerplate.go.txt
 
 # Depends on generate-groups.sh to install bin/deepcopy-gen
-${GOPATH}/bin/deepcopy-gen --input-dirs \
+go run k8s.io/code-generator/cmd/deepcopy-gen  --input-dirs \
   $(echo \
   knative.dev/pkg/apis \
   knative.dev/pkg/tracker \
diff --git a/vendor/modules.txt b/vendor/modules.txt
index aef1373b2..2cfed1919 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -914,7 +914,7 @@ k8s.io/utils/buffer
 k8s.io/utils/integer
 k8s.io/utils/pointer
 k8s.io/utils/trace
-# knative.dev/test-infra v0.0.0-20200505052144-5ea2f705bb55
+# knative.dev/test-infra v0.0.0-20200505192244-75864c82db21
 ## explicit
 knative.dev/test-infra/scripts
 # sigs.k8s.io/yaml v1.1.0


Test patch:
diff --git a/apis/duck/v1/kresource_type_test.go b/apis/duck/v1/kresource_type_test.go
new file mode 100644
index 000000000..357353b14
--- /dev/null
+++ b/apis/duck/v1/kresource_type_test.go
@@ -0,0 +1,43 @@
+/*
+Copyright 2020 The Knative Authors
+
+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
+
+    http://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 v1
+
+import (
+	"testing"
+
+	"knative.dev/pkg/apis"
+)
+
+func TestGetTopLevelCondition(t *testing.T) {
+	resource := KResource{}
+
+	condSet := apis.NewLivingConditionSet("Foo")
+	mgr := condSet.Manage(resource.GetStatus())
+	mgr.InitializeConditions()
+
+	if resource.GetTopLevelConditionType() != apis.ConditionReady {
+		t.Error("Expected Ready as happy condition for living condition set type")
+	}
+
+	condSet = apis.NewBatchConditionSet("Foo")
+	mgr = condSet.Manage(resource.GetStatus())
+	mgr.InitializeConditions()
+
+	if resource.GetTopLevelConditionType() != apis.ConditionSucceeded {
+		t.Error("Expected Succeeded as happy condition for living condition set type")
+	}
+}
diff --git a/apis/test/example/v1alpha1/fiz_types.go b/apis/test/example/v1alpha1/fiz_types.go
index df1c054f2..334e827a5 100644
--- a/apis/test/example/v1alpha1/fiz_types.go
+++ b/apis/test/example/v1alpha1/fiz_types.go
@@ -50,6 +50,7 @@ type ClusterFiz struct {
 var _ apis.Validatable = (*ClusterFiz)(nil)
 var _ apis.Defaultable = (*ClusterFiz)(nil)
 var _ kmeta.OwnerRefable = (*ClusterFiz)(nil)
+var _ duckv1.KRShaped = (*ClusterFiz)(nil)
 
 // ClusterFizSpec holds the desired state of the ClusterFiz (from the client).
 type ClusterFizSpec struct{}
@@ -92,3 +93,18 @@ func (f *ClusterFiz) Validate(ctx context.Context) *apis.FieldError {
 	// Nothing to validate.
 	return nil
 }
+
+// GetTypeMeta retrieves the ObjectMeta of the ClusterFiz. Implements the KRShaped interface.
+func (f *ClusterFiz) GetTypeMeta() *metav1.TypeMeta {
+	return &f.TypeMeta
+}
+
+// GetStatus retrieves the status of the ClusterFiz. Implements the KRShaped interface.
+func (f *ClusterFiz) GetStatus() *duckv1.Status {
+	return &f.Status.Status
+}
+
+// GetTopLevelConditionType retrieves the happy condition of this resource. Implements the KRShaped interface.
+func (*ClusterFiz) GetTopLevelConditionType() apis.ConditionType {
+	return apis.ConditionReady
+}
diff --git a/apis/test/example/v1alpha1/foo_types.go b/apis/test/example/v1alpha1/foo_types.go
index 22ac876ff..948254353 100644
--- a/apis/test/example/v1alpha1/foo_types.go
+++ b/apis/test/example/v1alpha1/foo_types.go
@@ -49,6 +49,7 @@ type Foo struct {
 var _ apis.Validatable = (*Foo)(nil)
 var _ apis.Defaultable = (*Foo)(nil)
 var _ kmeta.OwnerRefable = (*Foo)(nil)
+var _ duckv1.KRShaped = (*Foo)(nil)
 
 // FooSpec holds the desired state of the Foo (from the client).
 type FooSpec struct{}
@@ -91,3 +92,18 @@ func (f *Foo) Validate(ctx context.Context) *apis.FieldError {
 	// Nothing to validate.
 	return nil
 }
+
+// GetTypeMeta retrieves the ObjectMeta of the Foo. Implements the KRShaped interface.
+func (f *Foo) GetTypeMeta() *metav1.TypeMeta {
+	return &f.TypeMeta
+}
+
+// GetStatus retrieves the status of the Foo Implements the KRShaped interface.
+func (f *Foo) GetStatus() *duckv1.Status {
+	return &f.Status.Status
+}
+
+// GetTopLevelConditionType retrieves the happy condition of this resource. Implements the KRShaped interface.
+func (*Foo) GetTopLevelConditionType() apis.ConditionType {
+	return apis.ConditionSucceeded
+}
diff --git a/apis/test/pub/v1alpha1/bar_types.go b/apis/test/pub/v1alpha1/bar_types.go
index 2eefcd87a..f27d5f660 100644
--- a/apis/test/pub/v1alpha1/bar_types.go
+++ b/apis/test/pub/v1alpha1/bar_types.go
@@ -49,6 +49,7 @@ type Bar struct {
 var _ apis.Validatable = (*Bar)(nil)
 var _ apis.Defaultable = (*Bar)(nil)
 var _ kmeta.OwnerRefable = (*Bar)(nil)
+var _ duckv1.KRShaped = (*Bar)(nil)
 
 // BarSpec holds the desired state of the Bar (from the client).
 type BarSpec struct{}
@@ -91,3 +92,18 @@ func (b *Bar) Validate(ctx context.Context) *apis.FieldError {
 	// Nothing to validate.
 	return nil
 }
+
+// GetTypeMeta retrieves the ObjectMeta of the Bar. Implements the KRShaped interface.
+func (b *Bar) GetTypeMeta() *metav1.TypeMeta {
+	return &b.TypeMeta
+}
+
+// GetStatus retrieves the status of the Bar. Implements the KRShaped interface.
+func (b *Bar) GetStatus() *duckv1.Status {
+	return &b.Status.Status
+}
+
+// GetTopLevelConditionType retrieves the happy condition of this resource. Implements the KRShaped interface.
+func (*Bar) GetTopLevelConditionType() apis.ConditionType {
+	return apis.ConditionReady
+}
diff --git a/vendor/knative.dev/test-infra/scripts/presubmit-tests.sh b/vendor/knative.dev/test-infra/scripts/presubmit-tests.sh
index 42680979f..797f2fd3e 100644
--- a/vendor/knative.dev/test-infra/scripts/presubmit-tests.sh
+++ b/vendor/knative.dev/test-infra/scripts/presubmit-tests.sh
@@ -180,11 +180,13 @@ function default_build_test_runner() {
     # Consider an error message everything that's not a package name.
     errors_go1="$(grep -v '^\(github\.com\|knative\.dev\)/' "${report}" | sort | uniq)"
   fi
-  # Get all build tags in go code (ignore /vendor and /hack)
+  # Get all build tags in go code (ignore /vendor, /hack and /third_party)
   local tags="$(grep -r '// +build' . \
-    | grep -v '^./vendor/' | grep -v '^./hack/' | cut -f3 -d' ' | sort | uniq | tr '\n' ' ')"
+    | grep -v '^./vendor/' | grep -v '^./hack/' | grep -v '^./third_party' \
+    | cut -f3 -d' ' | sort | uniq | tr '\n' ' ')"
   local tagged_pkgs="$(grep -r '// +build' . \
-    | grep -v '^./vendor/' | grep -v '^./hack/' | grep ":// +build " | cut -f1 -d: | xargs dirname \
+    | grep -v '^./vendor/' | grep -v '^./hack/' | grep -v '^./third_party' \
+    | grep ":// +build " | cut -f1 -d: | xargs dirname \
     | sort | uniq | tr '\n' ' ')"
   for pkg in ${tagged_pkgs}; do
     # `go test -c` lets us compile the tests but do not run them.