Switch remote deploy to vendored source builds

Move remote deployment to a vendored source bundle built on the target host via Docker so redeploys no longer require local cross-compilation or host Go installation.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
GitHub Actions
2026-05-08 12:19:18 +08:00
parent bb27566e38
commit c1a0fe2949
1320 changed files with 497125 additions and 11 deletions

View File

@@ -0,0 +1,23 @@
package dispatcher
import (
"errors"
"github.com/wailsapp/wails/v2/internal/frontend"
)
// processBrowserMessage processing browser messages
func (d *Dispatcher) processBrowserMessage(message string, sender frontend.Frontend) (string, error) {
if len(message) < 2 {
return "", errors.New("Invalid Browser Message: " + message)
}
switch message[1] {
case 'O':
url := message[3:]
go sender.BrowserOpenURL(url)
default:
d.log.Error("unknown Browser message: %s", message)
}
return "", nil
}

View File

@@ -0,0 +1,86 @@
package dispatcher
import (
"encoding/json"
"fmt"
"strings"
"github.com/wailsapp/wails/v2/internal/frontend"
)
type callMessage struct {
Name string `json:"name"`
Args []json.RawMessage `json:"args"`
CallbackID string `json:"callbackID"`
}
func (d *Dispatcher) processCallMessage(message string, sender frontend.Frontend) (string, error) {
var payload callMessage
err := json.Unmarshal([]byte(message[1:]), &payload)
if err != nil {
return "", err
}
var result interface{}
// Handle different calls
switch true {
case strings.HasPrefix(payload.Name, systemCallPrefix):
result, err = d.processSystemCall(payload, sender)
default:
// Lookup method
registeredMethod := d.bindingsDB.GetMethod(payload.Name)
// Check we have it
if registeredMethod == nil {
return "", fmt.Errorf("method '%s' not registered", payload.Name)
}
args, err2 := registeredMethod.ParseArgs(payload.Args)
if err2 != nil {
errmsg := fmt.Errorf("error parsing arguments: %s", err2.Error())
result, _ := d.NewErrorCallback(errmsg.Error(), payload.CallbackID)
return result, errmsg
}
result, err = registeredMethod.Call(args)
}
callbackMessage := &CallbackMessage{
CallbackID: payload.CallbackID,
}
if err != nil {
// Use the error formatter if one was provided
if d.errfmt != nil {
callbackMessage.Err = d.errfmt(err)
} else {
callbackMessage.Err = err.Error()
}
} else {
callbackMessage.Result = result
}
messageData, err := json.Marshal(callbackMessage)
d.log.Trace("json call result data: %+v\n", string(messageData))
if err != nil {
// what now?
d.log.Fatal(err.Error())
}
return "c" + string(messageData), nil
}
// CallbackMessage defines a message that contains the result of a call
type CallbackMessage struct {
Result interface{} `json:"result"`
Err any `json:"error"`
CallbackID string `json:"callbackid"`
}
func (d *Dispatcher) NewErrorCallback(message string, callbackID string) (string, error) {
result := &CallbackMessage{
CallbackID: callbackID,
Err: message,
}
messageData, err := json.Marshal(result)
d.log.Trace("json call result data: %+v\n", string(messageData))
return string(messageData), err
}

View File

@@ -0,0 +1,81 @@
package dispatcher
import (
"context"
"fmt"
"github.com/pkg/errors"
"github.com/wailsapp/wails/v2/internal/binding"
"github.com/wailsapp/wails/v2/internal/frontend"
"github.com/wailsapp/wails/v2/internal/logger"
"github.com/wailsapp/wails/v2/pkg/options"
)
type Dispatcher struct {
log *logger.Logger
bindings *binding.Bindings
events frontend.Events
bindingsDB *binding.DB
ctx context.Context
errfmt options.ErrorFormatter
disablePanicRecovery bool
}
func NewDispatcher(ctx context.Context, log *logger.Logger, bindings *binding.Bindings, events frontend.Events, errfmt options.ErrorFormatter, disablePanicRecovery bool) *Dispatcher {
return &Dispatcher{
log: log,
bindings: bindings,
events: events,
bindingsDB: bindings.DB(),
ctx: ctx,
errfmt: errfmt,
disablePanicRecovery: disablePanicRecovery,
}
}
func (d *Dispatcher) ProcessMessage(message string, sender frontend.Frontend) (_ string, err error) {
if !d.disablePanicRecovery {
defer func() {
if e := recover(); e != nil {
if errPanic, ok := e.(error); ok {
err = errPanic
} else {
err = fmt.Errorf("%v", e)
}
}
if err != nil {
d.log.Error("process message error: %s -> %s", message, err)
}
}()
}
if message == "" {
return "", errors.New("No message to process")
}
switch message[0] {
case 'L':
return d.processLogMessage(message)
case 'E':
return d.processEventMessage(message, sender)
case 'C':
return d.processCallMessage(message, sender)
case 'c':
return d.processSecureCallMessage(message, sender)
case 'W':
return d.processWindowMessage(message, sender)
case 'B':
return d.processBrowserMessage(message, sender)
case 'D':
return d.processDragAndDropMessage(message)
case 'Q':
sender.Quit()
return "", nil
case 'S':
sender.Show()
return "", nil
case 'H':
sender.Hide()
return "", nil
default:
return "", errors.New("Unknown message from front end: " + message)
}
}

