From 86e04bb5a3b9c4ddaf207fb25b4390f6015fe7e0 Mon Sep 17 00:00:00 2001 From: Snider Date: Sun, 15 Mar 2026 15:28:58 +0000 Subject: [PATCH 01/12] chore: sync go.mod dependencies Co-Authored-By: Virgil --- go.mod | 97 ++++++++++++------------ go.sum | 227 +++++++++++++++++++++++++++++++-------------------------- 2 files changed, 172 insertions(+), 152 deletions(-) diff --git a/go.mod b/go.mod index f18b860..96bb7e4 100644 --- a/go.mod +++ b/go.mod @@ -3,38 +3,38 @@ module forge.lthn.ai/core/go-build go 1.26.0 require ( - forge.lthn.ai/core/api v0.1.0 - forge.lthn.ai/core/cli v0.3.0 - forge.lthn.ai/core/go-i18n v0.1.0 - forge.lthn.ai/core/go-io v0.1.0 - forge.lthn.ai/core/go-log v0.0.1 - forge.lthn.ai/core/go-ws v0.1.0 + forge.lthn.ai/core/api v0.1.3 + forge.lthn.ai/core/cli v0.3.1 + forge.lthn.ai/core/go-i18n v0.1.4 + forge.lthn.ai/core/go-io v0.1.2 + forge.lthn.ai/core/go-log v0.0.4 + forge.lthn.ai/core/go-ws v0.2.1 github.com/Snider/Borg v0.2.0 - github.com/getkin/kin-openapi v0.133.0 + github.com/getkin/kin-openapi v0.134.0 github.com/gin-gonic/gin v1.12.0 github.com/leaanthony/debme v1.2.1 github.com/leaanthony/gosod v1.0.4 - github.com/oasdiff/oasdiff v1.11.10 + github.com/oasdiff/oasdiff v1.12.1 github.com/stretchr/testify v1.11.1 - golang.org/x/net v0.51.0 - golang.org/x/text v0.34.0 + golang.org/x/net v0.52.0 + golang.org/x/text v0.35.0 gopkg.in/yaml.v3 v3.0.1 ) require ( cloud.google.com/go v0.123.0 // indirect - forge.lthn.ai/core/go v0.3.0 // indirect - forge.lthn.ai/core/go-crypt v0.1.0 // indirect - forge.lthn.ai/core/go-inference v0.0.2 // indirect - forge.lthn.ai/core/go-process v0.1.2 // indirect - github.com/99designs/gqlgen v0.17.87 // indirect + forge.lthn.ai/core/go v0.3.1 // indirect + forge.lthn.ai/core/go-crypt v0.1.7 // indirect + forge.lthn.ai/core/go-inference v0.1.4 // indirect + forge.lthn.ai/core/go-process v0.2.3 // indirect + github.com/99designs/gqlgen v0.17.88 // indirect github.com/KyleBanks/depth v1.2.1 // indirect - github.com/ProtonMail/go-crypto v1.3.0 // indirect + github.com/ProtonMail/go-crypto v1.4.0 // indirect github.com/TwiN/go-color v1.4.1 // indirect github.com/agnivade/levenshtein v1.2.1 // indirect github.com/andybalholm/brotli v1.2.0 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect - github.com/bmatcuk/doublestar/v4 v4.9.1 // indirect + github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect github.com/bytedance/gopkg v0.1.3 // indirect github.com/bytedance/sonic v1.15.0 // indirect github.com/bytedance/sonic/loader v0.5.0 // indirect @@ -42,7 +42,7 @@ require ( github.com/casbin/govaluate v1.10.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/charmbracelet/bubbletea v1.3.10 // indirect - github.com/charmbracelet/colorprofile v0.4.2 // indirect + github.com/charmbracelet/colorprofile v0.4.3 // indirect github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834 // indirect github.com/charmbracelet/x/ansi v0.11.6 // indirect github.com/charmbracelet/x/cellbuf v0.0.15 // indirect @@ -53,6 +53,7 @@ require ( github.com/cloudwego/base64x v0.1.6 // indirect github.com/coreos/go-oidc/v3 v3.17.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/gabriel-vasile/mimetype v1.4.13 // indirect github.com/gin-contrib/authz v1.0.6 // indirect @@ -71,21 +72,21 @@ require ( github.com/go-jose/go-jose/v4 v4.1.3 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-openapi/jsonpointer v0.22.4 // indirect - github.com/go-openapi/jsonreference v0.21.2 // indirect - github.com/go-openapi/spec v0.22.0 // indirect - github.com/go-openapi/swag/conv v0.25.1 // indirect - github.com/go-openapi/swag/jsonname v0.25.4 // indirect - github.com/go-openapi/swag/jsonutils v0.25.1 // indirect - github.com/go-openapi/swag/loading v0.25.1 // indirect - github.com/go-openapi/swag/stringutils v0.25.1 // indirect - github.com/go-openapi/swag/typeutils v0.25.1 // indirect - github.com/go-openapi/swag/yamlutils v0.25.1 // indirect + github.com/go-openapi/jsonpointer v0.22.5 // indirect + github.com/go-openapi/jsonreference v0.21.5 // indirect + github.com/go-openapi/spec v0.22.4 // indirect + github.com/go-openapi/swag/conv v0.25.5 // indirect + github.com/go-openapi/swag/jsonname v0.25.5 // indirect + github.com/go-openapi/swag/jsonutils v0.25.5 // indirect + github.com/go-openapi/swag/loading v0.25.5 // indirect + github.com/go-openapi/swag/stringutils v0.25.5 // indirect + github.com/go-openapi/swag/typeutils v0.25.5 // indirect + github.com/go-openapi/swag/yamlutils v0.25.5 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.30.1 // indirect github.com/go-viper/mapstructure/v2 v2.5.0 // indirect - github.com/goccy/go-json v0.10.5 // indirect + github.com/goccy/go-json v0.10.6 // indirect github.com/goccy/go-yaml v1.19.2 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/context v1.1.2 // indirect @@ -99,25 +100,26 @@ require ( github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/lucasb-eyer/go-colorful v1.3.0 // indirect - github.com/mailru/easyjson v0.9.1 // indirect + github.com/mailru/easyjson v0.9.2 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-localereader v0.0.1 // indirect - github.com/mattn/go-runewidth v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.21 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/termenv v0.16.0 // indirect - github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect - github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect + github.com/oasdiff/yaml v0.0.0-20260313112342-a3ea61cb4d4c // indirect + github.com/oasdiff/yaml3 v0.0.0-20260224194419-61cd415a242b // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/quic-go/qpack v0.6.0 // indirect github.com/quic-go/quic-go v0.59.0 // indirect + github.com/redis/go-redis/v9 v9.18.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect - github.com/sosodev/duration v1.3.1 // indirect + github.com/sosodev/duration v1.4.0 // indirect github.com/spf13/cobra v1.10.2 // indirect github.com/spf13/pflag v1.0.10 // indirect github.com/swaggo/files v1.0.1 // indirect @@ -137,19 +139,20 @@ require ( github.com/yargevad/filepathx v1.0.0 // indirect go.mongodb.org/mongo-driver/v2 v2.5.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.65.0 // indirect - go.opentelemetry.io/otel v1.40.0 // indirect - go.opentelemetry.io/otel/metric v1.40.0 // indirect - go.opentelemetry.io/otel/sdk v1.40.0 // indirect - go.opentelemetry.io/otel/trace v1.40.0 // indirect + go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.67.0 // indirect + go.opentelemetry.io/otel v1.42.0 // indirect + go.opentelemetry.io/otel/metric v1.42.0 // indirect + go.opentelemetry.io/otel/sdk v1.42.0 // indirect + go.opentelemetry.io/otel/trace v1.42.0 // indirect + go.uber.org/atomic v1.11.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/arch v0.23.0 // indirect - golang.org/x/crypto v0.48.0 // indirect - golang.org/x/mod v0.33.0 // indirect - golang.org/x/oauth2 v0.35.0 // indirect - golang.org/x/sync v0.19.0 // indirect - golang.org/x/sys v0.41.0 // indirect - golang.org/x/term v0.40.0 // indirect - golang.org/x/tools v0.42.0 // indirect + golang.org/x/arch v0.25.0 // indirect + golang.org/x/crypto v0.49.0 // indirect + golang.org/x/mod v0.34.0 // indirect + golang.org/x/oauth2 v0.36.0 // indirect + golang.org/x/sync v0.20.0 // indirect + golang.org/x/sys v0.42.0 // indirect + golang.org/x/term v0.41.0 // indirect + golang.org/x/tools v0.43.0 // indirect google.golang.org/protobuf v1.36.11 // indirect ) diff --git a/go.sum b/go.sum index 6fc7ded..710a2b5 100644 --- a/go.sum +++ b/go.sum @@ -1,31 +1,31 @@ cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE= cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU= -forge.lthn.ai/core/api v0.1.0 h1:ZKnQx+L9vxLQSEjwpsD1eNcIQrE4YKV1c2AlMtseM6o= -forge.lthn.ai/core/api v0.1.0/go.mod h1:c86Lk9AmaS0xbiRCEG/+du8s9KyYNHnp8RED35gR/Fo= -forge.lthn.ai/core/cli v0.3.0 h1:FpP1Wp4GwhOd+ZHWrjKZUCEnGyWoXOVDTwhytFb6hrA= -forge.lthn.ai/core/cli v0.3.0/go.mod h1:pocya1fKLbIKnNJ9rmfUDqBsH5bg02P426JvDBomcJo= -forge.lthn.ai/core/go v0.3.0 h1:mOG97ApMprwx9Ked62FdWVwXTGSF6JO6m0DrVpoH2Q4= -forge.lthn.ai/core/go v0.3.0/go.mod h1:gE6c8h+PJ2287qNhVUJ5SOe1kopEwHEquvinstpuyJc= -forge.lthn.ai/core/go-crypt v0.1.0 h1:92gwdQi7iAwktpvZhL/8Cu+QS6xKCtGP4FJfyInPGnw= -forge.lthn.ai/core/go-crypt v0.1.0/go.mod h1:zVAgx6ZiGtC+dbX4R/VKvEPqsEqjyuLl4gQZH9SXBUw= -forge.lthn.ai/core/go-i18n v0.1.0 h1:F7JVSoVkZtzx9JfhpntM9z3iQm1vnuMUi/Zklhz8PCI= -forge.lthn.ai/core/go-i18n v0.1.0/go.mod h1:Q4xsrxuNCl/6NfMv1daria7t1RSiyy8ml+6jiPtUcBs= -forge.lthn.ai/core/go-inference v0.0.2 h1:aHjBkYyLKxLr9tbO4AvzzV/lsZueGq/jeo33SLh113k= -forge.lthn.ai/core/go-inference v0.0.2/go.mod h1:jfWz+IJX55wAH98+ic6FEqqGB6/P31CHlg7VY7pxREw= -forge.lthn.ai/core/go-io v0.1.0 h1:aYNvmbU2VVsjXnut0WQ4DfVxcFdheziahJB32mfeJ7g= -forge.lthn.ai/core/go-io v0.1.0/go.mod h1:ZlU9OQpsvNFNmTJoaHbFIkisZyc0eCq0p8znVWQLRf0= -forge.lthn.ai/core/go-log v0.0.1 h1:x/E6EfF9vixzqiLHQOl2KT25HyBcMc9qiBkomqVlpPg= -forge.lthn.ai/core/go-log v0.0.1/go.mod h1:r14MXKOD3LF/sI8XUJQhRk/SZHBE7jAFVuCfgkXoZPw= -forge.lthn.ai/core/go-process v0.1.2 h1:0fdLJq/DPssilN9E5yude/xHNfZRKHghIjo++b5aXgc= -forge.lthn.ai/core/go-process v0.1.2/go.mod h1:9oxVALrZaZCqFe8YDdheIS5bRUV1SBz4tVW/MflAtxM= -forge.lthn.ai/core/go-ws v0.1.0 h1:P3lH2BM7UyIJAX5R2iVszEZ3M5B6oXGdEWGtuAW054M= -forge.lthn.ai/core/go-ws v0.1.0/go.mod h1:wBQLXDUod6FqESh1CM4OnAjyP3cmWg8Vd5M43RIdTwA= -github.com/99designs/gqlgen v0.17.87 h1:pSnCIMhBQezAE8bc1GNmfdLXFmnWtWl1GRDFEE/nHP8= -github.com/99designs/gqlgen v0.17.87/go.mod h1:fK05f1RqSNfQpd4CfW5qk/810Tqi4/56Wf6Nem0khAg= +forge.lthn.ai/core/api v0.1.3 h1:iYmNP6zK5SiNRunYEsXPvjppTh3bQADkMyoCC8lEs48= +forge.lthn.ai/core/api v0.1.3/go.mod h1:dBOZc6DS0HdnTfCJZ8FkZxWJio2cIf0d1UrCAlDanrA= +forge.lthn.ai/core/cli v0.3.1 h1:ZpHhaDrdbaV98JDxj/f0E5nytYk9tTMRu3qohGyK4M0= +forge.lthn.ai/core/cli v0.3.1/go.mod h1:28cOl9eK0H033Otkjrv9f/QCmtHcJl+IIx4om8JskOg= +forge.lthn.ai/core/go v0.3.1 h1:5FMTsUhLcxSr07F9q3uG0Goy4zq4eLivoqi8shSY4UM= +forge.lthn.ai/core/go v0.3.1/go.mod h1:gE6c8h+PJ2287qNhVUJ5SOe1kopEwHEquvinstpuyJc= +forge.lthn.ai/core/go-crypt v0.1.7 h1:tyDFnXjEksHFQpkFwCpEn+x7zvwh4LnaU+/fP3WmqZc= +forge.lthn.ai/core/go-crypt v0.1.7/go.mod h1:mQdr6K8lWOcyHmSEW24vZPTThQF8fteVgZi8CO+Ko3Y= +forge.lthn.ai/core/go-i18n v0.1.4 h1:zOHUUJDgRo88/3tj++kN+VELg/buyZ4T2OSdG3HBbLQ= +forge.lthn.ai/core/go-i18n v0.1.4/go.mod h1:aDyAfz7MMgWYgLkZCptfFmZ7jJg3ocwjEJ1WkJSvv4U= +forge.lthn.ai/core/go-inference v0.1.4 h1:fuAgWbqsEDajHniqAKyvHYbRcBrkGEiGSqR2pfTMRY0= +forge.lthn.ai/core/go-inference v0.1.4/go.mod h1:jfWz+IJX55wAH98+ic6FEqqGB6/P31CHlg7VY7pxREw= +forge.lthn.ai/core/go-io v0.1.2 h1:q8hj2jtOFqAgHlBr5wsUAOXtaFkxy9gqGrQT/il0WYA= +forge.lthn.ai/core/go-io v0.1.2/go.mod h1:PbNKW1Q25ywSOoQXeGdQHbV5aiIrTXvHIQ5uhplA//g= +forge.lthn.ai/core/go-log v0.0.4 h1:KTuCEPgFmuM8KJfnyQ8vPOU1Jg654W74h8IJvfQMfv0= +forge.lthn.ai/core/go-log v0.0.4/go.mod h1:r14MXKOD3LF/sI8XUJQhRk/SZHBE7jAFVuCfgkXoZPw= +forge.lthn.ai/core/go-process v0.2.3 h1:/ERqRYHgCNZjNT9NMinAAJJGJWSsHuCTiHFNEm6nTPY= +forge.lthn.ai/core/go-process v0.2.3/go.mod h1:gVTbxL16ccUIexlFcyDtCy7LfYvD8Rtyzfo8bnXAXrU= +forge.lthn.ai/core/go-ws v0.2.1 h1:QA+hZrgD/AdmyEKARwM4jL9Kr4p3wLEQi6qBElpwFsw= +forge.lthn.ai/core/go-ws v0.2.1/go.mod h1:LC+VXNvPcGYWmIyS/zMDqmYE2RjNAVyTz1UI2xDLVgU= +github.com/99designs/gqlgen v0.17.88 h1:neMQDgehMwT1vYIOx/w5ZYPUU/iMNAJzRO44I5Intoc= +github.com/99designs/gqlgen v0.17.88/go.mod h1:qeqYFEgOeSKqWedOjogPizimp2iu4E23bdPvl4jTYic= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= -github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw= -github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE= +github.com/ProtonMail/go-crypto v1.4.0 h1:Zq/pbM3F5DFgJiMouxEdSVY44MVoQNEKp5d5QxIQceQ= +github.com/ProtonMail/go-crypto v1.4.0/go.mod h1:e1OaTyu5SYVrO9gKOEhTc+5UcXtTUa+P3uLudwcgPqo= github.com/PuerkitoBio/goquery v1.11.0 h1:jZ7pwMQXIITcUXNH83LLk+txlaEy6NVOfTuP43xxfqw= github.com/PuerkitoBio/goquery v1.11.0/go.mod h1:wQHgxUOU3JGuj3oD/QFfxUdlzW6xPHfqyHre6VMY4DQ= github.com/Snider/Borg v0.2.0 h1:iCyDhY4WTXi39+FexRwXbn2YpZ2U9FUXVXDZk9xRCXQ= @@ -45,8 +45,12 @@ github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdK github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= -github.com/bmatcuk/doublestar/v4 v4.9.1 h1:X8jg9rRZmJd4yRy7ZeNDRnM+T3ZfHv15JiBJ/avrEXE= -github.com/bmatcuk/doublestar/v4 v4.9.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= +github.com/bmatcuk/doublestar/v4 v4.10.0 h1:zU9WiOla1YA122oLM6i4EXvGW62DvKZVxIe6TYWexEs= +github.com/bmatcuk/doublestar/v4 v4.10.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M= github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM= github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE= @@ -62,8 +66,8 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw= github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4= -github.com/charmbracelet/colorprofile v0.4.2 h1:BdSNuMjRbotnxHSfxy+PCSa4xAmz7szw70ktAtWRYrY= -github.com/charmbracelet/colorprofile v0.4.2/go.mod h1:0rTi81QpwDElInthtrQ6Ni7cG0sDtwAd4C4le060fT8= +github.com/charmbracelet/colorprofile v0.4.3 h1:QPa1IWkYI+AOB+fE+mg/5/4HRMZcaXex9t5KX76i20Q= +github.com/charmbracelet/colorprofile v0.4.3/go.mod h1:/zT4BhpD5aGFpqQQqw7a+VtHCzu+zrQtt1zhMt9mR4Q= github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834 h1:ZR7e0ro+SZZiIZD7msJyA+NjkCNNavuiPBLgerbOziE= github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834/go.mod h1:aKC/t2arECF6rNOnaKaVU6y4t4ZeHQzqfxedE/VkVhA= github.com/charmbracelet/x/ansi v0.11.6 h1:GhV21SiDz/45W9AnV2R61xZMRri5NlLnl6CVF7ihZW8= @@ -87,14 +91,16 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54 h1:SG7nF6SRlWhcT7cNTs5R6Hk4V2lcmLz2NsG2VnInyNo= github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM= github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= -github.com/getkin/kin-openapi v0.133.0 h1:pJdmNohVIJ97r4AUFtEXRXwESr8b0bD721u/Tz6k8PQ= -github.com/getkin/kin-openapi v0.133.0/go.mod h1:boAciF6cXk5FhPqe/NQeBTeenbjqU4LhWBf09ILVvWE= +github.com/getkin/kin-openapi v0.134.0 h1:/L5+1+kfe6dXh8Ot/wqiTgUkjOIEJiC0bbYVziHB8rU= +github.com/getkin/kin-openapi v0.134.0/go.mod h1:wK6ZLG/VgoETO9pcLJ/VmAtIcl/DNlMayNTb716EUxE= github.com/gin-contrib/authz v1.0.6 h1:qAO4sSSzOPCwYRZI6YtubC+h2tZVwhwSJeyEZn2W+5k= github.com/gin-contrib/authz v1.0.6/go.mod h1:A2B5Im1M/HIoHPjLc31j3RlENSE6j8euJY9NFdzZeYo= github.com/gin-contrib/cors v1.7.6 h1:3gQ8GMzs1Ylpf70y8bMw4fVpycXIeX1ZemuSQIsnQQY= @@ -130,31 +136,33 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/jsonpointer v0.22.4 h1:dZtK82WlNpVLDW2jlA1YCiVJFVqkED1MegOUy9kR5T4= -github.com/go-openapi/jsonpointer v0.22.4/go.mod h1:elX9+UgznpFhgBuaMQ7iu4lvvX1nvNsesQ3oxmYTw80= -github.com/go-openapi/jsonreference v0.21.2 h1:Wxjda4M/BBQllegefXrY/9aq1fxBA8sI5M/lFU6tSWU= -github.com/go-openapi/jsonreference v0.21.2/go.mod h1:pp3PEjIsJ9CZDGCNOyXIQxsNuroxm8FAJ/+quA0yKzQ= -github.com/go-openapi/spec v0.22.0 h1:xT/EsX4frL3U09QviRIZXvkh80yibxQmtoEvyqug0Tw= -github.com/go-openapi/spec v0.22.0/go.mod h1:K0FhKxkez8YNS94XzF8YKEMULbFrRw4m15i2YUht4L0= +github.com/go-openapi/jsonpointer v0.22.5 h1:8on/0Yp4uTb9f4XvTrM2+1CPrV05QPZXu+rvu2o9jcA= +github.com/go-openapi/jsonpointer v0.22.5/go.mod h1:gyUR3sCvGSWchA2sUBJGluYMbe1zazrYWIkWPjjMUY0= +github.com/go-openapi/jsonreference v0.21.5 h1:6uCGVXU/aNF13AQNggxfysJ+5ZcU4nEAe+pJyVWRdiE= +github.com/go-openapi/jsonreference v0.21.5/go.mod h1:u25Bw85sX4E2jzFodh1FOKMTZLcfifd1Q+iKKOUxExw= +github.com/go-openapi/spec v0.22.4 h1:4pxGjipMKu0FzFiu/DPwN3CTBRlVM2yLf/YTWorYfDQ= +github.com/go-openapi/spec v0.22.4/go.mod h1:WQ6Ai0VPWMZgMT4XySjlRIE6GP1bGQOtEThn3gcWLtQ= github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= -github.com/go-openapi/swag/conv v0.25.1 h1:+9o8YUg6QuqqBM5X6rYL/p1dpWeZRhoIt9x7CCP+he0= -github.com/go-openapi/swag/conv v0.25.1/go.mod h1:Z1mFEGPfyIKPu0806khI3zF+/EUXde+fdeksUl2NiDs= -github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI= -github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag= -github.com/go-openapi/swag/jsonutils v0.25.1 h1:AihLHaD0brrkJoMqEZOBNzTLnk81Kg9cWr+SPtxtgl8= -github.com/go-openapi/swag/jsonutils v0.25.1/go.mod h1:JpEkAjxQXpiaHmRO04N1zE4qbUEg3b7Udll7AMGTNOo= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.1 h1:DSQGcdB6G0N9c/KhtpYc71PzzGEIc/fZ1no35x4/XBY= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.1/go.mod h1:kjmweouyPwRUEYMSrbAidoLMGeJ5p6zdHi9BgZiqmsg= -github.com/go-openapi/swag/loading v0.25.1 h1:6OruqzjWoJyanZOim58iG2vj934TysYVptyaoXS24kw= -github.com/go-openapi/swag/loading v0.25.1/go.mod h1:xoIe2EG32NOYYbqxvXgPzne989bWvSNoWoyQVWEZicc= -github.com/go-openapi/swag/stringutils v0.25.1 h1:Xasqgjvk30eUe8VKdmyzKtjkVjeiXx1Iz0zDfMNpPbw= -github.com/go-openapi/swag/stringutils v0.25.1/go.mod h1:JLdSAq5169HaiDUbTvArA2yQxmgn4D6h4A+4HqVvAYg= -github.com/go-openapi/swag/typeutils v0.25.1 h1:rD/9HsEQieewNt6/k+JBwkxuAHktFtH3I3ysiFZqukA= -github.com/go-openapi/swag/typeutils v0.25.1/go.mod h1:9McMC/oCdS4BKwk2shEB7x17P6HmMmA6dQRtAkSnNb8= -github.com/go-openapi/swag/yamlutils v0.25.1 h1:mry5ez8joJwzvMbaTGLhw8pXUnhDK91oSJLDPF1bmGk= -github.com/go-openapi/swag/yamlutils v0.25.1/go.mod h1:cm9ywbzncy3y6uPm/97ysW8+wZ09qsks+9RS8fLWKqg= -github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls= -github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= +github.com/go-openapi/swag/conv v0.25.5 h1:wAXBYEXJjoKwE5+vc9YHhpQOFj2JYBMF2DUi+tGu97g= +github.com/go-openapi/swag/conv v0.25.5/go.mod h1:CuJ1eWvh1c4ORKx7unQnFGyvBbNlRKbnRyAvDvzWA4k= +github.com/go-openapi/swag/jsonname v0.25.5 h1:8p150i44rv/Drip4vWI3kGi9+4W9TdI3US3uUYSFhSo= +github.com/go-openapi/swag/jsonname v0.25.5/go.mod h1:jNqqikyiAK56uS7n8sLkdaNY/uq6+D2m2LANat09pKU= +github.com/go-openapi/swag/jsonutils v0.25.5 h1:XUZF8awQr75MXeC+/iaw5usY/iM7nXPDwdG3Jbl9vYo= +github.com/go-openapi/swag/jsonutils v0.25.5/go.mod h1:48FXUaz8YsDAA9s5AnaUvAmry1UcLcNVWUjY42XkrN4= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5 h1:SX6sE4FrGb4sEnnxbFL/25yZBb5Hcg1inLeErd86Y1U= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5/go.mod h1:/2KvOTrKWjVA5Xli3DZWdMCZDzz3uV/T7bXwrKWPquo= +github.com/go-openapi/swag/loading v0.25.5 h1:odQ/umlIZ1ZVRteI6ckSrvP6e2w9UTF5qgNdemJHjuU= +github.com/go-openapi/swag/loading v0.25.5/go.mod h1:I8A8RaaQ4DApxhPSWLNYWh9NvmX2YKMoB9nwvv6oW6g= +github.com/go-openapi/swag/stringutils v0.25.5 h1:NVkoDOA8YBgtAR/zvCx5rhJKtZF3IzXcDdwOsYzrB6M= +github.com/go-openapi/swag/stringutils v0.25.5/go.mod h1:PKK8EZdu4QJq8iezt17HM8RXnLAzY7gW0O1KKarrZII= +github.com/go-openapi/swag/typeutils v0.25.5 h1:EFJ+PCga2HfHGdo8s8VJXEVbeXRCYwzzr9u4rJk7L7E= +github.com/go-openapi/swag/typeutils v0.25.5/go.mod h1:itmFmScAYE1bSD8C4rS0W+0InZUBrB2xSPbWt6DLGuc= +github.com/go-openapi/swag/yamlutils v0.25.5 h1:kASCIS+oIeoc55j28T4o8KwlV2S4ZLPT6G0iq2SSbVQ= +github.com/go-openapi/swag/yamlutils v0.25.5/go.mod h1:Gek1/SjjfbYvM+Iq4QGwa/2lEXde9n2j4a3wI3pNuOQ= +github.com/go-openapi/testify/enable/yaml/v2 v2.4.0 h1:7SgOMTvJkM8yWrQlU8Jm18VeDPuAvB/xWrdxFJkoFag= +github.com/go-openapi/testify/enable/yaml/v2 v2.4.0/go.mod h1:14iV8jyyQlinc9StD7w1xVPW3CO3q1Gj04Jy//Kw4VM= +github.com/go-openapi/testify/v2 v2.4.0 h1:8nsPrHVCWkQ4p8h1EsRVymA2XABB4OT40gcvAu+voFM= +github.com/go-openapi/testify/v2 v2.4.0/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= @@ -167,8 +175,8 @@ github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= -github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= -github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU= +github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= @@ -213,7 +221,8 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag= github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8= +github.com/mailru/easyjson v0.9.2 h1:dX8U45hQsZpxd80nLvDGihsQ/OxlvTkVUXH2r/8cb2M= +github.com/mailru/easyjson v0.9.2/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ= github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= @@ -221,8 +230,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= -github.com/mattn/go-runewidth v0.0.20 h1:WcT52H91ZUAwy8+HUkdM3THM6gXqXuLJi9O3rjcQQaQ= -github.com/mattn/go-runewidth v0.0.20/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= +github.com/mattn/go-runewidth v0.0.21 h1:jJKAZiQH+2mIinzCJIaIG9Be1+0NR+5sz/lYEEjdM8w= +github.com/mattn/go-runewidth v0.0.21/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -236,12 +245,12 @@ github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELU github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= -github.com/oasdiff/oasdiff v1.11.10 h1:4I9VrktUoHmwydkJqVOC7Bd6BXKu9dc4UUP3PIu1VjM= -github.com/oasdiff/oasdiff v1.11.10/go.mod h1:GXARzmqBKN8lZHsTQD35ZM41ePbu6JdAZza4sRMeEKg= -github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY= -github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw= -github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c= -github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o= +github.com/oasdiff/oasdiff v1.12.1 h1:wnvBQS/WSqGqH23u1Jo3XVaF5y5X67TC5znSiy5nIug= +github.com/oasdiff/oasdiff v1.12.1/go.mod h1:4l8lF8SkdyiBVpa7AH3xc+oyDDXS1QTegX25mBS11/E= +github.com/oasdiff/yaml v0.0.0-20260313112342-a3ea61cb4d4c h1:7ACFcSaQsrWtrH4WHHfUqE1C+f8r2uv8KGaW0jTNjus= +github.com/oasdiff/yaml v0.0.0-20260313112342-a3ea61cb4d4c/go.mod h1:JKox4Gszkxt57kj27u7rvi7IFoIULvCZHUsBTUmQM/s= +github.com/oasdiff/yaml3 v0.0.0-20260224194419-61cd415a242b h1:vivRhVUAa9t1q0Db4ZmezBP8pWQWnXHFokZj0AOea2g= +github.com/oasdiff/yaml3 v0.0.0-20260224194419-61cd415a242b/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= @@ -253,6 +262,8 @@ 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.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw= github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU= +github.com/redis/go-redis/v9 v9.18.0 h1:pMkxYPkEbMPwRdenAzUNyFNrDgHx9U+DrBabWNfSRQs= +github.com/redis/go-redis/v9 v9.18.0/go.mod h1:k3ufPphLU5YXwNTUcCRXGxUoF1fqxnhFQmscfkCoDA0= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= @@ -260,8 +271,8 @@ github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= -github.com/sosodev/duration v1.3.1 h1:qtHBDMQ6lvMQsL15g4aopM4HEfOaYuhWBw3NPTtlqq4= -github.com/sosodev/duration v1.3.1/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg= +github.com/sosodev/duration v1.4.0 h1:35ed0KiVFriGHHzZZJaZLgmTEEICIyt8Sx0RQfj9IjE= +github.com/sosodev/duration v1.4.0/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -289,6 +300,7 @@ github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/match v1.2.0 h1:0pt8FlkOwjN2fPt4bIl4BoNxb98gGHN2ObFEDkrfZnM= +github.com/tidwall/match v1.2.0/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= @@ -305,6 +317,7 @@ github.com/vektah/gqlparser/v2 v2.5.32/go.mod h1:c1I28gSOVNzlfc4WuDlqU7voQnsqI6O github.com/wI2L/jsondiff v0.7.0 h1:1lH1G37GhBPqCfp/lrs91rf/2j3DktX6qYAKZkLuCQQ= github.com/wI2L/jsondiff v0.7.0/go.mod h1:KAEIojdQq66oJiHhDyQez2x+sRit0vIzC9KeK0yizxM= github.com/woodsbury/decimal128 v1.4.0 h1:xJATj7lLu4f2oObouMt2tgGiElE5gO6mSWUjQsBgUlc= +github.com/woodsbury/decimal128 v1.4.0/go.mod h1:BP46FUrVjVhdTbKT+XuQh2xfQaGki9LMIRJSFuh6THU= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= @@ -312,56 +325,60 @@ github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3i github.com/yargevad/filepathx v1.0.0 h1:SYcT+N3tYGi+NvazubCNlvgIPbzAk7i7y2dwg3I5FYc= github.com/yargevad/filepathx v1.0.0/go.mod h1:BprfX/gpYNJHJfc35GjRRpVcwWXS89gGulUIU5tK3tA= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs= +github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s= go.mongodb.org/mongo-driver/v2 v2.5.0 h1:yXUhImUjjAInNcpTcAlPHiT7bIXhshCTL3jVBkF3xaE= go.mongodb.org/mongo-driver/v2 v2.5.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.65.0 h1:LSJsvNqhj2sBNFb5NWHbyDK4QJ/skQ2ydjeOZ9OYNZ4= -go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.65.0/go.mod h1:0Q5ocj6h/+C6KYq8cnl4tDFVd4I1HBdsJ440aeagHos= -go.opentelemetry.io/contrib/propagators/b3 v1.40.0 h1:xariChe8OOVF3rNlfzGFgQc61npQmXhzZj/i82mxMfg= -go.opentelemetry.io/contrib/propagators/b3 v1.40.0/go.mod h1:72WvbdxbOfXaELEQfonFfOL6osvcVjI7uJEE8C2nkrs= -go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms= -go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.40.0 h1:MzfofMZN8ulNqobCmCAVbqVL5syHw+eB2qPRkCMA/fQ= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.40.0/go.mod h1:E73G9UFtKRXrxhBsHtG00TB5WxX57lpsQzogDkqBTz8= -go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g= -go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc= -go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8= -go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE= -go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw= -go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg= -go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw= -go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA= +go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.67.0 h1:E7DmskpIO7ZR6QI6zKSEKIDNUYoKw9oHXP23gzbCdU0= +go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.67.0/go.mod h1:WB2cS9y+AwqqKhoo9gw6/ZxlSjFBUQGZ8BQOaD3FVXM= +go.opentelemetry.io/contrib/propagators/b3 v1.42.0 h1:B2Pew5ufEtgkjLF+tSkXjgYZXQr9m7aCm1wLKB0URbU= +go.opentelemetry.io/contrib/propagators/b3 v1.42.0/go.mod h1:iPgUcSEF5DORW6+yNbdw/YevUy+QqJ508ncjhrRSCjc= +go.opentelemetry.io/otel v1.42.0 h1:lSQGzTgVR3+sgJDAU/7/ZMjN9Z+vUip7leaqBKy4sho= +go.opentelemetry.io/otel v1.42.0/go.mod h1:lJNsdRMxCUIWuMlVJWzecSMuNjE7dOYyWlqOXWkdqCc= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.42.0 h1:s/1iRkCKDfhlh1JF26knRneorus8aOwVIDhvYx9WoDw= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.42.0/go.mod h1:UI3wi0FXg1Pofb8ZBiBLhtMzgoTm1TYkMvn71fAqDzs= +go.opentelemetry.io/otel/metric v1.42.0 h1:2jXG+3oZLNXEPfNmnpxKDeZsFI5o4J+nz6xUlaFdF/4= +go.opentelemetry.io/otel/metric v1.42.0/go.mod h1:RlUN/7vTU7Ao/diDkEpQpnz3/92J9ko05BIwxYa2SSI= +go.opentelemetry.io/otel/sdk v1.42.0 h1:LyC8+jqk6UJwdrI/8VydAq/hvkFKNHZVIWuslJXYsDo= +go.opentelemetry.io/otel/sdk v1.42.0/go.mod h1:rGHCAxd9DAph0joO4W6OPwxjNTYWghRWmkHuGbayMts= +go.opentelemetry.io/otel/sdk/metric v1.42.0 h1:D/1QR46Clz6ajyZ3G8SgNlTJKBdGp84q9RKCAZ3YGuA= +go.opentelemetry.io/otel/sdk/metric v1.42.0/go.mod h1:Ua6AAlDKdZ7tdvaQKfSmnFTdHx37+J4ba8MwVCYM5hc= +go.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4LenLmOYY= +go.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg= -golang.org/x/arch v0.23.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A= +golang.org/x/arch v0.25.0 h1:qnk6Ksugpi5Bz32947rkUgDt9/s5qvqDPl/gBKdMJLE= +golang.org/x/arch v0.25.0/go.mod h1:0X+GdSIP+kL5wPmpK7sdkEVTt2XoYP0cSjQSbZBwOi8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= -golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= -golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa h1:Zt3DZoOFFYkKhDT3v7Lm9FDMEV06GpzjG2jrqW+QTE0= -golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA= +golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= +golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= +golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 h1:jiDhWWeC7jfWqR9c/uplMOqJ0sbNlNWv0UkzE0vX1MA= +golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90/go.mod h1:xE1HEv6b+1SCZ5/uscMRjUBKtIxworgEcEi+/n9NQDQ= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= -golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= +golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= +golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo= -golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y= -golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ= -golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= +golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= +golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= -golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -370,25 +387,25 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= -golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= -golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= +golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU= +golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= -golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= +golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= +golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= -golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= +golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s= +golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= From 9d6ae7f30026ed13e8e91569964e60e76024dc72 Mon Sep 17 00:00:00 2001 From: Snider Date: Sun, 15 Mar 2026 17:38:31 +0000 Subject: [PATCH 02/12] fix(i18n): use UK English spellings across codebase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - aur.go: initialize → initialise in comment and error string - ci.go: already_initialized → already_initialised i18n key - diff.go: add comment noting GetUncolorizedText is upstream oasdiff API Co-Authored-By: Virgil --- cmd/ci/ci.go | 2 +- pkg/release/publishers/aur.go | 4 ++-- pkg/sdk/diff.go | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/cmd/ci/ci.go b/cmd/ci/ci.go index 3b4da90..fa1d40d 100644 --- a/cmd/ci/ci.go +++ b/cmd/ci/ci.go @@ -160,7 +160,7 @@ func runCIReleaseInit() error { cli.Print("%s %s\n\n", dimStyle.Render(i18n.Label("init")), i18n.T("cmd.ci.init.initializing")) if release.ConfigExists(cwd) { - cli.Text(i18n.T("cmd.ci.init.already_initialized")) + cli.Text(i18n.T("cmd.ci.init.already_initialised")) return nil } diff --git a/pkg/release/publishers/aur.go b/pkg/release/publishers/aur.go index fa4608e..116cd74 100644 --- a/pkg/release/publishers/aur.go +++ b/pkg/release/publishers/aur.go @@ -225,14 +225,14 @@ func (p *AURPublisher) pushToAUR(ctx context.Context, data aurTemplateData, pkgb } defer func() { _ = os.RemoveAll(tmpDir) }() - // Clone existing AUR repo (or initialize new one) + // Clone existing AUR repo (or initialise new one) fmt.Printf("Cloning AUR package %s-bin...\n", data.PackageName) cmd := exec.CommandContext(ctx, "git", "clone", aurURL, tmpDir) if err := cmd.Run(); err != nil { // If clone fails, init a new repo cmd = exec.CommandContext(ctx, "git", "init", tmpDir) if err := cmd.Run(); err != nil { - return fmt.Errorf("aur.Publish: failed to initialize repo: %w", err) + return fmt.Errorf("aur.Publish: failed to initialise repo: %w", err) } cmd = exec.CommandContext(ctx, "git", "-C", tmpDir, "remote", "add", "origin", aurURL) if err := cmd.Run(); err != nil { diff --git a/pkg/sdk/diff.go b/pkg/sdk/diff.go index ebd4f6c..0540483 100644 --- a/pkg/sdk/diff.go +++ b/pkg/sdk/diff.go @@ -58,6 +58,7 @@ func Diff(basePath, revisionPath string) (*DiffResult, error) { localizer := checker.NewDefaultLocalizer() for _, b := range breaks { + // GetUncolorizedText uses US spelling — upstream oasdiff API. result.Changes = append(result.Changes, b.GetUncolorizedText(localizer)) } From 64486245906afa8f88561dfb4de124e71c37ba38 Mon Sep 17 00:00:00 2001 From: Snider Date: Mon, 16 Mar 2026 20:07:58 +0000 Subject: [PATCH 03/12] refactor: replace os.* and fmt.Errorf with go-io/go-log conventions Co-Authored-By: Virgil --- cmd/build/cmd_pwa.go | 51 ++++++++++++++------------- pkg/release/publishers/aur.go | 54 ++++++++++++++-------------- pkg/release/publishers/homebrew.go | 46 ++++++++++++------------ pkg/release/publishers/npm.go | 56 +++++++++++++++--------------- pkg/release/publishers/scoop.go | 42 +++++++++++----------- 5 files changed, 125 insertions(+), 124 deletions(-) diff --git a/cmd/build/cmd_pwa.go b/cmd/build/cmd_pwa.go index d94d50f..1a89504 100644 --- a/cmd/build/cmd_pwa.go +++ b/cmd/build/cmd_pwa.go @@ -8,7 +8,6 @@ package buildcmd import ( "encoding/json" - "errors" "fmt" "io" "net/http" @@ -19,6 +18,8 @@ import ( "strings" "forge.lthn.ai/core/go-i18n" + coreio "forge.lthn.ai/core/go-io" + coreerr "forge.lthn.ai/core/go-log" "github.com/leaanthony/debme" "github.com/leaanthony/gosod" "golang.org/x/net/html" @@ -26,8 +27,8 @@ import ( // Error sentinels for build commands var ( - errPathRequired = errors.New("the --path flag is required") - errURLRequired = errors.New("the --url flag is required") + errPathRequired = coreerr.E("buildcmd.Init", "the --path flag is required", nil) + errURLRequired = coreerr.E("buildcmd.Init", "the --url flag is required", nil) ) // runPwaBuild downloads a PWA from URL and builds it. @@ -36,13 +37,13 @@ func runPwaBuild(pwaURL string) error { tempDir, err := os.MkdirTemp("", "core-pwa-build-*") if err != nil { - return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "create temporary directory"}), err) + return coreerr.E("pwa.runPwaBuild", i18n.T("common.error.failed", map[string]any{"Action": "create temporary directory"}), err) } // defer os.RemoveAll(tempDir) // Keep temp dir for debugging fmt.Printf("%s %s\n", i18n.T("cmd.build.pwa.downloading_to"), tempDir) if err := downloadPWA(pwaURL, tempDir); err != nil { - return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "download PWA"}), err) + return coreerr.E("pwa.runPwaBuild", i18n.T("common.error.failed", map[string]any{"Action": "download PWA"}), err) } return runBuild(tempDir) @@ -53,13 +54,13 @@ func downloadPWA(baseURL, destDir string) error { // Fetch the main HTML page resp, err := http.Get(baseURL) if err != nil { - return fmt.Errorf("%s %s: %w", i18n.T("common.error.failed", map[string]any{"Action": "fetch URL"}), baseURL, err) + return coreerr.E("pwa.downloadPWA", i18n.T("common.error.failed", map[string]any{"Action": "fetch URL"})+" "+baseURL, err) } defer func() { _ = resp.Body.Close() }() body, err := io.ReadAll(resp.Body) if err != nil { - return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "read response body"}), err) + return coreerr.E("pwa.downloadPWA", i18n.T("common.error.failed", map[string]any{"Action": "read response body"}), err) } // Find the manifest URL from the HTML @@ -67,8 +68,8 @@ func downloadPWA(baseURL, destDir string) error { if err != nil { // If no manifest, it's not a PWA, but we can still try to package it as a simple site. fmt.Printf("%s %s\n", i18n.T("common.label.warning"), i18n.T("cmd.build.pwa.no_manifest")) - if err := os.WriteFile(filepath.Join(destDir, "index.html"), body, 0644); err != nil { - return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "write index.html"}), err) + if err := coreio.Local.Write(filepath.Join(destDir, "index.html"), string(body)); err != nil { + return coreerr.E("pwa.downloadPWA", i18n.T("common.error.failed", map[string]any{"Action": "write index.html"}), err) } return nil } @@ -78,7 +79,7 @@ func downloadPWA(baseURL, destDir string) error { // Fetch and parse the manifest manifest, err := fetchManifest(manifestURL) if err != nil { - return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "fetch or parse manifest"}), err) + return coreerr.E("pwa.downloadPWA", i18n.T("common.error.failed", map[string]any{"Action": "fetch or parse manifest"}), err) } // Download all assets listed in the manifest @@ -90,8 +91,8 @@ func downloadPWA(baseURL, destDir string) error { } // Also save the root index.html - if err := os.WriteFile(filepath.Join(destDir, "index.html"), body, 0644); err != nil { - return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "write index.html"}), err) + if err := coreio.Local.Write(filepath.Join(destDir, "index.html"), string(body)); err != nil { + return coreerr.E("pwa.downloadPWA", i18n.T("common.error.failed", map[string]any{"Action": "write index.html"}), err) } fmt.Println(i18n.T("cmd.build.pwa.download_complete")) @@ -130,7 +131,7 @@ func findManifestURL(htmlContent, baseURL string) (string, error) { f(doc) if manifestPath == "" { - return "", fmt.Errorf("%s", i18n.T("cmd.build.pwa.error.no_manifest_tag")) + return "", coreerr.E("pwa.findManifestURL", i18n.T("cmd.build.pwa.error.no_manifest_tag"), nil) } base, err := url.Parse(baseURL) @@ -203,7 +204,7 @@ func downloadAsset(assetURL, destDir string) error { } path := filepath.Join(destDir, filepath.FromSlash(u.Path)) - if err := os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil { + if err := coreio.Local.EnsureDir(filepath.Dir(path)); err != nil { return err } @@ -223,10 +224,10 @@ func runBuild(fromPath string) error { info, err := os.Stat(fromPath) if err != nil { - return fmt.Errorf("%s: %w", i18n.T("cmd.build.from_path.error.invalid_path"), err) + return coreerr.E("pwa.runBuild", i18n.T("cmd.build.from_path.error.invalid_path"), err) } if !info.IsDir() { - return fmt.Errorf("%s", i18n.T("cmd.build.from_path.error.must_be_directory")) + return coreerr.E("pwa.runBuild", i18n.T("cmd.build.from_path.error.must_be_directory"), nil) } buildDir := ".core/build/app" @@ -237,30 +238,30 @@ func runBuild(fromPath string) error { } outputExe := appName - if err := os.RemoveAll(buildDir); err != nil { - return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "clean build directory"}), err) + if err := coreio.Local.DeleteAll(buildDir); err != nil { + return coreerr.E("pwa.runBuild", i18n.T("common.error.failed", map[string]any{"Action": "clean build directory"}), err) } // 1. Generate the project from the embedded template fmt.Println(i18n.T("cmd.build.from_path.generating_template")) templateFS, err := debme.FS(guiTemplate, "tmpl/gui") if err != nil { - return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "anchor template filesystem"}), err) + return coreerr.E("pwa.runBuild", i18n.T("common.error.failed", map[string]any{"Action": "anchor template filesystem"}), err) } sod := gosod.New(templateFS) if sod == nil { - return fmt.Errorf("%s", i18n.T("common.error.failed", map[string]any{"Action": "create new sod instance"})) + return coreerr.E("pwa.runBuild", i18n.T("common.error.failed", map[string]any{"Action": "create new sod instance"}), nil) } templateData := map[string]string{"AppName": appName} if err := sod.Extract(buildDir, templateData); err != nil { - return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "extract template"}), err) + return coreerr.E("pwa.runBuild", i18n.T("common.error.failed", map[string]any{"Action": "extract template"}), err) } // 2. Copy the user's web app files fmt.Println(i18n.T("cmd.build.from_path.copying_files")) if err := copyDir(fromPath, htmlDir); err != nil { - return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "copy application files"}), err) + return coreerr.E("pwa.runBuild", i18n.T("common.error.failed", map[string]any{"Action": "copy application files"}), err) } // 3. Compile the application @@ -272,7 +273,7 @@ func runBuild(fromPath string) error { cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { - return fmt.Errorf("%s: %w", i18n.T("cmd.build.from_path.error.go_mod_tidy"), err) + return coreerr.E("pwa.runBuild", i18n.T("cmd.build.from_path.error.go_mod_tidy"), err) } // Run go build @@ -281,7 +282,7 @@ func runBuild(fromPath string) error { cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { - return fmt.Errorf("%s: %w", i18n.T("cmd.build.from_path.error.go_build"), err) + return coreerr.E("pwa.runBuild", i18n.T("cmd.build.from_path.error.go_build"), err) } fmt.Printf("\n%s %s/%s\n", i18n.T("cmd.build.from_path.success"), buildDir, outputExe) @@ -303,7 +304,7 @@ func copyDir(src, dst string) error { dstPath := filepath.Join(dst, relPath) if info.IsDir() { - return os.MkdirAll(dstPath, info.Mode()) + return coreio.Local.EnsureDir(dstPath) } srcFile, err := os.Open(path) diff --git a/pkg/release/publishers/aur.go b/pkg/release/publishers/aur.go index 116cd74..94694a6 100644 --- a/pkg/release/publishers/aur.go +++ b/pkg/release/publishers/aur.go @@ -5,7 +5,6 @@ import ( "bytes" "context" "embed" - "errors" "fmt" "os" "os/exec" @@ -14,7 +13,8 @@ import ( "text/template" "forge.lthn.ai/core/go-build/pkg/build" - "forge.lthn.ai/core/go-io" + coreio "forge.lthn.ai/core/go-io" + coreerr "forge.lthn.ai/core/go-log" ) //go:embed templates/aur/*.tmpl @@ -48,7 +48,7 @@ func (p *AURPublisher) Publish(ctx context.Context, release *Release, pubCfg Pub cfg := p.parseConfig(pubCfg, relCfg) if cfg.Maintainer == "" { - return errors.New("aur.Publish: maintainer is required (set publish.aur.maintainer in config)") + return coreerr.E("aur.Publish", "maintainer is required (set publish.aur.maintainer in config)", nil) } repo := "" @@ -58,7 +58,7 @@ func (p *AURPublisher) Publish(ctx context.Context, release *Release, pubCfg Pub if repo == "" { detectedRepo, err := detectRepository(release.ProjectDir) if err != nil { - return fmt.Errorf("aur.Publish: could not determine repository: %w", err) + return coreerr.E("aur.Publish", "could not determine repository", err) } repo = detectedRepo } @@ -133,7 +133,7 @@ func (p *AURPublisher) parseConfig(pubCfg PublisherConfig, relCfg ReleaseConfig) return cfg } -func (p *AURPublisher) dryRunPublish(m io.Medium, data aurTemplateData, cfg AURConfig) error { +func (p *AURPublisher) dryRunPublish(m coreio.Medium, data aurTemplateData, cfg AURConfig) error { fmt.Println() fmt.Println("=== DRY RUN: AUR Publish ===") fmt.Println() @@ -145,7 +145,7 @@ func (p *AURPublisher) dryRunPublish(m io.Medium, data aurTemplateData, cfg AURC pkgbuild, err := p.renderTemplate(m, "templates/aur/PKGBUILD.tmpl", data) if err != nil { - return fmt.Errorf("aur.dryRunPublish: %w", err) + return coreerr.E("aur.dryRunPublish", "failed to render PKGBUILD template", err) } fmt.Println("Generated PKGBUILD:") fmt.Println("---") @@ -155,7 +155,7 @@ func (p *AURPublisher) dryRunPublish(m io.Medium, data aurTemplateData, cfg AURC srcinfo, err := p.renderTemplate(m, "templates/aur/.SRCINFO.tmpl", data) if err != nil { - return fmt.Errorf("aur.dryRunPublish: %w", err) + return coreerr.E("aur.dryRunPublish", "failed to render .SRCINFO template", err) } fmt.Println("Generated .SRCINFO:") fmt.Println("---") @@ -173,12 +173,12 @@ func (p *AURPublisher) dryRunPublish(m io.Medium, data aurTemplateData, cfg AURC func (p *AURPublisher) executePublish(ctx context.Context, projectDir string, data aurTemplateData, cfg AURConfig, release *Release) error { pkgbuild, err := p.renderTemplate(release.FS, "templates/aur/PKGBUILD.tmpl", data) if err != nil { - return fmt.Errorf("aur.Publish: failed to render PKGBUILD: %w", err) + return coreerr.E("aur.Publish", "failed to render PKGBUILD", err) } srcinfo, err := p.renderTemplate(release.FS, "templates/aur/.SRCINFO.tmpl", data) if err != nil { - return fmt.Errorf("aur.Publish: failed to render .SRCINFO: %w", err) + return coreerr.E("aur.Publish", "failed to render .SRCINFO", err) } // If official config is enabled, write to output directory @@ -191,17 +191,17 @@ func (p *AURPublisher) executePublish(ctx context.Context, projectDir string, da } if err := release.FS.EnsureDir(output); err != nil { - return fmt.Errorf("aur.Publish: failed to create output directory: %w", err) + return coreerr.E("aur.Publish", "failed to create output directory", err) } pkgbuildPath := filepath.Join(output, "PKGBUILD") if err := release.FS.Write(pkgbuildPath, pkgbuild); err != nil { - return fmt.Errorf("aur.Publish: failed to write PKGBUILD: %w", err) + return coreerr.E("aur.Publish", "failed to write PKGBUILD", err) } srcinfoPath := filepath.Join(output, ".SRCINFO") if err := release.FS.Write(srcinfoPath, srcinfo); err != nil { - return fmt.Errorf("aur.Publish: failed to write .SRCINFO: %w", err) + return coreerr.E("aur.Publish", "failed to write .SRCINFO", err) } fmt.Printf("Wrote AUR files: %s\n", output) } @@ -221,9 +221,9 @@ func (p *AURPublisher) pushToAUR(ctx context.Context, data aurTemplateData, pkgb tmpDir, err := os.MkdirTemp("", "aur-package-*") if err != nil { - return fmt.Errorf("aur.Publish: failed to create temp directory: %w", err) + return coreerr.E("aur.pushToAUR", "failed to create temp directory", err) } - defer func() { _ = os.RemoveAll(tmpDir) }() + defer func() { _ = coreio.Local.DeleteAll(tmpDir) }() // Clone existing AUR repo (or initialise new one) fmt.Printf("Cloning AUR package %s-bin...\n", data.PackageName) @@ -232,20 +232,20 @@ func (p *AURPublisher) pushToAUR(ctx context.Context, data aurTemplateData, pkgb // If clone fails, init a new repo cmd = exec.CommandContext(ctx, "git", "init", tmpDir) if err := cmd.Run(); err != nil { - return fmt.Errorf("aur.Publish: failed to initialise repo: %w", err) + return coreerr.E("aur.pushToAUR", "failed to initialise repo", err) } cmd = exec.CommandContext(ctx, "git", "-C", tmpDir, "remote", "add", "origin", aurURL) if err := cmd.Run(); err != nil { - return fmt.Errorf("aur.Publish: failed to add remote: %w", err) + return coreerr.E("aur.pushToAUR", "failed to add remote", err) } } // Write files - if err := os.WriteFile(filepath.Join(tmpDir, "PKGBUILD"), []byte(pkgbuild), 0644); err != nil { - return fmt.Errorf("aur.Publish: failed to write PKGBUILD: %w", err) + if err := coreio.Local.Write(filepath.Join(tmpDir, "PKGBUILD"), pkgbuild); err != nil { + return coreerr.E("aur.pushToAUR", "failed to write PKGBUILD", err) } - if err := os.WriteFile(filepath.Join(tmpDir, ".SRCINFO"), []byte(srcinfo), 0644); err != nil { - return fmt.Errorf("aur.Publish: failed to write .SRCINFO: %w", err) + if err := coreio.Local.Write(filepath.Join(tmpDir, ".SRCINFO"), srcinfo); err != nil { + return coreerr.E("aur.pushToAUR", "failed to write .SRCINFO", err) } commitMsg := fmt.Sprintf("Update to %s", data.Version) @@ -253,7 +253,7 @@ func (p *AURPublisher) pushToAUR(ctx context.Context, data aurTemplateData, pkgb cmd = exec.CommandContext(ctx, "git", "add", ".") cmd.Dir = tmpDir if err := cmd.Run(); err != nil { - return fmt.Errorf("aur.Publish: git add failed: %w", err) + return coreerr.E("aur.pushToAUR", "git add failed", err) } cmd = exec.CommandContext(ctx, "git", "commit", "-m", commitMsg) @@ -261,7 +261,7 @@ func (p *AURPublisher) pushToAUR(ctx context.Context, data aurTemplateData, pkgb cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { - return fmt.Errorf("aur.Publish: git commit failed: %w", err) + return coreerr.E("aur.pushToAUR", "git commit failed", err) } cmd = exec.CommandContext(ctx, "git", "push", "origin", "master") @@ -269,14 +269,14 @@ func (p *AURPublisher) pushToAUR(ctx context.Context, data aurTemplateData, pkgb cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { - return fmt.Errorf("aur.Publish: git push failed: %w", err) + return coreerr.E("aur.pushToAUR", "git push failed", err) } fmt.Printf("Published to AUR: https://aur.archlinux.org/packages/%s-bin\n", data.PackageName) return nil } -func (p *AURPublisher) renderTemplate(m io.Medium, name string, data aurTemplateData) (string, error) { +func (p *AURPublisher) renderTemplate(m coreio.Medium, name string, data aurTemplateData) (string, error) { var content []byte var err error @@ -293,18 +293,18 @@ func (p *AURPublisher) renderTemplate(m io.Medium, name string, data aurTemplate if content == nil { content, err = aurTemplates.ReadFile(name) if err != nil { - return "", fmt.Errorf("failed to read template %s: %w", name, err) + return "", coreerr.E("aur.renderTemplate", "failed to read template "+name, err) } } tmpl, err := template.New(filepath.Base(name)).Parse(string(content)) if err != nil { - return "", fmt.Errorf("failed to parse template %s: %w", name, err) + return "", coreerr.E("aur.renderTemplate", "failed to parse template "+name, err) } var buf bytes.Buffer if err := tmpl.Execute(&buf, data); err != nil { - return "", fmt.Errorf("failed to execute template %s: %w", name, err) + return "", coreerr.E("aur.renderTemplate", "failed to execute template "+name, err) } return buf.String(), nil diff --git a/pkg/release/publishers/homebrew.go b/pkg/release/publishers/homebrew.go index 34c6ab1..2ae174b 100644 --- a/pkg/release/publishers/homebrew.go +++ b/pkg/release/publishers/homebrew.go @@ -5,7 +5,6 @@ import ( "bytes" "context" "embed" - "errors" "fmt" "os" "os/exec" @@ -14,7 +13,8 @@ import ( "text/template" "forge.lthn.ai/core/go-build/pkg/build" - "forge.lthn.ai/core/go-io" + coreio "forge.lthn.ai/core/go-io" + coreerr "forge.lthn.ai/core/go-log" ) //go:embed templates/homebrew/*.tmpl @@ -58,7 +58,7 @@ func (p *HomebrewPublisher) Publish(ctx context.Context, release *Release, pubCf // Validate configuration if cfg.Tap == "" && (cfg.Official == nil || !cfg.Official.Enabled) { - return errors.New("homebrew.Publish: tap is required (set publish.homebrew.tap in config)") + return coreerr.E("homebrew.Publish", "tap is required (set publish.homebrew.tap in config)", nil) } // Get repository and project info @@ -69,7 +69,7 @@ func (p *HomebrewPublisher) Publish(ctx context.Context, release *Release, pubCf if repo == "" { detectedRepo, err := detectRepository(release.ProjectDir) if err != nil { - return fmt.Errorf("homebrew.Publish: could not determine repository: %w", err) + return coreerr.E("homebrew.Publish", "could not determine repository", err) } repo = detectedRepo } @@ -162,7 +162,7 @@ func (p *HomebrewPublisher) parseConfig(pubCfg PublisherConfig, relCfg ReleaseCo } // dryRunPublish shows what would be done. -func (p *HomebrewPublisher) dryRunPublish(m io.Medium, data homebrewTemplateData, cfg HomebrewConfig) error { +func (p *HomebrewPublisher) dryRunPublish(m coreio.Medium, data homebrewTemplateData, cfg HomebrewConfig) error { fmt.Println() fmt.Println("=== DRY RUN: Homebrew Publish ===") fmt.Println() @@ -175,7 +175,7 @@ func (p *HomebrewPublisher) dryRunPublish(m io.Medium, data homebrewTemplateData // Generate and show formula formula, err := p.renderTemplate(m, "templates/homebrew/formula.rb.tmpl", data) if err != nil { - return fmt.Errorf("homebrew.dryRunPublish: %w", err) + return coreerr.E("homebrew.dryRunPublish", "failed to render template", err) } fmt.Println("Generated formula.rb:") fmt.Println("---") @@ -204,7 +204,7 @@ func (p *HomebrewPublisher) executePublish(ctx context.Context, projectDir strin // Generate formula formula, err := p.renderTemplate(release.FS, "templates/homebrew/formula.rb.tmpl", data) if err != nil { - return fmt.Errorf("homebrew.Publish: failed to render formula: %w", err) + return coreerr.E("homebrew.Publish", "failed to render formula", err) } // If official config is enabled, write to output directory @@ -217,12 +217,12 @@ func (p *HomebrewPublisher) executePublish(ctx context.Context, projectDir strin } if err := release.FS.EnsureDir(output); err != nil { - return fmt.Errorf("homebrew.Publish: failed to create output directory: %w", err) + return coreerr.E("homebrew.Publish", "failed to create output directory", err) } formulaPath := filepath.Join(output, fmt.Sprintf("%s.rb", strings.ToLower(data.FormulaClass))) if err := release.FS.Write(formulaPath, formula); err != nil { - return fmt.Errorf("homebrew.Publish: failed to write formula: %w", err) + return coreerr.E("homebrew.Publish", "failed to write formula", err) } fmt.Printf("Wrote Homebrew formula for official PR: %s\n", formulaPath) } @@ -242,9 +242,9 @@ func (p *HomebrewPublisher) commitToTap(ctx context.Context, tap string, data ho // Clone tap repo to temp directory tmpDir, err := os.MkdirTemp("", "homebrew-tap-*") if err != nil { - return fmt.Errorf("homebrew.Publish: failed to create temp directory: %w", err) + return coreerr.E("homebrew.commitToTap", "failed to create temp directory", err) } - defer func() { _ = os.RemoveAll(tmpDir) }() + defer func() { _ = coreio.Local.DeleteAll(tmpDir) }() // Clone the tap fmt.Printf("Cloning tap %s...\n", tap) @@ -252,19 +252,19 @@ func (p *HomebrewPublisher) commitToTap(ctx context.Context, tap string, data ho cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { - return fmt.Errorf("homebrew.Publish: failed to clone tap: %w", err) + return coreerr.E("homebrew.commitToTap", "failed to clone tap", err) } // Ensure Formula directory exists formulaDir := filepath.Join(tmpDir, "Formula") - if err := os.MkdirAll(formulaDir, 0755); err != nil { - return fmt.Errorf("homebrew.Publish: failed to create Formula directory: %w", err) + if err := coreio.Local.EnsureDir(formulaDir); err != nil { + return coreerr.E("homebrew.commitToTap", "failed to create Formula directory", err) } // Write formula formulaPath := filepath.Join(formulaDir, fmt.Sprintf("%s.rb", strings.ToLower(data.FormulaClass))) - if err := os.WriteFile(formulaPath, []byte(formula), 0644); err != nil { - return fmt.Errorf("homebrew.Publish: failed to write formula: %w", err) + if err := coreio.Local.Write(formulaPath, formula); err != nil { + return coreerr.E("homebrew.commitToTap", "failed to write formula", err) } // Git add, commit, push @@ -273,7 +273,7 @@ func (p *HomebrewPublisher) commitToTap(ctx context.Context, tap string, data ho cmd = exec.CommandContext(ctx, "git", "add", ".") cmd.Dir = tmpDir if err := cmd.Run(); err != nil { - return fmt.Errorf("homebrew.Publish: git add failed: %w", err) + return coreerr.E("homebrew.commitToTap", "git add failed", err) } cmd = exec.CommandContext(ctx, "git", "commit", "-m", commitMsg) @@ -281,7 +281,7 @@ func (p *HomebrewPublisher) commitToTap(ctx context.Context, tap string, data ho cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { - return fmt.Errorf("homebrew.Publish: git commit failed: %w", err) + return coreerr.E("homebrew.commitToTap", "git commit failed", err) } cmd = exec.CommandContext(ctx, "git", "push") @@ -289,7 +289,7 @@ func (p *HomebrewPublisher) commitToTap(ctx context.Context, tap string, data ho cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { - return fmt.Errorf("homebrew.Publish: git push failed: %w", err) + return coreerr.E("homebrew.commitToTap", "git push failed", err) } fmt.Printf("Updated Homebrew tap: %s\n", tap) @@ -297,7 +297,7 @@ func (p *HomebrewPublisher) commitToTap(ctx context.Context, tap string, data ho } // renderTemplate renders an embedded template with the given data. -func (p *HomebrewPublisher) renderTemplate(m io.Medium, name string, data homebrewTemplateData) (string, error) { +func (p *HomebrewPublisher) renderTemplate(m coreio.Medium, name string, data homebrewTemplateData) (string, error) { var content []byte var err error @@ -314,18 +314,18 @@ func (p *HomebrewPublisher) renderTemplate(m io.Medium, name string, data homebr if content == nil { content, err = homebrewTemplates.ReadFile(name) if err != nil { - return "", fmt.Errorf("failed to read template %s: %w", name, err) + return "", coreerr.E("homebrew.renderTemplate", "failed to read template "+name, err) } } tmpl, err := template.New(filepath.Base(name)).Parse(string(content)) if err != nil { - return "", fmt.Errorf("failed to parse template %s: %w", name, err) + return "", coreerr.E("homebrew.renderTemplate", "failed to parse template "+name, err) } var buf bytes.Buffer if err := tmpl.Execute(&buf, data); err != nil { - return "", fmt.Errorf("failed to execute template %s: %w", name, err) + return "", coreerr.E("homebrew.renderTemplate", "failed to execute template "+name, err) } return buf.String(), nil diff --git a/pkg/release/publishers/npm.go b/pkg/release/publishers/npm.go index 74ae06f..7ab9429 100644 --- a/pkg/release/publishers/npm.go +++ b/pkg/release/publishers/npm.go @@ -5,7 +5,6 @@ import ( "bytes" "context" "embed" - "errors" "fmt" "os" "os/exec" @@ -13,7 +12,8 @@ import ( "strings" "text/template" - "forge.lthn.ai/core/go-io" + coreio "forge.lthn.ai/core/go-io" + coreerr "forge.lthn.ai/core/go-log" ) //go:embed templates/npm/*.tmpl @@ -48,7 +48,7 @@ func (p *NpmPublisher) Publish(ctx context.Context, release *Release, pubCfg Pub // Validate configuration if npmCfg.Package == "" { - return errors.New("npm.Publish: package name is required (set publish.npm.package in config)") + return coreerr.E("npm.Publish", "package name is required (set publish.npm.package in config)", nil) } // Get repository @@ -59,7 +59,7 @@ func (p *NpmPublisher) Publish(ctx context.Context, release *Release, pubCfg Pub if repo == "" { detectedRepo, err := detectRepository(release.ProjectDir) if err != nil { - return fmt.Errorf("npm.Publish: could not determine repository: %w", err) + return coreerr.E("npm.Publish", "could not determine repository", err) } repo = detectedRepo } @@ -130,7 +130,7 @@ type npmTemplateData struct { } // dryRunPublish shows what would be done without actually publishing. -func (p *NpmPublisher) dryRunPublish(m io.Medium, data npmTemplateData, cfg *NpmConfig) error { +func (p *NpmPublisher) dryRunPublish(m coreio.Medium, data npmTemplateData, cfg *NpmConfig) error { fmt.Println() fmt.Println("=== DRY RUN: npm Publish ===") fmt.Println() @@ -144,7 +144,7 @@ func (p *NpmPublisher) dryRunPublish(m io.Medium, data npmTemplateData, cfg *Npm // Generate and show package.json pkgJSON, err := p.renderTemplate(m, "templates/npm/package.json.tmpl", data) if err != nil { - return fmt.Errorf("npm.dryRunPublish: %w", err) + return coreerr.E("npm.dryRunPublish", "failed to render template", err) } fmt.Println("Generated package.json:") fmt.Println("---") @@ -160,56 +160,56 @@ func (p *NpmPublisher) dryRunPublish(m io.Medium, data npmTemplateData, cfg *Npm } // executePublish actually creates and publishes the npm package. -func (p *NpmPublisher) executePublish(ctx context.Context, m io.Medium, data npmTemplateData, cfg *NpmConfig) error { +func (p *NpmPublisher) executePublish(ctx context.Context, m coreio.Medium, data npmTemplateData, cfg *NpmConfig) error { // Check for NPM_TOKEN if os.Getenv("NPM_TOKEN") == "" { - return errors.New("npm.Publish: NPM_TOKEN environment variable is required") + return coreerr.E("npm.Publish", "NPM_TOKEN environment variable is required", nil) } // Create temp directory for package tmpDir, err := os.MkdirTemp("", "npm-publish-*") if err != nil { - return fmt.Errorf("npm.Publish: failed to create temp directory: %w", err) + return coreerr.E("npm.Publish", "failed to create temp directory", err) } - defer func() { _ = os.RemoveAll(tmpDir) }() + defer func() { _ = coreio.Local.DeleteAll(tmpDir) }() // Create bin directory binDir := filepath.Join(tmpDir, "bin") - if err := os.MkdirAll(binDir, 0755); err != nil { - return fmt.Errorf("npm.Publish: failed to create bin directory: %w", err) + if err := coreio.Local.EnsureDir(binDir); err != nil { + return coreerr.E("npm.Publish", "failed to create bin directory", err) } // Generate package.json pkgJSON, err := p.renderTemplate(m, "templates/npm/package.json.tmpl", data) if err != nil { - return fmt.Errorf("npm.Publish: failed to render package.json: %w", err) + return coreerr.E("npm.Publish", "failed to render package.json", err) } - if err := os.WriteFile(filepath.Join(tmpDir, "package.json"), []byte(pkgJSON), 0644); err != nil { - return fmt.Errorf("npm.Publish: failed to write package.json: %w", err) + if err := coreio.Local.Write(filepath.Join(tmpDir, "package.json"), pkgJSON); err != nil { + return coreerr.E("npm.Publish", "failed to write package.json", err) } // Generate install.js installJS, err := p.renderTemplate(m, "templates/npm/install.js.tmpl", data) if err != nil { - return fmt.Errorf("npm.Publish: failed to render install.js: %w", err) + return coreerr.E("npm.Publish", "failed to render install.js", err) } - if err := os.WriteFile(filepath.Join(tmpDir, "install.js"), []byte(installJS), 0644); err != nil { - return fmt.Errorf("npm.Publish: failed to write install.js: %w", err) + if err := coreio.Local.Write(filepath.Join(tmpDir, "install.js"), installJS); err != nil { + return coreerr.E("npm.Publish", "failed to write install.js", err) } // Generate run.js runJS, err := p.renderTemplate(m, "templates/npm/run.js.tmpl", data) if err != nil { - return fmt.Errorf("npm.Publish: failed to render run.js: %w", err) + return coreerr.E("npm.Publish", "failed to render run.js", err) } - if err := os.WriteFile(filepath.Join(binDir, "run.js"), []byte(runJS), 0755); err != nil { - return fmt.Errorf("npm.Publish: failed to write run.js: %w", err) + if err := coreio.Local.Write(filepath.Join(binDir, "run.js"), runJS); err != nil { + return coreerr.E("npm.Publish", "failed to write run.js", err) } // Create .npmrc with token npmrc := "//registry.npmjs.org/:_authToken=${NPM_TOKEN}\n" - if err := os.WriteFile(filepath.Join(tmpDir, ".npmrc"), []byte(npmrc), 0600); err != nil { - return fmt.Errorf("npm.Publish: failed to write .npmrc: %w", err) + if err := coreio.Local.Write(filepath.Join(tmpDir, ".npmrc"), npmrc); err != nil { + return coreerr.E("npm.Publish", "failed to write .npmrc", err) } // Run npm publish @@ -221,7 +221,7 @@ func (p *NpmPublisher) executePublish(ctx context.Context, m io.Medium, data npm fmt.Printf("Publishing %s@%s to npm...\n", data.Package, data.Version) if err := cmd.Run(); err != nil { - return fmt.Errorf("npm.Publish: npm publish failed: %w", err) + return coreerr.E("npm.Publish", "npm publish failed", err) } fmt.Printf("Published %s@%s to npm\n", data.Package, data.Version) @@ -231,7 +231,7 @@ func (p *NpmPublisher) executePublish(ctx context.Context, m io.Medium, data npm } // renderTemplate renders an embedded template with the given data. -func (p *NpmPublisher) renderTemplate(m io.Medium, name string, data npmTemplateData) (string, error) { +func (p *NpmPublisher) renderTemplate(m coreio.Medium, name string, data npmTemplateData) (string, error) { var content []byte var err error @@ -248,18 +248,18 @@ func (p *NpmPublisher) renderTemplate(m io.Medium, name string, data npmTemplate if content == nil { content, err = npmTemplates.ReadFile(name) if err != nil { - return "", fmt.Errorf("failed to read template %s: %w", name, err) + return "", coreerr.E("npm.renderTemplate", "failed to read template "+name, err) } } tmpl, err := template.New(filepath.Base(name)).Parse(string(content)) if err != nil { - return "", fmt.Errorf("failed to parse template %s: %w", name, err) + return "", coreerr.E("npm.renderTemplate", "failed to parse template "+name, err) } var buf bytes.Buffer if err := tmpl.Execute(&buf, data); err != nil { - return "", fmt.Errorf("failed to execute template %s: %w", name, err) + return "", coreerr.E("npm.renderTemplate", "failed to execute template "+name, err) } return buf.String(), nil diff --git a/pkg/release/publishers/scoop.go b/pkg/release/publishers/scoop.go index 283f1af..af54091 100644 --- a/pkg/release/publishers/scoop.go +++ b/pkg/release/publishers/scoop.go @@ -5,7 +5,6 @@ import ( "bytes" "context" "embed" - "errors" "fmt" "os" "os/exec" @@ -14,7 +13,8 @@ import ( "text/template" "forge.lthn.ai/core/go-build/pkg/build" - "forge.lthn.ai/core/go-io" + coreio "forge.lthn.ai/core/go-io" + coreerr "forge.lthn.ai/core/go-log" ) //go:embed templates/scoop/*.tmpl @@ -46,7 +46,7 @@ func (p *ScoopPublisher) Publish(ctx context.Context, release *Release, pubCfg P cfg := p.parseConfig(pubCfg, relCfg) if cfg.Bucket == "" && (cfg.Official == nil || !cfg.Official.Enabled) { - return errors.New("scoop.Publish: bucket is required (set publish.scoop.bucket in config)") + return coreerr.E("scoop.Publish", "bucket is required (set publish.scoop.bucket in config)", nil) } repo := "" @@ -56,7 +56,7 @@ func (p *ScoopPublisher) Publish(ctx context.Context, release *Release, pubCfg P if repo == "" { detectedRepo, err := detectRepository(release.ProjectDir) if err != nil { - return fmt.Errorf("scoop.Publish: could not determine repository: %w", err) + return coreerr.E("scoop.Publish", "could not determine repository", err) } repo = detectedRepo } @@ -121,7 +121,7 @@ func (p *ScoopPublisher) parseConfig(pubCfg PublisherConfig, relCfg ReleaseConfi return cfg } -func (p *ScoopPublisher) dryRunPublish(m io.Medium, data scoopTemplateData, cfg ScoopConfig) error { +func (p *ScoopPublisher) dryRunPublish(m coreio.Medium, data scoopTemplateData, cfg ScoopConfig) error { fmt.Println() fmt.Println("=== DRY RUN: Scoop Publish ===") fmt.Println() @@ -133,7 +133,7 @@ func (p *ScoopPublisher) dryRunPublish(m io.Medium, data scoopTemplateData, cfg manifest, err := p.renderTemplate(m, "templates/scoop/manifest.json.tmpl", data) if err != nil { - return fmt.Errorf("scoop.dryRunPublish: %w", err) + return coreerr.E("scoop.dryRunPublish", "failed to render template", err) } fmt.Println("Generated manifest.json:") fmt.Println("---") @@ -160,7 +160,7 @@ func (p *ScoopPublisher) dryRunPublish(m io.Medium, data scoopTemplateData, cfg func (p *ScoopPublisher) executePublish(ctx context.Context, projectDir string, data scoopTemplateData, cfg ScoopConfig, release *Release) error { manifest, err := p.renderTemplate(release.FS, "templates/scoop/manifest.json.tmpl", data) if err != nil { - return fmt.Errorf("scoop.Publish: failed to render manifest: %w", err) + return coreerr.E("scoop.Publish", "failed to render manifest", err) } // If official config is enabled, write to output directory @@ -173,12 +173,12 @@ func (p *ScoopPublisher) executePublish(ctx context.Context, projectDir string, } if err := release.FS.EnsureDir(output); err != nil { - return fmt.Errorf("scoop.Publish: failed to create output directory: %w", err) + return coreerr.E("scoop.Publish", "failed to create output directory", err) } manifestPath := filepath.Join(output, fmt.Sprintf("%s.json", data.PackageName)) if err := release.FS.Write(manifestPath, manifest); err != nil { - return fmt.Errorf("scoop.Publish: failed to write manifest: %w", err) + return coreerr.E("scoop.Publish", "failed to write manifest", err) } fmt.Printf("Wrote Scoop manifest for official PR: %s\n", manifestPath) } @@ -196,16 +196,16 @@ func (p *ScoopPublisher) executePublish(ctx context.Context, projectDir string, func (p *ScoopPublisher) commitToBucket(ctx context.Context, bucket string, data scoopTemplateData, manifest string) error { tmpDir, err := os.MkdirTemp("", "scoop-bucket-*") if err != nil { - return fmt.Errorf("scoop.Publish: failed to create temp directory: %w", err) + return coreerr.E("scoop.commitToBucket", "failed to create temp directory", err) } - defer func() { _ = os.RemoveAll(tmpDir) }() + defer func() { _ = coreio.Local.DeleteAll(tmpDir) }() fmt.Printf("Cloning bucket %s...\n", bucket) cmd := exec.CommandContext(ctx, "gh", "repo", "clone", bucket, tmpDir, "--", "--depth=1") cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { - return fmt.Errorf("scoop.Publish: failed to clone bucket: %w", err) + return coreerr.E("scoop.commitToBucket", "failed to clone bucket", err) } // Ensure bucket directory exists @@ -215,8 +215,8 @@ func (p *ScoopPublisher) commitToBucket(ctx context.Context, bucket string, data } manifestPath := filepath.Join(bucketDir, fmt.Sprintf("%s.json", data.PackageName)) - if err := os.WriteFile(manifestPath, []byte(manifest), 0644); err != nil { - return fmt.Errorf("scoop.Publish: failed to write manifest: %w", err) + if err := coreio.Local.Write(manifestPath, manifest); err != nil { + return coreerr.E("scoop.commitToBucket", "failed to write manifest", err) } commitMsg := fmt.Sprintf("Update %s to %s", data.PackageName, data.Version) @@ -224,7 +224,7 @@ func (p *ScoopPublisher) commitToBucket(ctx context.Context, bucket string, data cmd = exec.CommandContext(ctx, "git", "add", ".") cmd.Dir = tmpDir if err := cmd.Run(); err != nil { - return fmt.Errorf("scoop.Publish: git add failed: %w", err) + return coreerr.E("scoop.commitToBucket", "git add failed", err) } cmd = exec.CommandContext(ctx, "git", "commit", "-m", commitMsg) @@ -232,7 +232,7 @@ func (p *ScoopPublisher) commitToBucket(ctx context.Context, bucket string, data cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { - return fmt.Errorf("scoop.Publish: git commit failed: %w", err) + return coreerr.E("scoop.commitToBucket", "git commit failed", err) } cmd = exec.CommandContext(ctx, "git", "push") @@ -240,14 +240,14 @@ func (p *ScoopPublisher) commitToBucket(ctx context.Context, bucket string, data cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { - return fmt.Errorf("scoop.Publish: git push failed: %w", err) + return coreerr.E("scoop.commitToBucket", "git push failed", err) } fmt.Printf("Updated Scoop bucket: %s\n", bucket) return nil } -func (p *ScoopPublisher) renderTemplate(m io.Medium, name string, data scoopTemplateData) (string, error) { +func (p *ScoopPublisher) renderTemplate(m coreio.Medium, name string, data scoopTemplateData) (string, error) { var content []byte var err error @@ -264,18 +264,18 @@ func (p *ScoopPublisher) renderTemplate(m io.Medium, name string, data scoopTemp if content == nil { content, err = scoopTemplates.ReadFile(name) if err != nil { - return "", fmt.Errorf("failed to read template %s: %w", name, err) + return "", coreerr.E("scoop.renderTemplate", "failed to read template "+name, err) } } tmpl, err := template.New(filepath.Base(name)).Parse(string(content)) if err != nil { - return "", fmt.Errorf("failed to parse template %s: %w", name, err) + return "", coreerr.E("scoop.renderTemplate", "failed to parse template "+name, err) } var buf bytes.Buffer if err := tmpl.Execute(&buf, data); err != nil { - return "", fmt.Errorf("failed to execute template %s: %w", name, err) + return "", coreerr.E("scoop.renderTemplate", "failed to execute template "+name, err) } return buf.String(), nil From d0e22f76e1cdf1753cbe7093af61062518a4325d Mon Sep 17 00:00:00 2001 From: Snider Date: Mon, 16 Mar 2026 21:03:21 +0000 Subject: [PATCH 04/12] refactor: replace remaining fmt.Errorf/errors.New with coreerr.E() Replace all fmt.Errorf and errors.New calls across 32 production files with coreerr.E("caller.Method", "message", err) from go-log, matching the convention already established in publishers/scoop.go and publishers/homebrew.go. Co-Authored-By: Virgil --- cmd/build/cmd_project.go | 21 +++++------ cmd/build/cmd_sdk.go | 3 +- cmd/ci/ci.go | 4 +-- cmd/sdk/cmd.go | 8 ++--- pkg/build/archive.go | 54 ++++++++++++++-------------- pkg/build/builders/cpp.go | 26 +++++++------- pkg/build/builders/docker.go | 14 ++++---- pkg/build/builders/go.go | 14 ++++---- pkg/build/builders/linuxkit.go | 14 ++++---- pkg/build/builders/taskfile.go | 8 ++--- pkg/build/builders/wails.go | 20 +++++------ pkg/build/checksum.go | 14 ++++---- pkg/build/config.go | 6 ++-- pkg/build/signing/codesign.go | 15 ++++---- pkg/build/signing/gpg.go | 7 ++-- pkg/build/signing/sign.go | 10 +++--- pkg/release/changelog.go | 5 +-- pkg/release/config.go | 16 ++++----- pkg/release/publishers/chocolatey.go | 30 ++++++++-------- pkg/release/publishers/docker.go | 13 +++---- pkg/release/publishers/github.go | 21 +++++------ pkg/release/publishers/linuxkit.go | 19 +++++----- pkg/release/release.go | 54 ++++++++++++++-------------- pkg/release/sdk.go | 14 ++++---- pkg/release/version.go | 4 ++- pkg/sdk/detect.go | 13 ++++--- pkg/sdk/diff.go | 7 ++-- pkg/sdk/generators/go.go | 6 ++-- pkg/sdk/generators/php.go | 9 +++-- pkg/sdk/generators/python.go | 4 +-- pkg/sdk/generators/typescript.go | 6 ++-- pkg/sdk/sdk.go | 5 +-- 32 files changed, 235 insertions(+), 229 deletions(-) diff --git a/cmd/build/cmd_project.go b/cmd/build/cmd_project.go index 9fb547e..ed81f7a 100644 --- a/cmd/build/cmd_project.go +++ b/cmd/build/cmd_project.go @@ -19,6 +19,7 @@ import ( "forge.lthn.ai/core/go-build/pkg/build/signing" "forge.lthn.ai/core/go-i18n" "forge.lthn.ai/core/go-io" + coreerr "forge.lthn.ai/core/go-log" ) // runProjectBuild handles the main `core build` command with auto-detection. @@ -29,13 +30,13 @@ func runProjectBuild(ctx context.Context, buildType string, ciMode bool, targets // Get current working directory as project root projectDir, err := os.Getwd() if err != nil { - return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "get working directory"}), err) + return coreerr.E("build.Run", "failed to get working directory", err) } // Load configuration from .core/build.yaml (or defaults) buildCfg, err := build.LoadConfig(fs, projectDir) if err != nil { - return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "load config"}), err) + return coreerr.E("build.Run", "failed to load config", err) } // Detect project type if not specified @@ -48,10 +49,10 @@ func runProjectBuild(ctx context.Context, buildType string, ciMode bool, targets } else { projectType, err = build.PrimaryType(fs, projectDir) if err != nil { - return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "detect project type"}), err) + return coreerr.E("build.Run", "failed to detect project type", err) } if projectType == "" { - return fmt.Errorf("%s", i18n.T("cmd.build.error.no_project_type", map[string]any{"Dir": projectDir})) + return coreerr.E("build.Run", "no buildable project type found in "+projectDir, nil) } } @@ -257,7 +258,7 @@ func runProjectBuild(ctx context.Context, buildType string, ciMode bool, targets // JSON output for CI output, err := json.MarshalIndent(outputArtifacts, "", " ") if err != nil { - return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "marshal artifacts"}), err) + return coreerr.E("build.Run", "failed to marshal artifacts", err) } fmt.Println(string(output)) } else if !verbose { @@ -346,7 +347,7 @@ func parseTargets(targetsFlag string) ([]build.Target, error) { osArch := strings.Split(part, "/") if len(osArch) != 2 { - return nil, fmt.Errorf("%s", i18n.T("cmd.build.error.invalid_target", map[string]any{"Target": part})) + return nil, coreerr.E("build.parseTargets", "invalid target format (expected os/arch): "+part, nil) } targets = append(targets, build.Target{ @@ -356,7 +357,7 @@ func parseTargets(targetsFlag string) ([]build.Target, error) { } if len(targets) == 0 { - return nil, fmt.Errorf("%s", i18n.T("cmd.build.error.no_targets")) + return nil, coreerr.E("build.parseTargets", "no valid targets specified", nil) } return targets, nil @@ -387,10 +388,10 @@ func getBuilder(projectType build.ProjectType) (build.Builder, error) { case build.ProjectTypeCPP: return builders.NewCPPBuilder(), nil case build.ProjectTypeNode: - return nil, fmt.Errorf("%s", i18n.T("cmd.build.error.node_not_implemented")) + return nil, coreerr.E("build.getBuilder", "node.js builder not yet implemented", nil) case build.ProjectTypePHP: - return nil, fmt.Errorf("%s", i18n.T("cmd.build.error.php_not_implemented")) + return nil, coreerr.E("build.getBuilder", "PHP builder not yet implemented", nil) default: - return nil, fmt.Errorf("%s: %s", i18n.T("cmd.build.error.unsupported_type"), projectType) + return nil, coreerr.E("build.getBuilder", "unsupported project type: "+string(projectType), nil) } } diff --git a/cmd/build/cmd_sdk.go b/cmd/build/cmd_sdk.go index 209bdfb..f6031a6 100644 --- a/cmd/build/cmd_sdk.go +++ b/cmd/build/cmd_sdk.go @@ -13,6 +13,7 @@ import ( "forge.lthn.ai/core/go-build/pkg/sdk" "forge.lthn.ai/core/go-i18n" + coreerr "forge.lthn.ai/core/go-log" ) // runBuildSDK handles the `core build sdk` command. @@ -21,7 +22,7 @@ func runBuildSDK(specPath, lang, version string, dryRun bool) error { projectDir, err := os.Getwd() if err != nil { - return fmt.Errorf("%s: %w", i18n.T("common.error.failed", map[string]any{"Action": "get working directory"}), err) + return coreerr.E("build.SDK", "failed to get working directory", err) } // Load config diff --git a/cmd/ci/ci.go b/cmd/ci/ci.go index fa1d40d..5999dc5 100644 --- a/cmd/ci/ci.go +++ b/cmd/ci/ci.go @@ -2,7 +2,6 @@ package ci import ( "context" - "errors" "os" "os/exec" "strings" @@ -10,6 +9,7 @@ import ( "forge.lthn.ai/core/cli/pkg/cli" "forge.lthn.ai/core/go-i18n" "forge.lthn.ai/core/go-build/pkg/release" + coreerr "forge.lthn.ai/core/go-log" ) // Style aliases from shared @@ -127,7 +127,7 @@ func runCIPublish(dryRun bool, version string, draft, prerelease bool) error { cli.Blank() if len(cfg.Publishers) == 0 { - return errors.New(i18n.T("cmd.ci.error.no_publishers")) + return coreerr.E("ci.Publish", i18n.T("cmd.ci.error.no_publishers"), nil) } rel, err := release.Publish(ctx, cfg, dryRun) diff --git a/cmd/sdk/cmd.go b/cmd/sdk/cmd.go index 70ae4f1..c367d9c 100644 --- a/cmd/sdk/cmd.go +++ b/cmd/sdk/cmd.go @@ -8,13 +8,13 @@ package sdkcmd import ( - "errors" "fmt" "os" "forge.lthn.ai/core/go-build/pkg/sdk" "forge.lthn.ai/core/cli/pkg/cli" "forge.lthn.ai/core/go-i18n" + coreerr "forge.lthn.ai/core/go-log" ) func init() { @@ -77,7 +77,7 @@ func AddSDKCommands(root *cli.Command) { func runSDKDiff(basePath, specPath string) error { projectDir, err := os.Getwd() if err != nil { - return fmt.Errorf("%s: %w", i18n.T("i18n.fail.get", "working directory"), err) + return coreerr.E("sdk.Diff", "failed to get working directory", err) } if specPath == "" { @@ -89,7 +89,7 @@ func runSDKDiff(basePath, specPath string) error { } if basePath == "" { - return errors.New(i18n.T("cmd.sdk.diff.error.base_required")) + return coreerr.E("sdk.Diff", i18n.T("cmd.sdk.diff.error.base_required"), nil) } fmt.Printf("%s %s\n", sdkHeaderStyle.Render(i18n.T("cmd.sdk.diff.label")), i18n.ProgressSubject("check", "breaking changes")) @@ -117,7 +117,7 @@ func runSDKDiff(basePath, specPath string) error { func runSDKValidate(specPath string) error { projectDir, err := os.Getwd() if err != nil { - return fmt.Errorf("%s: %w", i18n.T("i18n.fail.get", "working directory"), err) + return coreerr.E("sdk.Validate", "failed to get working directory", err) } s := sdk.New(projectDir, &sdk.Config{Spec: specPath}) diff --git a/pkg/build/archive.go b/pkg/build/archive.go index 75e9561..8916004 100644 --- a/pkg/build/archive.go +++ b/pkg/build/archive.go @@ -6,13 +6,13 @@ import ( "archive/zip" "bytes" "compress/gzip" - "errors" "fmt" "io" "path/filepath" "strings" io_interface "forge.lthn.ai/core/go-io" + coreerr "forge.lthn.ai/core/go-log" "github.com/Snider/Borg/pkg/compress" ) @@ -49,16 +49,16 @@ func ArchiveXZ(fs io_interface.Medium, artifact Artifact) (Artifact, error) { // Returns a new Artifact with Path pointing to the archive. func ArchiveWithFormat(fs io_interface.Medium, artifact Artifact, format ArchiveFormat) (Artifact, error) { if artifact.Path == "" { - return Artifact{}, errors.New("build.Archive: artifact path is empty") + return Artifact{}, coreerr.E("build.Archive", "artifact path is empty", nil) } // Verify the source file exists info, err := fs.Stat(artifact.Path) if err != nil { - return Artifact{}, fmt.Errorf("build.Archive: source file not found: %w", err) + return Artifact{}, coreerr.E("build.Archive", "source file not found", err) } if info.IsDir() { - return Artifact{}, errors.New("build.Archive: source path is a directory, expected file") + return Artifact{}, coreerr.E("build.Archive", "source path is a directory, expected file", nil) } // Determine archive type based on OS and format @@ -81,7 +81,7 @@ func ArchiveWithFormat(fs io_interface.Medium, artifact Artifact, format Archive // Create the archive if err := archiveFunc(fs, artifact.Path, archivePath); err != nil { - return Artifact{}, fmt.Errorf("build.Archive: failed to create archive: %w", err) + return Artifact{}, coreerr.E("build.Archive", "failed to create archive", err) } return Artifact{ @@ -115,7 +115,7 @@ func ArchiveAllWithFormat(fs io_interface.Medium, artifacts []Artifact, format A for _, artifact := range artifacts { arch, err := ArchiveWithFormat(fs, artifact, format) if err != nil { - return archived, fmt.Errorf("build.ArchiveAll: failed to archive %s: %w", artifact.Path, err) + return archived, coreerr.E("build.ArchiveAll", "failed to archive "+artifact.Path, err) } archived = append(archived, arch) } @@ -147,13 +147,13 @@ func createTarXzArchive(fs io_interface.Medium, src, dst string) error { // Open the source file srcFile, err := fs.Open(src) if err != nil { - return fmt.Errorf("failed to open source file: %w", err) + return coreerr.E("build.createTarXzArchive", "failed to open source file", err) } defer func() { _ = srcFile.Close() }() srcInfo, err := srcFile.Stat() if err != nil { - return fmt.Errorf("failed to stat source file: %w", err) + return coreerr.E("build.createTarXzArchive", "failed to stat source file", err) } // Create tar archive in memory @@ -163,37 +163,37 @@ func createTarXzArchive(fs io_interface.Medium, src, dst string) error { // Create tar header header, err := tar.FileInfoHeader(srcInfo, "") if err != nil { - return fmt.Errorf("failed to create tar header: %w", err) + return coreerr.E("build.createTarXzArchive", "failed to create tar header", err) } header.Name = filepath.Base(src) if err := tarWriter.WriteHeader(header); err != nil { - return fmt.Errorf("failed to write tar header: %w", err) + return coreerr.E("build.createTarXzArchive", "failed to write tar header", err) } if _, err := io.Copy(tarWriter, srcFile); err != nil { - return fmt.Errorf("failed to write file content to tar: %w", err) + return coreerr.E("build.createTarXzArchive", "failed to write file content to tar", err) } if err := tarWriter.Close(); err != nil { - return fmt.Errorf("failed to close tar writer: %w", err) + return coreerr.E("build.createTarXzArchive", "failed to close tar writer", err) } // Compress with xz using Borg xzData, err := compress.Compress(tarBuf.Bytes(), "xz") if err != nil { - return fmt.Errorf("failed to compress with xz: %w", err) + return coreerr.E("build.createTarXzArchive", "failed to compress with xz", err) } // Write to destination file dstFile, err := fs.Create(dst) if err != nil { - return fmt.Errorf("failed to create archive file: %w", err) + return coreerr.E("build.createTarXzArchive", "failed to create archive file", err) } defer func() { _ = dstFile.Close() }() if _, err := dstFile.Write(xzData); err != nil { - return fmt.Errorf("failed to write archive file: %w", err) + return coreerr.E("build.createTarXzArchive", "failed to write archive file", err) } return nil @@ -204,19 +204,19 @@ func createTarGzArchive(fs io_interface.Medium, src, dst string) error { // Open the source file srcFile, err := fs.Open(src) if err != nil { - return fmt.Errorf("failed to open source file: %w", err) + return coreerr.E("build.createTarGzArchive", "failed to open source file", err) } defer func() { _ = srcFile.Close() }() srcInfo, err := srcFile.Stat() if err != nil { - return fmt.Errorf("failed to stat source file: %w", err) + return coreerr.E("build.createTarGzArchive", "failed to stat source file", err) } // Create the destination file dstFile, err := fs.Create(dst) if err != nil { - return fmt.Errorf("failed to create archive file: %w", err) + return coreerr.E("build.createTarGzArchive", "failed to create archive file", err) } defer func() { _ = dstFile.Close() }() @@ -231,19 +231,19 @@ func createTarGzArchive(fs io_interface.Medium, src, dst string) error { // Create tar header header, err := tar.FileInfoHeader(srcInfo, "") if err != nil { - return fmt.Errorf("failed to create tar header: %w", err) + return coreerr.E("build.createTarGzArchive", "failed to create tar header", err) } // Use just the filename, not the full path header.Name = filepath.Base(src) // Write header if err := tarWriter.WriteHeader(header); err != nil { - return fmt.Errorf("failed to write tar header: %w", err) + return coreerr.E("build.createTarGzArchive", "failed to write tar header", err) } // Write file content if _, err := io.Copy(tarWriter, srcFile); err != nil { - return fmt.Errorf("failed to write file content to tar: %w", err) + return coreerr.E("build.createTarGzArchive", "failed to write file content to tar", err) } return nil @@ -254,19 +254,19 @@ func createZipArchive(fs io_interface.Medium, src, dst string) error { // Open the source file srcFile, err := fs.Open(src) if err != nil { - return fmt.Errorf("failed to open source file: %w", err) + return coreerr.E("build.createZipArchive", "failed to open source file", err) } defer func() { _ = srcFile.Close() }() srcInfo, err := srcFile.Stat() if err != nil { - return fmt.Errorf("failed to stat source file: %w", err) + return coreerr.E("build.createZipArchive", "failed to stat source file", err) } // Create the destination file dstFile, err := fs.Create(dst) if err != nil { - return fmt.Errorf("failed to create archive file: %w", err) + return coreerr.E("build.createZipArchive", "failed to create archive file", err) } defer func() { _ = dstFile.Close() }() @@ -277,7 +277,7 @@ func createZipArchive(fs io_interface.Medium, src, dst string) error { // Create zip header header, err := zip.FileInfoHeader(srcInfo) if err != nil { - return fmt.Errorf("failed to create zip header: %w", err) + return coreerr.E("build.createZipArchive", "failed to create zip header", err) } // Use just the filename, not the full path header.Name = filepath.Base(src) @@ -286,12 +286,12 @@ func createZipArchive(fs io_interface.Medium, src, dst string) error { // Create file in archive writer, err := zipWriter.CreateHeader(header) if err != nil { - return fmt.Errorf("failed to create zip entry: %w", err) + return coreerr.E("build.createZipArchive", "failed to create zip entry", err) } // Write file content if _, err := io.Copy(writer, srcFile); err != nil { - return fmt.Errorf("failed to write file content to zip: %w", err) + return coreerr.E("build.createZipArchive", "failed to write file content to zip", err) } return nil diff --git a/pkg/build/builders/cpp.go b/pkg/build/builders/cpp.go index 1dd809b..c2ba7fc 100644 --- a/pkg/build/builders/cpp.go +++ b/pkg/build/builders/cpp.go @@ -3,7 +3,6 @@ package builders import ( "context" - "errors" "fmt" "os" "os/exec" @@ -13,6 +12,7 @@ import ( "forge.lthn.ai/core/go-build/pkg/build" "forge.lthn.ai/core/go-io" + coreerr "forge.lthn.ai/core/go-log" ) // CPPBuilder implements the Builder interface for C++ projects using CMake + Conan. @@ -39,7 +39,7 @@ func (b *CPPBuilder) Detect(fs io.Medium, dir string) (bool, error) { // Cross-compilation is handled via Conan profiles specified in .core/build.yaml. func (b *CPPBuilder) Build(ctx context.Context, cfg *build.Config, targets []build.Target) ([]build.Artifact, error) { if cfg == nil { - return nil, errors.New("builders.CPPBuilder.Build: config is nil") + return nil, coreerr.E("CPPBuilder.Build", "config is nil", nil) } // Validate make is available @@ -61,7 +61,7 @@ func (b *CPPBuilder) Build(ctx context.Context, cfg *build.Config, targets []bui for _, target := range targets { built, err := b.buildTarget(ctx, cfg, target) if err != nil { - return artifacts, fmt.Errorf("builders.CPPBuilder.Build: %w", err) + return artifacts, coreerr.E("CPPBuilder.Build", "build failed", err) } artifacts = append(artifacts, built...) } @@ -87,17 +87,17 @@ func (b *CPPBuilder) buildHost(ctx context.Context, cfg *build.Config, target bu // Step 1: Configure (runs conan install + cmake configure) if err := b.runMake(ctx, cfg.ProjectDir, "configure"); err != nil { - return nil, fmt.Errorf("configure failed: %w", err) + return nil, coreerr.E("CPPBuilder.buildHost", "configure failed", err) } // Step 2: Build if err := b.runMake(ctx, cfg.ProjectDir, "build"); err != nil { - return nil, fmt.Errorf("build failed: %w", err) + return nil, coreerr.E("CPPBuilder.buildHost", "build failed", err) } // Step 3: Package if err := b.runMake(ctx, cfg.ProjectDir, "package"); err != nil { - return nil, fmt.Errorf("package failed: %w", err) + return nil, coreerr.E("CPPBuilder.buildHost", "package failed", err) } // Discover artifacts from build/packages/ @@ -110,14 +110,14 @@ func (b *CPPBuilder) buildCross(ctx context.Context, cfg *build.Config, target b // Map target to a Conan profile name profile := b.targetToProfile(target) if profile == "" { - return nil, fmt.Errorf("no Conan profile mapped for target %s/%s", target.OS, target.Arch) + return nil, coreerr.E("CPPBuilder.buildCross", "no Conan profile mapped for target "+target.OS+"/"+target.Arch, nil) } fmt.Printf("Building C++ project for %s/%s (cross: %s)\n", target.OS, target.Arch, profile) // The Makefile exposes each profile as a top-level target if err := b.runMake(ctx, cfg.ProjectDir, profile); err != nil { - return nil, fmt.Errorf("cross-compile for %s failed: %w", profile, err) + return nil, coreerr.E("CPPBuilder.buildCross", "cross-compile for "+profile+" failed", err) } return b.findArtifacts(cfg.FS, cfg.ProjectDir, target) @@ -132,7 +132,7 @@ func (b *CPPBuilder) runMake(ctx context.Context, projectDir string, target stri cmd.Env = os.Environ() if err := cmd.Run(); err != nil { - return fmt.Errorf("make %s: %w", target, err) + return coreerr.E("CPPBuilder.runMake", "make "+target+" failed", err) } return nil } @@ -148,7 +148,7 @@ func (b *CPPBuilder) findArtifacts(fs io.Medium, projectDir string, target build entries, err := fs.List(packagesDir) if err != nil { - return nil, fmt.Errorf("failed to list packages directory: %w", err) + return nil, coreerr.E("CPPBuilder.findArtifacts", "failed to list packages directory", err) } var artifacts []build.Artifact @@ -178,12 +178,12 @@ func (b *CPPBuilder) findBinaries(fs io.Medium, projectDir string, target build. binDir := filepath.Join(projectDir, "build", "release", "src") if !fs.IsDir(binDir) { - return nil, fmt.Errorf("no build output found in %s", binDir) + return nil, coreerr.E("CPPBuilder.findBinaries", "no build output found in "+binDir, nil) } entries, err := fs.List(binDir) if err != nil { - return nil, fmt.Errorf("failed to list build directory: %w", err) + return nil, coreerr.E("CPPBuilder.findBinaries", "failed to list build directory", err) } var artifacts []build.Artifact @@ -245,7 +245,7 @@ func (b *CPPBuilder) targetToProfile(target build.Target) string { // validateMake checks if make is available. func (b *CPPBuilder) validateMake() error { if _, err := exec.LookPath("make"); err != nil { - return errors.New("cpp: make not found. Install build-essential (Linux) or Xcode Command Line Tools (macOS)") + return coreerr.E("CPPBuilder.validateMake", "make not found. Install build-essential (Linux) or Xcode Command Line Tools (macOS)", nil) } return nil } diff --git a/pkg/build/builders/docker.go b/pkg/build/builders/docker.go index 22bd0dd..ba6b35a 100644 --- a/pkg/build/builders/docker.go +++ b/pkg/build/builders/docker.go @@ -3,7 +3,6 @@ package builders import ( "context" - "errors" "fmt" "os" "os/exec" @@ -12,6 +11,7 @@ import ( "forge.lthn.ai/core/go-build/pkg/build" "forge.lthn.ai/core/go-io" + coreerr "forge.lthn.ai/core/go-log" ) // DockerBuilder builds Docker images. @@ -56,7 +56,7 @@ func (b *DockerBuilder) Build(ctx context.Context, cfg *build.Config, targets [] // Validate Dockerfile exists if !cfg.FS.IsFile(dockerfile) { - return nil, fmt.Errorf("docker.Build: Dockerfile not found: %s", dockerfile) + return nil, coreerr.E("DockerBuilder.Build", "Dockerfile not found: "+dockerfile, nil) } // Determine image name @@ -153,7 +153,7 @@ func (b *DockerBuilder) Build(ctx context.Context, cfg *build.Config, targets [] // Create output directory if err := cfg.FS.EnsureDir(cfg.OutputDir); err != nil { - return nil, fmt.Errorf("docker.Build: failed to create output directory: %w", err) + return nil, coreerr.E("DockerBuilder.Build", "failed to create output directory", err) } // Execute build @@ -167,7 +167,7 @@ func (b *DockerBuilder) Build(ctx context.Context, cfg *build.Config, targets [] fmt.Printf(" Tags: %s\n", strings.Join(imageRefs, ", ")) if err := cmd.Run(); err != nil { - return nil, fmt.Errorf("docker.Build: buildx build failed: %w", err) + return nil, coreerr.E("DockerBuilder.Build", "buildx build failed", err) } // Create artifacts for each platform @@ -187,7 +187,7 @@ func (b *DockerBuilder) Build(ctx context.Context, cfg *build.Config, targets [] func (b *DockerBuilder) validateDockerCli() error { cmd := exec.Command("docker", "--version") if err := cmd.Run(); err != nil { - return errors.New("docker: docker CLI not found. Install it from https://docs.docker.com/get-docker/") + return coreerr.E("DockerBuilder.validateDockerCli", "docker CLI not found. Install it from https://docs.docker.com/get-docker/", nil) } return nil } @@ -197,7 +197,7 @@ func (b *DockerBuilder) ensureBuildx(ctx context.Context) error { // Check if buildx is available cmd := exec.CommandContext(ctx, "docker", "buildx", "version") if err := cmd.Run(); err != nil { - return errors.New("docker: buildx is not available. Install it from https://docs.docker.com/buildx/working-with-buildx/") + return coreerr.E("DockerBuilder.ensureBuildx", "buildx is not available. Install it from https://docs.docker.com/buildx/working-with-buildx/", nil) } // Check if we have a builder, create one if not @@ -208,7 +208,7 @@ func (b *DockerBuilder) ensureBuildx(ctx context.Context) error { cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { - return fmt.Errorf("docker: failed to create buildx builder: %w", err) + return coreerr.E("DockerBuilder.ensureBuildx", "failed to create buildx builder", err) } } diff --git a/pkg/build/builders/go.go b/pkg/build/builders/go.go index 87df5bb..a8f32ab 100644 --- a/pkg/build/builders/go.go +++ b/pkg/build/builders/go.go @@ -3,7 +3,6 @@ package builders import ( "context" - "errors" "fmt" "os" "os/exec" @@ -12,6 +11,7 @@ import ( "forge.lthn.ai/core/go-build/pkg/build" "forge.lthn.ai/core/go-io" + coreerr "forge.lthn.ai/core/go-log" ) // GoBuilder implements the Builder interface for Go projects. @@ -38,16 +38,16 @@ func (b *GoBuilder) Detect(fs io.Medium, dir string) (bool, error) { // applies ldflags and trimpath, and runs go build. func (b *GoBuilder) Build(ctx context.Context, cfg *build.Config, targets []build.Target) ([]build.Artifact, error) { if cfg == nil { - return nil, errors.New("builders.GoBuilder.Build: config is nil") + return nil, coreerr.E("GoBuilder.Build", "config is nil", nil) } if len(targets) == 0 { - return nil, errors.New("builders.GoBuilder.Build: no targets specified") + return nil, coreerr.E("GoBuilder.Build", "no targets specified", nil) } // Ensure output directory exists if err := cfg.FS.EnsureDir(cfg.OutputDir); err != nil { - return nil, fmt.Errorf("builders.GoBuilder.Build: failed to create output directory: %w", err) + return nil, coreerr.E("GoBuilder.Build", "failed to create output directory", err) } var artifacts []build.Artifact @@ -55,7 +55,7 @@ func (b *GoBuilder) Build(ctx context.Context, cfg *build.Config, targets []buil for _, target := range targets { artifact, err := b.buildTarget(ctx, cfg, target) if err != nil { - return artifacts, fmt.Errorf("builders.GoBuilder.Build: failed to build %s: %w", target.String(), err) + return artifacts, coreerr.E("GoBuilder.Build", "failed to build "+target.String(), err) } artifacts = append(artifacts, artifact) } @@ -79,7 +79,7 @@ func (b *GoBuilder) buildTarget(ctx context.Context, cfg *build.Config, target b // Create platform-specific output path: output/os_arch/binary platformDir := filepath.Join(cfg.OutputDir, fmt.Sprintf("%s_%s", target.OS, target.Arch)) if err := cfg.FS.EnsureDir(platformDir); err != nil { - return build.Artifact{}, fmt.Errorf("failed to create platform directory: %w", err) + return build.Artifact{}, coreerr.E("GoBuilder.buildTarget", "failed to create platform directory", err) } outputPath := filepath.Join(platformDir, binaryName) @@ -120,7 +120,7 @@ func (b *GoBuilder) buildTarget(ctx context.Context, cfg *build.Config, target b // Capture output for error messages output, err := cmd.CombinedOutput() if err != nil { - return build.Artifact{}, fmt.Errorf("go build failed: %w\nOutput: %s", err, string(output)) + return build.Artifact{}, coreerr.E("GoBuilder.buildTarget", "go build failed: "+string(output), err) } return build.Artifact{ diff --git a/pkg/build/builders/linuxkit.go b/pkg/build/builders/linuxkit.go index 319bd2f..bcc7976 100644 --- a/pkg/build/builders/linuxkit.go +++ b/pkg/build/builders/linuxkit.go @@ -3,7 +3,6 @@ package builders import ( "context" - "errors" "fmt" "os" "os/exec" @@ -12,6 +11,7 @@ import ( "forge.lthn.ai/core/go-build/pkg/build" "forge.lthn.ai/core/go-io" + coreerr "forge.lthn.ai/core/go-log" ) // LinuxKitBuilder builds LinuxKit images. @@ -79,12 +79,12 @@ func (b *LinuxKitBuilder) Build(ctx context.Context, cfg *build.Config, targets } if configPath == "" { - return nil, errors.New("linuxkit.Build: no LinuxKit config file found. Specify with --config or create linuxkit.yml") + return nil, coreerr.E("LinuxKitBuilder.Build", "no LinuxKit config file found. Specify with --config or create linuxkit.yml", nil) } // Validate config file exists if !cfg.FS.IsFile(configPath) { - return nil, fmt.Errorf("linuxkit.Build: config file not found: %s", configPath) + return nil, coreerr.E("LinuxKitBuilder.Build", "config file not found: "+configPath, nil) } // Determine output formats @@ -99,7 +99,7 @@ func (b *LinuxKitBuilder) Build(ctx context.Context, cfg *build.Config, targets outputDir = filepath.Join(cfg.ProjectDir, "dist") } if err := cfg.FS.EnsureDir(outputDir); err != nil { - return nil, fmt.Errorf("linuxkit.Build: failed to create output directory: %w", err) + return nil, coreerr.E("LinuxKitBuilder.Build", "failed to create output directory", err) } // Determine base name from config file or project name @@ -136,7 +136,7 @@ func (b *LinuxKitBuilder) Build(ctx context.Context, cfg *build.Config, targets fmt.Printf("Building LinuxKit image: %s (%s, %s)\n", outputName, format, target.Arch) if err := cmd.Run(); err != nil { - return nil, fmt.Errorf("linuxkit.Build: build failed for %s/%s: %w", target.Arch, format, err) + return nil, coreerr.E("LinuxKitBuilder.Build", "build failed for "+target.Arch+"/"+format, err) } // Determine the actual output file path @@ -147,7 +147,7 @@ func (b *LinuxKitBuilder) Build(ctx context.Context, cfg *build.Config, targets // Try alternate naming conventions artifactPath = b.findArtifact(cfg.FS, outputDir, outputName, format) if artifactPath == "" { - return nil, fmt.Errorf("linuxkit.Build: artifact not found after build: expected %s", b.getArtifactPath(outputDir, outputName, format)) + return nil, coreerr.E("LinuxKitBuilder.Build", "artifact not found after build: expected "+b.getArtifactPath(outputDir, outputName, format), nil) } } @@ -267,5 +267,5 @@ func (b *LinuxKitBuilder) validateLinuxKitCli() error { } } - return errors.New("linuxkit: linuxkit CLI not found. Install with: brew install linuxkit (macOS) or see https://github.com/linuxkit/linuxkit") + return coreerr.E("LinuxKitBuilder.validateLinuxKitCli", "linuxkit CLI not found. Install with: brew install linuxkit (macOS) or see https://github.com/linuxkit/linuxkit", nil) } diff --git a/pkg/build/builders/taskfile.go b/pkg/build/builders/taskfile.go index bbbb14d..ca3e807 100644 --- a/pkg/build/builders/taskfile.go +++ b/pkg/build/builders/taskfile.go @@ -3,7 +3,6 @@ package builders import ( "context" - "errors" "fmt" "os" "os/exec" @@ -12,6 +11,7 @@ import ( "forge.lthn.ai/core/go-build/pkg/build" "forge.lthn.ai/core/go-io" + coreerr "forge.lthn.ai/core/go-log" ) // TaskfileBuilder builds projects using Taskfile (https://taskfile.dev/). @@ -60,7 +60,7 @@ func (b *TaskfileBuilder) Build(ctx context.Context, cfg *build.Config, targets outputDir = filepath.Join(cfg.ProjectDir, "dist") } if err := cfg.FS.EnsureDir(outputDir); err != nil { - return nil, fmt.Errorf("taskfile.Build: failed to create output directory: %w", err) + return nil, coreerr.E("TaskfileBuilder.Build", "failed to create output directory", err) } var artifacts []build.Artifact @@ -142,7 +142,7 @@ func (b *TaskfileBuilder) runTask(ctx context.Context, cfg *build.Config, goos, } if err := cmd.Run(); err != nil { - return fmt.Errorf("taskfile.Build: task build failed: %w", err) + return coreerr.E("TaskfileBuilder.runTask", "task build failed", err) } return nil @@ -272,5 +272,5 @@ func (b *TaskfileBuilder) validateTaskCli() error { } } - return errors.New("taskfile: task CLI not found. Install with: brew install go-task (macOS), go install github.com/go-task/task/v3/cmd/task@latest, or see https://taskfile.dev/installation/") + return coreerr.E("TaskfileBuilder.validateTaskCli", "task CLI not found. Install with: brew install go-task (macOS), go install github.com/go-task/task/v3/cmd/task@latest, or see https://taskfile.dev/installation/", nil) } diff --git a/pkg/build/builders/wails.go b/pkg/build/builders/wails.go index 59685cd..e58bfa2 100644 --- a/pkg/build/builders/wails.go +++ b/pkg/build/builders/wails.go @@ -3,7 +3,6 @@ package builders import ( "context" - "errors" "fmt" "os/exec" "path/filepath" @@ -11,6 +10,7 @@ import ( "forge.lthn.ai/core/go-build/pkg/build" "forge.lthn.ai/core/go-io" + coreerr "forge.lthn.ai/core/go-log" ) // WailsBuilder implements the Builder interface for Wails v3 projects. @@ -38,11 +38,11 @@ func (b *WailsBuilder) Detect(fs io.Medium, dir string) (bool, error) { // - Wails v2: Uses 'wails build' command func (b *WailsBuilder) Build(ctx context.Context, cfg *build.Config, targets []build.Target) ([]build.Artifact, error) { if cfg == nil { - return nil, errors.New("builders.WailsBuilder.Build: config is nil") + return nil, coreerr.E("WailsBuilder.Build", "config is nil", nil) } if len(targets) == 0 { - return nil, errors.New("builders.WailsBuilder.Build: no targets specified") + return nil, coreerr.E("WailsBuilder.Build", "no targets specified", nil) } // Detect Wails version @@ -63,7 +63,7 @@ func (b *WailsBuilder) Build(ctx context.Context, cfg *build.Config, targets []b // Wails v2 strategy: Use 'wails build' // Ensure output directory exists if err := cfg.FS.EnsureDir(cfg.OutputDir); err != nil { - return nil, fmt.Errorf("builders.WailsBuilder.Build: failed to create output directory: %w", err) + return nil, coreerr.E("WailsBuilder.Build", "failed to create output directory", err) } // Note: Wails v2 handles frontend installation/building automatically via wails.json config @@ -73,7 +73,7 @@ func (b *WailsBuilder) Build(ctx context.Context, cfg *build.Config, targets []b for _, target := range targets { artifact, err := b.buildV2Target(ctx, cfg, target) if err != nil { - return artifacts, fmt.Errorf("builders.WailsBuilder.Build: failed to build %s: %w", target.String(), err) + return artifacts, coreerr.E("WailsBuilder.Build", "failed to build "+target.String(), err) } artifacts = append(artifacts, artifact) } @@ -117,7 +117,7 @@ func (b *WailsBuilder) buildV2Target(ctx context.Context, cfg *build.Config, tar // Capture output for error messages output, err := cmd.CombinedOutput() if err != nil { - return build.Artifact{}, fmt.Errorf("wails build failed: %w\nOutput: %s", err, string(output)) + return build.Artifact{}, coreerr.E("WailsBuilder.buildV2Target", "wails build failed: "+string(output), err) } // Wails v2 typically outputs to build/bin @@ -129,14 +129,14 @@ func (b *WailsBuilder) buildV2Target(ctx context.Context, cfg *build.Config, tar // Find the artifact in Wails output dir sourcePath, err := b.findArtifact(cfg.FS, wailsOutputDir, binaryName, target) if err != nil { - return build.Artifact{}, fmt.Errorf("failed to find Wails v2 build artifact: %w", err) + return build.Artifact{}, coreerr.E("WailsBuilder.buildV2Target", "failed to find Wails v2 build artifact", err) } // Move/Copy to our output dir // Create platform specific dir in our output platformDir := filepath.Join(cfg.OutputDir, fmt.Sprintf("%s_%s", target.OS, target.Arch)) if err := cfg.FS.EnsureDir(platformDir); err != nil { - return build.Artifact{}, fmt.Errorf("failed to create output dir: %w", err) + return build.Artifact{}, coreerr.E("WailsBuilder.buildV2Target", "failed to create output dir", err) } destPath := filepath.Join(platformDir, filepath.Base(sourcePath)) @@ -193,7 +193,7 @@ func (b *WailsBuilder) findArtifact(fs io.Medium, platformDir, binaryName string // If no specific candidate found, try to find any executable or package in the directory entries, err := fs.List(platformDir) if err != nil { - return "", fmt.Errorf("failed to read platform directory: %w", err) + return "", coreerr.E("WailsBuilder.findArtifact", "failed to read platform directory", err) } for _, entry := range entries { @@ -220,7 +220,7 @@ func (b *WailsBuilder) findArtifact(fs io.Medium, platformDir, binaryName string } } - return "", fmt.Errorf("no artifact found in %s", platformDir) + return "", coreerr.E("WailsBuilder.findArtifact", "no artifact found in "+platformDir, nil) } // detectPackageManager detects the frontend package manager based on lock files. diff --git a/pkg/build/checksum.go b/pkg/build/checksum.go index b5ef209..4d060a0 100644 --- a/pkg/build/checksum.go +++ b/pkg/build/checksum.go @@ -4,7 +4,6 @@ package build import ( "crypto/sha256" "encoding/hex" - "errors" "fmt" "io" "path/filepath" @@ -13,25 +12,26 @@ import ( "strings" io_interface "forge.lthn.ai/core/go-io" + coreerr "forge.lthn.ai/core/go-log" ) // Checksum computes SHA256 for an artifact and returns the artifact with the Checksum field filled. func Checksum(fs io_interface.Medium, artifact Artifact) (Artifact, error) { if artifact.Path == "" { - return Artifact{}, errors.New("build.Checksum: artifact path is empty") + return Artifact{}, coreerr.E("build.Checksum", "artifact path is empty", nil) } // Open the file file, err := fs.Open(artifact.Path) if err != nil { - return Artifact{}, fmt.Errorf("build.Checksum: failed to open file: %w", err) + return Artifact{}, coreerr.E("build.Checksum", "failed to open file", err) } defer func() { _ = file.Close() }() // Compute SHA256 hash hasher := sha256.New() if _, err := io.Copy(hasher, file); err != nil { - return Artifact{}, fmt.Errorf("build.Checksum: failed to hash file: %w", err) + return Artifact{}, coreerr.E("build.Checksum", "failed to hash file", err) } checksum := hex.EncodeToString(hasher.Sum(nil)) @@ -55,7 +55,7 @@ func ChecksumAll(fs io_interface.Medium, artifacts []Artifact) ([]Artifact, erro for _, artifact := range artifacts { cs, err := Checksum(fs, artifact) if err != nil { - return checksummed, fmt.Errorf("build.ChecksumAll: failed to checksum %s: %w", artifact.Path, err) + return checksummed, coreerr.E("build.ChecksumAll", "failed to checksum "+artifact.Path, err) } checksummed = append(checksummed, cs) } @@ -79,7 +79,7 @@ func WriteChecksumFile(fs io_interface.Medium, artifacts []Artifact, path string var lines []string for _, artifact := range artifacts { if artifact.Checksum == "" { - return fmt.Errorf("build.WriteChecksumFile: artifact %s has no checksum", artifact.Path) + return coreerr.E("build.WriteChecksumFile", "artifact "+artifact.Path+" has no checksum", nil) } filename := filepath.Base(artifact.Path) lines = append(lines, fmt.Sprintf("%s %s", artifact.Checksum, filename)) @@ -92,7 +92,7 @@ func WriteChecksumFile(fs io_interface.Medium, artifacts []Artifact, path string // Write the file using the medium (which handles directory creation in Write) if err := fs.Write(path, content); err != nil { - return fmt.Errorf("build.WriteChecksumFile: failed to write file: %w", err) + return coreerr.E("build.WriteChecksumFile", "failed to write file", err) } return nil diff --git a/pkg/build/config.go b/pkg/build/config.go index 6cffcf7..6c163ae 100644 --- a/pkg/build/config.go +++ b/pkg/build/config.go @@ -3,13 +3,13 @@ package build import ( - "fmt" "iter" "os" "path/filepath" "forge.lthn.ai/core/go-build/pkg/build/signing" "forge.lthn.ai/core/go-io" + coreerr "forge.lthn.ai/core/go-log" "gopkg.in/yaml.v3" ) @@ -80,13 +80,13 @@ func LoadConfig(fs io.Medium, dir string) (*BuildConfig, error) { if os.IsNotExist(err) { return DefaultConfig(), nil } - return nil, fmt.Errorf("build.LoadConfig: failed to read config file: %w", err) + return nil, coreerr.E("build.LoadConfig", "failed to read config file", err) } var cfg BuildConfig data := []byte(content) if err := yaml.Unmarshal(data, &cfg); err != nil { - return nil, fmt.Errorf("build.LoadConfig: failed to parse config file: %w", err) + return nil, coreerr.E("build.LoadConfig", "failed to parse config file", err) } // Apply defaults for any missing fields diff --git a/pkg/build/signing/codesign.go b/pkg/build/signing/codesign.go index 8e5f2bf..c8aeaf1 100644 --- a/pkg/build/signing/codesign.go +++ b/pkg/build/signing/codesign.go @@ -2,12 +2,11 @@ package signing import ( "context" - "errors" - "fmt" "os/exec" "runtime" "forge.lthn.ai/core/go-io" + coreerr "forge.lthn.ai/core/go-log" ) // MacOSSigner signs binaries using macOS codesign. @@ -43,7 +42,7 @@ func (s *MacOSSigner) Available() bool { // Sign codesigns a binary with hardened runtime. func (s *MacOSSigner) Sign(ctx context.Context, fs io.Medium, binary string) error { if !s.Available() { - return errors.New("codesign.Sign: codesign not available") + return coreerr.E("codesign.Sign", "codesign not available", nil) } cmd := exec.CommandContext(ctx, "codesign", @@ -56,7 +55,7 @@ func (s *MacOSSigner) Sign(ctx context.Context, fs io.Medium, binary string) err output, err := cmd.CombinedOutput() if err != nil { - return fmt.Errorf("codesign.Sign: %w\nOutput: %s", err, string(output)) + return coreerr.E("codesign.Sign", string(output), err) } return nil @@ -66,14 +65,14 @@ func (s *MacOSSigner) Sign(ctx context.Context, fs io.Medium, binary string) err // This blocks until Apple responds (typically 1-5 minutes). func (s *MacOSSigner) Notarize(ctx context.Context, fs io.Medium, binary string) error { if s.config.AppleID == "" || s.config.TeamID == "" || s.config.AppPassword == "" { - return errors.New("codesign.Notarize: missing Apple credentials (apple_id, team_id, app_password)") + return coreerr.E("codesign.Notarize", "missing Apple credentials (apple_id, team_id, app_password)", nil) } // Create ZIP for submission zipPath := binary + ".zip" zipCmd := exec.CommandContext(ctx, "zip", "-j", zipPath, binary) if output, err := zipCmd.CombinedOutput(); err != nil { - return fmt.Errorf("codesign.Notarize: failed to create zip: %w\nOutput: %s", err, string(output)) + return coreerr.E("codesign.Notarize", "failed to create zip: "+string(output), err) } defer func() { _ = fs.Delete(zipPath) }() @@ -86,13 +85,13 @@ func (s *MacOSSigner) Notarize(ctx context.Context, fs io.Medium, binary string) "--wait", ) if output, err := submitCmd.CombinedOutput(); err != nil { - return fmt.Errorf("codesign.Notarize: notarization failed: %w\nOutput: %s", err, string(output)) + return coreerr.E("codesign.Notarize", "notarization failed: "+string(output), err) } // Staple the ticket stapleCmd := exec.CommandContext(ctx, "xcrun", "stapler", "staple", binary) if output, err := stapleCmd.CombinedOutput(); err != nil { - return fmt.Errorf("codesign.Notarize: failed to staple: %w\nOutput: %s", err, string(output)) + return coreerr.E("codesign.Notarize", "failed to staple: "+string(output), err) } return nil diff --git a/pkg/build/signing/gpg.go b/pkg/build/signing/gpg.go index 14632c3..4170810 100644 --- a/pkg/build/signing/gpg.go +++ b/pkg/build/signing/gpg.go @@ -2,11 +2,10 @@ package signing import ( "context" - "errors" - "fmt" "os/exec" "forge.lthn.ai/core/go-io" + coreerr "forge.lthn.ai/core/go-log" ) // GPGSigner signs files using GPG. @@ -40,7 +39,7 @@ func (s *GPGSigner) Available() bool { // For file.txt, creates file.txt.asc func (s *GPGSigner) Sign(ctx context.Context, fs io.Medium, file string) error { if !s.Available() { - return errors.New("gpg.Sign: gpg not available or key not configured") + return coreerr.E("gpg.Sign", "gpg not available or key not configured", nil) } cmd := exec.CommandContext(ctx, "gpg", @@ -53,7 +52,7 @@ func (s *GPGSigner) Sign(ctx context.Context, fs io.Medium, file string) error { output, err := cmd.CombinedOutput() if err != nil { - return fmt.Errorf("gpg.Sign: %w\nOutput: %s", err, string(output)) + return coreerr.E("gpg.Sign", string(output), err) } return nil diff --git a/pkg/build/signing/sign.go b/pkg/build/signing/sign.go index 9c7d96e..3e17a29 100644 --- a/pkg/build/signing/sign.go +++ b/pkg/build/signing/sign.go @@ -2,11 +2,11 @@ package signing import ( "context" - "errors" "fmt" "runtime" "forge.lthn.ai/core/go-io" + coreerr "forge.lthn.ai/core/go-log" ) // Artifact represents a build output that can be signed. @@ -41,7 +41,7 @@ func SignBinaries(ctx context.Context, fs io.Medium, cfg SignConfig, artifacts [ fmt.Printf(" Signing %s...\n", artifact.Path) if err := signer.Sign(ctx, fs, artifact.Path); err != nil { - return fmt.Errorf("failed to sign %s: %w", artifact.Path, err) + return coreerr.E("signing.SignBinaries", "failed to sign "+artifact.Path, err) } } @@ -60,7 +60,7 @@ func NotarizeBinaries(ctx context.Context, fs io.Medium, cfg SignConfig, artifac signer := NewMacOSSigner(cfg.MacOS) if !signer.Available() { - return errors.New("notarization requested but codesign not available") + return coreerr.E("signing.NotarizeBinaries", "notarization requested but codesign not available", nil) } for _, artifact := range artifacts { @@ -70,7 +70,7 @@ func NotarizeBinaries(ctx context.Context, fs io.Medium, cfg SignConfig, artifac fmt.Printf(" Notarizing %s (this may take a few minutes)...\n", artifact.Path) if err := signer.Notarize(ctx, fs, artifact.Path); err != nil { - return fmt.Errorf("failed to notarize %s: %w", artifact.Path, err) + return coreerr.E("signing.NotarizeBinaries", "failed to notarize "+artifact.Path, err) } } @@ -90,7 +90,7 @@ func SignChecksums(ctx context.Context, fs io.Medium, cfg SignConfig, checksumFi fmt.Printf(" Signing %s with GPG...\n", checksumFile) if err := signer.Sign(ctx, fs, checksumFile); err != nil { - return fmt.Errorf("failed to sign checksums: %w", err) + return coreerr.E("signing.SignChecksums", "failed to sign checksums", err) } return nil diff --git a/pkg/release/changelog.go b/pkg/release/changelog.go index deba6fa..c4a308e 100644 --- a/pkg/release/changelog.go +++ b/pkg/release/changelog.go @@ -10,6 +10,7 @@ import ( "slices" "strings" + coreerr "forge.lthn.ai/core/go-log" "golang.org/x/text/cases" "golang.org/x/text/language" ) @@ -79,7 +80,7 @@ func Generate(dir, fromRef, toRef string) (string, error) { // Get commits between refs commits, err := getCommits(dir, fromRef, toRef) if err != nil { - return "", fmt.Errorf("changelog.Generate: failed to get commits: %w", err) + return "", coreerr.E("changelog.Generate", "failed to get commits", err) } // Parse conventional commits @@ -114,7 +115,7 @@ func GenerateWithConfig(dir, fromRef, toRef string, cfg *ChangelogConfig) (strin // Get commits between refs commits, err := getCommits(dir, fromRef, toRef) if err != nil { - return "", fmt.Errorf("changelog.GenerateWithConfig: failed to get commits: %w", err) + return "", coreerr.E("changelog.GenerateWithConfig", "failed to get commits", err) } // Build include/exclude sets diff --git a/pkg/release/config.go b/pkg/release/config.go index 3ecc043..c7718f9 100644 --- a/pkg/release/config.go +++ b/pkg/release/config.go @@ -2,12 +2,12 @@ package release import ( - "fmt" "iter" "os" "path/filepath" "forge.lthn.ai/core/go-io" + coreerr "forge.lthn.ai/core/go-log" "gopkg.in/yaml.v3" ) @@ -187,7 +187,7 @@ func LoadConfig(dir string) (*Config, error) { // Convert to absolute path for io.Local absPath, err := filepath.Abs(configPath) if err != nil { - return nil, fmt.Errorf("release.LoadConfig: failed to resolve path: %w", err) + return nil, coreerr.E("release.LoadConfig", "failed to resolve path", err) } content, err := io.Local.Read(absPath) @@ -197,12 +197,12 @@ func LoadConfig(dir string) (*Config, error) { cfg.projectDir = dir return cfg, nil } - return nil, fmt.Errorf("release.LoadConfig: failed to read config file: %w", err) + return nil, coreerr.E("release.LoadConfig", "failed to read config file", err) } var cfg Config if err := yaml.Unmarshal([]byte(content), &cfg); err != nil { - return nil, fmt.Errorf("release.LoadConfig: failed to parse config file: %w", err) + return nil, coreerr.E("release.LoadConfig", "failed to parse config file", err) } // Apply defaults for any missing fields @@ -306,22 +306,22 @@ func WriteConfig(cfg *Config, dir string) error { // Convert to absolute path for io.Local absPath, err := filepath.Abs(configPath) if err != nil { - return fmt.Errorf("release.WriteConfig: failed to resolve path: %w", err) + return coreerr.E("release.WriteConfig", "failed to resolve path", err) } // Ensure directory exists configDir := filepath.Dir(absPath) if err := io.Local.EnsureDir(configDir); err != nil { - return fmt.Errorf("release.WriteConfig: failed to create directory: %w", err) + return coreerr.E("release.WriteConfig", "failed to create directory", err) } data, err := yaml.Marshal(cfg) if err != nil { - return fmt.Errorf("release.WriteConfig: failed to marshal config: %w", err) + return coreerr.E("release.WriteConfig", "failed to marshal config", err) } if err := io.Local.Write(absPath, string(data)); err != nil { - return fmt.Errorf("release.WriteConfig: failed to write config file: %w", err) + return coreerr.E("release.WriteConfig", "failed to write config file", err) } return nil diff --git a/pkg/release/publishers/chocolatey.go b/pkg/release/publishers/chocolatey.go index 4e88cd4..04f1a25 100644 --- a/pkg/release/publishers/chocolatey.go +++ b/pkg/release/publishers/chocolatey.go @@ -5,7 +5,6 @@ import ( "bytes" "context" "embed" - "errors" "fmt" "os" "os/exec" @@ -16,6 +15,7 @@ import ( "forge.lthn.ai/core/go-build/pkg/build" "forge.lthn.ai/core/go-i18n" "forge.lthn.ai/core/go-io" + coreerr "forge.lthn.ai/core/go-log" ) //go:embed templates/chocolatey/*.tmpl templates/chocolatey/tools/*.tmpl @@ -55,7 +55,7 @@ func (p *ChocolateyPublisher) Publish(ctx context.Context, release *Release, pub if repo == "" { detectedRepo, err := detectRepository(release.ProjectDir) if err != nil { - return fmt.Errorf("chocolatey.Publish: could not determine repository: %w", err) + return coreerr.E("chocolatey.Publish", "could not determine repository", err) } repo = detectedRepo } @@ -151,7 +151,7 @@ func (p *ChocolateyPublisher) dryRunPublish(m io.Medium, data chocolateyTemplate nuspec, err := p.renderTemplate(m, "templates/chocolatey/package.nuspec.tmpl", data) if err != nil { - return fmt.Errorf("chocolatey.dryRunPublish: %w", err) + return coreerr.E("chocolatey.dryRunPublish", "failed to render nuspec", err) } fmt.Println("Generated package.nuspec:") fmt.Println("---") @@ -161,7 +161,7 @@ func (p *ChocolateyPublisher) dryRunPublish(m io.Medium, data chocolateyTemplate install, err := p.renderTemplate(m, "templates/chocolatey/tools/chocolateyinstall.ps1.tmpl", data) if err != nil { - return fmt.Errorf("chocolatey.dryRunPublish: %w", err) + return coreerr.E("chocolatey.dryRunPublish", "failed to render install script", err) } fmt.Println("Generated chocolateyinstall.ps1:") fmt.Println("---") @@ -183,12 +183,12 @@ func (p *ChocolateyPublisher) dryRunPublish(m io.Medium, data chocolateyTemplate func (p *ChocolateyPublisher) executePublish(ctx context.Context, projectDir string, data chocolateyTemplateData, cfg ChocolateyConfig, release *Release) error { nuspec, err := p.renderTemplate(release.FS, "templates/chocolatey/package.nuspec.tmpl", data) if err != nil { - return fmt.Errorf("chocolatey.Publish: failed to render nuspec: %w", err) + return coreerr.E("chocolatey.Publish", "failed to render nuspec", err) } install, err := p.renderTemplate(release.FS, "templates/chocolatey/tools/chocolateyinstall.ps1.tmpl", data) if err != nil { - return fmt.Errorf("chocolatey.Publish: failed to render install script: %w", err) + return coreerr.E("chocolatey.Publish", "failed to render install script", err) } // Create package directory @@ -202,18 +202,18 @@ func (p *ChocolateyPublisher) executePublish(ctx context.Context, projectDir str toolsDir := filepath.Join(output, "tools") if err := release.FS.EnsureDir(toolsDir); err != nil { - return fmt.Errorf("chocolatey.Publish: failed to create output directory: %w", err) + return coreerr.E("chocolatey.Publish", "failed to create output directory", err) } // Write files nuspecPath := filepath.Join(output, fmt.Sprintf("%s.nuspec", data.PackageName)) if err := release.FS.Write(nuspecPath, nuspec); err != nil { - return fmt.Errorf("chocolatey.Publish: failed to write nuspec: %w", err) + return coreerr.E("chocolatey.Publish", "failed to write nuspec", err) } installPath := filepath.Join(toolsDir, "chocolateyinstall.ps1") if err := release.FS.Write(installPath, install); err != nil { - return fmt.Errorf("chocolatey.Publish: failed to write install script: %w", err) + return coreerr.E("chocolatey.Publish", "failed to write install script", err) } fmt.Printf("Wrote Chocolatey package files: %s\n", output) @@ -232,7 +232,7 @@ func (p *ChocolateyPublisher) pushToChocolatey(ctx context.Context, packageDir s // Check for CHOCOLATEY_API_KEY apiKey := os.Getenv("CHOCOLATEY_API_KEY") if apiKey == "" { - return errors.New("chocolatey.Publish: CHOCOLATEY_API_KEY environment variable is required for push") + return coreerr.E("chocolatey.Publish", "CHOCOLATEY_API_KEY environment variable is required for push", nil) } // Pack the package @@ -242,7 +242,7 @@ func (p *ChocolateyPublisher) pushToChocolatey(ctx context.Context, packageDir s cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { - return fmt.Errorf("chocolatey.Publish: choco pack failed: %w", err) + return coreerr.E("chocolatey.Publish", "choco pack failed", err) } // Push the package @@ -250,7 +250,7 @@ func (p *ChocolateyPublisher) pushToChocolatey(ctx context.Context, packageDir s cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { - return fmt.Errorf("chocolatey.Publish: choco push failed: %w", err) + return coreerr.E("chocolatey.Publish", "choco push failed", err) } fmt.Printf("Published to Chocolatey: https://community.chocolatey.org/packages/%s\n", data.PackageName) @@ -274,18 +274,18 @@ func (p *ChocolateyPublisher) renderTemplate(m io.Medium, name string, data choc if content == nil { content, err = chocolateyTemplates.ReadFile(name) if err != nil { - return "", fmt.Errorf("failed to read template %s: %w", name, err) + return "", coreerr.E("chocolatey.renderTemplate", "failed to read template "+name, err) } } tmpl, err := template.New(filepath.Base(name)).Parse(string(content)) if err != nil { - return "", fmt.Errorf("failed to parse template %s: %w", name, err) + return "", coreerr.E("chocolatey.renderTemplate", "failed to parse template "+name, err) } var buf bytes.Buffer if err := tmpl.Execute(&buf, data); err != nil { - return "", fmt.Errorf("failed to execute template %s: %w", name, err) + return "", coreerr.E("chocolatey.renderTemplate", "failed to execute template "+name, err) } return buf.String(), nil diff --git a/pkg/release/publishers/docker.go b/pkg/release/publishers/docker.go index d04e039..21fa99c 100644 --- a/pkg/release/publishers/docker.go +++ b/pkg/release/publishers/docker.go @@ -3,12 +3,13 @@ package publishers import ( "context" - "errors" "fmt" "os" "os/exec" "path/filepath" "strings" + + coreerr "forge.lthn.ai/core/go-log" ) // DockerConfig holds configuration for the Docker publisher. @@ -52,7 +53,7 @@ func (p *DockerPublisher) Publish(ctx context.Context, release *Release, pubCfg // Validate Dockerfile exists if !release.FS.Exists(dockerCfg.Dockerfile) { - return fmt.Errorf("docker.Publish: Dockerfile not found: %s", dockerCfg.Dockerfile) + return coreerr.E("docker.Publish", "Dockerfile not found: "+dockerCfg.Dockerfile, nil) } if dryRun { @@ -180,7 +181,7 @@ func (p *DockerPublisher) executePublish(ctx context.Context, release *Release, fmt.Printf("Building and pushing Docker image: %s\n", cfg.Image) if err := cmd.Run(); err != nil { - return fmt.Errorf("docker.Publish: buildx build failed: %w", err) + return coreerr.E("docker.Publish", "buildx build failed", err) } return nil @@ -251,7 +252,7 @@ func (p *DockerPublisher) ensureBuildx(ctx context.Context) error { // Check if buildx is available cmd := exec.CommandContext(ctx, "docker", "buildx", "version") if err := cmd.Run(); err != nil { - return errors.New("docker: buildx is not available. Install it from https://docs.docker.com/buildx/working-with-buildx/") + return coreerr.E("docker.ensureBuildx", "buildx is not available. Install it from https://docs.docker.com/buildx/working-with-buildx/", nil) } // Check if we have a builder, create one if not @@ -262,7 +263,7 @@ func (p *DockerPublisher) ensureBuildx(ctx context.Context) error { cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { - return fmt.Errorf("docker: failed to create buildx builder: %w", err) + return coreerr.E("docker.ensureBuildx", "failed to create buildx builder", err) } } @@ -273,7 +274,7 @@ func (p *DockerPublisher) ensureBuildx(ctx context.Context) error { func validateDockerCli() error { cmd := exec.Command("docker", "--version") if err := cmd.Run(); err != nil { - return errors.New("docker: docker CLI not found. Install it from https://docs.docker.com/get-docker/") + return coreerr.E("docker.validateDockerCli", "docker CLI not found. Install it from https://docs.docker.com/get-docker/", nil) } return nil } diff --git a/pkg/release/publishers/github.go b/pkg/release/publishers/github.go index fc367e8..0c02fef 100644 --- a/pkg/release/publishers/github.go +++ b/pkg/release/publishers/github.go @@ -3,12 +3,13 @@ package publishers import ( "context" - "errors" "fmt" "os" "os/exec" "path/filepath" "strings" + + coreerr "forge.lthn.ai/core/go-log" ) // GitHubPublisher publishes releases to GitHub using the gh CLI. @@ -36,7 +37,7 @@ func (p *GitHubPublisher) Publish(ctx context.Context, release *Release, pubCfg // Try to detect from git remote detectedRepo, err := detectRepository(release.ProjectDir) if err != nil { - return fmt.Errorf("github.Publish: could not determine repository: %w", err) + return coreerr.E("github.Publish", "could not determine repository", err) } repo = detectedRepo } @@ -104,7 +105,7 @@ func (p *GitHubPublisher) executePublish(ctx context.Context, release *Release, cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { - return fmt.Errorf("github.Publish: gh release create failed: %w", err) + return coreerr.E("github.Publish", "gh release create failed", err) } return nil @@ -147,18 +148,18 @@ func validateGhCli() error { // Check if gh is installed cmd := exec.Command("gh", "--version") if err := cmd.Run(); err != nil { - return errors.New("github: gh CLI not found. Install it from https://cli.github.com") + return coreerr.E("github.validateGhCli", "gh CLI not found. Install it from https://cli.github.com", nil) } // Check if authenticated cmd = exec.Command("gh", "auth", "status") output, err := cmd.CombinedOutput() if err != nil { - return errors.New("github: not authenticated with gh CLI. Run 'gh auth login' first") + return coreerr.E("github.validateGhCli", "not authenticated with gh CLI. Run 'gh auth login' first", nil) } if !strings.Contains(string(output), "Logged in") { - return errors.New("github: not authenticated with gh CLI. Run 'gh auth login' first") + return coreerr.E("github.validateGhCli", "not authenticated with gh CLI. Run 'gh auth login' first", nil) } return nil @@ -170,7 +171,7 @@ func detectRepository(dir string) (string, error) { cmd.Dir = dir output, err := cmd.Output() if err != nil { - return "", fmt.Errorf("failed to get git remote: %w", err) + return "", coreerr.E("github.detectRepository", "failed to get git remote", err) } url := strings.TrimSpace(string(output)) @@ -197,7 +198,7 @@ func parseGitHubRepo(url string) (string, error) { return repo, nil } - return "", fmt.Errorf("not a GitHub URL: %s", url) + return "", coreerr.E("github.parseGitHubRepo", "not a GitHub URL: "+url, nil) } // UploadArtifact uploads a single artifact to an existing release. @@ -208,7 +209,7 @@ func UploadArtifact(ctx context.Context, repo, version, artifactPath string) err cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { - return fmt.Errorf("github.UploadArtifact: failed to upload %s: %w", artifactPath, err) + return coreerr.E("github.UploadArtifact", "failed to upload "+artifactPath, err) } return nil @@ -221,7 +222,7 @@ func DeleteRelease(ctx context.Context, repo, version string) error { cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { - return fmt.Errorf("github.DeleteRelease: failed to delete %s: %w", version, err) + return coreerr.E("github.DeleteRelease", "failed to delete "+version, err) } return nil diff --git a/pkg/release/publishers/linuxkit.go b/pkg/release/publishers/linuxkit.go index 1af2601..bddf327 100644 --- a/pkg/release/publishers/linuxkit.go +++ b/pkg/release/publishers/linuxkit.go @@ -3,12 +3,13 @@ package publishers import ( "context" - "errors" "fmt" "os" "os/exec" "path/filepath" "strings" + + coreerr "forge.lthn.ai/core/go-log" ) // LinuxKitConfig holds configuration for the LinuxKit publisher. @@ -49,10 +50,10 @@ func (p *LinuxKitPublisher) Publish(ctx context.Context, release *Release, pubCf // Validate config file exists if release.FS == nil { - return errors.New("linuxkit.Publish: release filesystem (FS) is nil") + return coreerr.E("linuxkit.Publish", "release filesystem (FS) is nil", nil) } if !release.FS.Exists(lkCfg.Config) { - return fmt.Errorf("linuxkit.Publish: config file not found: %s", lkCfg.Config) + return coreerr.E("linuxkit.Publish", "config file not found: "+lkCfg.Config, nil) } // Determine repository for artifact upload @@ -63,7 +64,7 @@ func (p *LinuxKitPublisher) Publish(ctx context.Context, release *Release, pubCf if repo == "" { detectedRepo, err := detectRepository(release.ProjectDir) if err != nil { - return fmt.Errorf("linuxkit.Publish: could not determine repository: %w", err) + return coreerr.E("linuxkit.Publish", "could not determine repository", err) } repo = detectedRepo } @@ -174,7 +175,7 @@ func (p *LinuxKitPublisher) executePublish(ctx context.Context, release *Release // Create output directory if err := release.FS.EnsureDir(outputDir); err != nil { - return fmt.Errorf("linuxkit.Publish: failed to create output directory: %w", err) + return coreerr.E("linuxkit.Publish", "failed to create output directory", err) } baseName := p.buildBaseName(release.Version) @@ -200,7 +201,7 @@ func (p *LinuxKitPublisher) executePublish(ctx context.Context, release *Release fmt.Printf("Building LinuxKit image: %s (%s)\n", outputName, format) if err := cmd.Run(); err != nil { - return fmt.Errorf("linuxkit.Publish: build failed for %s/%s: %w", platform, format, err) + return coreerr.E("linuxkit.Publish", "build failed for "+platform+"/"+format, err) } // Track artifact for upload @@ -212,11 +213,11 @@ func (p *LinuxKitPublisher) executePublish(ctx context.Context, release *Release // Upload artifacts to GitHub release for _, artifactPath := range artifacts { if !release.FS.Exists(artifactPath) { - return fmt.Errorf("linuxkit.Publish: artifact not found after build: %s", artifactPath) + return coreerr.E("linuxkit.Publish", "artifact not found after build: "+artifactPath, nil) } if err := UploadArtifact(ctx, repo, release.Version, artifactPath); err != nil { - return fmt.Errorf("linuxkit.Publish: failed to upload %s: %w", filepath.Base(artifactPath), err) + return coreerr.E("linuxkit.Publish", "failed to upload "+filepath.Base(artifactPath), err) } // Print helpful usage info for docker format @@ -298,7 +299,7 @@ func (p *LinuxKitPublisher) getFormatExtension(format string) string { func validateLinuxKitCli() error { cmd := exec.Command("linuxkit", "version") if err := cmd.Run(); err != nil { - return errors.New("linuxkit: linuxkit CLI not found. Install it from https://github.com/linuxkit/linuxkit") + return coreerr.E("linuxkit.validateLinuxKitCli", "linuxkit CLI not found. Install it from https://github.com/linuxkit/linuxkit", nil) } return nil } diff --git a/pkg/release/release.go b/pkg/release/release.go index c1a8905..17f90b0 100644 --- a/pkg/release/release.go +++ b/pkg/release/release.go @@ -5,7 +5,6 @@ package release import ( "context" - "errors" "fmt" "path/filepath" "strings" @@ -14,6 +13,7 @@ import ( "forge.lthn.ai/core/go-build/pkg/build/builders" "forge.lthn.ai/core/go-build/pkg/release/publishers" "forge.lthn.ai/core/go-io" + coreerr "forge.lthn.ai/core/go-log" ) // Release represents a release with its version, artifacts, and changelog. @@ -35,7 +35,7 @@ type Release struct { // If dryRun is true, it will show what would be done without actually publishing. func Publish(ctx context.Context, cfg *Config, dryRun bool) (*Release, error) { if cfg == nil { - return nil, errors.New("release.Publish: config is nil") + return nil, coreerr.E("release.Publish", "config is nil", nil) } m := io.Local @@ -48,7 +48,7 @@ func Publish(ctx context.Context, cfg *Config, dryRun bool) (*Release, error) { // Resolve to absolute path absProjectDir, err := filepath.Abs(projectDir) if err != nil { - return nil, fmt.Errorf("release.Publish: failed to resolve project directory: %w", err) + return nil, coreerr.E("release.Publish", "failed to resolve project directory", err) } // Step 1: Determine version @@ -56,7 +56,7 @@ func Publish(ctx context.Context, cfg *Config, dryRun bool) (*Release, error) { if version == "" { version, err = DetermineVersion(absProjectDir) if err != nil { - return nil, fmt.Errorf("release.Publish: failed to determine version: %w", err) + return nil, coreerr.E("release.Publish", "failed to determine version", err) } } @@ -64,11 +64,11 @@ func Publish(ctx context.Context, cfg *Config, dryRun bool) (*Release, error) { distDir := filepath.Join(absProjectDir, "dist") artifacts, err := findArtifacts(m, distDir) if err != nil { - return nil, fmt.Errorf("release.Publish: %w", err) + return nil, coreerr.E("release.Publish", "failed to find artifacts", err) } if len(artifacts) == 0 { - return nil, errors.New("release.Publish: no artifacts found in dist/\nRun 'core build' first to create artifacts") + return nil, coreerr.E("release.Publish", "no artifacts found in dist/\nRun 'core build' first to create artifacts", nil) } // Step 3: Generate changelog @@ -93,13 +93,13 @@ func Publish(ctx context.Context, cfg *Config, dryRun bool) (*Release, error) { for _, pubCfg := range cfg.Publishers { publisher, err := getPublisher(pubCfg.Type) if err != nil { - return release, fmt.Errorf("release.Publish: %w", err) + return release, coreerr.E("release.Publish", "unsupported publisher", err) } extendedCfg := buildExtendedConfig(pubCfg) publisherCfg := publishers.NewPublisherConfig(pubCfg.Type, pubCfg.Prerelease, pubCfg.Draft, extendedCfg) if err := publisher.Publish(ctx, pubRelease, publisherCfg, cfg, dryRun); err != nil { - return release, fmt.Errorf("release.Publish: publish to %s failed: %w", pubCfg.Type, err) + return release, coreerr.E("release.Publish", "publish to "+pubCfg.Type+" failed", err) } } } @@ -110,14 +110,14 @@ func Publish(ctx context.Context, cfg *Config, dryRun bool) (*Release, error) { // findArtifacts discovers pre-built artifacts in the dist directory. func findArtifacts(m io.Medium, distDir string) ([]build.Artifact, error) { if !m.IsDir(distDir) { - return nil, errors.New("dist/ directory not found") + return nil, coreerr.E("release.findArtifacts", "dist/ directory not found", nil) } var artifacts []build.Artifact entries, err := m.List(distDir) if err != nil { - return nil, fmt.Errorf("failed to read dist/: %w", err) + return nil, coreerr.E("release.findArtifacts", "failed to read dist/", err) } for _, entry := range entries { @@ -146,7 +146,7 @@ func findArtifacts(m io.Medium, distDir string) ([]build.Artifact, error) { // If dryRun is true, it will show what would be done without actually publishing. func Run(ctx context.Context, cfg *Config, dryRun bool) (*Release, error) { if cfg == nil { - return nil, errors.New("release.Run: config is nil") + return nil, coreerr.E("release.Run", "config is nil", nil) } m := io.Local @@ -159,7 +159,7 @@ func Run(ctx context.Context, cfg *Config, dryRun bool) (*Release, error) { // Resolve to absolute path absProjectDir, err := filepath.Abs(projectDir) if err != nil { - return nil, fmt.Errorf("release.Run: failed to resolve project directory: %w", err) + return nil, coreerr.E("release.Run", "failed to resolve project directory", err) } // Step 1: Determine version @@ -167,7 +167,7 @@ func Run(ctx context.Context, cfg *Config, dryRun bool) (*Release, error) { if version == "" { version, err = DetermineVersion(absProjectDir) if err != nil { - return nil, fmt.Errorf("release.Run: failed to determine version: %w", err) + return nil, coreerr.E("release.Run", "failed to determine version", err) } } @@ -181,7 +181,7 @@ func Run(ctx context.Context, cfg *Config, dryRun bool) (*Release, error) { // Step 3: Build artifacts artifacts, err := buildArtifacts(ctx, m, cfg, absProjectDir, version) if err != nil { - return nil, fmt.Errorf("release.Run: build failed: %w", err) + return nil, coreerr.E("release.Run", "build failed", err) } release := &Release{ @@ -200,14 +200,14 @@ func Run(ctx context.Context, cfg *Config, dryRun bool) (*Release, error) { for _, pubCfg := range cfg.Publishers { publisher, err := getPublisher(pubCfg.Type) if err != nil { - return release, fmt.Errorf("release.Run: %w", err) + return release, coreerr.E("release.Run", "unsupported publisher", err) } // Build extended config for publisher-specific settings extendedCfg := buildExtendedConfig(pubCfg) publisherCfg := publishers.NewPublisherConfig(pubCfg.Type, pubCfg.Prerelease, pubCfg.Draft, extendedCfg) if err := publisher.Publish(ctx, pubRelease, publisherCfg, cfg, dryRun); err != nil { - return release, fmt.Errorf("release.Run: publish to %s failed: %w", pubCfg.Type, err) + return release, coreerr.E("release.Run", "publish to "+pubCfg.Type+" failed", err) } } } @@ -220,7 +220,7 @@ func buildArtifacts(ctx context.Context, fs io.Medium, cfg *Config, projectDir, // Load build configuration buildCfg, err := build.LoadConfig(fs, projectDir) if err != nil { - return nil, fmt.Errorf("failed to load build config: %w", err) + return nil, coreerr.E("release.buildArtifacts", "failed to load build config", err) } // Determine targets @@ -259,7 +259,7 @@ func buildArtifacts(ctx context.Context, fs io.Medium, cfg *Config, projectDir, // Get builder (detect project type) projectType, err := build.PrimaryType(fs, projectDir) if err != nil { - return nil, fmt.Errorf("failed to detect project type: %w", err) + return nil, coreerr.E("release.buildArtifacts", "failed to detect project type", err) } builder, err := getBuilder(projectType) @@ -280,25 +280,25 @@ func buildArtifacts(ctx context.Context, fs io.Medium, cfg *Config, projectDir, // Build artifacts, err := builder.Build(ctx, buildConfig, targets) if err != nil { - return nil, fmt.Errorf("build failed: %w", err) + return nil, coreerr.E("release.buildArtifacts", "build failed", err) } // Archive artifacts archivedArtifacts, err := build.ArchiveAll(fs, artifacts) if err != nil { - return nil, fmt.Errorf("archive failed: %w", err) + return nil, coreerr.E("release.buildArtifacts", "archive failed", err) } // Compute checksums checksummedArtifacts, err := build.ChecksumAll(fs, archivedArtifacts) if err != nil { - return nil, fmt.Errorf("checksum failed: %w", err) + return nil, coreerr.E("release.buildArtifacts", "checksum failed", err) } // Write CHECKSUMS.txt checksumPath := filepath.Join(outputDir, "CHECKSUMS.txt") if err := build.WriteChecksumFile(fs, checksummedArtifacts, checksumPath); err != nil { - return nil, fmt.Errorf("failed to write checksums file: %w", err) + return nil, coreerr.E("release.buildArtifacts", "failed to write checksums file", err) } // Add CHECKSUMS.txt as an artifact @@ -318,11 +318,11 @@ func getBuilder(projectType build.ProjectType) (build.Builder, error) { case build.ProjectTypeGo: return builders.NewGoBuilder(), nil case build.ProjectTypeNode: - return nil, errors.New("node.js builder not yet implemented") + return nil, coreerr.E("release.getBuilder", "node.js builder not yet implemented", nil) case build.ProjectTypePHP: - return nil, errors.New("PHP builder not yet implemented") + return nil, coreerr.E("release.getBuilder", "PHP builder not yet implemented", nil) default: - return nil, fmt.Errorf("unsupported project type: %s", projectType) + return nil, coreerr.E("release.getBuilder", "unsupported project type: "+string(projectType), nil) } } @@ -346,7 +346,7 @@ func getPublisher(pubType string) (publishers.Publisher, error) { case "chocolatey": return publishers.NewChocolateyPublisher(), nil default: - return nil, fmt.Errorf("unsupported publisher type: %s", pubType) + return nil, coreerr.E("release.getPublisher", "unsupported publisher type: "+pubType, nil) } } @@ -412,7 +412,7 @@ func buildExtendedConfig(pubCfg PublisherConfig) map[string]any { ext["maintainer"] = pubCfg.Maintainer } - // Chocolatey-specific config + // Chocolatey-specific configuration if pubCfg.Push { ext["push"] = pubCfg.Push } diff --git a/pkg/release/sdk.go b/pkg/release/sdk.go index 34be074..59598ba 100644 --- a/pkg/release/sdk.go +++ b/pkg/release/sdk.go @@ -3,10 +3,10 @@ package release import ( "context" - "errors" "fmt" "forge.lthn.ai/core/go-build/pkg/sdk" + coreerr "forge.lthn.ai/core/go-log" ) // SDKRelease holds the result of an SDK release. @@ -23,10 +23,10 @@ type SDKRelease struct { // If dryRun is true, it shows what would be done without generating. func RunSDK(ctx context.Context, cfg *Config, dryRun bool) (*SDKRelease, error) { if cfg == nil { - return nil, errors.New("release.RunSDK: config is nil") + return nil, coreerr.E("release.RunSDK", "config is nil", nil) } if cfg.SDK == nil { - return nil, errors.New("release.RunSDK: sdk not configured in .core/release.yaml") + return nil, coreerr.E("release.RunSDK", "sdk not configured in .core/release.yaml", nil) } projectDir := cfg.projectDir @@ -40,7 +40,7 @@ func RunSDK(ctx context.Context, cfg *Config, dryRun bool) (*SDKRelease, error) var err error version, err = DetermineVersion(projectDir) if err != nil { - return nil, fmt.Errorf("release.RunSDK: failed to determine version: %w", err) + return nil, coreerr.E("release.RunSDK", "failed to determine version", err) } } @@ -52,7 +52,7 @@ func RunSDK(ctx context.Context, cfg *Config, dryRun bool) (*SDKRelease, error) fmt.Printf("Warning: diff check failed: %v\n", err) } else if breaking { if cfg.SDK.Diff.FailOnBreaking { - return nil, errors.New("release.RunSDK: breaking API changes detected") + return nil, coreerr.E("release.RunSDK", "breaking API changes detected", nil) } fmt.Printf("Warning: breaking API changes detected\n") } @@ -80,7 +80,7 @@ func RunSDK(ctx context.Context, cfg *Config, dryRun bool) (*SDKRelease, error) s.SetVersion(version) if err := s.Generate(ctx); err != nil { - return nil, fmt.Errorf("release.RunSDK: generation failed: %w", err) + return nil, coreerr.E("release.RunSDK", "generation failed", err) } return result, nil @@ -91,7 +91,7 @@ func checkBreakingChanges(projectDir string, cfg *SDKConfig) (bool, error) { // Get previous tag for comparison (uses getPreviousTag from changelog.go) prevTag, err := getPreviousTag(projectDir, "HEAD") if err != nil { - return false, fmt.Errorf("no previous tag found: %w", err) + return false, coreerr.E("release.checkBreakingChanges", "no previous tag found", err) } // Detect spec path diff --git a/pkg/release/version.go b/pkg/release/version.go index 335ced7..ea28f39 100644 --- a/pkg/release/version.go +++ b/pkg/release/version.go @@ -7,6 +7,8 @@ import ( "regexp" "strconv" "strings" + + coreerr "forge.lthn.ai/core/go-log" ) // semverRegex matches semantic version strings with or without 'v' prefix. @@ -99,7 +101,7 @@ func IncrementMajor(current string) string { func ParseVersion(version string) (int, int, int, string, string, error) { matches := semverRegex.FindStringSubmatch(version) if matches == nil { - return 0, 0, 0, "", "", fmt.Errorf("invalid semver: %s", version) + return 0, 0, 0, "", "", coreerr.E("release.ParseVersion", "invalid semver: "+version, nil) } major, _ := strconv.Atoi(matches[1]) diff --git a/pkg/sdk/detect.go b/pkg/sdk/detect.go index 699596d..c318a63 100644 --- a/pkg/sdk/detect.go +++ b/pkg/sdk/detect.go @@ -1,12 +1,11 @@ package sdk import ( - "errors" - "fmt" "path/filepath" "strings" coreio "forge.lthn.ai/core/go-io" + coreerr "forge.lthn.ai/core/go-log" ) // commonSpecPaths are checked in order when no spec is configured. @@ -30,7 +29,7 @@ func (s *SDK) DetectSpec() (string, error) { if coreio.Local.IsFile(specPath) { return specPath, nil } - return "", fmt.Errorf("sdk.DetectSpec: configured spec not found: %s", s.config.Spec) + return "", coreerr.E("sdk.DetectSpec", "configured spec not found: "+s.config.Spec, nil) } // 2. Check common paths @@ -47,14 +46,14 @@ func (s *SDK) DetectSpec() (string, error) { return specPath, nil } - return "", errors.New("sdk.DetectSpec: no OpenAPI spec found (checked config, common paths, Scramble)") + return "", coreerr.E("sdk.DetectSpec", "no OpenAPI spec found (checked config, common paths, Scramble)", nil) } // detectScramble checks for Laravel Scramble and exports the spec. func (s *SDK) detectScramble() (string, error) { composerPath := filepath.Join(s.projectDir, "composer.json") if !coreio.Local.IsFile(composerPath) { - return "", errors.New("no composer.json") + return "", coreerr.E("sdk.detectScramble", "no composer.json", nil) } // Check for scramble in composer.json @@ -65,11 +64,11 @@ func (s *SDK) detectScramble() (string, error) { // Simple check for scramble package if !containsScramble(data) { - return "", errors.New("scramble not found in composer.json") + return "", coreerr.E("sdk.detectScramble", "scramble not found in composer.json", nil) } // TODO: Run php artisan scramble:export - return "", errors.New("scramble export not implemented") + return "", coreerr.E("sdk.detectScramble", "scramble export not implemented", nil) } // containsScramble checks if composer.json includes scramble. diff --git a/pkg/sdk/diff.go b/pkg/sdk/diff.go index 0540483..540d1ce 100644 --- a/pkg/sdk/diff.go +++ b/pkg/sdk/diff.go @@ -3,6 +3,7 @@ package sdk import ( "fmt" + coreerr "forge.lthn.ai/core/go-log" "github.com/getkin/kin-openapi/openapi3" "github.com/oasdiff/oasdiff/checker" "github.com/oasdiff/oasdiff/diff" @@ -27,18 +28,18 @@ func Diff(basePath, revisionPath string) (*DiffResult, error) { // Load specs baseSpec, err := load.NewSpecInfo(loader, load.NewSource(basePath)) if err != nil { - return nil, fmt.Errorf("sdk.Diff: failed to load base spec: %w", err) + return nil, coreerr.E("sdk.Diff", "failed to load base spec", err) } revSpec, err := load.NewSpecInfo(loader, load.NewSource(revisionPath)) if err != nil { - return nil, fmt.Errorf("sdk.Diff: failed to load revision spec: %w", err) + return nil, coreerr.E("sdk.Diff", "failed to load revision spec", err) } // Compute diff with operations sources map for better error reporting diffResult, operationsSources, err := diff.GetWithOperationsSourcesMap(diff.NewConfig(), baseSpec, revSpec) if err != nil { - return nil, fmt.Errorf("sdk.Diff: failed to compute diff: %w", err) + return nil, coreerr.E("sdk.Diff", "failed to compute diff", err) } // Check for breaking changes diff --git a/pkg/sdk/generators/go.go b/pkg/sdk/generators/go.go index b3ea0f7..f55c31c 100644 --- a/pkg/sdk/generators/go.go +++ b/pkg/sdk/generators/go.go @@ -8,7 +8,7 @@ import ( "path/filepath" coreio "forge.lthn.ai/core/go-io" - "forge.lthn.ai/core/go-log" + coreerr "forge.lthn.ai/core/go-log" ) // GoGenerator generates Go SDKs from OpenAPI specs. @@ -38,7 +38,7 @@ func (g *GoGenerator) Install() string { // Generate creates SDK from OpenAPI spec. func (g *GoGenerator) Generate(ctx context.Context, opts Options) error { if err := coreio.Local.EnsureDir(opts.OutputDir); err != nil { - return log.E("go.Generate", "failed to create output dir", err) + return coreerr.E("go.Generate", "failed to create output dir", err) } if g.Available() { @@ -60,7 +60,7 @@ func (g *GoGenerator) generateNative(ctx context.Context, opts Options) error { cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { - return log.E("go.generateNative", "oapi-codegen failed", err) + return coreerr.E("go.generateNative", "oapi-codegen failed", err) } goMod := fmt.Sprintf("module %s\n\ngo 1.21\n", opts.PackageName) diff --git a/pkg/sdk/generators/php.go b/pkg/sdk/generators/php.go index f848e56..556e33e 100644 --- a/pkg/sdk/generators/php.go +++ b/pkg/sdk/generators/php.go @@ -2,13 +2,12 @@ package generators import ( "context" - "errors" - "fmt" "os" "os/exec" "path/filepath" coreio "forge.lthn.ai/core/go-io" + coreerr "forge.lthn.ai/core/go-log" ) // PHPGenerator generates PHP SDKs from OpenAPI specs. @@ -38,11 +37,11 @@ func (g *PHPGenerator) Install() string { // Generate creates SDK from OpenAPI spec. func (g *PHPGenerator) Generate(ctx context.Context, opts Options) error { if !g.Available() { - return errors.New("php.Generate: Docker is required but not available") + return coreerr.E("php.Generate", "Docker is required but not available", nil) } if err := coreio.Local.EnsureDir(opts.OutputDir); err != nil { - return fmt.Errorf("php.Generate: failed to create output dir: %w", err) + return coreerr.E("php.Generate", "failed to create output dir", err) } specDir := filepath.Dir(opts.SpecPath) @@ -65,7 +64,7 @@ func (g *PHPGenerator) Generate(ctx context.Context, opts Options) error { cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { - return fmt.Errorf("php.Generate: %w", err) + return coreerr.E("php.Generate", "docker run failed", err) } return nil } diff --git a/pkg/sdk/generators/python.go b/pkg/sdk/generators/python.go index c542555..17dd40d 100644 --- a/pkg/sdk/generators/python.go +++ b/pkg/sdk/generators/python.go @@ -2,12 +2,12 @@ package generators import ( "context" - "fmt" "os" "os/exec" "path/filepath" coreio "forge.lthn.ai/core/go-io" + coreerr "forge.lthn.ai/core/go-log" ) // PythonGenerator generates Python SDKs from OpenAPI specs. @@ -37,7 +37,7 @@ func (g *PythonGenerator) Install() string { // Generate creates SDK from OpenAPI spec. func (g *PythonGenerator) Generate(ctx context.Context, opts Options) error { if err := coreio.Local.EnsureDir(opts.OutputDir); err != nil { - return fmt.Errorf("python.Generate: failed to create output dir: %w", err) + return coreerr.E("python.Generate", "failed to create output dir", err) } if g.Available() { diff --git a/pkg/sdk/generators/typescript.go b/pkg/sdk/generators/typescript.go index 3ca5a6d..190b6e0 100644 --- a/pkg/sdk/generators/typescript.go +++ b/pkg/sdk/generators/typescript.go @@ -2,12 +2,12 @@ package generators import ( "context" - "fmt" "os" "os/exec" "path/filepath" coreio "forge.lthn.ai/core/go-io" + coreerr "forge.lthn.ai/core/go-log" ) // TypeScriptGenerator generates TypeScript SDKs from OpenAPI specs. @@ -41,7 +41,7 @@ func (g *TypeScriptGenerator) Install() string { // Generate creates SDK from OpenAPI spec. func (g *TypeScriptGenerator) Generate(ctx context.Context, opts Options) error { if err := coreio.Local.EnsureDir(opts.OutputDir); err != nil { - return fmt.Errorf("typescript.Generate: failed to create output dir: %w", err) + return coreerr.E("typescript.Generate", "failed to create output dir", err) } if g.nativeAvailable() { @@ -106,7 +106,7 @@ func (g *TypeScriptGenerator) generateDocker(ctx context.Context, opts Options) cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { - return fmt.Errorf("typescript.generateDocker: %w", err) + return coreerr.E("typescript.generateDocker", "docker run failed", err) } return nil } diff --git a/pkg/sdk/sdk.go b/pkg/sdk/sdk.go index f00bfef..0ea723f 100644 --- a/pkg/sdk/sdk.go +++ b/pkg/sdk/sdk.go @@ -6,6 +6,7 @@ import ( "fmt" "path/filepath" + coreerr "forge.lthn.ai/core/go-log" "forge.lthn.ai/core/go-build/pkg/sdk/generators" ) @@ -115,7 +116,7 @@ func (s *SDK) GenerateLanguage(ctx context.Context, lang string) error { gen, ok := registry.Get(lang) if !ok { - return fmt.Errorf("sdk.GenerateLanguage: unknown language: %s", lang) + return coreerr.E("sdk.GenerateLanguage", "unknown language: "+lang, nil) } if !gen.Available() { @@ -133,7 +134,7 @@ func (s *SDK) GenerateLanguage(ctx context.Context, lang string) error { fmt.Printf("Generating %s SDK...\n", lang) if err := gen.Generate(ctx, opts); err != nil { - return fmt.Errorf("sdk.GenerateLanguage: %s generation failed: %w", lang, err) + return coreerr.E("sdk.GenerateLanguage", lang+" generation failed", err) } fmt.Printf("Generated %s SDK at %s\n", lang, outputDir) From cd3d82def75d08dfdcfb12167d98340beb71c38c Mon Sep 17 00:00:00 2001 From: Snider Date: Mon, 16 Mar 2026 22:19:48 +0000 Subject: [PATCH 05/12] chore: sync dependencies for v0.2.4 Co-Authored-By: Virgil --- go.mod | 19 ++++++++----------- go.sum | 38 ++++++++++++++++---------------------- 2 files changed, 24 insertions(+), 33 deletions(-) diff --git a/go.mod b/go.mod index 96bb7e4..1bf9206 100644 --- a/go.mod +++ b/go.mod @@ -4,17 +4,17 @@ go 1.26.0 require ( forge.lthn.ai/core/api v0.1.3 - forge.lthn.ai/core/cli v0.3.1 - forge.lthn.ai/core/go-i18n v0.1.4 - forge.lthn.ai/core/go-io v0.1.2 + forge.lthn.ai/core/cli v0.3.5 + forge.lthn.ai/core/go-i18n v0.1.6 + forge.lthn.ai/core/go-io v0.1.5 forge.lthn.ai/core/go-log v0.0.4 - forge.lthn.ai/core/go-ws v0.2.1 + forge.lthn.ai/core/go-ws v0.2.3 github.com/Snider/Borg v0.2.0 github.com/getkin/kin-openapi v0.134.0 github.com/gin-gonic/gin v1.12.0 github.com/leaanthony/debme v1.2.1 github.com/leaanthony/gosod v1.0.4 - github.com/oasdiff/oasdiff v1.12.1 + github.com/oasdiff/oasdiff v1.12.3 github.com/stretchr/testify v1.11.1 golang.org/x/net v0.52.0 golang.org/x/text v0.35.0 @@ -24,12 +24,9 @@ require ( require ( cloud.google.com/go v0.123.0 // indirect forge.lthn.ai/core/go v0.3.1 // indirect - forge.lthn.ai/core/go-crypt v0.1.7 // indirect forge.lthn.ai/core/go-inference v0.1.4 // indirect - forge.lthn.ai/core/go-process v0.2.3 // indirect github.com/99designs/gqlgen v0.17.88 // indirect github.com/KyleBanks/depth v1.2.1 // indirect - github.com/ProtonMail/go-crypto v1.4.0 // indirect github.com/TwiN/go-color v1.4.1 // indirect github.com/agnivade/levenshtein v1.2.1 // indirect github.com/andybalholm/brotli v1.2.0 // indirect @@ -49,7 +46,6 @@ require ( github.com/charmbracelet/x/term v0.2.2 // indirect github.com/clipperhouse/displaywidth v0.11.0 // indirect github.com/clipperhouse/uax29/v2 v2.7.0 // indirect - github.com/cloudflare/circl v1.6.3 // indirect github.com/cloudwego/base64x v0.1.6 // indirect github.com/coreos/go-oidc/v3 v3.17.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect @@ -110,8 +106,9 @@ require ( github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/termenv v0.16.0 // indirect - github.com/oasdiff/yaml v0.0.0-20260313112342-a3ea61cb4d4c // indirect - github.com/oasdiff/yaml3 v0.0.0-20260224194419-61cd415a242b // indirect + github.com/oasdiff/kin-openapi v0.136.0 // indirect + github.com/oasdiff/yaml v0.0.1 // indirect + github.com/oasdiff/yaml3 v0.0.1 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect diff --git a/go.sum b/go.sum index 710a2b5..76cc24b 100644 --- a/go.sum +++ b/go.sum @@ -2,30 +2,24 @@ cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE= cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU= forge.lthn.ai/core/api v0.1.3 h1:iYmNP6zK5SiNRunYEsXPvjppTh3bQADkMyoCC8lEs48= forge.lthn.ai/core/api v0.1.3/go.mod h1:dBOZc6DS0HdnTfCJZ8FkZxWJio2cIf0d1UrCAlDanrA= -forge.lthn.ai/core/cli v0.3.1 h1:ZpHhaDrdbaV98JDxj/f0E5nytYk9tTMRu3qohGyK4M0= -forge.lthn.ai/core/cli v0.3.1/go.mod h1:28cOl9eK0H033Otkjrv9f/QCmtHcJl+IIx4om8JskOg= +forge.lthn.ai/core/cli v0.3.5 h1:P7yK0DmSA1QnUMFuCjJZf/fk/akKPIxopQ6OwD8Sar8= +forge.lthn.ai/core/cli v0.3.5/go.mod h1:SeArHx+hbpX5iZqgASCD7Q1EDoc6uaaGiGBotmNzIx4= forge.lthn.ai/core/go v0.3.1 h1:5FMTsUhLcxSr07F9q3uG0Goy4zq4eLivoqi8shSY4UM= forge.lthn.ai/core/go v0.3.1/go.mod h1:gE6c8h+PJ2287qNhVUJ5SOe1kopEwHEquvinstpuyJc= -forge.lthn.ai/core/go-crypt v0.1.7 h1:tyDFnXjEksHFQpkFwCpEn+x7zvwh4LnaU+/fP3WmqZc= -forge.lthn.ai/core/go-crypt v0.1.7/go.mod h1:mQdr6K8lWOcyHmSEW24vZPTThQF8fteVgZi8CO+Ko3Y= -forge.lthn.ai/core/go-i18n v0.1.4 h1:zOHUUJDgRo88/3tj++kN+VELg/buyZ4T2OSdG3HBbLQ= -forge.lthn.ai/core/go-i18n v0.1.4/go.mod h1:aDyAfz7MMgWYgLkZCptfFmZ7jJg3ocwjEJ1WkJSvv4U= +forge.lthn.ai/core/go-i18n v0.1.6 h1:Z9h6sEZsgJmWlkkq3ZPZyfgWipeeqN5lDCpzltpamHU= +forge.lthn.ai/core/go-i18n v0.1.6/go.mod h1:C6CbwdN7sejTx/lbutBPrxm77b8paMHBO6uHVLHOdqQ= forge.lthn.ai/core/go-inference v0.1.4 h1:fuAgWbqsEDajHniqAKyvHYbRcBrkGEiGSqR2pfTMRY0= forge.lthn.ai/core/go-inference v0.1.4/go.mod h1:jfWz+IJX55wAH98+ic6FEqqGB6/P31CHlg7VY7pxREw= -forge.lthn.ai/core/go-io v0.1.2 h1:q8hj2jtOFqAgHlBr5wsUAOXtaFkxy9gqGrQT/il0WYA= -forge.lthn.ai/core/go-io v0.1.2/go.mod h1:PbNKW1Q25ywSOoQXeGdQHbV5aiIrTXvHIQ5uhplA//g= +forge.lthn.ai/core/go-io v0.1.5 h1:+XJ1YhaGGFLGtcNbPtVlndTjk+pO0Ydi2hRDj5/cHOM= +forge.lthn.ai/core/go-io v0.1.5/go.mod h1:FRtXSsi8W+U9vewCU+LBAqqbIj3wjXA4dBdSv3SAtWI= forge.lthn.ai/core/go-log v0.0.4 h1:KTuCEPgFmuM8KJfnyQ8vPOU1Jg654W74h8IJvfQMfv0= forge.lthn.ai/core/go-log v0.0.4/go.mod h1:r14MXKOD3LF/sI8XUJQhRk/SZHBE7jAFVuCfgkXoZPw= -forge.lthn.ai/core/go-process v0.2.3 h1:/ERqRYHgCNZjNT9NMinAAJJGJWSsHuCTiHFNEm6nTPY= -forge.lthn.ai/core/go-process v0.2.3/go.mod h1:gVTbxL16ccUIexlFcyDtCy7LfYvD8Rtyzfo8bnXAXrU= -forge.lthn.ai/core/go-ws v0.2.1 h1:QA+hZrgD/AdmyEKARwM4jL9Kr4p3wLEQi6qBElpwFsw= -forge.lthn.ai/core/go-ws v0.2.1/go.mod h1:LC+VXNvPcGYWmIyS/zMDqmYE2RjNAVyTz1UI2xDLVgU= +forge.lthn.ai/core/go-ws v0.2.3 h1:qTeMtJQjtTdTwfPvtbOBdch2Dmbde+Aso8Ow1qvg/bk= +forge.lthn.ai/core/go-ws v0.2.3/go.mod h1:C3riJyLLcV6QhLvYlq3P/XkGTsN598qQeGBoLdoHBU4= github.com/99designs/gqlgen v0.17.88 h1:neMQDgehMwT1vYIOx/w5ZYPUU/iMNAJzRO44I5Intoc= github.com/99designs/gqlgen v0.17.88/go.mod h1:qeqYFEgOeSKqWedOjogPizimp2iu4E23bdPvl4jTYic= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= -github.com/ProtonMail/go-crypto v1.4.0 h1:Zq/pbM3F5DFgJiMouxEdSVY44MVoQNEKp5d5QxIQceQ= -github.com/ProtonMail/go-crypto v1.4.0/go.mod h1:e1OaTyu5SYVrO9gKOEhTc+5UcXtTUa+P3uLudwcgPqo= github.com/PuerkitoBio/goquery v1.11.0 h1:jZ7pwMQXIITcUXNH83LLk+txlaEy6NVOfTuP43xxfqw= github.com/PuerkitoBio/goquery v1.11.0/go.mod h1:wQHgxUOU3JGuj3oD/QFfxUdlzW6xPHfqyHre6VMY4DQ= github.com/Snider/Borg v0.2.0 h1:iCyDhY4WTXi39+FexRwXbn2YpZ2U9FUXVXDZk9xRCXQ= @@ -80,8 +74,6 @@ github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSE github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0= github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk= github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= -github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8= -github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4= github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= github.com/coreos/go-oidc/v3 v3.17.0 h1:hWBGaQfbi0iVviX4ibC7bk8OKT5qNr4klBaCHVNvehc= @@ -245,12 +237,14 @@ github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELU github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= -github.com/oasdiff/oasdiff v1.12.1 h1:wnvBQS/WSqGqH23u1Jo3XVaF5y5X67TC5znSiy5nIug= -github.com/oasdiff/oasdiff v1.12.1/go.mod h1:4l8lF8SkdyiBVpa7AH3xc+oyDDXS1QTegX25mBS11/E= -github.com/oasdiff/yaml v0.0.0-20260313112342-a3ea61cb4d4c h1:7ACFcSaQsrWtrH4WHHfUqE1C+f8r2uv8KGaW0jTNjus= -github.com/oasdiff/yaml v0.0.0-20260313112342-a3ea61cb4d4c/go.mod h1:JKox4Gszkxt57kj27u7rvi7IFoIULvCZHUsBTUmQM/s= -github.com/oasdiff/yaml3 v0.0.0-20260224194419-61cd415a242b h1:vivRhVUAa9t1q0Db4ZmezBP8pWQWnXHFokZj0AOea2g= -github.com/oasdiff/yaml3 v0.0.0-20260224194419-61cd415a242b/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o= +github.com/oasdiff/kin-openapi v0.136.0 h1:znR1xptw6DoK5drWIGEHAPTjgdCXZ6kKNpBCkdBDvBw= +github.com/oasdiff/kin-openapi v0.136.0/go.mod h1:BMeaLn+GmFJKtHJ31JrgXFt91eZi/q+Og4tr7sq0BzI= +github.com/oasdiff/oasdiff v1.12.3 h1:eUzJ/AiyyCY1KwUZPv7fosgDyETacIZbFesJrRz+QdY= +github.com/oasdiff/oasdiff v1.12.3/go.mod h1:ApEJGlkuRdrcBgTE4ioicwIM7nzkxPqLPPvcB5AytQ0= +github.com/oasdiff/yaml v0.0.1 h1:dPrn0F2PJ7HdzHPndJkArvB2Fw0cwgFdVUKCEkoFuds= +github.com/oasdiff/yaml v0.0.1/go.mod h1:r8bgVgpWT5iIN/AgP0GljFvB6CicK+yL1nIAbm+8/QQ= +github.com/oasdiff/yaml3 v0.0.1 h1:kReOSraQLTxuuGNX9aNeJ7tcsvUB2MS+iupdUrWe4Z0= +github.com/oasdiff/yaml3 v0.0.1/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= From 275d41f3fc4745ff4f4e2ed297b514b5456ad9df Mon Sep 17 00:00:00 2001 From: Snider Date: Tue, 17 Mar 2026 00:34:39 +0000 Subject: [PATCH 06/12] feat: add en-GB locale file for build/CI/SDK commands 132 translation keys for build, ci, sdk commands. Co-Authored-By: Virgil --- locales/en.json | 206 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 locales/en.json diff --git a/locales/en.json b/locales/en.json new file mode 100644 index 0000000..a7fa66d --- /dev/null +++ b/locales/en.json @@ -0,0 +1,206 @@ +{ + "common": { + "error": { + "failed": "Failed to {{.Action}}" + }, + "flag": { + "spec": "Path to OpenAPI spec file", + "verbose": "Enable verbose output" + }, + "label": { + "error": "Error", + "spec": "Spec:", + "success": "Done", + "warning": "Warning" + } + }, + "cmd": { + "build": { + "short": "Build project artefacts", + "long": "Build binaries, archives, and checksums for the current project. Supports Go, Docker, and PWA builds with cross-compilation and code signing.", + "building_project": "Building project...", + "built_artifacts": "Built {{.Count}} artefact(s)", + "computing_checksums": "Computing checksums...", + "creating_archives": "Creating archives...", + "signing_binaries": "Signing binaries...", + "error": { + "archive_failed": "Archive creation failed", + "checksum_failed": "Checksum computation failed", + "gpg_signing_failed": "GPG signing failed", + "notarization_failed": "Notarisation failed", + "signing_failed": "Code signing failed" + }, + "flag": { + "archive": "Create release archives", + "checksum": "Generate checksums for artefacts", + "ci": "Run in CI mode (all targets, JSON output)", + "config": "Path to build configuration file", + "format": "Output format (e.g. json, text)", + "image": "Container image name", + "no_sign": "Skip code signing", + "notarize": "Notarise macOS binaries", + "output": "Output directory for build artefacts", + "push": "Push container image after build", + "targets": "Comma-separated build targets (os/arch)", + "type": "Project type override (go, docker, pwa)" + }, + "from_path": { + "short": "Build a Go binary from a local path", + "compiling": "Compiling...", + "copying_files": "Copying application files...", + "generating_template": "Generating build template...", + "starting": "Starting build from path:", + "success": "Build complete:", + "error": { + "go_build": "Go build failed", + "go_mod_tidy": "go mod tidy failed", + "invalid_path": "Invalid path", + "must_be_directory": "Path must be a directory" + }, + "flag": { + "path": "Path to source directory" + } + }, + "label": { + "archive": "Archive", + "binary": "Binary:", + "build": "Build", + "checksum": "Checksum", + "ok": "OK", + "output": "Output:", + "sign": "Sign", + "targets": "Targets:", + "type": "Type:" + }, + "pwa": { + "short": "Build a PWA from a URL", + "download_complete": "Download complete", + "downloading_to": "Downloading to:", + "found_manifest": "Found manifest:", + "no_manifest": "No manifest found; saving raw HTML", + "starting": "Starting PWA build:", + "error": { + "no_manifest_tag": "No manifest link tag found in HTML" + }, + "flag": { + "url": "URL of the PWA to build" + } + }, + "release": { + "short": "Build and publish a release", + "long": "Build all artefacts and publish them to configured release channels (GitHub, Homebrew, Scoop, etc.).", + "building_and_publishing": "Building and publishing release...", + "completed": "Release published successfully", + "dry_run_hint": "Dry run — no artefacts will be published", + "error": { + "no_config": "No build configuration found" + }, + "flag": { + "draft": "Create release as draft", + "go_for_launch": "Confirm publish (required for non-dry-run)", + "prerelease": "Mark release as pre-release", + "version": "Release version (e.g. v1.2.3)" + }, + "hint": { + "create_config": "Create a .core/build.yaml to configure builds" + }, + "label": { + "artifacts": "Artefacts:", + "published": "Published:", + "release": "Release" + } + }, + "sdk": { + "short": "Generate SDK client libraries", + "long": "Generate typed SDK client libraries from an OpenAPI spec. Supports multiple languages and versioned output.", + "complete": "SDK generation complete", + "dry_run_mode": "Dry run — no files will be written", + "generated_label": "Generated:", + "generating": "Generating SDK...", + "label": "SDK", + "language_label": "Language:", + "languages_label": "Languages:", + "would_generate": "Would generate SDK (dry run)", + "flag": { + "dry_run": "Preview without generating files", + "lang": "Target language (e.g. go, typescript, python)", + "version": "SDK version to generate" + } + } + }, + "ci": { + "short": "Run CI pipeline", + "long": "Execute the full CI pipeline: build, test, and publish artefacts to configured release channels.", + "dry_run_hint": "Dry run — no artefacts will be published", + "go_for_launch": "We are go for launch!", + "publish_completed": "Publish completed successfully", + "publishing": "Publishing...", + "error": { + "no_publishers": "No publishers configured" + }, + "flag": { + "draft": "Create release as draft", + "go_for_launch": "Confirm publish (required for non-dry-run)", + "prerelease": "Mark release as pre-release", + "version": "Release version (e.g. v1.2.3)" + }, + "label": { + "artifacts": "Artefacts:", + "ci": "CI", + "published": "Published:" + }, + "changelog": { + "short": "Generate a changelog", + "long": "Generate a changelog from git history between two references (tags, commits, or branches).", + "generating": "Generating changelog", + "no_tags": "No tags found in repository", + "flag": { + "from": "Start reference (tag, commit, or branch)", + "to": "End reference (defaults to HEAD)" + } + }, + "init": { + "short": "Initialise CI configuration", + "long": "Create a default .core/build.yaml configuration file for the current project.", + "already_initialised": "Configuration already exists", + "created_config": "Created .core/build.yaml", + "edit_config": "1. Edit .core/build.yaml to configure your build", + "initializing": "Initialising CI configuration...", + "next_steps": "Next steps:", + "run_ci": "2. Run 'core ci' to build and publish" + }, + "version": { + "short": "Show or bump the project version", + "long": "Display the current project version or compute the next version based on conventional commits." + } + }, + "sdk": { + "short": "OpenAPI SDK tools", + "long": "Tools for validating OpenAPI specs and generating SDK client libraries with breaking-change detection.", + "label": { + "ok": "OK", + "sdk": "SDK" + }, + "diff": { + "short": "Detect breaking API changes", + "long": "Compare two OpenAPI specs and report any breaking changes between versions.", + "base_label": "Base:", + "breaking": "Breaking changes detected", + "label": "Diff", + "error": { + "base_required": "Base spec path is required" + }, + "flag": { + "base": "Path to base (previous) OpenAPI spec", + "spec": "Path to current OpenAPI spec" + } + }, + "validate": { + "short": "Validate an OpenAPI spec", + "long": "Parse and validate an OpenAPI specification file for correctness.", + "valid": "Spec is valid", + "validating": "Validating spec..." + } + } + } +} From d4f10ea18f9c3a8bd4fdf3523c332dc93df59bbc Mon Sep 17 00:00:00 2001 From: Snider Date: Tue, 17 Mar 2026 00:45:17 +0000 Subject: [PATCH 07/12] feat: embed and load locale translations on init Co-Authored-By: Virgil --- cmd/build/cmd_build.go | 4 ++-- locales/embed.go | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 locales/embed.go diff --git a/cmd/build/cmd_build.go b/cmd/build/cmd_build.go index 294c99a..40aefca 100644 --- a/cmd/build/cmd_build.go +++ b/cmd/build/cmd_build.go @@ -2,13 +2,13 @@ package buildcmd import ( - "embed" - "forge.lthn.ai/core/cli/pkg/cli" + "forge.lthn.ai/core/go-build/locales" "forge.lthn.ai/core/go-i18n" ) func init() { + i18n.LoadFS(locales.FS, ".") cli.RegisterCommands(AddBuildCommands) } diff --git a/locales/embed.go b/locales/embed.go new file mode 100644 index 0000000..410cb55 --- /dev/null +++ b/locales/embed.go @@ -0,0 +1,7 @@ +// Package locales embeds translation files for this module. +package locales + +import "embed" + +//go:embed *.json +var FS embed.FS From 9c609a526c2482cf88edc54fab99b87f4dba63cc Mon Sep 17 00:00:00 2001 From: Snider Date: Tue, 17 Mar 2026 01:38:30 +0000 Subject: [PATCH 08/12] refactor: pass locales via RegisterCommands Co-Authored-By: Virgil --- cmd/build/cmd_build.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmd/build/cmd_build.go b/cmd/build/cmd_build.go index 40aefca..e8f9b3a 100644 --- a/cmd/build/cmd_build.go +++ b/cmd/build/cmd_build.go @@ -8,8 +8,7 @@ import ( ) func init() { - i18n.LoadFS(locales.FS, ".") - cli.RegisterCommands(AddBuildCommands) + cli.RegisterCommands(AddBuildCommands, locales.FS) } // Style aliases from shared package From 4c63d30a2439a32d1e2e7778c7e1dc5bc4f2bd25 Mon Sep 17 00:00:00 2001 From: Snider Date: Tue, 17 Mar 2026 02:28:07 +0000 Subject: [PATCH 09/12] fix: switch to oasdiff/kin-openapi, restore embed import SDK diff.go used getkin/kin-openapi but oasdiff v1.12+ uses its own fork. Switched import to match. Restores full CLI binary compilation. Co-Authored-By: Virgil --- cmd/build/cmd_build.go | 2 ++ pkg/sdk/diff.go | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd/build/cmd_build.go b/cmd/build/cmd_build.go index e8f9b3a..84bcf5c 100644 --- a/cmd/build/cmd_build.go +++ b/cmd/build/cmd_build.go @@ -2,6 +2,8 @@ package buildcmd import ( + "embed" + "forge.lthn.ai/core/cli/pkg/cli" "forge.lthn.ai/core/go-build/locales" "forge.lthn.ai/core/go-i18n" diff --git a/pkg/sdk/diff.go b/pkg/sdk/diff.go index 540d1ce..340a236 100644 --- a/pkg/sdk/diff.go +++ b/pkg/sdk/diff.go @@ -4,7 +4,7 @@ import ( "fmt" coreerr "forge.lthn.ai/core/go-log" - "github.com/getkin/kin-openapi/openapi3" + "github.com/oasdiff/kin-openapi/openapi3" "github.com/oasdiff/oasdiff/checker" "github.com/oasdiff/oasdiff/diff" "github.com/oasdiff/oasdiff/load" From 356e5315b96f45d87a68ddde5c0b5260542bbd0b Mon Sep 17 00:00:00 2001 From: Snider Date: Tue, 17 Mar 2026 02:31:47 +0000 Subject: [PATCH 10/12] fix: defer i18n.T() for package-level command vars Move i18n.T() calls out of package-level var declarations (which run at import time, before i18n is initialised) into setXxxI18n() functions called from AddXxxCommands at registration time. Co-Authored-By: Virgil --- cmd/build/cmd_build.go | 24 ++++++++++++++---------- cmd/build/cmd_release.go | 13 +++++++++---- cmd/ci/ci.go | 29 ++++++++++++++++------------- cmd/ci/cmd.go | 2 ++ cmd/sdk/cmd.go | 23 ++++++++++++++--------- 5 files changed, 55 insertions(+), 36 deletions(-) diff --git a/cmd/build/cmd_build.go b/cmd/build/cmd_build.go index 84bcf5c..bf14040 100644 --- a/cmd/build/cmd_build.go +++ b/cmd/build/cmd_build.go @@ -59,17 +59,14 @@ var ( ) var buildCmd = &cli.Command{ - Use: "build", - Short: i18n.T("cmd.build.short"), - Long: i18n.T("cmd.build.long"), + Use: "build", RunE: func(cmd *cli.Command, args []string) error { return runProjectBuild(cmd.Context(), buildType, ciMode, targets, outputDir, doArchive, doChecksum, configPath, format, push, imageName, noSign, notarize, verbose) }, } var fromPathCmd = &cli.Command{ - Use: "from-path", - Short: i18n.T("cmd.build.from_path.short"), + Use: "from-path", RunE: func(cmd *cli.Command, args []string) error { if fromPath == "" { return errPathRequired @@ -79,8 +76,7 @@ var fromPathCmd = &cli.Command{ } var pwaCmd = &cli.Command{ - Use: "pwa", - Short: i18n.T("cmd.build.pwa.short"), + Use: "pwa", RunE: func(cmd *cli.Command, args []string) error { if pwaURL == "" { return errURLRequired @@ -90,14 +86,21 @@ var pwaCmd = &cli.Command{ } var sdkBuildCmd = &cli.Command{ - Use: "sdk", - Short: i18n.T("cmd.build.sdk.short"), - Long: i18n.T("cmd.build.sdk.long"), + Use: "sdk", RunE: func(cmd *cli.Command, args []string) error { return runBuildSDK(sdkSpec, sdkLang, sdkVersion, sdkDryRun) }, } +func setBuildI18n() { + buildCmd.Short = i18n.T("cmd.build.short") + buildCmd.Long = i18n.T("cmd.build.long") + fromPathCmd.Short = i18n.T("cmd.build.from_path.short") + pwaCmd.Short = i18n.T("cmd.build.pwa.short") + sdkBuildCmd.Short = i18n.T("cmd.build.sdk.short") + sdkBuildCmd.Long = i18n.T("cmd.build.sdk.long") +} + func initBuildFlags() { // Main build command flags buildCmd.Flags().StringVar(&buildType, "type", "", i18n.T("cmd.build.flag.type")) @@ -138,6 +141,7 @@ func initBuildFlags() { // AddBuildCommands registers the 'build' command and all subcommands. func AddBuildCommands(root *cli.Command) { + setBuildI18n() initBuildFlags() AddReleaseCommand(buildCmd) root.AddCommand(buildCmd) diff --git a/cmd/build/cmd_release.go b/cmd/build/cmd_release.go index 6293e19..61871d0 100644 --- a/cmd/build/cmd_release.go +++ b/cmd/build/cmd_release.go @@ -21,15 +21,18 @@ var ( ) var releaseCmd = &cli.Command{ - Use: "release", - Short: i18n.T("cmd.build.release.short"), - Long: i18n.T("cmd.build.release.long"), + Use: "release", RunE: func(cmd *cli.Command, args []string) error { return runRelease(cmd.Context(), !releaseGoForLaunch, releaseVersion, releaseDraft, releasePrerelease) }, } -func init() { +func setReleaseI18n() { + releaseCmd.Short = i18n.T("cmd.build.release.short") + releaseCmd.Long = i18n.T("cmd.build.release.long") +} + +func initReleaseFlags() { releaseCmd.Flags().BoolVar(&releaseGoForLaunch, "we-are-go-for-launch", false, i18n.T("cmd.build.release.flag.go_for_launch")) releaseCmd.Flags().StringVar(&releaseVersion, "version", "", i18n.T("cmd.build.release.flag.version")) releaseCmd.Flags().BoolVar(&releaseDraft, "draft", false, i18n.T("cmd.build.release.flag.draft")) @@ -38,6 +41,8 @@ func init() { // AddReleaseCommand adds the release subcommand to the build command. func AddReleaseCommand(buildCmd *cli.Command) { + setReleaseI18n() + initReleaseFlags() buildCmd.AddCommand(releaseCmd) } diff --git a/cmd/ci/ci.go b/cmd/ci/ci.go index 5999dc5..c976417 100644 --- a/cmd/ci/ci.go +++ b/cmd/ci/ci.go @@ -36,9 +36,7 @@ var ( ) var ciCmd = &cli.Command{ - Use: "ci", - Short: i18n.T("cmd.ci.short"), - Long: i18n.T("cmd.ci.long"), + Use: "ci", RunE: func(cmd *cli.Command, args []string) error { dryRun := !ciGoForLaunch return runCIPublish(dryRun, ciVersion, ciDraft, ciPrerelease) @@ -46,33 +44,38 @@ var ciCmd = &cli.Command{ } var ciInitCmd = &cli.Command{ - Use: "init", - Short: i18n.T("cmd.ci.init.short"), - Long: i18n.T("cmd.ci.init.long"), + Use: "init", RunE: func(cmd *cli.Command, args []string) error { return runCIReleaseInit() }, } var ciChangelogCmd = &cli.Command{ - Use: "changelog", - Short: i18n.T("cmd.ci.changelog.short"), - Long: i18n.T("cmd.ci.changelog.long"), + Use: "changelog", RunE: func(cmd *cli.Command, args []string) error { return runChangelog(changelogFromRef, changelogToRef) }, } var ciVersionCmd = &cli.Command{ - Use: "version", - Short: i18n.T("cmd.ci.version.short"), - Long: i18n.T("cmd.ci.version.long"), + Use: "version", RunE: func(cmd *cli.Command, args []string) error { return runCIReleaseVersion() }, } -func init() { +func setCII18n() { + ciCmd.Short = i18n.T("cmd.ci.short") + ciCmd.Long = i18n.T("cmd.ci.long") + ciInitCmd.Short = i18n.T("cmd.ci.init.short") + ciInitCmd.Long = i18n.T("cmd.ci.init.long") + ciChangelogCmd.Short = i18n.T("cmd.ci.changelog.short") + ciChangelogCmd.Long = i18n.T("cmd.ci.changelog.long") + ciVersionCmd.Short = i18n.T("cmd.ci.version.short") + ciVersionCmd.Long = i18n.T("cmd.ci.version.long") +} + +func initCIFlags() { // Main ci command flags ciCmd.Flags().BoolVar(&ciGoForLaunch, "we-are-go-for-launch", false, i18n.T("cmd.ci.flag.go_for_launch")) ciCmd.Flags().StringVar(&ciVersion, "version", "", i18n.T("cmd.ci.flag.version")) diff --git a/cmd/ci/cmd.go b/cmd/ci/cmd.go index c7508e7..dce91e4 100644 --- a/cmd/ci/cmd.go +++ b/cmd/ci/cmd.go @@ -19,5 +19,7 @@ func init() { // AddCICommands registers the 'ci' command and all subcommands. func AddCICommands(root *cli.Command) { + setCII18n() + initCIFlags() root.AddCommand(ciCmd) } diff --git a/cmd/sdk/cmd.go b/cmd/sdk/cmd.go index c367d9c..853b89e 100644 --- a/cmd/sdk/cmd.go +++ b/cmd/sdk/cmd.go @@ -30,18 +30,14 @@ var ( ) var sdkCmd = &cli.Command{ - Use: "sdk", - Short: i18n.T("cmd.sdk.short"), - Long: i18n.T("cmd.sdk.long"), + Use: "sdk", } var diffBasePath string var diffSpecPath string var sdkDiffCmd = &cli.Command{ - Use: "diff", - Short: i18n.T("cmd.sdk.diff.short"), - Long: i18n.T("cmd.sdk.diff.long"), + Use: "diff", RunE: func(cmd *cli.Command, args []string) error { return runSDKDiff(diffBasePath, diffSpecPath) }, @@ -50,16 +46,25 @@ var sdkDiffCmd = &cli.Command{ var validateSpecPath string var sdkValidateCmd = &cli.Command{ - Use: "validate", - Short: i18n.T("cmd.sdk.validate.short"), - Long: i18n.T("cmd.sdk.validate.long"), + Use: "validate", RunE: func(cmd *cli.Command, args []string) error { return runSDKValidate(validateSpecPath) }, } +func setSDKI18n() { + sdkCmd.Short = i18n.T("cmd.sdk.short") + sdkCmd.Long = i18n.T("cmd.sdk.long") + sdkDiffCmd.Short = i18n.T("cmd.sdk.diff.short") + sdkDiffCmd.Long = i18n.T("cmd.sdk.diff.long") + sdkValidateCmd.Short = i18n.T("cmd.sdk.validate.short") + sdkValidateCmd.Long = i18n.T("cmd.sdk.validate.long") +} + // AddSDKCommands registers the 'sdk' command and all subcommands. func AddSDKCommands(root *cli.Command) { + setSDKI18n() + // sdk diff flags sdkDiffCmd.Flags().StringVar(&diffBasePath, "base", "", i18n.T("cmd.sdk.diff.flag.base")) sdkDiffCmd.Flags().StringVar(&diffSpecPath, "spec", "", i18n.T("cmd.sdk.diff.flag.spec")) From 11050c1a319fa8f93d82f268da50b5981df46116 Mon Sep 17 00:00:00 2001 From: Snider Date: Tue, 17 Mar 2026 07:54:19 +0000 Subject: [PATCH 11/12] fix(dx): audit and fix conventions, broken build, and test coverage - CLAUDE.md: update error wrapping convention from fmt.Errorf to coreerr.E() - cmd/build: fix RegisterCommands API mismatch (cli v0.3.5 compat) - locales: self-register translations via i18n.RegisterLocales in init() - cmd/sdk: replace fmt.Printf with cli.Print for consistent output - cmd/build/cmd_pwa: replace os.Stat with coreio.Local.IsDir - pkg/build/builders: replace os.Stat with io.Local.IsFile for CLI checks - pkg/release/publishers/scoop: replace os.Stat with coreio.Local.IsDir - pkg/api: add tests for getBuilder, resolveDir, and medium initialisation Co-Authored-By: Virgil --- CLAUDE.md | 2 +- cmd/build/cmd_build.go | 4 ++-- cmd/build/cmd_pwa.go | 6 +----- cmd/sdk/cmd.go | 25 +++++++++++----------- locales/embed.go | 10 ++++++++- pkg/api/provider_test.go | 38 +++++++++++++++++++++++++++++++++ pkg/build/builders/linuxkit.go | 2 +- pkg/build/builders/taskfile.go | 2 +- pkg/release/publishers/scoop.go | 2 +- 9 files changed, 66 insertions(+), 25 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 64c06eb..278880b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -60,7 +60,7 @@ All file operations use `io.Medium` from `forge.lthn.ai/core/go-io`. Production - **UK English** in comments and strings (colour, organisation, notarisation) - **Strict types** — all parameters and return types explicitly typed -- **Error wrapping** — `fmt.Errorf("package.Function: message: %w", err)` +- **Error wrapping** — `coreerr.E("package.Function", "message", err)` via `coreerr "forge.lthn.ai/core/go-log"` - **testify** (`assert`/`require`) for assertions - **Test naming** — `_Good` (happy path), `_Bad` (expected errors), `_Ugly` (edge cases) - **Conventional commits** — `type(scope): description` diff --git a/cmd/build/cmd_build.go b/cmd/build/cmd_build.go index bf14040..58176fe 100644 --- a/cmd/build/cmd_build.go +++ b/cmd/build/cmd_build.go @@ -5,12 +5,12 @@ import ( "embed" "forge.lthn.ai/core/cli/pkg/cli" - "forge.lthn.ai/core/go-build/locales" + _ "forge.lthn.ai/core/go-build/locales" // registers locale translations "forge.lthn.ai/core/go-i18n" ) func init() { - cli.RegisterCommands(AddBuildCommands, locales.FS) + cli.RegisterCommands(AddBuildCommands) } // Style aliases from shared package diff --git a/cmd/build/cmd_pwa.go b/cmd/build/cmd_pwa.go index 1a89504..6444783 100644 --- a/cmd/build/cmd_pwa.go +++ b/cmd/build/cmd_pwa.go @@ -222,11 +222,7 @@ func downloadAsset(assetURL, destDir string) error { func runBuild(fromPath string) error { fmt.Printf("%s %s\n", i18n.T("cmd.build.from_path.starting"), fromPath) - info, err := os.Stat(fromPath) - if err != nil { - return coreerr.E("pwa.runBuild", i18n.T("cmd.build.from_path.error.invalid_path"), err) - } - if !info.IsDir() { + if !coreio.Local.IsDir(fromPath) { return coreerr.E("pwa.runBuild", i18n.T("cmd.build.from_path.error.must_be_directory"), nil) } diff --git a/cmd/sdk/cmd.go b/cmd/sdk/cmd.go index 853b89e..92c139f 100644 --- a/cmd/sdk/cmd.go +++ b/cmd/sdk/cmd.go @@ -8,11 +8,10 @@ package sdkcmd import ( - "fmt" "os" - "forge.lthn.ai/core/go-build/pkg/sdk" "forge.lthn.ai/core/cli/pkg/cli" + "forge.lthn.ai/core/go-build/pkg/sdk" "forge.lthn.ai/core/go-i18n" coreerr "forge.lthn.ai/core/go-log" ) @@ -97,10 +96,10 @@ func runSDKDiff(basePath, specPath string) error { return coreerr.E("sdk.Diff", i18n.T("cmd.sdk.diff.error.base_required"), nil) } - fmt.Printf("%s %s\n", sdkHeaderStyle.Render(i18n.T("cmd.sdk.diff.label")), i18n.ProgressSubject("check", "breaking changes")) - fmt.Printf(" %s %s\n", i18n.T("cmd.sdk.diff.base_label"), sdkDimStyle.Render(basePath)) - fmt.Printf(" %s %s\n", i18n.Label("current"), sdkDimStyle.Render(specPath)) - fmt.Println() + cli.Print("%s %s\n", sdkHeaderStyle.Render(i18n.T("cmd.sdk.diff.label")), i18n.ProgressSubject("check", "breaking changes")) + cli.Print(" %s %s\n", i18n.T("cmd.sdk.diff.base_label"), sdkDimStyle.Render(basePath)) + cli.Print(" %s %s\n", i18n.Label("current"), sdkDimStyle.Render(specPath)) + cli.Blank() result, err := sdk.Diff(basePath, specPath) if err != nil { @@ -108,14 +107,14 @@ func runSDKDiff(basePath, specPath string) error { } if result.Breaking { - fmt.Printf("%s %s\n", sdkErrorStyle.Render(i18n.T("cmd.sdk.diff.breaking")), result.Summary) + cli.Print("%s %s\n", sdkErrorStyle.Render(i18n.T("cmd.sdk.diff.breaking")), result.Summary) for _, change := range result.Changes { - fmt.Printf(" - %s\n", change) + cli.Print(" - %s\n", change) } return cli.Exit(1, cli.Err("%s", result.Summary)) } - fmt.Printf("%s %s\n", sdkSuccessStyle.Render(i18n.T("cmd.sdk.label.ok")), result.Summary) + cli.Print("%s %s\n", sdkSuccessStyle.Render(i18n.T("cmd.sdk.label.ok")), result.Summary) return nil } @@ -127,15 +126,15 @@ func runSDKValidate(specPath string) error { s := sdk.New(projectDir, &sdk.Config{Spec: specPath}) - fmt.Printf("%s %s\n", sdkHeaderStyle.Render(i18n.T("cmd.sdk.label.sdk")), i18n.T("cmd.sdk.validate.validating")) + cli.Print("%s %s\n", sdkHeaderStyle.Render(i18n.T("cmd.sdk.label.sdk")), i18n.T("cmd.sdk.validate.validating")) detectedPath, err := s.DetectSpec() if err != nil { - fmt.Printf("%s %v\n", sdkErrorStyle.Render(i18n.Label("error")), err) + cli.Print("%s %v\n", sdkErrorStyle.Render(i18n.Label("error")), err) return err } - fmt.Printf(" %s %s\n", i18n.Label("spec"), sdkDimStyle.Render(detectedPath)) - fmt.Printf("%s %s\n", sdkSuccessStyle.Render(i18n.T("cmd.sdk.label.ok")), i18n.T("cmd.sdk.validate.valid")) + cli.Print(" %s %s\n", i18n.Label("spec"), sdkDimStyle.Render(detectedPath)) + cli.Print("%s %s\n", sdkSuccessStyle.Render(i18n.T("cmd.sdk.label.ok")), i18n.T("cmd.sdk.validate.valid")) return nil } diff --git a/locales/embed.go b/locales/embed.go index 410cb55..ac9a964 100644 --- a/locales/embed.go +++ b/locales/embed.go @@ -1,7 +1,15 @@ // Package locales embeds translation files for this module. package locales -import "embed" +import ( + "embed" + + "forge.lthn.ai/core/go-i18n" +) //go:embed *.json var FS embed.FS + +func init() { + i18n.RegisterLocales(FS, ".") +} diff --git a/pkg/api/provider_test.go b/pkg/api/provider_test.go index dccad3c..d559b8d 100644 --- a/pkg/api/provider_test.go +++ b/pkg/api/provider_test.go @@ -3,9 +3,12 @@ package api import ( + "os" "testing" + "forge.lthn.ai/core/go-build/pkg/build" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestBuildProvider_Good_Identity(t *testing.T) { @@ -75,3 +78,38 @@ func TestBuildProvider_Good_NilHub(t *testing.T) { // emitEvent should not panic with nil hub p.emitEvent("build.started", map[string]any{"test": true}) } + +func TestGetBuilder_Good_SupportedTypes(t *testing.T) { + b, err := getBuilder(build.ProjectTypeGo) + require.NoError(t, err) + assert.Equal(t, "go", b.Name()) + + b, err = getBuilder(build.ProjectTypeWails) + require.NoError(t, err) + assert.Equal(t, "wails", b.Name()) +} + +func TestGetBuilder_Bad_UnsupportedType(t *testing.T) { + _, err := getBuilder(build.ProjectType("unknown")) + assert.ErrorIs(t, err, os.ErrNotExist) +} + +func TestBuildProvider_Good_ResolveDir(t *testing.T) { + p := NewProvider("/tmp", nil) + dir, err := p.resolveDir() + require.NoError(t, err) + assert.Equal(t, "/tmp", dir) +} + +func TestBuildProvider_Good_ResolveDirRelative(t *testing.T) { + p := NewProvider(".", nil) + dir, err := p.resolveDir() + require.NoError(t, err) + // Should return an absolute path + assert.True(t, len(dir) > 1 && dir[0] == '/') +} + +func TestBuildProvider_Good_MediumSet(t *testing.T) { + p := NewProvider(".", nil) + assert.NotNil(t, p.medium, "medium should be set to io.Local") +} diff --git a/pkg/build/builders/linuxkit.go b/pkg/build/builders/linuxkit.go index bcc7976..ef254fe 100644 --- a/pkg/build/builders/linuxkit.go +++ b/pkg/build/builders/linuxkit.go @@ -262,7 +262,7 @@ func (b *LinuxKitBuilder) validateLinuxKitCli() error { } for _, p := range paths { - if _, err := os.Stat(p); err == nil { + if io.Local.IsFile(p) { return nil } } diff --git a/pkg/build/builders/taskfile.go b/pkg/build/builders/taskfile.go index ca3e807..de68e87 100644 --- a/pkg/build/builders/taskfile.go +++ b/pkg/build/builders/taskfile.go @@ -267,7 +267,7 @@ func (b *TaskfileBuilder) validateTaskCli() error { } for _, p := range paths { - if _, err := os.Stat(p); err == nil { + if io.Local.IsFile(p) { return nil } } diff --git a/pkg/release/publishers/scoop.go b/pkg/release/publishers/scoop.go index af54091..8b15ba0 100644 --- a/pkg/release/publishers/scoop.go +++ b/pkg/release/publishers/scoop.go @@ -210,7 +210,7 @@ func (p *ScoopPublisher) commitToBucket(ctx context.Context, bucket string, data // Ensure bucket directory exists bucketDir := filepath.Join(tmpDir, "bucket") - if _, err := os.Stat(bucketDir); os.IsNotExist(err) { + if !coreio.Local.IsDir(bucketDir) { bucketDir = tmpDir // Some repos put manifests in root } From 49fb446739ef026d6d50eb3212b63f6c3cb4d8ef Mon Sep 17 00:00:00 2001 From: Snider Date: Tue, 17 Mar 2026 13:46:48 +0000 Subject: [PATCH 12/12] fix(coderabbit): address review findings - chocolatey: pass API key via env var instead of CLI arg - npm: remove unused cfg parameter from dryRunPublish - signing: include checksums file path in error message - codesign: provide specific reason when codesign unavailable - scoop: handle error from custom template read - wails: wrap Read/Write errors with coreerr.E - github: include actual error in validateGhCli coreerr.E calls - docker: include actual error in validate/buildx coreerr.E calls Co-Authored-By: Virgil --- pkg/build/builders/docker.go | 4 ++-- pkg/build/builders/wails.go | 4 ++-- pkg/build/signing/codesign.go | 8 +++++++- pkg/build/signing/sign.go | 2 +- pkg/release/publishers/chocolatey.go | 5 +++-- pkg/release/publishers/github.go | 4 ++-- pkg/release/publishers/npm.go | 4 ++-- pkg/release/publishers/npm_test.go | 13 ++----------- pkg/release/publishers/scoop.go | 5 +++-- 9 files changed, 24 insertions(+), 25 deletions(-) diff --git a/pkg/build/builders/docker.go b/pkg/build/builders/docker.go index ba6b35a..beb7cfd 100644 --- a/pkg/build/builders/docker.go +++ b/pkg/build/builders/docker.go @@ -187,7 +187,7 @@ func (b *DockerBuilder) Build(ctx context.Context, cfg *build.Config, targets [] func (b *DockerBuilder) validateDockerCli() error { cmd := exec.Command("docker", "--version") if err := cmd.Run(); err != nil { - return coreerr.E("DockerBuilder.validateDockerCli", "docker CLI not found. Install it from https://docs.docker.com/get-docker/", nil) + return coreerr.E("DockerBuilder.validateDockerCli", "docker CLI not found. Install it from https://docs.docker.com/get-docker/", err) } return nil } @@ -197,7 +197,7 @@ func (b *DockerBuilder) ensureBuildx(ctx context.Context) error { // Check if buildx is available cmd := exec.CommandContext(ctx, "docker", "buildx", "version") if err := cmd.Run(); err != nil { - return coreerr.E("DockerBuilder.ensureBuildx", "buildx is not available. Install it from https://docs.docker.com/buildx/working-with-buildx/", nil) + return coreerr.E("DockerBuilder.ensureBuildx", "buildx is not available. Install it from https://docs.docker.com/buildx/working-with-buildx/", err) } // Check if we have a builder, create one if not diff --git a/pkg/build/builders/wails.go b/pkg/build/builders/wails.go index e58bfa2..4e40e16 100644 --- a/pkg/build/builders/wails.go +++ b/pkg/build/builders/wails.go @@ -144,10 +144,10 @@ func (b *WailsBuilder) buildV2Target(ctx context.Context, cfg *build.Config, tar // Simple copy using the medium content, err := cfg.FS.Read(sourcePath) if err != nil { - return build.Artifact{}, err + return build.Artifact{}, coreerr.E("WailsBuilder.buildV2Target", "failed to read artifact "+sourcePath, err) } if err := cfg.FS.Write(destPath, content); err != nil { - return build.Artifact{}, err + return build.Artifact{}, coreerr.E("WailsBuilder.buildV2Target", "failed to write artifact "+destPath, err) } return build.Artifact{ diff --git a/pkg/build/signing/codesign.go b/pkg/build/signing/codesign.go index c8aeaf1..5357759 100644 --- a/pkg/build/signing/codesign.go +++ b/pkg/build/signing/codesign.go @@ -42,7 +42,13 @@ func (s *MacOSSigner) Available() bool { // Sign codesigns a binary with hardened runtime. func (s *MacOSSigner) Sign(ctx context.Context, fs io.Medium, binary string) error { if !s.Available() { - return coreerr.E("codesign.Sign", "codesign not available", nil) + if runtime.GOOS != "darwin" { + return coreerr.E("codesign.Sign", "codesign is only available on macOS", nil) + } + if s.config.Identity == "" { + return coreerr.E("codesign.Sign", "codesign identity not configured", nil) + } + return coreerr.E("codesign.Sign", "codesign tool not found in PATH", nil) } cmd := exec.CommandContext(ctx, "codesign", diff --git a/pkg/build/signing/sign.go b/pkg/build/signing/sign.go index 3e17a29..0539999 100644 --- a/pkg/build/signing/sign.go +++ b/pkg/build/signing/sign.go @@ -90,7 +90,7 @@ func SignChecksums(ctx context.Context, fs io.Medium, cfg SignConfig, checksumFi fmt.Printf(" Signing %s with GPG...\n", checksumFile) if err := signer.Sign(ctx, fs, checksumFile); err != nil { - return coreerr.E("signing.SignChecksums", "failed to sign checksums", err) + return coreerr.E("signing.SignChecksums", "failed to sign checksums file "+checksumFile, err) } return nil diff --git a/pkg/release/publishers/chocolatey.go b/pkg/release/publishers/chocolatey.go index 04f1a25..7df2b0e 100644 --- a/pkg/release/publishers/chocolatey.go +++ b/pkg/release/publishers/chocolatey.go @@ -245,8 +245,9 @@ func (p *ChocolateyPublisher) pushToChocolatey(ctx context.Context, packageDir s return coreerr.E("chocolatey.Publish", "choco pack failed", err) } - // Push the package - cmd = exec.CommandContext(ctx, "choco", "push", nupkgPath, "--source", "https://push.chocolatey.org/", "--api-key", apiKey) + // Push the package — pass API key via environment variable to avoid exposing it in process listings + cmd = exec.CommandContext(ctx, "choco", "push", nupkgPath, "--source", "https://push.chocolatey.org/") + cmd.Env = append(os.Environ(), "chocolateyApiKey="+apiKey) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { diff --git a/pkg/release/publishers/github.go b/pkg/release/publishers/github.go index 0c02fef..2efb1af 100644 --- a/pkg/release/publishers/github.go +++ b/pkg/release/publishers/github.go @@ -148,14 +148,14 @@ func validateGhCli() error { // Check if gh is installed cmd := exec.Command("gh", "--version") if err := cmd.Run(); err != nil { - return coreerr.E("github.validateGhCli", "gh CLI not found. Install it from https://cli.github.com", nil) + return coreerr.E("github.validateGhCli", "gh CLI not found. Install it from https://cli.github.com", err) } // Check if authenticated cmd = exec.Command("gh", "auth", "status") output, err := cmd.CombinedOutput() if err != nil { - return coreerr.E("github.validateGhCli", "not authenticated with gh CLI. Run 'gh auth login' first", nil) + return coreerr.E("github.validateGhCli", "not authenticated with gh CLI. Run 'gh auth login' first", err) } if !strings.Contains(string(output), "Logged in") { diff --git a/pkg/release/publishers/npm.go b/pkg/release/publishers/npm.go index 7ab9429..da246ef 100644 --- a/pkg/release/publishers/npm.go +++ b/pkg/release/publishers/npm.go @@ -91,7 +91,7 @@ func (p *NpmPublisher) Publish(ctx context.Context, release *Release, pubCfg Pub } if dryRun { - return p.dryRunPublish(release.FS, data, &npmCfg) + return p.dryRunPublish(release.FS, data) } return p.executePublish(ctx, release.FS, data, &npmCfg) @@ -130,7 +130,7 @@ type npmTemplateData struct { } // dryRunPublish shows what would be done without actually publishing. -func (p *NpmPublisher) dryRunPublish(m coreio.Medium, data npmTemplateData, cfg *NpmConfig) error { +func (p *NpmPublisher) dryRunPublish(m coreio.Medium, data npmTemplateData) error { fmt.Println() fmt.Println("=== DRY RUN: npm Publish ===") fmt.Println() diff --git a/pkg/release/publishers/npm_test.go b/pkg/release/publishers/npm_test.go index ff21819..aeeb24f 100644 --- a/pkg/release/publishers/npm_test.go +++ b/pkg/release/publishers/npm_test.go @@ -161,12 +161,7 @@ func TestNpmPublisher_DryRunPublish_Good(t *testing.T) { BinaryName: "mycli", Description: "My CLI", } - cfg := &NpmConfig{ - Package: "@myorg/mycli", - Access: "public", - } - - err := p.dryRunPublish(io.Local, data, cfg) + err := p.dryRunPublish(io.Local, data) _ = w.Close() var buf bytes.Buffer @@ -199,12 +194,8 @@ func TestNpmPublisher_DryRunPublish_Good(t *testing.T) { Repository: "org/repo", BinaryName: "cli", } - cfg := &NpmConfig{ - Package: "@private/cli", - Access: "restricted", - } - err := p.dryRunPublish(io.Local, data, cfg) + err := p.dryRunPublish(io.Local, data) _ = w.Close() var buf bytes.Buffer diff --git a/pkg/release/publishers/scoop.go b/pkg/release/publishers/scoop.go index af54091..9a7132c 100644 --- a/pkg/release/publishers/scoop.go +++ b/pkg/release/publishers/scoop.go @@ -255,9 +255,10 @@ func (p *ScoopPublisher) renderTemplate(m coreio.Medium, name string, data scoop customPath := filepath.Join(".core", name) if m != nil && m.IsFile(customPath) { customContent, err := m.Read(customPath) - if err == nil { - content = []byte(customContent) + if err != nil { + return "", coreerr.E("scoop.renderTemplate", "failed to read custom template "+customPath, err) } + content = []byte(customContent) } // Fallback to embedded template