Skip to content

Commit 8690d29

Browse files
committed
added docker passthrough, host key path absolute path fix
1 parent c763cc0 commit 8690d29

File tree

7 files changed

+87
-29
lines changed

7 files changed

+87
-29
lines changed

.goreleaser.yaml

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -255,29 +255,19 @@ release:
255255
header: |
256256
## Feature Update with Graft 🚀
257257
258-
### V2.3 Highlights:
258+
### V2.4 Highlights:
259+
1. **Docker Passthrough Commands**: Execute docker commands directly on remote hosts or specific registries via Graft.
260+
2. **Bug Fixes and Improvements**
261+
262+
### V2 Series Highlights (v2.0 - v2.3):
259263
1. **Rollback Feature**: Added rollback functionality to revert to previous deployments.
260-
2. **Webhook Mapping**: Added webhook mapping functionality to map webhook domains to servers.
261-
3. **Multi Environment Support**: Added multi environment support to manage multiple deployment environments.
262-
- Environment Creation
263-
- Environment Specific Configuration
264-
- Environment Specific Workflow Generation
265-
- Environment Specific Host and Deployment
266-
- Environment Specific Commands
267-
- Environment Specific DNS Configurations
268-
- Environment Specific Rollback
269-
4. **Bug Fixes and Improvements**
270-
271-
### V2 Highlights:
272-
This release introduces significant enhancements to Graft, moving from basic local-to-server deployments to a more robust CI/CD-driven workflow using GitHub Actions and our new `graft-hook` service.
273-
274-
1. **Git Remote Modes**: Move away from direct local-to-server channels. Deployments now go through GitHub Workflows and CI/CD channels.
275-
2. **Graft-Hook**: A new webhook service personalized specifically for Graft-based servers, maintaining a zero-config solution with proper security.
276-
3. **Automated GitHub Workflows**: Graft now generates production-ready workflows automatically from your `graft-compose.yml` context.
277-
4. **Enhanced Monitoring**: Easy CLI-based monitoring for `graft-hook`, including real-time build error logs.
278-
5. **Cloudflare API Integration**: Automated DNS zone mapping based on Traefik host labels.
279-
6. **Cloudflare Zone Registry**: Manage multiple Cloudflare accounts and zones with an interactive selection menu.
280-
7. **Database Backup Automation(Infrastructure)**: Easily backup Infrastructure databases to S3/R2(Experimental).
264+
2. **Multi Environment Support**: Comprehensive management for multiple deployment environments including specific configurations, workflows, and DNS.
265+
3. **Webhook Mapping**: Map webhook domains to servers using Graft.
266+
4. **Git Remote Modes**: Deployments now go through GitHub Workflows and CI/CD channels.
267+
5. **Graft-Hook**: A new webhook service for Graft-based servers.
268+
6. **Automated GitHub Workflows**: Generate workflows automatically from `graft-compose.yml`.
269+
7. **Cloudflare Integration**: Automated DNS zone mapping and zone management.
270+
8. **Database Backup Automation**: Infrastructure database backups to S3/R2 (Experimental).
281271
282272
footer: |
283273
---

cmd/graft/executors/env.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ func (e *Executor) RunNewEnv(name string) {
3535

3636
// 2. Server Selection (Copy from RunInit)
3737
gCfg := e.GlobalConfig
38-
srv, err := project.SelectOrAddServer(reader,gCfg, prompt.PromptNewServer)
38+
srv, err := project.SelectOrAddServer(reader,gCfg)
3939
if err != nil {
4040
fmt.Printf("Error: %v\n", err)
4141
return

cmd/graft/executors/host.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,3 +283,26 @@ func (e *Executor) RunHostShell(commandArgs []string) {
283283
}
284284
}
285285
}
286+
287+
func (e *Executor) RunHostDocker(commandArgs []string) {
288+
289+
290+
client, err := e.getClient()
291+
if err != nil {
292+
fmt.Printf("Error: %v\n", err)
293+
return
294+
}
295+
defer client.Close()
296+
297+
if len(commandArgs) == 0 {
298+
// Interactive SSH
299+
fmt.Println("Usage: graft host [init|clean|sh|self-destruct|any docker command]")
300+
} else {
301+
// Non-interactive command
302+
cmdStr := "sudo docker " + strings.Join(commandArgs, " ")
303+
fmt.Printf("🚀 Executing on '%s': %s\n", e.Server.RegistryName, cmdStr)
304+
if err := client.RunCommand(cmdStr, os.Stdout, os.Stderr); err != nil {
305+
fmt.Printf("Error: %v\n", err)
306+
}
307+
}
308+
}

cmd/graft/executors/registry.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,38 @@ func (e *Executor) RunRegistryShell(registryName string, commandArgs []string) {
121121
}
122122
}
123123

