Merge branch 'main' into ui

Ayman Bagabas created

Change summary

.github/workflows/build.yml         |  2 +-
.github/workflows/schema-update.yml |  2 +-
.github/workflows/security.yml      | 12 ++++++------
internal/app/app.go                 |  9 ++++++++-
internal/env/env.go                 | 13 +------------
internal/env/env_test.go            |  6 ++++--
internal/lsp/client.go              |  2 +-
internal/message/message.go         | 10 +++++-----
internal/pubsub/broker.go           |  4 +---
internal/pubsub/events.go           |  7 -------
internal/tui/tui.go                 |  2 +-
11 files changed, 29 insertions(+), 40 deletions(-)

Detailed changes

.github/workflows/build.yml 🔗

@@ -18,7 +18,7 @@ jobs:
       - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
         with:
           persist-credentials: false
-      - uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
+      - uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
         with:
           go-version-file: go.mod
       - run: go mod tidy

.github/workflows/schema-update.yml 🔗

@@ -14,7 +14,7 @@ jobs:
       - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
         with:
           token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
-      - uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
+      - uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
         with:
           go-version-file: go.mod
       - run: go run . schema > ./schema.json

.github/workflows/security.yml 🔗

@@ -30,11 +30,11 @@ jobs:
       - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
         with:
           persist-credentials: false
-      - uses: github/codeql-action/init@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v4.31.7
+      - uses: github/codeql-action/init@cdefb33c0f6224e58673d9004f47f7cb3e328b89 # v4.31.10
         with:
           languages: ${{ matrix.language }}
-      - uses: github/codeql-action/autobuild@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v4.31.7
-      - uses: github/codeql-action/analyze@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v4.31.7
+      - uses: github/codeql-action/autobuild@cdefb33c0f6224e58673d9004f47f7cb3e328b89 # v4.31.10
+      - uses: github/codeql-action/analyze@cdefb33c0f6224e58673d9004f47f7cb3e328b89 # v4.31.10
 
   grype:
     runs-on: ubuntu-latest
@@ -46,13 +46,13 @@ jobs:
       - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
         with:
           persist-credentials: false
-      - uses: anchore/scan-action@40a61b52209e9d50e87917c5b901783d546b12d0 # v7.2.1
+      - uses: anchore/scan-action@62b74fb7bb810d2c45b1865f47a77655621862a5 # v7.2.3
         id: scan
         with:
           path: "."
           fail-build: true
           severity-cutoff: critical
-      - uses: github/codeql-action/upload-sarif@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v4.31.7
+      - uses: github/codeql-action/upload-sarif@cdefb33c0f6224e58673d9004f47f7cb3e328b89 # v4.31.10
         with:
           sarif_file: ${{ steps.scan.outputs.sarif }}
 
@@ -73,7 +73,7 @@ jobs:
       - name: Run govulncheck
         run: |
           govulncheck -C . -format sarif ./... > results.sarif
-      - uses: github/codeql-action/upload-sarif@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v4.31.7
+      - uses: github/codeql-action/upload-sarif@cdefb33c0f6224e58673d9004f47f7cb3e328b89 # v4.31.10
         with:
           sarif_file: results.sarif
 

internal/app/app.go 🔗

@@ -40,6 +40,13 @@ import (
 	"github.com/charmbracelet/x/term"
 )
 