View File

@@ -0,0 +1,38 @@
package dispatcher
import (
"errors"
"strconv"
"strings"
)
func (d *Dispatcher) processDragAndDropMessage(message string) (string, error) {
switch message[1] {
case 'D':
msg := strings.SplitN(message[3:], ":", 3)
if len(msg) != 3 {
return "", errors.New("Invalid drag and drop Message: " + message)
}
x, err := strconv.Atoi(msg[0])
if err != nil {
return "", errors.New("Invalid x coordinate in drag and drop Message: " + message)
}
y, err := strconv.Atoi(msg[1])
if err != nil {
return "", errors.New("Invalid y coordinate in drag and drop Message: " + message)
}
paths := strings.Split(msg[2], "\n")
if len(paths) < 1 {
return "", errors.New("Invalid drag and drop Message: " + message)
}
d.events.Emit("wails:file-drop", x, y, paths)
default:
return "", errors.New("Invalid drag and drop Message: " + message)
}
return "", nil
}

View File

@@ -0,0 +1,34 @@
package dispatcher
import (
"encoding/json"
"errors"
"github.com/wailsapp/wails/v2/internal/frontend"
)
type EventMessage struct {
Name string `json:"name"`
Data []interface{} `json:"data"`
}
func (d *Dispatcher) processEventMessage(message string, sender frontend.Frontend) (string, error) {
if len(message) < 3 {
return "", errors.New("Invalid Event Message: " + message)
}
switch message[1] {
case 'E':
var eventMessage EventMessage
err := json.Unmarshal([]byte(message[2:]), &eventMessage)
if err != nil {
return "", err
}
go d.events.Notify(sender, eventMessage.Name, eventMessage.Data...)
case 'X':
eventName := message[2:]
go d.events.Off(eventName)
}
return "", nil
}

View File

@@ -0,0 +1,49 @@
package dispatcher
import (
"github.com/pkg/errors"
"github.com/wailsapp/wails/v2/internal/logger"
pkgLogger "github.com/wailsapp/wails/v2/pkg/logger"
)
var logLevelMap = map[byte]logger.LogLevel{
'1': pkgLogger.TRACE,
'2': pkgLogger.DEBUG,
'3': pkgLogger.INFO,
'4': pkgLogger.WARNING,
'5': pkgLogger.ERROR,
}
func (d *Dispatcher) processLogMessage(message string) (string, error) {
if len(message) < 3 {
return "", errors.New("Invalid Log Message: " + message)
}
messageText := message[2:]
switch message[1] {
case 'T':
d.log.Trace(messageText)
case 'P':
d.log.Print(messageText)
case 'D':
d.log.Debug(messageText)
case 'I':
d.log.Info(messageText)
case 'W':
d.log.Warning(messageText)
case 'E':
d.log.Error(messageText)
case 'F':
d.log.Fatal(messageText)
case 'S':
loglevel, exists := logLevelMap[message[2]]
if !exists {
return "", errors.New("Invalid Set Log Level Message: " + message)
}
d.log.SetLogLevel(loglevel)
default:
return "", errors.New("Invalid Log Message: " + message)
}
return "", nil
}

View File

