<commit_before>// Copyright © 2016 Theotime LEVEQUE theotime@protonmail.com
//
// 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 cmd

import (
	"encoding/json"
	"fmt"
	"os"
	"os/exec"
	"strings"

	"github.com/fatih/color"
	"github.com/spf13/cobra"
	"github.com/thylong/ian/backend/command"
	"github.com/thylong/ian/backend/config"
	"github.com/thylong/ian/backend/projects"
	"github.com/thylong/ian/backend/repo"
)

var customCmdDescription string
var customCmdName string
var softDeleteCmdParam bool

func init() {
	RootCmd.AddCommand(projectCmd)

	for pName, pCmd := range config.GetProjects() {
		projectCmd.AddCommand(pCmd)
		projectCmd.AddCommand(addProjectCmd())

		deleteProjectCmd := deleteProjectCmd()
		setProjectCmd := setProjectCmd()

		deleteProjectCmd.Flags().BoolVar(&softDeleteCmdParam, "soft", false, "Delete only ian configuration but keeps history")
		setProjectCmd.Flags().StringVarP(&customCmdDescription, "description", "d", "", "Description of the custom project command (40 characters maximum)")
		setProjectCmd.Flags().StringVarP(&customCmdName, "command", "c", "", "The custom command")

		pCmd.AddCommand(
			statusProjectCmd(),
			statsProjectCmd(),
			configProjectCmd(),
			deployProjectCmd(),
			rollbackProjectCmd(),
			dbProjectCmd(),
			addProjectCmd(),
			deleteProjectCmd,
			unsetProjectCmd(),
			cloneCmd(),
			setProjectCmd,
		)

		for _, customCmd := range config.GetCustomCmds(pName) {
			pCmd.AddCommand(customCmd)
		}
	}
}

// projectCmd represents the project command
var projectCmd = &cobra.Command{
	Use:   "project",
	Short: "Interact with local projects",
	Long:  `Interact with a project using predefined commands, or define custom commands.`,
	Run: func(cmd *cobra.Command, args []string) {
		if len(config.Vipers["projects"].AllSettings()) == 0 {
			fmt.Println("/!\\ You currently have no projects set up.")
			in := config.GetBoolUserInput("Would you like to add one ? (Y/n)")
			if !in {
				return
			}
			addProjectCmd().Execute()
		}
		cmd.Usage()
	},
}

func statusProjectCmd() *cobra.Command {
	return &cobra.Command{
		Use:   "status",
		Short: "Display health status",
		Long:  `Display health status.`,
		Run: func(cmd *cobra.Command, args []string) {
			baseURL, ok := config.Vipers["projects"].GetStringMapString(cmd.Parent().Use)["url"]
			if !ok {
				baseURL = config.GetUserInput("Enter the project URL (example: http://goian.io)")
			}
			healthEndpoint, ok := config.Vipers["projects"].GetStringMapString(cmd.Parent().Use)["health"]
			if !ok {
				healthEndpoint = config.GetUserInput("Enter the health check relative URL (example: /status)")
			}
			status := projects.Status(cmd.Parent().Use, baseURL, healthEndpoint)
			fmt.Printf("%v", status)

			repo.Status(cmd.Parent().Use)
		},
	}
}

func statsProjectCmd() *cobra.Command {
	return &cobra.Command{
		Use:   "stats",
		Short: "Display Github stat's for a project",
		Long:  `Display Github stat's for a project.`,
		Run: func(cmd *cobra.Command, args []string) {
			repositoryURL := fmt.Sprintf(
				"https://api.github.com/repos/%s",
				config.Vipers["projects"].GetStringMapString(cmd.Parent().Use)["repository"],
			)
			stats, _ := projects.Stats(cmd.Parent().Use, repositoryURL)
			prettyStats, _ := json.MarshalIndent(stats, "", "  ")
			fmt.Printf("%s", prettyStats)
		},
	}
}

func configProjectCmd() *cobra.Command {
	return &cobra.Command{
		Use:   "config",
		Short: "Display a project's configuration",
		Long:  `Display a project's configuration.`,
		Run: func(cmd *cobra.Command, args []string) {
			settings := config.Vipers["projects"].GetStringMap(cmd.Parent().Use)
			prettySettings, _ := json.MarshalIndent(settings, "", "  ")
			fmt.Printf("%s configuration:\n%s\n}", cmd.Parent().Use, prettySettings)
		},
	}
}

func deployProjectCmd() *cobra.Command {
	return &cobra.Command{
		Use:   "deploy",
		Short: "Deploy a new version of a project",
		Long:  `Deploy a new version of a project.`,
		Run: func(cmd *cobra.Command, args []string) {
			deployCmdContent := config.Vipers["projects"].GetStringMapString(cmd.Parent().Use)["deploy_cmd"]
			deployCmd := strings.Split(deployCmdContent, " ")
			termCmd := exec.Command(deployCmd[0], deployCmd[1:]...)
			termCmd.Dir = config.Vipers["config"].GetString("repositories_path")
			command.ExecuteInteractiveCommand(termCmd)
		},
	}
}

func rollbackProjectCmd() *cobra.Command {
	return &cobra.Command{
		Use:   "rollback",
		Short: "Rollback to a previous version of a project",
		Long:  `Rollback to a previous version of a project.`,
		Run: func(cmd *cobra.Command, args []string) {
			rollbackCmdContent := config.Vipers["projects"].GetStringMapString(cmd.Parent().Use)["rollback_cmd"]
			rollbackCmd := strings.Split(rollbackCmdContent, " ")
			termCmd := exec.Command(rollbackCmd[0], rollbackCmd[:1]...)
			termCmd.Dir = config.Vipers["config"].GetString("repositories_path")
			command.ExecuteInteractiveCommand(termCmd)
		},
	}
}

func dbProjectCmd() *cobra.Command {
	return &cobra.Command{
		Use:   "db",
		Short: "Connect to the project's database",
		Long:  `Connect to the project's database.`,
		Run: func(cmd *cobra.Command, args []string) {
			cmdContent := config.Vipers["projects"].GetStringMapString(cmd.Parent().Use)["db_cmd"]
			cmdContent = strings.Replace(cmdContent, "\"", "", -1)
			dbCmd := strings.Split(cmdContent, " ")

			termCmd := exec.Command(dbCmd[0], dbCmd[1:]...)
			termCmd.Dir = config.Vipers["config"].GetString("repositories_path")
			command.ExecuteInteractiveCommand(termCmd)
		},
	}
}

func addProjectCmd() *cobra.Command {
	return &cobra.Command{
		Use:   "add",
		Short: "Add a new project configuration",
		Long:  `Add a new project configuration.`,
		Run: func(cmd *cobra.Command, args []string) {
			NewP := make(map[string]string)
			projectName := config.GetUserInput("Enter the name of the project")
			NewP["description"] = config.GetUserInput("Enter the project description")
			NewP["url"] = config.GetUserInput("Enter the project URL (example: http://goian.io)")
			s := config.GetUserInput("Enter the project repository (example: thylong/ian)")
			NewP["repository"] = strings.TrimPrefix(strings.TrimSuffix(s, ".git"), "git@github.com:")
			NewP["health"] = config.GetUserInput("Enter the health check relative URL (example: /status)")
			NewP["db_cmd"] = config.GetUserInput("Enter the db connection command (example: mongo localhost)")
			NewP["deploy_cmd"] = config.GetUserInput("Enter the deploy command (example: bash deploy.sh)")
			NewP["rollback_cmd"] = config.GetUserInput("Enter the rollback command (example: bash rollback.sh)")

			projectsContent := config.Vipers["projects"].AllSettings()
			projectsContent[projectName] = NewP
			config.UpdateYamlFile(
				config.ConfigFilesPathes["projects"],
				projectsContent,
			)
		},
	}
}

func deleteProjectCmd() *cobra.Command {
	return &cobra.Command{
		Use:   "delete",
		Short: "Delete a project configuration",
		Long:  `Delete a project configuration.`,
		Run: func(cmd *cobra.Command, args []string) {
			if !softDeleteCmdParam {
				repo.Remove(cmd.Parent().Use)
			}

			editContent := config.Vipers["projects"].AllSettings()
			delete(editContent, cmd.Parent().Use)

			config.UpdateYamlFile(
				config.ConfigFilesPathes["projects"],
				config.Vipers["projects"].AllSettings(),
			)
		},
	}
}

func setProjectCmd() *cobra.Command {
	return &cobra.Command{
		Use:   "set",
		Short: "Define a command for the project",
		Long: `Define a command for the project

Example: ian project dotfiles set -c bonjour -d "Say bonjour" echo bonjour !.`,
		Run: func(cmd *cobra.Command, args []string) {
			if len(args) < 2 && (customCmdName == "" || customCmdDescription == "") {
				customCmdName = config.GetUserInput("Enter the name of the command")
				customCmdDescription = config.GetUserInput("Enter the description of the project")
			}
			if len(customCmdDescription) < 5 || len(customCmdDescription) > 40 {
				fmt.Fprintf(os.Stderr, "%v Description must be between 5 and 40 alphanumeric characters.\n\n", color.RedString("Error:"))
				cmd.Usage()
				os.Exit(1)
			}

			editContent := config.Vipers["projects"].GetStringMapString(cmd.Parent().Use)
			editContent[fmt.Sprintf("%s_custom_cmd", customCmdName)] = fmt.Sprintf("%s=%s", customCmdDescription, strings.Join(args, " "))
			config.Vipers["projects"].Set(cmd.Parent().Use, editContent)

			projectsContent := config.Vipers["projects"].AllSettings()
			config.UpdateYamlFile(
				config.ConfigFilesPathes["projects"],
				projectsContent,
			)
		},
	}
}

func cloneCmd() *cobra.Command {
	return &cobra.Command{
		Use:   "clone",
		Short: "Clone project repository",
		Long: `Clone the project repository.

Example: ian project dotfiles unset bonjour -d "Say bonjour" echo bonjour !.`,
		Run: func(cmd *cobra.Command, args []string) {
			editContent := config.Vipers["projects"].GetStringMapString(cmd.Parent().Use)
			if projectRepository, ok := editContent["repository"]; ok {
				repo.Clone(repo.GetGitRepositorySSHPath(projectRepository))
			}
		},
	}
}

func unsetProjectCmd() *cobra.Command {
	return &cobra.Command{
		Use:   "unset",
		Short: "Remove a project command",
		Long:  `Remove a project subcommand.`,
		Run: func(cmd *cobra.Command, args []string) {
			if len(args) < 1 {
				args[0] = config.GetUserInput("Enter the name of the command to unset")
			}

			editContent := config.Vipers["projects"].GetStringMapString(cmd.Parent().Use)
			delete(editContent, fmt.Sprintf("%s_cmd", args[0]))

			config.UpdateYamlFile(
				config.ConfigFilesPathes["projects"],
				config.Vipers["projects"].AllSettings(),
			)
		},
	}
}
<commit_msg>Projects: Fix cmd typo.<commit_after>// Copyright © 2016 Theotime LEVEQUE theotime@protonmail.com
//
// 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 cmd

import (
	"encoding/json"
	"fmt"
	"os"
	"os/exec"
	"strings"

	"github.com/fatih/color"
	"github.com/spf13/cobra"
	"github.com/thylong/ian/backend/command"
	"github.com/thylong/ian/backend/config"
	"github.com/thylong/ian/backend/projects"
	"github.com/thylong/ian/backend/repo"
)

var customCmdDescription string
var customCmdName string
var softDeleteCmdParam bool

func init() {
	RootCmd.AddCommand(projectCmd)
	projectCmd.AddCommand(addProjectCmd())

	for pName, pCmd := range config.GetProjects() {
		projectCmd.AddCommand(pCmd)

		deleteProjectCmd := deleteProjectCmd()
		setProjectCmd := setProjectCmd()

		deleteProjectCmd.Flags().BoolVar(&softDeleteCmdParam, "soft", false, "Delete only ian configuration but keeps history")
		setProjectCmd.Flags().StringVarP(&customCmdDescription, "description", "d", "", "Description of the custom project command (40 characters maximum)")
		setProjectCmd.Flags().StringVarP(&customCmdName, "command", "c", "", "The custom command")

		pCmd.AddCommand(
			statusProjectCmd(),
			statsProjectCmd(),
			configProjectCmd(),
			deployProjectCmd(),
			rollbackProjectCmd(),
			dbProjectCmd(),
			addProjectCmd(),
			deleteProjectCmd,
			unsetProjectCmd(),
			cloneCmd(),
			setProjectCmd,
		)

		for _, customCmd := range config.GetCustomCmds(pName) {
			pCmd.AddCommand(customCmd)
		}
	}
}

