Skip to content

Commit f0df9ac

Browse files
committed
Add linode provider updates + general cleanup/refactor
1 parent 26a87c3 commit f0df9ac

File tree

31 files changed

+1832
-297
lines changed

31 files changed

+1832
-297
lines changed

AGENTS.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -320,14 +320,18 @@ When adding framework detection:
320320

321321
### Provider Registry System
322322

323-
Lightfold uses a provider registry pattern that makes adding new cloud providers trivial. All providers implement the `Provider` interface and auto-register themselves at init time.
323+
Lightfold uses a provider registry pattern that makes adding new cloud providers straightforward. All providers implement the `Provider` interface and auto-register themselves at init time.
324324

325325
**Key Components:**
326326
1. **Provider Interface** (`pkg/providers/provider.go`): Defines standard methods all providers must implement
327327
2. **Provider Registry** (`pkg/providers/registry.go`): Central factory for creating provider instances
328328
3. **Provider Implementations**: Self-contained packages (e.g., `pkg/providers/digitalocean/`, `pkg/providers/hetzner/`)
329329

330330
**Adding a New Provider:**
331+
332+
See **[docs/ADDING_NEW_PROVIDERS.md](docs/ADDING_NEW_PROVIDERS.md)** for a complete step-by-step integration guide.
333+
334+
Quick overview:
331335
```go
332336
// 1. Create pkg/providers/newprovider/client.go
333337
package newprovider
@@ -345,7 +349,11 @@ func (c *Client) DisplayName() string { return "New Provider" }
345349
// ... implement remaining interface methods
346350
```
347351

348-
That's it! The provider is now available throughout the application with zero changes to orchestrator, CLI, or config layers.
352+
**Critical Integration Points** (see guide for details):
353+
- Import provider in `pkg/deploy/orchestrator.go` (blank import)
354+
- Add config struct to `pkg/config/config.go`
355+
- Add switch cases to 5 key functions (orchestrator + utilities)
356+
- Test provisioning, IP recovery, and multi-app flows
349357

350358
### Deployment Flow Architecture
351359

cmd/deploy.go

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ Examples:
315315
fmt.Printf("%s %s\n", deploySuccessStyle.Render("✓"), deployMutedStyle.Render("Configuring environment variables..."))
316316
}
317317

318-
if err := executor.DeployWithHealthCheck(releasePath, 5, 3*time.Second); err != nil {
318+
if err := executor.DeployWithHealthCheck(releasePath, target.Port, 5, 3*time.Second); err != nil {
319319
state.MarkPushFailed(targetName, fmt.Sprintf("deployment failed: %v", err))
320320
fmt.Fprintf(os.Stderr, "Error during deployment: %v\n", err)
321321
os.Exit(1)
@@ -349,13 +349,26 @@ Examples:
349349
fmt.Sprintf("%s %s", deployMutedStyle.Render("Release:"), deployValueStyle.Render(releaseTimestamp)),
350350
}
351351

352-
// Add port information
352+
// Check if this is a multi-app deployment
353+
serverState, serverStateErr := state.GetServerState(sshProviderCfg.GetIP())
354+
isMultiApp := serverStateErr == nil && len(serverState.DeployedApps) > 1
355+
356+
// Add port and access information
353357
if target.Port > 0 {
354358
if target.Domain != nil && target.Domain.Domain != "" {
355-
successLines = append(successLines, fmt.Sprintf("%s %s (proxied via nginx)", deployMutedStyle.Render("Port:"), deployValueStyle.Render(fmt.Sprintf("%d", target.Port))))
359+
// Has custom domain - nginx proxies to app port
360+
// Don't show port for single-app with domain
361+
if isMultiApp {
362+
successLines = append(successLines, fmt.Sprintf("%s %s (proxied via nginx)", deployMutedStyle.Render("Port:"), deployValueStyle.Render(fmt.Sprintf("%d", target.Port))))
363+
}
364+
} else if !isMultiApp {
365+
// Single-app without domain - nginx proxies port 80 to app port
366+
// Don't show internal port, just the access URL
367+
successLines = append(successLines, fmt.Sprintf("%s %s", deployMutedStyle.Render("Access:"), deployValueStyle.Render(fmt.Sprintf("http://%s", sshProviderCfg.GetIP()))))
356368
} else {
369+
// Multi-app without domain - needs domain or direct port access
357370
successLines = append(successLines, fmt.Sprintf("%s %s", deployMutedStyle.Render("Port:"), deployValueStyle.Render(fmt.Sprintf("%d", target.Port))))
358-
successLines = append(successLines, fmt.Sprintf("%s %s", deployMutedStyle.Render("Access:"), deployValueStyle.Render(fmt.Sprintf("http://%s:%d", sshProviderCfg.GetIP(), target.Port))))
371+
successLines = append(successLines, fmt.Sprintf("%s %s", deployMutedStyle.Render("Access:"), deployValueStyle.Render(fmt.Sprintf("http://%s:%d (direct port access)", sshProviderCfg.GetIP(), target.Port))))
359372
}
360373
}
361374

cmd/push.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ Examples:
267267
fmt.Printf("%s %s\n", pushSuccessStyle.Render("✓"), pushMutedStyle.Render("Configuring environment variables..."))
268268
}
269269

