Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
version: 2
updates:
# Monitor Go dependencies (go.mod)
- package-ecosystem: "gomod"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 10
# Group minor and patch updates to reduce the number of PRs and branches
groups:
go-dependencies:
patterns:
- "*"
update-types:
- "minor"
- "patch"

# Monitor GitHub Actions workflows
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
134 changes: 134 additions & 0 deletions .github/workflows/go-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
name: Go CI

on:
push:
branches: ["main"]
paths:
- "**/*.go"
- "go.mod"
- "go.sum"
- ".golangci.yml"
- "db/queries/**/*.sql"
- "db/migrations/**/*.sql"
- ".github/workflows/go-ci.yml"
pull_request:
branches: ["main"]
paths:
- "**/*.go"
- "go.mod"
- "go.sum"
- ".golangci.yml"
- "db/queries/**/*.sql"
- "db/migrations/**/*.sql"
- ".github/workflows/go-ci.yml"

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
lint:
name: Lint
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true
- name: golangci-lint
uses: golangci/golangci-lint-action@v9
with:
version: v2.9.0

test:
name: Test
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true
- name: Run Tests
run: |
go test -v -race -coverprofile=coverage.txt -covermode=atomic ./...
- name: Upload coverage to Codecov
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false }}
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: ./coverage.txt
fail_ci_if_error: false # Don't fail if Codecov upload fails
name: Go CI

on:
push:
branches: ["main"]
paths:
- "**/*.go"
- "go.mod"
- "go.sum"
- ".golangci.yml"
- "db/queries/**/*.sql"
- "db/migrations/**/*.sql"
- ".github/workflows/go-ci.yml"
pull_request:
branches: ["main"]
paths:
- "**/*.go"
- "go.mod"
- "go.sum"
- ".golangci.yml"
- "db/queries/**/*.sql"
- "db/migrations/**/*.sql"
- ".github/workflows/go-ci.yml"

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
lint:
name: Lint
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true
- name: golangci-lint
uses: golangci/golangci-lint-action@v9
with:
version: v2.9.0

test:
name: Test
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true
- name: Run Tests
run: |
go test -v -race -coverprofile=coverage.txt -covermode=atomic ./...
- name: Upload coverage to Codecov
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false }}
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: ./coverage.txt
fail_ci_if_error: false # Don't fail if Codecov upload fails
111 changes: 111 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
version: "2"

run:
timeout: 15m
go: "1.25"
tests: true

output:
show-stats: false

issues:
max-issues-per-linter: 0
max-same-issues: 0

linters:
default: none
enable:
- errcheck
- errorlint
- exptostd
- fatcontext
- gocritic
- godot
- govet
- ineffassign
- misspell
- modernize
- nilnesserr
- perfsprint
- predeclared
- revive
- sloglint
- staticcheck
- testifylint
- unconvert
- unused
- usestdlibvars
- whitespace
exclusions:
paths:
- internal/db/sqlc
- ^.*\.(pb|l|y)\.go$

formatters:
enable:
- gci
- gofumpt
- goimports
settings:
gci:
sections:
- standard
- default
- prefix(github.com/memohai/memoh)
gofumpt:
extra-rules: false
goimports:
local-prefixes:
- github.com/memohai/memoh

linters-settings:
govet:
enable-all: true
disable:
- shadow
- fieldalignment
gocyclo:
min-complexity: 10
funlen:
lines: 60
statements: 30
modernize:
disable:
- omitzero
perfsprint:
int-conversion: true
err-error: true
errorf: true
sprintf1: true
strconcat: false
concat-loop: false
revive:
rules:
- name: blank-imports
- name: comment-spacings
- name: context-as-argument
arguments:
- allowTypesBefore: "*testing.T,testing.TB"
- name: dot-imports
- name: error-naming
- name: error-return
- name: error-strings
- name: exported
- name: increment-decrement
- name: var-naming
- name: var-declaration
- name: unreachable-code
- name: unused-parameter
- name: unused-receiver
sloglint:
attr-only: true
no-global: default
context-only: scope
static-msg: true
key-naming-case: snake
forbidden-keys: [time, level, msg, source]
testifylint:
enable-all: true
disable:
- float-compare
- go-require
19 changes: 10 additions & 9 deletions cmd/agent/main.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// Package main is the entry point for the Memoh agent server (HTTP API, channels, containers).
package main

