release: prepare v2.1.4

This commit is contained in:
Aimili
2026-08-27 20:44:58 +08:00
parent f2f8afda76
commit 6249c92528
6 changed files with 26 additions and 20 deletions
+2 -2
View File
@@ -7,9 +7,9 @@ on:
workflow_dispatch:
inputs:
release_tag:
description: Existing formal tag to publish, for example v2.1.3
description: Existing formal tag to publish, for example v2.1.4
required: true
default: v2.1.3
default: v2.1.4
type: string
concurrency:
+11 -5
View File
@@ -1,6 +1,12 @@
# AimiliVPN V2.1.3 正式版
# AimiliVPN V2.1.4 正式版
V2.1.3 是一次完整的稳定性、安全性和节点识别修复版本。重点解决节点切换期间状态显示过早、未知网络误判住宅、部分 VPS 节点源回退不合理,以及安装卸载残留等问题
V2.1.4 包含 V2.1.3 的全部稳定性、安全性和节点识别改进,并修复正式版重新安装验收中发现的手动断开状态遗漏
## V2.1.4 热修复
- 修复点击“断开连接”后 OpenVPN、活动节点和 table 100 已清理,但 Web API 的 `tunnel_ready` / `proxy_ready` 仍短暂保留为 `true` 的问题。
- 手动断开现在统一复用完整连接状态清理流程;活动节点、连接中状态、隧道、代理出口、出口 IP 与路由规则会同步归零。
- 新增手动断开状态回归测试,并在真实 VPS 上完成断开与重新连接复验。
## IP 类型识别
@@ -31,7 +37,7 @@ V2.1.3 是一次完整的稳定性、安全性和节点识别修复版本。重
## 验证与兼容性
- 本地 Python 单元测试 51/51 通过,Python 编译、Dashboard JavaScript 语法、安装脚本语法和补丁格式检查通过。
- 本地 Python 单元测试 52/52 通过,Python 编译、Dashboard JavaScript 语法、安装脚本语法和补丁格式检查通过。
- Ubuntu 22.04 x86_64 VPS 已完成真实卸载、重新安装、节点源、连接切换、代理出口、Web API、权限和路由残留验证。
- 发布流水线会在 Python 3.9、3.11、3.13 上运行完整测试。
- Docker 会分别构建验证 `linux/amd64``linux/386``linux/arm64``linux/arm/v7`,并发布统一多架构镜像清单。
@@ -44,8 +50,8 @@ V2.1.3 是一次完整的稳定性、安全性和节点识别修复版本。重
## 下载与更新
- GitHub Release`aimilivpn-v2.1.3-linux-source.tar.gz``sha256sums.txt`
- GHCR`2.1.3``2.1``latest` 多架构镜像标签。
- GitHub Release`aimilivpn-v2.1.4-linux-source.tar.gz``sha256sums.txt`
- GHCR`2.1.4``2.1``latest` 多架构镜像标签。
Python 源码安装更新:
+1 -1
View File
@@ -1 +1 @@
2.1.3
2.1.4
+2 -2
View File
@@ -204,9 +204,9 @@ def get_app_version():
try:
with open(os.path.join(INSTALL_DIR, "VERSION"), "r", encoding="utf-8") as f:
version = f.read().strip().lstrip("vV")
return version or "2.1.3"
return version or "2.1.4"
except Exception:
return "2.1.3"
return "2.1.4"
def load_ui_cfg():
import json
+6 -6
View File
@@ -828,8 +828,8 @@ class ManagerLogicTests(unittest.TestCase):
self.assertEqual(519, entries[-1]["index"])
def test_web_update_controls_only_expose_stable_main_channel(self) -> None:
self.assertEqual("2.1.3", manager.APP_VERSION)
self.assertEqual("V2.1.3 正式版", manager.APP_VERSION_LABEL)
self.assertEqual("2.1.4", manager.APP_VERSION)
self.assertEqual("V2.1.4 正式版", manager.APP_VERSION_LABEL)
self.assertIn("检测更新", manager.INDEX_HTML)
self.assertIn("/api/check_update", manager.INDEX_HTML)
self.assertIn("/tree/main", manager.INDEX_HTML)
@@ -874,7 +874,7 @@ class ManagerLogicTests(unittest.TestCase):
def test_release_workflow_uses_full_patch_version(self) -> None:
workflow_text = (manager.ROOT_DIR / ".github" / "workflows" / "release.yml").read_text(encoding="utf-8")
self.assertIn("default: v2.1.3", workflow_text)
self.assertIn("default: v2.1.4", workflow_text)
self.assertIn("AimiliVPN V$(tr -d '\\r\\n' < VERSION) 正式版", workflow_text)
self.assertNotIn("cut -d. -f1,2 VERSION", workflow_text)
@@ -901,8 +901,8 @@ class ManagerLogicTests(unittest.TestCase):
def test_latest_release_check_reports_current_formal_version(self) -> None:
release = {
"tag_name": "v2.1.3",
"name": "AimiliVPN V2.1.3 正式版",
"tag_name": "v2.1.4",
"name": "AimiliVPN V2.1.4 正式版",
"draft": False,
"prerelease": False,
}
@@ -910,7 +910,7 @@ class ManagerLogicTests(unittest.TestCase):
result = manager.check_latest_release()
self.assertFalse(result["update_available"])
self.assertEqual("V2.1.3 正式版", result["current_version_label"])
self.assertEqual("V2.1.4 正式版", result["current_version_label"])
def test_latest_release_check_reports_source_update_command(self) -> None:
release = {"tag_name": "v2.2.0", "draft": False, "prerelease": False}
+4 -4
View File
@@ -141,7 +141,7 @@ UPDATE_COMMAND = (
)
ROOT_DIR = Path(sys.executable).resolve().parent if globals().get("__compiled__") else Path(__file__).resolve().parent
DEFAULT_APP_VERSION = "2.1.3"
DEFAULT_APP_VERSION = "2.1.4"
try:
_version_text = (ROOT_DIR / "VERSION").read_text(encoding="utf-8").strip()
except OSError:
@@ -4165,12 +4165,12 @@ INDEX_HTML = r"""<!doctype html>
<div class="dropdown">
<button id="github_btn" class="btn-primary" type="button" aria-expanded="false" aria-controls="github_dropdown" style="background: rgba(255, 255, 255, 0.08); border: 1px solid var(--border-color); color: var(--text-primary);">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" viewBox="0 0 16 16" style="vertical-align: middle; margin-right: 4px;"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z"/></svg>
<span id="github_version_label">V2.1.3 正式版</span>
<span id="github_version_label">V2.1.4 正式版</span>
<svg xmlns="http://www.w3.org/2000/svg" style="width:12px; height:12px; margin-left: 2px;" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="3"><path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7" /></svg>
</button>
<div id="github_dropdown" class="dropdown-content github-dropdown">
<div class="version-current">
<div id="current_version_label" class="version-current-label">V2.1.3 正式版</div>
<div id="current_version_label" class="version-current-label">V2.1.4 正式版</div>
<div id="deployment_mode_label" class="version-current-meta">Python 源码部署 · 更新通道main</div>
</div>
<button id="check_update_btn" type="button" onclick="checkForUpdate(event)">
@@ -4997,7 +4997,7 @@ function stableSortNodes() {
}
function render(){
const versionLabel = state.app_version_label || "V2.1.3 正式版";
const versionLabel = state.app_version_label || "V2.1.4 正式版";
if ($("github_version_label")) $("github_version_label").textContent = versionLabel;
if ($("current_version_label")) $("current_version_label").textContent = versionLabel;
if ($("deployment_mode_label")) {