refactor(edge): 抽取边缘运行时共享包并完成 Phase 3 重构

- 新增 internal/apps/edge/,三组件改为薄包装,删除 3000+ 行重复代码
- Agent 心跳周期下沉至 heartbeat/cycle.go
- 协议类型迁入 pkg/protocol/agent.go
- 补充设计文档与 changelog
This commit is contained in:
ryan
2026-06-19 14:56:00 +08:00
parent cc5e53c51e
commit db9a9f98fd
53 changed files with 2091 additions and 2947 deletions
@@ -0,0 +1,41 @@
package heartbeat
import (
"context"
"log/slog"
"strings"
edgeupdater "github.com/Rain-kl/Wavelet/internal/apps/edge/updater"
)
type AutoUpdateSettings struct {
AutoUpdate bool
UpdateNow bool
UpdateRepo string
UpdateChannel string
UpdateTag string
}
func TryAutoUpdate(ctx context.Context, updater *edgeupdater.Service, settings *AutoUpdateSettings, logLabel string) {
if settings == nil || updater == nil {
return
}
force := settings.UpdateNow
shouldCheck := settings.AutoUpdate || force
if !shouldCheck || strings.TrimSpace(settings.UpdateRepo) == "" {
return
}
channel := "stable"
if force && strings.TrimSpace(settings.UpdateChannel) != "" {
channel = settings.UpdateChannel
}
slog.Info("checking for "+logLabel+" updates", "repo", settings.UpdateRepo, "channel", channel, "force", force)
err := updater.CheckAndUpdate(ctx, settings.UpdateRepo, edgeupdater.UpdateOptions{
Channel: channel,
TagName: settings.UpdateTag,
Force: force,
})
if err != nil {
slog.Error(logLabel+" update check failed", "error", err)
}
}
+23
View File
@@ -0,0 +1,23 @@
package heartbeat
import (
"context"
"time"
)
// RunLoop invokes fn immediately, then on each interval tick until ctx is cancelled.
func RunLoop(ctx context.Context, interval time.Duration, fn func(context.Context)) {
fn(ctx)
ticker := time.NewTicker(interval)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
fn(ctx)
}
}
}
+41
View File
@@ -0,0 +1,41 @@
package heartbeat
import (
"context"
"sync/atomic"
"testing"
"time"
)
func TestRunLoopImmediateAndTicker(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var calls atomic.Int32
interval := 20 * time.Millisecond
done := make(chan struct{})
go func() {
RunLoop(ctx, interval, func(context.Context) {
calls.Add(1)
})
close(done)
}()
time.Sleep(5 * time.Millisecond)
if got := calls.Load(); got != 1 {
t.Fatalf("expected 1 immediate call, got %d", got)
}
time.Sleep(35 * time.Millisecond)
if got := calls.Load(); got < 2 {
t.Fatalf("expected at least 2 calls after ticker, got %d", got)
}
cancel()
select {
case <-done:
case <-time.After(time.Second):
t.Fatal("RunLoop did not exit after context cancellation")
}
}