1490 lines · 38.4 KB · Go
RawDownload
package main import (	"context"	"errors"	"fmt"	"io"	"log"	"net/url"	"os"	"os/exec"	"path/filepath"	"strings" 	"git.sr.ht/~emersion/gqlclient"	"github.com/dustin/go-humanize"	"github.com/spf13/cobra" 	"git.sr.ht/~xenrox/hut/srht/gitsrht"	"git.sr.ht/~xenrox/hut/termfmt") func newGitCommand() *cobra.Command {	cmd := &cobra.Command{		Use:   "git",		Short: "Use the git API",	}	cmd.AddCommand(newGitArtifactCommand())	cmd.AddCommand(newGitCreateCommand())	cmd.AddCommand(newGitListCommand())	cmd.AddCommand(newGitDeleteCommand())	cmd.AddCommand(newGitCloneCommand())	cmd.AddCommand(newGitSetupCommand())	cmd.AddCommand(newGitACLCommand())	cmd.AddCommand(newGitShowCommand())	cmd.AddCommand(newGitUserWebhookCommand())	cmd.AddCommand(newGitUpdateCommand())	cmd.AddCommand(newGitWebhookCommand())	cmd.PersistentFlags().StringP("repo", "r", "", "name of repository")	cmd.RegisterFlagCompletionFunc("repo", completeGitRepo)	return cmd} func newGitCreateCommand() *cobra.Command {	var visibility, desc, importURL string	var clone bool	run := func(cmd *cobra.Command, args []string) {		ctx := cmd.Context()		c := createClient("git", cmd) 		gitVisibility, err := gitsrht.ParseVisibility(visibility)		if err != nil {			log.Fatal(err)		} 		var importURLPtr *string		if importURL != "" {			importURLPtr = &importURL		} 		var description *string		if desc != "" {			description = &desc		} 		repo, err := gitsrht.CreateRepository(c.Client, ctx, args[0],			gitVisibility, description, importURLPtr)		if err != nil {			log.Fatal(err)		} 		log.Printf("Created repository %q\n", repo.Name) 		ver, err := gitsrht.SshSettings(c.Client, ctx)		if err != nil {			log.Fatalf("failed to retrieve settings: %v", err)		} 		u, err := url.Parse(c.BaseURL)		if err != nil {			log.Fatalf("failed to parse base URL: %v", err)		} 		cloneURL := fmt.Sprintf("%s@%s:%s/%s", ver.Settings.SshUser, u.Hostname(),			repo.Owner.CanonicalName, repo.Name) 		if clone {			cloneCmd := exec.Command("git", "clone", cloneURL)			cloneCmd.Stdin = os.Stdin			cloneCmd.Stdout = os.Stdout			cloneCmd.Stderr = os.Stderr 			err = cloneCmd.Run()			if err != nil {				log.Fatal(err)			}		} else {			fmt.Printf("%s\n", cloneURL)		}	} 	cmd := &cobra.Command{		Use:               "create <name>",		Short:             "Create a repository",		Args:              cobra.ExactArgs(1),		ValidArgsFunction: cobra.NoFileCompletions,		Run:               run,	}	cmd.Flags().StringVarP(&visibility, "visibility", "v", "public", "repo visibility")	cmd.RegisterFlagCompletionFunc("visibility", completeVisibility)	cmd.Flags().StringVarP(&desc, "description", "d", "", "repo description")	cmd.RegisterFlagCompletionFunc("description", cobra.NoFileCompletions)	cmd.Flags().BoolVarP(&clone, "clone", "c", false, "autoclone repo")	cmd.Flags().StringVar(&importURL, "import-url", "", "import repo from given URL")	cmd.RegisterFlagCompletionFunc("import-url", cobra.NoFileCompletions)	return cmd} func newGitListCommand() *cobra.Command {	var count, printed int	run := func(cmd *cobra.Command, args []string) {		ctx := cmd.Context()		var cursor *gitsrht.Cursor		var owner, instance string		if len(args) > 0 {			owner, instance = parseOwnerName(args[0])		} 		err := pagerify(func(p pager) error {			var repos *gitsrht.RepositoryCursor			if len(owner) > 0 {				c := createClientWithInstance("git", cmd, instance)				username := strings.TrimLeft(owner, ownerPrefixes)				user, err := gitsrht.RepositoriesByUser(c.Client, ctx, username, cursor)				if err != nil {					return err				} else if user == nil {					return errors.New("no such user")				}				repos = user.Repositories			} else {				c := createClient("git", cmd)				var err error				repos, err = gitsrht.Repositories(c.Client, ctx, cursor)				if err != nil {					return err				}			} 			for _, repo := range repos.Results {				if count != 0 && printed == count {					return pagerDone				}				printGitRepo(p, &repo)				printed += 1			} 			cursor = repos.Cursor			if p.IsDone(cursor, len(repos.Results)) {				return pagerDone			} 			return nil		}, count)		if err != nil {			log.Fatal(err)		}	} 	cmd := &cobra.Command{		Use:               "list [user]",		Short:             "List repos",		Args:              cobra.MaximumNArgs(1),		ValidArgsFunction: cobra.NoFileCompletions,		Run:               run,	}	cmd.Flags().IntVar(&count, "count", 0, "number of repos to fetch")	cmd.RegisterFlagCompletionFunc("count", cobra.NoFileCompletions)	return cmd} func printGitRepo(w io.Writer, repo *gitsrht.Repository) {	fmt.Fprintf(w, "%s (%s)\n", termfmt.Bold.String(repo.Name), repo.Visibility.TermString())	if repo.Description != nil && *repo.Description != "" {		fmt.Fprintf(w, "  %s\n", *repo.Description)	}	fmt.Fprintln(w)} func newGitDeleteCommand() *cobra.Command {	var autoConfirm bool	run := func(cmd *cobra.Command, args []string) {		ctx := cmd.Context() 		var name, owner, instance string		if len(args) > 0 {			name, owner, instance = parseResourceName(args[0])		} else {			var err error			name, owner, instance, err = getGitRepoName(ctx, cmd)			if err != nil {				log.Fatal(err)			}		} 		c := createClientWithInstance("git", cmd, instance)		id, _, err := getGitRepoID(c, ctx, name, owner)		if err != nil {			log.Fatal(err)		} 		if !autoConfirm && !getConfirmation(fmt.Sprintf("Do you really want to delete the repo %s", name)) {			log.Println("Aborted")			return		}		repo, err := gitsrht.DeleteRepository(c.Client, ctx, id)		if err != nil {			log.Fatal(err)		} 		log.Printf("Deleted repository %q\n", repo.Name)	} 	cmd := &cobra.Command{		Use:               "delete [repo]",		Short:             "Delete a repository",		Args:              cobra.MaximumNArgs(1),		ValidArgsFunction: completeGitRepo,		Run:               run,	}	cmd.Flags().BoolVarP(&autoConfirm, "yes", "y", false, "auto confirm")	return cmd} func newGitCloneCommand() *cobra.Command {	run := func(cmd *cobra.Command, args []string) {		ctx := cmd.Context()		cloneURL := args[0]		if !isURL(cloneURL) && !strings.Contains(cloneURL, "@") {			name, owner, instance := parseResourceName(cloneURL)			c := createClientWithInstance("git", cmd, instance) 			ver, err := gitsrht.SshSettings(c.Client, ctx)			if err != nil {				log.Fatalf("failed to retrieve settings: %v", err)			} 			if owner == "" {				me, err := gitsrht.MyUsername(c.Client, ctx)				if err != nil {					log.Fatalf("failed to retrieve username: %v", err)				} 				owner = me.CanonicalName			} 			u, err := url.Parse(c.BaseURL)			if err != nil {				log.Fatalf("failed to parse base URL: %v", err)			}			cloneURL = fmt.Sprintf("%s@%s:%s/%s", ver.Settings.SshUser, u.Hostname(), owner, name)		} 		log.Println("Cloning repository")		cloneCmd := exec.Command("git", "clone", cloneURL)		cloneCmd.Stdin = os.Stdin		cloneCmd.Stdout = os.Stdout		cloneCmd.Stderr = os.Stderr 		err := cloneCmd.Run()		if err != nil {			log.Fatalf("failed to clone repo: %v", err)		} 		s := strings.Split(cloneURL, "/")		dir, err := os.Getwd()		if err != nil {			log.Fatalf("failed to get current directory: %v", err)		} 		err = os.Chdir(filepath.Join(dir, s[len(s)-1]))		if err != nil {			log.Fatalf("failed to change current working directory: %v", err)		} 		cfg, err := loadProjectConfig()		if err != nil {			log.Fatalf("failed to load project config: %v", err)		} 		if cfg != nil {			if cfg.DevList != "" {				log.Printf("Configuring repository for %q\n", "git send-email")				err = setGitSendEmailTo(ctx, cfg.DevList)				if err != nil {					log.Fatalf("failed to set %q: %v", "git config sendemail.to", err)				}			} 			if cfg.PatchPrefix {				prefixCmd := exec.Command("git", "config", "format.subjectPrefix", fmt.Sprintf("PATCH %s", s[len(s)-1]))				prefixCmd.Stdin = os.Stdin				prefixCmd.Stdout = os.Stdout				prefixCmd.Stderr = os.Stderr 				err = prefixCmd.Run()				if err != nil {					log.Fatalf("failed to set %q: %v", "git config format.subjectPrefix", err)				}			}		}	}	cmd := &cobra.Command{		Use:               "clone <repo>",		Short:             "Clone a repository",		Args:              cobra.ExactArgs(1),		ValidArgsFunction: cobra.NoFileCompletions,		Run:               run,	}	return cmd} func newGitSetupCommand() *cobra.Command {	var force bool	run := func(cmd *cobra.Command, args []string) {		ctx := cmd.Context()		success := false 		checkCmd := exec.Command("git", "config", "sendemail.to")		checkCmd.Stdin = os.Stdin		checkCmd.Stderr = os.Stderr 		b, err := checkCmd.Output()		if err == nil {			if strings.TrimSpace(string(b)) != "" && !force {				log.Println("Repository is already configured. Skipping setup.")				return			}		} 		if strings.TrimSpace(string(b)) != "" && !force {			log.Println("Repository is already configured. Skipping setup.")			return		} 		// TODO: hub.sr.ht API, .b4-config		cfg, err := loadProjectConfig()		if err != nil {			log.Fatalf("failed to load project config: %v", err)		} 		if cfg != nil {			if cfg.DevList != "" {				err = setGitSendEmailTo(ctx, cfg.DevList)				if err != nil {					log.Fatalf("failed to set %q: %v", "git config sendemail.to", err)				}				success = true			} 			if cfg.PatchPrefix {				name, _, _, err := guessGitRepoName(ctx, cmd)				if err != nil {					log.Fatalf("failed to get repository name: %v", err)				} 				prefixCmd := exec.Command("git", "config", "format.subjectPrefix", fmt.Sprintf("PATCH %s", name))				prefixCmd.Stdin = os.Stdin				prefixCmd.Stdout = os.Stdout				prefixCmd.Stderr = os.Stderr 				err = prefixCmd.Run()				if err != nil {					log.Fatalf("failed to set %q: %v", "git config format.subjectPrefix", err)				}			}		} 		if success {			log.Println("Configured repository")		} else {			log.Fatalln("Failed to configure repository")		}	}	cmd := &cobra.Command{		Use:   "setup",		Short: "Setup a repository for `git send-email`",		Args:  cobra.ExactArgs(0),		Run:   run,	}	cmd.Flags().BoolVarP(&force, "force", "f", false, "force setup even if already configured")	return cmd} func newGitArtifactCommand() *cobra.Command {	cmd := &cobra.Command{		Use:   "artifact",		Short: "Manage artifacts",	}	cmd.AddCommand(newGitArtifactUploadCommand())	cmd.AddCommand(newGitArtifactListCommand())	cmd.AddCommand(newGitArtifactDeleteCommand())	return cmd} func newGitArtifactUploadCommand() *cobra.Command {	var rev string	run := func(cmd *cobra.Command, args []string) {		ctx := cmd.Context()		repoName, owner, instance, err := getGitRepoName(ctx, cmd)		if err != nil {			log.Fatal(err)		} 		c := createClientWithInstance("git", cmd, instance)		c.HTTP.Timeout = fileTransferTimeout		repoID, _, err := getGitRepoID(c, ctx, repoName, owner)		if err != nil {			log.Fatal(err)		} 		if rev == "" {			var err error			rev, err = guessRev()			if err != nil {				log.Fatal(err)			}		} 		for _, filename := range args {			f, err := os.Open(filename)			if err != nil {				log.Fatalf("failed to open input file: %v", err)			}			defer f.Close() 			file := gqlclient.Upload{Filename: filepath.Base(filename), Body: f}			artifact, err := gitsrht.UploadArtifact(c.Client, ctx, repoID, rev, file)			if err != nil {				log.Fatal(err)			} 			log.Printf("Uploaded %q\n", artifact.Filename)		}	} 	cmd := &cobra.Command{		Use:   "upload <filename...>",		Short: "Upload artifacts",		Args:  cobra.MinimumNArgs(1),		Run:   run,	}	cmd.Flags().StringVar(&rev, "rev", "", "revision tag")	cmd.RegisterFlagCompletionFunc("rev", completeRev)	return cmd} func newGitArtifactListCommand() *cobra.Command {	// TODO: Filter by rev 	run := func(cmd *cobra.Command, args []string) {		ctx := cmd.Context()		repoName, owner, instance, err := getGitRepoName(ctx, cmd)		if err != nil {			log.Fatal(err)		} 		c := createClientWithInstance("git", cmd, instance) 		var (			username string			user     *gitsrht.User		) 		if owner != "" {			username = strings.TrimLeft(owner, ownerPrefixes)			user, err = gitsrht.ListArtifactsByUser(c.Client, ctx, username, repoName)		} else {			user, err = gitsrht.ListArtifacts(c.Client, ctx, repoName)		} 		if err != nil {			log.Fatal(err)		} else if user == nil {			log.Fatalf("no such user %q", username)		} else if user.Repository == nil {			log.Fatalf("no such repository %q", repoName)		} 		for _, ref := range user.Repository.References.Results {			if len(ref.Artifacts.Results) == 0 {				continue			} 			name := ref.Name[strings.LastIndex(ref.Name, "/")+1:]			fmt.Printf("Tag %s:\n", termfmt.Bold.String(name))			for _, artifact := range ref.Artifacts.Results {				fmt.Printf("  %s %s\n", termfmt.DarkYellow.Sprintf("#%d", artifact.Id), artifact.Filename)			}		}

Lines 1–500 of 1490.