// projectCmd represents the project command
var projectCmd = &cobra.Command{
	Use:   "project",
	Short: "Interact with local projects",
	Long:  `Interact with a project using predefined commands, or define custom commands.`,
	Run: func(cmd *cobra.Command, args []string) {
		if len(config.Vipers["projects"].AllSettings()) == 0 {
			fmt.Println("/!\\ You currently have no projects set up.")
			in := config.GetBoolUserInput("Would you like to add one ? (Y/n)")
			if !in {
				return
			}
			addProjectCmd().Execute()
		}
		cmd.Usage()
	},
}

func statusProjectCmd() *cobra.Command {
	return &cobra.Command{
		Use:   "status",
		Short: "Display health status",
		Long:  `Display health status.`,
		Run: func(cmd *cobra.Command, args []string) {
			baseURL, ok := config.Vipers["projects"].GetStringMapString(cmd.Parent().Use)["url"]
			if !ok {
				baseURL = config.GetUserInput("Enter the project URL (example: http://goian.io)")
			}
			healthEndpoint, ok := config.Vipers["projects"].GetStringMapString(cmd.Parent().Use)["health"]
			if !ok {
				healthEndpoint = config.GetUserInput("Enter the health check relative URL (example: /status)")
			}
			status := projects.Status(cmd.Parent().Use, baseURL, healthEndpoint)
			fmt.Printf("%v", status)

			repo.Status(cmd.Parent().Use)
		},
	}
}

func statsProjectCmd() *cobra.Command {
	return &cobra.Command{
		Use:   "stats",
		Short: "Display Github stat's for a project",
		Long:  `Display Github stat's for a project.`,
		Run: func(cmd *cobra.Command, args []string) {
			repositoryURL := fmt.Sprintf(
				"https://api.github.com/repos/%s",
				config.Vipers["projects"].GetStringMapString(cmd.Parent().Use)["repository"],
			)
			stats, _ := projects.Stats(cmd.Parent().Use, repositoryURL)
			prettyStats, _ := json.MarshalIndent(stats, "", "  ")
			fmt.Printf("%s", prettyStats)
		},
	}
}

func configProjectCmd() *cobra.Command {
	return &cobra.Command{
		Use:   "config",
		Short: "Display a project's configuration",
		Long:  `Display a project's configuration.`,
		Run: func(cmd *cobra.Command, args []string) {
			settings := config.Vipers["projects"].GetStringMap(cmd.Parent().Use)
			prettySettings, _ := json.MarshalIndent(settings, "", "  ")
			fmt.Printf("%s configuration:\n%s\n}", cmd.Parent().Use, prettySettings)
		},
	}
}

func deployProjectCmd() *cobra.Command {
	return &cobra.Command{
		Use:   "deploy",
		Short: "Deploy a new version of a project",
		Long:  `Deploy a new version of a project.`,
		Run: func(cmd *cobra.Command, args []string) {
			deployCmdContent := config.Vipers["projects"].GetStringMapString(cmd.Parent().Use)["deploy_cmd"]
			deployCmd := strings.Split(deployCmdContent, " ")
			termCmd := exec.Command(deployCmd[0], deployCmd[1:]...)
			termCmd.Dir = config.Vipers["config"].GetString("repositories_path")
			command.ExecuteInteractiveCommand(termCmd)
		},
	}
}

func rollbackProjectCmd() *cobra.Command {
	return &cobra.Command{
		Use:   "rollback",
		Short: "Rollback to a previous version of a project",
		Long:  `Rollback to a previous version of a project.`,
		Run: func(cmd *cobra.Command, args []string) {
			rollbackCmdContent := config.Vipers["projects"].GetStringMapString(cmd.Parent().Use)["rollback_cmd"]
			rollbackCmd := strings.Split(rollbackCmdContent, " ")
			termCmd := exec.Command(rollbackCmd[0], rollbackCmd[:1]...)
			termCmd.Dir = config.Vipers["config"].GetString("repositories_path")
			command.ExecuteInteractiveCommand(termCmd)
		},
	}
}

func dbProjectCmd() *cobra.Command {
	return &cobra.Command{
		Use:   "db",
		Short: "Connect to the project's database",
		Long:  `Connect to the project's database.`,
		Run: func(cmd *cobra.Command, args []string) {
			cmdContent := config.Vipers["projects"].GetStringMapString(cmd.Parent().Use)["db_cmd"]
			cmdContent = strings.Replace(cmdContent, "\"", "", -1)
			dbCmd := strings.Split(cmdContent, " ")

			termCmd := exec.Command(dbCmd[0], dbCmd[1:]...)
			termCmd.Dir = config.Vipers["config"].GetString("repositories_path")
			command.ExecuteInteractiveCommand(termCmd)
		},
	}
}

func addProjectCmd() *cobra.Command {
	return &cobra.Command{
		Use:   "add",
		Short: "Add a new project configuration",
		Long:  `Add a new project configuration.`,
		Run: func(cmd *cobra.Command, args []string) {
			NewP := make(map[string]string)
			projectName := config.GetUserInput("Enter the name of the project")
			NewP["description"] = config.GetUserInput("Enter the project description")
			NewP["url"] = config.GetUserInput("Enter the project URL (example: http://goian.io)")
			s := config.GetUserInput("Enter the project repository (example: thylong/ian)")
			NewP["repository"] = strings.TrimPrefix(strings.TrimSuffix(s, ".git"), "git@github.com:")
			NewP["health"] = config.GetUserInput("Enter the health check relative URL (example: /status)")
			NewP["db_cmd"] = config.GetUserInput("Enter the db connection command (example: mongo localhost)")
			NewP["deploy_cmd"] = config.GetUserInput("Enter the deploy command (example: bash deploy.sh)")
			NewP["rollback_cmd"] = config.GetUserInput("Enter the rollback command (example: bash rollback.sh)")

			projectsContent := config.Vipers["projects"].AllSettings()
			projectsContent[projectName] = NewP
			config.UpdateYamlFile(
				config.ConfigFilesPathes["projects"],
				projectsContent,
			)
		},
	}
}

func deleteProjectCmd() *cobra.Command {
	return &cobra.Command{
		Use:   "delete",
		Short: "Delete a project configuration",
		Long:  `Delete a project configuration.`,
		Run: func(cmd *cobra.Command, args []string) {
			if !softDeleteCmdParam {
				repo.Remove(cmd.Parent().Use)
			}

			editContent := config.Vipers["projects"].AllSettings()
			delete(editContent, cmd.Parent().Use)

			config.UpdateYamlFile(
				config.ConfigFilesPathes["projects"],
				config.Vipers["projects"].AllSettings(),
			)
		},
	}
}

func setProjectCmd() *cobra.Command {
	return &cobra.Command{
		Use:   "set",
		Short: "Define a command for the project",
		Long: `Define a command for the project

Example: ian project dotfiles set -c bonjour -d "Say bonjour" echo bonjour !.`,
		Run: func(cmd *cobra.Command, args []string) {
			if len(args) < 2 && (customCmdName == "" || customCmdDescription == "") {
				customCmdName = config.GetUserInput("Enter the name of the command")
				customCmdDescription = config.GetUserInput("Enter the description of the project")
			}
			if len(customCmdDescription) < 5 || len(customCmdDescription) > 40 {
				fmt.Fprintf(os.Stderr, "%v Description must be between 5 and 40 alphanumeric characters.\n\n", color.RedString("Error:"))
				cmd.Usage()
				os.Exit(1)
			}

			editContent := config.Vipers["projects"].GetStringMapString(cmd.Parent().Use)
			editContent[fmt.Sprintf("%s_custom_cmd", customCmdName)] = fmt.Sprintf("%s=%s", customCmdDescription, strings.Join(args, " "))
			config.Vipers["projects"].Set(cmd.Parent().Use, editContent)

			projectsContent := config.Vipers["projects"].AllSettings()
			config.UpdateYamlFile(
				config.ConfigFilesPathes["projects"],
				projectsContent,
			)
		},
	}
}

func cloneCmd() *cobra.Command {
	return &cobra.Command{
		Use:   "clone",
		Short: "Clone project repository",
		Long: `Clone the project repository.

Example: ian project dotfiles unset bonjour -d "Say bonjour" echo bonjour !.`,
		Run: func(cmd *cobra.Command, args []string) {
			editContent := config.Vipers["projects"].GetStringMapString(cmd.Parent().Use)
			if projectRepository, ok := editContent["repository"]; ok {
				repo.Clone(repo.GetGitRepositorySSHPath(projectRepository))
			}
		},
	}
}

