Skip to content

Commit 63e2b12

Browse files
authored
Pull command (#21)
1 parent 7539825 commit 63e2b12

File tree

5 files changed

+181
-0
lines changed

5 files changed

+181
-0
lines changed

clients/api/client.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,3 +253,15 @@ func (c *Client) CreateApplicationVersion(applicationID string) (*CreateApplicat
253253
}
254254
return &resp, nil
255255
}
256+
257+
// GetOrganizationApplications retrieves all applications for an organization
258+
func (c *Client) GetOrganizationApplications(organizationID string) (*GetOrganizationApplicationsResponse, error) {
259+
path := fmt.Sprintf("/organizations/applications?organizationId=%s", organizationID)
260+
261+
var resp GetOrganizationApplicationsResponse
262+
err := c.doRequest("GET", path, nil, &resp)
263+
if err != nil {
264+
return nil, err
265+
}
266+
return &resp, nil
267+
}

clients/api/structs.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,15 @@ type CreateApplicationVersionRequest struct {
109109
type CreateApplicationVersionResponse struct {
110110
VersionID string `json:"versionId"`
111111
}
112+
113+
// ApplicationItem represents a single application in the list
114+
type ApplicationItem struct {
115+
ID string `json:"id"`
116+
Name string `json:"name"`
117+
GithubRepositoryName string `json:"githubRepositoryName"`
118+
}
119+
120+
// GetOrganizationApplicationsResponse represents the response from GET /organizations/applications
121+
type GetOrganizationApplicationsResponse struct {
122+
Applications []ApplicationItem `json:"applications"`
123+
}

clients/git/client.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,3 +158,14 @@ func PushToMain() error {
158158
cmd.Stderr = os.Stderr
159159
return cmd.Run()
160160
}
161+
162+
// Pull pulls the latest changes from the remote repository
163+
func Pull(repoDir string) error {
164+
cmd := exec.Command("git", "pull")
165+
if repoDir != "" {
166+
cmd.Dir = repoDir
167+
}
168+
cmd.Stdout = os.Stdout
169+
cmd.Stderr = os.Stderr
170+
return cmd.Run()
171+
}

cmd/app/app.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,5 @@ func init() {
2020
Cmd.AddCommand(startCmd)
2121
Cmd.AddCommand(deployCmd)
2222
Cmd.AddCommand(editCmd)
23+
Cmd.AddCommand(pullCmd)
2324
}

cmd/app/pull.go

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
package app
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"path/filepath"
7+
8+
"github.com/charmbracelet/huh"
9+
"github.com/major-technology/cli/clients/api"
10+
"github.com/major-technology/cli/clients/git"
11+
"github.com/major-technology/cli/clients/token"
12+
"github.com/major-technology/cli/singletons"
13+
"github.com/spf13/cobra"
14+
)
15+
16+
// pullCmd represents the app pull command
17+
var pullCmd = &cobra.Command{
18+
Use: "pull",
19+
Short: "Pull an application repository",
20+
Long: `Select and pull an application repository from your organization, then generate env and resources.`,
21+
Run: func(cmd *cobra.Command, args []string) {
22+
cobra.CheckErr(runPull(cmd))
23+
},
24+
}
25+
26+
func runPull(cmd *cobra.Command) error {
27+
// Get the default organization ID from keyring
28+
orgID, orgName, err := token.GetDefaultOrg()
29+
if err != nil {
30+
return fmt.Errorf("failed to get default organization: %w\nPlease run 'major org select' to set a default organization", err)
31+
}
32+
33+
cmd.Printf("Fetching applications for organization: %s\n", orgName)
34+
35+
// Get API client
36+
apiClient := singletons.GetAPIClient()
37+
if apiClient == nil {
38+
return fmt.Errorf("API client not initialized")
39+
}
40+
41+
// Get applications for the organization
42+
appsResp, err := apiClient.GetOrganizationApplications(orgID)
43+
if err != nil {
44+
return fmt.Errorf("failed to get applications: %w", err)
45+
}
46+
47+
if len(appsResp.Applications) == 0 {
48+
cmd.Println("No applications available for this organization")
49+
return nil
50+
}
51+
52+
// Let user select application
53+
selectedApp, err := selectApplication(cmd, appsResp.Applications)
54+
if err != nil {
55+
return fmt.Errorf("failed to select application: %w", err)
56+
}
57+
58+
cmd.Printf("Selected application: %s\n", selectedApp.Name)
59+
60+
// Determine the target directory (use the repository name)
61+
targetDir := filepath.Join(".", selectedApp.GithubRepositoryName)
62+
63+
// Check if the directory already exists
64+
if _, err := os.Stat(targetDir); err == nil {
65+
// Directory exists, just pull
66+
cmd.Printf("Directory '%s' already exists. Pulling latest changes...\n", targetDir)
67+
68+
if err := git.Pull(targetDir); err != nil {
69+
return fmt.Errorf("failed to pull repository: %w", err)
70+
}
71+
72+
cmd.Println("Successfully pulled latest changes")
73+
} else if os.IsNotExist(err) {
74+
return fmt.Errorf("directory '%s' does not exist. Please clone the repository first", targetDir)
75+
} else {
76+
return fmt.Errorf("failed to check directory: %w", err)
77+
}
78+
79+
// Generate env file
80+
cmd.Println("\nGenerating .env file...")
81+
envFilePath, numVars, err := generateEnvFile(targetDir)
82+
if err != nil {
83+
return fmt.Errorf("failed to generate .env file: %w", err)
84+
}
85+
cmd.Printf("Successfully generated .env file at: %s\n", envFilePath)
86+
cmd.Printf("Environment variables written: %d\n", numVars)
87+
88+
// Generate resources file
89+
cmd.Println("\nGenerating RESOURCES.md file...")
90+
resourcesFilePath, numResources, err := generateResourcesFile(targetDir)
91+
if err != nil {
92+
return fmt.Errorf("failed to generate RESOURCES.md file: %w", err)
93+
}
94+
cmd.Printf("Successfully generated RESOURCES.md file at: %s\n", resourcesFilePath)
95+
cmd.Printf("Resources written: %d\n", numResources)
96+
97+
cmd.Println("\n✓ Application pull complete!")
98+
99+
return nil
100+
}
101+
102+
// selectApplication prompts the user to select an application from the list
103+
func selectApplication(cmd *cobra.Command, apps []api.ApplicationItem) (*api.ApplicationItem, error) {
104+
if len(apps) == 0 {
105+
return nil, fmt.Errorf("no applications available")
106+
}
107+
108+
// If only one application, automatically select it
109+
if len(apps) == 1 {
110+
cmd.Printf("Only one application available. Automatically selecting it.\n")
111+
return &apps[0], nil
112+
}
113+
114+
// Create options for huh select
115+
options := make([]huh.Option[string], len(apps))
116+
for i, app := range apps {
117+
options[i] = huh.NewOption(app.Name, app.ID)
118+
}
119+
120+
var selectedID string
121+
122+
// Create and run the select form
123+
form := huh.NewForm(
124+
huh.NewGroup(
125+
huh.NewSelect[string]().
126+
Title("Select an application to pull").
127+
Options(options...).
128+
Value(&selectedID),
129+
),
130+
)
131+
132+
err := form.Run()
133+
if err != nil {
134+
return nil, fmt.Errorf("failed to get selection: %w", err)
135+
}
136+
137+
// Find the selected application
138+
for i, app := range apps {
139+
if app.ID == selectedID {
140+
return &apps[i], nil
141+
}
142+
}
143+
144+
return nil, fmt.Errorf("selected application not found")
145+
}

0 commit comments

Comments
 (0)