+114
cmd/install.go
+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
+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