+// UpdateAvailableMsg is sent when a new version is available.
+type UpdateAvailableMsg struct {
+	CurrentVersion string
+	LatestVersion  string
+	IsDevelopment  bool
+}
+
 type App struct {
 	Sessions    session.Service
 	Messages    message.Service
@@ -452,7 +459,7 @@ func (app *App) checkForUpdates(ctx context.Context) {
 	if err != nil || !info.Available() {
 		return
 	}
-	app.events <- pubsub.UpdateAvailableMsg{
+	app.events <- UpdateAvailableMsg{
 		CurrentVersion: info.Current,
 		LatestVersion:  info.Latest,
 		IsDevelopment:  info.IsDevelopment(),

internal/env/env.go 🔗

@@ -2,7 +2,6 @@ package env
 
 import (
 	"os"
-	"testing"
 )
 
 type Env interface {
@@ -18,17 +17,10 @@ func (o *osEnv) Get(key string) string {
 }
 
 func (o *osEnv) Env() []string {
-	env := os.Environ()
-	if len(env) == 0 {
-		return nil
-	}
-	return env
+	return os.Environ()
 }
 
 func New() Env {
-	if testing.Testing() {
-		return NewFromMap(nil)
-	}
 	return &osEnv{}
 }
 
@@ -46,9 +38,6 @@ func (m *mapEnv) Get(key string) string {
 
 // Env implements Env.
 func (m *mapEnv) Env() []string {
-	if len(m.m) == 0 {
-		return nil
-	}
 	env := make([]string, 0, len(m.m))
 	for k, v := range m.m {
 		env = append(env, k+"="+v)

internal/env/env_test.go 🔗

@@ -90,13 +90,15 @@ func TestMapEnv_Env(t *testing.T) {
 	t.Run("empty map", func(t *testing.T) {
 		env := NewFromMap(map[string]string{})
 		envVars := env.Env()
-		require.Nil(t, envVars)
+		require.NotNil(t, envVars)
+		require.Len(t, envVars, 0)
 	})
 
 	t.Run("nil map", func(t *testing.T) {
 		env := NewFromMap(nil)
 		envVars := env.Env()
-		require.Nil(t, envVars)
+		require.NotNil(t, envVars)
+		require.Len(t, envVars, 0)
 	})
 }
 

internal/lsp/client.go 🔗

@@ -345,7 +345,7 @@ func (c *Client) CloseAllFiles(ctx context.Context) {
 			slog.Debug("Closing file", "file", uri)
 		}
 		if err := c.client.NotifyDidCloseTextDocument(ctx, uri); err != nil {
-			slog.Warn("Error closing rile", "uri", uri, "error", err)
+			slog.Warn("Error closing file", "uri", uri, "error", err)
 			continue
 		}
 		c.openFiles.Del(uri)

internal/message/message.go 🔗

@@ -63,7 +63,7 @@ func (s *service) Create(ctx context.Context, sessionID string, params CreateMes
 			Reason: "stop",
 		})
 	}
-	partsJSON, err := marshallParts(params.Parts)
+	partsJSON, err := marshalParts(params.Parts)
 	if err != nil {
 		return Message{}, err
 	}
@@ -110,7 +110,7 @@ func (s *service) DeleteSessionMessages(ctx context.Context, sessionID string) e
 }
 
 func (s *service) Update(ctx context.Context, message Message) error {
-	parts, err := marshallParts(message.Parts)
+	parts, err := marshalParts(message.Parts)
 	if err != nil {
 		return err
 	}
@@ -158,7 +158,7 @@ func (s *service) List(ctx context.Context, sessionID string) ([]Message, error)
 }
 
 func (s *service) fromDBItem(item db.Message) (Message, error) {
-	parts, err := unmarshallParts([]byte(item.Parts))
+	parts, err := unmarshalParts([]byte(item.Parts))
 	if err != nil {
 		return Message{}, err
 	}
@@ -192,7 +192,7 @@ type partWrapper struct {
 	Data ContentPart `json:"data"`
 }
 
-func marshallParts(parts []ContentPart) ([]byte, error) {
+func marshalParts(parts []ContentPart) ([]byte, error) {
 	wrappedParts := make([]partWrapper, len(parts))
 
 	for i, part := range parts {
@@ -225,7 +225,7 @@ func marshallParts(parts []ContentPart) ([]byte, error) {
 	return json.Marshal(wrappedParts)
 }
 
-func unmarshallParts(data []byte) ([]ContentPart, error) {
+func unmarshalParts(data []byte) ([]ContentPart, error) {
 	temp := []json.RawMessage{}
 
 	if err := json.Unmarshal(data, &temp); err != nil {

internal/pubsub/broker.go 🔗

@@ -20,13 +20,11 @@ func NewBroker[T any]() *Broker[T] {
 }
 
 func NewBrokerWithOptions[T any](channelBufferSize, maxEvents int) *Broker[T] {
-	b := &Broker[T]{
+	return &Broker[T]{
 		subs:      make(map[chan Event[T]]struct{}),
 		done:      make(chan struct{}),
-		subCount:  0,
 		maxEvents: maxEvents,
 	}
-	return b
 }
 
 func (b *Broker[T]) Shutdown() {

internal/pubsub/events.go 🔗

@@ -26,10 +26,3 @@ type (
 		Publish(EventType, T)
 	}
 )
-
-// UpdateAvailableMsg is sent when a new version is available.
-type UpdateAvailableMsg struct {
-	CurrentVersion string
-	LatestVersion  string
-	IsDevelopment  bool
-}

internal/tui/tui.go 🔗

@@ -385,7 +385,7 @@ func (a *appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 		}
 		return a, tea.Batch(cmds...)
 	// Update Available
-	case pubsub.UpdateAvailableMsg:
+	case app.UpdateAvailableMsg:
 		// Show update notification in status bar
 		statusMsg := fmt.Sprintf("Crush update available: v%s → v%s.", msg.CurrentVersion, msg.LatestVersion)
 		if msg.IsDevelopment {