init
This commit is contained in:
commit
b38c39030b
22 changed files with 1925 additions and 0 deletions
93
internal/app/deploy_service.go
Normal file
93
internal/app/deploy_service.go
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (a *App) handleDeployWebhook(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
path := strings.TrimPrefix(r.URL.Path, "/api/webhooks/deploy/")
|
||||
parts := strings.Split(path, "/")
|
||||
if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
|
||||
http.Error(w, "bad path", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
projectID, err := strconv.ParseInt(parts[0], 10, 64)
|
||||
if err != nil {
|
||||
http.Error(w, "invalid project id", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
p, err := a.getProjectForWebhook(r.Context(), projectID, parts[1])
|
||||
if err != nil {
|
||||
http.Error(w, "unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
var payload struct {
|
||||
Ref string `json:"ref"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
|
||||
http.Error(w, "invalid json", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if payload.Ref != "refs/heads/main" {
|
||||
writeJSON(w, http.StatusAccepted, map[string]string{"status": "ignored", "reason": "not main branch"})
|
||||
return
|
||||
}
|
||||
if err := a.deployProjectFromMain(r.Context(), p); err != nil {
|
||||
log.Printf("deploy failed project_id=%d err=%v", p.ID, err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, map[string]string{"status": "deployed"})
|
||||
}
|
||||
|
||||
func (a *App) getProjectForWebhook(ctx context.Context, projectID int64, token string) (Project, error) {
|
||||
var p Project
|
||||
err := a.db.QueryRowContext(ctx, `SELECT p.id, p.user_id, p.name, p.slug, p.description, p.repo_url, p.service_name, p.route_host, p.target_port, p.workspace_id, w.name, w.unix_user, p.deploy_token, p.created_at
|
||||
FROM projects p JOIN workspaces w ON w.id=p.workspace_id WHERE p.id=$1 AND p.deploy_token=$2`, projectID, token).
|
||||
Scan(&p.ID, &p.UserID, &p.Name, &p.Slug, &p.Description, &p.RepoURL, &p.ServiceName, &p.RouteHost, &p.TargetPort, &p.WorkspaceID, &p.Workspace, &p.UnixUser, &p.DeployToken, &p.CreatedAt)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return Project{}, errors.New("project not found")
|
||||
}
|
||||
return Project{}, err
|
||||
}
|
||||
p.WebhookURL = a.deployWebhookURL(p.ID, p.DeployToken)
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (a *App) deployProjectFromMain(_ context.Context, p Project) error {
|
||||
if p.RepoURL == "" {
|
||||
return errors.New("project has no repo_url to deploy from")
|
||||
}
|
||||
projectDir := filepath.Join(a.cfg.ProjectRoot, slugify(p.UserID), p.Workspace, p.Slug)
|
||||
repoDir := filepath.Join(projectDir, "repo")
|
||||
if _, err := os.Stat(repoDir); os.IsNotExist(err) {
|
||||
if err := runCmd("git", "clone", "--branch", "main", "--single-branch", p.RepoURL, repoDir); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := runCmd("git", "-C", repoDir, "remote", "set-url", "origin", p.RepoURL); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := runCmd("git", "-C", repoDir, "fetch", "origin", "main", "--prune"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := runCmd("git", "-C", repoDir, "reset", "--hard", "origin/main"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return runCmd("sudo", "-n", "-u", p.UnixUser, "systemctl", "--user", "restart", p.ServiceName)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue