diff --git a/internal/app/db.go b/internal/app/db.go index c1951e7..379c3b1 100644 --- a/internal/app/db.go +++ b/internal/app/db.go @@ -61,9 +61,24 @@ func (a *App) migrate(ctx context.Context) error { return err } } - if _, err := a.db.ExecContext(ctx, `ALTER TABLE projects DROP COLUMN IF EXISTS target_port;`); err != nil { + if _, err := a.db.ExecContext(ctx, `CREATE TABLE IF NOT EXISTS schema_migrations ( + key TEXT PRIMARY KEY, + applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW() + );`); err != nil { return err } + var dropDone bool + if err := a.db.QueryRowContext(ctx, `SELECT EXISTS(SELECT 1 FROM schema_migrations WHERE key='drop_projects_target_port')`).Scan(&dropDone); err != nil { + return err + } + if !dropDone { + if _, err := a.db.ExecContext(ctx, `ALTER TABLE projects DROP COLUMN IF EXISTS target_port;`); err != nil { + return err + } + if _, err := a.db.ExecContext(ctx, `INSERT INTO schema_migrations(key) VALUES ('drop_projects_target_port') ON CONFLICT (key) DO NOTHING`); err != nil { + return err + } + } rows, err := a.db.QueryContext(ctx, `SELECT id FROM projects WHERE deploy_token=''`) if err != nil { return err diff --git a/internal/app/deploy_service.go b/internal/app/deploy_service.go index e4db077..6431ccf 100644 --- a/internal/app/deploy_service.go +++ b/internal/app/deploy_service.go @@ -81,7 +81,8 @@ func (a *App) deployProjectFromMain(_ context.Context, p Project) error { if err := a.syncProjectRepoFromMain(p, projectDir); err != nil { return err } - if err := a.buildProjectBinary(p); err != nil { + repoDir := filepath.Join(projectDir, "repo") + if err := a.buildProjectBinaryFromRepoDir(p, repoDir); err != nil { return err } return runSystemctlUser(p.UnixUser, "restart", p.ServiceName) @@ -99,6 +100,14 @@ func (a *App) buildProjectBinary(p Project) error { return err } repoDir := filepath.Join(projectDir, "repo") + return a.buildProjectBinaryFromRepoDir(p, repoDir) +} + +func (a *App) buildProjectBinaryFromRepoDir(p Project, repoDir string) error { + projectDir, err := a.projectDirFor(p) + if err != nil { + return err + } binDir := filepath.Join(projectDir, "bin") if err := os.MkdirAll(binDir, 0o755); err != nil { return err @@ -121,20 +130,30 @@ func (a *App) buildProjectBinary(p Project) error { func (a *App) syncProjectRepoFromMain(p Project, projectDir string) error { repoDir := filepath.Join(projectDir, "repo") + plainRepoURL := strings.TrimSpace(p.RepoURL) repoCloneURL := a.repoURLForGitAuth(p.RepoURL) if _, err := os.Stat(repoDir); os.IsNotExist(err) { - if err := runCmd("git", "clone", "--branch", "main", "--single-branch", repoCloneURL, repoDir); err != nil { + if err := os.MkdirAll(repoDir, 0o755); err != nil { + return err + } + if err := runCmd("git", "-C", repoDir, "init"); err != nil { + return err + } + if err := runCmd("git", "-C", repoDir, "remote", "add", "origin", plainRepoURL); err != nil { + return err + } + if err := runCmd("git", "-C", repoDir, "fetch", repoCloneURL, "main", "--prune"); err != nil { + return err + } + if err := runCmd("git", "-C", repoDir, "reset", "--hard", "FETCH_HEAD"); err != nil { return err } return nil } - if err := runCmd("git", "-C", repoDir, "remote", "set-url", "origin", repoCloneURL); err != nil { + if err := runCmd("git", "-C", repoDir, "fetch", repoCloneURL, "main", "--prune"); 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 { + if err := runCmd("git", "-C", repoDir, "reset", "--hard", "FETCH_HEAD"); err != nil { return err } return nil diff --git a/internal/app/integrations.go b/internal/app/integrations.go index 57aba5d..e21a0fa 100644 --- a/internal/app/integrations.go +++ b/internal/app/integrations.go @@ -44,7 +44,10 @@ func (a *App) createForgejoRepo(ctx context.Context, name, description string, p HTMLURL string `json:"html_url"` } if err := json.Unmarshal(respBody, &parsed); err != nil { - return "", nil + return "", fmt.Errorf("forgejo create repo parse failed: %w", err) + } + if strings.TrimSpace(parsed.HTMLURL) == "" { + return "", errors.New("forgejo create repo response missing html_url") } return parsed.HTMLURL, nil } diff --git a/internal/app/routes_handlers.go b/internal/app/routes_handlers.go index d3877f4..9f816c2 100644 --- a/internal/app/routes_handlers.go +++ b/internal/app/routes_handlers.go @@ -271,6 +271,13 @@ func (a *App) handleCaddyConfig(w http.ResponseWriter, r *http.Request, _ User) if a.cfg.CaddyRouteEnabled && strings.TrimSpace(a.cfg.CaddyFragmentPath) != "" { content, err := os.ReadFile(a.cfg.CaddyFragmentPath) if err != nil { + if os.IsNotExist(err) { + writeJSON(w, http.StatusOK, map[string]any{ + "format": "caddyfile", + "content": "# Managed by project-manager. No routes provisioned yet.\n", + }) + return + } http.Error(w, err.Error(), http.StatusBadGateway) return } diff --git a/internal/app/systemd_service.go b/internal/app/systemd_service.go index 4c3fd9b..7773f8a 100644 --- a/internal/app/systemd_service.go +++ b/internal/app/systemd_service.go @@ -107,8 +107,12 @@ WantedBy=default.target if err := ensureOwnedByUnixUser(servicePath, p.UnixUser, 0o644); err != nil { return err } - _ = runSystemctlUser(p.UnixUser, "daemon-reload") - _ = runSystemctlUser(p.UnixUser, "enable", "--now", p.ServiceName) + if err := runSystemctlUser(p.UnixUser, "daemon-reload"); err != nil { + return err + } + if err := runSystemctlUser(p.UnixUser, "enable", "--now", p.ServiceName); err != nil { + return err + } return nil }