import (
Expand Down Expand Up @@ -192,7 +193,7 @@ func provideContainerdClient(lc fx.Lifecycle, rc *boot.RuntimeConfig) (*containe
return nil, fmt.Errorf("connect containerd: %w", err)
}
lc.Append(fx.Hook{
OnStop: func(ctx context.Context) error {
OnStop: func(_ context.Context) error {
return client.Close()
},
})
Expand All @@ -205,7 +206,7 @@ func provideDBConn(lc fx.Lifecycle, cfg config.Config) (*pgxpool.Pool, error) {
return nil, fmt.Errorf("db connect: %w", err)
}
lc.Append(fx.Hook{
OnStop: func(ctx context.Context) error {
OnStop: func(_ context.Context) error {
conn.Close()
return nil
},
Expand Down Expand Up @@ -321,8 +322,8 @@ func provideChatResolver(log *slog.Logger, cfg config.Config, modelsService *mod

func provideChannelRegistry(log *slog.Logger, hub *local.RouteHub) *channel.Registry {
registry := channel.NewRegistry()
registry.MustRegister(telegram.NewTelegramAdapter(log))
registry.MustRegister(feishu.NewFeishuAdapter(log))
registry.MustRegister(telegram.NewAdapter(log))
registry.MustRegister(feishu.NewAdapter(log))
registry.MustRegister(local.NewCLIAdapter(hub))
registry.MustRegister(local.NewWebAdapter(hub))
return registry
Expand Down Expand Up @@ -383,7 +384,7 @@ func provideMemoryHandler(log *slog.Logger, service *memory.Service, chatService
if strings.TrimSpace(execWorkDir) == "" {
execWorkDir = config.DefaultDataMount
}
h.SetMemoryFS(memory.NewMemoryFS(log, manager, execWorkDir))
h.SetMemoryFS(memory.NewFS(log, manager, execWorkDir))
}
return h
}
Expand Down Expand Up @@ -435,7 +436,7 @@ func provideServer(params serverParams) *server.Server {

func startMemoryWarmup(lc fx.Lifecycle, memoryService *memory.Service, logger *slog.Logger) {
lc.Append(fx.Hook{
OnStart: func(ctx context.Context) error {
OnStart: func(_ context.Context) error {
go func() {
if err := memoryService.WarmupBM25(context.Background(), 200); err != nil {
logger.Warn("bm25 warmup failed", slog.Any("error", err))
Expand Down Expand Up @@ -526,7 +527,7 @@ func buildTextEmbedder(resolver *embeddings.Resolver, textModel models.GetRespon

func ensureAdminUser(ctx context.Context, log *slog.Logger, queries *dbsqlc.Queries, cfg config.Config) error {
if queries == nil {
return fmt.Errorf("db queries not configured")
return errors.New("db queries not configured")
}
count, err := queries.CountAccounts(ctx)
if err != nil {
Expand All @@ -540,7 +541,7 @@ func ensureAdminUser(ctx context.Context, log *slog.Logger, queries *dbsqlc.Quer
password := strings.TrimSpace(cfg.Admin.Password)
email := strings.TrimSpace(cfg.Admin.Email)
if username == "" || password == "" {
return fmt.Errorf("admin username/password required in config.toml")
return errors.New("admin username/password required in config.toml")
}
if password == "change-your-password-here" {
log.Warn("admin password uses default placeholder; please update config.toml")
Expand Down Expand Up @@ -629,7 +630,7 @@ func (c *lazyLLMClient) DetectLanguage(ctx context.Context, text string) (string

func (c *lazyLLMClient) resolve(ctx context.Context) (memory.LLM, error) {
if c.modelsService == nil || c.queries == nil {
return nil, fmt.Errorf("models service not configured")
return nil, errors.New("models service not configured")
}
memoryModel, memoryProvider, err := models.SelectMemoryModel(ctx, c.modelsService, c.queries)
if err != nil {
Expand Down
9 changes: 6 additions & 3 deletions cmd/cli/main.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
// Package main is the entry point for the Memoh CLI.
package main

import (
"context"
"encoding/json"
"errors"
"flag"
"fmt"
"io"
Expand Down Expand Up @@ -42,7 +44,8 @@ func main() {

cmd := buildMCPCommand(*containerID)
if err := runWithStdio(cmd); err != nil {
if exitErr, ok := err.(*exec.ExitError); ok {
exitErr := &exec.ExitError{}
if errors.As(err, &exitErr) {
os.Exit(exitErr.ExitCode())
}
os.Exit(1)
Expand Down Expand Up @@ -214,10 +217,10 @@ func parseCNIArgs(args []string) (string, string, error) {
return "", "", err
}
if *id == "" {
return "", "", fmt.Errorf("missing --id")
return "", "", errors.New("missing --id")
}
if *netns == "" && *pid == 0 {
return "", "", fmt.Errorf("missing --netns or --pid")
return "", "", errors.New("missing --netns or --pid")
}
if *netns == "" {
*netns = filepath.Join("/proc", strconv.Itoa(*pid), "ns", "net")
Expand Down
3 changes: 1 addition & 2 deletions cmd/feishu-echo/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,9 @@ import (
"sync/atomic"
"time"

"github.com/larksuite/oapi-sdk-go/v3/event/dispatcher"
larkim "github.com/larksuite/oapi-sdk-go/v3/service/im/v1"
larkws "github.com/larksuite/oapi-sdk-go/v3/ws"

"github.com/larksuite/oapi-sdk-go/v3/event/dispatcher"
)

type eventCounts struct {
Expand Down
Loading