<commit_before>package try

import (
	"bufio"
	"context"
	"fmt"
	"os"
	"regexp"
	"sort"
	"strings"

	"github.com/urfave/cli/v2"

	"go.skia.org/infra/go/exec"
	"go.skia.org/infra/go/gerrit"
	"go.skia.org/infra/go/httputils"
	"go.skia.org/infra/go/repo_root"
	"go.skia.org/infra/go/skerr"
	"go.skia.org/infra/task_scheduler/go/specs"
)

// Command returns a cli.Command instance which represents the "try" command.
func Command() *cli.Command {
	return &cli.Command{
		Name:        "try",
		Usage:       "try [job name or regex]...",
		Description: "Run try jobs against the active CL",
		Action: func(ctx *cli.Context) error {
			return try(ctx.Context, ctx.Args().Slice())
		},
	}
}

// try loads the available try jobs, filters by the given request strings, and
// triggers the try jobs selected by the user.
func try(ctx context.Context, jobRequests []string) error {
	// Setup.
	if err := fixupIssue(ctx); err != nil {
		return err
	}
	jobs, err := getTryJobs(ctx)
	if err != nil {
		return err
	}

	// Filter by the given requested job names/regexes.
	filteredJobs := jobs
	if len(jobRequests) > 0 {
		jobRegexes := make([]*regexp.Regexp, 0, len(jobRequests))
		for _, jobRequest := range jobRequests {
			jobRegex, err := regexp.Compile(jobRequest)
			if err != nil {
				return err
			}
			jobRegexes = append(jobRegexes, jobRegex)
		}
		filteredJobs = map[string][]string{}
		for bucket, jobList := range jobs {
			for _, job := range jobList {
				for _, jobRegex := range jobRegexes {
					if jobRegex.MatchString(job) {
						filteredJobs[bucket] = append(filteredJobs[bucket], job)
						break
					}
				}
			}
		}
	}

	// Prompt the user before triggering jobs.
	count := 0
	for _, jobList := range filteredJobs {
		count += len(jobList)
		sort.Strings(jobList)
	}
	if count == 0 {
		return skerr.Fmt("Found no jobs matching %v", jobRequests)
	}
	fmt.Println(fmt.Sprintf("Found %d jobs:", count))
	for bucket, jobList := range filteredJobs {
		fmt.Println(fmt.Sprintf("  %s:", bucket))
		for _, job := range jobList {
			fmt.Println(fmt.Sprintf("    %s", job))
		}
	}
	if len(jobRequests) == 0 || count == 0 {
		return nil
	}
	fmt.Printf("Do you want to trigger these jobs? (y/n or i for interactive): ")
	read, err := bufio.NewReader(os.Stdin).ReadString('\n')
	if err != nil {
		return err
	}
	read = strings.TrimSpace(read)
	if read != "y" && read != "i" {
		return nil
	}
	jobsToTrigger := filteredJobs
	if read == "i" {
		jobsToTrigger = map[string][]string{}
		for bucket, jobList := range filteredJobs {
			for _, job := range jobList {
				fmt.Printf("Trigger %s? (y/n): ", job)
				trigger, err := bufio.NewReader(os.Stdin).ReadString('\n')
				if err != nil {
					return err
				}
				if strings.TrimSpace(trigger) == "y" {
					jobsToTrigger[bucket] = append(jobsToTrigger[bucket], job)
				}
			}
		}
	}

	// Trigger the try jobs.
	for bucket, jobList := range jobsToTrigger {
		cmd := []string{"git", "cl", "try", "-B", bucket}
		for _, job := range jobList {
			cmd = append(cmd, "-b", job)
		}
		if _, err := exec.RunCwd(ctx, ".", cmd...); err != nil {
			return err<commit_msg>[sk] Add -y flag to avoid interactive prompt<commit_after>package try

import (
	"bufio"
	"context"
	"fmt"
	"os"
	"regexp"
	"sort"
	"strings"

	"github.com/urfave/cli/v2"

	"go.skia.org/infra/go/exec"
	"go.skia.org/infra/go/gerrit"
	"go.skia.org/infra/go/httputils"
	"go.skia.org/infra/go/repo_root"
	"go.skia.org/infra/go/skerr"
	"go.skia.org/infra/task_scheduler/go/specs"
)

// Command returns a cli.Command instance which represents the "try" command.
func Command() *cli.Command {
	yFlag := "y"
	return &cli.Command{
		Name:        "try",
		Usage:       "try [-y] [job name or regex]...",
		Description: "Run try jobs against the active CL",
		Flags: []cli.Flag{
			&cli.BoolFlag{
				Name:  yFlag,
				Value: false,
				Usage: "Trigger all matching try jobs without asking for confirmation.",
			},
		},
		Action: func(ctx *cli.Context) error {
			return try(ctx.Context, ctx.Args().Slice(), ctx.Bool(yFlag))
		},
	}
}

// try loads the available try jobs, filters by the given request strings, and
// triggers the try jobs selected by the user.
func try(ctx context.Context, jobRequests []string, triggerWithoutPrompt bool) error {
	// Setup.
	if err := fixupIssue(ctx); err != nil {
		return err
	}
	jobs, err := getTryJobs(ctx)
	if err != nil {
		return err
	}

	// Filter by the given requested job names/regexes.
	filteredJobs := jobs
	if len(jobRequests) > 0 {
		jobRegexes := make([]*regexp.Regexp, 0, len(jobRequests))
		for _, jobRequest := range jobRequests {
			jobRegex, err := regexp.Compile(jobRequest)
			if err != nil {
				return err
			}
			jobRegexes = append(jobRegexes, jobRegex)
		}
		filteredJobs = map[string][]string{}
		for bucket, jobList := range jobs {
			for _, job := range jobList {
				for _, jobRegex := range jobRegexes {
					if jobRegex.MatchString(job) {
						filteredJobs[bucket] = append(filteredJobs[bucket], job)
						break
					}
				}
			}
		}
	}

	// Prompt the user before triggering jobs.
	count := 0
	for _, jobList := range filteredJobs {
		count += len(jobList)
		sort.Strings(jobList)
	}
	if count == 0 {
		return skerr.Fmt("Found no jobs matching %v", jobRequests)
	}
	fmt.Println(fmt.Sprintf("Found %d jobs:", count))
	for bucket, jobList := range filteredJobs {
		fmt.Println(fmt.Sprintf("  %s:", bucket))
		for _, job := range jobList {
			fmt.Println(fmt.Sprintf("    %s", job))
		}
	}
	if len(jobRequests) == 0 || count == 0 {
		return nil
	}

	jobsToTrigger := filteredJobs
	if !triggerWithoutPrompt {
		fmt.Printf("Do you want to trigger these jobs? (y/n or i for interactive): ")
		read, err := bufio.NewReader(os.Stdin).ReadString('\n')
		if err != nil {
			return err
		}
		read = strings.TrimSpace(read)
		if read != "y" && read != "i" {
			return nil
		}
		if read == "i" {
			jobsToTrigger = map[string][]string{}
			for bucket, jobList := range filteredJobs {
				for _, job := range jobList {
					fmt.Printf("Trigger %s? (y/n): ", job)
					trigger, err := bufio.NewReader(os.Stdin).ReadString('\n')
					if err != nil {
						return err
					}
					if strings.TrimSpace(trigger) == "y" {
						jobsToTrigger[bucket] = append(jobsToTrigger[bucket], job)
					}
				}
			}
		}
	}

	// Trigger the try jobs.
	for bucket, jobList := range jobsToTrigger {
		cmd := []string{"git", "cl", "try", "-B", bucket}
		for _, job := range jobList {
			cmd = append(cmd, "-b", job)
		}
		if _, err := exec.RunCwd(ctx, ".", cmd...); err != nil {
			return err
{'fraction_non_alphanumeric': 0.15366937337214648, 'fraction_numerical': 0.002144936417956182, 'mean_word_length': 3.1082441787287602, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, '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': '2175549', 'n_tokens_mistral': 2647, 'n_tokens_neox': 2347, 'n_words': 621}