this repo has no description

add install command

Changed files
+115
cmd
+114
cmd/install.go
··· 1 + package cmd 2 + 3 + import ( 4 + "fmt" 5 + "os/exec" 6 + "path/filepath" 7 + "regexp" 8 + "slices" 9 + "strings" 10 + 11 + "github.com/spf13/cobra" 12 + ) 13 + 14 + var ghRepoRegexp = regexp.MustCompile(`^[a-z\d](?:[a-z\d-]{0,38}[a-z\d])?/[a-zA-Z0-9_.-]+$`) 15 + 16 + func NewCmdInstall() *cobra.Command { 17 + cmd := &cobra.Command{ 18 + Use: "install <repo> [app]", 19 + Args: cobra.RangeArgs(1, 2), 20 + Short: "Install an app", 21 + Long: "Install an app a remote repository.", 22 + PreRunE: func(cmd *cobra.Command, args []string) error { 23 + if _, err := exec.LookPath("git"); err != nil { 24 + return fmt.Errorf("git is not installed: %w", err) 25 + } 26 + 27 + return nil 28 + }, 29 + RunE: func(cmd *cobra.Command, args []string) error { 30 + repoUrl := args[0] 31 + // check if repo is <user>/<repo> 32 + if ghRepoRegexp.MatchString(repoUrl) { 33 + repoUrl = fmt.Sprintf("https://github.com/%s.git", repoUrl) 34 + } 35 + 36 + var appName string 37 + if len(args) > 1 { 38 + appName = args[1] 39 + } else { 40 + parts := strings.Split(repoUrl, "/") 41 + if len(parts) < 2 { 42 + return fmt.Errorf("invalid repository URL: %s", repoUrl) 43 + } 44 + 45 + repoName := strings.TrimSuffix(parts[len(parts)-1], ".git") 46 + appName = strings.TrimPrefix(repoName, "smallweb-") 47 + } 48 + appDir := filepath.Join(k.String("dir"), appName) 49 + if _, err := exec.LookPath(appDir); err == nil { 50 + return fmt.Errorf("directory already exists: %s", appDir) 51 + } 52 + 53 + branches, err := listBranches(repoUrl) 54 + if err != nil { 55 + return fmt.Errorf("failed to list branches: %w", err) 56 + } 57 + 58 + if len(branches) == 0 { 59 + return fmt.Errorf("no branches found in the repository") 60 + } 61 + 62 + if slices.Contains(branches, "smallweb") { 63 + cmd.PrintErrf("Cloning branch 'smallweb' from %s to %s...\n", repoUrl, appDir) 64 + cloneCmd := exec.Command("git", "clone", "--single-branch", "--branch", "smallweb", repoUrl, appDir) 65 + if err := cloneCmd.Run(); err != nil { 66 + return fmt.Errorf("failed to clone branch: %w", err) 67 + } 68 + 69 + cmd.PrintErrf("App %s available at https://%s.%s\n", appName, appName, k.String("domain")) 70 + return nil 71 + } 72 + 73 + cmd.PrintErrf("Cloning %s to %s...\n", repoUrl, appDir) 74 + cloneCmd := exec.Command("git", "clone", "--single-branch", appDir) 75 + if err := cloneCmd.Run(); err != nil { 76 + return fmt.Errorf("failed to clone repository: %w", err) 77 + } 78 + 79 + cmd.Printf("App %s available at https://%s.%s\n", appName, appName, k.String("domain")) 80 + return nil 81 + }, 82 + } 83 + 84 + cmd.Flags().StringP("url", "u", "", "URL of the app to install") 85 + cmd.Flags().StringP("dir", "d", "", "Directory to install the app in") 86 + 87 + return cmd 88 + } 89 + 90 + func listBranches(remote string) ([]string, error) { 91 + out, err := exec.Command("git", "ls-remote", "--heads", remote).Output() 92 + if err != nil { 93 + return nil, fmt.Errorf("failed to list branches: %w", err) 94 + } 95 + 96 + lines := strings.Split(string(out), "\n") 97 + branches := make([]string, 0, len(lines)) 98 + 99 + for _, line := range lines { 100 + if !strings.Contains(line, "refs/heads/") { 101 + continue 102 + } 103 + 104 + parts := strings.Fields(line) 105 + if len(parts) < 2 { 106 + continue 107 + } 108 + 109 + branch := strings.TrimPrefix(parts[1], "refs/heads/") 110 + branches = append(branches, branch) 111 + } 112 + 113 + return branches, nil 114 + }
+1
cmd/root.go
··· 151 151 rootCmd.AddCommand(NewCmdSecrets()) 152 152 rootCmd.AddCommand(NewCmdGitReceivePack()) 153 153 rootCmd.AddCommand(NewCmdGitUploadPack()) 154 + rootCmd.AddCommand(NewCmdInstall()) 154 155 155 156 if _, ok := os.LookupEnv("SMALLWEB_DISABLE_COMPLETIONS"); ok { 156 157 rootCmd.CompletionOptions.DisableDefaultCmd = true