124+
func (e *Executor) RunRegistryDocker(registry string, args []string){
125+
gCfg := e.GlobalConfig
126+
if gCfg == nil {
127+
fmt.Println("Error: Could not load global registry.")
128+
return
129+
}
130+
srv, exists := gCfg.Servers[registry]
131+
if !exists {
132+
fmt.Printf("Error: Registry '%s' not found.\n", registry)
133+
return
134+
}
135+
136+
client, err := ssh.NewClient(srv.Host, srv.Port, srv.User, srv.KeyPath)
137+
if err != nil {
138+
fmt.Printf("Error: %v\n", err)
139+
return
140+
}
141+
defer client.Close()
142+
143+
if len(args) == 0 {
144+
fmt.Printf("Usage: graft -r <registry name> <any docker command>\n")
145+
fmt.Printf("Example: graft -r prod-us ps\n")
146+
return
147+
} else {
148+
cmdStr := "sudo docker " + strings.Join(args, " ")
149+
fmt.Printf("🚀 Executing on '%s': %s\n", registry, cmdStr)
150+
if err := client.RunCommand(cmdStr, os.Stdout, os.Stderr); err != nil {
151+
fmt.Printf("Error: %v\n", err)
152+
}
153+
}
154+
}
155+
124156
func (e *Executor) RunRegistryLs() {
125157
gCfg := e.GlobalConfig
126158
if gCfg == nil || len(gCfg.Servers) == 0 {

cmd/graft/main.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ func main() {
4545
e.RunRegistryShell(registryContext, args[1:])
4646
return
4747
}
48+
// Handle shell directly after -r: graft -r name -sh ...
49+
if len(args) > 0 {
50+
51+
e.RunRegistryDocker(registryContext, args)
52+
return
53+
}
4854
}
4955

5056
// Handle project context flag: graft -p projectname ...
@@ -159,7 +165,7 @@ e.RunHook(args[1:])
159165
case "self-destruct":
160166
e.RunHostSelfDestruct()
161167
default:
162-
fmt.Println("Usage: graft host [init|clean|sh|self-destruct]")
168+
e.RunHostDocker(args[1:])
163169
}
164170
case "db":
165171
if len(args) < 3 || args[2] != "init" {

internal/project/init.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ func CheckLocalDirectory(reader *bufio.Reader) error {
3939
}
4040

4141
// SelectOrAddServer prompts the user to select an existing server or add a new one
42-
func SelectOrAddServer(reader *bufio.Reader, gCfg *config.GlobalConfig, promptNewServerFunc func(*bufio.Reader) (string, int, string, string)) (*config.ServerConfig, error) {
42+
func SelectOrAddServer(reader *bufio.Reader, gCfg *config.GlobalConfig ) (*config.ServerConfig, error) {
4343
var host string
4444
var port int
4545
var user string
@@ -62,7 +62,7 @@ func SelectOrAddServer(reader *bufio.Reader, gCfg *config.GlobalConfig, promptNe
6262
input = strings.TrimSpace(input)
6363

6464
if input == "/new" {
65-
host, port, user, keyPath = promptNewServerFunc(reader)
65+
host, port, user, keyPath = prompt.PromptNewServer(reader)
6666
fmt.Print("Registry Name (e.g. prod-us): ")
6767
registryName, _ = reader.ReadString('\n')
6868
registryName = strings.TrimSpace(registryName)
@@ -78,15 +78,15 @@ func SelectOrAddServer(reader *bufio.Reader, gCfg *config.GlobalConfig, promptNe
7878
fmt.Printf("✅ Using server: %s\n", registryName)
7979
} else {
8080
fmt.Println("Invalid selection, entering new server details...")
81-
host, port, user, keyPath = promptNewServerFunc(reader)
81+
host, port, user, keyPath = prompt.PromptNewServer(reader)
8282
fmt.Print("Registry Name (e.g. prod-us): ")
8383
registryName, _ = reader.ReadString('\n')
8484
registryName = strings.TrimSpace(registryName)
8585
}
8686
}
8787
} else {
8888
fmt.Println("No servers found in registry. Enter new server details:")
89-
host, port, user, keyPath = promptNewServerFunc(reader)
89+
host, port, user, keyPath = prompt.PromptNewServer(reader)
9090
fmt.Print("Registry Name (e.g. prod-us): ")
9191
registryName, _ = reader.ReadString('\n')
9292
registryName = strings.TrimSpace(registryName)
@@ -273,7 +273,7 @@ func InitProjectWorkflow(reader *bufio.Reader, force bool, gCfg *config.GlobalCo
273273
}
274274

275275
// Server Selection
276-
srv, err = SelectOrAddServer(reader, gCfg, prompt.PromptNewServer)
276+
srv, err = SelectOrAddServer(reader, gCfg)
277277
if err != nil {
278278
return "", nil, fmt.Errorf("server selection failed: %w", err)
279279
}

internal/prompt/prompt.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bufio"
55
"fmt"
66
"os/exec"
7+
"path/filepath"
78
"strconv"
89
"strings"
910
)
@@ -128,6 +129,12 @@ func PromptNewServer(reader *bufio.Reader) (string, int, string, string) {
128129
fmt.Print("Key Path: ")
129130
keyPath, _ := reader.ReadString('\n')
130131
keyPath = strings.TrimSpace(keyPath)
132+
//if key path starts with ./ then dow pwd and marge to find absolute keypath
133+
keyPath, err := filepath.Abs(keyPath)
134+
if err != nil {
135+
fmt.Println("Error getting absolute path:", err)
136+
return host, port, user, keyPath
137+
}
131138

132139
return host, port, user, keyPath
133140
}

0 commit comments

Comments
 (0)