func unsetProjectCmd() *cobra.Command {
	return &cobra.Command{
		Use:   "unset",
		Short: "Remove a project command",
		Long:  `Remove a project subcommand.`,
		Run: func(cmd *cobra.Command, args []string) {
			if len(args) < 1 {
				args[0] = config.GetUserInput("Enter the name of the command to unset")
			}

			editContent := config.Vipers["projects"].GetStringMapString(cmd.Parent().Use)
			delete(editContent, fmt.Sprintf("%s_cmd", args[0]))

			config.UpdateYamlFile(
				config.ConfigFilesPathes["projects"],
				config.Vipers["projects"].AllSettings(),
			)
		},
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"errors"
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"
	"syscall"
	"time"

	docopt "github.com/flynn/go-docopt"
	tuf "github.com/theupdateframework/go-tuf"
	"github.com/theupdateframework/go-tuf/util"
	"golang.org/x/term"
)

func main() {
	log.SetFlags(0)

	usage := `usage: tuf [-h|--help] [-d|--dir=<dir>] [--insecure-plaintext] <command> [<args>...]

Options:
  -h, --help
  -d <dir>              The path to the repository (defaults to the current working directory)
  --insecure-plaintext  Don't encrypt signing keys

Commands:
  help               Show usage for a specific command
  init               Initialize a new repository
  gen-key            Generate a new signing key for a specific metadata file
  revoke-key         Revoke a signing key
  add                Add target file(s)
  remove             Remove a target file
  snapshot           Update the snapshot metadata file
  timestamp          Update the timestamp metadata file
  payload            Output a role's metadata file for signing
  add-signatures     Adds signatures generated offline
  sign               Sign a role's metadata file
  sign-payload       Sign a file from the "payload" command.
  commit             Commit staged files to the repository
  regenerate         Recreate the targets metadata file [Not supported yet]
  set-threshold      Sets the threshold for a role
  get-threshold      Outputs the threshold for a role
  change-passphrase  Changes the passphrase for given role keys file
  root-keys          Output a JSON serialized array of root keys to STDOUT
  clean              Remove all staged metadata files

See "tuf help <command>" for more information on a specific command
`

	args, _ := docopt.Parse(usage, nil, true, "", true)
	cmd := args.String["<command>"]
	cmdArgs := args.All["<args>"].([]string)

	if cmd == "help" {
		if len(cmdArgs) == 0 { // `tuf help`
			fmt.Println(usage)
			return
		} else { // `tuf help <command>`
			cmd = cmdArgs[0]
			cmdArgs = []string{"--help"}
		}
	}

	dir, ok := args.String["-d"]
	if !ok {
		dir = args.String["--dir"]
	}
	if dir == "" {
		var err error
		dir, err = os.Getwd()
		if err != nil {
			log.Fatal(err)
		}
	}

	if err := runCommand(cmd, cmdArgs, dir, args.Bool["--insecure-plaintext"]); err != nil {
		log.Fatalln("ERROR:", err)
	}
}

type cmdFunc func(*docopt.Args, *tuf.Repo) error

type command struct {
	usage string
	f     cmdFunc
}

var commands = make(map[string]*command)

func register(name string, f cmdFunc, usage string) {
	commands[name] = &command{usage: usage, f: f}
}

func runCommand(name string, args []string, dir string, insecure bool) error {
	argv := make([]string, 1, 1+len(args))
	argv[0] = name
	argv = append(argv, args...)

	cmd, ok := commands[name]
	if !ok {
		return fmt.Errorf("%s is not a tuf command. See 'tuf help'", name)
	}

	parsedArgs, err := docopt.Parse(cmd.usage, argv, true, "", true)
	if err != nil {
		return err
	}

	var p util.PassphraseFunc
	if !insecure {
		p = getPassphrase
	}
	repo, err := tuf.NewRepo(tuf.FileSystemStore(dir, p))
	if err != nil {
		return err
	}
	return cmd.f(parsedArgs, repo)
}

func parseExpires(arg string) (time.Time, error) {
	days, err := strconv.Atoi(arg)
	if err != nil {
		return time.Time{}, fmt.Errorf("failed to parse --expires arg: %s", err)
	}
	return time.Now().AddDate(0, 0, days).UTC(), nil
}

func getPassphrase(role string, confirm bool, change bool) ([]byte, error) {
	// In case of change we need to prompt explicitly for a new passphrase
	// and not read it from the environment variable, if present
	if pass := os.Getenv(fmt.Sprintf("TUF_%s_PASSPHRASE", strings.ToUpper(role))); pass != "" && !change {
		return []byte(pass), nil
	}
	// Alter role string if we are prompting for a passphrase change
	if change {
		// Check if environment variable for new passphrase exist
		if new_pass := os.Getenv(fmt.Sprintf("TUF_NEW_%s_PASSPHRASE", strings.ToUpper(role))); new_pass != "" {
			// If so, just read the new passphrase from it and return
			return []byte(new_pass), nil
		}
		// No environment variable set, so proceed prompting for new passphrase
		role = fmt.Sprintf("new %s", role)
	}
	fmt.Printf("Enter %s keys passphrase: ", role)
	passphrase, err := term.ReadPassword(int(syscall.Stdin))
	fmt.Println()
	if err != nil {
		return nil, err
	}

	if !confirm {
		return passphrase, nil
	}

	fmt.Printf("Repeat %s keys passphrase: ", role)
	confirmation, err := term.ReadPassword(int(syscall.Stdin))
	fmt.Println()
	if err != nil {
		return nil, err
	}

	if !bytes.Equal(passphrase, confirmation) {
		return nil, errors.New("the entered passphrases do not match")
	}
	return passphrase, nil
}
<commit_msg>fix: Redirect passphrase output to Standard error (#343)<commit_after>package main

import (
	"bytes"
	"errors"
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"
	"syscall"
	"time"

	docopt "github.com/flynn/go-docopt"
	tuf "github.com/theupdateframework/go-tuf"
	"github.com/theupdateframework/go-tuf/util"
	"golang.org/x/term"
)

func main() {
	log.SetFlags(0)

	usage := `usage: tuf [-h|--help] [-d|--dir=<dir>] [--insecure-plaintext] <command> [<args>...]

Options:
  -h, --help
  -d <dir>              The path to the repository (defaults to the current working directory)
  --insecure-plaintext  Don't encrypt signing keys

Commands:
  help               Show usage for a specific command
  init               Initialize a new repository
  gen-key            Generate a new signing key for a specific metadata file
  revoke-key         Revoke a signing key
  add                Add target file(s)
  remove             Remove a target file
  snapshot           Update the snapshot metadata file
  timestamp          Update the timestamp metadata file
  payload            Output a role's metadata file for signing
  add-signatures     Adds signatures generated offline
  sign               Sign a role's metadata file
  sign-payload       Sign a file from the "payload" command.
  commit             Commit staged files to the repository
  regenerate         Recreate the targets metadata file [Not supported yet]
  set-threshold      Sets the threshold for a role
  get-threshold      Outputs the threshold for a role
  change-passphrase  Changes the passphrase for given role keys file
  root-keys          Output a JSON serialized array of root keys to STDOUT
  clean              Remove all staged metadata files

See "tuf help <command>" for more information on a specific command
`

	args, _ := docopt.Parse(usage, nil, true, "", true)
	cmd := args.String["<command>"]
	cmdArgs := args.All["<args>"].([]string)

	if cmd == "help" {
		if len(cmdArgs) == 0 { // `tuf help`
			fmt.Println(usage)
			return
		} else { // `tuf help <command>`
			cmd = cmdArgs[0]
			cmdArgs = []string{"--help"}
		}
	}

	dir, ok := args.String["-d"]
	if !ok {
		dir = args.String["--dir"]
	}
	if dir == "" {
		var err error
		dir, err = os.Getwd()
		if err != nil {
			log.Fatal(err)
		}
	}

	if err := runCommand(cmd, cmdArgs, dir, args.Bool["--insecure-plaintext"]); err != nil {
		log.Fatalln("ERROR:", err)
	}
}

type cmdFunc func(*docopt.Args, *tuf.Repo) error

type command struct {
	usage string
	f     cmdFunc
}

var commands = make(map[string]*command)

func register(name string, f cmdFunc, usage string) {
	commands[name] = &command{usage: usage, f: f}
}

func runCommand(name string, args []string, dir string, insecure bool) error {
	argv := make([]string, 1, 1+len(args))
	argv[0] = name
	argv = append(argv, args...)

	cmd, ok := commands[name]
	if !ok {
		return fmt.Errorf("%s is not a tuf command. See 'tuf help'", name)
	}

	parsedArgs, err := docopt.Parse(cmd.usage, argv, true, "", true)
	if err != nil {
		return err
	}

	var p util.PassphraseFunc
	if !insecure {
		p = getPassphrase
	}
	repo, err := tuf.NewRepo(tuf.FileSystemStore(dir, p))
	if err != nil {
		return err
	}
	return cmd.f(parsedArgs, repo)
}

func parseExpires(arg string) (time.Time, error) {
	days, err := strconv.Atoi(arg)
	if err != nil {
		return time.Time{}, fmt.Errorf("failed to parse --expires arg: %s", err)
	}
	return time.Now().AddDate(0, 0, days).UTC(), nil
}

func getPassphrase(role string, confirm bool, change bool) ([]byte, error) {
	// In case of change we need to prompt explicitly for a new passphrase
	// and not read it from the environment variable, if present
	if pass := os.Getenv(fmt.Sprintf("TUF_%s_PASSPHRASE", strings.ToUpper(role))); pass != "" && !change {
		return []byte(pass), nil
	}
	// Alter role string if we are prompting for a passphrase change
	if change {
		// Check if environment variable for new passphrase exist
		if new_pass := os.Getenv(fmt.Sprintf("TUF_NEW_%s_PASSPHRASE", strings.ToUpper(role))); new_pass != "" {
			// If so, just read the new passphrase from it and return
			return []byte(new_pass), nil
		}
		// No environment variable set, so proceed prompting for new passphrase
		role = fmt.Sprintf("new %s", role)
	}
	fmt.Fprintf(os.Stderr, "Enter %s keys passphrase: ", role)
	passphrase, err := term.ReadPassword(int(syscall.Stdin))
	fmt.Fprintln(os.Stderr)
	if err != nil {
		return nil, err
	}

	if !confirm {
		return passphrase, nil
	}

	fmt.Fprintf(os.Stderr, "Repeat %s keys passphrase: ", role)
	confirmation, err := term.ReadPassword(int(syscall.Stdin))
	fmt.Fprintln(os.Stderr)
	if err != nil {
		return nil, err
	}

	if !bytes.Equal(passphrase, confirmation) {
		return nil, errors.New("the entered passphrases do not match")
	}
	return passphrase, nil
}
<|endoftext|>


<commit_before>// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Vet is a simple checker for static errors in Go source code.
// See doc.go for more information.
package main

import (
	"bytes"
	"flag"
	"fmt"
	"go/ast"
	"go/build"
	"go/parser"
	"go/printer"
	"go/token"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"code.google.com/p/go.tools/go/exact"
	"code.google.com/p/go.tools/go/types"
)

var verbose = flag.Bool("v", false, "verbose")
var exitCode = 0

// Flags to control which checks to perform. "all" is set to true here, and disabled later if
// a flag is set explicitly.
var report = map[string]*bool{
	"all":         flag.Bool("all", true, "check everything; disabled if any explicit check is requested"),
	"asmdecl":     flag.Bool("asmdecl", false, "check assembly against Go declarations"),
	"assign":      flag.Bool("assign", false, "check for useless assignments"),
	"atomic":      flag.Bool("atomic", false, "check for common mistaken usages of the sync/atomic package"),
	"buildtags":   flag.Bool("buildtags", false, "check that +build tags are valid"),
	"composites":  flag.Bool("composites", false, "check that composite literals used type-tagged elements"),
	"methods":     flag.Bool("methods", false, "check that canonically named methods are canonically defined"),
	"printf":      flag.Bool("printf", false, "check printf-like invocations"),
	"rangeloops":  flag.Bool("rangeloops", false, "check that range loop variables are used correctly"),
	"structtags":  flag.Bool("structtags", false, "check that struct field tags have canonical format"),
	"unreachable": flag.Bool("unreachable", false, "check for unreachable code"),
}

// TODO: Need a flag to set build tags when parsing the package.

// vet tells whether to report errors for the named check, a flag name.
func vet(name string) bool {
	return *report["all"] || *report[name]
}

// setExit sets the value for os.Exit when it is called, later.  It
// remembers the highest value.
func setExit(err int) {
	if err > exitCode {
		exitCode = err
	}
}

// Usage is a replacement usage function for the flags package.
func Usage() {
	fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
	fmt.Fprintf(os.Stderr, "\tvet [flags] directory...\n")
	fmt.Fprintf(os.Stderr, "\tvet [flags] files... # Must be a single package\n")
	flag.PrintDefaults()
	os.Exit(2)
}

// File is a wrapper for the state of a file used in the parser.
// The parse tree walkers are all methods of this type.
type File struct {
	pkg     *Package
	fset    *token.FileSet
	name    string
	content []byte
	file    *ast.File
	b       bytes.Buffer // for use by methods
}

func main() {
	flag.Usage = Usage
	flag.Parse()

	// If a check is named explicitly, turn off the 'all' flag.
	for name, ptr := range report {
		if name != "all" && *ptr {
			*report["all"] = false
			break
		}
	}

	if *printfuncs != "" {
		for _, name := range strings.Split(*printfuncs, ",") {
			if len(name) == 0 {
				flag.Usage()
			}
			skip := 0
			if colon := strings.LastIndex(name, ":"); colon > 0 {
				var err error
				skip, err = strconv.Atoi(name[colon+1:])
				if err != nil {
					errorf(`illegal format for "Func:N" argument %q; %s`, name, err)
				}
				name = name[:colon]
			}
			name = strings.ToLower(name)
			if name[len(name)-1] == 'f' {
				printfList[name] = skip
			} else {
				printList[name] = skip
			}
		}
	}

	if flag.NArg() == 0 {
		Usage()
	}
	dirs := false
	files := false
	for _, name := range flag.Args() {
		// Is it a directory?
		fi, err := os.Stat(name)
		if err != nil {
			warnf("error walking tree: %s", err)
			continue
		}
		if fi.IsDir() {
			dirs = true
		} else {
			files = true
		}
	}
	if dirs && files {
		Usage()
	}
	if dirs {
		for _, name := range flag.Args() {
			walkDir(name)
		}
		return
	}
	doPackage(".", flag.Args())
	os.Exit(exitCode)
}

// prefixDirectory places the directory name on the beginning of each name in the list.
func prefixDirectory(directory string, names []string) {
	if directory != "." {
		for i, name := range names {
			names[i] = filepath.Join(directory, name)
		}
	}
}

// doPackageDir analyzes the single package found in the directory, if there is one,
// plus a test package, if there is one.
func doPackageDir(directory string) {
	pkg, err := build.Default.ImportDir(directory, 0)
	if err != nil {
		// If it's just that there are no go source files, that's fine.
		if _, nogo := err.(*build.NoGoError); nogo {
			return
		}
		// Non-fatal: we are doing a recursive walk and there may be other directories.
		warnf("cannot process directory %s: %s", directory, err)
		return
	}
	var names []string
	names = append(names, pkg.GoFiles...)
	names = append(names, pkg.CgoFiles...)
	names = append(names, pkg.TestGoFiles...) // These are also in the "foo" package.
	names = append(names, pkg.SFiles...)
	prefixDirectory(directory, names)
	doPackage(directory, names)
	// Is there also a "foo_test" package? If so, do that one as well.
	if len(pkg.XTestGoFiles) > 0 {
		names = pkg.XTestGoFiles
		prefixDirectory(directory, names)
		doPackage(directory, names)
	}
}

type Package struct {
	path   string
	types  map[ast.Expr]types.Type
	values map[ast.Expr]exact.Value
	files  []*File
}

// doPackage analyzes the single package constructed from the named files.
func doPackage(directory string, names []string) {
	var files []*File
	var astFiles []*ast.File
	fs := token.NewFileSet()
	for _, name := range names {
		f, err := os.Open(name)
		if err != nil {
			// Warn but continue to next package.
			warnf("%s: %s", name, err)
			return
		}
		defer f.Close()
		data, err := ioutil.ReadAll(f)
		if err != nil {
			warnf("%s: %s", name, err)
			return
		}
		checkBuildTag(name, data)
		var parsedFile *ast.File
		if strings.HasSuffix(name, ".go") {
			parsedFile, err = parser.ParseFile(fs, name, bytes.NewReader(data), 0)
			if err != nil {
				warnf("%s: %s", name, err)
				return
			}
			astFiles = append(astFiles, parsedFile)
		}
		files = append(files, &File{fset: fs, content: data, name: name, file: parsedFile})
	}
	pkg := new(Package)
	if directory == "." {
		// Special case: Use the base name so we don't see "." as the package name in messages.
		directory = filepath.Base(filepath.Dir(names[0]))
	}
	pkg.path = directory
	pkg.files = files
	// Type check the package.
	err := pkg.check(fs, astFiles)
	if err != nil && *verbose {
		warnf("%s", err)
	}
	for _, file := range files {
		file.pkg = pkg
		if file.file != nil {
			file.walkFile(file.name, file.file)
		}
	}
	asmCheck(pkg)
}

func visit(path string, f os.FileInfo, err error) error {
	if err != nil {
		warnf("walk error: %s", err)
		return err
	}
	// One package per directory. Ignore the files themselves.
	if !f.IsDir() {
		return nil
	}
	doPackageDir(path)
	return nil
}

func (pkg *Package) hasFileWithSuffix(suffix string) bool {
	for _, f := range pkg.files {
		if strings.HasSuffix(f.name, suffix) {
			return true
		}
	}
	return false
}

// walkDir recursively walks the tree looking for Go packages.
func walkDir(root string) {
	filepath.Walk(root, visit)
}

// errorf formats the error to standard error, adding program
// identification and a newline, and exits.
func errorf(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, "vet: "+format+"\n", args...)
	os.Exit(2)
}

// warnf formats the error to standard error, adding program
// identification and a newline, but does not exit.
func warnf(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, "vet: "+format+"\n", args...)
	setExit(1)
}

