feat: initial commit

This commit is contained in:
eric
2026-03-12 22:16:34 +01:00
parent 8555b02752
commit f13f4a9a69
155 changed files with 11988 additions and 0 deletions

View File

@@ -0,0 +1,23 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "wails",
srcs = ["service.go"],
importpath = "github.com/Eriyc/rules_wails/pkg/wails3kit/updates/wails",
visibility = ["//visibility:public"],
deps = [
"//pkg/wails3kit/updates",
"@com_github_wailsapp_wails_v3//pkg/application",
],
)
go_test(
name = "wails_test",
srcs = ["service_test.go"],
embed = [":wails"],
deps = [
"//pkg/wails3kit/updates",
"//pkg/wails3kit/updates/storage/file",
"@com_github_wailsapp_wails_v3//pkg/application",
],
)

View File

@@ -0,0 +1,110 @@
package wails
import (
"context"
"github.com/Eriyc/rules_wails/pkg/wails3kit/updates"
"github.com/wailsapp/wails/v3/pkg/application"
)
type Options struct {
App *application.App
Controller *updates.Controller
EventName string
AutoCheckOnStartup bool
Emitter func(string, any)
}
type Service struct {
controller *updates.Controller
eventName string
autoCheckOnStartup bool
emitter func(string, any)
stop func()
done chan struct{}
}
func RegisterEvents(eventName string) {
if eventName == "" {
eventName = "updates:state"
}
application.RegisterEvent[updates.Snapshot](eventName)
}
func NewService(opts Options) *Service {
eventName := opts.EventName
if eventName == "" {
eventName = "updates:state"
}
emitter := opts.Emitter
if emitter == nil && opts.App != nil {
emitter = func(name string, data any) {
opts.App.Event.Emit(name, data)
}
}
return &Service{
controller: opts.Controller,
eventName: eventName,
autoCheckOnStartup: opts.AutoCheckOnStartup,
emitter: emitter,
done: make(chan struct{}),
}
}
func (service *Service) ServiceStartup(_ context.Context, _ application.ServiceOptions) error {
if service.controller == nil {
return updates.ErrInvalidConfig
}
channel, stop := service.controller.Subscribe(4)
service.stop = stop
go func() {
for {
select {
case <-service.done:
return
case snapshot := <-channel:
if service.emitter != nil {
service.emitter(service.eventName, snapshot)
}
}
}
}()
if service.autoCheckOnStartup {
go func() {
_, _ = service.Check()
}()
}
return nil
}
func (service *Service) ServiceShutdown() error {
if service.stop != nil {
service.stop()
}
select {
case <-service.done:
default:
close(service.done)
}
return nil
}
func (service *Service) Snapshot() updates.Snapshot {
return service.controller.Snapshot()
}
func (service *Service) Check() (updates.Snapshot, error) {
return service.controller.Check(context.Background(), updates.CheckRequest{})
}
func (service *Service) Download() (updates.Snapshot, error) {
return service.controller.Download(context.Background())
}
func (service *Service) ApplyAndRestart() error {
return service.controller.ApplyAndRestart(context.Background())
}

View File

@@ -0,0 +1,104 @@
package wails
import (
"context"
"io"
"path/filepath"
"testing"
"time"
"github.com/Eriyc/rules_wails/pkg/wails3kit/updates"
filestore "github.com/Eriyc/rules_wails/pkg/wails3kit/updates/storage/file"
"github.com/wailsapp/wails/v3/pkg/application"
)
func TestServiceEmitsSnapshots(t *testing.T) {
t.Parallel()
release := &updates.Release{
Version: "1.1.0",
Channel: updates.ChannelStable,
Artifact: updates.Artifact{
Kind: updates.ArtifactKindBundleArchive,
Format: updates.ArtifactFormatZip,
URL: "https://example.invalid/app.zip",
SHA256: "abc",
},
}
controller, err := updates.NewController(updates.Config{
App: updates.AppDescriptor{
ProductID: "com.example.app",
CurrentVersion: "1.0.0",
Channel: updates.ChannelStable,
OS: "linux",
Arch: "amd64",
ExecutablePath: filepath.Join(t.TempDir(), "App"),
},
Provider: serviceFakeProvider{release: release},
Downloader: serviceFakeDownloader{},
Store: filestore.New(filepath.Join(t.TempDir(), "snapshot.json")),
Platform: &serviceFakePlatform{root: updates.InstallRoot{Path: t.TempDir()}},
})
if err != nil {
t.Fatalf("NewController returned error: %v", err)
}
emitted := make(chan updates.Snapshot, 2)
service := NewService(Options{
Controller: controller,
Emitter: func(_ string, data any) {
emitted <- data.(updates.Snapshot)
},
})
if err := service.ServiceStartup(context.Background(), application.ServiceOptions{}); err != nil {
t.Fatalf("ServiceStartup returned error: %v", err)
}
defer service.ServiceShutdown()
if _, err := service.Check(); err != nil {
t.Fatalf("Check returned error: %v", err)
}
select {
case snapshot := <-emitted:
if snapshot.State == "" {
t.Fatal("expected emitted snapshot state")
}
case <-time.After(time.Second):
t.Fatal("timed out waiting for emitted snapshot")
}
}
type serviceFakeProvider struct {
release *updates.Release
}
func (provider serviceFakeProvider) Resolve(context.Context, updates.ResolveRequest) (*updates.Release, error) {
return provider.release, nil
}
func (provider serviceFakeProvider) OpenArtifact(context.Context, updates.Release) (io.ReadCloser, error) {
return nil, nil
}
type serviceFakeDownloader struct{}
func (serviceFakeDownloader) Download(context.Context, updates.Artifact) (updates.DownloadedFile, error) {
return updates.DownloadedFile{}, nil
}
type serviceFakePlatform struct {
root updates.InstallRoot
}
func (platform *serviceFakePlatform) DetectInstallRoot(updates.AppDescriptor) (updates.InstallRoot, error) {
return platform.root, nil
}
func (platform *serviceFakePlatform) Stage(context.Context, updates.InstallRoot, updates.DownloadedFile, updates.Release) (updates.StagedArtifact, error) {
return updates.StagedArtifact{}, nil
}
func (platform *serviceFakePlatform) SpawnApplyAndRestart(context.Context, updates.ApplyRequest) error {
return nil
}