Repository: knative-sandbox/kn-plugin-func
Pull request #869 (2022-02-28 12:11:26)

src: promote verbosity to constructor arg
# Changes

**tl;dr** Probably best to use _either_ constructor args _or_ struct member initialization and not mix.  We have chosen constructors for package public API and struct members for package internal implementations.  This enforces that choice with regards to the oft-ignored verbosity flag.

- :broom: Moves verbosity flag to constructor arg

This rectifies a long-standing inconsistency where, early in the system's evolution all subsystems were initialized as simple structs.  When we moved to using proper constructors, the Verbosity flag was left as a struct member.  This is a bit confusing and awkward because it mixes constructor-based configuration with the direct setting of struct members.  The former is often best for external package APIs, the latter is often best for internal.  This situation straddles the line, because these packages are used by their parent package, so either way could be a defensible position.

Explicitly requiring the verbosity to be provided in the constructor is not awe-inspiring elegance, but I suppose mixing constructor-based initialization with struct members much less so.  This is evidenced by how much `main`'s default Client constructor is simplified by requiring this flag in the constructor.

/kind cleanup


knative-prow-robot: @lkingland: The label(s) `kind/<kind>` cannot be applied, because the repository doesn't have them.

<details>

In response to [this](https://github.com/knative-sandbox/kn-plugin-func/pull/869):

># Changes
>
>Moves the `Verbosity` flag from a struct member to a constructor parameter.
>
>This rectifies a long-standing inconsistency where, early on all subsystems were simple structs.  When we moved to using proper constructors, the Verbosity flag was left as a struct member.  This is a bit confusing and awkward, because it mixes constructor-based configuration with the direct setting struct members.  The former is best for external package APIs, the latter best for internal.  This straddles the line, so either way could be defensible.  Explicitly requiring the verbosity to be provided in the constructor is not desireable, but mixing constructor-based initialization with struct members much less so.  This is evidenced by how much `main`'s default Client constructor is simplified by requiring this flag in the constructor.
>
>tl;dr We should use either constructors or struct member initialization, not both.  We have chosen constructors for package public API and struct members for package internal implementations.  This enforces that choice with regards to the oft-ignored verbosity flag.
>
>
>-
>-
>-
>
>
>/kind <kind>
>
>
>Fixes #
>
>
>
>**Release Note**
>
>
>```release-note
>
>```
>
>**Docs**
>
>
>```docs
>
>```
>


Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>
knative-prow-robot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/knative-sandbox/kn-plugin-func/pull/869#" title="Author self-approved">lkingland</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=knative-sandbox%2Fkn-plugin-func).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/knative-sandbox/kn-plugin-func/blob/main/OWNERS)~~ [lkingland]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
codecov[bot]: # [Codecov](https://codecov.io/gh/knative-sandbox/kn-plugin-func/pull/869?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative-sandbox) Report
> Merging [#869](https://codecov.io/gh/knative-sandbox/kn-plugin-func/pull/869?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative-sandbox) (eddb917) into [main](https://codecov.io/gh/knative-sandbox/kn-plugin-func/commit/663fd29b76d23c24c0b1443e9e172f5dac815c8f?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative-sandbox) (663fd29) will **increase** coverage by `1.91%`.
> The diff coverage is `53.91%`.

[![Impacted file tree graph](https://codecov.io/gh/knative-sandbox/kn-plugin-func/pull/869/graphs/tree.svg?width=650&height=150&src=pr&token=da1B9bMsQ9&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative-sandbox)](https://codecov.io/gh/knative-sandbox/kn-plugin-func/pull/869?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative-sandbox)

```diff
@@            Coverage Diff             @@
##             main     #869      +/-   ##
==========================================
+ Coverage   42.87%   44.79%   +1.91%     
==========================================
  Files          49       48       -1     
  Lines        4786     4751      -35     
==========================================
+ Hits         2052     2128      +76     
+ Misses       2446     2329     -117     
- Partials      288      294       +6     
```


| [Impacted Files](https://codecov.io/gh/knative-sandbox/kn-plugin-func/pull/869?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative-sandbox) | Coverage Δ | |
|---|---|---|
| [cmd/client.go](https://codecov.io/gh/knative-sandbox/kn-plugin-func/pull/869/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative-sandbox#diff-Y21kL2NsaWVudC5nbw==) | `18.86% <0.00%> (+2.47%)` | :arrow_up: |
| [cmd/completion\_util.go](https://codecov.io/gh/knative-sandbox/kn-plugin-func/pull/869/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative-sandbox#diff-Y21kL2NvbXBsZXRpb25fdXRpbC5nbw==) | `0.00% <0.00%> (ø)` | |
| [cmd/run.go](https://codecov.io/gh/knative-sandbox/kn-plugin-func/pull/869/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative-sandbox#diff-Y21kL3J1bi5nbw==) | `61.11% <0.00%> (+2.44%)` | :arrow_up: |
| [docker/pusher.go](https://codecov.io/gh/knative-sandbox/kn-plugin-func/pull/869/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative-sandbox#diff-ZG9ja2VyL3B1c2hlci5nbw==) | `61.98% <0.00%> (-0.32%)` | :arrow_down: |
| [knative/deployer.go](https://codecov.io/gh/knative-sandbox/kn-plugin-func/pull/869/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative-sandbox#diff-a25hdGl2ZS9kZXBsb3llci5nbw==) | `5.56% <0.00%> (-0.02%)` | :arrow_down: |
| [knative/describer.go](https://codecov.io/gh/knative-sandbox/kn-plugin-func/pull/869/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative-sandbox#diff-a25hdGl2ZS9kZXNjcmliZXIuZ28=) | `0.00% <0.00%> (ø)` | |
| [knative/lister.go](https://codecov.io/gh/knative-sandbox/kn-plugin-func/pull/869/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative-sandbox#diff-a25hdGl2ZS9saXN0ZXIuZ28=) | `0.00% <0.00%> (ø)` | |
| [knative/remover.go](https://codecov.io/gh/knative-sandbox/kn-plugin-func/pull/869/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative-sandbox#diff-a25hdGl2ZS9yZW1vdmVyLmdv) | `0.00% <0.00%> (ø)` | |
| [docker/runner.go](https://codecov.io/gh/knative-sandbox/kn-plugin-func/pull/869/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative-sandbox#diff-ZG9ja2VyL3J1bm5lci5nbw==) | `14.61% <50.00%> (ø)` | |
| [cmd/version.go](https://codecov.io/gh/knative-sandbox/kn-plugin-func/pull/869/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative-sandbox#diff-Y21kL3ZlcnNpb24uZ28=) | `66.66% <60.71%> (-33.34%)` | :arrow_down: |
| ... and [4 more](https://codecov.io/gh/knative-sandbox/kn-plugin-func/pull/869/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative-sandbox) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/knative-sandbox/kn-plugin-func/pull/869?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative-sandbox).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative-sandbox)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/knative-sandbox/kn-plugin-func/pull/869?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative-sandbox). Last update [663fd29...eddb917](https://codecov.io/gh/knative-sandbox/kn-plugin-func/pull/869?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative-sandbox). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative-sandbox).

knative-prow-robot: @lkingland: PR needs rebase.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>
knative-prow-robot: @lkingland: PR needs rebase.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>

Patch:
diff --git a/buildpacks/builder.go b/buildpacks/builder.go
index 0612ade0..fb6cbd26 100644
--- a/buildpacks/builder.go
+++ b/buildpacks/builder.go
@@ -24,12 +24,12 @@ import (
 //Builder holds the configuration that will be passed to
 //Buildpack builder
 type Builder struct {
-	Verbose bool
+	verbose bool
 }
 
 //NewBuilder builds the new Builder configuration
-func NewBuilder() *Builder {
-	return &Builder{}
+func NewBuilder(verbose bool) *Builder {
+	return &Builder{verbose: verbose}
 }
 
 var v330 = semver.MustParse("v3.3.0")
@@ -57,7 +57,7 @@ func (builder *Builder) Build(ctx context.Context, f fn.Function) (err error) {
 
 	// log output is either STDOUt or kept in a buffer to be printed on error.
 	var logWriter io.Writer
-	if builder.Verbose {
+	if builder.verbose {
 		// pass stdout as non-closeable writer
 		// otherwise pack client would close it which is bad
 		logWriter = stdoutWrapper{os.Stdout}
@@ -130,7 +130,7 @@ func (builder *Builder) Build(ctx context.Context, f fn.Function) (err error) {
 		if ctx.Err() != nil {
 			// received SIGINT
 			return
-		} else if !builder.Verbose {
+		} else if !builder.verbose {
 			// If the builder was not showing logs, embed the full logs in the error.
 			err = fmt.Errorf("%v\noutput: %s\n", err, logWriter.(*bytes.Buffer).String())
 		}
diff --git a/cmd/client.go b/cmd/client.go
index f2877d92..ed793faf 100644
--- a/cmd/client.go
+++ b/cmd/client.go
@@ -52,11 +52,10 @@ func NewDefaultClientFactory() (newClient ClientFactory, cleanUp func() error) {
 	}
 
 	newClient = func(clientOptions ClientOptions) *fn.Client {
-		builder := buildpacks.NewBuilder()
-		builder.Verbose = clientOptions.Verbose
+		verbose := clientOptions.Verbose
+		builder := buildpacks.NewBuilder(verbose)
 
-		progressListener := progress.New()
-		progressListener.Verbose = clientOptions.Verbose
+		progressListener := progress.New(verbose)
 
 		credentialsProvider := creds.NewCredentialsProvider(
 			creds.WithPromptForCredentials(newPromptForCredentials()),
@@ -64,37 +63,30 @@ func NewDefaultClientFactory() (newClient ClientFactory, cleanUp func() error) {
 			creds.WithTransport(transport),
 			creds.WithAdditionalCredentialLoaders(additionalCredLoaders...))
 
-		pusher := docker.NewPusher(
+		pusher := docker.NewPusher(verbose,
 			docker.WithCredentialsProvider(credentialsProvider),
 			docker.WithProgressListener(progressListener),
 			docker.WithTransport(transport))
-		pusher.Verbose = clientOptions.Verbose
 
-		deployer := knative.NewDeployer(clientOptions.Namespace)
-		deployer.Verbose = clientOptions.Verbose
+		deployer := knative.NewDeployer(clientOptions.Namespace, verbose)
 
-		pipelinesProvider := tekton.NewPipelinesProvider(
+		pipelinesProvider := tekton.NewPipelinesProvider(verbose,
 			tekton.WithNamespace(clientOptions.Namespace),
 			tekton.WithProgressListener(progressListener),
 			tekton.WithCredentialsProvider(credentialsProvider))
-		pipelinesProvider.Verbose = clientOptions.Verbose
 
-		remover := knative.NewRemover(clientOptions.Namespace)
-		remover.Verbose = clientOptions.Verbose
+		remover := knative.NewRemover(clientOptions.Namespace, verbose)
 
-		describer := knative.NewDescriber(clientOptions.Namespace)
-		describer.Verbose = clientOptions.Verbose
+		describer := knative.NewDescriber(clientOptions.Namespace, verbose)
 
-		lister := knative.NewLister(clientOptions.Namespace)
-		lister.Verbose = clientOptions.Verbose
+		lister := knative.NewLister(clientOptions.Namespace, verbose)
 
-		runner := docker.NewRunner()
-		runner.Verbose = clientOptions.Verbose
+		runner := docker.NewRunner(verbose)
 
 		opts := []fn.Option{
 			fn.WithRepository(clientOptions.Repository), // URI of repository override
 			fn.WithRegistry(clientOptions.Registry),
-			fn.WithVerbose(clientOptions.Verbose),
+			fn.WithVerbose(verbose),
 			fn.WithTransport(transport),
 			fn.WithProgressListener(progressListener),
 			fn.WithBuilder(builder),
diff --git a/cmd/completion_util.go b/cmd/completion_util.go
index 4f1943b1..11bf0c82 100644
--- a/cmd/completion_util.go
+++ b/cmd/completion_util.go
@@ -15,7 +15,7 @@ import (
 )
 
 func CompleteFunctionList(cmd *cobra.Command, args []string, toComplete string) (strings []string, directive cobra.ShellCompDirective) {
-	lister := knative.NewLister("")
+	lister := knative.NewLister("", false)
 
 	list, err := lister.List(cmd.Context())
 	if err != nil {
diff --git a/cmd/root.go b/cmd/root.go
index d30a97ac..deb75ce7 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -62,7 +62,7 @@ EXAMPLES
 	// Environment Variables
 	// Evaluated first after static defaults, set all flags to be associated with
 	// a version prefixed by "FUNC_"
-	viper.AutomaticEnv()       // read in environment variables that match
+	viper.AutomaticEnv()       // read in environment variables for FUNC_<flag>
 	viper.SetEnvPrefix("func") // ensure thay all have the prefix
 
 	// Flags
@@ -366,7 +366,7 @@ func (v Version) String() string {
 	// value v0.0.0 as the default indicating there is no version information
 	// available.
 	if strings.HasPrefix(v.Vers, "v") {
-		// TODO: this is the naieve approach, perhaps consider actually parse it
+		// TODO: this is the naive approach, perhaps consider actually parse it
 		// using the semver lib
 		return v.Vers
 	}
diff --git a/cmd/run.go b/cmd/run.go
index 25cb7279..43795a24 100644
--- a/cmd/run.go
+++ b/cmd/run.go
@@ -17,14 +17,11 @@ import (
 
 func newRunClient(cfg runConfig) *fn.Client {
 	bc := newBuildConfig()
-	runner := docker.NewRunner()
-	runner.Verbose = cfg.Verbose
+	runner := docker.NewRunner(cfg.Verbose)
 
 	// builder fields
-	builder := buildpacks.NewBuilder()
-	listener := progress.New()
-	builder.Verbose = cfg.Verbose
-	listener.Verbose = cfg.Verbose
+	builder := buildpacks.NewBuilder(cfg.Verbose)
+	listener := progress.New(cfg.Verbose)
 	return fn.New(
 		fn.WithBuilder(builder),
 		fn.WithProgressListener(listener),
diff --git a/cmd/version.go b/cmd/version.go
index 2d3bcc56..2d08bf36 100644
--- a/cmd/version.go
+++ b/cmd/version.go
@@ -2,7 +2,6 @@ package cmd
 
 import (
 	"fmt"
-	"text/template"
 
 	"github.com/ory/viper"
 	"github.com/spf13/cobra"
@@ -35,13 +34,12 @@ DESCRIPTION
 	}
 
 	// Help Action
-	cmd.SetHelpFunc(runVersionHelp)
+	cmd.SetHelpFunc(defaultTemplatedHelp)
 
 	// Run Action
 	cmd.Run = func(cmd *cobra.Command, args []string) {
 		runVersion(cmd, args, version)
 	}
-	cmd.SetHelpFunc(defaultTemplatedHelp)
 
 	return cmd
 }
@@ -51,22 +49,3 @@ func runVersion(cmd *cobra.Command, args []string, version Version) {
 	version.Verbose = viper.GetBool("verbose")
 	fmt.Fprintf(cmd.OutOrStdout(), "%v\n", version)
 }
-
-// Help
-func runVersionHelp(cmd *cobra.Command, args []string) {
-	var (
-		body = cmd.Long + "\n\n" + cmd.UsageString()
-		t    = template.New("version")
-		tpl  = template.Must(t.Parse(body))
-	)
-
-	var data = struct {
-		Name string
-	}{
-		Name: cmd.Root().Name(),
-	}
-
-	if err := tpl.Execute(cmd.OutOrStdout(), data); err != nil {
-		fmt.Fprintf(cmd.ErrOrStderr(), "unable to display help text: %v", err)
-	}
-}
diff --git a/docker/pusher.go b/docker/pusher.go
index ac693cb2..8e5c8514 100644
--- a/docker/pusher.go
+++ b/docker/pusher.go
@@ -47,8 +47,7 @@ type PusherDockerClientFactory func() (PusherDockerClient, error)
 
 // Pusher of images from local to remote registry.
 type Pusher struct {
-	// Verbose logging.
-	Verbose             bool
+	verbose             bool // verbose logging.
 	credentialsProvider CredentialsProvider
 	progressListener    fn.ProgressListener
 	transport           http.RoundTripper
@@ -84,9 +83,8 @@ func EmptyCredentialsProvider(ctx context.Context, registry string) (Credentials
 }
 
 // NewPusher creates an instance of a docker-based image pusher.
-func NewPusher(opts ...Opt) *Pusher {
+func NewPusher(verbose bool, opts ...Opt) *Pusher {
 	result := &Pusher{
-		Verbose:             false,
 		credentialsProvider: EmptyCredentialsProvider,
 		progressListener:    &fn.NoopProgressListener{},
 		transport:           http.DefaultTransport,
@@ -94,6 +92,7 @@ func NewPusher(opts ...Opt) *Pusher {
 			c, _, err := NewClient(client.DefaultDockerHost)
 			return c, err
 		},
+		verbose: verbose,
 	}
 	for _, opt := range opts {
 		opt(result)
@@ -116,7 +115,7 @@ func (n *Pusher) Push(ctx context.Context, f fn.Function) (digest string, err er
 
 	var output io.Writer
 
-	if n.Verbose {
+	if n.verbose {
 		output = os.Stderr
 	} else {
 		output = io.Discard
diff --git a/docker/runner.go b/docker/runner.go
index 0c368dbb..e686c0f0 100644
--- a/docker/runner.go
+++ b/docker/runner.go
@@ -36,13 +36,12 @@ const (
 
 // Runner starts and stops Functions as local contaieners.
 type Runner struct {
-	// Verbose logging
-	Verbose bool
+	verbose bool // Verbose logging
 }
 
 // NewRunner creates an instance of a docker-backed runner.
-func NewRunner() *Runner {
-	return &Runner{}
+func NewRunner(verbose bool) *Runner {
+	return &Runner{verbose: verbose}
 }
 
 // Run the Function.
@@ -69,7 +68,7 @@ func (n *Runner) Run(ctx context.Context, f fn.Function) (job *fn.Job, err error
 	if c, _, err = NewClient(client.DefaultDockerHost); err != nil {
 		return job, errors.Wrap(err, "failed to create Docker API client")
 	}
-	if id, err = newContainer(ctx, c, f, port, n.Verbose); err != nil {
+	if id, err = newContainer(ctx, c, f, port, n.verbose); err != nil {
 		return job, errors.Wrap(err, "runner unable to create container")
 	}
 	if conn, err = copyStdio(ctx, c, id, copyErrCh); err != nil {
diff --git a/knative/deployer.go b/knative/deployer.go
index b50cf40e..8824bf45 100644
--- a/knative/deployer.go
+++ b/knative/deployer.go
@@ -32,13 +32,14 @@ type Deployer struct {
 	// Namespace with which to override that set on the default configuration (such as the ~/.kube/config).
 	// If left blank, deployment will commence to the configured namespace.
 	Namespace string
-	// Verbose logging enablement flag.
-	Verbose bool
+	// verbose logging enablement flag.
+	verbose bool
 }
 
-func NewDeployer(namespaceOverride string) *Deployer {
+func NewDeployer(namespaceOverride string, verbose bool) *Deployer {
 	return &Deployer{
 		Namespace: namespaceOverride,
+		verbose:   verbose,
 	}
 }
 
@@ -110,7 +111,7 @@ func (d *Deployer) Deploy(ctx context.Context, f fn.Function) (result fn.Deploym
 				return fn.DeploymentResult{}, err
 			}
 
-			if d.Verbose {
+			if d.verbose {
 				fmt.Println("Waiting for Knative Service to become ready")
 			}
 			chprivate := make(chan bool)
@@ -159,7 +160,7 @@ func (d *Deployer) Deploy(ctx context.Context, f fn.Function) (result fn.Deploym
 				return fn.DeploymentResult{}, err
 			}
 
-			if d.Verbose {
+			if d.verbose {
 				fmt.Println("Function deployed at URL: " + route.Status.URL.String())
 			}
 			return fn.DeploymentResult{
diff --git a/knative/describer.go b/knative/describer.go
index d1105be3..22e81df2 100644
--- a/knative/describer.go
+++ b/knative/describer.go
@@ -12,13 +12,14 @@ import (
 )
 
 type Describer struct {
-	Verbose   bool
 	namespace string
+	verbose   bool
 }
 
-func NewDescriber(namespaceOverride string) *Describer {
+func NewDescriber(namespaceOverride string, verbose bool) *Describer {
 	return &Describer{
 		namespace: namespaceOverride,
+		verbose:   verbose,
 	}
 }
 
diff --git a/knative/lister.go b/knative/lister.go
index 5f908083..167eaef6 100644
--- a/knative/lister.go
+++ b/knative/lister.go
@@ -13,13 +13,14 @@ import (
 )
 
 type Lister struct {
-	Verbose   bool
 	Namespace string
+	verbose   bool
 }
 
-func NewLister(namespaceOverride string) *Lister {
+func NewLister(namespaceOverride string, verbose bool) *Lister {
 	return &Lister{
 		Namespace: namespaceOverride,
+		verbose:   verbose,
 	}
 }
 
diff --git a/knative/remover.go b/knative/remover.go
index c26dfc5d..ad516f88 100644
--- a/knative/remover.go
+++ b/knative/remover.go
@@ -10,15 +10,16 @@ import (
 
 const RemoveTimeout = 120 * time.Second
 
-func NewRemover(namespaceOverride string) *Remover {
+func NewRemover(namespaceOverride string, verbose bool) *Remover {
 	return &Remover{
 		Namespace: namespaceOverride,
+		verbose:   verbose,
 	}
 }
 
 type Remover struct {
 	Namespace string
-	Verbose   bool
+	verbose   bool
 }
 
 func (remover *Remover) Remove(ctx context.Context, name string) (err error) {
diff --git a/pipelines/tekton/pipeplines_provider.go b/pipelines/tekton/pipeplines_provider.go
index 5d048c12..3530fca7 100644
--- a/pipelines/tekton/pipeplines_provider.go
+++ b/pipelines/tekton/pipeplines_provider.go
@@ -30,7 +30,7 @@ type PipelinesProvider struct {
 	// namespace with which to override that set on the default configuration (such as the ~/.kube/config).
 	// If left blank, pipeline creation/run will commence to the configured namespace.
 	namespace           string
-	Verbose             bool
+	verbose             bool
 	progressListener    fn.ProgressListener
 	credentialsProvider docker.CredentialsProvider
 }
@@ -53,8 +53,8 @@ func WithCredentialsProvider(credentialsProvider docker.CredentialsProvider) Opt
 	}
 }
 
-func NewPipelinesProvider(opts ...Opt) *PipelinesProvider {
-	pp := &PipelinesProvider{}
+func NewPipelinesProvider(verbose bool, opts ...Opt) *PipelinesProvider {
+	pp := &PipelinesProvider{verbose: verbose}
 
 	for _, opt := range opts {
 		opt(pp)
diff --git a/progress/progress.go b/progress/progress.go
index ddc07e0d..2b77c243 100644
--- a/progress/progress.go
+++ b/progress/progress.go
@@ -43,7 +43,7 @@ type Bar struct {
 
 	// verbose mode disables progress spinner and line overwrites, instead
 	// printing single, full line updates.
-	Verbose bool
+	verbose bool
 
 	// print verbose-style updates even when not attached to an interactive terminal.
 	printWhileHeadless bool
@@ -78,9 +78,10 @@ func WithPrintStepCounter(s bool) Option {
 	}
 }
 
-func New(options ...Option) *Bar {
+func New(verbose bool, options ...Option) *Bar {
 	b := &Bar{
-		out: os.Stdout,
+		out:     os.Stdout,
+		verbose: verbose,
 	}
 	for _, o := range options {
 		o(b)
@@ -110,7 +111,7 @@ func (b *Bar) Increment(text string) {
 	}
 
 	// If we're in verbose mode, do a simple write
-	if b.Verbose {
+	if b.verbose {
 		b.write()
 		return
 	}
@@ -144,7 +145,7 @@ func (b *Bar) Complete(text string) {
 	}
 
 	// If we're interactive, but in verbose mode do a simple write
-	if b.Verbose {
+	if b.verbose {
 		b.write()
 		return
 	}
@@ -213,7 +214,7 @@ func (b *Bar) String() string {
 
 // Write a spinner at the beginning of the previous line.
 func (b *Bar) spin(ch <-chan time.Time) {
-	if b.Verbose {
+	if b.verbose {
 		return
 	}
 	// Various options for spinners.


Test patch:
diff --git a/client_int_test.go b/client_int_test.go
index fe1f8034..73139ece 100644
--- a/client_int_test.go
+++ b/client_int_test.go
@@ -59,7 +59,7 @@ func TestList(t *testing.T) {
 	verbose := true
 
 	// Assemble
-	lister := knative.NewLister(DefaultNamespace)
+	lister := knative.NewLister(DefaultNamespace, verbose)
 
 	client := fn.New(
 		fn.WithLister(lister),
@@ -203,20 +203,11 @@ func TestRemoteRepositories(t *testing.T) {
 // newClient creates an instance of the func client whose concrete impls
 // match those created by the kn func plugin CLI.
 func newClient(verbose bool) *fn.Client {
-	builder := buildpacks.NewBuilder()
-	builder.Verbose = verbose
-
-	pusher := docker.NewPusher()
-	pusher.Verbose = verbose
-
-	deployer := knative.NewDeployer(DefaultNamespace)
-	deployer.Verbose = verbose
-
-	remover := knative.NewRemover(DefaultNamespace)
-	remover.Verbose = verbose
-
-	lister := knative.NewLister(DefaultNamespace)
-	lister.Verbose = verbose
+	builder := buildpacks.NewBuilder(verbose)
+	pusher := docker.NewPusher(verbose)
+	deployer := knative.NewDeployer(DefaultNamespace, verbose)
+	remover := knative.NewRemover(DefaultNamespace, verbose)
+	lister := knative.NewLister(DefaultNamespace, verbose)
 
 	return fn.New(
 		fn.WithRegistry(DefaultRegistry),
diff --git a/docker/pusher_test.go b/docker/pusher_test.go
index 5b73fed9..2a6dd190 100644
--- a/docker/pusher_test.go
+++ b/docker/pusher_test.go
@@ -105,7 +105,7 @@ func TestDaemonPush(t *testing.T) {
 	dockerClientFactory := func() (docker.PusherDockerClient, error) {
 		return dockerClient, nil
 	}
-	pusher := docker.NewPusher(
+	pusher := docker.NewPusher(false,
 		docker.WithCredentialsProvider(testCredProvider),
 		docker.WithPusherDockerClientFactory(dockerClientFactory))
 
@@ -182,7 +182,7 @@ func TestNonDaemonPush(t *testing.T) {
 		return dockerClient, nil
 	}
 
-	pusher := docker.NewPusher(
+	pusher := docker.NewPusher(false,
 		docker.WithTransport(transport),
 		docker.WithCredentialsProvider(testCredProvider),
 		docker.WithPusherDockerClientFactory(dockerClientFactory))
diff --git a/docker/runner_test.go b/docker/runner_test.go
index 20b9a5f3..f39d03ec 100644
--- a/docker/runner_test.go
+++ b/docker/runner_test.go
@@ -35,8 +35,7 @@ func TestDockerRun(t *testing.T) {
 
 	// NOTE: test requires that the image be built already.
 
-	runner := docker.NewRunner()
-	runner.Verbose = true
+	runner := docker.NewRunner(true)
 	if _, err = runner.Run(context.Background(), f); err != nil {
 		t.Fatal(err)
 	}
@@ -48,7 +47,7 @@ func TestDockerRun(t *testing.T) {
 }
 
 func TestDockerRunImagelessError(t *testing.T) {
-	runner := docker.NewRunner()
+	runner := docker.NewRunner(true)
 	f := fn.NewFunctionWith(fn.Function{})
 
 	_, err := runner.Run(context.Background(), f)
diff --git a/filesystem_test.go b/filesystem_test.go
index d99cbcab..52f86289 100644
--- a/filesystem_test.go
+++ b/filesystem_test.go
@@ -145,7 +145,7 @@ func TestFileSystems(t *testing.T) {
 					return nil
 				}
 
-				if runtime.GOOS != "windows" && embeddedFileStats.Mode().Perm() != localFileStat.Mode().Perm() {
+				if runtime.GOOS != "windows" && (embeddedFileStats.Mode().Perm()&0100) != (localFileStat.Mode().Perm()&0100) {
 					t.Errorf("mode mismatch on %q (expected: %o, actual: %o)",
 						path, localFileStat.Mode().Perm(), embeddedFileStats.Mode().Perm())
 					return nil