Skip to content

Commit de3d49b

Browse files
committed
added proper webhook handling, github workflow generation, better ux and bug fixes
1 parent 1218e2c commit de3d49b

File tree

6 files changed

+70
-24
lines changed

6 files changed

+70
-24
lines changed

.goreleaser.yaml

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,6 @@ archives:
2626
- goos: windows
2727
formats: [zip]
2828

29-
changelog:
30-
sort: asc
31-
filters:
32-
exclude:
33-
- "^docs:"
34-
- "^test:"
3529

3630
checksum:
3731
name_template: "checksums.txt"
@@ -40,7 +34,17 @@ snapshot:
4034
version_template: "{{ .Tag }}-next"
4135

4236
release:
43-
37+
header: |
38+
## Major Feature Update with Graft 🚀
39+
40+
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.
41+
42+
### Key Highlights:
43+
1. **Git Remote Modes**: Move away from direct local-to-server channels. Deployments now go through GitHub Workflows and CI/CD channels.
44+
2. **Graft-Hook**: A new webhook service personalized specifically for Graft-based servers, maintaining a zero-config solution with proper security.
45+
3. **Automated GitHub Workflows**: Graft now generates production-ready workflows automatically from your `graft-compose.yml` context.
46+
4. **Enhanced Monitoring**: Easy CLI-based monitoring for `graft-hook`, including real-time build error logs.
47+
5. **Bug Fixes & UX**: Numerous performance improvements and user experience refinements.
4448
footer: |
4549
---
4650
Released by **skssmd** using [GoReleaser](https://github.com/goreleaser/goreleaser).

COMMANDS.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,21 @@ graft logs frontend
440440

441441
---
442442

443+
### `graft hook logs`
444+
Monitor the `graft-hook` service logs, including build errors and deployment events.
445+
446+
```bash
447+
graft hook logs graft-hook
448+
```
449+
450+
**Features:**
451+
- ✅ Real-time monitoring of webhook events
452+
- ✅ View detailed build and deployment errors
453+
- ✅ Perfect for debugging CI/CD pipeline failures
454+
- ✅ Press Ctrl+C to stop
455+
456+
---
457+
443458
## Docker Compose Passthrough
444459

445460
**Any command not listed above is automatically passed to `docker compose` on the remote server!**

cmd/graft/main.go

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,7 @@ func runInit(args []string) {
587587
}
588588

589589
// Graft-Hook detection and deployment for automated modes
590+
var currentHookURL string
590591
if deploymentMode == "git-images" || deploymentMode == "git-repo-serverbuild" || deploymentMode == "git-manual" {
591592
if client != nil {
592593
installHook := false
@@ -601,6 +602,10 @@ func runInit(args []string) {
601602
installHook = true
602603
} else {
603604
fmt.Println("\n✅ graft-hook is already installed on the server.")
605+
// Fetch existing hook URL from global registry if available
606+
if srv, exists := gCfg.Servers[registryName]; exists {
607+
currentHookURL = srv.GraftHookURL
608+
}
604609
}
605610
}
606611

@@ -643,6 +648,14 @@ networks:
643648
os.Remove(tmpFile)
644649
client.RunCommand("sudo docker compose -f /opt/graft/webhook/docker-compose.yml up -d", os.Stdout, os.Stderr)
645650
fmt.Println("✅ graft-hook deployed.")
651+
currentHookURL = fmt.Sprintf("https://%s", hookDomain)
652+
653+
// Save to global registry
654+
if srv, exists := gCfg.Servers[registryName]; exists {
655+
srv.GraftHookURL = currentHookURL
656+
gCfg.Servers[registryName] = srv
657+
config.SaveGlobalConfig(gCfg)
658+
}
646659
}
647660
}
648661
}
@@ -690,6 +703,7 @@ networks:
690703
Name: projName,
691704
RemotePath: fmt.Sprintf("/opt/graft/projects/%s", projName),
692705
DeploymentMode: deploymentMode,
706+
GraftHookURL: currentHookURL,
693707
}
694708
if err := config.SaveProjectMetadata(meta); err != nil {
695709
fmt.Printf("Warning: Could not save project metadata: %v\n", err)
@@ -947,7 +961,7 @@ func runSync(args []string) {
947961
}
948962

949963
// Generate Workflows
950-
if err := deploy.GenerateWorkflows(p, remoteURL, meta.DeploymentMode ); err != nil {
964+
if err := deploy.GenerateWorkflows(p, remoteURL, meta.DeploymentMode, meta.GraftHookURL); err != nil {
951965
fmt.Printf("Error generating workflows: %v\n", err)
952966
return
953967
}
@@ -957,11 +971,7 @@ func runSync(args []string) {
957971
// Ask for compose generation and transfer
958972

959973

960-
// Save project to ensure graft-compose.yml is local
961-
if err := p.Save("."); err != nil {
962-
fmt.Printf("Error saving project: %v\n", err)
963-
return
964-
}
974+
965975

966976
client, err := ssh.NewClient(cfg.Server.Host, cfg.Server.Port, cfg.Server.User, cfg.Server.KeyPath)
967977
if err != nil {

internal/config/config.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ type ServerConfig struct {
1818
Port int `json:"port"`
1919
User string `json:"user"`
2020
KeyPath string `json:"key_path"`
21+
GraftHookURL string `json:"graft_hook_url,omitempty"`
2122
}
2223

2324
type InfraConfig struct {
@@ -181,6 +182,7 @@ type ProjectMetadata struct {
181182
RemotePath string `json:"remote_path"`
182183
Initialized bool `json:"initialized"`
183184
DeploymentMode string `json:"deployment_mode,omitempty"` // "git-images", "git-repo-serverbuild", "git-manual", "direct-serverbuild", "direct-localbuild"
185+
GraftHookURL string `json:"graft_hook_url,omitempty"`
184186
}
185187

186188
// SaveProjectMetadata saves project metadata to .graft/project.json and registers it globally

internal/deploy/compose_sync.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -139,13 +139,13 @@ func SyncComposeOnly(client *ssh.Client, p *Project, heave bool, stdout, stderr
139139
if err := client.UploadFile("docker-compose.yml", remoteCompose); err != nil {
140140
return fmt.Errorf("failed to upload docker-compose.yml: %v", err)
141141
}
142-
fmt.Fprintln(stdout, "✅ docker-compose.yml uploaded successfully!")
142+
143143
}
144144

145-
if heave {
145+
146146
fmt.Fprintf(stdout, "✅ %s file uploaded!\n", printstr)
147-
return nil
148-
}
147+
148+
149149

150150
if !heave {
151151
// Restart services without rebuilding

internal/deploy/project.go

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ func EnsureGitignore(dir string) error {
340340
}
341341

342342
// GenerateWorkflows creates .github/workflows directory and populates it with CI and Deploy templates
343-
func GenerateWorkflows(p *Project, remoteURL string, mode string) error {
343+
func GenerateWorkflows(p *Project, remoteURL string, mode string, webhook string) error {
344344
fmt.Println("received workflow mode: ", mode)
345345
workflowsDir := filepath.Join(".github", "workflows")
346346
if err := os.MkdirAll(workflowsDir, 0755); err != nil {
@@ -389,10 +389,23 @@ func GenerateWorkflows(p *Project, remoteURL string, mode string) error {
389389
}
390390
}
391391

392+
// Prepare webhook URL
393+
hookURL := webhook
394+
if hookURL == "" {
395+
hookURL = "https://graft-hook.example.com"
396+
}
397+
if !strings.HasSuffix(hookURL, "/webhook") && !strings.Contains(hookURL, "/webhook?") {
398+
if strings.HasSuffix(hookURL, "/") {
399+
hookURL += "webhook"
400+
} else {
401+
hookURL += "/webhook"
402+
}
403+
}
404+
392405
deployTemplate := `name: Deploy
393406
394407
on:
395-
%s
408+
%%s
396409
release:
397410
types: [published]
398411
workflow_dispatch:
@@ -401,16 +414,16 @@ jobs:
401414
deploy:
402415
name: Deploy via Webhook
403416
runs-on: ubuntu-latest
404-
%s
417+
%%s
405418
environment: CI CD
406419
407420
steps:
408421
- name: Send Webhook Request
409422
run: |
410-
curl -X POST https://graft-hook.example.com/webhook \
423+
curl -X POST %s \
411424
-H "Content-Type: application/json" \
412425
-d '{
413-
"project": "%%s",
426+
"project": "%%%%s",
414427
"repository": "${{ github.event.repository.name }}",
415428
"token": "${{ secrets.GITHUB_TOKEN }}",
416429
"user": "${{ github.actor }}",
@@ -419,8 +432,10 @@ jobs:
419432
}'
420433
`
421434
deployPath := filepath.Join(workflowsDir, "deploy.yml")
422-
deployContent := fmt.Sprintf(deployTemplate, triggers, condition, deployType)
423-
// Now format the project name into the content
435+
deployContent := fmt.Sprintf(deployTemplate, hookURL, deployType)
436+
// Now format the triggers and condition into the content (%%s -> %s)
437+
deployContent = fmt.Sprintf(deployContent, triggers, condition)
438+
// Now format the project name into the content (%%%%s -> %s)
424439
deployContent = fmt.Sprintf(deployContent, p.Name)
425440

426441
if err := os.WriteFile(deployPath, []byte(deployContent), 0644); err != nil {

0 commit comments

Comments
 (0)