feat: 增加版本化构建与发布流程
引入 VERSION 作为统一版本源,避免前端、后端、桌面打包和发布资产之间的版本漂移。 新增 tag 驱动的 Draft Release 流程与版本化资产命名,使本地演进和 GitHub 发布共享同一套约束。
This commit is contained in:
@@ -25,6 +25,7 @@ import (
|
||||
"nex/backend/internal/provider"
|
||||
"nex/backend/internal/repository"
|
||||
"nex/backend/internal/service"
|
||||
"nex/backend/pkg/buildinfo"
|
||||
|
||||
"github.com/getlantern/systray"
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -151,7 +152,11 @@ func main() {
|
||||
shutdownCtx, shutdownCancel = context.WithCancel(context.Background())
|
||||
|
||||
go func() {
|
||||
zapLogger.Info("AI Gateway 启动", zap.String("addr", server.Addr))
|
||||
zapLogger.Info("AI Gateway 启动",
|
||||
zap.String("addr", server.Addr),
|
||||
zap.String("version", buildinfo.Version()),
|
||||
zap.String("commit", buildinfo.Commit()),
|
||||
zap.String("build_time", buildinfo.BuildTime()))
|
||||
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
zapLogger.Fatal("服务器启动失败", zap.Error(err))
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"nex/backend/internal/provider"
|
||||
"nex/backend/internal/repository"
|
||||
"nex/backend/internal/service"
|
||||
"nex/backend/pkg/buildinfo"
|
||||
pkgLogger "nex/backend/pkg/logger"
|
||||
)
|
||||
|
||||
@@ -111,7 +112,11 @@ func main() {
|
||||
}
|
||||
|
||||
go func() {
|
||||
zapLogger.Info("AI Gateway 启动", zap.String("addr", srv.Addr))
|
||||
zapLogger.Info("AI Gateway 启动",
|
||||
zap.String("addr", srv.Addr),
|
||||
zap.String("version", buildinfo.Version()),
|
||||
zap.String("commit", buildinfo.Commit()),
|
||||
zap.String("build_time", buildinfo.BuildTime()))
|
||||
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
zapLogger.Fatal("服务器启动失败", zap.Error(err))
|
||||
}
|
||||
|
||||
119
backend/cmd/versionctl/main.go
Normal file
119
backend/cmd/versionctl/main.go
Normal file
@@ -0,0 +1,119 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"nex/backend/pkg/projectversion"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := run(os.Args[1:]); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func run(args []string) error {
|
||||
if len(args) == 0 {
|
||||
return usageError()
|
||||
}
|
||||
|
||||
root, err := projectversion.FindRepoRoot(mustGetwd())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch args[0] {
|
||||
case "print":
|
||||
version, readErr := projectversion.ReadString(root)
|
||||
if readErr != nil {
|
||||
return readErr
|
||||
}
|
||||
fmt.Println(version)
|
||||
return nil
|
||||
case "sync":
|
||||
return projectversion.Sync(root)
|
||||
case "check":
|
||||
return projectversion.Check(root)
|
||||
case "verify-tag":
|
||||
if len(args) != 2 {
|
||||
return fmt.Errorf("verify-tag 需要一个 tag 参数")
|
||||
}
|
||||
return projectversion.VerifyTag(root, args[1])
|
||||
case "macos-plist":
|
||||
if len(args) != 2 {
|
||||
return fmt.Errorf("macos-plist 需要一个最低系统版本参数")
|
||||
}
|
||||
return printMacOSPlist(root, args[1])
|
||||
case "asset-name":
|
||||
return printAssetName(root, args[1:])
|
||||
default:
|
||||
return usageError()
|
||||
}
|
||||
}
|
||||
|
||||
func printMacOSPlist(root, minMacOSVersion string) error {
|
||||
version, err := projectversion.ReadString(root)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
plist, err := projectversion.DesktopInfoPlist(version, minMacOSVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Print(plist)
|
||||
return nil
|
||||
}
|
||||
|
||||
func printAssetName(root string, args []string) error {
|
||||
if len(args) < 2 {
|
||||
return fmt.Errorf("asset-name 至少需要 kind 和 platform 参数")
|
||||
}
|
||||
|
||||
version, err := projectversion.ReadString(root)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch args[0] {
|
||||
case "server":
|
||||
if len(args) != 3 {
|
||||
return fmt.Errorf("server 资产命名需要 platform 和 arch 参数")
|
||||
}
|
||||
name, nameErr := projectversion.ServerAssetName(version, args[1], args[2])
|
||||
if nameErr != nil {
|
||||
return nameErr
|
||||
}
|
||||
fmt.Println(name)
|
||||
return nil
|
||||
case "desktop":
|
||||
if len(args) != 2 {
|
||||
return fmt.Errorf("desktop 资产命名只需要 platform 参数")
|
||||
}
|
||||
name, nameErr := projectversion.DesktopAssetName(version, args[1])
|
||||
if nameErr != nil {
|
||||
return nameErr
|
||||
}
|
||||
fmt.Println(name)
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("不支持的资产类型 %q", args[0])
|
||||
}
|
||||
}
|
||||
|
||||
func mustGetwd() string {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
return wd
|
||||
}
|
||||
|
||||
func usageError() error {
|
||||
return fmt.Errorf("用法: versionctl <print|sync|check|verify-tag|macos-plist|asset-name>")
|
||||
}
|
||||
22
backend/pkg/buildinfo/buildinfo.go
Normal file
22
backend/pkg/buildinfo/buildinfo.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package buildinfo
|
||||
|
||||
var (
|
||||
version = "dev"
|
||||
commit = "unknown"
|
||||
buildTime = "unknown"
|
||||
)
|
||||
|
||||
// Version 返回构建注入的版本号。
|
||||
func Version() string {
|
||||
return version
|
||||
}
|
||||
|
||||
// Commit 返回构建注入的 git commit。
|
||||
func Commit() string {
|
||||
return commit
|
||||
}
|
||||
|
||||
// BuildTime 返回构建注入的构建时间。
|
||||
func BuildTime() string {
|
||||
return buildTime
|
||||
}
|
||||
17
backend/pkg/buildinfo/buildinfo_test.go
Normal file
17
backend/pkg/buildinfo/buildinfo_test.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package buildinfo
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestDefaults(t *testing.T) {
|
||||
if Version() == "" {
|
||||
t.Fatal("Version() 不应为空")
|
||||
}
|
||||
|
||||
if Commit() == "" {
|
||||
t.Fatal("Commit() 不应为空")
|
||||
}
|
||||
|
||||
if BuildTime() == "" {
|
||||
t.Fatal("BuildTime() 不应为空")
|
||||
}
|
||||
}
|
||||
342
backend/pkg/projectversion/version.go
Normal file
342
backend/pkg/projectversion/version.go
Normal file
@@ -0,0 +1,342 @@
|
||||
package projectversion
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const versionFileName = "VERSION"
|
||||
|
||||
var (
|
||||
semverRegex = regexp.MustCompile(`^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$`)
|
||||
packageVersionRegex = regexp.MustCompile(`(?m)^(\s*"version"\s*:\s*")([^"]+)(",?)$`)
|
||||
frontendVersionFiles = []string{
|
||||
"frontend/.env.production",
|
||||
"frontend/.env.development",
|
||||
"frontend/.env.desktop",
|
||||
}
|
||||
)
|
||||
|
||||
type Version struct {
|
||||
Major int
|
||||
Minor int
|
||||
Patch int
|
||||
}
|
||||
|
||||
func Parse(raw string) (Version, error) {
|
||||
trimmed := strings.TrimSpace(raw)
|
||||
parts := semverRegex.FindStringSubmatch(trimmed)
|
||||
if parts == nil {
|
||||
return Version{}, fmt.Errorf("版本号 %q 不符合 x.y.z 格式", raw)
|
||||
}
|
||||
|
||||
major, err := strconv.Atoi(parts[1])
|
||||
if err != nil {
|
||||
return Version{}, fmt.Errorf("解析 major 失败: %w", err)
|
||||
}
|
||||
|
||||
minor, err := strconv.Atoi(parts[2])
|
||||
if err != nil {
|
||||
return Version{}, fmt.Errorf("解析 minor 失败: %w", err)
|
||||
}
|
||||
|
||||
patch, err := strconv.Atoi(parts[3])
|
||||
if err != nil {
|
||||
return Version{}, fmt.Errorf("解析 patch 失败: %w", err)
|
||||
}
|
||||
|
||||
return Version{Major: major, Minor: minor, Patch: patch}, nil
|
||||
}
|
||||
|
||||
func (v Version) String() string {
|
||||
return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch)
|
||||
}
|
||||
|
||||
func FindRepoRoot(start string) (string, error) {
|
||||
current := start
|
||||
for {
|
||||
workspacePath := filepath.Join(current, "go.work")
|
||||
if _, err := os.Stat(workspacePath); err == nil {
|
||||
return current, nil
|
||||
}
|
||||
|
||||
parent := filepath.Dir(current)
|
||||
if parent == current {
|
||||
return "", errors.New("未找到仓库根目录 go.work")
|
||||
}
|
||||
|
||||
current = parent
|
||||
}
|
||||
}
|
||||
|
||||
func Read(root string) (Version, error) {
|
||||
content, err := os.ReadFile(filepath.Join(root, versionFileName))
|
||||
if err != nil {
|
||||
return Version{}, fmt.Errorf("读取 VERSION 失败: %w", err)
|
||||
}
|
||||
|
||||
return Parse(string(content))
|
||||
}
|
||||
|
||||
func ReadString(root string) (string, error) {
|
||||
version, err := Read(root)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return version.String(), nil
|
||||
}
|
||||
|
||||
func Sync(root string) error {
|
||||
version, err := ReadString(root)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
packageJSONPath := filepath.Join(root, "frontend", "package.json")
|
||||
packageJSONContent, err := os.ReadFile(packageJSONPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("读取 frontend/package.json 失败: %w", err)
|
||||
}
|
||||
|
||||
updatedPackageJSON, err := UpdatePackageJSONVersion(string(packageJSONContent), version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.WriteFile(packageJSONPath, []byte(updatedPackageJSON), 0o644); err != nil {
|
||||
return fmt.Errorf("写入 frontend/package.json 失败: %w", err)
|
||||
}
|
||||
|
||||
for _, relPath := range frontendVersionFiles {
|
||||
fullPath := filepath.Join(root, relPath)
|
||||
content, err := os.ReadFile(fullPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("读取 %s 失败: %w", relPath, err)
|
||||
}
|
||||
|
||||
updated := UpsertEnvVar(string(content), "VITE_APP_VERSION", version)
|
||||
if err := os.WriteFile(fullPath, []byte(updated), 0o644); err != nil {
|
||||
return fmt.Errorf("写入 %s 失败: %w", relPath, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Check(root string) error {
|
||||
version, err := ReadString(root)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var errs []error
|
||||
|
||||
packageJSONPath := filepath.Join(root, "frontend", "package.json")
|
||||
packageJSONContent, err := os.ReadFile(packageJSONPath)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("读取 frontend/package.json 失败: %w", err))
|
||||
} else {
|
||||
actualVersion, readErr := ReadPackageJSONVersion(string(packageJSONContent))
|
||||
if readErr != nil {
|
||||
errs = append(errs, readErr)
|
||||
} else if actualVersion != version {
|
||||
errs = append(errs, fmt.Errorf("frontend/package.json 版本为 %s,期望 %s", actualVersion, version))
|
||||
}
|
||||
}
|
||||
|
||||
for _, relPath := range frontendVersionFiles {
|
||||
fullPath := filepath.Join(root, relPath)
|
||||
content, readErr := os.ReadFile(fullPath)
|
||||
if readErr != nil {
|
||||
errs = append(errs, fmt.Errorf("读取 %s 失败: %w", relPath, readErr))
|
||||
continue
|
||||
}
|
||||
|
||||
actualValue, ok := ReadEnvVar(string(content), "VITE_APP_VERSION")
|
||||
if !ok {
|
||||
errs = append(errs, fmt.Errorf("%s 缺少 VITE_APP_VERSION", relPath))
|
||||
continue
|
||||
}
|
||||
|
||||
if actualValue != version {
|
||||
errs = append(errs, fmt.Errorf("%s 的 VITE_APP_VERSION 为 %s,期望 %s", relPath, actualValue, version))
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func VerifyTag(root, tag string) error {
|
||||
version, err := ReadString(root)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(tag, "v") {
|
||||
return fmt.Errorf("tag %q 必须以 v 开头", tag)
|
||||
}
|
||||
|
||||
if tag[1:] != version {
|
||||
return fmt.Errorf("tag %q 与 VERSION %q 不一致", tag, version)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func UpdatePackageJSONVersion(content, version string) (string, error) {
|
||||
if _, err := Parse(version); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if !packageVersionRegex.MatchString(content) {
|
||||
return "", errors.New("frontend/package.json 缺少 version 字段")
|
||||
}
|
||||
|
||||
updated := packageVersionRegex.ReplaceAllString(content, `${1}`+version+`${3}`)
|
||||
return updated, nil
|
||||
}
|
||||
|
||||
func ReadPackageJSONVersion(content string) (string, error) {
|
||||
parts := packageVersionRegex.FindStringSubmatch(content)
|
||||
if parts == nil {
|
||||
return "", errors.New("frontend/package.json 缺少 version 字段")
|
||||
}
|
||||
|
||||
if _, err := Parse(parts[2]); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return parts[2], nil
|
||||
}
|
||||
|
||||
func UpsertEnvVar(content, key, value string) string {
|
||||
lines := strings.Split(strings.TrimRight(content, "\n"), "\n")
|
||||
if len(lines) == 1 && lines[0] == "" {
|
||||
lines = lines[:0]
|
||||
}
|
||||
|
||||
updated := false
|
||||
for i, line := range lines {
|
||||
if strings.HasPrefix(line, key+"=") {
|
||||
lines[i] = key + "=" + value
|
||||
updated = true
|
||||
}
|
||||
}
|
||||
|
||||
if !updated {
|
||||
lines = append(lines, key+"="+value)
|
||||
}
|
||||
|
||||
return strings.Join(lines, "\n") + "\n"
|
||||
}
|
||||
|
||||
func ReadEnvVar(content, key string) (string, bool) {
|
||||
for _, line := range strings.Split(content, "\n") {
|
||||
if strings.HasPrefix(line, key+"=") {
|
||||
return strings.TrimPrefix(line, key+"="), true
|
||||
}
|
||||
}
|
||||
|
||||
return "", false
|
||||
}
|
||||
|
||||
func ServerAssetName(version, goos, arch string) (string, error) {
|
||||
if _, err := Parse(version); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
switch goos {
|
||||
case "linux", "windows", "darwin":
|
||||
default:
|
||||
return "", fmt.Errorf("不支持的 server 平台 %q", goos)
|
||||
}
|
||||
|
||||
if arch == "" {
|
||||
return "", errors.New("server 资产命名缺少架构")
|
||||
}
|
||||
|
||||
ext := ".tar.gz"
|
||||
if goos == "windows" {
|
||||
ext = ".zip"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("nex-server_%s_%s_%s%s", version, goos, arch, ext), nil
|
||||
}
|
||||
|
||||
func DesktopAssetName(version, platform string) (string, error) {
|
||||
if _, err := Parse(version); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
switch platform {
|
||||
case "linux":
|
||||
return fmt.Sprintf("Nex_%s_linux_amd64.tar.gz", version), nil
|
||||
case "windows":
|
||||
return fmt.Sprintf("Nex_%s_windows_amd64.zip", version), nil
|
||||
case "macos":
|
||||
return fmt.Sprintf("Nex_%s_macOS_universal.zip", version), nil
|
||||
default:
|
||||
return "", fmt.Errorf("不支持的 desktop 平台 %q", platform)
|
||||
}
|
||||
}
|
||||
|
||||
func DesktopInfoPlist(version, minMacOSVersion string) (string, error) {
|
||||
if _, err := Parse(version); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if strings.TrimSpace(minMacOSVersion) == "" {
|
||||
return "", errors.New("min macOS version 不能为空")
|
||||
}
|
||||
|
||||
content := strings.Join([]string{
|
||||
`<?xml version="1.0" encoding="UTF-8"?>`,
|
||||
`<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">`,
|
||||
`<plist version="1.0">`,
|
||||
`<dict>`,
|
||||
` <key>CFBundleDevelopmentRegion</key>`,
|
||||
` <string>zh-Hans</string>`,
|
||||
` <key>CFBundleExecutable</key>`,
|
||||
` <string>nex</string>`,
|
||||
` <key>CFBundleIconFile</key>`,
|
||||
` <string>icon</string>`,
|
||||
` <key>CFBundleIdentifier</key>`,
|
||||
` <string>com.lanyuanxiaoyao.nex</string>`,
|
||||
` <key>CFBundleInfoDictionaryVersion</key>`,
|
||||
` <string>6.0</string>`,
|
||||
` <key>LSApplicationCategoryType</key>`,
|
||||
` <string>public.app-category.developer-tools</string>`,
|
||||
` <key>CFBundleName</key>`,
|
||||
` <string>Nex</string>`,
|
||||
` <key>CFBundleDisplayName</key>`,
|
||||
` <string>Nex</string>`,
|
||||
` <key>CFBundlePackageType</key>`,
|
||||
` <string>APPL</string>`,
|
||||
` <key>CFBundleShortVersionString</key>`,
|
||||
` <string>` + version + `</string>`,
|
||||
` <key>CFBundleVersion</key>`,
|
||||
` <string>` + version + `</string>`,
|
||||
` <key>NSHumanReadableCopyright</key>`,
|
||||
` <string>Copyright © 2026 Nex</string>`,
|
||||
` <key>LSMinimumSystemVersion</key>`,
|
||||
` <string>` + minMacOSVersion + `</string>`,
|
||||
` <key>LSUIElement</key>`,
|
||||
` <true/>`,
|
||||
` <key>NSHighResolutionCapable</key>`,
|
||||
` <true/>`,
|
||||
`</dict>`,
|
||||
`</plist>`,
|
||||
}, "\n")
|
||||
|
||||
return content + "\n", nil
|
||||
}
|
||||
113
backend/pkg/projectversion/version_test.go
Normal file
113
backend/pkg/projectversion/version_test.go
Normal file
@@ -0,0 +1,113 @@
|
||||
package projectversion
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
version, err := Parse("1.2.3")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, Version{Major: 1, Minor: 2, Patch: 3}, version)
|
||||
assert.Equal(t, "1.2.3", version.String())
|
||||
})
|
||||
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
invalidValues := []string{"", "1.2", "1.2.3.4", "v1.2.3", "01.2.3", "1.02.3"}
|
||||
for _, tc := range invalidValues {
|
||||
_, err := Parse(tc)
|
||||
assert.Error(t, err, "%q 应校验失败", tc)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestUpdatePackageJSONVersion(t *testing.T) {
|
||||
content := "{\n \"name\": \"frontend\",\n \"version\": \"0.0.0\"\n}\n"
|
||||
updated, err := UpdatePackageJSONVersion(content, "1.2.3")
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, updated, `"version": "1.2.3"`)
|
||||
|
||||
version, err := ReadPackageJSONVersion(updated)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "1.2.3", version)
|
||||
}
|
||||
|
||||
func TestUpsertEnvVar(t *testing.T) {
|
||||
updated := UpsertEnvVar("VITE_API_BASE=/api\n", "VITE_APP_VERSION", "1.2.3")
|
||||
assert.Contains(t, updated, "VITE_API_BASE=/api\n")
|
||||
assert.Contains(t, updated, "VITE_APP_VERSION=1.2.3\n")
|
||||
|
||||
updated = UpsertEnvVar(updated, "VITE_APP_VERSION", "2.0.0")
|
||||
value, ok := ReadEnvVar(updated, "VITE_APP_VERSION")
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, "2.0.0", value)
|
||||
assert.Equal(t, 1, strings.Count(updated, "VITE_APP_VERSION="))
|
||||
}
|
||||
|
||||
func TestSyncAndCheck(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
require.NoError(t, os.WriteFile(filepath.Join(root, "VERSION"), []byte("1.2.3\n"), 0o644))
|
||||
require.NoError(t, os.MkdirAll(filepath.Join(root, "frontend"), 0o755))
|
||||
require.NoError(t, os.WriteFile(filepath.Join(root, "frontend", "package.json"), []byte("{\n \"name\": \"frontend\",\n \"version\": \"0.0.0\"\n}\n"), 0o644))
|
||||
require.NoError(t, os.WriteFile(filepath.Join(root, "frontend", ".env.production"), []byte("VITE_API_BASE=/api\n"), 0o644))
|
||||
require.NoError(t, os.WriteFile(filepath.Join(root, "frontend", ".env.development"), []byte("VITE_API_BASE=\n"), 0o644))
|
||||
require.NoError(t, os.WriteFile(filepath.Join(root, "frontend", ".env.desktop"), []byte("VITE_API_BASE=\n"), 0o644))
|
||||
|
||||
require.NoError(t, Sync(root))
|
||||
require.NoError(t, Check(root))
|
||||
|
||||
packageJSONContent, err := os.ReadFile(filepath.Join(root, "frontend", "package.json"))
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, string(packageJSONContent), `"version": "1.2.3"`)
|
||||
|
||||
for _, relPath := range frontendVersionFiles {
|
||||
content, readErr := os.ReadFile(filepath.Join(root, relPath))
|
||||
require.NoError(t, readErr)
|
||||
assert.Contains(t, string(content), "VITE_APP_VERSION=1.2.3\n")
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyTag(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
require.NoError(t, os.WriteFile(filepath.Join(root, "VERSION"), []byte("1.2.3\n"), 0o644))
|
||||
|
||||
require.NoError(t, VerifyTag(root, "v1.2.3"))
|
||||
assert.Error(t, VerifyTag(root, "1.2.3"))
|
||||
assert.Error(t, VerifyTag(root, "v1.2.4"))
|
||||
}
|
||||
|
||||
func TestAssetNames(t *testing.T) {
|
||||
linuxServer, err := ServerAssetName("1.2.3", "linux", "amd64")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "nex-server_1.2.3_linux_amd64.tar.gz", linuxServer)
|
||||
|
||||
macServer, err := ServerAssetName("1.2.3", "darwin", "arm64")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "nex-server_1.2.3_darwin_arm64.tar.gz", macServer)
|
||||
|
||||
macDesktop, err := DesktopAssetName("1.2.3", "macos")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "Nex_1.2.3_macOS_universal.zip", macDesktop)
|
||||
|
||||
_, err = DesktopAssetName("1.2.3", "ios")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestDesktopInfoPlist(t *testing.T) {
|
||||
plist, err := DesktopInfoPlist("1.2.3", "13.0")
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, plist, "<key>CFBundleShortVersionString</key>\n <string>1.2.3</string>")
|
||||
assert.Contains(t, plist, "<key>CFBundleVersion</key>\n <string>1.2.3</string>")
|
||||
assert.Contains(t, plist, "<key>LSMinimumSystemVersion</key>\n <string>13.0</string>")
|
||||
|
||||
_, err = DesktopInfoPlist("1.2", "13.0")
|
||||
assert.Error(t, err)
|
||||
_, err = DesktopInfoPlist("1.2.3", "")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
Reference in New Issue
Block a user