From d2aa7e3605ad4668a96ca92eae35f29e4d0b0f9f Mon Sep 17 00:00:00 2001 From: GitCaddy Date: Wed, 14 Jan 2026 07:22:04 +0000 Subject: [PATCH] feat(ai): Add auto-seed for error patterns on fresh deploy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add options/seed/error_patterns.json with default upload error patterns - Add models/ai/seed.go with SeedErrorPatternsIfEmpty function - Call seed on startup after models.Init if error_pattern table is empty - Seeds 10 common error patterns for uploads, .NET, etc. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- models/ai/seed.go | 73 +++++++++++++++++++++++++ options/seed/error_patterns.json | 94 ++++++++++++++++++++++++++++++++ routers/init.go | 2 + 3 files changed, 169 insertions(+) create mode 100644 models/ai/seed.go create mode 100644 options/seed/error_patterns.json diff --git a/models/ai/seed.go b/models/ai/seed.go new file mode 100644 index 0000000000..3fb6eed125 --- /dev/null +++ b/models/ai/seed.go @@ -0,0 +1,73 @@ +// Copyright 2026 MarketAlly. All rights reserved. +// SPDX-License-Identifier: MIT + +package ai + +import ( + "context" + "encoding/json" + + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/options" +) + +// SeedData represents the structure of the seed JSON file +type SeedData struct { + Patterns []struct { + Pattern string `json:"pattern"` + RunnerType string `json:"runner_type"` + ProjectType string `json:"project_type"` + Framework string `json:"framework"` + ErrorMessage string `json:"error_message"` + Diagnosis string `json:"diagnosis"` + Solution string `json:"solution"` + } `json:"patterns"` +} + +// SeedErrorPatternsIfEmpty seeds error patterns from the seed file if the table is empty +func SeedErrorPatternsIfEmpty(ctx context.Context) error { + count, err := db.GetEngine(ctx).Count(&ErrorPattern{}) + if err != nil { + return err + } + + if count > 0 { + log.Debug("Error patterns table already has %d entries, skipping seed", count) + return nil + } + + log.Info("Seeding error patterns from seed file...") + + // Load seed data from embedded options + data, err := options.AssetFS().ReadFile("seed/error_patterns.json") + if err != nil { + log.Warn("Could not read error patterns seed file: %v", err) + return nil // Not an error, just no seed file + } + + var seedData SeedData + if err := json.Unmarshal(data, &seedData); err != nil { + log.Error("Failed to parse error patterns seed file: %v", err) + return err + } + + return db.WithTx(ctx, func(ctx context.Context) error { + for _, p := range seedData.Patterns { + ep := &ErrorPattern{ + Pattern: p.Pattern, + RunnerType: p.RunnerType, + ProjectType: p.ProjectType, + Framework: p.Framework, + ErrorMessage: p.ErrorMessage, + Diagnosis: p.Diagnosis, + Solution: p.Solution, + } + if err := db.Insert(ctx, ep); err != nil { + return err + } + } + log.Info("Seeded %d error patterns", len(seedData.Patterns)) + return nil + }) +} diff --git a/options/seed/error_patterns.json b/options/seed/error_patterns.json new file mode 100644 index 0000000000..fca03bae6e --- /dev/null +++ b/options/seed/error_patterns.json @@ -0,0 +1,94 @@ +{ + "patterns": [ + { + "pattern": "curl: (28) Connection timed out", + "runner_type": "linux", + "project_type": "release-upload", + "framework": "curl", + "error_message": "curl: (28) Connection timed out after X milliseconds", + "diagnosis": "The upload connection timed out. This typically occurs with large files over unstable networks or when server timeouts are too short.", + "solution": "Use HTTP/1.1 with keepalive and extended timeouts: curl --http1.1 --keepalive-time 60 --connect-timeout 120 --max-time 3600 --retry 5 --retry-delay 10" + }, + { + "pattern": "curl: (56) Recv failure: Connection reset by peer", + "runner_type": "linux", + "project_type": "release-upload", + "framework": "curl", + "error_message": "curl: (56) Recv failure: Connection reset by peer", + "diagnosis": "The connection was reset during upload. This often happens with HTTP/2 multiplexing issues or when proxies/load balancers terminate idle connections.", + "solution": "Force HTTP/1.1 to avoid multiplexing issues: curl --http1.1 --retry 5 --retry-all-errors -X POST -F attachment=@file" + }, + { + "pattern": "file_too_large", + "runner_type": "", + "project_type": "release-upload", + "framework": "gitcaddy-api", + "error_message": "File exceeds maximum size", + "diagnosis": "The uploaded file exceeds the server maximum attachment size limit.", + "solution": "Check max size with GET /api/v2/upload/instructions. Split large files or request server config increase. Default limit is typically 500MB." + }, + { + "pattern": "curl: (35) SSL connect error", + "runner_type": "linux", + "project_type": "release-upload", + "framework": "curl", + "error_message": "curl: (35) SSL connect error or OpenSSL SSL_connect error", + "diagnosis": "SSL/TLS handshake failed. May be caused by certificate issues, protocol mismatch, or network interference.", + "solution": "Try with explicit TLS version: curl --tlsv1.2 or check certificate validity. For self-signed certs in testing, use -k flag." + }, + { + "pattern": "curl: (6) Could not resolve host", + "runner_type": "linux", + "project_type": "release-upload", + "framework": "curl", + "error_message": "curl: (6) Could not resolve host: git.example.com", + "diagnosis": "DNS resolution failed for the upload server hostname.", + "solution": "Pre-warm DNS before upload: curl -s -o /dev/null -I https://server/api/healthz; then retry upload. Check /etc/resolv.conf and network connectivity." + }, + { + "pattern": "upload_failed", + "runner_type": "", + "project_type": "release-upload", + "framework": "gitcaddy-api", + "error_message": "upload_failed: unexpected EOF or connection closed", + "diagnosis": "Upload was interrupted before completion. Often caused by network instability or timeout during large file transfers.", + "solution": "Use gitcaddy-upload helper with automatic retry: gitcaddy-upload -url URL -token TOKEN -file FILE -retries 5. Or use curl with --retry 5 --retry-all-errors flags." + }, + { + "pattern": "HTTP 413 Request Entity Too Large", + "runner_type": "", + "project_type": "release-upload", + "framework": "http", + "error_message": "HTTP 413 Request Entity Too Large or Payload Too Large", + "diagnosis": "The request body exceeds the server or reverse proxy limit. Nginx/Caddy may have lower limits than the application.", + "solution": "Check proxy config: nginx client_max_body_size, Caddy request_body max_size. Ensure all layers allow the file size." + }, + { + "pattern": "NETSDK1147", + "runner_type": "macos", + "project_type": "dotnet-maui", + "framework": "net9.0-android", + "error_message": "NETSDK1147: To build this project, the following workloads must be installed: maui-android", + "diagnosis": "The .NET MAUI Android workload is not installed on the build machine.", + "solution": "Install the workload: dotnet workload install maui-android. For CI, add this step before build." + }, + { + "pattern": "XA5300", + "runner_type": "macos", + "project_type": "dotnet-maui", + "framework": "net9.0-android", + "error_message": "XA5300: The Android SDK directory could not be found", + "diagnosis": "Android SDK is not installed or ANDROID_HOME environment variable is not set.", + "solution": "Set ANDROID_HOME to the SDK path. On macOS: export ANDROID_HOME=$HOME/Library/Android/sdk" + }, + { + "pattern": "MSB4019", + "runner_type": "", + "project_type": "dotnet", + "framework": "", + "error_message": "MSB4019: The imported project was not found", + "diagnosis": "A required MSBuild targets file is missing, usually due to missing SDK or workload.", + "solution": "Install the required workload or SDK. Run: dotnet workload list to see installed workloads." + } + ] +} diff --git a/routers/init.go b/routers/init.go index b8185690ca..39cbd4c3f9 100644 --- a/routers/init.go +++ b/routers/init.go @@ -10,6 +10,7 @@ import ( "runtime" "code.gitea.io/gitea/models" + ai_model "code.gitea.io/gitea/models/ai" authmodel "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/eventsource" @@ -151,6 +152,7 @@ func InitWebInstalled(ctx context.Context) { mustInit(release_service.Init) mustInitCtx(ctx, models.Init) + mustInitCtx(ctx, ai_model.SeedErrorPatternsIfEmpty) mustInitCtx(ctx, authmodel.Init) mustInitCtx(ctx, repo_service.Init)