@@ -0,0 +1,57 @@
package dispatcher
import (
"encoding/json"
"fmt"
"github.com/wailsapp/wails/v2/internal/frontend"
)
type secureCallMessage struct {
ID int `json:"id"`
Args []json.RawMessage `json:"args"`
CallbackID string `json:"callbackID"`
}
func (d *Dispatcher) processSecureCallMessage(message string, sender frontend.Frontend) (string, error) {
var payload secureCallMessage
err := json.Unmarshal([]byte(message[1:]), &payload)
if err != nil {
return "", err
}
var result interface{}
// Lookup method
registeredMethod := d.bindingsDB.GetObfuscatedMethod(payload.ID)
// Check we have it
if registeredMethod == nil {
return "", fmt.Errorf("method '%d' not registered", payload.ID)
}
args, err2 := registeredMethod.ParseArgs(payload.Args)
if err2 != nil {
errmsg := fmt.Errorf("error parsing arguments: %s", err2.Error())
result, _ := d.NewErrorCallback(errmsg.Error(), payload.CallbackID)
return result, errmsg
}
result, err = registeredMethod.Call(args)
callbackMessage := &CallbackMessage{
CallbackID: payload.CallbackID,
}
if err != nil {
callbackMessage.Err = err.Error()
} else {
callbackMessage.Result = result
}
messageData, err := json.Marshal(callbackMessage)
d.log.Trace("json call result data: %+v\n", string(messageData))
if err != nil {
// what now?
d.log.Fatal(err.Error())
}
return "c" + string(messageData), nil
}

View File

@@ -0,0 +1,163 @@
package dispatcher
import (
"encoding/json"
"errors"
"fmt"
"strings"
"github.com/wailsapp/wails/v2/pkg/runtime"
"github.com/wailsapp/wails/v2/internal/frontend"
)
const systemCallPrefix = ":wails:"
type position struct {
X int `json:"x"`
Y int `json:"y"`
}
type size struct {
W int `json:"w"`
H int `json:"h"`
}
func (d *Dispatcher) processSystemCall(payload callMessage, sender frontend.Frontend) (interface{}, error) {
// Strip prefix
name := strings.TrimPrefix(payload.Name, systemCallPrefix)
switch name {
case "WindowGetPos":
x, y := sender.WindowGetPosition()
return &position{x, y}, nil
case "WindowGetSize":
w, h := sender.WindowGetSize()
return &size{w, h}, nil
case "ScreenGetAll":
return sender.ScreenGetAll()
case "WindowIsMaximised":
return sender.WindowIsMaximised(), nil
case "WindowIsMinimised":
return sender.WindowIsMinimised(), nil
case "WindowIsNormal":
return sender.WindowIsNormal(), nil
case "WindowIsFullscreen":
return sender.WindowIsFullscreen(), nil
case "Environment":
return runtime.Environment(d.ctx), nil
case "ClipboardGetText":
t, err := sender.ClipboardGetText()
return t, err
case "ClipboardSetText":
if len(payload.Args) < 1 {
return false, errors.New("empty argument, cannot set clipboard")
}
var arg string
if err := json.Unmarshal(payload.Args[0], &arg); err != nil {
return false, err
}
if err := sender.ClipboardSetText(arg); err != nil {
return false, err
}
return true, nil
case "InitializeNotifications":
err := sender.InitializeNotifications()
return nil, err
case "CleanupNotifications":
sender.CleanupNotifications()
return nil, nil
case "IsNotificationAvailable":
return sender.IsNotificationAvailable(), nil
case "RequestNotificationAuthorization":
authorized, err := sender.RequestNotificationAuthorization()
if err != nil {
return nil, err
}
return authorized, nil
case "CheckNotificationAuthorization":
authorized, err := sender.CheckNotificationAuthorization()
if err != nil {
return nil, err
}
return authorized, nil
case "SendNotification":
if len(payload.Args) < 1 {
return nil, errors.New("empty argument, cannot send notification")
}
var options frontend.NotificationOptions
if err := json.Unmarshal(payload.Args[0], &options); err != nil {
return nil, err
}
err := sender.SendNotification(options)
return nil, err
case "SendNotificationWithActions":
if len(payload.Args) < 1 {
return nil, errors.New("empty argument, cannot send notification")
}
var options frontend.NotificationOptions
if err := json.Unmarshal(payload.Args[0], &options); err != nil {
return nil, err
}
err := sender.SendNotificationWithActions(options)
return nil, err
case "RegisterNotificationCategory":
if len(payload.Args) < 1 {
return nil, errors.New("empty argument, cannot register category")
}
var category frontend.NotificationCategory
if err := json.Unmarshal(payload.Args[0], &category); err != nil {
return nil, err
}
err := sender.RegisterNotificationCategory(category)
return nil, err
case "RemoveNotificationCategory":
if len(payload.Args) < 1 {
return nil, errors.New("empty argument, cannot remove category")
}
var categoryId string
if err := json.Unmarshal(payload.Args[0], &categoryId); err != nil {
return nil, err
}
err := sender.RemoveNotificationCategory(categoryId)
return nil, err
case "RemoveAllPendingNotifications":
err := sender.RemoveAllPendingNotifications()
return nil, err
case "RemovePendingNotification":
if len(payload.Args) < 1 {
return nil, errors.New("empty argument, cannot remove notification")
}
var identifier string
if err := json.Unmarshal(payload.Args[0], &identifier); err != nil {
return nil, err
}
err := sender.RemovePendingNotification(identifier)
return nil, err
case "RemoveAllDeliveredNotifications":
err := sender.RemoveAllDeliveredNotifications()
return nil, err
case "RemoveDeliveredNotification":
if len(payload.Args) < 1 {
return nil, errors.New("empty argument, cannot remove notification")
}
var identifier string
if err := json.Unmarshal(payload.Args[0], &identifier); err != nil {
return nil, err
}
err := sender.RemoveDeliveredNotification(identifier)
return nil, err
case "RemoveNotification":
if len(payload.Args) < 1 {
return nil, errors.New("empty argument, cannot remove notification")
}
var identifier string
if err := json.Unmarshal(payload.Args[0], &identifier); err != nil {
return nil, err
}
err := sender.RemoveNotification(identifier)
return nil, err
default:
return nil, fmt.Errorf("unknown systemcall message: %s", payload.Name)
}
}