270-
if err := executor.DeployWithHealthCheck(releasePath, 5, 3*time.Second); err != nil {
270+
if err := executor.DeployWithHealthCheck(releasePath, target.Port, 5, 3*time.Second); err != nil {
271271
state.MarkPushFailed(targetNameResolved, fmt.Sprintf("deployment failed: %v", err))
272272
fmt.Fprintf(os.Stderr, "Error during deployment: %v\n", err)
273273
os.Exit(1)

cmd/root.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
"github.com/spf13/cobra"
1414
)
1515

16-
const Version = "0.1.2"
16+
const Version = "0.1.3"
1717

1818
var (
1919
jsonOutput bool

cmd/ui/sequential/model.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -559,7 +559,7 @@ func (m FlowModel) renderHelp() string {
559559
func (m FlowModel) renderCompleted() string {
560560
mutedStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("245"))
561561

562-
var content string
562+
var lines []string
563563

564564
for i, step := range m.Steps {
565565
if stepState, exists := m.StepStates[i]; exists {
@@ -593,17 +593,25 @@ func (m FlowModel) renderCompleted() string {
593593
}
594594

595595
if value != "" {
596-
content += fmt.Sprintf("%s %s\n", label, value)
596+
lines = append(lines, fmt.Sprintf("%s %s", label, value))
597597
}
598598
}
599599
}
600600

601+
content := ""
602+
for i, line := range lines {
603+
content += line
604+
if i < len(lines)-1 {
605+
content += "\n"
606+
}
607+
}
608+
601609
mutedBox := lipgloss.NewStyle().
602610
Border(lipgloss.RoundedBorder()).
603611
BorderForeground(lipgloss.Color("245")).
604-
Padding(0, 1)
612+
Padding(0, 1, 0, 1)
605613

606-
return "\n" + mutedBox.Render(mutedStyle.Render(content)) + "\n"
614+
return "\n" + mutedBox.Render(mutedStyle.Render(content))
607615
}
608616

609617
func (m FlowModel) GetResults() map[string]string {

cmd/utils/provider_recovery.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,14 @@ func updateProviderConfigWithIP(target *config.TargetConfig, providerName, ip, s
8585
vultrConfig.IP = ip
8686
vultrConfig.InstanceID = serverID
8787
return target.SetProviderConfig("vultr", vultrConfig)
88+
case "linode":
89+
linodeConfig, err := target.GetLinodeConfig()
90+
if err != nil {
91+
return err
92+
}
93+
linodeConfig.IP = ip
94+
linodeConfig.InstanceID = serverID
95+
return target.SetProviderConfig("linode", linodeConfig)
8896
default:
8997
return fmt.Errorf("unsupported provider: %s", providerName)
9098
}

cmd/utils/server_setup.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,15 @@ func SetupTargetWithExistingServer(target *config.TargetConfig, serverIP string,
9797
Provisioned: false,
9898
}
9999
target.SetProviderConfig("vultr", vultrConfig)
100+
case "linode":
101+
linodeConfig := &config.LinodeConfig{
102+
IP: serverIP,
103+
InstanceID: serverState.ServerID,
104+
SSHKey: sshKey,
105+
Username: "deploy",
106+
Provisioned: false,
107+
}
108+
target.SetProviderConfig("linode", linodeConfig)
100109
case "byos":
101110
// For BYOS, use the SSH key we found
102111
doConfig := &config.DigitalOceanConfig{

0 commit comments

Comments
 (0)