fix(ci): migrate to pure-Go SQLite and GoReleaser v2
Fixes nightly build failures caused by: GoReleaser v2 requiring version 2 config syntax Zig cross-compilation failing for macOS CGO targets SQLite Driver Migration: Replace gorm.io/driver/sqlite with github.com/glebarez/sqlite (pure-Go) Execute PRAGMA statements via SQL instead of DSN parameters All platforms now build with CGO_ENABLED=0 GoReleaser v2 Migration: Update version: 1 → version: 2 snapshot.name_template → version_template archives.format → formats (array syntax) archives.builds → ids nfpms.builds → ids Remove Zig cross-compilation environment Also fixes Docker Compose E2E image reference: Use CHARON_E2E_IMAGE_TAG instead of bare digest Add fallback default for local development All database tests pass with the pure-Go SQLite driver.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
version: 1
|
||||
version: 2
|
||||
|
||||
project_name: charon
|
||||
|
||||
@@ -8,9 +8,7 @@ builds:
|
||||
main: ./cmd/api
|
||||
binary: charon
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
- CC=zig cc -target {{ if eq .Arch "amd64" }}x86_64{{ else }}aarch64{{ end }}-linux-gnu
|
||||
- CXX=zig c++ -target {{ if eq .Arch "amd64" }}x86_64{{ else }}aarch64{{ end }}-linux-gnu
|
||||
- CGO_ENABLED=0
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
@@ -27,9 +25,7 @@ builds:
|
||||
main: ./cmd/api
|
||||
binary: charon
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
- CC=zig cc -target x86_64-windows-gnu
|
||||
- CXX=zig c++ -target x86_64-windows-gnu
|
||||
- CGO_ENABLED=0
|
||||
goos:
|
||||
- windows
|
||||
goarch:
|
||||
@@ -45,9 +41,7 @@ builds:
|
||||
main: ./cmd/api
|
||||
binary: charon
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
- CC=zig cc -target {{ if eq .Arch "amd64" }}x86_64{{ else }}aarch64{{ end }}-macos-none
|
||||
- CXX=zig c++ -target {{ if eq .Arch "amd64" }}x86_64{{ else }}aarch64{{ end }}-macos-none
|
||||
- CGO_ENABLED=0
|
||||
goos:
|
||||
- darwin
|
||||
goarch:
|
||||
@@ -60,9 +54,10 @@ builds:
|
||||
- -X github.com/Wikid82/charon/backend/internal/version.BuildTime={{.Date}}
|
||||
|
||||
archives:
|
||||
- format: tar.gz
|
||||
- formats:
|
||||
- tar.gz
|
||||
id: nix
|
||||
builds:
|
||||
ids:
|
||||
- linux
|
||||
- darwin
|
||||
name_template: >-
|
||||
@@ -74,9 +69,10 @@ archives:
|
||||
- LICENSE
|
||||
- README.md
|
||||
|
||||
- format: zip
|
||||
- formats:
|
||||
- zip
|
||||
id: windows
|
||||
builds:
|
||||
ids:
|
||||
- windows
|
||||
name_template: >-
|
||||
{{ .ProjectName }}_
|
||||
@@ -89,7 +85,7 @@ archives:
|
||||
|
||||
nfpms:
|
||||
- id: packages
|
||||
builds:
|
||||
ids:
|
||||
- linux
|
||||
package_name: charon
|
||||
vendor: Charon
|
||||
@@ -115,7 +111,7 @@ checksum:
|
||||
name_template: 'checksums.txt'
|
||||
|
||||
snapshot:
|
||||
name_template: "{{ .Tag }}-next"
|
||||
version_template: "{{ .Tag }}-next"
|
||||
|
||||
changelog:
|
||||
sort: asc
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/Wikid82/charon/backend/internal/util"
|
||||
"github.com/google/uuid"
|
||||
"github.com/sirupsen/logrus"
|
||||
"gorm.io/driver/sqlite"
|
||||
"github.com/glebarez/sqlite"
|
||||
"gorm.io/gorm"
|
||||
gormlogger "gorm.io/gorm/logger"
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ require (
|
||||
github.com/docker/docker v28.5.2+incompatible
|
||||
github.com/gin-contrib/gzip v1.2.5
|
||||
github.com/gin-gonic/gin v1.11.0
|
||||
github.com/glebarez/sqlite v1.11.0
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
@@ -37,10 +38,12 @@ require (
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
github.com/docker/go-connections v0.6.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/fatih/color v1.15.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.12 // indirect
|
||||
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||
github.com/glebarez/go-sqlite v1.21.2 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
@@ -76,6 +79,7 @@ require (
|
||||
github.com/prometheus/procfs v0.16.1 // indirect
|
||||
github.com/quic-go/qpack v0.6.0 // indirect
|
||||
github.com/quic-go/quic-go v0.57.1 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.3.0 // indirect
|
||||
@@ -93,4 +97,8 @@ require (
|
||||
google.golang.org/protobuf v1.36.10 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gotest.tools/v3 v3.5.2 // indirect
|
||||
modernc.org/libc v1.22.5 // indirect
|
||||
modernc.org/mathutil v1.5.0 // indirect
|
||||
modernc.org/memory v1.5.0 // indirect
|
||||
modernc.org/sqlite v1.23.1 // indirect
|
||||
)
|
||||
|
||||
@@ -35,6 +35,8 @@ github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pM
|
||||
github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
|
||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
@@ -47,6 +49,10 @@ github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w
|
||||
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
||||
github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
|
||||
github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls=
|
||||
github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo=
|
||||
github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
|
||||
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
|
||||
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
@@ -73,8 +79,8 @@ github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
@@ -155,6 +161,9 @@ github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
|
||||
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
|
||||
github.com/quic-go/quic-go v0.57.1 h1:25KAAR9QR8KZrCZRThWMKVAwGoiHIrNbT72ULHTuI10=
|
||||
github.com/quic-go/quic-go v0.57.1/go.mod h1:ly4QBAjHA2VhdnxhojRsCUOeJwKYg+taDlos92xb1+s=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
@@ -240,3 +249,11 @@ gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg=
|
||||
gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
|
||||
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
|
||||
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
|
||||
modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE=
|
||||
modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY=
|
||||
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
|
||||
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
|
||||
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
||||
modernc.org/sqlite v1.23.1 h1:nrSBg4aRQQwq59JpvGEQ15tNxoO5pX/kUjcRNwSAGQM=
|
||||
modernc.org/sqlite v1.23.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk=
|
||||
|
||||
@@ -4,30 +4,18 @@ package database
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/Wikid82/charon/backend/internal/logger"
|
||||
"gorm.io/driver/sqlite"
|
||||
"github.com/glebarez/sqlite"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Connect opens a SQLite database connection with optimized settings.
|
||||
// Uses WAL mode for better concurrent read/write performance.
|
||||
func Connect(dbPath string) (*gorm.DB, error) {
|
||||
// Add SQLite performance pragmas if not already present
|
||||
dsn := dbPath
|
||||
if !strings.Contains(dsn, "?") {
|
||||
dsn += "?"
|
||||
} else {
|
||||
dsn += "&"
|
||||
}
|
||||
// WAL mode: better concurrent access, faster writes
|
||||
// busy_timeout: wait up to 5s instead of failing immediately on lock
|
||||
// cache: shared cache for better memory usage
|
||||
// synchronous=NORMAL: good balance of safety and speed
|
||||
dsn += "_journal_mode=WAL&_busy_timeout=5000&_synchronous=NORMAL&_cache_size=-64000"
|
||||
|
||||
db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{
|
||||
// Open the database connection
|
||||
// Note: PRAGMA settings are applied after connection for modernc.org/sqlite compatibility
|
||||
db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{
|
||||
// Skip default transaction for single operations (faster)
|
||||
SkipDefaultTransaction: true,
|
||||
// Prepare statements for reuse
|
||||
@@ -44,12 +32,27 @@ func Connect(dbPath string) (*gorm.DB, error) {
|
||||
}
|
||||
configurePool(sqlDB)
|
||||
|
||||
// Set SQLite performance pragmas via SQL execution
|
||||
// This is required for modernc.org/sqlite (pure-Go driver) which doesn't
|
||||
// support DSN-based pragma parameters like mattn/go-sqlite3
|
||||
pragmas := []string{
|
||||
"PRAGMA journal_mode=WAL", // Better concurrent access, faster writes
|
||||
"PRAGMA busy_timeout=5000", // Wait up to 5s instead of failing immediately on lock
|
||||
"PRAGMA synchronous=NORMAL", // Good balance of safety and speed
|
||||
"PRAGMA cache_size=-64000", // 64MB cache for better performance
|
||||
}
|
||||
for _, pragma := range pragmas {
|
||||
if _, err := sqlDB.Exec(pragma); err != nil {
|
||||
return nil, fmt.Errorf("failed to execute %s: %w", pragma, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Verify WAL mode is enabled and log confirmation
|
||||
var journalMode string
|
||||
if err := db.Raw("PRAGMA journal_mode").Scan(&journalMode).Error; err != nil {
|
||||
logger.Log().WithError(err).Warn("Failed to verify SQLite journal mode")
|
||||
} else {
|
||||
logger.Log().WithField("journal_mode", journalMode).Info("SQLite database connected with WAL mode enabled")
|
||||
logger.Log().WithField("journal_mode", journalMode).Info("SQLite database connected with optimized settings")
|
||||
}
|
||||
|
||||
// Run quick integrity check on startup (non-blocking, warn-only)
|
||||
|
||||
@@ -3,7 +3,7 @@ package testutil
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gorm.io/driver/sqlite"
|
||||
"github.com/glebarez/sqlite"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
|
||||
@@ -1,448 +1,557 @@
|
||||
# Docker Compose CI Failure Remediation Plan
|
||||
# GoReleaser v2 Migration & Nightly Build Failure Remediation
|
||||
|
||||
**Status**: Active
|
||||
**Created**: 2026-01-30
|
||||
**Priority**: CRITICAL (Blocking CI)
|
||||
**Priority**: CRITICAL (Blocking Nightly Builds)
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
The E2E test workflow (`e2e-tests.yml`) is failing when attempting to start containers via `docker-compose.playwright-ci.yml`. The root cause is an incorrect Docker image reference format in the compose file that attempts to use a bare SHA256 digest instead of a fully-qualified image reference with registry and repository.
|
||||
The nightly build workflow (`nightly-build.yml`) is failing with multiple issues:
|
||||
|
||||
**Error Message**:
|
||||
1. **GoReleaser v2 Compatibility**: Config uses deprecated v1 syntax
|
||||
2. **Zig Cross-Compilation**: Incorrect macOS target triple format
|
||||
3. **🆕 CGO/SQLite Dependency**: Disabling CGO for darwin breaks SQLite (`mattn/go-sqlite3` requires CGO)
|
||||
|
||||
**Error Messages**:
|
||||
```
|
||||
charon-app Error pull access denied for sha256, repository does not exist or may require 'docker login': denied: requested access to the resource is denied
|
||||
only version: 2 configuration files are supported, yours is version: 1, please update your configuration
|
||||
```
|
||||
|
||||
**Root Cause**: The compose file's `image:` directive evaluates to a bare SHA256 digest (e.g., `sha256:057a9998...`) instead of a properly formatted image reference like `ghcr.io/wikid82/charon@sha256:057a9998...`.
|
||||
**Deprecation Warnings**:
|
||||
- `snapshot.name_template` is deprecated
|
||||
- `archives.format` is deprecated
|
||||
- `archives.builds` is deprecated
|
||||
- `nfpms.builds` is deprecated
|
||||
|
||||
**Build Error** (Zig):
|
||||
```
|
||||
error: unable to find or provide libc for target 'x86_64-macos.11.7.1...13.3-gnu'
|
||||
info: zig can provide libc for related target x86_64-macos.11-none
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Issue 1: Nightly Build - GoReleaser macOS Cross-Compile Failure
|
||||
## 🔴 Critical Dependency: SQLite CGO Issue
|
||||
|
||||
### Problem Statement
|
||||
|
||||
The current SQLite driver (`gorm.io/driver/sqlite`) depends on `mattn/go-sqlite3`, which is a CGO-based library. This means:
|
||||
|
||||
- **CGO_ENABLED=0** will cause build failures when SQLite is used
|
||||
- **Cross-compilation** for darwin from Linux is blocked by CGO complexity
|
||||
- The proposed fix of disabling CGO for darwin builds **will break the application**
|
||||
|
||||
### Solution: Migrate to Pure-Go SQLite
|
||||
|
||||
**Recommended Migration Path:**
|
||||
|
||||
| Current | New | Notes |
|
||||
|---------|-----|-------|
|
||||
| `gorm.io/driver/sqlite` | `github.com/glebarez/sqlite` | GORM-compatible pure-Go driver |
|
||||
| `mattn/go-sqlite3` (indirect) | `modernc.org/sqlite` (indirect) | Pure-Go SQLite implementation |
|
||||
|
||||
**Benefits:**
|
||||
1. ✅ No CGO required for any platform
|
||||
2. ✅ Simplified cross-compilation (no Zig needed for SQLite)
|
||||
3. ✅ Smaller binary size
|
||||
4. ✅ Faster build times
|
||||
5. ✅ Same GORM API - minimal code changes required
|
||||
|
||||
### Files Requiring SQLite Driver Changes
|
||||
|
||||
| File | Line | Change Required |
|
||||
|------|------|-----------------|
|
||||
| [backend/internal/database/database.go](../../backend/internal/database/database.go#L10) | 10 | `gorm.io/driver/sqlite` → `github.com/glebarez/sqlite` |
|
||||
| [backend/internal/testutil/db_test.go](../../backend/internal/testutil/db_test.go#L6) | 6 | `gorm.io/driver/sqlite` → `github.com/glebarez/sqlite` |
|
||||
| [backend/cmd/seed/main.go](../../backend/cmd/seed/main.go#L13) | 13 | `gorm.io/driver/sqlite` → `github.com/glebarez/sqlite` |
|
||||
| [backend/go.mod](../../backend/go.mod#L19) | 19 | Replace `gorm.io/driver/sqlite` with `github.com/glebarez/sqlite` |
|
||||
|
||||
---
|
||||
|
||||
## Issue 1: GoReleaser v1 → v2 Migration (CRITICAL)
|
||||
|
||||
### Problem Statement
|
||||
|
||||
GoReleaser v2 (currently v2.13.3) no longer supports `version: 1` configuration files. The nightly workflow uses GoReleaser `~> v2` which requires v2 config syntax.
|
||||
|
||||
### Root Cause Analysis
|
||||
|
||||
Current `.goreleaser.yaml` uses deprecated v1 syntax:
|
||||
|
||||
```yaml
|
||||
version: 1 # ❌ v2 requires "version: 2"
|
||||
```
|
||||
|
||||
Multiple deprecated fields need updating:
|
||||
| Deprecated Field | v2 Replacement |
|
||||
|-----------------|----------------|
|
||||
| `snapshot.name_template` | `snapshot.version_template` |
|
||||
| `archives.format` | `archives.formats` (array) |
|
||||
| `archives.builds` | `archives.ids` |
|
||||
| `nfpms.builds` | `nfpms.ids` |
|
||||
|
||||
### GoReleaser Deprecation Reference
|
||||
|
||||
From [goreleaser.com/deprecations](https://goreleaser.com/deprecations):
|
||||
|
||||
1. **`snapshot.name_template`** → `snapshot.version_template`
|
||||
- Changed in v2.0.0
|
||||
- The template generates a version string, not a "name"
|
||||
|
||||
2. **`archives.format`** → `archives.formats`
|
||||
- Changed to array to support multiple formats per archive config
|
||||
- Must be `formats: [tar.gz]` not `format: tar.gz`
|
||||
|
||||
3. **`archives.builds`** → `archives.ids`
|
||||
- Renamed for clarity: it filters by build `id`, not "builds"
|
||||
|
||||
4. **`nfpms.builds`** → `nfpms.ids`
|
||||
- Same rationale as archives
|
||||
|
||||
### Required Changes
|
||||
|
||||
```diff
|
||||
--- a/.goreleaser.yaml
|
||||
+++ b/.goreleaser.yaml
|
||||
@@ -1,4 +1,4 @@
|
||||
-version: 1
|
||||
+version: 2
|
||||
|
||||
project_name: charon
|
||||
|
||||
@@ -62,10 +62,10 @@
|
||||
- -X github.com/Wikid82/charon/backend/internal/version.BuildTime={{.Date}}
|
||||
|
||||
archives:
|
||||
- - format: tar.gz
|
||||
+ - formats: [tar.gz]
|
||||
id: nix
|
||||
- builds:
|
||||
+ ids:
|
||||
- linux
|
||||
- darwin
|
||||
name_template: >-
|
||||
@@ -76,9 +76,9 @@
|
||||
- LICENSE
|
||||
- README.md
|
||||
|
||||
- - format: zip
|
||||
+ - formats: [zip]
|
||||
id: windows
|
||||
- builds:
|
||||
+ ids:
|
||||
- windows
|
||||
name_template: >-
|
||||
{{ .ProjectName }}_
|
||||
@@ -90,7 +90,7 @@
|
||||
|
||||
nfpms:
|
||||
- id: packages
|
||||
- builds:
|
||||
+ ids:
|
||||
- linux
|
||||
package_name: charon
|
||||
vendor: Charon
|
||||
@@ -116,7 +116,7 @@
|
||||
name_template: 'checksums.txt'
|
||||
|
||||
snapshot:
|
||||
- name_template: "{{ .Tag }}-next"
|
||||
+ version_template: "{{ .Tag }}-next"
|
||||
|
||||
changelog:
|
||||
sort: asc
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Issue 2: Zig Cross-Compilation for macOS
|
||||
|
||||
### Problem Statement
|
||||
|
||||
The nightly build fails during GoReleaser release step when cross-compiling for macOS (darwin) using Zig:
|
||||
|
||||
```text
|
||||
release failed after 4m19s
|
||||
error=
|
||||
build failed: exit status 1: go: downloading github.com/gin-gonic/gin v1.11.0
|
||||
info: zig can provide libc for related target x86_64-macos.11-none
|
||||
target=darwin_amd64_v1
|
||||
error: unable to find or provide libc for target 'x86_64-macos.11.7.1...13.3-gnu'
|
||||
info: zig can provide libc for related target x86_64-macos.11-none
|
||||
```
|
||||
|
||||
### Root Cause Analysis
|
||||
|
||||
The `.goreleaser.yaml` darwin build uses incorrect Zig target specification:
|
||||
The `.goreleaser.yaml` darwin build uses **`-macos-none`** which is correct, but examining the actual file shows **`-macos-none`** is already in place. The error message suggests something is injecting version numbers.
|
||||
|
||||
**Current (WRONG):**
|
||||
**Wait** - Re-reading the current config, I see it actually says `-macos-none` already. Let me check if there's a different issue.
|
||||
|
||||
Actually, looking at the error more carefully:
|
||||
```
|
||||
target 'x86_64-macos.11.7.1...13.3-gnu'
|
||||
```
|
||||
|
||||
This suggests the **Go runtime/cgo is detecting the macOS version range** and passing it to Zig incorrectly. The `-gnu` suffix shouldn't be there for macOS.
|
||||
|
||||
**Current Configuration**:
|
||||
```yaml
|
||||
CC=zig cc -target {{ if eq .Arch "amd64" }}x86_64{{ else }}aarch64{{ end }}-macos-gnu
|
||||
CXX=zig c++ -target {{ if eq .Arch "amd64" }}x86_64{{ else }}aarch64{{ end }}-macos-gnu
|
||||
CC=zig cc -target {{ if eq .Arch "amd64" }}x86_64{{ else }}aarch64{{ end }}-macos-none
|
||||
```
|
||||
|
||||
**Issue:** macOS uses its own libc (libSystem), not GNU libc. The `-gnu` suffix is invalid for macOS targets. Zig expects `-macos-none` or `-macos.11-none` for macOS builds.
|
||||
The current config is correct (`-macos-none`), but CGO may be interfering.
|
||||
|
||||
### Affected Files
|
||||
### ~~Recommended Fix: Disable CGO for Darwin~~
|
||||
|
||||
| File | Change Type |
|
||||
|------|-------------|
|
||||
| `.goreleaser.yaml` | Fix Zig target for darwin builds |
|
||||
> **⚠️ UPDATE:** This section is superseded by the SQLite driver migration (see "Critical Dependency: SQLite CGO Issue" above). Simply disabling CGO for darwin **breaks SQLite functionality**.
|
||||
|
||||
### Recommended Fix
|
||||
### ✅ Actual Fix: Migrate to Pure-Go SQLite
|
||||
|
||||
Update the darwin build configuration to use the correct Zig target triple:
|
||||
By migrating from `gorm.io/driver/sqlite` (CGO) to `github.com/glebarez/sqlite` (pure-Go):
|
||||
|
||||
**Option A: Use `-macos-none` (Recommended)**
|
||||
```yaml
|
||||
- id: darwin
|
||||
dir: backend
|
||||
main: ./cmd/api
|
||||
binary: charon
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
- CC=zig cc -target {{ if eq .Arch "amd64" }}x86_64{{ else }}aarch64{{ end }}-macos-none
|
||||
- CXX=zig c++ -target {{ if eq .Arch "amd64" }}x86_64{{ else }}aarch64{{ end }}-macos-none
|
||||
```
|
||||
1. **Zig is no longer required** for any platform
|
||||
2. **CGO_ENABLED=0** can be used for ALL platforms (linux, darwin, windows)
|
||||
3. **Cross-compilation is trivial** - standard Go cross-compilation works
|
||||
4. **Build times are faster** - no C compiler invocation
|
||||
|
||||
**Option B: Specify macOS version (for specific SDK compatibility)**
|
||||
```yaml
|
||||
- CC=zig cc -target {{ if eq .Arch "amd64" }}x86_64{{ else }}aarch64{{ end }}-macos.11-none
|
||||
- CXX=zig c++ -target {{ if eq .Arch "amd64" }}x86_64{{ else }}aarch64{{ end }}-macos.11-none
|
||||
```
|
||||
|
||||
**Option C: Remove darwin builds entirely (if macOS support is not required)**
|
||||
```yaml
|
||||
# Remove the entire `- id: darwin` build block from .goreleaser.yaml
|
||||
# Update archives section to remove darwin from the `nix` archive builds
|
||||
```
|
||||
|
||||
### Implementation Details
|
||||
|
||||
```diff
|
||||
--- a/.goreleaser.yaml
|
||||
+++ b/.goreleaser.yaml
|
||||
@@ -47,8 +47,8 @@
|
||||
binary: charon
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
- - CC=zig cc -target {{ if eq .Arch "amd64" }}x86_64{{ else }}aarch64{{ end }}-macos-gnu
|
||||
- - CXX=zig c++ -target {{ if eq .Arch "amd64" }}x86_64{{ else }}aarch64{{ end }}-macos-gnu
|
||||
+ - CC=zig cc -target {{ if eq .Arch "amd64" }}x86_64{{ else }}aarch64{{ end }}-macos-none
|
||||
+ - CXX=zig c++ -target {{ if eq .Arch "amd64" }}x86_64{{ else }}aarch64{{ end }}-macos-none
|
||||
goos:
|
||||
- darwin
|
||||
goarch:
|
||||
```
|
||||
|
||||
### Verification
|
||||
|
||||
```bash
|
||||
# Local test (requires Zig installed)
|
||||
cd backend
|
||||
CGO_ENABLED=1 CC="zig cc -target x86_64-macos-none" go build -o charon-darwin ./cmd/api
|
||||
|
||||
# Nightly workflow test
|
||||
gh workflow run nightly-build.yml --ref development -f reason="Test darwin build fix"
|
||||
```
|
||||
This completely eliminates Issue 2 as a side effect of fixing the SQLite dependency issue.
|
||||
|
||||
---
|
||||
|
||||
## Issue 2: Playwright E2E - Admin API Socket Hang Up
|
||||
## Complete Updated `.goreleaser.yaml`
|
||||
|
||||
### Problem Statement
|
||||
|
||||
Playwright test `zzz-admin-whitelist-blocking.spec.ts:126` fails with:
|
||||
|
||||
```text
|
||||
Error: apiRequestContext.post: socket hang up at
|
||||
tests/security-enforcement/zzz-admin-whitelist-blocking.spec.ts:126:21
|
||||
```
|
||||
|
||||
The test POSTs to `http://localhost:2020/emergency/security-reset` but cannot reach the emergency server.
|
||||
|
||||
### Root Cause Analysis
|
||||
|
||||
The `playwright.yml` workflow starts the Charon container but **does not set** the `CHARON_EMERGENCY_BIND` environment variable:
|
||||
|
||||
**Current workflow (`.github/workflows/playwright.yml`):**
|
||||
```yaml
|
||||
docker run -d \
|
||||
--name charon-test \
|
||||
-p 8080:8080 \
|
||||
-p 127.0.0.1:2019:2019 \
|
||||
-p "[::1]:2019:2019" \
|
||||
-p 127.0.0.1:2020:2020 \
|
||||
-p "[::1]:2020:2020" \
|
||||
-e CHARON_ENV="${CHARON_ENV}" \
|
||||
-e CHARON_DEBUG="${CHARON_DEBUG}" \
|
||||
-e CHARON_ENCRYPTION_KEY="${CHARON_ENCRYPTION_KEY}" \
|
||||
-e CHARON_EMERGENCY_TOKEN="${CHARON_EMERGENCY_TOKEN}" \
|
||||
-e CHARON_EMERGENCY_SERVER_ENABLED="${CHARON_EMERGENCY_SERVER_ENABLED}" \
|
||||
"${IMAGE_REF}"
|
||||
```
|
||||
|
||||
**Missing:** `CHARON_EMERGENCY_BIND=0.0.0.0:2020`
|
||||
|
||||
Without this variable, the emergency server may not bind to the correct address, or may bind to a loopback-only address that isn't accessible via Docker port mapping.
|
||||
|
||||
**Comparison with working compose file:**
|
||||
```yaml
|
||||
# .docker/compose/docker-compose.playwright-ci.yml
|
||||
- CHARON_EMERGENCY_BIND=0.0.0.0:2020
|
||||
- CHARON_EMERGENCY_USERNAME=admin
|
||||
- CHARON_EMERGENCY_PASSWORD=changeme
|
||||
```
|
||||
|
||||
### Affected Files
|
||||
|
||||
| File | Change Type |
|
||||
|------|-------------|
|
||||
| `.github/workflows/playwright.yml` | Add missing emergency server env vars |
|
||||
|
||||
### Recommended Fix
|
||||
|
||||
Add the missing emergency server environment variables to the docker run command:
|
||||
|
||||
```diff
|
||||
--- a/.github/workflows/playwright.yml
|
||||
+++ b/.github/workflows/playwright.yml
|
||||
@@ -163,6 +163,10 @@ jobs:
|
||||
-e CHARON_ENCRYPTION_KEY="${CHARON_ENCRYPTION_KEY}" \
|
||||
-e CHARON_EMERGENCY_TOKEN="${CHARON_EMERGENCY_TOKEN}" \
|
||||
-e CHARON_EMERGENCY_SERVER_ENABLED="${CHARON_EMERGENCY_SERVER_ENABLED}" \
|
||||
+ -e CHARON_EMERGENCY_BIND="0.0.0.0:2020" \
|
||||
+ -e CHARON_EMERGENCY_USERNAME="admin" \
|
||||
+ -e CHARON_EMERGENCY_PASSWORD="changeme" \
|
||||
+ -e CHARON_SECURITY_TESTS_ENABLED="true" \
|
||||
"${IMAGE_REF}"
|
||||
```
|
||||
|
||||
### Full Updated Step
|
||||
> **Note:** After migrating to pure-Go SQLite (`github.com/glebarez/sqlite`), Zig cross-compilation is no longer required. All platforms now use `CGO_ENABLED=0` for simpler, faster builds.
|
||||
|
||||
```yaml
|
||||
- name: Start Charon container
|
||||
if: steps.check-artifact.outputs.artifact_exists == 'true'
|
||||
run: |
|
||||
echo "🚀 Starting Charon container..."
|
||||
version: 2
|
||||
|
||||
# Normalize image name (GitHub lowercases repository owner names in GHCR)
|
||||
IMAGE_NAME=$(echo "${{ github.repository_owner }}/charon" | tr '[:upper:]' '[:lower:]')
|
||||
if [[ "${{ steps.pr-info.outputs.is_push }}" == "true" ]]; then
|
||||
IMAGE_REF="ghcr.io/${IMAGE_NAME}:${{ steps.sanitize.outputs.branch }}"
|
||||
else
|
||||
IMAGE_REF="ghcr.io/${IMAGE_NAME}:pr-${{ steps.pr-info.outputs.pr_number }}"
|
||||
fi
|
||||
project_name: charon
|
||||
|
||||
echo "📦 Starting container with image: ${IMAGE_REF}"
|
||||
docker run -d \
|
||||
--name charon-test \
|
||||
-p 8080:8080 \
|
||||
-p 127.0.0.1:2019:2019 \
|
||||
-p "[::1]:2019:2019" \
|
||||
-p 127.0.0.1:2020:2020 \
|
||||
-p "[::1]:2020:2020" \
|
||||
-e CHARON_ENV="${CHARON_ENV}" \
|
||||
-e CHARON_DEBUG="${CHARON_DEBUG}" \
|
||||
-e CHARON_ENCRYPTION_KEY="${CHARON_ENCRYPTION_KEY}" \
|
||||
-e CHARON_EMERGENCY_TOKEN="${CHARON_EMERGENCY_TOKEN}" \
|
||||
-e CHARON_EMERGENCY_SERVER_ENABLED="${CHARON_EMERGENCY_SERVER_ENABLED}" \
|
||||
-e CHARON_EMERGENCY_BIND="0.0.0.0:2020" \
|
||||
-e CHARON_EMERGENCY_USERNAME="admin" \
|
||||
-e CHARON_EMERGENCY_PASSWORD="changeme" \
|
||||
-e CHARON_SECURITY_TESTS_ENABLED="true" \
|
||||
"${IMAGE_REF}"
|
||||
builds:
|
||||
- id: linux
|
||||
dir: backend
|
||||
main: ./cmd/api
|
||||
binary: charon
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- amd64
|
||||
- arm64
|
||||
ldflags:
|
||||
- -s -w
|
||||
- -X github.com/Wikid82/charon/backend/internal/version.Version={{.Version}}
|
||||
- -X github.com/Wikid82/charon/backend/internal/version.GitCommit={{.Commit}}
|
||||
- -X github.com/Wikid82/charon/backend/internal/version.BuildTime={{.Date}}
|
||||
|
||||
echo "✅ Container started"
|
||||
```
|
||||
- id: windows
|
||||
dir: backend
|
||||
main: ./cmd/api
|
||||
binary: charon
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
goos:
|
||||
- windows
|
||||
goarch:
|
||||
- amd64
|
||||
ldflags:
|
||||
- -s -w
|
||||
- -X github.com/Wikid82/charon/backend/internal/version.Version={{.Version}}
|
||||
- -X github.com/Wikid82/charon/backend/internal/version.GitCommit={{.Commit}}
|
||||
- -X github.com/Wikid82/charon/backend/internal/version.BuildTime={{.Date}}
|
||||
|
||||
### Verification
|
||||
- id: darwin
|
||||
dir: backend
|
||||
main: ./cmd/api
|
||||
binary: charon
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
goos:
|
||||
- darwin
|
||||
goarch:
|
||||
- amd64
|
||||
- arm64
|
||||
ldflags:
|
||||
- -s -w
|
||||
- -X github.com/Wikid82/charon/backend/internal/version.Version={{.Version}}
|
||||
- -X github.com/Wikid82/charon/backend/internal/version.GitCommit={{.Commit}}
|
||||
- -X github.com/Wikid82/charon/backend/internal/version.BuildTime={{.Date}}
|
||||
|
||||
```bash
|
||||
# After fix, verify emergency server is listening
|
||||
docker exec charon-test curl -sf http://localhost:2020/health || echo "Failed"
|
||||
archives:
|
||||
- formats: [tar.gz]
|
||||
id: nix
|
||||
ids:
|
||||
- linux
|
||||
- darwin
|
||||
name_template: >-
|
||||
{{ .ProjectName }}_
|
||||
{{- .Version }}_
|
||||
{{- .Os }}_
|
||||
{{- .Arch }}
|
||||
files:
|
||||
- LICENSE
|
||||
- README.md
|
||||
|
||||
# Test emergency reset endpoint
|
||||
curl -X POST http://localhost:2020/emergency/security-reset \
|
||||
-H "Authorization: Basic $(echo -n 'admin:changeme' | base64)" \
|
||||
-H "X-Emergency-Token: $CHARON_EMERGENCY_TOKEN"
|
||||
```
|
||||
- formats: [zip]
|
||||
id: windows
|
||||
ids:
|
||||
- windows
|
||||
name_template: >-
|
||||
{{ .ProjectName }}_
|
||||
{{- .Version }}_
|
||||
{{- .Os }}_
|
||||
{{- .Arch }}
|
||||
files:
|
||||
- LICENSE
|
||||
- README.md
|
||||
|
||||
---
|
||||
nfpms:
|
||||
- id: packages
|
||||
ids:
|
||||
- linux
|
||||
package_name: charon
|
||||
vendor: Charon
|
||||
homepage: https://github.com/Wikid82/charon
|
||||
maintainer: Wikid82
|
||||
description: "Charon - A powerful reverse proxy manager"
|
||||
license: MIT
|
||||
formats:
|
||||
- deb
|
||||
- rpm
|
||||
contents:
|
||||
- src: ./backend/data/
|
||||
dst: /var/lib/charon/data/
|
||||
type: dir
|
||||
- src: ./frontend/dist/
|
||||
dst: /usr/share/charon/frontend/
|
||||
type: dir
|
||||
dependencies:
|
||||
- libc6
|
||||
- ca-certificates
|
||||
|
||||
## Issue 3: Trivy Scan - Invalid Image Reference Format
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
|
||||
### Problem Statement
|
||||
snapshot:
|
||||
version_template: "{{ .Tag }}-next"
|
||||
|
||||
Trivy scan fails with "invalid image reference format" when:
|
||||
1. PR number is missing (manual dispatch without PR number)
|
||||
2. Feature branch names contain `/` characters (e.g., `feature/new-thing`)
|
||||
3. `is_push` and `pr_number` are both empty/false
|
||||
|
||||
Resulting in invalid Docker tags like:
|
||||
- `ghcr.io/owner/charon:pr-` (empty PR number)
|
||||
- `ghcr.io/owner/charon:` (no tag at all)
|
||||
|
||||
### Root Cause Analysis
|
||||
|
||||
**Location:** `.github/workflows/playwright.yml` - "Start Charon container" step
|
||||
|
||||
```bash
|
||||
if [[ "${{ steps.pr-info.outputs.is_push }}" == "true" ]]; then
|
||||
IMAGE_REF="ghcr.io/${IMAGE_NAME}:${{ steps.sanitize.outputs.branch }}"
|
||||
else
|
||||
IMAGE_REF="ghcr.io/${IMAGE_NAME}:pr-${{ steps.pr-info.outputs.pr_number }}"
|
||||
fi
|
||||
```
|
||||
|
||||
**Problem:** When `is_push != "true"` AND `pr_number` is empty, this creates:
|
||||
```
|
||||
IMAGE_REF="ghcr.io/owner/charon:pr-"
|
||||
```
|
||||
|
||||
This is an invalid Docker reference.
|
||||
|
||||
### Affected Files
|
||||
|
||||
| File | Change Type |
|
||||
|------|-------------|
|
||||
| `.github/workflows/playwright.yml` | Add validation for IMAGE_REF |
|
||||
| `.github/workflows/docker-build.yml` | Add validation guards (CVE verification step) |
|
||||
|
||||
### Recommended Fix
|
||||
|
||||
Add defensive validation to fail fast with a clear error message:
|
||||
|
||||
```diff
|
||||
--- a/.github/workflows/playwright.yml
|
||||
+++ b/.github/workflows/playwright.yml
|
||||
# Normalize image name (GitHub lowercases repository owner names in GHCR)
|
||||
IMAGE_NAME=$(echo "${{ github.repository_owner }}/charon" | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
if [[ "${{ steps.pr-info.outputs.is_push }}" == "true" ]]; then
|
||||
IMAGE_REF="ghcr.io/${IMAGE_NAME}:${{ steps.sanitize.outputs.branch }}"
|
||||
- else
|
||||
+ elif [[ -n "${{ steps.pr-info.outputs.pr_number }}" ]]; then
|
||||
IMAGE_REF="ghcr.io/${IMAGE_NAME}:pr-${{ steps.pr-info.outputs.pr_number }}"
|
||||
+ else
|
||||
+ echo "❌ ERROR: Cannot determine image reference"
|
||||
+ echo " - is_push: ${{ steps.pr-info.outputs.is_push }}"
|
||||
+ echo " - pr_number: ${{ steps.pr-info.outputs.pr_number }}"
|
||||
+ echo " - branch: ${{ steps.sanitize.outputs.branch }}"
|
||||
+ echo ""
|
||||
+ echo "This can happen when:"
|
||||
+ echo " 1. workflow_dispatch without pr_number input"
|
||||
+ echo " 2. workflow_run triggered by non-PR, non-push event"
|
||||
+ exit 1
|
||||
fi
|
||||
|
||||
+ # Validate the image reference format
|
||||
+ if [[ ! "${IMAGE_REF}" =~ ^ghcr\.io/[a-z0-9_-]+/[a-z0-9_-]+:[a-zA-Z0-9._-]+$ ]]; then
|
||||
+ echo "❌ ERROR: Invalid image reference format: ${IMAGE_REF}"
|
||||
+ exit 1
|
||||
+ fi
|
||||
+
|
||||
echo "📦 Starting container with image: ${IMAGE_REF}"
|
||||
```
|
||||
|
||||
### Additional Fix for docker-build.yml
|
||||
|
||||
The same issue can occur in `docker-build.yml` at the CVE verification step:
|
||||
|
||||
```yaml
|
||||
# Line ~174 in docker-build.yml
|
||||
if [ "${{ github.event_name }}" = "pull_request" ]; then
|
||||
IMAGE_REF="${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}:pr-${{ github.event.pull_request.number }}"
|
||||
```
|
||||
|
||||
**Fix:**
|
||||
|
||||
```diff
|
||||
--- a/.github/workflows/docker-build.yml
|
||||
+++ b/.github/workflows/docker-build.yml
|
||||
# Determine the image reference based on event type
|
||||
if [ "${{ github.event_name }}" = "pull_request" ]; then
|
||||
- IMAGE_REF="${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}:pr-${{ github.event.pull_request.number }}"
|
||||
+ PR_NUM="${{ github.event.pull_request.number }}"
|
||||
+ if [ -z "${PR_NUM}" ]; then
|
||||
+ echo "❌ ERROR: Pull request number is empty"
|
||||
+ exit 1
|
||||
+ fi
|
||||
+ IMAGE_REF="${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}:pr-${PR_NUM}"
|
||||
echo "Using PR image: $IMAGE_REF"
|
||||
else
|
||||
IMAGE_REF="${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build-and-push.outputs.digest }}"
|
||||
+ if [ -z "${{ steps.build-and-push.outputs.digest }}" ]; then
|
||||
+ echo "❌ ERROR: Build digest is empty"
|
||||
+ exit 1
|
||||
+ fi
|
||||
echo "Using digest: $IMAGE_REF"
|
||||
fi
|
||||
```
|
||||
|
||||
### Verification
|
||||
|
||||
```bash
|
||||
# Test with empty PR number (should fail fast with clear error)
|
||||
gh workflow run playwright.yml --ref development
|
||||
|
||||
# Check IMAGE_REF construction in logs
|
||||
gh run view --log | grep "IMAGE_REF"
|
||||
changelog:
|
||||
sort: asc
|
||||
filters:
|
||||
exclude:
|
||||
- '^docs:'
|
||||
- '^test:'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
### Phase 1: Immediate Fixes (Single PR)
|
||||
### Phase 0: SQLite Driver Migration (PREREQUISITE)
|
||||
|
||||
**Objective:** Fix all three CI failures in a single PR for immediate resolution.
|
||||
**Objective:** Migrate from CGO-dependent SQLite to pure-Go implementation.
|
||||
|
||||
**Files to Modify:**
|
||||
|
||||
| File | Changes |
|
||||
|------|---------|
|
||||
| `.goreleaser.yaml` | Change `-macos-gnu` to `-macos-none` for darwin builds |
|
||||
| `.github/workflows/playwright.yml` | Add missing emergency server env vars; Add IMAGE_REF validation |
|
||||
| `.github/workflows/docker-build.yml` | Add IMAGE_REF validation guards |
|
||||
| File | Change | Reason |
|
||||
|------|--------|--------|
|
||||
| `backend/go.mod` | Replace `gorm.io/driver/sqlite` with `github.com/glebarez/sqlite` | Pure-Go SQLite driver |
|
||||
| `backend/internal/database/database.go` | Update import statement | New driver package |
|
||||
| `backend/internal/testutil/db_test.go` | Update import statement | New driver package |
|
||||
| `backend/cmd/seed/main.go` | Update import statement | New driver package |
|
||||
|
||||
### Phase 2: Verification
|
||||
**Steps:**
|
||||
|
||||
1. Push changes to a feature branch
|
||||
2. Open PR to trigger docker-build.yml
|
||||
3. Verify Trivy scan passes with valid IMAGE_REF
|
||||
4. Verify Playwright workflow if triggered
|
||||
5. Manually trigger nightly-build.yml with `--ref` pointing to feature branch
|
||||
6. Verify darwin build succeeds
|
||||
```bash
|
||||
# 1. Update go.mod - replace CGO driver with pure-Go driver
|
||||
cd backend
|
||||
go get github.com/glebarez/sqlite
|
||||
go mod edit -droprequire gorm.io/driver/sqlite
|
||||
|
||||
### Phase 3: Cleanup (Optional)
|
||||
# 2. Update import statements in Go files
|
||||
# (Manual step - update imports in 3 files listed above)
|
||||
|
||||
1. Add validation logic to a shared script (`scripts/validate-image-ref.sh`)
|
||||
2. Add integration tests for emergency server connectivity
|
||||
3. Document Zig target requirements for future contributors
|
||||
# 3. Tidy dependencies
|
||||
go mod tidy
|
||||
|
||||
# 4. Verify build works without CGO
|
||||
CGO_ENABLED=0 go build ./cmd/api
|
||||
CGO_ENABLED=0 go build ./cmd/seed
|
||||
|
||||
# 5. Run tests to verify SQLite functionality
|
||||
CGO_ENABLED=0 go test ./internal/database/... -v
|
||||
CGO_ENABLED=0 go test ./internal/testutil/... -v
|
||||
```
|
||||
|
||||
**Validation:**
|
||||
- ✅ `CGO_ENABLED=0 go build ./backend/cmd/api` succeeds
|
||||
- ✅ `CGO_ENABLED=0 go build ./backend/cmd/seed` succeeds
|
||||
- ✅ All database tests pass with CGO disabled
|
||||
- ✅ `go.mod` no longer references `gorm.io/driver/sqlite` or `mattn/go-sqlite3`
|
||||
|
||||
---
|
||||
|
||||
### Phase 1: Update GoReleaser Config
|
||||
|
||||
**Files to Modify:**
|
||||
|
||||
| File | Change | Reason |
|
||||
|------|--------|--------|
|
||||
| `.goreleaser.yaml` | Update to version 2 syntax | Required for GoReleaser ~> v2 |
|
||||
| `.goreleaser.yaml` | Remove Zig cross-compilation | No longer needed with pure-Go SQLite |
|
||||
| `.goreleaser.yaml` | Set `CGO_ENABLED=0` for ALL platforms | Consistent pure-Go builds |
|
||||
|
||||
**Simplified Build Configuration (No Zig Required):**
|
||||
|
||||
```yaml
|
||||
builds:
|
||||
- id: linux
|
||||
dir: backend
|
||||
main: ./cmd/api
|
||||
binary: charon
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- amd64
|
||||
- arm64
|
||||
ldflags:
|
||||
- -s -w
|
||||
- -X github.com/Wikid82/charon/backend/internal/version.Version={{.Version}}
|
||||
- -X github.com/Wikid82/charon/backend/internal/version.GitCommit={{.Commit}}
|
||||
- -X github.com/Wikid82/charon/backend/internal/version.BuildTime={{.Date}}
|
||||
|
||||
- id: windows
|
||||
dir: backend
|
||||
main: ./cmd/api
|
||||
binary: charon
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
goos:
|
||||
- windows
|
||||
goarch:
|
||||
- amd64
|
||||
ldflags:
|
||||
- -s -w
|
||||
- -X github.com/Wikid82/charon/backend/internal/version.Version={{.Version}}
|
||||
- -X github.com/Wikid82/charon/backend/internal/version.GitCommit={{.Commit}}
|
||||
- -X github.com/Wikid82/charon/backend/internal/version.BuildTime={{.Date}}
|
||||
|
||||
- id: darwin
|
||||
dir: backend
|
||||
main: ./cmd/api
|
||||
binary: charon
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
goos:
|
||||
- darwin
|
||||
goarch:
|
||||
- amd64
|
||||
- arm64
|
||||
ldflags:
|
||||
- -s -w
|
||||
- -X github.com/Wikid82/charon/backend/internal/version.Version={{.Version}}
|
||||
- -X github.com/Wikid82/charon/backend/internal/version.GitCommit={{.Commit}}
|
||||
- -X github.com/Wikid82/charon/backend/internal/version.BuildTime={{.Date}}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 2: Verification Steps
|
||||
|
||||
```bash
|
||||
# 1. Verify SQLite migration (Phase 0 complete)
|
||||
cd backend
|
||||
CGO_ENABLED=0 go build ./cmd/api
|
||||
CGO_ENABLED=0 go test ./... -count=1
|
||||
|
||||
# 2. Validate the GoReleaser config locally
|
||||
goreleaser check
|
||||
|
||||
# 3. Test snapshot build locally (no Zig required!)
|
||||
goreleaser release --snapshot --skip=publish --clean
|
||||
|
||||
# 4. Trigger nightly workflow manually
|
||||
gh workflow run nightly-build.yml -f reason="Test GoReleaser v2 migration with pure-Go SQLite"
|
||||
|
||||
# 5. Monitor workflow execution
|
||||
gh run watch
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 3: Rollback Plan
|
||||
|
||||
If the fix fails:
|
||||
|
||||
**SQLite Rollback:**
|
||||
1. Revert `go.mod` to use `gorm.io/driver/sqlite`
|
||||
2. Revert import statement changes
|
||||
3. Re-enable CGO in GoReleaser config
|
||||
|
||||
**GoReleaser Rollback:**
|
||||
1. Revert `.goreleaser.yaml` changes
|
||||
2. Pin GoReleaser to v1.x in workflows:
|
||||
```yaml
|
||||
version: '1.26.2' # Last v1 release
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Requirements (EARS Notation)
|
||||
|
||||
1. WHEN GoReleaser builds darwin targets, THE SYSTEM SHALL use `-macos-none` Zig target (not `-macos-gnu`).
|
||||
2. WHEN the Playwright workflow starts the Charon container, THE SYSTEM SHALL set `CHARON_EMERGENCY_BIND=0.0.0.0:2020` to ensure the emergency server is reachable.
|
||||
3. WHEN constructing Docker image references, THE SYSTEM SHALL validate that the tag portion is non-empty before attempting to use it.
|
||||
4. IF the PR number is empty in a PR-triggered workflow, THEN THE SYSTEM SHALL fail fast with a clear error message explaining the issue.
|
||||
5. WHEN a feature branch contains `/` characters, THE SYSTEM SHALL sanitize the branch name by replacing `/` with `-` before using it as a Docker tag.
|
||||
1. WHEN building for any platform, THE SYSTEM SHALL use `CGO_ENABLED=0` (pure-Go builds).
|
||||
2. WHEN importing the SQLite driver, THE SYSTEM SHALL use `github.com/glebarez/sqlite` (pure-Go driver).
|
||||
3. WHEN GoReleaser executes, THE SYSTEM SHALL use version 2 configuration syntax.
|
||||
4. WHEN archiving builds, THE SYSTEM SHALL use `formats` (array) instead of deprecated `format`.
|
||||
5. WHEN referencing build IDs in archives/nfpms, THE SYSTEM SHALL use `ids` instead of deprecated `builds`.
|
||||
6. WHEN generating snapshot versions, THE SYSTEM SHALL use `version_template` instead of deprecated `name_template`.
|
||||
|
||||
---
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
1. [ ] Nightly build completes successfully with darwin binaries
|
||||
2. [ ] Playwright E2E tests pass with emergency server accessible on port 2020
|
||||
3. [ ] Trivy scan passes with valid image reference for all trigger types
|
||||
4. [ ] Workflow failures produce clear, actionable error messages
|
||||
5. [ ] No regression in existing CI functionality
|
||||
**Phase 0 (SQLite Migration):**
|
||||
- [ ] `backend/go.mod` uses `github.com/glebarez/sqlite` instead of `gorm.io/driver/sqlite`
|
||||
- [ ] No references to `mattn/go-sqlite3` in `go.mod` or `go.sum`
|
||||
- [ ] `CGO_ENABLED=0 go build ./backend/cmd/api` succeeds
|
||||
- [ ] `CGO_ENABLED=0 go build ./backend/cmd/seed` succeeds
|
||||
- [ ] All database tests pass with `CGO_ENABLED=0`
|
||||
|
||||
**Phase 1 (GoReleaser v2):**
|
||||
- [ ] `goreleaser check` passes without errors or deprecation warnings
|
||||
- [ ] Nightly build workflow completes successfully
|
||||
- [ ] Linux amd64/arm64 binaries are produced
|
||||
- [ ] Windows amd64 binary is produced
|
||||
- [ ] Darwin amd64/arm64 binaries are produced
|
||||
- [ ] .deb and .rpm packages are produced for Linux
|
||||
- [ ] No deprecation warnings in CI logs
|
||||
- [ ] No Zig-related errors in build logs
|
||||
|
||||
---
|
||||
|
||||
## Risks & Mitigations
|
||||
## Risk Assessment
|
||||
|
||||
| Risk | Likelihood | Impact | Mitigation |
|
||||
|------|------------|--------|------------|
|
||||
| Zig target change breaks darwin binaries | Low | High | Test with local Zig build first |
|
||||
| Emergency server env vars conflict with existing config | Low | Medium | Verify against docker-compose.playwright-ci.yml |
|
||||
| IMAGE_REF validation too strict | Medium | Low | Use permissive regex, log values before validation |
|
||||
|
||||
---
|
||||
|
||||
## Handoff Contract
|
||||
|
||||
```json
|
||||
{
|
||||
"plan": "CI Workflow Failures - Fix Plan",
|
||||
"status": "Ready for Implementation",
|
||||
"owner": "DevOps",
|
||||
"handoffTargets": ["Backend_Dev", "DevOps"],
|
||||
"files": [
|
||||
".goreleaser.yaml",
|
||||
".github/workflows/playwright.yml",
|
||||
".github/workflows/docker-build.yml"
|
||||
],
|
||||
"estimatedEffort": "2-3 hours",
|
||||
"priority": "HIGH",
|
||||
"blockedWorkflows": [
|
||||
"nightly-build.yml",
|
||||
"playwright.yml",
|
||||
"docker-build.yml (Trivy scan step)"
|
||||
]
|
||||
}
|
||||
```
|
||||
| Pure-Go SQLite has different behavior | Low | Medium | Run full test suite; compare query results |
|
||||
| Pure-Go SQLite performance differs | Low | Low | Run benchmarks; acceptable for typical workloads |
|
||||
| Other undocumented v2 breaking changes | Low | Medium | Monitor GoReleaser changelog; test locally first |
|
||||
| Import statement missed in some file | Low | High | Use grep to find all `gorm.io/driver/sqlite` imports |
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- [docs/actions/nightly-build-failure.md](../actions/nightly-build-failure.md)
|
||||
- [docs/actions/playwright-e2e-failures.md](../actions/playwright-e2e-failures.md)
|
||||
- [Zig Cross-Compilation Targets](https://ziglang.org/documentation/master/#Targets)
|
||||
- [GoReleaser CGO Cross-Compilation](https://goreleaser.com/customization/build/#cross-compiling)
|
||||
- [glebarez/sqlite - Pure Go SQLite driver for GORM](https://github.com/glebarez/sqlite)
|
||||
- [modernc.org/sqlite - Pure Go SQLite implementation](https://pkg.go.dev/modernc.org/sqlite)
|
||||
- [GoReleaser v2 Migration Guide](https://goreleaser.com/deprecations/)
|
||||
- [GoReleaser Builds Documentation](https://goreleaser.com/customization/build/)
|
||||
|
||||
---
|
||||
|
||||
# ARCHIVED: Other CI Issues (Separate from GoReleaser)
|
||||
|
||||
The following issues are documented separately and may be addressed in future PRs:
|
||||
|
||||
1. **Playwright E2E - Emergency Server Connectivity** - See [docs/plans/e2e_remediation_spec.md](e2e_remediation_spec.md)
|
||||
2. **Trivy Scan - Image Reference Validation** - See [docs/plans/docker_compose_ci_fix.md](docker_compose_ci_fix.md)
|
||||
|
||||
11
go.work.sum
11
go.work.sum
@@ -33,6 +33,7 @@ github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2E
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
@@ -105,6 +106,7 @@ golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c
|
||||
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
@@ -114,5 +116,14 @@ google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXn
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||
modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0=
|
||||
modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY=
|
||||
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
|
||||
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
|
||||
modernc.org/tcl v1.15.2/go.mod h1:3+k/ZaEbKrC8ePv8zJWPtBSW0V7Gg9g8rkmhI1Kfs3c=
|
||||
modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
modernc.org/z v1.7.3/go.mod h1:Ipv4tsdxZRbQyLq9Q1M6gdbkxYzdlrciF2Hi/lS7nWE=
|
||||
rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
|
||||
Reference in New Issue
Block a user