// Println is fmt.Println guarded by -v.
func Println(args ...interface{}) {
	if !*verbose {
		return
	}
	fmt.Println(args...)
}

// Printf is fmt.Printf guarded by -v.
func Printf(format string, args ...interface{}) {
	if !*verbose {
		return
	}
	fmt.Printf(format+"\n", args...)
}

// Bad reports an error and sets the exit code..
func (f *File) Bad(pos token.Pos, args ...interface{}) {
	f.Warn(pos, args...)
	setExit(1)
}

// Badf reports a formatted error and sets the exit code.
func (f *File) Badf(pos token.Pos, format string, args ...interface{}) {
	f.Warnf(pos, format, args...)
	setExit(1)
}

func (f *File) loc(pos token.Pos) string {
	if pos == token.NoPos {
		return ""
	}
	// Do not print columns. Because the pos often points to the start of an
	// expression instead of the inner part with the actual error, the
	// precision can mislead.
	posn := f.fset.Position(pos)
	return fmt.Sprintf("%s:%d: ", posn.Filename, posn.Line)
}

// Warn reports an error but does not set the exit code.
func (f *File) Warn(pos token.Pos, args ...interface{}) {
	fmt.Fprint(os.Stderr, f.loc(pos)+fmt.Sprintln(args...))
}

// Warnf reports a formatted error but does not set the exit code.
func (f *File) Warnf(pos token.Pos, format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, f.loc(pos)+format+"\n", args...)
}

// walkFile walks the file's tree.
func (f *File) walkFile(name string, file *ast.File) {
	Println("Checking file", name)
	ast.Walk(f, file)
}

// Visit implements the ast.Visitor interface.
func (f *File) Visit(node ast.Node) ast.Visitor {
	switch n := node.(type) {
	case *ast.AssignStmt:
		f.walkAssignStmt(n)
	case *ast.CallExpr:
		f.walkCallExpr(n)
	case *ast.CompositeLit:
		f.walkCompositeLit(n)
	case *ast.Field:
		f.walkFieldTag(n)
	case *ast.FuncDecl:
		f.walkFuncDecl(n)
	case *ast.FuncLit:
		f.walkFuncLit(n)
	case *ast.InterfaceType:
		f.walkInterfaceType(n)
	case *ast.RangeStmt:
		f.walkRangeStmt(n)
	}
	return f
}

// walkAssignStmt walks an assignment statement
func (f *File) walkAssignStmt(stmt *ast.AssignStmt) {
	f.checkAssignStmt(stmt)
	f.checkAtomicAssignment(stmt)
}

// walkCall walks a call expression.
func (f *File) walkCall(call *ast.CallExpr, name string) {
	f.checkFmtPrintfCall(call, name)
}

// walkCallExpr walks a call expression.
func (f *File) walkCallExpr(call *ast.CallExpr) {
	switch x := call.Fun.(type) {
	case *ast.Ident:
		f.walkCall(call, x.Name)
	case *ast.SelectorExpr:
		f.walkCall(call, x.Sel.Name)
	}
}

// walkCompositeLit walks a composite literal.
func (f *File) walkCompositeLit(c *ast.CompositeLit) {
	f.checkUntaggedLiteral(c)
}

// walkFieldTag walks a struct field tag.
func (f *File) walkFieldTag(field *ast.Field) {
	if field.Tag == nil {
		return
	}
	f.checkCanonicalFieldTag(field)
}

// walkMethod walks the method's signature.
func (f *File) walkMethod(id *ast.Ident, t *ast.FuncType) {
	f.checkCanonicalMethod(id, t)
}

// walkFuncDecl walks a function declaration.
func (f *File) walkFuncDecl(d *ast.FuncDecl) {
	f.checkUnreachable(d.Body)
	if d.Recv != nil {
		f.walkMethod(d.Name, d.Type)
	}
}

// walkFuncLit walks a function literal.
func (f *File) walkFuncLit(x *ast.FuncLit) {
	f.checkUnreachable(x.Body)
}

// walkInterfaceType walks the method signatures of an interface.
func (f *File) walkInterfaceType(t *ast.InterfaceType) {
	for _, field := range t.Methods.List {
		for _, id := range field.Names {
			f.walkMethod(id, field.Type.(*ast.FuncType))
		}
	}
}

// walkRangeStmt walks a range statement.
func (f *File) walkRangeStmt(n *ast.RangeStmt) {
	checkRangeLoop(f, n)
}

// gofmt returns a string representation of the expression.
func (f *File) gofmt(x ast.Expr) string {
	f.b.Reset()
	printer.Fprint(&f.b, f.fset, x)
	return f.b.String()
}
<commit_msg>go.tools/cmd/vet: set the package name unconditionally A better fix than the one in CL 10400044<commit_after>// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Vet is a simple checker for static errors in Go source code.
// See doc.go for more information.
package main

import (
	"bytes"
	"flag"
	"fmt"
	"go/ast"
	"go/build"
	"go/parser"
	"go/printer"
	"go/token"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"code.google.com/p/go.tools/go/exact"
	"code.google.com/p/go.tools/go/types"
)

var verbose = flag.Bool("v", false, "verbose")
var exitCode = 0

// Flags to control which checks to perform. "all" is set to true here, and disabled later if
// a flag is set explicitly.
var report = map[string]*bool{
	"all":         flag.Bool("all", true, "check everything; disabled if any explicit check is requested"),
	"asmdecl":     flag.Bool("asmdecl", false, "check assembly against Go declarations"),
	"assign":      flag.Bool("assign", false, "check for useless assignments"),
	"atomic":      flag.Bool("atomic", false, "check for common mistaken usages of the sync/atomic package"),
	"buildtags":   flag.Bool("buildtags", false, "check that +build tags are valid"),
	"composites":  flag.Bool("composites", false, "check that composite literals used type-tagged elements"),
	"methods":     flag.Bool("methods", false, "check that canonically named methods are canonically defined"),
	"printf":      flag.Bool("printf", false, "check printf-like invocations"),
	"rangeloops":  flag.Bool("rangeloops", false, "check that range loop variables are used correctly"),
	"structtags":  flag.Bool("structtags", false, "check that struct field tags have canonical format"),
	"unreachable": flag.Bool("unreachable", false, "check for unreachable code"),
}

// TODO: Need a flag to set build tags when parsing the package.

// vet tells whether to report errors for the named check, a flag name.
func vet(name string) bool {
	return *report["all"] || *report[name]
}

// setExit sets the value for os.Exit when it is called, later.  It
// remembers the highest value.
func setExit(err int) {
	if err > exitCode {
		exitCode = err
	}
}

// Usage is a replacement usage function for the flags package.
func Usage() {
	fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
	fmt.Fprintf(os.Stderr, "\tvet [flags] directory...\n")
	fmt.Fprintf(os.Stderr, "\tvet [flags] files... # Must be a single package\n")
	flag.PrintDefaults()
	os.Exit(2)
}

// File is a wrapper for the state of a file used in the parser.
// The parse tree walkers are all methods of this type.
type File struct {
	pkg     *Package
	fset    *token.FileSet
	name    string
	content []byte
	file    *ast.File
	b       bytes.Buffer // for use by methods
}

func main() {
	flag.Usage = Usage
	flag.Parse()

	// If a check is named explicitly, turn off the 'all' flag.
	for name, ptr := range report {
		if name != "all" && *ptr {
			*report["all"] = false
			break
		}
	}

	if *printfuncs != "" {
		for _, name := range strings.Split(*printfuncs, ",") {
			if len(name) == 0 {
				flag.Usage()
			}
			skip := 0
			if colon := strings.LastIndex(name, ":"); colon > 0 {
				var err error
				skip, err = strconv.Atoi(name[colon+1:])
				if err != nil {
					errorf(`illegal format for "Func:N" argument %q; %s`, name, err)
				}
				name = name[:colon]
			}
			name = strings.ToLower(name)
			if name[len(name)-1] == 'f' {
				printfList[name] = skip
			} else {
				printList[name] = skip
			}
		}
	}

	if flag.NArg() == 0 {
		Usage()
	}
	dirs := false
	files := false
	for _, name := range flag.Args() {
		// Is it a directory?
		fi, err := os.Stat(name)
		if err != nil {
			warnf("error walking tree: %s", err)
			continue
		}
		if fi.IsDir() {
			dirs = true
		} else {
			files = true
		}
	}
	if dirs && files {
		Usage()
	}
	if dirs {
		for _, name := range flag.Args() {
			walkDir(name)
		}
		return
	}
	doPackage(".", flag.Args())
	os.Exit(exitCode)
}

// prefixDirectory places the directory name on the beginning of each name in the list.
func prefixDirectory(directory string, names []string) {
	if directory != "." {
		for i, name := range names {
			names[i] = filepath.Join(directory, name)
		}
	}
}

// doPackageDir analyzes the single package found in the directory, if there is one,
// plus a test package, if there is one.
func doPackageDir(directory string) {
	pkg, err := build.Default.ImportDir(directory, 0)
	if err != nil {
		// If it's just that there are no go source files, that's fine.
		if _, nogo := err.(*build.NoGoError); nogo {
			return
		}
		// Non-fatal: we are doing a recursive walk and there may be other directories.
		warnf("cannot process directory %s: %s", directory, err)
		return
	}
	var names []string
	names = append(names, pkg.GoFiles...)
	names = append(names, pkg.CgoFiles...)
	names = append(names, pkg.TestGoFiles...) // These are also in the "foo" package.
	names = append(names, pkg.SFiles...)
	prefixDirectory(directory, names)
	doPackage(directory, names)
	// Is there also a "foo_test" package? If so, do that one as well.
	if len(pkg.XTestGoFiles) > 0 {
		names = pkg.XTestGoFiles
		prefixDirectory(directory, names)
		doPackage(directory, names)
	}
}

type Package struct {
	path   string
	types  map[ast.Expr]types.Type
	values map[ast.Expr]exact.Value
	files  []*File
}

// doPackage analyzes the single package constructed from the named files.
func doPackage(directory string, names []string) {
	var files []*File
	var astFiles []*ast.File
	fs := token.NewFileSet()
	for _, name := range names {
		f, err := os.Open(name)
		if err != nil {
			// Warn but continue to next package.
			warnf("%s: %s", name, err)
			return
		}
		defer f.Close()
		data, err := ioutil.ReadAll(f)
		if err != nil {
			warnf("%s: %s", name, err)
			return
		}
		checkBuildTag(name, data)
		var parsedFile *ast.File
		if strings.HasSuffix(name, ".go") {
			parsedFile, err = parser.ParseFile(fs, name, bytes.NewReader(data), 0)
			if err != nil {
				warnf("%s: %s", name, err)
				return
			}
			astFiles = append(astFiles, parsedFile)
		}
		files = append(files, &File{fset: fs, content: data, name: name, file: parsedFile})
	}
	pkg := new(Package)
	pkg.path = astFiles[0].Name.Name
	pkg.files = files
	// Type check the package.
	err := pkg.check(fs, astFiles)
	if err != nil && *verbose {
		warnf("%s", err)
	}
	for _, file := range files {
		file.pkg = pkg
		if file.file != nil {
			file.walkFile(file.name, file.file)
		}
	}
	asmCheck(pkg)
}

