refactor: replace os.ReadFile/WriteFile and fmt.Errorf/errors.New with framework equivalents

Replace os.ReadFile with coreio.Local.Read for consistent filesystem abstraction.
Replace fmt.Errorf/errors.New with log.E() from go-log for structured error context.

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Snider 2026-03-16 19:09:50 +00:00
parent cea8624497
commit 4c5e12c9f8
15 changed files with 79 additions and 68 deletions

View file

@ -9,6 +9,7 @@ import (
"forge.lthn.ai/core/cli/pkg/cli"
"forge.lthn.ai/core/go-devops/deploy/coolify"
"forge.lthn.ai/core/go-i18n"
log "forge.lthn.ai/core/go-log"
)
var (
@ -266,7 +267,7 @@ func runCall(cmd *cli.Command, args []string) error {
var params map[string]any
if len(args) > 1 {
if err := json.Unmarshal([]byte(args[1]), &params); err != nil {
return fmt.Errorf("invalid JSON params: %w", err)
return log.E("deploy", "invalid JSON params", err)
}
}

View file

@ -1,12 +1,12 @@
package dev
import (
"errors"
"slices"
"forge.lthn.ai/core/cli/pkg/cli"
"forge.lthn.ai/core/go-i18n"
"forge.lthn.ai/core/go-io"
log "forge.lthn.ai/core/go-log"
"forge.lthn.ai/core/go-scm/repos"
)
@ -55,14 +55,14 @@ func runImpact(registryPath string, repoName string) error {
return cli.Wrap(err, "failed to load registry")
}
} else {
return errors.New(i18n.T("cmd.dev.impact.requires_registry"))
return log.E("dev.impact", i18n.T("cmd.dev.impact.requires_registry"), nil)
}
}
// Check repo exists
repo, exists := reg.Get(repoName)
if !exists {
return errors.New(i18n.T("error.repo_not_found", map[string]any{"Name": repoName}))
return log.E("dev.impact", i18n.T("error.repo_not_found", map[string]any{"Name": repoName}), nil)
}
// Build reverse dependency graph

View file

@ -11,6 +11,7 @@ import (
"forge.lthn.ai/core/cli/pkg/cli"
"forge.lthn.ai/core/go-i18n"
log "forge.lthn.ai/core/go-log"
)
// Tag command flags
@ -77,7 +78,7 @@ func runTag(registryPath string, dryRun, force bool) error {
next, err := bumpPatch(current)
if err != nil {
return fmt.Errorf("%s: failed to bump version %s: %w", repo.Name, current, err)
return log.E("dev.tag", fmt.Sprintf("%s: failed to bump version %s", repo.Name, current), err)
}
hasGoMod := fileExists(filepath.Join(repo.Path, "go.mod"))
@ -207,11 +208,11 @@ func bumpPatch(tag string) (string, error) {
v := strings.TrimPrefix(tag, "v")
parts := strings.Split(v, ".")
if len(parts) != 3 {
return "", fmt.Errorf("invalid semver: %s", tag)
return "", log.E("dev.tag", fmt.Sprintf("invalid semver: %s", tag), nil)
}
patch, err := strconv.Atoi(parts[2])
if err != nil {
return "", fmt.Errorf("invalid patch version: %s", parts[2])
return "", log.E("dev.tag", fmt.Sprintf("invalid patch version: %s", parts[2]), nil)
}
return fmt.Sprintf("v%s.%s.%d", parts[0], parts[1], patch+1), nil
}
@ -223,7 +224,7 @@ func goGetUpdate(ctx context.Context, repoPath string) error {
cmd.Env = append(os.Environ(), "GOWORK=off")
out, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("%s: %w", strings.TrimSpace(string(out)), err)
return log.E("dev.tag", strings.TrimSpace(string(out)), err)
}
return nil
}
@ -235,7 +236,7 @@ func goModTidy(ctx context.Context, repoPath string) error {
cmd.Env = append(os.Environ(), "GOWORK=off")
out, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("%s: %w", strings.TrimSpace(string(out)), err)
return log.E("dev.tag", strings.TrimSpace(string(out)), err)
}
return nil
}
@ -261,7 +262,7 @@ func commitGoMod(ctx context.Context, repoPath, version string) error {
addCmd := exec.CommandContext(ctx, "git", "add", "go.mod", "go.sum")
addCmd.Dir = repoPath
if out, err := addCmd.CombinedOutput(); err != nil {
return fmt.Errorf("git add: %s: %w", strings.TrimSpace(string(out)), err)
return log.E("dev.tag", "git add: "+strings.TrimSpace(string(out)), err)
}
// Check if anything is actually staged
@ -276,7 +277,7 @@ func commitGoMod(ctx context.Context, repoPath, version string) error {
commitCmd := exec.CommandContext(ctx, "git", "commit", "-m", msg)
commitCmd.Dir = repoPath
if out, err := commitCmd.CombinedOutput(); err != nil {
return fmt.Errorf("git commit: %s: %w", strings.TrimSpace(string(out)), err)
return log.E("dev.tag", "git commit: "+strings.TrimSpace(string(out)), err)
}
return nil
}
@ -286,7 +287,7 @@ func createTag(ctx context.Context, repoPath, tag string) error {
cmd := exec.CommandContext(ctx, "git", "tag", "-a", tag, "-m", tag)
cmd.Dir = repoPath
if out, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("%s: %w", strings.TrimSpace(string(out)), err)
return log.E("dev.tag", strings.TrimSpace(string(out)), err)
}
return nil
}

