This commit is contained in:
Matthew Knight 2026-03-02 18:39:51 -08:00
parent 03df6a4c93
commit fe4c293adb
No known key found for this signature in database
10 changed files with 51 additions and 47 deletions

View File

@ -17,20 +17,20 @@ type Config struct {
S3SecretKey string S3SecretKey string
S3UseSSL bool S3UseSSL bool
ForgejoURL string ForgejoURL string
ForgejoToken string ForgejoToken string
ForgejoWebhookSecret string ForgejoWebhookSecret string
} }
func Load() (*Config, error) { func Load() (*Config, error) {
c := &Config{ c := &Config{
ListenAddr: envOr("CAIRN_LISTEN_ADDR", ":8080"), ListenAddr: envOr("CAIRN_LISTEN_ADDR", ":8080"),
DatabaseURL: envOr("CAIRN_DATABASE_URL", "postgres://cairn:cairn@localhost:5432/cairn?sslmode=disable"), DatabaseURL: envOr("CAIRN_DATABASE_URL", "postgres://cairn:cairn@localhost:5432/cairn?sslmode=disable"),
S3Endpoint: envOr("CAIRN_S3_ENDPOINT", "localhost:9000"), S3Endpoint: envOr("CAIRN_S3_ENDPOINT", "localhost:9000"),
S3Bucket: envOr("CAIRN_S3_BUCKET", "cairn-artifacts"), S3Bucket: envOr("CAIRN_S3_BUCKET", "cairn-artifacts"),
S3AccessKey: envOr("CAIRN_S3_ACCESS_KEY", "minioadmin"), S3AccessKey: envOr("CAIRN_S3_ACCESS_KEY", "minioadmin"),
S3SecretKey: envOr("CAIRN_S3_SECRET_KEY", "minioadmin"), S3SecretKey: envOr("CAIRN_S3_SECRET_KEY", "minioadmin"),
S3UseSSL: envBool("CAIRN_S3_USE_SSL", false), S3UseSSL: envBool("CAIRN_S3_USE_SSL", false),
ForgejoURL: envOr("CAIRN_FORGEJO_URL", ""), ForgejoURL: envOr("CAIRN_FORGEJO_URL", ""),
ForgejoToken: envOr("CAIRN_FORGEJO_TOKEN", ""), ForgejoToken: envOr("CAIRN_FORGEJO_TOKEN", ""),
ForgejoWebhookSecret: envOr("CAIRN_FORGEJO_WEBHOOK_SECRET", ""), ForgejoWebhookSecret: envOr("CAIRN_FORGEJO_WEBHOOK_SECRET", ""),

View File

@ -9,9 +9,9 @@ import (
const maxFrames = 8 const maxFrames = 8
var ( var (
hexAddrRe = regexp.MustCompile(`0x[0-9a-fA-F]+`) hexAddrRe = regexp.MustCompile(`0x[0-9a-fA-F]+`)
templateParamRe = regexp.MustCompile(`<[^>]*>`) templateParamRe = regexp.MustCompile(`<[^>]*>`)
abiTagRe = regexp.MustCompile(`\[abi:[^\]]*\]`) abiTagRe = regexp.MustCompile(`\[abi:[^\]]*\]`)
) )
// runtimePrefixes are function prefixes for runtime/library frames to filter out. // runtimePrefixes are function prefixes for runtime/library frames to filter out.

View File

@ -7,15 +7,17 @@ import (
) )
// ASan/MSan/TSan/UBSan frame patterns: // ASan/MSan/TSan/UBSan frame patterns:
// #0 0x55a3b4 in function_name /path/to/file.c:42:13 //
// #0 0x55a3b4 in function_name (/path/to/binary+0x1234) // #0 0x55a3b4 in function_name /path/to/file.c:42:13
// #1 0x55a3b4 (/path/to/binary+0x1234) // #0 0x55a3b4 in function_name (/path/to/binary+0x1234)
// #1 0x55a3b4 (/path/to/binary+0x1234)
var asanFrameRe = regexp.MustCompile( var asanFrameRe = regexp.MustCompile(
`^\s*#(\d+)\s+(0x[0-9a-fA-F]+)\s+(?:in\s+(\S+)\s+)?(.*)$`, `^\s*#(\d+)\s+(0x[0-9a-fA-F]+)\s+(?:in\s+(\S+)\s+)?(.*)$`,
) )
// ASan error header line, e.g.: // ASan error header line, e.g.:
// ==12345==ERROR: AddressSanitizer: heap-buffer-overflow //
// ==12345==ERROR: AddressSanitizer: heap-buffer-overflow
var asanHeaderRe = regexp.MustCompile( var asanHeaderRe = regexp.MustCompile(
`==\d+==ERROR:\s+(Address|Memory|Thread|Undefined)Sanitizer`, `==\d+==ERROR:\s+(Address|Memory|Thread|Undefined)Sanitizer`,
) )

View File

@ -7,9 +7,10 @@ import (
) )
// GDB backtrace frame patterns: // GDB backtrace frame patterns:
// #0 function_name (args) at /path/to/file.c:42 //
// #0 0x00007fff in function_name () from /lib/libfoo.so // #0 function_name (args) at /path/to/file.c:42
// #0 0x00007fff in ?? () // #0 0x00007fff in function_name () from /lib/libfoo.so
// #0 0x00007fff in ?? ()
var gdbFrameRe = regexp.MustCompile( var gdbFrameRe = regexp.MustCompile(
`^\s*#(\d+)\s+(?:(0x[0-9a-fA-F]+)\s+in\s+)?(\S+)\s*\(([^)]*)\)\s*(?:at\s+(\S+?)(?::(\d+))?)?(?:\s+from\s+(\S+))?`, `^\s*#(\d+)\s+(?:(0x[0-9a-fA-F]+)\s+in\s+)?(\S+)\s*\(([^)]*)\)\s*(?:at\s+(\S+?)(?::(\d+))?)?(?:\s+from\s+(\S+))?`,
) )

View File

@ -7,8 +7,9 @@ import (
) )
// Zig panic/stack trace patterns: // Zig panic/stack trace patterns:
// /path/to/file.zig:42:13: 0x1234 in function_name (module) //
// ???:?:?: 0x1234 in ??? (???) // /path/to/file.zig:42:13: 0x1234 in function_name (module)
// ???:?:?: 0x1234 in ??? (???)
var zigFrameRe = regexp.MustCompile( var zigFrameRe = regexp.MustCompile(
`^\s*(.+?):(\d+):\d+:\s+(0x[0-9a-fA-F]+)\s+in\s+(\S+)\s+\(([^)]*)\)`, `^\s*(.+?):(\d+):\d+:\s+(0x[0-9a-fA-F]+)\s+in\s+(\S+)\s+\(([^)]*)\)`,
) )

View File

@ -38,9 +38,9 @@ type Issue struct {
// CreateIssueRequest is the body for creating a Forgejo issue. // CreateIssueRequest is the body for creating a Forgejo issue.
type CreateIssueRequest struct { type CreateIssueRequest struct {
Title string `json:"title"` Title string `json:"title"`
Body string `json:"body"` Body string `json:"body"`
Labels []int64 `json:"labels,omitempty"` Labels []int64 `json:"labels,omitempty"`
} }
// CommitStatus represents a Forgejo commit status. // CommitStatus represents a Forgejo commit status.

View File

@ -12,13 +12,13 @@ import (
// WebhookEvent is the parsed payload from a Forgejo webhook. // WebhookEvent is the parsed payload from a Forgejo webhook.
type WebhookEvent struct { type WebhookEvent struct {
Action string `json:"action"` Action string `json:"action"`
Issue *WebhookIssue `json:"issue,omitempty"` Issue *WebhookIssue `json:"issue,omitempty"`
Repo *WebhookRepo `json:"repository,omitempty"` Repo *WebhookRepo `json:"repository,omitempty"`
Sender *WebhookUser `json:"sender,omitempty"` Sender *WebhookUser `json:"sender,omitempty"`
Ref string `json:"ref,omitempty"` Ref string `json:"ref,omitempty"`
After string `json:"after,omitempty"` After string `json:"after,omitempty"`
Before string `json:"before,omitempty"` Before string `json:"before,omitempty"`
} }
type WebhookIssue struct { type WebhookIssue struct {

View File

@ -6,12 +6,12 @@ import (
) )
type Repository struct { type Repository struct {
ID string `json:"id"` ID string `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Owner string `json:"owner"` Owner string `json:"owner"`
ForgejoURL string `json:"forgejo_url,omitempty"` ForgejoURL string `json:"forgejo_url,omitempty"`
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"` UpdatedAt time.Time `json:"updated_at"`
} }
type Commit struct { type Commit struct {

View File

@ -9,13 +9,13 @@ import (
// Result holds the regression comparison between two commits. // Result holds the regression comparison between two commits.
type Result struct { type Result struct {
BaseSHA string `json:"base_sha"` BaseSHA string `json:"base_sha"`
HeadSHA string `json:"head_sha"` HeadSHA string `json:"head_sha"`
RepoName string `json:"repo_name"` RepoName string `json:"repo_name"`
New []string `json:"new"` // Fingerprints in head but not base. New []string `json:"new"` // Fingerprints in head but not base.
Fixed []string `json:"fixed"` // Fingerprints in base but not head. Fixed []string `json:"fixed"` // Fingerprints in base but not head.
Recurring []string `json:"recurring"` // Fingerprints in both. Recurring []string `json:"recurring"` // Fingerprints in both.
IsRegression bool `json:"is_regression"` IsRegression bool `json:"is_regression"`
} }
// Compare computes the set difference of crash fingerprints between a base and head commit. // Compare computes the set difference of crash fingerprints between a base and head commit.

View File

@ -13,10 +13,10 @@ import (
) )
type RouterConfig struct { type RouterConfig struct {
Pool *pgxpool.Pool Pool *pgxpool.Pool
Store blob.Store Store blob.Store
ForgejoClient *forgejo.Client ForgejoClient *forgejo.Client
WebhookSecret string WebhookSecret string
} }
func NewRouter(cfg RouterConfig) (*gin.Engine, error) { func NewRouter(cfg RouterConfig) (*gin.Engine, error) {