func visit(path string, f os.FileInfo, err error) error {
	if err != nil {
		warnf("walk error: %s", err)
		return err
	}
	// One package per directory. Ignore the files themselves.
	if !f.IsDir() {
		return nil
	}
	doPackageDir(path)
	return nil
}

func (pkg *Package) hasFileWithSuffix(suffix string) bool {
	for _, f := range pkg.files {
		if strings.HasSuffix(f.name, suffix) {
			return true
		}
	}
	return false
}

// walkDir recursively walks the tree looking for Go packages.
func walkDir(root string) {
	filepath.Walk(root, visit)
}

// errorf formats the error to standard error, adding program
// identification and a newline, and exits.
func errorf(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, "vet: "+format+"\n", args...)
	os.Exit(2)
}

// warnf formats the error to standard error, adding program
// identification and a newline, but does not exit.
func warnf(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, "vet: "+format+"\n", args...)
	setExit(1)
}

// Println is fmt.Println guarded by -v.
func Println(args ...interface{}) {
	if !*verbose {
		return
	}
	fmt.Println(args...)
}

// Printf is fmt.Printf guarded by -v.
func Printf(format string, args ...interface{}) {
	if !*verbose {
		return
	}
	fmt.Printf(format+"\n", args...)
}

// Bad reports an error and sets the exit code..
func (f *File) Bad(pos token.Pos, args ...interface{}) {
	f.Warn(pos, args...)
	setExit(1)
}

// Badf reports a formatted error and sets the exit code.
func (f *File) Badf(pos token.Pos, format string, args ...interface{}) {
	f.Warnf(pos, format, args...)
	setExit(1)
}

func (f *File) loc(pos token.Pos) string {
	if pos == token.NoPos {
		return ""
	}
	// Do not print columns. Because the pos often points to the start of an
	// expression instead of the inner part with the actual error, the
	// precision can mislead.
	posn := f.fset.Position(pos)
	return fmt.Sprintf("%s:%d: ", posn.Filename, posn.Line)
}

// Warn reports an error but does not set the exit code.
func (f *File) Warn(pos token.Pos, args ...interface{}) {
	fmt.Fprint(os.Stderr, f.loc(pos)+fmt.Sprintln(args...))
}

// Warnf reports a formatted error but does not set the exit code.
func (f *File) Warnf(pos token.Pos, format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, f.loc(pos)+format+"\n", args...)
}

// walkFile walks the file's tree.
func (f *File) walkFile(name string, file *ast.File) {
	Println("Checking file", name)
	ast.Walk(f, file)
}

// Visit implements the ast.Visitor interface.
func (f *File) Visit(node ast.Node) ast.Visitor {
	switch n := node.(type) {
	case *ast.AssignStmt:
		f.walkAssignStmt(n)
	case *ast.CallExpr:
		f.walkCallExpr(n)
	case *ast.CompositeLit:
		f.walkCompositeLit(n)
	case *ast.Field:
		f.walkFieldTag(n)
	case *ast.FuncDecl:
		f.walkFuncDecl(n)
	case *ast.FuncLit:
		f.walkFuncLit(n)
	case *ast.InterfaceType:
		f.walkInterfaceType(n)
	case *ast.RangeStmt:
		f.walkRangeStmt(n)
	}
	return f
}

// walkAssignStmt walks an assignment statement
func (f *File) walkAssignStmt(stmt *ast.AssignStmt) {
	f.checkAssignStmt(stmt)
	f.checkAtomicAssignment(stmt)
}

// walkCall walks a call expression.
func (f *File) walkCall(call *ast.CallExpr, name string) {
	f.checkFmtPrintfCall(call, name)
}

// walkCallExpr walks a call expression.
func (f *File) walkCallExpr(call *ast.CallExpr) {
	switch x := call.Fun.(type) {
	case *ast.Ident:
		f.walkCall(call, x.Name)
	case *ast.SelectorExpr:
		f.walkCall(call, x.Sel.Name)
	}
}

// walkCompositeLit walks a composite literal.
func (f *File) walkCompositeLit(c *ast.CompositeLit) {
	f.checkUntaggedLiteral(c)
}

// walkFieldTag walks a struct field tag.
func (f *File) walkFieldTag(field *ast.Field) {
	if field.Tag == nil {
		return
	}
	f.checkCanonicalFieldTag(field)
}

// walkMethod walks the method's signature.
func (f *File) walkMethod(id *ast.Ident, t *ast.FuncType) {
	f.checkCanonicalMethod(id, t)
}

// walkFuncDecl walks a function declaration.
func (f *File) walkFuncDecl(d *ast.FuncDecl) {
	f.checkUnreachable(d.Body)
	if d.Recv != nil {
		f.walkMethod(d.Name, d.Type)
	}
}

// walkFuncLit walks a function literal.
func (f *File) walkFuncLit(x *ast.FuncLit) {
	f.checkUnreachable(x.Body)
}

// walkInterfaceType walks the method signatures of an interface.
func (f *File) walkInterfaceType(t *ast.InterfaceType) {
	for _, field := range t.Methods.List {
		for _, id := range field.Names {
			f.walkMethod(id, field.Type.(*ast.FuncType))
		}
	}
}

// walkRangeStmt walks a range statement.
func (f *File) walkRangeStmt(n *ast.RangeStmt) {
	checkRangeLoop(f, n)
}

// gofmt returns a string representation of the expression.
func (f *File) gofmt(x ast.Expr) string {
	f.b.Reset()
	printer.Fprint(&f.b, f.fset, x)
	return f.b.String()
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/sdwolfe32/ANIRip/anirip"
)

// Trims the first couple seconds off of the video to remove any logos
func trimMKV(fileName string, adLength, estKeyFrame int, engineDir, tempDir string) error {
	// Removes a stale temp files to avoid conflcts in func
	os.Remove(tempDir + "\\" + "untrimmed." + fileName + ".mkv")
	os.Remove(tempDir + "\\" + "prefix." + fileName + ".mkv")
	os.Remove(tempDir + "\\" + "video." + fileName + ".mkv")
	os.Remove(tempDir + "\\" + "list." + fileName + ".txt")

	// Recursively retries rename to temp filename before execution
	if err := anirip.Rename(tempDir+"\\"+fileName+".mkv", tempDir+"\\"+"untrimmed."+fileName+".mkv", 10); err != nil {
		return err
	}

	// Store the untrimmed video length so we can find the video prefix length later
	untrimmedLength, err := getVideoLength("untrimmed."+fileName+".mkv", engineDir, tempDir)
	if err != nil {
		return err
	}

	// Finds ffmpeg so we can call system commands on it
	ffmpeg, err := filepath.Abs(engineDir + "\\ffmpeg.exe")
	if err != nil {
		return anirip.Error{Message: "Unable to find ffmpeg.exe in \\" + engineDir + "\\ directory", Err: err}
	}

	// Calculates the keyframe offsets for trimming the meat of the video
	keyFrameOffset := float64(estKeyFrame) / 1000
	keyFrameOffsetString := strconv.FormatFloat(keyFrameOffset, 'f', 3, 64)

	// Creates the cmd for rough frame trimming
	cmd := exec.Command(ffmpeg,
		"-ss", keyFrameOffsetString,
		"-i", "untrimmed."+fileName+".mkv",
		"-c:v", "copy",
		"-c:a", "copy", "-y",
		"video."+fileName+".mkv")
	cmd.Dir = tempDir // Sets working directory to temp so our fragments end up there

	// Executes the command
	_, err = cmd.Output()
	if err != nil {
		return anirip.Error{Message: "There was an error while creating the video clip", Err: err}
	}

	// Gets the new video length and calculates the prefix length based on the sizes
	videoLength, err := getVideoLength("video."+fileName+".mkv", engineDir, tempDir)
	if err != nil {
		return err
	}
	keyFrameGap := (untrimmedLength - videoLength) - adLength - 0040

	// Calculates the intro offsets we will use and represents it as a string
	trueOffset := float64(adLength) / 1000
	trueOffsetString := strconv.FormatFloat(trueOffset, 'f', 3, 64)
	gapOffset := float64(keyFrameGap) / 1000
	gapOffsetString := strconv.FormatFloat(gapOffset, 'f', 3, 64)

	// We need to ask ffprobe explicitly for an exact framerate because ffmpeg's auto
	// frame reader thinks 30.30 frames is 30.3, resulting in frame jumps at end of prefix
	frameRate, err := getVideoFrameRate("video."+fileName+".mkv", engineDir, tempDir)
	if err != nil {
		return err
	}
	frameRateString := strconv.FormatFloat(frameRate, 'f', 2, 64)

	// Executes the fine intro trim and waits for the command to finish
	cmd = exec.Command(ffmpeg,
		"-ss", trueOffsetString, // Exact timestamp of the ad endings
		"-i", "untrimmed."+fileName+".mkv",
		"-t", gapOffsetString, // The exact time between ad ending and frame next keyframe
		"-crf", "5",
		"-vsync", "1",
		"-r", frameRateString,
		"-c:a", "aac", "-y", // Use AAC as audio codec to match video.mkv
		"prefix."+fileName+".mkv")
	cmd.Dir = tempDir // Sets working directory to temp

	// Executes the command
	_, err = cmd.Output()
	if err != nil {
		return anirip.Error{Message: "There was an error while creating the prefix clip", Err: err}
	}

	// Creates a text file containing the file names of the 2 files created above
	fileListBytes := []byte("file 'prefix." + fileName + ".mkv'\r\nfile 'video." + fileName + ".mkv'")
	if err = ioutil.WriteFile(tempDir+"\\"+"list."+fileName+".txt", fileListBytes, 0644); err != nil {
		return anirip.Error{Message: "There was an error while creating list." + fileName + ".txt", Err: err}
	}

	// Executes the merge of our two temporary files
	cmd = exec.Command(ffmpeg,
		"-f", "concat",
		"-i", "list."+fileName+".txt",
		"-c:v", "copy",
		"-c:a", "copy", "-y",
		fileName+".mkv")
	cmd.Dir = tempDir // Sets working directory to temp

	// Executes the command
	_, err = cmd.Output()
	if err != nil {
		return anirip.Error{Message: "There was an error while merging video and prefix", Err: err}
	}

	// Removes the temporary files we created as they are no longer needed
	os.Remove(tempDir + "\\" + "untrimmed." + fileName + ".mkv")
	os.Remove(tempDir + "\\" + "prefix." + fileName + ".mkv")
	os.Remove(tempDir + "\\" + "video." + fileName + ".mkv")
	os.Remove(tempDir + "\\" + "list." + fileName + ".txt")
	return nil
}

// Merges a VIDEO.mkv and a VIDEO.ass
func mergeSubtitles(fileName, audioLang, subtitleLang, engineDir, tempDir string) error {
	// Removes a stale temp files to avoid conflcts in func
	os.Remove(tempDir + "\\" + "unmerged." + fileName + ".mkv")

	// Recursively retries rename to temp filename before execution
	if err := anirip.Rename(tempDir+"\\"+fileName+".mkv", tempDir+"\\"+"unmerged."+fileName+".mkv", 10); err != nil {
		return err
	}

	path, err := filepath.Abs(engineDir + "\\ffmpeg.exe")
	if err != nil {
		return anirip.Error{Message: "Unable to find ffmpeg.exe in \\" + engineDir + "\\ directory", Err: err}
	}

	// Creates the command which we will use to merge our subtitles and video
	cmd := new(exec.Cmd)
	if subtitleLang == "" {
		cmd = exec.Command(path,
			"-i", "unmerged."+fileName+".mkv",
			"-c:v", "copy",
			"-c:a", "copy",
			"-metadata:s:a:0", "language="+audioLang, // sets audio language to passed audioLang
			"-y", fileName+".mkv")
	} else {
		cmd = exec.Command(path,
			"-i", "unmerged."+fileName+".mkv",
			"-f", "ass",
			"-i", fileName+".ass",
			"-c:v", "copy",
			"-c:a", "copy",
			"-metadata:s:a:0", "language="+audioLang, // sets audio language to passed audioLang
			"-metadata:s:s:0", "language="+subtitleLang, // sets subtitle language to subtitleLang
			"-disposition:s:0", "default",
			"-y", fileName+".mkv")
	}
	cmd.Dir = tempDir // Sets working directory to temp

	// Executes the command
	_, err = cmd.Output()
	if err != nil {
		return anirip.Error{Message: "There was an error while merging subtitles", Err: err}
	}

	// Removes old temp files
	os.Remove(tempDir + "\\" + fileName + ".ass")
	os.Remove(tempDir + "\\" + "unmerged." + fileName + ".mkv")
	return nil
}

