mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-05-12 02:59:42 +08:00
60
.github/workflows/release.yml
vendored
60
.github/workflows/release.yml
vendored
@@ -22,26 +22,56 @@ jobs:
|
||||
os_name: MacOS
|
||||
arch_name: Amd64
|
||||
build_name: gonavi-build-darwin-amd64
|
||||
wails_tags: ""
|
||||
artifact_suffix: ""
|
||||
build_optional_agents: true
|
||||
linux_webkit: ""
|
||||
- os: macos-latest
|
||||
platform: darwin/arm64
|
||||
os_name: MacOS
|
||||
arch_name: Arm64
|
||||
build_name: gonavi-build-darwin-arm64
|
||||
wails_tags: ""
|
||||
artifact_suffix: ""
|
||||
build_optional_agents: true
|
||||
linux_webkit: ""
|
||||
- os: windows-latest
|
||||
platform: windows/amd64
|
||||
os_name: Windows
|
||||
arch_name: Amd64
|
||||
build_name: gonavi-build-windows-amd64
|
||||
wails_tags: ""
|
||||
artifact_suffix: ""
|
||||
build_optional_agents: true
|
||||
linux_webkit: ""
|
||||
- os: windows-latest
|
||||
platform: windows/arm64
|
||||
os_name: Windows
|
||||
arch_name: Arm64
|
||||
build_name: gonavi-build-windows-arm64
|
||||
wails_tags: ""
|
||||
artifact_suffix: ""
|
||||
build_optional_agents: true
|
||||
linux_webkit: ""
|
||||
- os: ubuntu-22.04
|
||||
platform: linux/amd64
|
||||
os_name: Linux
|
||||
arch_name: Amd64
|
||||
build_name: gonavi-build-linux-amd64
|
||||
wails_tags: ""
|
||||
artifact_suffix: ""
|
||||
build_optional_agents: true
|
||||
linux_webkit: "4.0"
|
||||
# Debian 13 (trixie) 默认仓库已切到 WebKitGTK 4.1:单独提供 4.1 变体产物
|
||||
- os: ubuntu-24.04
|
||||
platform: linux/amd64
|
||||
os_name: Linux
|
||||
arch_name: Amd64
|
||||
build_name: gonavi-build-linux-amd64-webkit41
|
||||
wails_tags: "webkit2_41"
|
||||
artifact_suffix: "-WebKit41"
|
||||
build_optional_agents: false
|
||||
linux_webkit: "4.1"
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
@@ -63,7 +93,17 @@ jobs:
|
||||
if: contains(matrix.platform, 'linux')
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libfuse2
|
||||
sudo apt-get install -y libgtk-3-dev
|
||||
|
||||
# WebKitGTK 4.1 需要 libsoup3;4.0 使用 libsoup2(通常由 webkit2gtk dev 包拉起)
|
||||
if [ "${{ matrix.linux_webkit }}" = "4.1" ]; then
|
||||
sudo apt-get install -y libwebkit2gtk-4.1-dev libsoup-3.0-dev
|
||||
else
|
||||
sudo apt-get install -y libwebkit2gtk-4.0-dev
|
||||
fi
|
||||
|
||||
# AppImage 运行/打包可能需要 FUSE2。不同发行版/版本包名不同,做兼容兜底。
|
||||
sudo apt-get install -y libfuse2 || sudo apt-get install -y libfuse2t64 || true
|
||||
|
||||
# Download linuxdeploy tools for AppImage packaging
|
||||
LINUXDEPLOY_URL="https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage"
|
||||
@@ -94,9 +134,15 @@ jobs:
|
||||
- name: Build
|
||||
shell: bash
|
||||
run: |
|
||||
wails build -platform ${{ matrix.platform }} -clean -o ${{ matrix.build_name }} -ldflags "-s -w -X GoNavi-Wails/internal/app.AppVersion=${{ github.ref_name }}"
|
||||
set -euo pipefail
|
||||
TAG_ARGS=()
|
||||
if [ -n "${{ matrix.wails_tags }}" ]; then
|
||||
TAG_ARGS+=(-tags "${{ matrix.wails_tags }}")
|
||||
fi
|
||||
wails build -platform ${{ matrix.platform }} -clean -o ${{ matrix.build_name }} "${TAG_ARGS[@]}" -ldflags "-s -w -X GoNavi-Wails/internal/app.AppVersion=${{ github.ref_name }}"
|
||||
|
||||
- name: Build Optional Driver Agents
|
||||
if: ${{ matrix.build_optional_agents }}
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
@@ -160,7 +206,7 @@ jobs:
|
||||
codesign --force --options runtime --deep --sign - "$APP_NAME"
|
||||
|
||||
DMG_NAME="${{ matrix.build_name }}.dmg"
|
||||
FINAL_NAME="GoNavi-$VERSION-${{ matrix.os_name }}-${{ matrix.arch_name }}.dmg"
|
||||
FINAL_NAME="GoNavi-$VERSION-${{ matrix.os_name }}-${{ matrix.arch_name }}${{ matrix.artifact_suffix }}.dmg"
|
||||
echo "📦 正在生成 DMG: $DMG_NAME..."
|
||||
|
||||
create-dmg \
|
||||
@@ -187,8 +233,8 @@ jobs:
|
||||
$version = $version.Substring(1)
|
||||
}
|
||||
$target = "${{ matrix.build_name }}"
|
||||
$finalExeName = "GoNavi-$version-${{ matrix.os_name }}-${{ matrix.arch_name }}.exe"
|
||||
$finalZipName = "GoNavi-$version-${{ matrix.os_name }}-${{ matrix.arch_name }}.zip"
|
||||
$finalExeName = "GoNavi-$version-${{ matrix.os_name }}-${{ matrix.arch_name }}${{ matrix.artifact_suffix }}.exe"
|
||||
$finalZipName = "GoNavi-$version-${{ matrix.os_name }}-${{ matrix.arch_name }}${{ matrix.artifact_suffix }}.zip"
|
||||
|
||||
if (Test-Path "$target.exe") {
|
||||
$finalExe = "$target.exe"
|
||||
@@ -214,8 +260,8 @@ jobs:
|
||||
VERSION="${VERSION#v}"
|
||||
cd build/bin
|
||||
TARGET="${{ matrix.build_name }}"
|
||||
TAR_NAME="GoNavi-$VERSION-${{ matrix.os_name }}-${{ matrix.arch_name }}.tar.gz"
|
||||
APPIMAGE_NAME="GoNavi-$VERSION-${{ matrix.os_name }}-${{ matrix.arch_name }}.AppImage"
|
||||
TAR_NAME="GoNavi-$VERSION-${{ matrix.os_name }}-${{ matrix.arch_name }}${{ matrix.artifact_suffix }}.tar.gz"
|
||||
APPIMAGE_NAME="GoNavi-$VERSION-${{ matrix.os_name }}-${{ matrix.arch_name }}${{ matrix.artifact_suffix }}.AppImage"
|
||||
|
||||
if [ ! -f "$TARGET" ]; then
|
||||
echo "❌ 未找到构建产物 '$TARGET'!"
|
||||
|
||||
22
README.md
22
README.md
@@ -129,6 +129,7 @@ wails build -clean
|
||||
支持构建:
|
||||
* macOS (AMD64 / ARM64)
|
||||
* Windows (AMD64)
|
||||
* Linux (AMD64,提供 WebKitGTK 4.0 与 4.1 变体产物)
|
||||
|
||||
---
|
||||
|
||||
@@ -146,6 +147,27 @@ wails build -clean
|
||||
```
|
||||
4. 或者:在 Finder 中右键点击应用图标,按住 `Control` 键选择 **打开**,然后在弹出的窗口中再次点击 **打开**。
|
||||
|
||||
### Linux 启动报错缺少 `libwebkit2gtk` / `libjavascriptcoregtk`
|
||||
|
||||
GoNavi 的 Linux 二进制依赖系统 WebKitGTK 运行库。不同发行版默认版本不同:
|
||||
|
||||
- Debian 13 / Ubuntu 24.04 及更新版本:通常为 WebKitGTK 4.1
|
||||
- Ubuntu 22.04 / Debian 12 等:通常为 WebKitGTK 4.0
|
||||
|
||||
如果启动时报错(如 `libwebkit2gtk-4.0.so.37: cannot open shared object file`),请按系统安装对应依赖后重试:
|
||||
|
||||
```bash
|
||||
# Debian 13 / Ubuntu 24.04+
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libgtk-3-0 libwebkit2gtk-4.1-0 libjavascriptcoregtk-4.1-0
|
||||
|
||||
# Ubuntu 22.04 / Debian 12
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libgtk-3-0 libwebkit2gtk-4.0-37 libjavascriptcoregtk-4.0-18
|
||||
```
|
||||
|
||||
如果你使用的是 Release 中带 `-WebKit41` 后缀的 Linux 产物,请优先在 Debian 13 / Ubuntu 24.04+ 上使用;普通 Linux 产物更适合 WebKitGTK 4.0 运行环境。
|
||||
|
||||
---
|
||||
|
||||
## 🤝 贡献指南
|
||||
|
||||
@@ -465,6 +465,7 @@ func (a *App) DBGetTables(config connection.ConnectionConfig, dbName string) con
|
||||
|
||||
func (a *App) DBShowCreateTable(config connection.ConnectionConfig, dbName string, tableName string) connection.QueryResult {
|
||||
runConfig := normalizeRunConfig(config, dbName)
|
||||
dbType := resolveDDLDBType(config)
|
||||
|
||||
dbInst, err := a.getDatabase(runConfig)
|
||||
if err != nil {
|
||||
@@ -478,10 +479,120 @@ func (a *App) DBShowCreateTable(config connection.ConnectionConfig, dbName strin
|
||||
logger.Error(err, "DBShowCreateTable 获取建表语句失败:%s 表=%s", formatConnSummary(runConfig), tableName)
|
||||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||||
}
|
||||
if shouldFallbackCreateStatement(dbType, sqlStr) {
|
||||
columns, colErr := dbInst.GetColumns(schemaName, pureTableName)
|
||||
if colErr != nil {
|
||||
logger.Error(colErr, "DBShowCreateTable 兜底加载字段失败:%s 表=%s", formatConnSummary(runConfig), tableName)
|
||||
return connection.QueryResult{Success: false, Message: colErr.Error()}
|
||||
}
|
||||
fallbackDDL, buildErr := buildFallbackCreateStatement(dbType, schemaName, pureTableName, columns)
|
||||
if buildErr != nil {
|
||||
logger.Error(buildErr, "DBShowCreateTable 兜底生成 DDL 失败:%s 表=%s", formatConnSummary(runConfig), tableName)
|
||||
return connection.QueryResult{Success: false, Message: buildErr.Error()}
|
||||
}
|
||||
sqlStr = fallbackDDL
|
||||
}
|
||||
|
||||
return connection.QueryResult{Success: true, Data: sqlStr}
|
||||
}
|
||||
|
||||
func shouldFallbackCreateStatement(dbType string, ddl string) bool {
|
||||
switch dbType {
|
||||
case "postgres", "kingbase", "highgo", "vastbase":
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
trimmed := strings.TrimSpace(ddl)
|
||||
if trimmed == "" {
|
||||
return true
|
||||
}
|
||||
if hasCreateTableHead(trimmed) {
|
||||
return false
|
||||
}
|
||||
|
||||
lower := strings.ToLower(trimmed)
|
||||
if strings.Contains(lower, "not fully supported") ||
|
||||
strings.Contains(lower, "not directly supported") ||
|
||||
strings.Contains(lower, "not supported") {
|
||||
return true
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func hasCreateTableHead(sqlText string) bool {
|
||||
lines := strings.Split(sqlText, "\n")
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(line, "--") || strings.HasPrefix(line, "/*") || strings.HasPrefix(line, "*") {
|
||||
continue
|
||||
}
|
||||
return strings.HasPrefix(strings.ToLower(line), "create table")
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func buildFallbackCreateStatement(dbType string, schemaName string, tableName string, columns []connection.ColumnDefinition) (string, error) {
|
||||
table := strings.TrimSpace(tableName)
|
||||
if table == "" {
|
||||
return "", fmt.Errorf("表名不能为空")
|
||||
}
|
||||
if len(columns) == 0 {
|
||||
return "", fmt.Errorf("未获取到字段定义,无法生成建表语句")
|
||||
}
|
||||
|
||||
qualifiedTable := quoteTableIdentByType(dbType, schemaName, table)
|
||||
columnLines := make([]string, 0, len(columns)+1)
|
||||
primaryKeys := make([]string, 0, 2)
|
||||
|
||||
for _, col := range columns {
|
||||
colNameRaw := strings.TrimSpace(col.Name)
|
||||
if colNameRaw == "" {
|
||||
continue
|
||||
}
|
||||
colType := strings.TrimSpace(col.Type)
|
||||
if colType == "" {
|
||||
colType = "text"
|
||||
}
|
||||
|
||||
colName := quoteIdentByType(dbType, colNameRaw)
|
||||
defParts := []string{fmt.Sprintf("%s %s", colName, colType)}
|
||||
|
||||
if strings.EqualFold(strings.TrimSpace(col.Nullable), "NO") {
|
||||
defParts = append(defParts, "NOT NULL")
|
||||
}
|
||||
if col.Default != nil {
|
||||
defVal := strings.TrimSpace(*col.Default)
|
||||
if defVal != "" {
|
||||
defParts = append(defParts, "DEFAULT "+defVal)
|
||||
}
|
||||
}
|
||||
|
||||
columnLines = append(columnLines, " "+strings.Join(defParts, " "))
|
||||
if strings.EqualFold(strings.TrimSpace(col.Key), "PRI") {
|
||||
primaryKeys = append(primaryKeys, colName)
|
||||
}
|
||||
}
|
||||
|
||||
if len(columnLines) == 0 {
|
||||
return "", fmt.Errorf("字段定义为空,无法生成建表语句")
|
||||
}
|
||||
if len(primaryKeys) > 0 {
|
||||
columnLines = append(columnLines, " PRIMARY KEY ("+strings.Join(primaryKeys, ", ")+")")
|
||||
}
|
||||
|
||||
ddl := strings.Builder{}
|
||||
ddl.WriteString("CREATE TABLE ")
|
||||
ddl.WriteString(qualifiedTable)
|
||||
ddl.WriteString(" (\n")
|
||||
ddl.WriteString(strings.Join(columnLines, ",\n"))
|
||||
ddl.WriteString("\n);")
|
||||
return ddl.String(), nil
|
||||
}
|
||||
|
||||
func (a *App) DBGetColumns(config connection.ConnectionConfig, dbName string, tableName string) connection.QueryResult {
|
||||
runConfig := normalizeRunConfig(config, dbName)
|
||||
|
||||
|
||||
9
internal/db/agent_process_stub.go
Normal file
9
internal/db/agent_process_stub.go
Normal file
@@ -0,0 +1,9 @@
|
||||
//go:build !windows
|
||||
|
||||
package db
|
||||
|
||||
import "os/exec"
|
||||
|
||||
func configureAgentProcess(cmd *exec.Cmd) {
|
||||
_ = cmd
|
||||
}
|
||||
20
internal/db/agent_process_windows.go
Normal file
20
internal/db/agent_process_windows.go
Normal file
@@ -0,0 +1,20 @@
|
||||
//go:build windows
|
||||
|
||||
package db
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const windowsCreateNoWindow = 0x08000000
|
||||
|
||||
func configureAgentProcess(cmd *exec.Cmd) {
|
||||
if cmd == nil {
|
||||
return
|
||||
}
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
HideWindow: true,
|
||||
CreationFlags: windowsCreateNoWindow,
|
||||
}
|
||||
}
|
||||
@@ -67,11 +67,16 @@ func newMySQLAgentClient(executablePath string) (*mysqlAgentClient, error) {
|
||||
if pathText == "" {
|
||||
return nil, fmt.Errorf("MySQL 驱动代理路径为空")
|
||||
}
|
||||
if info, err := os.Stat(pathText); err != nil || info.IsDir() {
|
||||
info, err := os.Stat(pathText)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("MySQL 驱动代理不存在:%s", pathText)
|
||||
}
|
||||
if info.IsDir() {
|
||||
return nil, fmt.Errorf("MySQL 驱动代理路径是目录:%s", pathText)
|
||||
}
|
||||
|
||||
cmd := exec.Command(pathText)
|
||||
configureAgentProcess(cmd)
|
||||
stdin, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("创建 MySQL 驱动代理 stdin 失败:%w", err)
|
||||
|
||||
@@ -68,11 +68,16 @@ func newOptionalDriverAgentClient(driverType string, executablePath string) (*op
|
||||
if pathText == "" {
|
||||
return nil, fmt.Errorf("%s 驱动代理路径为空", driverDisplayName(driverType))
|
||||
}
|
||||
if info, err := os.Stat(pathText); err != nil || info.IsDir() {
|
||||
info, err := os.Stat(pathText)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s 驱动代理不存在:%s", driverDisplayName(driverType), pathText)
|
||||
}
|
||||
if info.IsDir() {
|
||||
return nil, fmt.Errorf("%s 驱动代理路径是目录:%s", driverDisplayName(driverType), pathText)
|
||||
}
|
||||
|
||||
cmd := exec.Command(pathText)
|
||||
configureAgentProcess(cmd)
|
||||
stdin, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("创建 %s 驱动代理 stdin 失败:%w", driverDisplayName(driverType), err)
|
||||
|
||||
Reference in New Issue
Block a user