mirror of
https://gitee.com/xmz7891/ykxmz.git
synced 2026-10-18 11:20:43 +00:00
2.0.0:安装时可设中转服务器 + 手表端新增设置页 + 中转默认留空
按用户定的版本规矩,这一轮既有新内容又有重构,所以升第二位: 1.9.0 -> 2.0.0(最后一位清 0)。 == 一、安装时就能设中转服务器(用户要求)== 安装向导那一页现在是两个输入框:配对码 + 中转服务器。 装的时候是"第一次设置",最该一次把地址填好,不然装完还得去 双击 exe 找设置窗口,很容易漏。 只填域名就行(自动补 https://),留空 = 只走局域网。 批量静默部署同样支持: ykxmz-setup-2.0.0.exe /VERYSILENT /CODE=246810 /RELAY=relay.你的域名 为此新增了一个 --relay-url 参数: --relay 连接一次、打印状态、然后退出(人工排查用) --relay-url 只写进配置,不连接、不退出(安装流程用) 用错那个的话,安装包的命令链会在中间就 return,后面的 --silent 根本跑不到 —— 所以必须分开。 == 二、手表端新增「设置」页(用户要求)== 用户原话:"安卓端加一个设置,各种设置就在这里面调"。 以前设置散在首页(只有一个"中转设置"按钮),现在统一收进设置页: * 中转服务器(填写 / 一键清空) * 记住的配对码(显示 / 清除) * 已配对的电脑(看数量 / 清空,带二次确认) * 关于(版本号从 APK 读,不会和代码对不上) == 三、中转地址默认留空 + 一次性迁移(用户报的问题)== 用户原话:"安卓端为什么还是默认填的我的中转站,以后改成手动填"。 代码里的默认值早就是空的了,但用户看到的还是那个地址 —— 因为**老版本 1.5.0 把作者的域名硬编码成了默认值**,第一次运行就 存进了 SharedPreferences。装新 APK 不会清偏好,所以那个值一直留着。 用户其实从来没"选"过这个地址,是程序硬塞的。 所以加了 migrateLegacyRelay():存的值如果正好等于当时内置的域名, 就当成没设过、清掉。只清完全相等的,用户手填的不会动。 == 四、两个测试自身的毛病 == * test_lan_optimize 那条"没有把旧公网域名当默认值"太粗 —— 它查的是源码里任何位置,于是迁移注释里引用那行老代码就把它判红了。 改成先剥掉注释再查(断言要测的是"有没有把它当默认值")。 * test_image_modes 偶发红:它开真实 Tk 全屏窗口,在全量套件里跑 偶尔起不来(单独跑 19/19 全过)。加了有界重试。 偶发红的测试会让"全量通过"失去意义。 == 五、新增防复发断言 == _tests/test_relay_default_blank.py(15 项,回归阶段 3a3b): * 默认值必须是空串 * 除了迁移清单,源码里不许有写死的中转域名 * 迁移必须真的被调用(不是只定义) * 迁移必须是 remove,不能换成另一个硬编码值 * Windows 端的默认也得是空 理由:用户说过"这个中转站我就自己用,不对外" —— 值得用断言钉死。 == 六、顺手修的 == * release_all.py 崩在 Gitee 那一步:Gitee 的 GET /releases/tags/{tag} 在"标签存在、但还没发行版"时返回的是 **HTTP 200 + JSON null**(不是 404),于是 .get("id") 直接 AttributeError。发 1.9.0 时踩到,已改成容忍 None。 * installer/build_installer.py 里 license-combined.txt 是从 docs/ 三份文档重新生成的,所以光改生成物没用(下次构建又变回去)。 改成在 build_license() 里统一注入当前版本。 * ExE 文件属性里的版本(version_info.txt)从 1.7.1 起就一直没跟上。 全量回归 43 个阶段全部通过。
This commit is contained in:
@@ -1,3 +1,33 @@
|
||||
## 2.0.0
|
||||
|
||||
- **安装时就能设中转服务器**:安装向导那一页现在是两个输入框 ——
|
||||
**配对码**和**中转服务器**。装的时候是"第一次设置",那时候最该一次
|
||||
把地址填好,不然装完还得去双击 exe 找设置窗口,很容易漏。
|
||||
只填域名就行(自动补 `https://`),留空 = 只走局域网。
|
||||
批量部署同样支持:`/VERYSILENT /CODE=246810 /RELAY=relay.你的域名`
|
||||
|
||||
- **手表端新增「设置」页**:以前设置散在首页(只有一个"中转设置"按钮),
|
||||
现在统一收进来,在里面调:
|
||||
- **中转服务器**(填写 / 一键清空)
|
||||
- **记住的配对码**(显示 / 清除)
|
||||
- **已配对的电脑**(看数量 / 清空)
|
||||
- **关于**(版本号 + 连接顺序说明)
|
||||
|
||||
- **中转地址改成手动填,默认留空**:以前有一版把作者的服务器地址写死成
|
||||
默认值,第一次运行就存进了偏好设置,装新版本也不会清 ——
|
||||
看起来就像"默认还是别人的中转站"。现在默认是空的,
|
||||
并且会做**一次性迁移**把那个旧的内置地址清掉
|
||||
(只清完全相等的,你自己填的不会动)。
|
||||
|
||||
- **修「局域网连上后要推出去点设备才能控制」**:手表上有四个页面,
|
||||
切换时靠手工藏上一页,新加的「局域网连接」页漏藏了 ——
|
||||
所以从它连上之后控制面板其实已经显示,却被盖住看不见。
|
||||
|
||||
- **修「双击 exe 没反应」**:装完之后双击现在会打开设置窗口。
|
||||
|
||||
- 新增**版本号一致性检查**和**中转地址默认值检查**两道断言,
|
||||
防止这类"漏改一处"的问题再出现。
|
||||
|
||||
## 1.9.0
|
||||
|
||||
- **被控端终于有设置窗口了**:装完之后双击 `ykxmz.exe` 就能打开,里面可以改
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
2.0.0:安装时可设中转服务器 + 手表端新增设置页 + 中转默认留空
|
||||
|
||||
按用户定的版本规矩,这一轮既有新内容又有重构,所以升第二位:
|
||||
1.9.0 -> 2.0.0(最后一位清 0)。
|
||||
|
||||
== 一、安装时就能设中转服务器(用户要求)==
|
||||
|
||||
安装向导那一页现在是两个输入框:配对码 + 中转服务器。
|
||||
装的时候是"第一次设置",最该一次把地址填好,不然装完还得去
|
||||
双击 exe 找设置窗口,很容易漏。
|
||||
|
||||
只填域名就行(自动补 https://),留空 = 只走局域网。
|
||||
批量静默部署同样支持:
|
||||
ykxmz-setup-2.0.0.exe /VERYSILENT /CODE=246810 /RELAY=relay.你的域名
|
||||
|
||||
为此新增了一个 --relay-url 参数:
|
||||
--relay 连接一次、打印状态、然后退出(人工排查用)
|
||||
--relay-url 只写进配置,不连接、不退出(安装流程用)
|
||||
用错那个的话,安装包的命令链会在中间就 return,后面的 --silent
|
||||
根本跑不到 —— 所以必须分开。
|
||||
|
||||
== 二、手表端新增「设置」页(用户要求)==
|
||||
|
||||
用户原话:"安卓端加一个设置,各种设置就在这里面调"。
|
||||
以前设置散在首页(只有一个"中转设置"按钮),现在统一收进设置页:
|
||||
* 中转服务器(填写 / 一键清空)
|
||||
* 记住的配对码(显示 / 清除)
|
||||
* 已配对的电脑(看数量 / 清空,带二次确认)
|
||||
* 关于(版本号从 APK 读,不会和代码对不上)
|
||||
|
||||
== 三、中转地址默认留空 + 一次性迁移(用户报的问题)==
|
||||
|
||||
用户原话:"安卓端为什么还是默认填的我的中转站,以后改成手动填"。
|
||||
|
||||
代码里的默认值早就是空的了,但用户看到的还是那个地址 ——
|
||||
因为**老版本 1.5.0 把作者的域名硬编码成了默认值**,第一次运行就
|
||||
存进了 SharedPreferences。装新 APK 不会清偏好,所以那个值一直留着。
|
||||
用户其实从来没"选"过这个地址,是程序硬塞的。
|
||||
|
||||
所以加了 migrateLegacyRelay():存的值如果正好等于当时内置的域名,
|
||||
就当成没设过、清掉。只清完全相等的,用户手填的不会动。
|
||||
|
||||
== 四、两个测试自身的毛病 ==
|
||||
|
||||
* test_lan_optimize 那条"没有把旧公网域名当默认值"太粗 ——
|
||||
它查的是源码里任何位置,于是迁移注释里引用那行老代码就把它判红了。
|
||||
改成先剥掉注释再查(断言要测的是"有没有把它当默认值")。
|
||||
|
||||
* test_image_modes 偶发红:它开真实 Tk 全屏窗口,在全量套件里跑
|
||||
偶尔起不来(单独跑 19/19 全过)。加了有界重试。
|
||||
偶发红的测试会让"全量通过"失去意义。
|
||||
|
||||
== 五、新增防复发断言 ==
|
||||
|
||||
_tests/test_relay_default_blank.py(15 项,回归阶段 3a3b):
|
||||
* 默认值必须是空串
|
||||
* 除了迁移清单,源码里不许有写死的中转域名
|
||||
* 迁移必须真的被调用(不是只定义)
|
||||
* 迁移必须是 remove,不能换成另一个硬编码值
|
||||
* Windows 端的默认也得是空
|
||||
|
||||
理由:用户说过"这个中转站我就自己用,不对外" —— 值得用断言钉死。
|
||||
|
||||
== 六、顺手修的 ==
|
||||
|
||||
* release_all.py 崩在 Gitee 那一步:Gitee 的
|
||||
GET /releases/tags/{tag} 在"标签存在、但还没发行版"时返回的是
|
||||
**HTTP 200 + JSON null**(不是 404),于是 .get("id") 直接
|
||||
AttributeError。发 1.9.0 时踩到,已改成容忍 None。
|
||||
|
||||
* installer/build_installer.py 里 license-combined.txt 是从
|
||||
docs/ 三份文档重新生成的,所以光改生成物没用(下次构建又变回去)。
|
||||
改成在 build_license() 里统一注入当前版本。
|
||||
|
||||
* ExE 文件属性里的版本(version_info.txt)从 1.7.1 起就一直没跟上。
|
||||
|
||||
全量回归 43 个阶段全部通过。
|
||||
@@ -0,0 +1,401 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
r'''安卓端:中转地址改成"手动填" + 加一个设置页。
|
||||
|
||||
== 用户报的问题 ==
|
||||
|
||||
「安卓端为什么还是默认填的我的中转站,以后改成手动填,
|
||||
安卓端加一个设置,各种设置就在这里面调」
|
||||
|
||||
== 根因 ==
|
||||
|
||||
代码里的默认值**已经改成空**了:
|
||||
|
||||
private static final String DEFAULT_RELAY_URL = "";
|
||||
|
||||
但你手表上看到的还是 `https://relay.xxmxxs.top` —— 因为
|
||||
**老版本 1.5.0 把作者的域名硬编码成默认值**(git 历史里查到了):
|
||||
|
||||
private static final String DEFAULT_RELAY_URL = "https://relay.xxmxxs.top";
|
||||
|
||||
第一次运行它就 `setRelayUrl(默认值)` 存进了 SharedPreferences。
|
||||
装新 APK **不会清偏好设置**,所以那个值一直留着。
|
||||
|
||||
**用户从来没"选"过这个地址** —— 是程序硬塞的。所以正确做法是
|
||||
**一次性迁移**:如果存的就是当时内置的那个域名,当成没设过,清掉。
|
||||
|
||||
== 做的事 ==
|
||||
|
||||
1. 迁移:把老版本硬塞的那个域名从偏好里清掉
|
||||
2. 新增「设置」页,把散落的设置收进来:
|
||||
* 中转服务器(手动填 / 清空)
|
||||
* 记住的配对码(看 / 清)
|
||||
* 已配对设备(看数量 / 清空)
|
||||
* 关于(版本号、连接方式说明)
|
||||
3. 首页那个「中转设置(跨网络用)」按钮改成「设置」,进设置页
|
||||
4. settingsPanel 纳入 showOnly(),避免再出现"面板漏藏"那类问题
|
||||
'''
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
SRC = (ROOT / "android" / "src" / "com" / "lanremote" / "watch"
|
||||
/ "MainActivity.java")
|
||||
|
||||
|
||||
# ======================================================================
|
||||
# 1. 迁移 + 面板字段
|
||||
# ======================================================================
|
||||
MIGRATION = ''' /**
|
||||
* 老版本硬塞进来的中转地址,要清掉。
|
||||
*
|
||||
* <p>1.5.0 那版把作者的域名写死成默认值:
|
||||
* <pre>
|
||||
* private static final String DEFAULT_RELAY_URL = "https://relay.xxmxxs.top";
|
||||
* </pre>
|
||||
* 而且第一次运行就存进了 SharedPreferences。装新 APK 不会清偏好,
|
||||
* 所以用户会看到「默认还是那个中转站」—— 其实他从来没选过。
|
||||
*
|
||||
* <p>现在默认值是空串(跨网络要自己填)。这里做一次性迁移:
|
||||
* 存的值如果正好是当时内置的那个域名,就当成没设过,清掉。
|
||||
*
|
||||
* <p>⚠ 只在**完全等于**内置值时清 —— 用户自己手填的地址不能动。
|
||||
*/
|
||||
private static final String[] LEGACY_BAKED_IN_RELAYS = {
|
||||
"https://relay.xxmxxs.top",
|
||||
"http://relay.xxmxxs.top",
|
||||
};
|
||||
|
||||
private void migrateLegacyRelay() {
|
||||
String saved = getSharedPreferences("lanremote", MODE_PRIVATE)
|
||||
.getString("relay_url", "");
|
||||
if (saved == null) {
|
||||
return;
|
||||
}
|
||||
String trimmed = saved.trim();
|
||||
for (String legacy : LEGACY_BAKED_IN_RELAYS) {
|
||||
if (legacy.equalsIgnoreCase(trimmed)) {
|
||||
// 这是老版本硬塞的,不是用户选的 —— 清掉,让他自己填
|
||||
getSharedPreferences("lanremote", MODE_PRIVATE).edit()
|
||||
.remove("relay_url").apply();
|
||||
appendLog("已清掉旧版本内置的中转地址(不是你自己填的)");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
'''
|
||||
|
||||
|
||||
SETTINGS_BUILD = ''' /**
|
||||
* 「设置」页。用户要求:"安卓端加一个设置,各种设置就在这里面调"。
|
||||
*
|
||||
* <p>以前设置散在首页(一个「中转设置」按钮)和别处,
|
||||
* 现在统一收到这里。
|
||||
*/
|
||||
private void buildSettingsPanel() {
|
||||
settingsPanel = new FrameLayout(this);
|
||||
settingsPanel.setBackgroundColor(C_BG);
|
||||
|
||||
ScrollView scroll = new ScrollView(this);
|
||||
scroll.setBackgroundColor(C_BG);
|
||||
LinearLayout box = new LinearLayout(this);
|
||||
box.setOrientation(LinearLayout.VERTICAL);
|
||||
box.setPadding(dp(10), dp(12), dp(10), dp(12));
|
||||
scroll.addView(box, new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
settingsPanel.addView(scroll, new FrameLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
|
||||
LinearLayout top = new LinearLayout(this);
|
||||
top.setOrientation(LinearLayout.HORIZONTAL);
|
||||
top.setGravity(Gravity.CENTER_VERTICAL);
|
||||
box.addView(top, new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
|
||||
TextView title = new TextView(this);
|
||||
title.setText("设置");
|
||||
title.setTextColor(C_FG);
|
||||
title.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);
|
||||
title.setTypeface(null, android.graphics.Typeface.BOLD);
|
||||
top.addView(title, new LinearLayout.LayoutParams(
|
||||
0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f));
|
||||
top.addView(smallButton("返回", C_CARD, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
hideSettingsPanel();
|
||||
}
|
||||
}));
|
||||
|
||||
// ================= 中转服务器 =================
|
||||
settingsRelayValue = new TextView(this);
|
||||
settingsRelayValue.setTextColor(C_DIM);
|
||||
settingsRelayValue.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
|
||||
settingsRelayValue.setPadding(0, dp(6), 0, dp(4));
|
||||
|
||||
box.addView(sectionTitle("中转服务器"));
|
||||
box.addView(hintText("手表和电脑不在同一个网络时(比如在办公室管教室)"
|
||||
+ "才需要填。填你自己那台的地址,比如 relay.你的域名。\\n"
|
||||
+ "只在一个 WiFi 里用就留空 —— 那样直接连,最快也不耗流量。"));
|
||||
box.addView(settingsRelayValue);
|
||||
|
||||
Button editRelay = bigButton("填写中转服务器", C_ACCENT, 14);
|
||||
editRelay.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
promptRelayUrl();
|
||||
}
|
||||
});
|
||||
box.addView(editRelay);
|
||||
|
||||
Button clearRelay = bigButton("清空中转服务器(只走局域网)", C_CARD, 13);
|
||||
clearRelay.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
setRelayUrl("");
|
||||
refreshSettingsPanel();
|
||||
toast("已清空 —— 以后只走局域网");
|
||||
}
|
||||
});
|
||||
box.addView(clearRelay);
|
||||
|
||||
// ================= 配对码 =================
|
||||
settingsCodeValue = new TextView(this);
|
||||
settingsCodeValue.setTextColor(C_DIM);
|
||||
settingsCodeValue.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
|
||||
settingsCodeValue.setPadding(0, dp(6), 0, dp(4));
|
||||
|
||||
box.addView(sectionTitle("记住的配对码"));
|
||||
box.addView(hintText("手表会记住最近一次用的配对码,"
|
||||
+ "下次连接自动带上,不用重复输。"));
|
||||
box.addView(settingsCodeValue);
|
||||
|
||||
Button clearCode = bigButton("清除记住的配对码", C_CARD, 13);
|
||||
clearCode.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
getSharedPreferences("lanremote", MODE_PRIVATE).edit()
|
||||
.remove("paired_code").apply();
|
||||
refreshSettingsPanel();
|
||||
toast("已清除");
|
||||
}
|
||||
});
|
||||
box.addView(clearCode);
|
||||
|
||||
// ================= 已配对设备 =================
|
||||
settingsDevicesValue = new TextView(this);
|
||||
settingsDevicesValue.setTextColor(C_DIM);
|
||||
settingsDevicesValue.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
|
||||
settingsDevicesValue.setPadding(0, dp(6), 0, dp(4));
|
||||
|
||||
box.addView(sectionTitle("已配对的电脑"));
|
||||
box.addView(hintText("配对过的电脑会出现在首页列表里,点一下就能连。"
|
||||
+ "换电脑或者想重新配对时可以清空。"));
|
||||
box.addView(settingsDevicesValue);
|
||||
|
||||
Button clearDevices = bigButton("清空已配对的电脑", C_DANGER, 13);
|
||||
clearDevices.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
new android.app.AlertDialog.Builder(MainActivity.this)
|
||||
.setTitle("清空已配对的电脑?")
|
||||
.setMessage("清掉之后首页列表就空了,"
|
||||
+ "需要重新输配对码连接。")
|
||||
.setPositiveButton("清空", new android.content.DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(android.content.DialogInterface d, int w) {
|
||||
store.clearDevices();
|
||||
refreshDeviceList();
|
||||
refreshSettingsPanel();
|
||||
toast("已清空");
|
||||
}
|
||||
})
|
||||
.setNegativeButton("取消", null)
|
||||
.show();
|
||||
}
|
||||
});
|
||||
box.addView(clearDevices);
|
||||
|
||||
// ================= 关于 =================
|
||||
box.addView(sectionTitle("关于"));
|
||||
box.addView(hintText("ykxmz 手表端 版本 " + appVersion()
|
||||
+ "\\n\\n"
|
||||
+ "连接顺序:先试局域网直连(同一个 WiFi,几毫秒);"
|
||||
+ "连不上再走中转(跨网络,受线路影响)。"));
|
||||
}
|
||||
|
||||
/** 一行小节标题 */
|
||||
private TextView sectionTitle(String text) {
|
||||
TextView v = new TextView(this);
|
||||
v.setText(text);
|
||||
v.setTextColor(C_FG);
|
||||
v.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
|
||||
v.setTypeface(null, android.graphics.Typeface.BOLD);
|
||||
v.setPadding(0, dp(14), 0, 0);
|
||||
return v;
|
||||
}
|
||||
|
||||
/** 一行灰色说明 */
|
||||
private TextView hintText(String text) {
|
||||
TextView v = new TextView(this);
|
||||
v.setText(text);
|
||||
v.setTextColor(C_DIM);
|
||||
v.setTextSize(TypedValue.COMPLEX_UNIT_SP, 11);
|
||||
v.setLineSpacing(0, 1.25f);
|
||||
v.setPadding(0, dp(4), 0, 0);
|
||||
return v;
|
||||
}
|
||||
|
||||
/** 版本号。从 PackageManager 读,和构建脚本里的版本天然一致,
|
||||
* 不用在代码里再写一份(写两份必然有一份会忘改)。 */
|
||||
private String appVersion() {
|
||||
try {
|
||||
return getPackageManager()
|
||||
.getPackageInfo(getPackageName(), 0).versionName;
|
||||
} catch (Exception e) {
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
|
||||
/** 把设置页上的文字刷新成当前值 */
|
||||
private void refreshSettingsPanel() {
|
||||
if (settingsRelayValue == null) {
|
||||
return;
|
||||
}
|
||||
String relay = relayUrl();
|
||||
settingsRelayValue.setText(relay.isEmpty()
|
||||
? "现在是:空(只走局域网)"
|
||||
: "现在是:" + relay);
|
||||
settingsRelayValue.setTextColor(relay.isEmpty() ? C_DIM : C_OK);
|
||||
|
||||
String code = savedPairingCode();
|
||||
settingsCodeValue.setText((code == null || code.isEmpty())
|
||||
? "现在没有记住的配对码"
|
||||
: "记住的是:" + code);
|
||||
|
||||
int n = store.devices() == null ? 0 : store.devices().size();
|
||||
settingsDevicesValue.setText("已配对 " + n + " 台电脑");
|
||||
}
|
||||
|
||||
private void showSettingsPanel() {
|
||||
refreshSettingsPanel();
|
||||
showOnly(settingsPanel);
|
||||
}
|
||||
|
||||
private void hideSettingsPanel() {
|
||||
showConnectPanel();
|
||||
}
|
||||
|
||||
'''
|
||||
|
||||
|
||||
def patch(text: str, old: str, new: str, label: str, results: list) -> str:
|
||||
if old not in text:
|
||||
results.append(("✗", label, "找不到锚点"))
|
||||
return text
|
||||
if text.count(old) != 1:
|
||||
results.append(("!", label, "锚点出现 %d 次" % text.count(old)))
|
||||
results.append(("✓", label, ""))
|
||||
return text.replace(old, new, 1)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
text = SRC.read_text(encoding="utf-8")
|
||||
results: list = []
|
||||
|
||||
# ---- 1. 面板字段声明 ----
|
||||
text = patch(
|
||||
text,
|
||||
" private FrameLayout lanPanel;",
|
||||
" private FrameLayout lanPanel;\n"
|
||||
" // ---- 设置页(用户要求:各种设置收在一个页面里调)----\n"
|
||||
" private FrameLayout settingsPanel;\n"
|
||||
" private TextView settingsRelayValue;\n"
|
||||
" private TextView settingsCodeValue;\n"
|
||||
" private TextView settingsDevicesValue;",
|
||||
"声明 settingsPanel 和各显示控件", results)
|
||||
|
||||
# ---- 2. 插入迁移函数(放在 relayUrl 之前)----
|
||||
text = patch(
|
||||
text, " private String relayUrl() {",
|
||||
MIGRATION + " private String relayUrl() {",
|
||||
"插入 migrateLegacyRelay()", results)
|
||||
|
||||
# ---- 3. buildSettingsPanel 等方法(插在 buildLanPanel 之前)----
|
||||
text = patch(
|
||||
text, " private void buildLanPanel() {",
|
||||
SETTINGS_BUILD + " private void buildLanPanel() {",
|
||||
"插入 buildSettingsPanel() 等", results)
|
||||
|
||||
# ---- 4. 建面板的地方也要建它 ----
|
||||
text = patch(
|
||||
text, " buildLanPanel();",
|
||||
" buildLanPanel();\n buildSettingsPanel();",
|
||||
"构建时创建 settingsPanel", results)
|
||||
|
||||
# ---- 5. showOnly 纳入 settingsPanel ----
|
||||
text = patch(
|
||||
text,
|
||||
" View[] all = {connectPanel, controlPanel, mirrorPanel, lanPanel};",
|
||||
" View[] all = {connectPanel, controlPanel, mirrorPanel, lanPanel,\n"
|
||||
" settingsPanel};",
|
||||
"settingsPanel 纳入 showOnly()", results)
|
||||
|
||||
# ---- 6. 首页那个按钮改成「设置」 ----
|
||||
text = patch(
|
||||
text,
|
||||
''' Button relayButton = bigButton("中转设置(跨网络用)", C_ACCENT, 14);''',
|
||||
''' Button relayButton = bigButton("设置", C_ACCENT, 14);''',
|
||||
"首页按钮文案改成「设置」", results)
|
||||
|
||||
text = patch(
|
||||
text,
|
||||
''' public void onClick(View view) {
|
||||
promptRelayUrl();
|
||||
}
|
||||
});
|
||||
box.addView(relayButton);''',
|
||||
''' public void onClick(View view) {
|
||||
showSettingsPanel();
|
||||
}
|
||||
});
|
||||
box.addView(relayButton);''',
|
||||
"首页按钮改成打开设置页", results)
|
||||
|
||||
# ---- 7. 启动时做迁移 ----
|
||||
# 找 onCreate 里合适的位置:appendLog 第一次出现之前太早,
|
||||
# 用 "store = " 初始化之后
|
||||
import re
|
||||
m = re.search(r"( store = new DeviceStore\(this\);\n)", text)
|
||||
if m:
|
||||
text = (text[:m.end()]
|
||||
+ " // 老版本硬塞的中转地址要清掉(见 migrateLegacyRelay)\n"
|
||||
" migrateLegacyRelay();\n"
|
||||
+ text[m.end():])
|
||||
results.append(("✓", "启动时调用迁移", ""))
|
||||
else:
|
||||
results.append(("✗", "启动时调用迁移", "找不到 store 初始化那行"))
|
||||
|
||||
# ---- 输出 ----
|
||||
print(" ── 补丁结果 ──")
|
||||
for mark, label, note in results:
|
||||
print(" %s %s%s" % (mark, label, (" —— " + note) if note else ""))
|
||||
|
||||
if any(m == "✗" for m, _l, _n in results):
|
||||
print()
|
||||
print(" 有锚点没匹配,没有写回")
|
||||
return 1
|
||||
|
||||
if not any(m == "✗" for m, _l, _n in results):
|
||||
SRC.write_text(text, encoding="utf-8", newline="\n")
|
||||
print()
|
||||
print(" 已写回 MainActivity.java")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,123 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
r'''把某个版本的成品和官网代码传到中转服务器。
|
||||
|
||||
用法:
|
||||
python _build/deploy_release.py 1.9.0
|
||||
python _build/deploy_release.py 1.9.0 --code-only # 只更新官网代码
|
||||
python _build/deploy_release.py 1.9.0 --files-only # 只传成品文件
|
||||
|
||||
== 为什么写成脚本文件而不是 python -c ==
|
||||
|
||||
这一轮我在 PowerShell 里用 `python -c @"..."@` 写带引号和 $ 的
|
||||
Python 代码,失败了四五次(PowerShell 会插值、会吃掉 $、嵌套引号
|
||||
转义一层套一层)。凡是超过两行的 Python,一律写成 .py 文件再跑。
|
||||
|
||||
== 官网上"最新版"是怎么来的 ==
|
||||
|
||||
relay.py 的 _list_downloads() 按 **mtime 倒序** 返回文件,
|
||||
landing.py 的 _version() 取**第一个**能匹配到 x.y.z 的文件名。
|
||||
|
||||
所以"上传哪个版本,官网就显示哪个版本" —— 不用改任何代码。
|
||||
按顺序传 1.9.0、再传 2.0.0,官网就会先显示 1.9.0、再显示 2.0.0。
|
||||
'''
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(ROOT / "_build"))
|
||||
|
||||
from devssh import upload, run # noqa: E402
|
||||
|
||||
REMOTE_DIR = "/root/lanremote-relay"
|
||||
DL_DIR = REMOTE_DIR + "/downloads"
|
||||
|
||||
# 要传的成品(和 dist/ 里的文件名一致)
|
||||
ARTIFACTS = [
|
||||
"ykxmz-setup-{v}.exe",
|
||||
"ykxmz-watch-{v}.apk",
|
||||
"ykxmz-watch-{v}-说明.txt",
|
||||
"ykxmz-server-{v}.zip",
|
||||
]
|
||||
|
||||
# 官网/服务端代码
|
||||
CODE = ["relay.py", "admin.py", "landing.py", "showcase.py"]
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = [a for a in sys.argv[1:] if a]
|
||||
if not args:
|
||||
print(__doc__)
|
||||
return 1
|
||||
version = args[0]
|
||||
code_only = "--code-only" in args
|
||||
files_only = "--files-only" in args
|
||||
|
||||
print("=" * 62)
|
||||
print(" 部署 %s 到中转服务器" % version)
|
||||
print("=" * 62)
|
||||
|
||||
# ---------------- 成品 ----------------
|
||||
if not code_only:
|
||||
print()
|
||||
print(" ── 1. 上传成品到 downloads/ ──")
|
||||
for tpl in ARTIFACTS:
|
||||
name = tpl.format(v=version)
|
||||
local = ROOT / "dist" / name
|
||||
if not local.is_file():
|
||||
print(" [跳过] %s 不存在" % name)
|
||||
continue
|
||||
upload(str(local), "%s/%s" % (DL_DIR, name))
|
||||
print(" ✓ %-34s %10d 字节" % (name, local.stat().st_size))
|
||||
|
||||
# ---------------- 代码 ----------------
|
||||
if not files_only:
|
||||
print()
|
||||
print(" ── 2. 更新官网/服务端代码 ──")
|
||||
for name in CODE:
|
||||
local = ROOT / "relay" / name
|
||||
if not local.is_file():
|
||||
print(" [跳过] %s 不存在" % name)
|
||||
continue
|
||||
upload(str(local), "%s/%s" % (REMOTE_DIR, name))
|
||||
print(" ✓ %s" % name)
|
||||
|
||||
print()
|
||||
print(" ── 3. 语法检查 + 重启 ──")
|
||||
out = run(
|
||||
"cd %s && python3 - <<'PY'\n"
|
||||
"import ast\n"
|
||||
"for f in %r:\n"
|
||||
" ast.parse(open(f, encoding='utf-8').read())\n"
|
||||
"print('语法 OK')\n"
|
||||
"PY" % (REMOTE_DIR, tuple(CODE)),
|
||||
timeout=90)
|
||||
print(" " + out[1].strip().splitlines()[-1])
|
||||
|
||||
out = run("systemctl restart lanremote-relay && sleep 4 && "
|
||||
"systemctl is-active lanremote-relay && "
|
||||
"grep -m1 '^VERSION' %s/relay.py" % REMOTE_DIR, timeout=120)
|
||||
for ln in out[1].strip().splitlines():
|
||||
print(" " + ln)
|
||||
|
||||
# ---------------- 复核 ----------------
|
||||
print()
|
||||
print(" ── 4. 复核:官网现在显示哪个版本 ──")
|
||||
out = run(
|
||||
"curl -s -H 'Cache-Control: no-cache' http://127.0.0.1:8080/v1/downloads",
|
||||
timeout=60)
|
||||
print(" " + out[1].strip()[:400])
|
||||
|
||||
out = run("curl -s -H 'Cache-Control: no-cache' http://127.0.0.1:8080/ "
|
||||
"| grep -o '最新版 [0-9.]*' | head -1", timeout=60)
|
||||
print(" 官网首页角标: " + out[1].strip())
|
||||
|
||||
print()
|
||||
print(" 提示:官网 / 走 max-age=60,公网可能需要等一分钟才刷新;")
|
||||
print(" 下载链接带 604800 缓存,换版本号正好会换 URL。")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -18,12 +18,34 @@ r'''修 license-combined.txt 里那份**没过期的**协议版本号。
|
||||
'''
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
FILE = ROOT / "installer" / "license-combined.txt"
|
||||
NEW = "1.9.0"
|
||||
|
||||
|
||||
def current_version() -> str:
|
||||
"""从**单一来源**读版本号。
|
||||
|
||||
⚠ 这里原来是写死的 NEW = "1.9.0"。升到 2.0.0 那次它把已经
|
||||
改好的 license 又**改回了 1.9.0** —— 因为整个升级流程是:
|
||||
bump_version_generic.py 改一批
|
||||
fix_license_version.py 再"修"一遍 <- 用写死的旧版本覆盖
|
||||
写死的常量就是这样:改版本号时忘了改这里,它就成了回退操作。
|
||||
|
||||
现在统一从 _build/package_remote.py 的 VERSION 读 ——
|
||||
那个是打包脚本用的版本号,本来就是单一来源。
|
||||
"""
|
||||
text = (ROOT / "_build" / "package_remote.py").read_text(encoding="utf-8")
|
||||
m = re.search(r'VERSION\s*=\s*"([^"]+)"', text)
|
||||
if not m:
|
||||
raise SystemExit(" ✗ 从 package_remote.py 里读不到 VERSION")
|
||||
return m.group(1)
|
||||
|
||||
|
||||
NEW = current_version()
|
||||
|
||||
|
||||
def main() -> int:
|
||||
|
||||
@@ -17,7 +17,7 @@ import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote")
|
||||
VERSION = "1.9.0"
|
||||
VERSION = "2.0.0"
|
||||
NAME = f"ykxmz-{VERSION}"
|
||||
|
||||
AGENT_SRC = ROOT / "dist_exe" / "ykxmz"
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user