// Uses ffprobe to find the length of a video and returns it in ms
func getVideoLength(fileName, engineDir, tempDir string) (int, error) {
	// Gets the ffprobe path which we will use to figure out the video length
	ffprobe, err := filepath.Abs(engineDir + "\\ffprobe.exe")
	if err != nil {
		return 0, anirip.Error{Message: "Unable to find ffprobe.exe in \\" + engineDir + "\\ directory", Err: err}
	}

	// Asks for the length of our video
	cmd := exec.Command(ffprobe,
		"-v", "error",
		"-show_entries", "format=duration",
		"-of", "default=noprint_wrappers=1:nokey=1",
		fileName)
	cmd.Dir = tempDir // Sets working directory to temp

	// Executes the command
	output, err := cmd.Output()
	if err != nil {
		return 0, anirip.Error{Message: "There was an error measuring " + fileName, Err: err}
	}

	// Grabs the output and parses it to a float64
	length, err := strconv.ParseFloat(strings.Replace(string(output), "\r\n", "", -1), 64)
	if err != nil {
		return 0, anirip.Error{Message: "There was an error parsing the length of " + fileName, Err: err}
	}
	return int(length * 1000), nil
}

// Uses ffprobe to get the exact framerate of the video and returns it as a float64
func getVideoFrameRate(fileName, engineDir, tempDir string) (float64, error) {
	// Gets the ffprobe path which we will use to figure out the video length
	ffprobe, err := filepath.Abs(engineDir + "\\ffprobe.exe")
	if err != nil {
		return 0, anirip.Error{Message: "Unable to find ffprobe.exe in \\" + engineDir + "\\ directory", Err: err}
	}

	// Asks for the length of our video
	cmd := exec.Command(ffprobe,
		"-v", "error",
		"-select_streams", "v:0",
		"-show_entries", "stream=avg_frame_rate",
		"-of", "default=noprint_wrappers=1:nokey=1",
		fileName)
	cmd.Dir = tempDir // Sets working directory to temp

	// Executes the command
	output, err := cmd.Output()
	if err != nil {
		return 0, anirip.Error{Message: "There was an error measuring " + fileName, Err: err}
	}

	// Output will be represented as a fraction which needs to be solved
	frameRateArray := strings.Split(strings.Replace(string(output), "\r\n", "", -1), "/")
	numerator, err := strconv.ParseFloat(frameRateArray[0], 64)
	if err != nil {
		return 0, anirip.Error{Message: "There was an error parsing the numerator of our framerate for " + fileName, Err: err}
	}
	denominator, err := strconv.ParseFloat(frameRateArray[1], 64)
	if err != nil {
		return 0, anirip.Error{Message: "There was an error parsing the denominator of our framerate for " + fileName, Err: err}
	}
	return numerator / denominator, nil
}

// Cleans up the mkv, optimizing it for playback
func cleanMKV(fileName, engineDir, tempDir string) error {
	// Removes a stale temp file to avoid conflcts in func
	os.Remove(tempDir + "\\" + "dirty." + fileName + ".mkv")

	// Recursively retries rename to temp filename before execution
	if err := anirip.Rename(tempDir+"\\"+fileName+".mkv", tempDir+"\\"+"dirty."+fileName+".mkv", 10); err != nil {
		return err
	}

	// Finds the path of mkclean.exe so we can perform system calls on it
	path, err := filepath.Abs(engineDir + "\\mkclean.exe")
	if err != nil {
		return anirip.Error{Message: "Unable to find mkclean.exe in \\" + engineDir + "\\ directory", Err: err}
	}

	// Creates the command which we will use to clean our mkv to "video.clean.mkv"
	cmd := exec.Command(path,
		"--optimize",
		"dirty."+fileName+".mkv",
		fileName+".mkv")
	cmd.Dir = tempDir // Sets working directory to temp

	// Executes the command
	_, err = cmd.Output()
	if err != nil {
		return anirip.Error{Message: "There was an error while optimizing our mkv", Err: err}
	}

	// Deletes the old, un-needed dirty mkv file
	os.Remove(tempDir + "\\" + "dirty." + fileName + ".mkv")
	return nil
}

// Gets user input from the user and unmarshalls it into the input
func getStandardUserInput(prefixText string, input *string) error {
	fmt.Printf(prefixText)
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		*input = scanner.Text()
		break
	}
	if err := scanner.Err(); err != nil {
		return anirip.Error{Message: "There was an error getting standard user input", Err: err}
	}
	return nil
}

// Blocks execution and waits for the user to press enter
func pause() {
	fmt.Print("Press 'Enter' to continue...")
	bufio.NewReader(os.Stdin).ReadBytes('\n')
}
<commit_msg>Keeping 0.40ms we were previously trimming and using more acurate precision on fps for improved trimming<commit_after>package main

import (
	"bufio"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/sdwolfe32/ANIRip/anirip"
)

// Trims the first couple seconds off of the video to remove any logos
func trimMKV(fileName string, adLength, estKeyFrame int, engineDir, tempDir string) error {
	// Removes a stale temp files to avoid conflcts in func
	os.Remove(tempDir + "\\" + "untrimmed." + fileName + ".mkv")
	os.Remove(tempDir + "\\" + "prefix." + fileName + ".mkv")
	os.Remove(tempDir + "\\" + "video." + fileName + ".mkv")
	os.Remove(tempDir + "\\" + "list." + fileName + ".txt")

	// Recursively retries rename to temp filename before execution
	if err := anirip.Rename(tempDir+"\\"+fileName+".mkv", tempDir+"\\"+"untrimmed."+fileName+".mkv", 10); err != nil {
		return err
	}

	// Store the untrimmed video length so we can find the video prefix length later
	untrimmedLength, err := getVideoLength("untrimmed."+fileName+".mkv", engineDir, tempDir)
	if err != nil {
		return err
	}

	// Finds ffmpeg so we can call system commands on it
	ffmpeg, err := filepath.Abs(engineDir + "\\ffmpeg.exe")
	if err != nil {
		return anirip.Error{Message: "Unable to find ffmpeg.exe in \\" + engineDir + "\\ directory", Err: err}
	}

	// Calculates the keyframe offsets for trimming the meat of the video
	keyFrameOffset := float64(estKeyFrame) / 1000
	keyFrameOffsetString := strconv.FormatFloat(keyFrameOffset, 'f', 3, 64)

	// Creates the cmd for rough frame trimming
	cmd := exec.Command(ffmpeg,
		"-ss", keyFrameOffsetString,
		"-i", "untrimmed."+fileName+".mkv",
		"-c", "copy",
		"-avoid_negative_ts", "1", "-y",
		"video."+fileName+".mkv")
	cmd.Dir = tempDir // Sets working directory to temp so our fragments end up there

	// Executes the command
	_, err = cmd.Output()
	if err != nil {
		return anirip.Error{Message: "There was an error while creating the video clip", Err: err}
	}

	// Gets the new video length and calculates the prefix length based on the sizes
	videoLength, err := getVideoLength("video."+fileName+".mkv", engineDir, tempDir)
	if err != nil {
		return err
	}
	keyFrameGap := (untrimmedLength - videoLength) - adLength //- 0040

	// Calculates the intro offsets we will use and represents it as a string
	trueOffset := float64(adLength) / 1000
	trueOffsetString := strconv.FormatFloat(trueOffset, 'f', 3, 64)
	gapOffset := float64(keyFrameGap) / 1000
	gapOffsetString := strconv.FormatFloat(gapOffset, 'f', 3, 64)

	// We need to ask ffprobe explicitly for an exact framerate because ffmpeg's auto
	// frame reader thinks 30.30 frames is 30.3, resulting in frame jumps at end of prefix
	frameRate, err := getVideoFrameRate("video."+fileName+".mkv", engineDir, tempDir)
	if err != nil {
		return err
	}
	frameRateString := strconv.FormatFloat(frameRate, 'f', 8, 64)

	// Executes the fine intro trim and waits for the command to finish
	cmd = exec.Command(ffmpeg,
		"-ss", trueOffsetString, // Exact timestamp of the ad endings
		"-i", "untrimmed."+fileName+".mkv",
		"-t", gapOffsetString, // The exact time between ad ending and frame next keyframe
		"-crf", "5",
		"-vsync", "1",
		"-r", frameRateString,
		"-c:a", "aac", "-y", // Use AAC as audio codec to match video.mkv
		"prefix."+fileName+".mkv")
	cmd.Dir = tempDir // Sets working directory to temp

	// Executes the command
	_, err = cmd.Output()
	if err != nil {
		return anirip.Error{Message: "There was an error while creating the prefix clip", Err: err}
	}

	// Creates a text file containing the file names of the 2 files created above
	fileListBytes := []byte("file 'prefix." + fileName + ".mkv'\r\nfile 'video." + fileName + ".mkv'")
	if err = ioutil.WriteFile(tempDir+"\\"+"list."+fileName+".txt", fileListBytes, 0644); err != nil {
		return anirip.Error{Message: "There was an error while creating list." + fileName + ".txt", Err: err}
	}

	// Executes the merge of our two temporary files
	cmd = exec.Command(ffmpeg,
		"-f", "concat",
		"-i", "list."+fileName+".txt",
		"-c", "copy", "-y",
		fileName+".mkv")
	cmd.Dir = tempDir // Sets working directory to temp

	// Executes the command
	_, err = cmd.Output()
	if err != nil {
		return anirip.Error{Message: "There was an error while merging video and prefix", Err: err}
	}

	// Removes the temporary files we created as they are no longer needed
	os.Remove(tempDir + "\\" + "untrimmed." + fileName + ".mkv")
	os.Remove(tempDir + "\\" + "prefix." + fileName + ".mkv")
	os.Remove(tempDir + "\\" + "video." + fileName + ".mkv")
	os.Remove(tempDir + "\\" + "list." + fileName + ".txt")
	return nil
}

// Merges a VIDEO.mkv and a VIDEO.ass
func mergeSubtitles(fileName, audioLang, subtitleLang, engineDir, tempDir string) error {
	// Removes a stale temp files to avoid conflcts in func
	os.Remove(tempDir + "\\" + "unmerged." + fileName + ".mkv")

	// Recursively retries rename to temp filename before execution
	if err := anirip.Rename(tempDir+"\\"+fileName+".mkv", tempDir+"\\"+"unmerged."+fileName+".mkv", 10); err != nil {
		return err
	}

	path, err := filepath.Abs(engineDir + "\\ffmpeg.exe")
	if err != nil {
		return anirip.Error{Message: "Unable to find ffmpeg.exe in \\" + engineDir + "\\ directory", Err: err}
	}

	// Creates the command which we will use to merge our subtitles and video
	cmd := new(exec.Cmd)
	if subtitleLang == "" {
		cmd = exec.Command(path,
			"-i", "unmerged."+fileName+".mkv",
			"-c:v", "copy",
			"-c:a", "copy",
			"-metadata:s:a:0", "language="+audioLang, // sets audio language to passed audioLang
			"-y", fileName+".mkv")
	} else {
		cmd = exec.Command(path,
			"-i", "unmerged."+fileName+".mkv",
			"-f", "ass",
			"-i", fileName+".ass",
			"-c:v", "copy",
			"-c:a", "copy",
			"-metadata:s:a:0", "language="+audioLang, // sets audio language to passed audioLang
			"-metadata:s:s:0", "language="+subtitleLang, // sets subtitle language to subtitleLang
			"-disposition:s:0", "default",
			"-y", fileName+".mkv")
	}
	cmd.Dir = tempDir // Sets working directory to temp

	// Executes the command
	_, err = cmd.Output()
	if err != nil {
		return anirip.Error{Message: "There was an error while merging subtitles", Err: err}
	}

	// Removes old temp files
	os.Remove(tempDir + "\\" + fileName + ".ass")
	os.Remove(tempDir + "\\" + "unmerged." + fileName + ".mkv")
	return nil
}