View file

@ -2,7 +2,6 @@ package dev
import (
"context"
"errors"
"os"
"time"
@ -10,6 +9,7 @@ import (
"forge.lthn.ai/core/go-container/devenv"
"forge.lthn.ai/core/go-i18n"
"forge.lthn.ai/core/go-io"
log "forge.lthn.ai/core/go-log"
)
// addVMCommands adds the dev environment VM commands to the dev parent command.
@ -119,7 +119,7 @@ func runVMBoot(memory, cpus int, fresh bool) error {
}
if !d.IsInstalled() {
return errors.New(i18n.T("cmd.dev.vm.not_installed"))
return log.E("dev.vm", i18n.T("cmd.dev.vm.not_installed"), nil)
}
opts := devenv.DefaultBootOptions()

View file

@ -1,13 +1,13 @@
package dev
import (
"fmt"
"os"
"path/filepath"
"strings"
"code.gitea.io/sdk/gitea"
coreio "forge.lthn.ai/core/go-io"
log "forge.lthn.ai/core/go-log"
"forge.lthn.ai/core/go-scm/forge"
)
@ -19,7 +19,7 @@ func forgeAPIClient() (*gitea.Client, error) {
return nil, err
}
if token == "" {
return nil, fmt.Errorf("no Forge API token configured (set FORGE_TOKEN or run: core forge config --token TOKEN)")
return nil, log.E("dev.forge", "no Forge API token configured (set FORGE_TOKEN or run: core forge config --token TOKEN)", nil)
}
return gitea.NewClient(forgeURL, gitea.SetToken(token))
}
@ -28,12 +28,12 @@ func forgeAPIClient() (*gitea.Client, error) {
// Falls back to fallbackOrg/repoName if no forge.lthn.ai remote is found.
func forgeRepoIdentity(repoPath, fallbackOrg, repoName string) (owner, repo string) {
configPath := filepath.Join(repoPath, ".git", "config")
content, err := os.ReadFile(configPath)
content, err := coreio.Local.Read(configPath)
if err != nil {
return fallbackOrg, repoName
}
for _, line := range strings.Split(string(content), "\n") {
for _, line := range strings.Split(content, "\n") {
line = strings.TrimSpace(line)
if !strings.HasPrefix(line, "url = ") {
continue

View file

@ -16,6 +16,7 @@ import (
"forge.lthn.ai/core/agent/cmd/workspace"
"forge.lthn.ai/core/go-i18n"
coreio "forge.lthn.ai/core/go-io"
log "forge.lthn.ai/core/go-log"
"forge.lthn.ai/core/go-scm/repos"
)
@ -46,7 +47,7 @@ func runSetupOrchestrator(registryPath, only string, dryRun, all bool, projectNa
func runBootstrap(ctx context.Context, only string, dryRun, all bool, projectName string, runBuild bool) error {
cwd, err := os.Getwd()
if err != nil {
return fmt.Errorf("failed to get working directory: %w", err)
return log.E("setup.bootstrap", "failed to get working directory", err)
}
fmt.Printf("%s %s\n", dimStyle.Render(">>"), i18n.T("cmd.setup.bootstrap_mode"))
@ -56,7 +57,7 @@ func runBootstrap(ctx context.Context, only string, dryRun, all bool, projectNam
// Check if current directory is empty
empty, err := isDirEmpty(cwd)
if err != nil {
return fmt.Errorf("failed to check directory: %w", err)
return log.E("setup.bootstrap", "failed to check directory", err)
}
if empty {
@ -71,7 +72,7 @@ func runBootstrap(ctx context.Context, only string, dryRun, all bool, projectNam
// Offer choice: setup working directory or create package
choice, err := promptSetupChoice()
if err != nil {
return fmt.Errorf("failed to get choice: %w", err)
return log.E("setup.bootstrap", "failed to get choice", err)
}
if choice == "setup" {
@ -88,7 +89,7 @@ func runBootstrap(ctx context.Context, only string, dryRun, all bool, projectNam
} else {
projectName, err = promptProjectName(defaultOrg)
if err != nil {
return fmt.Errorf("failed to get project name: %w", err)
return log.E("setup.bootstrap", "failed to get project name", err)
}
}
}
@ -98,7 +99,7 @@ func runBootstrap(ctx context.Context, only string, dryRun, all bool, projectNam
if !dryRun {
if err := coreio.Local.EnsureDir(targetDir); err != nil {
return fmt.Errorf("failed to create directory: %w", err)
return log.E("setup.bootstrap", "failed to create directory", err)
}
}
}
@ -110,7 +111,7 @@ func runBootstrap(ctx context.Context, only string, dryRun, all bool, projectNam
if !dryRun {
if err := gitClone(ctx, defaultOrg, devopsRepo, devopsPath); err != nil {
return fmt.Errorf("failed to clone %s: %w", devopsRepo, err)
return log.E("setup.bootstrap", fmt.Sprintf("failed to clone %s", devopsRepo), err)
}
fmt.Printf("%s %s %s\n", successStyle.Render(">>"), devopsRepo, i18n.T("cmd.setup.cloned"))
} else {
@ -130,7 +131,7 @@ func runBootstrap(ctx context.Context, only string, dryRun, all bool, projectNam
reg, err := repos.LoadRegistry(coreio.Local, registryPath)
if err != nil {
return fmt.Errorf("failed to load registry from %s: %w", devopsRepo, err)
return log.E("setup.bootstrap", fmt.Sprintf("failed to load registry from %s", devopsRepo), err)
}
// Override base path to target directory

View file

@ -18,13 +18,13 @@
package setup
import (
"errors"
"os/exec"
"path/filepath"
"forge.lthn.ai/core/cli/pkg/cli"
"forge.lthn.ai/core/go-i18n"
coreio "forge.lthn.ai/core/go-io"
log "forge.lthn.ai/core/go-log"
"forge.lthn.ai/core/go-scm/repos"
)
@ -69,12 +69,12 @@ func addGitHubCommand(parent *cli.Command) {
func runGitHubSetup() error {
// Check gh is available
if _, err := exec.LookPath("gh"); err != nil {
return errors.New(i18n.T("error.gh_not_found"))
return log.E("setup.github", i18n.T("error.gh_not_found"), nil)
}
// Check gh is authenticated
if !cli.GhAuthenticated() {
return errors.New(i18n.T("cmd.setup.github.error.not_authenticated"))
return log.E("setup.github", i18n.T("cmd.setup.github.error.not_authenticated"), nil)
}
// Find registry
@ -118,14 +118,14 @@ func runGitHubSetup() error {
// Reject conflicting flags
if ghRepo != "" && ghAll {
return errors.New(i18n.T("cmd.setup.github.error.conflicting_flags"))
return log.E("setup.github", i18n.T("cmd.setup.github.error.conflicting_flags"), nil)
}
if ghRepo != "" {
// Single repo mode
repo, ok := reg.Get(ghRepo)
if !ok {
return errors.New(i18n.T("error.repo_not_found", map[string]any{"Name": ghRepo}))
return log.E("setup.github", i18n.T("error.repo_not_found", map[string]any{"Name": ghRepo}), nil)
}
reposToProcess = []*repos.Repo{repo}
} else if ghAll {

View file

@ -17,6 +17,7 @@ import (
"forge.lthn.ai/core/cli/pkg/cli"
"forge.lthn.ai/core/go-i18n"
coreio "forge.lthn.ai/core/go-io"
log "forge.lthn.ai/core/go-log"
"forge.lthn.ai/core/go-scm/repos"
)
@ -24,7 +25,7 @@ import (
func runRegistrySetup(ctx context.Context, registryPath, only string, dryRun, all, runBuild bool) error {
reg, err := repos.LoadRegistry(coreio.Local, registryPath)
if err != nil {
return fmt.Errorf("failed to load registry: %w", err)
return log.E("setup.registry", "failed to load registry", err)
}
// Check workspace config for default_only if no filter specified
@ -82,7 +83,7 @@ func runRegistrySetupWithReg(ctx context.Context, reg *repos.Registry, registryP
// Ensure base path exists
if !dryRun {
if err := coreio.Local.EnsureDir(basePath); err != nil {
return fmt.Errorf("failed to create packages directory: %w", err)
return log.E("setup.registry", "failed to create packages directory", err)
}
}
@ -99,7 +100,7 @@ func runRegistrySetupWithReg(ctx context.Context, reg *repos.Registry, registryP
if useWizard {
selected, err := runPackageWizard(reg, typeFilter)
if err != nil {
return fmt.Errorf("wizard error: %w", err)
return log.E("setup.registry", "wizard error", err)
}
// Build set of selected repos
@ -227,7 +228,7 @@ func runRegistrySetupWithReg(ctx context.Context, reg *repos.Registry, registryP
buildCmd.Stdout = os.Stdout
buildCmd.Stderr = os.Stderr
if err := buildCmd.Run(); err != nil {
return fmt.Errorf("%s: %w", i18n.T("i18n.fail.run", "build"), err)
return log.E("setup.registry", i18n.T("i18n.fail.run", "build"), err)
}
}
@ -249,7 +250,7 @@ func gitClone(ctx context.Context, org, repo, path string) error {
// Only fall through to SSH if it's an auth error
if !strings.Contains(errStr, "Permission denied") &&
!strings.Contains(errStr, "could not read") {
return fmt.Errorf("%s", errStr)
return log.E("setup.registry", errStr, nil)
}
}
@ -258,7 +259,7 @@ func gitClone(ctx context.Context, org, repo, path string) error {
cmd := exec.CommandContext(ctx, "git", "clone", url, path)
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("%s", strings.TrimSpace(string(output)))
return log.E("setup.registry", strings.TrimSpace(string(output)), nil)
}
return nil
}

View file

@ -14,6 +14,7 @@ import (
"forge.lthn.ai/core/go-i18n"
coreio "forge.lthn.ai/core/go-io"
log "forge.lthn.ai/core/go-log"
)
// runRepoSetup sets up the current repository with .core/ configuration.
@ -28,7 +29,7 @@ func runRepoSetup(repoPath string, dryRun bool) error {
coreDir := filepath.Join(repoPath, ".core")
if !dryRun {
if err := coreio.Local.EnsureDir(coreDir); err != nil {
return fmt.Errorf("failed to create .core directory: %w", err)
return log.E("setup.repo", "failed to create .core directory", err)
}
}
@ -55,7 +56,7 @@ func runRepoSetup(repoPath string, dryRun bool) error {
for filename, content := range configs {
configPath := filepath.Join(coreDir, filename)
if err := coreio.Local.Write(configPath, content); err != nil {
return fmt.Errorf("failed to write %s: %w", filename, err)
return log.E("setup.repo", fmt.Sprintf("failed to write %s", filename), err)
}
fmt.Printf("%s %s %s\n", successStyle.Render(">>"), i18n.T("cmd.setup.repo.created"), configPath)
}

View file

@ -13,6 +13,7 @@ import (
"strings"
coreio "forge.lthn.ai/core/go-io"
log "forge.lthn.ai/core/go-log"
"gopkg.in/yaml.v3"
)
@ -67,7 +68,7 @@ type SecurityConfig struct {
func LoadGitHubConfig(path string) (*GitHubConfig, error) {
data, err := coreio.Local.Read(path)
if err != nil {
return nil, fmt.Errorf("failed to read config file: %w", err)
return nil, log.E("setup.github_config", "failed to read config file", err)
}
// Expand environment variables before parsing
@ -75,7 +76,7 @@ func LoadGitHubConfig(path string) (*GitHubConfig, error) {
var config GitHubConfig
if err := yaml.Unmarshal([]byte(expanded), &config); err != nil {
return nil, fmt.Errorf("failed to parse config file: %w", err)
return nil, log.E("setup.github_config", "failed to parse config file", err)
}
// Set defaults
@ -131,7 +132,7 @@ func FindGitHubConfig(registryDir, specifiedPath string) (string, error) {
if coreio.Local.IsFile(specifiedPath) {
return specifiedPath, nil
}
return "", fmt.Errorf("config file not found: %s", specifiedPath)
return "", log.E("setup.github_config", fmt.Sprintf("config file not found: %s", specifiedPath), nil)
}
// Search in common locations (using filepath.Join for OS-portable paths)
@ -146,26 +147,26 @@ func FindGitHubConfig(registryDir, specifiedPath string) (string, error) {
}
}
return "", fmt.Errorf("github.yaml not found in %s/.core/ or %s/", registryDir, registryDir)
return "", log.E("setup.github_config", fmt.Sprintf("github.yaml not found in %s/.core/ or %s/", registryDir, registryDir), nil)
}
// Validate checks the configuration for errors.
func (c *GitHubConfig) Validate() error {
if c.Version != 1 {
return fmt.Errorf("unsupported config version: %d (expected 1)", c.Version)
return log.E("setup.github_config", fmt.Sprintf("unsupported config version: %d (expected 1)", c.Version), nil)
}
// Validate labels
for i, label := range c.Labels {
if label.Name == "" {
return fmt.Errorf("label %d: name is required", i+1)
return log.E("setup.github_config", fmt.Sprintf("label %d: name is required", i+1), nil)
}
if label.Color == "" {
return fmt.Errorf("label %q: color is required", label.Name)
return log.E("setup.github_config", fmt.Sprintf("label %q: color is required", label.Name), nil)
}
// Validate color format (hex without #)
if !isValidHexColor(label.Color) {
return fmt.Errorf("label %q: invalid color %q (expected 6-digit hex without #)", label.Name, label.Color)
return log.E("setup.github_config", fmt.Sprintf("label %q: invalid color %q (expected 6-digit hex without #)", label.Name, label.Color), nil)
}
}
@ -176,14 +177,14 @@ func (c *GitHubConfig) Validate() error {
continue
}
if len(wh.Events) == 0 {
return fmt.Errorf("webhook %q: at least one event is required", name)
return log.E("setup.github_config", fmt.Sprintf("webhook %q: at least one event is required", name), nil)
}
}
// Validate branch protection
for i, bp := range c.BranchProtection {
if bp.Branch == "" {
return fmt.Errorf("branch_protection %d: branch is required", i+1)
return log.E("setup.github_config", fmt.Sprintf("branch_protection %d: branch is required", i+1), nil)
}
}

View file

@ -13,6 +13,7 @@ import (
"strings"
"forge.lthn.ai/core/cli/pkg/cli"
log "forge.lthn.ai/core/go-log"
)
// GitHubBranchProtection represents branch protection rules from the GitHub API.
@ -68,7 +69,7 @@ type RequiredConversationResolution struct {
func GetBranchProtection(repoFullName, branch string) (*GitHubBranchProtection, error) {
parts := strings.Split(repoFullName, "/")
if len(parts) != 2 {
return nil, fmt.Errorf("invalid repo format: %s", repoFullName)
return nil, log.E("setup.github", fmt.Sprintf("invalid repo format: %s", repoFullName), nil)
}
endpoint := fmt.Sprintf("repos/%s/%s/branches/%s/protection", parts[0], parts[1], branch)
@ -101,7 +102,7 @@ func GetBranchProtection(repoFullName, branch string) (*GitHubBranchProtection,
func SetBranchProtection(repoFullName, branch string, config BranchProtectionConfig) error {
parts := strings.Split(repoFullName, "/")
if len(parts) != 2 {
return fmt.Errorf("invalid repo format: %s", repoFullName)
return log.E("setup.github", fmt.Sprintf("invalid repo format: %s", repoFullName), nil)
}
// Build the protection payload

View file

@ -15,6 +15,7 @@ import (
"strings"
"forge.lthn.ai/core/cli/pkg/cli"
log "forge.lthn.ai/core/go-log"
)
// GitHubSecurityStatus represents the security settings status of a repository.
@ -46,7 +47,7 @@ type SecurityFeature struct {
func GetSecuritySettings(repoFullName string) (*GitHubSecurityStatus, error) {
parts := strings.Split(repoFullName, "/")
if len(parts) != 2 {
return nil, fmt.Errorf("invalid repo format: %s", repoFullName)
return nil, log.E("setup.github", fmt.Sprintf("invalid repo format: %s", repoFullName), nil)
}
status := &GitHubSecurityStatus{}
@ -102,7 +103,7 @@ func GetSecuritySettings(repoFullName string) (*GitHubSecurityStatus, error) {
func EnableDependabotAlerts(repoFullName string) error {
parts := strings.Split(repoFullName, "/")
if len(parts) != 2 {
return fmt.Errorf("invalid repo format: %s", repoFullName)
return log.E("setup.github", fmt.Sprintf("invalid repo format: %s", repoFullName), nil)
}
endpoint := fmt.Sprintf("repos/%s/%s/vulnerability-alerts", parts[0], parts[1])
@ -118,7 +119,7 @@ func EnableDependabotAlerts(repoFullName string) error {
func EnableDependabotSecurityUpdates(repoFullName string) error {
parts := strings.Split(repoFullName, "/")
if len(parts) != 2 {
return fmt.Errorf("invalid repo format: %s", repoFullName)
return log.E("setup.github", fmt.Sprintf("invalid repo format: %s", repoFullName), nil)
}
endpoint := fmt.Sprintf("repos/%s/%s/automated-security-fixes", parts[0], parts[1])
@ -134,7 +135,7 @@ func EnableDependabotSecurityUpdates(repoFullName string) error {
func DisableDependabotSecurityUpdates(repoFullName string) error {
parts := strings.Split(repoFullName, "/")
if len(parts) != 2 {
return fmt.Errorf("invalid repo format: %s", repoFullName)
return log.E("setup.github", fmt.Sprintf("invalid repo format: %s", repoFullName), nil)
}
endpoint := fmt.Sprintf("repos/%s/%s/automated-security-fixes", parts[0], parts[1])
@ -150,7 +151,7 @@ func DisableDependabotSecurityUpdates(repoFullName string) error {
func UpdateSecurityAndAnalysis(repoFullName string, secretScanning, pushProtection bool) error {
parts := strings.Split(repoFullName, "/")
if len(parts) != 2 {
return fmt.Errorf("invalid repo format: %s", repoFullName)
return log.E("setup.github", fmt.Sprintf("invalid repo format: %s", repoFullName), nil)
}
// Build the payload

View file

@ -13,6 +13,7 @@ import (
"strings"
"forge.lthn.ai/core/cli/pkg/cli"
log "forge.lthn.ai/core/go-log"
)
// GitHubWebhook represents a webhook as returned by the GitHub API.
@ -35,7 +36,7 @@ type GitHubWebhookConfig struct {
func ListWebhooks(repoFullName string) ([]GitHubWebhook, error) {
parts := strings.Split(repoFullName, "/")
if len(parts) != 2 {
return nil, fmt.Errorf("invalid repo format: %s", repoFullName)
return nil, log.E("setup.github", fmt.Sprintf("invalid repo format: %s", repoFullName), nil)
}
endpoint := fmt.Sprintf("repos/%s/%s/hooks", parts[0], parts[1])
@ -65,7 +66,7 @@ func ListWebhooks(repoFullName string) ([]GitHubWebhook, error) {
func CreateWebhook(repoFullName string, name string, config WebhookConfig) error {
parts := strings.Split(repoFullName, "/")
if len(parts) != 2 {
return fmt.Errorf("invalid repo format: %s", repoFullName)
return log.E("setup.github", fmt.Sprintf("invalid repo format: %s", repoFullName), nil)
}
// Build the webhook payload
@ -108,7 +109,7 @@ func CreateWebhook(repoFullName string, name string, config WebhookConfig) error
func UpdateWebhook(repoFullName string, hookID int, config WebhookConfig) error {
parts := strings.Split(repoFullName, "/")
if len(parts) != 2 {
return fmt.Errorf("invalid repo format: %s", repoFullName)
return log.E("setup.github", fmt.Sprintf("invalid repo format: %s", repoFullName), nil)
}
payload := map[string]any{

View file

@ -3,11 +3,12 @@ package coolify
import (
"context"
"encoding/json"
"errors"
"fmt"
"os"
"sync"
log "forge.lthn.ai/core/go-log"
"forge.lthn.ai/core/go-devops/deploy/python"
)
@ -42,15 +43,15 @@ func DefaultConfig() Config {
// NewClient creates a new Coolify client.
func NewClient(cfg Config) (*Client, error) {
if cfg.BaseURL == "" {
return nil, errors.New("COOLIFY_URL not set")
return nil, log.E("coolify", "COOLIFY_URL not set", nil)
}
if cfg.APIToken == "" {
return nil, errors.New("COOLIFY_TOKEN not set")
return nil, log.E("coolify", "COOLIFY_TOKEN not set", nil)
}
// Initialize Python runtime
if err := python.Init(); err != nil {
return nil, fmt.Errorf("failed to initialize Python: %w", err)
return nil, log.E("coolify", "failed to initialize Python", err)
}
return &Client{
@ -73,11 +74,11 @@ func (c *Client) Call(ctx context.Context, operationID string, params map[string
// Generate and run Python script
script, err := python.CoolifyScript(c.baseURL, c.apiToken, operationID, params)
if err != nil {
return nil, fmt.Errorf("failed to generate script: %w", err)
return nil, log.E("coolify", "failed to generate script", err)
}
output, err := python.RunScript(ctx, script)
if err != nil {
return nil, fmt.Errorf("API call %s failed: %w", operationID, err)
return nil, log.E("coolify", fmt.Sprintf("API call %s failed", operationID), err)
}
// Parse JSON result
@ -88,7 +89,7 @@ func (c *Client) Call(ctx context.Context, operationID string, params map[string
if err2 := json.Unmarshal([]byte(output), &arrResult); err2 == nil {
return map[string]any{"result": arrResult}, nil
}
return nil, fmt.Errorf("failed to parse response: %w (output: %s)", err, output)
return nil, log.E("coolify", fmt.Sprintf("failed to parse response (output: %s)", output), err)
}
return result, nil

View file

@ -3,9 +3,10 @@ package snapshot
import (
"encoding/json"
"errors"
"time"
log "forge.lthn.ai/core/go-log"
"forge.lthn.ai/core/go-scm/manifest"
)
@ -35,7 +36,7 @@ func Generate(m *manifest.Manifest, commit, tag string) ([]byte, error) {
// GenerateAt creates a core.json snapshot with an explicit build timestamp.
func GenerateAt(m *manifest.Manifest, commit, tag string, built time.Time) ([]byte, error) {
if m == nil {
return nil, errors.New("snapshot: manifest is nil")
return nil, log.E("snapshot", "manifest is nil", nil)
}
snap := Snapshot{