View File

@@ -0,0 +1,99 @@
package dispatcher
import (
"encoding/json"
"errors"
"strconv"
"strings"
"github.com/wailsapp/wails/v2/internal/frontend"
"github.com/wailsapp/wails/v2/pkg/options"
)
func (d *Dispatcher) mustAtoI(input string) int {
result, err := strconv.Atoi(input)
if err != nil {
d.log.Error("cannot convert %s to integer!", input)
}
return result
}
func (d *Dispatcher) processWindowMessage(message string, sender frontend.Frontend) (string, error) {
if len(message) < 2 {
return "", errors.New("Invalid Window Message: " + message)
}
switch message[1] {
case 'A':
switch message[2:] {
case "SDT":
go sender.WindowSetSystemDefaultTheme()
case "LT":
go sender.WindowSetLightTheme()
case "DT":
go sender.WindowSetDarkTheme()
case "TP:0", "TP:1":
if message[2:] == "TP:0" {
go sender.WindowSetAlwaysOnTop(false)
} else if message[2:] == "TP:1" {
go sender.WindowSetAlwaysOnTop(true)
}
}
case 'c':
go sender.WindowCenter()
case 'T':
title := message[2:]
go sender.WindowSetTitle(title)
case 'F':
go sender.WindowFullscreen()
case 'f':
go sender.WindowUnfullscreen()
case 's':
parts := strings.Split(message[3:], ":")
w := d.mustAtoI(parts[0])
h := d.mustAtoI(parts[1])
go sender.WindowSetSize(w, h)
case 'p':
parts := strings.Split(message[3:], ":")
x := d.mustAtoI(parts[0])
y := d.mustAtoI(parts[1])
go sender.WindowSetPosition(x, y)
case 'H':
go sender.WindowHide()
case 'S':
go sender.WindowShow()
case 'R':
go sender.WindowReloadApp()
case 'r':
var rgba options.RGBA
err := json.Unmarshal([]byte(message[3:]), &rgba)
if err != nil {
return "", err
}
go sender.WindowSetBackgroundColour(&rgba)
case 'M':
go sender.WindowMaximise()
case 't':
go sender.WindowToggleMaximise()
case 'U':
go sender.WindowUnmaximise()
case 'm':
go sender.WindowMinimise()
case 'u':
go sender.WindowUnminimise()
case 'Z':
parts := strings.Split(message[3:], ":")
w := d.mustAtoI(parts[0])
h := d.mustAtoI(parts[1])
go sender.WindowSetMaxSize(w, h)
case 'z':
parts := strings.Split(message[3:], ":")
w := d.mustAtoI(parts[0])
h := d.mustAtoI(parts[1])
go sender.WindowSetMinSize(w, h)
default:
d.log.Error("unknown Window message: %s", message)
}
return "", nil
}