// Uses ffprobe to find the length of a video and returns it in ms
func getVideoLength(fileName, engineDir, tempDir string) (int, error) {
	// Gets the ffprobe path which we will use to figure out the video length
	ffprobe, err := filepath.Abs(engineDir + "\\ffprobe.exe")
	if err != nil {
		return 0, anirip.Error{Message: "Unable to find ffprobe.exe in \\" + engineDir + "\\ directory", Err: err}
	}

	// Asks for the length of our video
	cmd := exec.Command(ffprobe,
		"-v", "error",
		"-show_entries", "format=duration",
		"-of", "default=noprint_wrappers=1:nokey=1",
		fileName)
	cmd.Dir = tempDir // Sets working directory to temp

	// Executes the command
	output, err := cmd.Output()
	if err != nil {
		return 0, anirip.Error{Message: "There was an error measuring " + fileName, Err: err}
	}

	// Grabs the output and parses it to a float64
	length, err := strconv.ParseFloat(strings.Replace(string(output), "\r\n", "", -1), 64)
	if err != nil {
		return 0, anirip.Error{Message: "There was an error parsing the length of " + fileName, Err: err}
	}
	return int(length * 1000), nil
}

// Uses ffprobe to get the exact framerate of the video and returns it as a float64
func getVideoFrameRate(fileName, engineDir, tempDir string) (float64, error) {
	// Gets the ffprobe path which we will use to figure out the video length
	ffprobe, err := filepath.Abs(engineDir + "\\ffprobe.exe")
	if err != nil {
		return 0, anirip.Error{Message: "Unable to find ffprobe.exe in \\" + engineDir + "\\ directory", Err: err}
	}

	// Asks for the length of our video
	cmd := exec.Command(ffprobe,
		"-v", "error",
		"-select_streams", "v:0",
		"-show_entries", "stream=avg_frame_rate",
		"-of", "default=noprint_wrappers=1:nokey=1",
		fileName)
	cmd.Dir = tempDir // Sets working directory to temp

	// Executes the command
	output, err := cmd.Output()
	if err != nil {
		return 0, anirip.Error{Message: "There was an error measuring " + fileName, Err: err}
	}

	// Output will be represented as a fraction which needs to be solved
	frameRateArray := strings.Split(strings.Replace(string(output), "\r\n", "", -1), "/")
	numerator, err := strconv.ParseFloat(frameRateArray[0], 64)
	if err != nil {
		return 0, anirip.Error{Message: "There was an error parsing the numerator of our framerate for " + fileName, Err: err}
	}
	denominator, err := strconv.ParseFloat(frameRateArray[1], 64)
	if err != nil {
		return 0, anirip.Error{Message: "There was an error parsing the denominator of our framerate for " + fileName, Err: err}
	}
	return numerator / denominator, nil
}

// Cleans up the mkv, optimizing it for playback
func cleanMKV(fileName, engineDir, tempDir string) error {
	// Removes a stale temp file to avoid conflcts in func
	os.Remove(tempDir + "\\" + "dirty." + fileName + ".mkv")

	// Recursively retries rename to temp filename before execution
	if err := anirip.Rename(tempDir+"\\"+fileName+".mkv", tempDir+"\\"+"dirty."+fileName+".mkv", 10); err != nil {
		return err
	}

	// Finds the path of mkclean.exe so we can perform system calls on it
	path, err := filepath.Abs(engineDir + "\\mkclean.exe")
	if err != nil {
		return anirip.Error{Message: "Unable to find mkclean.exe in \\" + engineDir + "\\ directory", Err: err}
	}

	// Creates the command which we will use to clean our mkv to "video.clean.mkv"
	cmd := exec.Command(path,
		"--optimize",
		"dirty."+fileName+".mkv",
		fileName+".mkv")
	cmd.Dir = tempDir // Sets working directory to temp

	// Executes the command
	_, err = cmd.Output()
	if err != nil {
		return anirip.Error{Message: "There was an error while optimizing our mkv", Err: err}
	}

	// Deletes the old, un-needed dirty mkv file
	os.Remove(tempDir + "\\" + "dirty." + fileName + ".mkv")
	return nil
}

// Gets user input from the user and unmarshalls it into the input
func getStandardUserInput(prefixText string, input *string) error {
	fmt.Printf(prefixText)
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		*input = scanner.Text()
		break
	}
	if err := scanner.Err(); err != nil {
		return anirip.Error{Message: "There was an error getting standard user input", Err: err}
	}
	return nil
}

// Blocks execution and waits for the user to press enter
func pause() {
	fmt.Print("Press 'Enter' to continue...")
	bufio.NewReader(os.Stdin).ReadBytes('\n')
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"io/ioutil"

	"github.com/jinzhu/gorm"
	_ "github.com/lib/pq"
)

type ThumbnailSize struct {
	Width  uint
	Height uint
}

var ThumbnailSizes = map[string]ThumbnailSize{
	"small": ThumbnailSize{100, 100},
	"large": ThumbnailSize{500, 500},
}

type Config struct {
	SourceFolderPath         string `json:"source_folder_path"`
	DestinationFolderPath    string `json:"destination_folder_path"`
	ThumbnailsFolderPath     string `json:"thumbnails_folder_path"`
	DatabaseConnectionString string `json:"database_connection_string"`
}

func LoadConfig(configPath string) (Config, error) {
	var config Config
	file, err := ioutil.ReadFile(configPath)
	if err != nil {
		return config, err
	}
	err = json.Unmarshal(file, &config)
	return config, err
}

func SetupDatabase(connectionString string) gorm.DB {
	db, err := gorm.Open("postgres", connectionString)
	if err != nil {
		panic("Unable to open database")
	}

	db.AutoMigrate(&Photo{}, &SimilarPhoto{})
	return db
}
<commit_msg>Add method to partition id into thumbnail path<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"path"

	"github.com/jinzhu/gorm"
	_ "github.com/lib/pq"
)

type ThumbnailSize struct {
	Width  uint
	Height uint
}

var ThumbnailSizes = map[string]ThumbnailSize{
	"small": ThumbnailSize{100, 100},
	"large": ThumbnailSize{500, 500},
}

type Config struct {
	SourceFolderPath         string `json:"source_folder_path"`
	DestinationFolderPath    string `json:"destination_folder_path"`
	ThumbnailsFolderPath     string `json:"thumbnails_folder_path"`
	DatabaseConnectionString string `json:"database_connection_string"`
}

func LoadConfig(configPath string) (Config, error) {
	var config Config
	file, err := ioutil.ReadFile(configPath)
	if err != nil {
		return config, err
	}
	err = json.Unmarshal(file, &config)
	return config, err
}

func SetupDatabase(connectionString string) gorm.DB {
	db, err := gorm.Open("postgres", connectionString)
	if err != nil {
		panic("Unable to open database")
	}

	db.AutoMigrate(&Photo{}, &SimilarPhoto{})
	return db
}

func PartitionIdAsPath(input int64) string {
	inputRunes := []rune(fmt.Sprintf("%09d", input))
	return path.Join(
		string(inputRunes[0:3]),
		string(inputRunes[3:6]),
		string(inputRunes[6:]),
	)
}
<|endoftext|>


<commit_before>package gorpc

import (
	"encoding/gob"
	"io"
	"log"
	"sync/atomic"
	"time"
)

const (
	// Default timeout for the request on the Client.
	DefaultRequestTimeout = 20 * time.Second

	// Default number of pending messages handled by Client and Server.
	DefaultPendingMessages = 32 * 1024

	// Default delay between message flushes on Client and Server.
	DefaultFlushDelay = 5 * time.Millisecond

	// Default size for Client and Server buffers (both write and read)
	DefaultBufferSize = 64 * 1024
)

// LoggerFunc is an error logging function to pass to gorpc.SetErrorLogger().
type LoggerFunc func(format string, args ...interface{})

var errorLogger = LoggerFunc(log.Printf)

// SetErrorLogger sets the given error logger to use in gorpc.
//
// By default log.Printf is used for error logging.
func SetErrorLogger(f LoggerFunc) {
	errorLogger = f
}

// RegisterType registers the given type to send via rpc.
//
// The client must register all the response types the server may send.
// The server must register all the request types the client may send.
//
// There is no need in registering base Go types such as int, string, bool,
// float64, etc. or arrays, slices and maps containing base Go types.
func RegisterType(x interface{}) {
	gob.Register(x)
}

type wireMessage struct {
	ID   uint64
	Data interface{}
}

func logError(format string, args ...interface{}) {
	errorLogger(format, args...)
}

// ConnStats provides connection statistics. Applied to both gorpc.Client
// and gorpc.Server.
type ConnStats struct {
	// The number of bytes written to the underlying connections.
	BytesWritten uint64

	// The number of bytes read from the underlying connections.
	BytesRead uint64

	// The number of Read() calls.
	ReadCalls uint64

	// The number of Read() errors.
	ReadErrors uint64

	// The number of Write() calls.
	WriteCalls uint64

	// The number of Write() errors.
	WriteErrors uint64

	// The number of Dial() calls.
	DialCalls uint64

	// The number of Dial() errors.
	DialErrors uint64

	// The number of Accept() calls.
	AcceptCalls uint64

	// The number of Accept() errors.
	AcceptErrors uint64
}

type writerCounter struct {
	w            io.Writer
	bytesWritten *uint64
	writeCalls   *uint64
	writeErrors  *uint64
}

type readerCounter struct {
	r          io.Reader
	bytesRead  *uint64
	readCalls  *uint64
	readErrors *uint64
}

func newWriterCounter(w io.Writer, s *ConnStats) io.Writer {
	return &writerCounter{
		w:            w,
		bytesWritten: &s.BytesWritten,
		writeCalls:   &s.WriteCalls,
		writeErrors:  &s.WriteErrors,
	}
}

func newReaderCounter(r io.Reader, s *ConnStats) io.Reader {
	return &readerCounter{
		r:          r,
		bytesRead:  &s.BytesRead,
		readCalls:  &s.ReadCalls,
		readErrors: &s.ReadErrors,
	}
}

func (w *writerCounter) Write(p []byte) (int, error) {
	n, err := w.w.Write(p)
	atomic.AddUint64(w.writeCalls, 1)
	if err != nil {
		atomic.AddUint64(w.writeErrors, 1)
	}
	atomic.AddUint64(w.bytesWritten, uint64(n))
	return n, err
}

func (r *readerCounter) Read(p []byte) (int, error) {
	n, err := r.r.Read(p)
	atomic.AddUint64(r.readCalls, 1)
	if err != nil {
		atomic.AddUint64(r.readErrors, 1)
	}
	atomic.AddUint64(r.bytesRead, uint64(n))
	return n, err
}
<commit_msg>Fixed lint warnings at http://go-lint.appspot.com/github.com/valyala/gorpc<commit_after>package gorpc

import (
	"encoding/gob"
	"io"
	"log"
	"sync/atomic"
	"time"
)

const (
	// DefaultRequestTimeout is the default timeout for client request.
	DefaultRequestTimeout = 20 * time.Second

	// DefaultPendingMessages is the default number of pending messages
	// handled by Client and Server.
	DefaultPendingMessages = 32 * 1024

	// DefaultFlushDelay is the default delay between message flushes
	// on Client and Server.
	DefaultFlushDelay = 5 * time.Millisecond

	// DefaultBufferSize is the default size for Client and Server buffers.
	DefaultBufferSize = 64 * 1024
)

// LoggerFunc is an error logging function to pass to gorpc.SetErrorLogger().
type LoggerFunc func(format string, args ...interface{})

var errorLogger = LoggerFunc(log.Printf)

// SetErrorLogger sets the given error logger to use in gorpc.
//
// By default log.Printf is used for error logging.
func SetErrorLogger(f LoggerFunc) {
	errorLogger = f
}

// RegisterType registers the given type to send via rpc.
//
// The client must register all the response types the server may send.
// The server must register all the request types the client may send.
//
// There is no need in registering base Go types such as int, string, bool,
// float64, etc. or arrays, slices and maps containing base Go types.
func RegisterType(x interface{}) {
	gob.Register(x)
}

type wireMessage struct {
	ID   uint64
	Data interface{}
}

func logError(format string, args ...interface{}) {
	errorLogger(format, args...)
}

// ConnStats provides connection statistics. Applied to both gorpc.Client
// and gorpc.Server.
type ConnStats struct {
	// The number of bytes written to the underlying connections.
	BytesWritten uint64

	// The number of bytes read from the underlying connections.
	BytesRead uint64

	// The number of Read() calls.
	ReadCalls uint64

	// The number of Read() errors.
	ReadErrors uint64

	// The number of Write() calls.
	WriteCalls uint64

	// The number of Write() errors.
	WriteErrors uint64

	// The number of Dial() calls.
	DialCalls uint64

	// The number of Dial() errors.
	DialErrors uint64

	// The number of Accept() calls.
	AcceptCalls uint64

	// The number of Accept() errors.
	AcceptErrors uint64
}

