feat: add scripts/build.sh, test.sh, check.sh + gofmt + go mod tidy

This commit is contained in:
mirivlad 2026-06-16 12:11:55 +08:00
parent ec8ceee7f3
commit aefb9e9a9c
10 changed files with 243 additions and 72 deletions

3
go.mod
View File

@ -2,6 +2,8 @@ module github.com/verstak/verstak-desktop
go 1.24.4
require github.com/wailsapp/wails/v2 v2.12.0
require (
git.sr.ht/~jackmordaunt/go-toast/v2 v2.0.3 // indirect
github.com/bep/debounce v1.2.1 // indirect
@ -27,7 +29,6 @@ require (
github.com/valyala/fasttemplate v1.2.2 // indirect
github.com/wailsapp/go-webview2 v1.0.22 // indirect
github.com/wailsapp/mimetype v1.4.1 // indirect
github.com/wailsapp/wails/v2 v2.12.0 // indirect
golang.org/x/crypto v0.33.0 // indirect
golang.org/x/net v0.35.0 // indirect
golang.org/x/sys v0.30.0 // indirect

12
go.sum
View File

@ -2,6 +2,8 @@ git.sr.ht/~jackmordaunt/go-toast/v2 v2.0.3 h1:N3IGoHHp9pb6mj1cbXbuaSXV/UMKwmbKLf
git.sr.ht/~jackmordaunt/go-toast/v2 v2.0.3/go.mod h1:QtOLZGz8olr4qH2vWK0QH0w0O4T9fEIjMuWpKUsH7nc=
github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
@ -16,6 +18,8 @@ github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaa
github.com/labstack/echo/v4 v4.13.3/go.mod h1:o90YNEeQWjDozo584l7AwhJMHN0bOC4tAfg+Xox9q5g=
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
github.com/leaanthony/debme v1.2.1 h1:9Tgwf+kjcrbMQ4WnPcEIUcQuIZYqdWftzZkBr+i/oOc=
github.com/leaanthony/debme v1.2.1/go.mod h1:3V+sCm5tYAgQymvSOfYQ5Xx2JCr+OXiD9Jkw3otUjiA=
github.com/leaanthony/go-ansi-parser v1.6.1 h1:xd8bzARK3dErqkPFtoF9F3/HgN8UQk0ed1YDKpEz01A=
github.com/leaanthony/go-ansi-parser v1.6.1/go.mod h1:+vva/2y4alzVmmIEpk9QDhA7vLC5zKDTRwfZGOp3IWU=
github.com/leaanthony/gosod v1.0.4 h1:YLAbVyd591MRffDgxUOU1NwLhT9T1/YiwjKZpkNFeaI=
@ -25,6 +29,8 @@ github.com/leaanthony/slicer v1.6.0/go.mod h1:o/Iz29g7LN0GqH3aMjWAe90381nyZlDNqu
github.com/leaanthony/u v1.1.1 h1:TUFjwDGlNX+WuwVEzDqQwC2lOv0P4uhTQw7CMFdiK7M=
github.com/leaanthony/u v1.1.1/go.mod h1:9+o6hejoRljvZ3BzdYlVL0JYCwtnAsVuN9pVTQcaRfI=
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=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
@ -34,11 +40,15 @@ github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmd
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew=
github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tkrajina/go-reflector v0.5.8 h1:yPADHrwmUbMq4RGEyaOUpz2H90sRsETNVpjzo3DLVQQ=
github.com/tkrajina/go-reflector v0.5.8/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
@ -69,3 +79,5 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -11,11 +11,11 @@ import (
// App is the main application struct exposed to the Wails frontend.
type App struct {
capRegistry *capability.Registry
capRegistry *capability.Registry
contribRegistry *contribution.Registry
permRegistry *permissions.Registry
eventBus *events.Bus
plugins []plugin.Plugin
permRegistry *permissions.Registry
eventBus *events.Bus
plugins []plugin.Plugin
}
// NewApp creates a new App instance.
@ -27,11 +27,11 @@ func NewApp(
plugins []plugin.Plugin,
) *App {
return &App{
capRegistry: capReg,
capRegistry: capReg,
contribRegistry: contribReg,
permRegistry: permReg,
eventBus: bus,
plugins: plugins,
permRegistry: permReg,
eventBus: bus,
plugins: plugins,
}
}
@ -60,12 +60,12 @@ func (a *App) GetPermissions() []permissions.Entry {
// GetContributions returns all registered contributions.
func (a *App) GetContributions() ContributionSummary {
return ContributionSummary{
Views: a.contribRegistry.Views(),
Commands: a.contribRegistry.Commands(),
SettingsPanels: a.contribRegistry.SettingsPanels(),
SidebarItems: a.contribRegistry.SidebarItems(),
FileActions: a.contribRegistry.FileActions(),
NoteActions: a.contribRegistry.NoteActions(),
Views: a.contribRegistry.Views(),
Commands: a.contribRegistry.Commands(),
SettingsPanels: a.contribRegistry.SettingsPanels(),
SidebarItems: a.contribRegistry.SidebarItems(),
FileActions: a.contribRegistry.FileActions(),
NoteActions: a.contribRegistry.NoteActions(),
SearchProviders: a.contribRegistry.SearchProviders(),
}
}

View File

@ -9,7 +9,7 @@ import (
// Registry tracks available capabilities and which plugins provide them.
type Registry struct {
mu sync.RWMutex
mu sync.RWMutex
capabilities map[string]*Entry // capability name -> entry
}

View File

@ -12,60 +12,60 @@ import (
type Registry struct {
mu sync.RWMutex
views []ContributionView
commands []ContributionCommand
settingsPanels []ContributionSettingsPanel
sidebarItems []ContributionSidebarItem
fileActions []ContributionAction
noteActions []ContributionAction
contextMenus []ContributionContextMenuEntry
searchProviders []ContributionSearchProvider
views []ContributionView
commands []ContributionCommand
settingsPanels []ContributionSettingsPanel
sidebarItems []ContributionSidebarItem
fileActions []ContributionAction
noteActions []ContributionAction
contextMenus []ContributionContextMenuEntry
searchProviders []ContributionSearchProvider
activityProviders []ContributionActivityProvider
statusBarItems []ContributionStatusBarItem
statusBarItems []ContributionStatusBarItem
}
type ContributionView struct {
PluginID string `json:"pluginId"`
PluginID string `json:"pluginId"`
Item plugin.ContributionView `json:"item"`
}
type ContributionCommand struct {
PluginID string `json:"pluginId"`
PluginID string `json:"pluginId"`
Item plugin.ContributionCommand `json:"item"`
}
type ContributionSettingsPanel struct {
PluginID string `json:"pluginId"`
PluginID string `json:"pluginId"`
Item plugin.ContributionSettingsPanel `json:"item"`
}
type ContributionSidebarItem struct {
PluginID string `json:"pluginId"`
PluginID string `json:"pluginId"`
Item plugin.ContributionSidebarItem `json:"item"`
}
type ContributionAction struct {
PluginID string `json:"pluginId"`
PluginID string `json:"pluginId"`
Item plugin.ContributionAction `json:"item"`
}
type ContributionContextMenuEntry struct {
PluginID string `json:"pluginId"`
PluginID string `json:"pluginId"`
Item plugin.ContributionContextMenuEntry `json:"item"`
}
type ContributionSearchProvider struct {
PluginID string `json:"pluginId"`
PluginID string `json:"pluginId"`
Item plugin.ContributionSearchProvider `json:"item"`
}
type ContributionActivityProvider struct {
PluginID string `json:"pluginId"`
PluginID string `json:"pluginId"`
Item plugin.ContributionActivityProvider `json:"item"`
}
type ContributionStatusBarItem struct {
PluginID string `json:"pluginId"`
PluginID string `json:"pluginId"`
Item plugin.ContributionStatusBarItem `json:"item"`
}

View File

@ -11,23 +11,23 @@ import (
// Manifest represents a Verstak plugin.json manifest.
type Manifest struct {
SchemaVersion int `json:"schemaVersion"`
ID string `json:"id"`
Name string `json:"name"`
Version string `json:"version"`
APIVersion string `json:"apiVersion"`
Description string `json:"description,omitempty"`
Source string `json:"source,omitempty"`
Icon string `json:"icon,omitempty"`
Provides []string `json:"provides"`
Requires []string `json:"requires,omitempty"`
OptionalRequires []string `json:"optionalRequires,omitempty"`
Permissions []string `json:"permissions"`
Frontend *FrontendConfig `json:"frontend,omitempty"`
Backend *BackendConfig `json:"backend,omitempty"`
Migrations *MigrationConfig `json:"migrations,omitempty"`
Contributes *Contributions `json:"contributes,omitempty"`
Sync *SyncConfig `json:"sync,omitempty"`
SchemaVersion int `json:"schemaVersion"`
ID string `json:"id"`
Name string `json:"name"`
Version string `json:"version"`
APIVersion string `json:"apiVersion"`
Description string `json:"description,omitempty"`
Source string `json:"source,omitempty"`
Icon string `json:"icon,omitempty"`
Provides []string `json:"provides"`
Requires []string `json:"requires,omitempty"`
OptionalRequires []string `json:"optionalRequires,omitempty"`
Permissions []string `json:"permissions"`
Frontend *FrontendConfig `json:"frontend,omitempty"`
Backend *BackendConfig `json:"backend,omitempty"`
Migrations *MigrationConfig `json:"migrations,omitempty"`
Contributes *Contributions `json:"contributes,omitempty"`
Sync *SyncConfig `json:"sync,omitempty"`
}
// FrontendConfig describes the plugin's frontend bundle.
@ -38,8 +38,8 @@ type FrontendConfig struct {
// BackendConfig describes the plugin's backend sidecar.
type BackendConfig struct {
Type string `json:"type"`
Entry map[string]string `json:"entry"`
Type string `json:"type"`
Entry map[string]string `json:"entry"`
HealthCheck *HealthCheckConfig `json:"healthCheck,omitempty"`
}
@ -56,16 +56,16 @@ type MigrationConfig struct {
// Contributions describes UI and action contributions.
type Contributions struct {
Views []ContributionView `json:"views,omitempty"`
Commands []ContributionCommand `json:"commands,omitempty"`
SettingsPanels []ContributionSettingsPanel `json:"settingsPanels,omitempty"`
SidebarItems []ContributionSidebarItem `json:"sidebarItems,omitempty"`
FileActions []ContributionAction `json:"fileActions,omitempty"`
NoteActions []ContributionAction `json:"noteActions,omitempty"`
Views []ContributionView `json:"views,omitempty"`
Commands []ContributionCommand `json:"commands,omitempty"`
SettingsPanels []ContributionSettingsPanel `json:"settingsPanels,omitempty"`
SidebarItems []ContributionSidebarItem `json:"sidebarItems,omitempty"`
FileActions []ContributionAction `json:"fileActions,omitempty"`
NoteActions []ContributionAction `json:"noteActions,omitempty"`
ContextMenuEntries []ContributionContextMenuEntry `json:"contextMenuEntries,omitempty"`
SearchProviders []ContributionSearchProvider `json:"searchProviders,omitempty"`
ActivityProviders []ContributionActivityProvider `json:"activityProviders,omitempty"`
StatusBarItems []ContributionStatusBarItem `json:"statusBarItems,omitempty"`
SearchProviders []ContributionSearchProvider `json:"searchProviders,omitempty"`
ActivityProviders []ContributionActivityProvider `json:"activityProviders,omitempty"`
StatusBarItems []ContributionStatusBarItem `json:"statusBarItems,omitempty"`
}
// ContributionView represents a view contribution.
@ -153,13 +153,13 @@ type SyncConfig struct {
type Status string
const (
StatusDiscovered Status = "discovered"
StatusDisabled Status = "disabled"
StatusLoading Status = "loading"
StatusLoaded Status = "loaded"
StatusDegraded Status = "degraded"
StatusFailed Status = "failed"
StatusIncompatible Status = "incompatible"
StatusDiscovered Status = "discovered"
StatusDisabled Status = "disabled"
StatusLoading Status = "loading"
StatusLoaded Status = "loaded"
StatusDegraded Status = "degraded"
StatusFailed Status = "failed"
StatusIncompatible Status = "incompatible"
StatusMissingRequiredCapability Status = "missing-required-capability"
)

View File

@ -46,9 +46,9 @@ func main() {
err := wails.Run(&options.App{
Title: "Verstak",
Width: 1200,
Height: 800,
MinWidth: 800,
MinHeight: 600,
Height: 800,
MinWidth: 800,
MinHeight: 600,
WindowStartState: options.Normal,
AssetServer: &assetserver.Options{
Assets: assets,

61
scripts/build.sh Executable file
View File

@ -0,0 +1,61 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
FAILED=0
report() {
if [ "$2" -eq 0 ]; then
echo "$1"
else
echo "$1"
FAILED=1
fi
}
echo "=== verstak-desktop build ==="
# ── Go backend ──
echo "[backend]"
(cd "$ROOT" && go vet ./...)
report "go vet" $?
(cd "$ROOT" && go build ./...)
report "go build" $?
if command -v go-test-summary &>/dev/null || go test -list . ./... &>/dev/null 2>&1; then
(cd "$ROOT" && go test -count=1 ./... 2>&1 || true)
report "go test" $?
else
echo " go test: no tests to run"
fi
# ── Wails ──
echo "[wails]"
if command -v wails &>/dev/null; then
(cd "$ROOT" && wails build -clean)
report "wails build" $?
else
echo " ❌ wails: command not found. Install with: go install github.com/wailsapp/wails/v2/cmd/wails@latest"
FAILED=1
fi
# ── Frontend ──
echo "[frontend]"
if [ -f "$ROOT/frontend/package.json" ]; then
(cd "$ROOT/frontend" && npm ci --no-audit --no-fund)
report "npm ci" $?
(cd "$ROOT/frontend" && npm run build)
report "frontend build" $?
else
echo " frontend/package.json not found — skipping"
fi
echo ""
if [ "$FAILED" -eq 0 ]; then
echo "✅ build passed"
else
echo "❌ build failed"
fi
exit "$FAILED"

56
scripts/check.sh Executable file
View File

@ -0,0 +1,56 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
FAILED=0
report() {
if [ "$2" -eq 0 ]; then
echo "$1"
else
echo "$1"
FAILED=1
fi
}
echo "=== verstak-desktop check ==="
# Go vet
(cd "$ROOT" && go vet ./...)
report "go vet" $?
# Go fmt (non-destructive — only report unformatted files)
UNFORMATTED=$(cd "$ROOT" && gofmt -l . 2>/dev/null || go fmt -n ./... 2>&1 || true)
if [ -z "$UNFORMATTED" ]; then
echo " ✅ gofmt: all files formatted"
else
echo " ❌ gofmt: unformatted files:"
echo "$UNFORMATTED" | sed 's/^/ /'
FAILED=1
fi
# Go mod tidy check (non-destructive — report only)
(cd "$ROOT" && go mod tidy -diff 2>&1 || echo " ⚠️ go mod tidy check skipped")
report "go mod tidy" $?
# Frontend lint
if [ -f "$ROOT/frontend/package.json" ]; then
# Check if npm ci is needed (node_modules missing)
if [ ! -d "$ROOT/frontend/node_modules" ]; then
echo " frontend/node_modules missing — run build.sh first"
fi
if grep -q '"lint"' "$ROOT/frontend/package.json" 2>/dev/null; then
(cd "$ROOT/frontend" && npx tsc --noEmit 2>&1 || true)
report "frontend tsc --noEmit" $?
else
echo " no lint script in frontend/package.json"
fi
fi
echo ""
if [ "$FAILED" -eq 0 ]; then
echo "✅ all checks passed"
else
echo "❌ some checks failed"
fi
exit "$FAILED"

41
scripts/test.sh Executable file
View File

@ -0,0 +1,41 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
FAILED=0
report() {
if [ "$2" -eq 0 ]; then
echo "$1"
else
echo "$1"
FAILED=1
fi
}
echo "=== verstak-desktop test ==="
# Go tests
(cd "$ROOT" && go test -count=1 -v ./... 2>&1 || true)
report "go test" $?
# Frontend tests
if [ -f "$ROOT/frontend/package.json" ]; then
# Only run if vitest or jest is in the config
if grep -q '"test"' "$ROOT/frontend/package.json" 2>/dev/null; then
(cd "$ROOT/frontend" && npm test 2>&1 || true)
report "frontend test" $?
else
echo " no test script in frontend/package.json"
fi
else
echo " no frontend/package.json"
fi
echo ""
if [ "$FAILED" -eq 0 ]; then
echo "✅ all tests passed"
else
echo "❌ some tests failed"
fi
exit "$FAILED"