<commit_before>/*
Copyright 2019 The Kubernetes 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 app

import (
	"fmt"
	"os"
	"strings"

	"github.com/pkg/errors"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"

	"k8s.io/test-infra/kubetest2/pkg/app/shim"
	"k8s.io/test-infra/kubetest2/pkg/app/testers"
	"k8s.io/test-infra/kubetest2/pkg/types"
)

// Run instantiates and executes the kubetest2 cobra command, returning the result
func Run(deployerName string, newDeployer types.NewDeployer) error {
	return NewCommand(deployerName, newDeployer).Execute()
}

// NewCommand returns a new cobra.Command for kubetest2
func NewCommand(deployerName string, newDeployer types.NewDeployer) *cobra.Command {
	cmd := &cobra.Command{
		Use: fmt.Sprintf("%s %s", shim.BinaryName, deployerName),
		// we defer showing usage, so that we can include deployer and test
		// specific usage in RealMain(...)
		SilenceUsage:  true,
		SilenceErrors: true,
		RunE: func(cmd *cobra.Command, args []string) error {
			return runE(cmd, args, deployerName, newDeployer)
		},
	}
	// we implement custom flag parsing below
	cmd.DisableFlagParsing = true
	return cmd
}

// runE implements the custom CLI logic
func runE(
	cmd *cobra.Command, args []string,
	deployerName string, newDeployer types.NewDeployer,
) error {
	// setup the options struct & flags, etc.
	opts := &options{}
	kubetest2Flags := pflag.NewFlagSet(deployerName, pflag.ContinueOnError)
	opts.bindFlags(kubetest2Flags)

	// NOTE: unknown flags are forwarded to the deployer as arguments
	kubetest2Flags.ParseErrorsWhitelist.UnknownFlags = true

	// parse arguments, splitting out test args (after the `--`)
	deployerArgs, testerArgs := splitArgs(args)

	// setup usage metadata for deffered usage printing
	usage := &usage{
		deployerName:   deployerName,
		kubetest2Flags: kubetest2Flags,
	}

	// parse the kubetest2 common flags flags
	// NOTE: parseError should contain the first error from parsing.
	// We will later show this + usage if there is one
	parseError := kubetest2Flags.Parse(deployerArgs)

	// now that we've parsed flags we can look up the tester
	var newTester types.NewTester
	if opts.test != "" {
		n, testerUsage, ok := testers.Get(opts.test)
		newTester = n

		// if the tester exists, record usage info
		if ok {
			usage.testerUsage = testerUsage
			usage.testerName = opts.test
		} else if parseError == nil {
			// otherwise fail if the named tester does not exist
			// NOTE: we only retain the first parse error currently, and handle below
			// TODO(bentheelder): inform the user which testers exist
			parseError = errors.Errorf("no such tester: %#v", opts.test)
		}
	}

	// instantiate the deployer
	deployer, deployerFlags := newDeployer(opts)

	// capture deployer flags for usage
	usage.deployerFlags = deployerFlags

	// parse the combined deployer flags and kubetest2 flags
	allFlags := pflag.NewFlagSet(deployerName, pflag.ContinueOnError)
	allFlags.AddFlagSet(deployerFlags)
	allFlags.AddFlagSet(kubetest2Flags)
	if err := allFlags.Parse(deployerArgs); err != nil {
		// NOTE: we only retain the first parse error currently, and handle below
		if err != nil && parseError == nil {
			parseError = err
		}
	}

	// instantiate the tester if testing was specified
	// NOTE: we need to do this before building etc, so we can fail fast on
	// invalid options to the tester
	var tester types.Tester
	if newTester != nil {
		t, err := newTester(opts, testerArgs, deployer)
		tester = t

		// NOTE: we only retain the first parse error currently, and handle below
		if err != nil && parseError == nil {
			parseError = err
		}
	}<commit_msg>sanity check that flags are not duplicately registered<commit_after>/*
Copyright 2019 The Kubernetes 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 app

import (
	"fmt"
	"os"
	"strings"

	"github.com/pkg/errors"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"

	"k8s.io/test-infra/kubetest2/pkg/app/shim"
	"k8s.io/test-infra/kubetest2/pkg/app/testers"
	"k8s.io/test-infra/kubetest2/pkg/types"
)

// Run instantiates and executes the kubetest2 cobra command, returning the result
func Run(deployerName string, newDeployer types.NewDeployer) error {
	return NewCommand(deployerName, newDeployer).Execute()
}

// NewCommand returns a new cobra.Command for kubetest2
func NewCommand(deployerName string, newDeployer types.NewDeployer) *cobra.Command {
	cmd := &cobra.Command{
		Use: fmt.Sprintf("%s %s", shim.BinaryName, deployerName),
		// we defer showing usage, so that we can include deployer and test
		// specific usage in RealMain(...)
		SilenceUsage:  true,
		SilenceErrors: true,
		RunE: func(cmd *cobra.Command, args []string) error {
			return runE(cmd, args, deployerName, newDeployer)
		},
	}
	// we implement custom flag parsing below
	cmd.DisableFlagParsing = true
	return cmd
}

// runE implements the custom CLI logic
func runE(
	cmd *cobra.Command, args []string,
	deployerName string, newDeployer types.NewDeployer,
) error {
	// setup the options struct & flags, etc.
	opts := &options{}
	kubetest2Flags := pflag.NewFlagSet(deployerName, pflag.ContinueOnError)
	opts.bindFlags(kubetest2Flags)

	// NOTE: unknown flags are forwarded to the deployer as arguments
	kubetest2Flags.ParseErrorsWhitelist.UnknownFlags = true

	// parse arguments, splitting out test args (after the `--`)
	deployerArgs, testerArgs := splitArgs(args)

	// setup usage metadata for deffered usage printing
	usage := &usage{
		deployerName:   deployerName,
		kubetest2Flags: kubetest2Flags,
	}

	// parse the kubetest2 common flags flags
	// NOTE: parseError should contain the first error from parsing.
	// We will later show this + usage if there is one
	parseError := kubetest2Flags.Parse(deployerArgs)

	// now that we've parsed flags we can look up the tester
	var newTester types.NewTester
	if opts.test != "" {
		n, testerUsage, ok := testers.Get(opts.test)
		newTester = n

		// if the tester exists, record usage info
		if ok {
			usage.testerUsage = testerUsage
			usage.testerName = opts.test
		} else if parseError == nil {
			// otherwise fail if the named tester does not exist
			// NOTE: we only retain the first parse error currently, and handle below
			// TODO(bentheelder): inform the user which testers exist
			parseError = errors.Errorf("no such tester: %#v", opts.test)
		}
	}

	// instantiate the deployer
	deployer, deployerFlags := newDeployer(opts)

	// capture deployer flags for usage
	usage.deployerFlags = deployerFlags

	// sanity check that the deployer did not register any identical flags
	deployerFlags.VisitAll(func(f *pflag.Flag) {
		if kubetest2Flags.Lookup(f.Name) != nil {
			panic(errors.Errorf("kubetest2 common flag %#v re-registered by deployer", f.Name))
		}
		if f.Shorthand != "" && kubetest2Flags.ShorthandLookup(f.Shorthand) != nil {
			panic(errors.Errorf("kubetest2 common shorthand flag %#v re-registered by deployer", f.Shorthand))
		}
	})

	// parse the combined deployer flags and kubetest2 flags
	allFlags := pflag.NewFlagSet(deployerName, pflag.ContinueOnError)
	allFlags.AddFlagSet(kubetest2Flags)
	allFlags.AddFlagSet(deployerFlags)
	if err := allFlags.Parse(deployerArgs); err != nil {
		// NOTE: we only retain the first parse error currently, and handle below
		if err != nil && parseError == nil {
			parseError = err
		}
	}

	// instantiate the tester if testing was specified
	// NOTE: we need to do this before building etc, so we can fail fast on
	// invalid options to the tester
	var tester types.Tester
	if newTester != nil {
		t, err := newTester(opts, testerArgs, deployer)
		tester = t

		// NOTE: we only retain the first parse error currently, and handle below
		if err != nil && parseError == nil {
			parseError = err
		}
	}
{'fraction_non_alphanumeric': 0.0932690098782449, 'fraction_numerical': 0.007121525384792098, 'mean_word_length': 3.986827033218786, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4789444', 'n_tokens_mistral': 2986, 'n_tokens_neox': 2746, 'n_words': 946}