type writerCounter struct {
	w            io.Writer
	bytesWritten *uint64
	writeCalls   *uint64
	writeErrors  *uint64
}

type readerCounter struct {
	r          io.Reader
	bytesRead  *uint64
	readCalls  *uint64
	readErrors *uint64
}

func newWriterCounter(w io.Writer, s *ConnStats) io.Writer {
	return &writerCounter{
		w:            w,
		bytesWritten: &s.BytesWritten,
		writeCalls:   &s.WriteCalls,
		writeErrors:  &s.WriteErrors,
	}
}

func newReaderCounter(r io.Reader, s *ConnStats) io.Reader {
	return &readerCounter{
		r:          r,
		bytesRead:  &s.BytesRead,
		readCalls:  &s.ReadCalls,
		readErrors: &s.ReadErrors,
	}
}

func (w *writerCounter) Write(p []byte) (int, error) {
	n, err := w.w.Write(p)
	atomic.AddUint64(w.writeCalls, 1)
	if err != nil {
		atomic.AddUint64(w.writeErrors, 1)
	}
	atomic.AddUint64(w.bytesWritten, uint64(n))
	return n, err
}

func (r *readerCounter) Read(p []byte) (int, error) {
	n, err := r.r.Read(p)
	atomic.AddUint64(r.readCalls, 1)
	if err != nil {
		atomic.AddUint64(r.readErrors, 1)
	}
	atomic.AddUint64(r.bytesRead, uint64(n))
	return n, err
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/md5"
	"flag"
	"fmt"
	"html/template"
	"net/http"
	"strings"
	"sync"
	"time"

	"github.com/gorilla/mux"
	"go.skia.org/infra/go/auth"
	"go.skia.org/infra/go/common"
	"go.skia.org/infra/go/httputils"
	"go.skia.org/infra/go/sklog"
	sheets "google.golang.org/api/sheets/v4"
)

const (
	index = `<!DOCTYPE html>

    <title>Skia Drawing Contest</title>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

Skia Coding Contest

	  Create your entry at fiddle.skia.org and submit the URL of your completed fiddle here.

  {{range .Values}}

					<video autoplay loop width=256 height=256 src='https://fiddle.skia.org/i/{{.Hash}}_cpu.webm' poster='https://fiddle.skia.org/i/{{.Hash}}_raster.png'></video>

{{.Name}}

	{{end}}

`
)

// flags
var (
	local    = flag.Bool("local", false, "Running locally, not in prod.")
	port     = flag.String("port", ":8000", "HTTP service address (e.g., ':8000')")
	promPort = flag.String("prom_port", ":20000", "Metrics service address (e.g., ':10110')")
)

// Values contains the fiddle hash and the user's name of a single contest entry.
type Values struct {
	Hash string
	Name string
}

// Context is used to expand the HTML template.
type Context struct {
	Values []*Values
	Hash   string
}

var (
	indexTemplate = template.Must(template.New("index").Parse(index))
	ss            *sheets.Service

	// mutex protects values and valueHash.
	mutex      sync.Mutex
	values     []*Values
	valuesHash string // valueHash is an md5 hash of values.
)

func mainHandler(w http.ResponseWriter, r *http.Request) {
	mutex.Lock()
	defer mutex.Unlock()
	w.Header().Set("Content-Type", "text/html")
	if err := indexTemplate.Execute(w, Context{
		Values: values,
		Hash:   valuesHash,
	}); err != nil {
		sklog.Errorf("Failed to expand template: %s", err)
	}
}

// updateHandler returns valuesHash.
func updateHandler(w http.ResponseWriter, r *http.Request) {
	mutex.Lock()
	defer mutex.Unlock()
	if _, err := w.Write([]byte(valuesHash)); err != nil {
		sklog.Errorf("Failed to write hash response: %s", err)
	}
}

// hash computes an md5 hash of date in 'values'.
func hash(values []*Values) string {
	h := md5.New()
	for _, v := range values {
		_, _ = h.Write([]byte(v.Hash))
		_, _ = h.Write([]byte(v.Name))
	}
	return fmt.Sprintf("%x", h.Sum(nil))
}

// step does a single update of 'values' and 'valueHash' from the Google spreadsheet.
func step() error {
	v, err := ss.Spreadsheets.Values.Get("1Jbv7pWwH8NwHtoEBez1Bkwmk2wjhMnCk9UKttYqPjNQ", "A1:C200").Do()
	if err != nil {
		return err
	}
	// Should parse out the hash from the URL, also drop the first row since that's the header.
	newValues := []*Values{}
	for _, slice := range v.Values {
		parts := strings.Split(slice[1].(string), "/")
		if len(parts) != 5 {
			continue
		}
		hash := parts[4]
		if hash == "" {
			continue
		}
		newValues = append(newValues, &Values{
			Hash: hash,
			Name: slice[2].(string),
		})
	}
	mutex.Lock()
	defer mutex.Unlock()
	values = newValues
	valuesHash = hash(values)
	return nil
}

func main() {
	common.InitWithMust(
		"contest",
		common.PrometheusOpt(promPort),
		common.MetricsLoggingOpt(),
	)

	ts, err := auth.NewDefaultTokenSource(*local, sheets.SpreadsheetsReadonlyScope)
	if err != nil {
		sklog.Fatalf("Failed to auth: %s", err)
	}
	client := httputils.DefaultClientConfig().WithTokenSource(ts).With2xxOnly().Client()
	ss, err = sheets.New(client)
	if err != nil {
		sklog.Fatalf("Failed to create Sheets client: %s", err)
	}

	if err := step(); err != nil {
		sklog.Fatalf("Failed initial population of contest entries: %s", err)
	}
	go func() {
		for range time.Tick(15 * time.Second) {
			if err := step(); err != nil {
				sklog.Fatalf("Failed to refresh from Google Sheets: %s", err)
			}
		}
	}()
	r := mux.NewRouter()
	r.HandleFunc("/", mainHandler)
	r.HandleFunc("/update", updateHandler)
	h := httputils.LoggingGzipRequestResponse(r)
	if !*local {
		h = httputils.HealthzAndHTTPS(h)
	}
	http.Handle("/", h)
	sklog.Infoln("Ready to serve.")
	sklog.Fatal(http.ListenAndServe(*port, nil))
}
<commit_msg>Rebrand contest to gallery.skia.org<commit_after>package main

import (
	"crypto/md5"
	"flag"
	"fmt"
	"html/template"
	"net/http"
	"strings"
	"sync"
	"time"

	"github.com/gorilla/mux"
	"go.skia.org/infra/go/auth"
	"go.skia.org/infra/go/common"
	"go.skia.org/infra/go/httputils"
	"go.skia.org/infra/go/sklog"
	sheets "google.golang.org/api/sheets/v4"
)

const (
	index = `<!DOCTYPE html>

    <title>Skia Drawing Gallery</title>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

Skia Coding Gallery

	  Create your entry at fiddle.skia.org and submit the URL of your completed fiddle here.

  {{range .Values}}

					<video autoplay loop width=256 height=256 src='https://fiddle.skia.org/i/{{.Hash}}_cpu.webm' poster='https://fiddle.skia.org/i/{{.Hash}}_raster.png'></video>

{{.Name}}

	{{end}}

`
)

// flags
var (
	local    = flag.Bool("local", false, "Running locally, not in prod.")
	port     = flag.String("port", ":8000", "HTTP service address (e.g., ':8000')")
	promPort = flag.String("prom_port", ":20000", "Metrics service address (e.g., ':10110')")
)

// Values contains the fiddle hash and the user's name of a single contest entry.
type Values struct {
	Hash string
	Name string
}

// Context is used to expand the HTML template.
type Context struct {
	Values []*Values
	Hash   string
}

var (
	indexTemplate = template.Must(template.New("index").Parse(index))
	ss            *sheets.Service

	// mutex protects values and valueHash.
	mutex      sync.Mutex
	values     []*Values
	valuesHash string // valueHash is an md5 hash of values.
)

func mainHandler(w http.ResponseWriter, r *http.Request) {
	mutex.Lock()
	defer mutex.Unlock()
	w.Header().Set("Content-Type", "text/html")
	if err := indexTemplate.Execute(w, Context{
		Values: values,
		Hash:   valuesHash,
	}); err != nil {
		sklog.Errorf("Failed to expand template: %s", err)
	}
}

// updateHandler returns valuesHash.
func updateHandler(w http.ResponseWriter, r *http.Request) {
	mutex.Lock()
	defer mutex.Unlock()
	if _, err := w.Write([]byte(valuesHash)); err != nil {
		sklog.Errorf("Failed to write hash response: %s", err)
	}
}

// hash computes an md5 hash of date in 'values'.
func hash(values []*Values) string {
	h := md5.New()
	for _, v := range values {
		_, _ = h.Write([]byte(v.Hash))
		_, _ = h.Write([]byte(v.Name))
	}
	return fmt.Sprintf("%x", h.Sum(nil))
}

// step does a single update of 'values' and 'valueHash' from the Google spreadsheet.
func step() error {
	v, err := ss.Spreadsheets.Values.Get("1r0-HUMcZfeVS8SqaDDUZyrUJZy0AQ8lHhmWCI6gIlMw", "A1:C200").Do()
	if err != nil {
		return err
	}
	// Should parse out the hash from the URL, also drop the first row since that's the header.
	newValues := []*Values{}
	for _, slice := range v.Values {
		parts := strings.Split(slice[1].(string), "/")
		if len(parts) != 5 {
			continue
		}
		hash := parts[4]
		if hash == "" {
			continue
		}
		newValues = append(newValues, &Values{
			Hash: hash,
			Name: slice[2].(string),
		})
	}
	mutex.Lock()
	defer mutex.Unlock()
	values = newValues
	valuesHash = hash(values)
	return nil
}

func main() {
	common.InitWithMust(
		"contest",
		common.PrometheusOpt(promPort),
		common.MetricsLoggingOpt(),
	)

	ts, err := auth.NewDefaultTokenSource(*local, sheets.SpreadsheetsReadonlyScope)
	if err != nil {
		sklog.Fatalf("Failed to auth: %s", err)
	}
	client := httputils.DefaultClientConfig().WithTokenSource(ts).With2xxOnly().Client()
	ss, err = sheets.New(client)
	if err != nil {
		sklog.Fatalf("Failed to create Sheets client: %s", err)
	}

	if err := step(); err != nil {
		sklog.Fatalf("Failed initial population of contest entries: %s", err)
	}
	go func() {
		for range time.Tick(15 * time.Second) {
			if err := step(); err != nil {
				sklog.Fatalf("Failed to refresh from Google Sheets: %s", err)
			}
		}
	}()
	r := mux.NewRouter()
	r.HandleFunc("/", mainHandler)
	r.HandleFunc("/update", updateHandler)
	h := httputils.LoggingGzipRequestResponse(r)
	if !*local {
		h = httputils.HealthzAndHTTPS(h)
	}
	http.Handle("/", h)
	sklog.Infoln("Ready to serve.")
	sklog.Fatal(http.ListenAndServe(*port, nil))
}
<|endoftext|>


<commit_before>package validation

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

// ValidatePathParam ... validate for path parameter
func ValidatePathParam(name string, vs ...Validator) gin.HandlerFunc {
	return func(c *gin.Context) {
		for _, v := range vs {
			if !v.Validate(c.Params.ByName(name)) {
				c.AbortWithStatus(http.StatusBadRequest)
				return
			}
		}
	}
}

// ValidateRequestParam ... validate for request parameter
func ValidateRequestParam(name string, vs ...Validator) gin.HandlerFunc {
	return func(c *gin.Context) {
		for _, v := range vs {
			if !v.Validate(c.Request.FormValue(name)) {
				c.AbortWithStatus(http.StatusBadRequest)
				return
			}
		}
	}
}
<commit_msg>Update middleware.go<commit_after>package validation

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

// ValidatePathParam ... validate for path parameter
func ValidatePathParam(name string, vs ...Validator) gin.HandlerFunc {
	return func(c *gin.Context) {
		p := c.Params.ByName(name)
		for _, v := range vs {
			if !v.Validate(p) {
				c.AbortWithStatus(http.StatusBadRequest)
				return
			}
		}
	}
}

// ValidateRequestParam ... validate for request parameter
func ValidateRequestParam(name string, vs ...Validator) gin.HandlerFunc {
	return func(c *gin.Context) {
		p := c.Request.FormValue(name)
		for _, v := range vs {
			if !v.Validate(p) {
				c.AbortWithStatus(http.StatusBadRequest)
				return
			}
		}
	}
}
<|endoftext|>