Repository navigation
Releases: MacWaveOrg/MacWave
Release list
MacWave 3.0.2
MacWave 3.0.2
Build number: 283F2358
新增
--ver增加新用法:现在wave install jq --ver 1.0与wave install jq --ver=1.0都有效
(此前--ver写在帮助里却不可用:不在参数白名单内,任何用法都会返回Input contains illegal fields.)。
同时--flag=value对长参数统一生效,--limit-rate=300K、--proxy=...不再被读成=300K。
安全加固
- 名字与版本号白名单:包名、版本号、远端
@common里的bin_name、依赖引用,一律只允许
[A-Za-z0-9][A-Za-z0-9._+-]*。含/ \ % @ :、空白、..或以-/.开头一律拒绝——
它们在进入 URL 或文件路径之前就被挡下,而不是拼接进去再说。 - 最终路径必须落在安装目录内:所有会被创建、改名、删除的路径都会先归一化并校验是否位于
BASE_DIR(bin/、links/、deps/、downloads/tmp)之内,防路径穿越与软链接逃逸。 - shell 侧再兜一层:
surfboard/depsmanager.sh会独立复核目标路径是否在BASE_DIR内,
即使有调用方绕过 Python 校验也写不出去。
CI
format-test现在跑 Python 3.14 + 3.15 两条矩阵线(3.15 由 Homebrew 提供,因为
actions/python-versions还没有 3.15.0 正式版)。- 新增 随机化端到端回归 job:每次随机抽 10–20 个包(必含
wget,>40MB 重抽),
随机指定/不指定版本号、随机长/短参数、10% 概率插入非法参数、1–2 个不存在的版本、
1–2 个非法版本;-C随机打断 0–3 次、--skip-ssl、--limit-rate(瞬时 + 全程速度复核)、
5% 概率在 Mach-O 重定向中途打断;list/search/info/selfupdate/link/linkquery/unlink各 5 次;
5 个非法命令与-h/--help/-V/--version(含非法参数);每次随机挑一个安装目录(33% 为~/test dir)。 - 运行时在拿到
GH_TOKEN/GITHUB_TOKEN时会给api.github.com带上鉴权,缓解未认证
60 次/小时的限流(token 只发给 api.github.com)。
不设
--latest的CI-Update20261010为独立的 CI 变更说明,本 Release 才是 3.0.2 版本发布。
附件
| 附件 | 说明 |
|---|---|
| 30 个源码文件 | 3.0.2 的全部源码,逐个单独上传(清单见下) |
format-test.yml |
更新后的 CI 工作流源码 |
MacWave-3.0.2.tar / MacWave-3.0.2.tar.gz |
3.0.1 以来改动的全部文件(运行时 + CI),保持仓库相对路径,解到仓库根目录即可 |
源码文件按 2.5 的附件格式平铺上传(附件名即文件名,不含目录),对照见下表:
| 来源目录 | 文件 |
|---|---|
lib/ |
wave.py help.py configerror.py configpaths.py selfupdate.py selfupdate.sh install.sh uninstall.sh |
pkg/ |
pkginstaller.py pkginstaller.sh pkginfohelper.py uninstaller.py pkgversionparser.py pkgunzip.sh linker.py |
scripts/ |
audit_deps.py random_test.py configpath_test.sh deps_test.sh format_test.sh link_test.sh sandbox_test.sh selfupdate_test.sh |
surfboard/ |
depsinstaller.py depsinstaller.sh depsmanager.sh depsversionparser.py querier.py tagger.sh transfer.sh |
八个 scripts/*_test.sh 与 scripts/audit_deps.py 是仓库自身的测试/审计脚本,2.5 起就一并附在 Release 里;
.Pseudocode/pkg/pkginstaller.py 是按设计不可执行的伪代码,未附上。
两个归档内容一致;打包时已归一化 mtime 与属主,因此同内容每次打出相同 SHA256:
MacWave-3.0.2.tar = 99795b99…,MacWave-3.0.2.tar.gz = 02d2dd3f…。
同步
- README(中英文)已标注 3.0.2;官网版本号已同步。
configdata的versiondata/latest_version已改为3.0.2/2026-10-10/283F2358,
并补上updatedata/3.0.2/(install.sh与wave selfupdate按「正在安装的完整版本号」查迁移数据,
补丁版本要各放一份),所以安装脚本与wave selfupdate现在拉取的就是 3.0.2。
CI-Update20261010
CI-Update20261010
本次只改 CI 与测试脚本,不改任何 MacWave 运行时逻辑。已同时合入 main 与 3.0(两个分支指向同一提交)。
一、format-test 工作流的变化
文件:.github/workflows/format-test.yml
-
新增 Python 3.15 矩阵:原来
python-version固定为'3.14',现在改为矩阵strategy: fail-fast: false matrix: python-version: ['3.14', '3.15']
3.14 是环境下限(
.conda包依赖 3.14 的compression.zstd),3.15 是上游最新正式版(2026-10-09 发布)。
fail-fast: false保证一条线挂掉不会取消另一条,便于判断是哪个版本的问题。
原有的configpath_test.sh/audit_deps.py/format_test.sh/deps_test.sh/link_test.sh/selfupdate_test.sh全部保留、不动。 -
新增
random-testjob:跑全新的随机化端到端回归,自带安装目录挑选、部署与清理,不需要sudo /opt/macwave_config。
二、新 CI:随机化端到端回归
覆盖内容
| 项目 | 规则 |
|---|---|
| 随机软件包 | 从 infosource 的 pkginfo_<arch> 抽 10~20 个,必含 wget;产物体积 > 40MB 的包重抽(HEAD 探 Content-Length);test_* 测试夹具默认排除。逐个 install → 校验 → uninstall。 |
| 版本号指定 | 指定 / 不指定各约一半,但两组都必须落在 [5, 15] 内,否则重新分配。校验含:安装目录、不带版本号的软链接、SHA256 通过、二进制可执行(dyld 未解析即失败)、wave list 可见。 |
| 版本号异常 | 指定版本号的包里,1~2 个不存在的版本、1~2 个非法版本(not-a-version、1.0.0-!!!、..%2f..%2fetc%2fpasswd、v1.2.3@beta),其余正常。这些 token 同样喂给 install / info / search。 |
-C 断点续传 |
同一次下载随机打断 0~3 次,每轮都断言 .partial 从未回退(即真的续传而非重下)、进度条有渲染;结尾断言完整装好。 |
--skip-ssl |
断言出现 You selected --skip-ssl 安全提示,且回答 y / n 都能正常下载(n 时仍做 SHA256 校验)。 |
--limit-rate |
全程采样 .partial 增长算瞬时速率(默认 400K,容差 ×1.5),结尾再用 体积/时间 复核全程平均速度(容差 ×1.25)。 |
| Mach-O 重定向中断 | 普通安装有 5% 概率在 surfboard/transfer.sh 运行期间发 SIGINT;断言进程干净退出(无栈回溯),并断言再次 install 能收敛到完整正确;打断后的半成品状态记为 WARN(现状会留下半个 bin/ 目录且对 wave list 可见)。 |
| 固定命令覆盖(必测) | list / search / info / selfupdate / link / linkquery / unlink 各 5 次;install / uninstall 由随机包循环覆盖。 |
| 非法输入 | 5 个非法命令;裸 -h / --help / -V / --version 以及各自带非法参数的形式;任何命令都有 10% 概率被插入非法参数。 |
| 参数形态 | 等效参数长/短形式随机(-h/--help、-V/--version、-a/--all、-C/--continue、-v/--verbose)。 |
| 随机安装目录 | 每次随机挑一种方案,其中 33% 概率是自定义方案 ~/test dir(顺带压空格路径);另有随机 HOME(配置目录一起随机)、中文/深层路径等方案。跑完只清理自己创建的东西。 |
用法
python3 scripts/random_test.py --infosource infosource-data调试 / 复现用参数(都已实现):
--seed N 固定随机种子,复现某次失败
--count N 覆盖 10~20 的包数
--packages a,b,c 指定包名
--dry-run 只打印计划,不执行任何 wave 命令
--no-size-probe 跳过 HEAD 探体积(离线自测)
--include-test-packages 把 test_* 也放进随机池
--config-dir DIR 覆盖配置目录
--limit-rate 400K 限速测试用的速率
--transfer-interrupt-probability 覆盖 5% 的重定向中断概率(调试可设 1)
--keep 跑完保留安装目录
安全约束
脚本只清理本次运行亲手创建的目录。已存在的配置目录(可能是一份真实安装)或已存在的非空安装目录会被直接拒绝而不是覆盖——这条是硬性要求。
已知限制
search/info/ 不带版本的install会访问api.github.com。未认证只有 60 次/小时,而 Runner 共用出口 IP,因此可能被限流;被判定为限流的用例记为 SKIP(不计失败)而不是伪装成产品 bug。若要彻底消除,需要让运行时代码(pkg/pkginfohelper.py、pkg/pkginstaller.py)像scripts/audit_deps.py那样在有GH_TOKEN时带上Authorization头,并在 CI 注入secrets.GITHUB_TOKEN——本次未改动运行时代码。- 依赖公网(GitHub Releases / conda.anaconda.org / raw.githubusercontent.com)。
- 每个矩阵线最多 20 个包、单包上限 40MB,最短也可能跑到几十分钟;job 设了
timeout-minutes: 150。 selfupdate在这条流程里走「已是最新」的短路分支(VERSION.json写成 configdata 的当前版本),真实自更新仍由scripts/selfupdate_test.sh覆盖。
三、产物
format-test.yml— 更新后的 CI 工作流源码random_test.py— 新增的随机化回归脚本源码
本 Release 未设为 latest,MacWave 自身的「最新版本」仍由
MacWaveOrg/configdata的versiondata/latest_version决定。
四、整合打包(附件)
除了两个单独的源码文件,本次 Release 还附上了把全部新 CI 整合在一起的两种归档:
| 附件 | 大小 | SHA256 |
|---|---|---|
ci-update20261010.tar |
61,440 字节 | 33e921d2cbba5daccb23f6d41dc25cc5964a2120422d573da886932c36b785fe |
ci-update20261010.tar.gz |
17,345 字节 | f64cc01e7361335983f82154d062d68e079dc8a4285faa61ecae085e317f67bb |
归档内保持仓库内的相对路径,所以解到仓库根目录就能直接落位:
tar -xf ci-update20261010.tar -C /path/to/MacWave
tar -xzf ci-update20261010.tar.gz -C /path/to/MacWave包含:
.github/workflows/format-test.ymlscripts/random_test.py
两种格式内容完全一致;打包时已把 mtime 与属主归一化(mtime=0、uid/gid=0、gzip -n),所以同一份输入每次打出的 SHA256 相同,便于校验。
MacWave 3.0.1
Release Note 更新日志
本次为兼容更新:3.0 装过的用户可以直接 wave selfupdate 升到 3.0.1,不需要重装。
新功能:--dir-option=<路径> 可以省略菜单编号
以前要指定自定义安装目录,必须写成 --dir-option=<编号>=<路径>,其中编号是「other」那一项在目录菜单里的序号(arm64 是 3,Intel 是 4):
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/MacWaveOrg/MacWave/HEAD/lib/install.sh)" -- --silent --dir-option=3=/opt/my-macwave
3.0.1 起,编号可以直接省略,把路径写在等号后面即可:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/MacWaveOrg/MacWave/HEAD/lib/install.sh)" -- --silent --dir-option=/opt/my-macwave
对全部三种写法等价:
| 写法 | 含义 |
|---|---|
--dir-option=N |
静默选目录菜单第 N 项 |
--dir-option=DIR |
自定义目录 DIR(= --dir-option=<自定义项>=DIR) |
--dir-option=N=DIR |
选第 N 项;N 为自定义项时用 DIR 作为安装目录 |
这是纯语法糖,旧的 --dir-option=N=DIR 完全保留,脚本与 CI 都能继续用。
细节
- 只有当
=左边是纯数字时才按N=DIR拆分;所以--dir-option=/opt/a=b这种路径本身带=的写法不会被误拆,整条按路径处理。 - 路径仍然支持
~(会展开成$HOME)与空格(用引号括起来即可)。 - 编号越界、给非自定义项传路径等错误情况,报错信息与之前一致。
这个改动装得上吗
--dir-option 属于安装引导脚本(install.sh / selfupdate.sh,直接从仓库拉取运行),不在文件清单里、不会被安装到本地。所以:
- 已经装好 3.0 的用户:
wave selfupdate可以直接升到 3.0.1(同大版本,守卫放行)。本次功能对已装用户无影响,想用新写法时重跑一次上面的install.sh命令即可。 - 全新安装:直接用新写法即可。
升级方式
wave selfupdate
或重新安装(不是必须):
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/MacWaveOrg/MacWave/HEAD/lib/install.sh)"
其他改动
lib/install.sh:--dir-option解析与帮助文本更新(见上)。scripts/sandbox_test.sh:新增两个用例覆盖简写与含空格路径。README.md/README.zh-Hans.md:--dir-option文档更新为「编号可省略」。- 官网
macwave.org版本信息同步到3.0.1 / 2026-10-09。 MacWaveOrg/pkgtest的 README 标题从旧的MacWavePkgTest改为MacWave pkgtest(仅文档,仓库地址不变)。
Release Notes
Compatible update: users already on 3.0 can upgrade with wave selfupdate — no reinstall needed.
New: --dir-option=<path> no longer needs the menu number
Previously, a custom install directory required --dir-option=<number>=<path>, where the number was the "other" entry in the directory menu (3 on arm64, 4 on Intel):
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/MacWaveOrg/MacWave/HEAD/lib/install.sh)" -- --silent --dir-option=3=/opt/my-macwave
From 3.0.1 the number may be omitted and the path written directly after the =:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/MacWaveOrg/MacWave/HEAD/lib/install.sh)" -- --silent --dir-option=/opt/my-macwave
The three forms are equivalent:
| Form | Meaning |
|---|---|
--dir-option=N |
Silently pick entry N of the directory menu |
--dir-option=DIR |
Custom directory DIR (= --dir-option=<custom entry>=DIR) |
--dir-option=N=DIR |
Pick entry N; use DIR when N is the custom entry |
This is pure sugar — the older --dir-option=N=DIR form is kept as is, so existing scripts and CI keep working.
Details
- An
=is only treated as theN=DIRseparator when the left side is all digits, so a path that itself contains=, e.g.--dir-option=/opt/a=b, is not split and is taken as the path. - Paths still support
~(expanded to$HOME) and spaces (quote them). - Error messages for out-of-range numbers or a path passed with a non-custom entry are unchanged.
Does this change reach installed users?
--dir-option lives in the bootstrap scripts (install.sh / selfupdate.sh), which are fetched from the repository and run directly — they are not in the file list and are never installed locally. Therefore:
- Existing 3.0 users:
wave selfupdateupgrades you to 3.0.1 (same major version, guard allows it). This feature does not affect an existing install; re-run theinstall.shcommand above if you want to use the new form. - Fresh installs: use the new form right away.
How to upgrade
wave selfupdate
Or reinstall (not required):
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/MacWaveOrg/MacWave/HEAD/lib/install.sh)"
Other changes
lib/install.sh:--dir-optionparsing and help text updated (see above).scripts/sandbox_test.sh: two new cases covering the shorthand and a path with a space.README.md/README.zh-Hans.md:--dir-optiondocs updated to "the number may be omitted".- Website
macwave.orgversion info synced to3.0.1 / 2026-10-09. MacWaveOrg/pkgtestREADME heading renamed from the oldMacWavePkgTesttoMacWave pkgtest(docs only; the repository URL is unchanged).
Index Update-2026101001
Index Update 索引更新
Add packages: tree
新增软件包:tree
由于wave install会自动拉取最新数据,你无需手动更新
Because of wave install automatically pulls the latest data, you don't need to update manually.
Index Update-2026100901
Index Update 索引更新
Add packages: yq
新增软件包:yq
由于wave install会自动拉取最新数据,你无需手动更新
Because of wave install automatically pulls the latest data, you don't need to update manually.
MacWave 3.0
Release Note 更新日志
本次为不兼容更新:包与版本数据从「代码仓库的分支」迁出到独立仓库,依赖数据目录由 surfboard/ 改名为 deps/;并且 3.0 不能通过 wave selfupdate 升级,必须用 install.sh 重新安装。
本次是不兼容更新
3.0 之前,包元数据与版本数据都放在代码仓库 MacWaveOrg/MacWave 的分支里(infosource / configdata),依赖数据则放在该分支的 surfboard/ 目录下。
3.0 起:
- 数据迁到各自的独立仓库,路径与引用全部改变;
- 依赖数据的目录名
surfboard/→deps/; - 官网站点从代码仓库的
gh-pages分支迁到独立仓库。
旧版本(≤ 2.6.1)读的仍是旧地址。 旧分支目前还在,但已冻结、不再更新——老版本继续能用,只是拿不到 3.0 之后的内容。要拿到 3.0,请按下文用 install.sh 重装。
不能通过 selfupdate 升级到 3.0
3.0 的 selfupdate.sh 增加了跨大版本守卫:当目标版本与当前已装版本的大版本号不同时,直接拒绝并提示改用 install.sh。
| 当前版本 | 目标 | 行为 |
|---|---|---|
2.x(任意) |
3.0 |
拒绝,提示改用 install.sh ⛔ |
3.0 |
3.0 |
已是最新,短路退出 |
3.0 |
3.x |
正常自更新 ✅ |
这是一条通用规则,不是 3.0 专属:每个大版本的第一个版本、以及任何跨大版本升级,都不能走 selfupdate(这类升级通常同时改动仓库与目录结构)。守卫写在 selfupdate.sh 里、随目标分支下发,因此以后每个大版本都自动生效,无需逐版写死在数据里。
仓库 / 分支迁移前后地址
| 内容 | 迁移前 | 迁移后 |
|---|---|---|
| 代码仓库 | MacWaveOrg/MacWave |
不变(仍是 MacWaveOrg/MacWave;main 与 3.0 同步) |
| 包元数据(描述 / 下载地址 / 校验值) | MacWave 仓库的 infosource 分支raw.githubusercontent.com/MacWaveOrg/MacWave/infosource/pkg/… |
MacWaveOrg/infosource 仓库的 main 分支raw.githubusercontent.com/MacWaveOrg/infosource/main/pkg/… |
| 依赖数据 | 同一分支的 surfboard/ 目录…/MacWaveOrg/MacWave/infosource/surfboard/depsinfo_{arch}/… |
新仓库、目录改名 deps/…/MacWaveOrg/infosource/main/deps/depsinfo_{arch}/… |
| 包列表 API | api.github.com/repos/MacWaveOrg/MacWave/contents/pkg/pkginfo_{arch}?ref=infosource |
api.github.com/repos/MacWaveOrg/infosource/contents/pkg/pkginfo_{arch}?ref=main |
| 版本 / 迁移数据 | MacWave 仓库的 configdata 分支raw.githubusercontent.com/MacWaveOrg/MacWave/configdata/… |
MacWaveOrg/configdata 仓库的 main 分支raw.githubusercontent.com/MacWaveOrg/configdata/main/… |
| 官网站点 | MacWave 仓库的 gh-pages 分支 |
MacWaveOrg/Pages 仓库的 main 分支(自定义域仍是 macwave.org) |
| 测试包(格式测试用) | MacWaveOrg/MacWavePkgTest |
MacWaveOrg/pkgtest(旧名 301 重定向) |
| 迁移前的历史归档 | — | 新增 MacWaveOrg/legacy-MacWave:迁移前的完整镜像,已归档只读,保留全部提交历史 |
为什么要发一版
这次迁移改动的是数据来源,而不是功能,但必须发一版才能把「怎么升级」送达用户:
因为 3.0 无法用 wave selfupdate 到达(见上),release 说明本身就是升级通知——存量用户需要知道要改用 install.sh 重装,而不是等一个永远不会到来的自更新。
升级方式
重新安装(推荐):
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/MacWaveOrg/MacWave/HEAD/lib/install.sh)"
安装脚本现在从 configdata 读取版本号与代码来源分支(不再写死在脚本里),因此它会自动安装 configdata 当前公布的版本(本次为 3.0)。
其他改动
lib/install.sh:MACWAVE_VERSION与代码来源分支改为从configdata的versiondata/latest_version读取(version/branch一起读,保证「下载的代码」与「写进VERSION.json的版本号」对应同一版本)lib/selfupdate.sh:新增跨大版本守卫(见上)- 新增工作流
Sync main with the release branch:main与「发布分支」自动互相检测并快进同步;发布分支由configdata的branch字段决定,分叉时直接报错交人工处理,历史分支(2.3/2.4/2.5)不会被触碰 configdata:versiondata/latest_version更新为3.0(构建号281D2012,branch: "3.0"),并新增updatedata/3.0迁移副本(沿用自 2.5 起的惯例)- 官网与 README 显示的版本号同步为
3.0 - 站点迁移后
macwave.org一度返回 404(新仓库尚未构建),已通过触发构建修复
验证方式
- 回归套件:隔离沙箱 64 条断言全部通过(
64 PASS / 0 FAIL / 0 SKIP),其中包含「VERSION.json版本与 configdata 一致」的断言 - 跨大版本守卫实测:在一次性沙箱 HOME 里以已装
2.6.1、目标3.0运行 → 被拒绝(退出码 1),VERSION.json未改动、未写入任何文件;同大版本(3.0→3.1)不被误拦 - 站点实测:
https://macwave.org/返回200,页面显示Latest Version : 3.0 - 双向同步实测:只推
main→3.0自动快进;只推3.0→main自动快进;分叉场景报错且两边均不动(本地模拟验证)
Release Notes
This is a breaking update: package and version data moved out of the code repository's branches into dedicated repositories, the dependency data folder was renamed surfboard/ → deps/, and 3.0 cannot be reached with wave selfupdate — it must be installed with install.sh.
This is a breaking update
Before 3.0, the package metadata and the version data both lived in branches of the code
repository MacWaveOrg/MacWave (infosource / configdata), with the dependency data
under a surfboard/ directory on that branch.
As of 3.0:
- the data moved into dedicated repositories, changing every path and reference;
- the dependency data folder was renamed
surfboard/→deps/; - the website moved from the code repository's
gh-pagesbranch into its own repository.
Older versions (<= 2.6.1) still read the old addresses. Those branches are still
present but frozen — old installs keep working, they simply stop receiving anything
published after 3.0. To get 3.0, reinstall with install.sh as described below.
You cannot update to 3.0 with selfupdate
selfupdate.sh now carries a cross-major guard: when the target version's major
number differs from the installed one, it refuses outright and points at install.sh.
| Installed | Target | Behaviour |
|---|---|---|
2.x (any) |
3.0 |
refused, points at install.sh ⛔ |
3.0 |
3.0 |
already up to date, exits early |
3.0 |
3.x |
self-update runs ✅ |
This is a general rule, not a 3.0 special case: the first release of every major
version — and any cross-major upgrade — cannot go through selfupdate (such upgrades
usually change both repositories and directory layout). The guard lives in
selfupdate.sh and is delivered by the target branch, so every future major gets it
automatically instead of it being hardcoded per release.
Repository / branch addresses before and after
| What | Before | After |
|---|---|---|
| Code repository | MacWaveOrg/MacWave |
unchanged (still MacWaveOrg/MacWave; main and 3.0 kept in sync) |
| Package metadata (description / download url / checksum) | infosource branch of MacWaveraw.githubusercontent.com/MacWaveOrg/MacWave/infosource/pkg/… |
main branch of MacWaveOrg/infosourceraw.githubusercontent.com/MacWaveOrg/infosource/main/pkg/… |
| Dependency data | surfboard/ on that same branch…/MacWaveOrg/MacWave/infosource/surfboard/depsinfo_{arch}/… |
new repository, folder renamed deps/…/MacWaveOrg/infosource/main/deps/depsinfo_{arch}/… |
| Package listing API | api.github.com/repos/MacWaveOrg/MacWave/contents/pkg/pkginfo_{arch}?ref=infosource |
api.github.com/repos/MacWaveOrg/infosource/contents/pkg/pkginfo_{arch}?ref=main |
| Version / migration data | configdata branch of MacWaveraw.githubusercontent.com/MacWaveOrg/MacWave/configdata/… |
main branch of MacWaveOrg/configdataraw.githubusercontent.com/MacWaveOrg/configdata/main/… |
| Website | gh-pages branch of MacWave |
main branch of MacWaveOrg/Pages (custom domain is still macwave.org) |
| Test packages (format tests) | MacWaveOrg/MacWavePkgTest |
MacWaveOrg/pkgtest (old name 301-redirects) |
| Pre-migration archive | — | new MacWaveOrg/legacy-MacWave: a full mirror of the repository as it was, archived read-only, all commit history kept |
Why cut a release at all
This migration changed where the data comes from, not what it does, yet a release is
the only way to deliver the "how to upgrade" message:
because 3.0 cannot be reached with wave selfupdate (see above), these release notes
are themselves the upgrade notice — existing users need to know they must reinstall
with install.sh rather than wait for a self-update that will never arrive.
How to upgrade
Reinstall (recommended):
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/MacWaveOrg/MacWave/HEAD/lib/install.sh)"
The installer now reads the version and the code source branch from configdata
instead of hardcoding them, so it installs whatever version configdata currently
publishes (3.0 as of this release).
Other changes
lib/install.sh:MACWAVE_VERSIONand the code source branch are now read from
configdata'sversiondata/latest_version(version/branchare read together so
the code downloaded and the version recorded inVERSION.jsonalways match)lib/selfupdate.sh: the cross-major guard described above- new workflow
Sync main with the release branch:mainand the release branch
detect each other and fast-forward automatically; the release branch comes from
configdata'sbranchfield, divergence fails the run for a human, and historical
branches (2.3/2.4/2.5) are never touched configdata:versiondata/latest_versionbumped to3.0(build281D2012,
branch: "3.0") plus a copy of the 2.5 config migration atupdatedata/3.0, following
the convention kept since 2.5- the version shown on the site and in the READMEs is now
3.0 - after the site move,
macwave.orgbriefly returned 404 (the new repository had never
been built); fixed by triggering a build
How it was verified
- Regression suite: all 64 assertions pass in an isolated sandbox
(64 PASS / 0 FAIL / 0 SKIP), including the check thatVERSION.jsonmatches
configdata - Cross-major guard, exercised for real: in a throwaway sandbox HOME, installed
2.6.1upgrading to3.0is refused (exit code 1) withVERSION.jsonuntouched
and no file written; a same-major upgrade (3.0→3.1) is not blocked - Site:
https://macwave.org/returns200and showsLatest Version : 3.0 - Two-way sync: pushing only
mainfast-forwards3.0, pushing only3.0
fast-forwardsmain, and a diverged pair fails the run without moving either side
(verified by local simulation)
MacWave 2.6.1
Release Note 更新日志
本次为通告性发布:LinuxWave 官网由 linuxwave.macwave.org 迁移至 linuxwave.org。
发生了什么
LinuxWave 的官网自定义域已迁移到 linuxwave.org。旧域
linuxwave.macwave.org 已配置 301 跳转到新域(配置时间较晚,301 生效存在等待期,
在生效前旧链接会短暂返回 404)。
这不是 MacWave 自己的搬家——macwave.org 与 sw.macwave.org 都不受影响——但指向
LinuxWave 的链接都在 MacWave 这边,需要跟着更新。
改了哪些链接
| 位置 | 说明 |
|---|---|
macwave.org |
顶部「Linux? View LinuxWave →」按钮(中英各一条) |
sw.macwave.org |
项目卡片中的 LinuxWave,以及「关于项目」表格里的 LinuxWave 链接 |
两处都已改为 https://linuxwave.org。
MacWave 装到用户机器上的代码里没有一处 LinuxWave 网址,
lib/与pkg/都不含
相关引用,因此不需要为这次域迁移改动任何逻辑。README 里指向的是 LinuxWave 的
GitHub 仓库(LinuxWaveOrg/LinuxWave),地址有效,保持不变。
为什么要发一版
域名迁移本身不需要改 MacWave 的任何代码,但发一版才能把这件事送达存量用户:
wave selfupdate 在「当前版本不低于线上版本」时会直接短路退出,所以仅更新网站链接的话,
用户的本地副本不会刷新,站内版本号也会一直停在 2.6.0。
抬到 2.6.1 后,2.6.0 及更早的用户执行自更新即可拿到与线上一致的一份。
升级路径已验证
| 当前版本 | 目标 | 行为 |
|---|---|---|
2.5.0 / 2.5.1 / 2.5.2 / 2.6.0 |
2.6.1 |
执行自更新 ✅ |
2.6.1 |
2.6.1 |
已是最新,短路退出 |
wave selfupdate
关于配置迁移
本次不涉及目录结构变更。configdata 仍照既有惯例提供了 updatedata/2.6.1——它与 2.5
的那份迁移是同一脚本、幂等,只会在发现 2.5 之前的旧配置时才动作,否则立即退出,
所以正常升级不会搬动你的配置目录。
之所以仍然放这份副本:install.sh / selfupdate.sh 是按完整版本号查找
updatedata/<版本号> 的。若缺了它,从 2.5 之前用 install.sh 升级到用户级目录时会跳过
迁移,留下老旧的系统级 /opt/macwave_config 把新的用户级配置盖住。
验证方式
- 回归套件:隔离沙箱里的 64 条断言全部通过(
64 PASS / 0 FAIL / 0 SKIP)——
本次为纯版本号改动,未触碰任何逻辑分支。 - 链接实测:
linuxwave.org返回 200;macwave.org与sw.macwave.org的源文件里
LinuxWave 链接均为https://linuxwave.org。 - 分支一致性:
gh-pages/install.sh与main/lib/install.sh逐字节相同。
其他改动
- 版本号
2.6.0→2.6.1,构建号280H2023 - 网站显示的版本号同步为
2.6.1 configdata新增updatedata/2.6.1(见上)
Release Notes
This is an announcement release: LinuxWave's site moved from linuxwave.macwave.org to linuxwave.org.
What happened
LinuxWave's site custom domain has moved to linuxwave.org. The old
linuxwave.macwave.org now has a 301 redirect to the new domain (it was
configured recently, so the redirect is still propagating; until it takes
effect the old links briefly return 404).
This is not MacWave moving - neither macwave.org nor sw.macwave.org is
affected - but the links pointing at LinuxWave live on MacWave's side and
needed updating.
Which links changed
| Where | Detail |
|---|---|
macwave.org |
The "Linux? View LinuxWave →" button at the top (one per language) |
sw.macwave.org |
The LinuxWave project card, and the LinuxWave row in the About table |
Both now point at https://linuxwave.org.
MacWave's installed code contains no LinuxWave URL at all - neither
lib/
norpkg/references it - so no logic needed changing for this domain move. The
README links to LinuxWave's GitHub repository (LinuxWaveOrg/LinuxWave),
which is still valid and unchanged.
Why cut a release at all
The domain move needs no MacWave code change, but a release is what delivers the
news to existing users: wave selfupdate exits early when the installed version
is already >= the published one, so updating only the website links would leave
local copies unrefreshed and the version shown on the site stuck at 2.6.0.
Raising it to 2.6.1 means anyone on 2.6.0 or older gets a copy matching what
the site serves by running the self-update.
Upgrade path verified
| From | To | Behaviour |
|---|---|---|
2.5.0 / 2.5.1 / 2.5.2 / 2.6.0 |
2.6.1 |
self-update runs ✅ |
2.6.1 |
2.6.1 |
already up to date, exits early |
wave selfupdate
About the config migration
No directory structure change is involved. configdata still ships
updatedata/2.6.1, following the established convention - the same idempotent
script as the 2.5 migration, which only acts when it finds a pre-2.5
configuration and otherwise exits immediately, so a normal upgrade will not move
your config directory.
The copy is shipped because install.sh / selfupdate.sh look the migration up by
the exact version being installed. Without it, upgrading from a pre-2.5 install
with install.sh into a user-level directory would skip the migration and keep
the stale system-level /opt/macwave_config, which then shadows the new
user-level config.
How it was verified
- Regression suite: all 64 assertions pass in the isolation sandbox
(64 PASS / 0 FAIL / 0 SKIP) - this release only changes version strings and
touches no logic branch. - Links:
linuxwave.orgreturns 200; the sources ofmacwave.organd
sw.macwave.orgboth carryhttps://linuxwave.org. - Branch consistency:
gh-pages/install.shis byte-identical to
main/lib/install.sh.
Other Changes
- Version
2.6.0→2.6.1, build number280H2023 - The version shown on the site is now
2.6.1 configdatagainsupdatedata/2.6.1(see above)
Install 安装
运行以下命令以下载此版本
Run the following command to download/update to this version.
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/MacWaveOrg/MacWave/HEAD/lib/install.sh)" && source ~/.zshrc
也可以直接执行一次自更新
Or simply run the self-update once
wave selfupdate
MacWave 2.6.0
Release Note 更新日志
本次发布只为迁移,功能没有任何变化。
仓库迁移
MacWave 的代码仓库已从 github.com/Sha0huaZhang/MacWave 迁移到
github.com/MacWaveOrg/MacWave。
这是 2.6.0 唯一的改动:新版本的代码里所有地址都直接指向新组织,不再依赖旧地址的跳转。
为什么发一个版本
迁移本身不需要改代码,但需要用户拿到指向新地址的代码。而 wave selfupdate 在
「已是最新」时会直接短路退出(当前版本 ≥ 远端版本即不动作),所以仅改地址的老用户
不会主动去取新地址——于是本次把版本号提到 2.6.0,让 2.5.x 的用户执行
wave selfupdate 时真正触发更新,一次性完成迁移。
你要做什么
推荐:执行一次自更新
wave selfupdate
执行后你的本地代码会换成指向新组织的版本,从此不再依赖旧地址跳转。
或者:重新安装
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/MacWaveOrg/MacWave/HEAD/lib/install.sh)"
什么都不做也可以
旧地址目前仍然可用(实测:返回内容与新地址一致),wave install / list /
uninstall 都不受影响。已安装的软件包、配置、软链接也不会被动到——
本次自更新只覆盖程序自身的代码文件与 VERSION.json。
升级路径已验证
| 当前版本 | 目标 | 行为 |
|---|---|---|
2.5.0 / 2.5.1 / 2.5.2 |
2.6.0 |
执行自更新 ✅ |
2.6.0 |
2.6.0 |
已是最新,短路退出 |
关于配置迁移:本次不涉及目录结构变更,但 configdata 仍提供了
updatedata/2.6.0——它与 2.5 的那份迁移是同一脚本、幂等,只会在发现 2.5 之前的
旧配置时才动作,否则立即退出,所以正常升级不会搬动你的配置目录。
之所以仍然放这份副本:install.sh / selfupdate.sh 是按完整版本号查找
updatedata/<版本号> 的。若缺了它,从 2.5 之前用 install.sh 升级到用户级目录
时会跳过迁移,留下老旧的系统级 /opt/macwave_config 把新的用户级配置盖住。
新地址
| 仓库 | https://github.com/MacWaveOrg/MacWave |
| 发布页 | https://github.com/MacWaveOrg/MacWave/releases |
| 网站 | https://macwave.org (不变) |
| 反馈邮箱 | hi@macwave.org (不变) |
请认准官方地址
官方仓库只有 MacWaveOrg/MacWave,官方网站只有 macwave.org,
官方反馈邮箱只有 hi@macwave.org。从其它来源获取的安装命令请勿执行。
其他改动
- 版本号
2.5.2→2.6.0,构建号280H1200 - 网站显示的版本号同步为
2.6.0 configdata新增updatedata/2.6.0(见上)
Release Notes
This release exists only for the migration. Nothing about the feature set changed.
Repository migration
The MacWave repository has moved from github.com/Sha0huaZhang/MacWave to
github.com/MacWaveOrg/MacWave.
That is the only change in 2.6.0: the code now points straight at the new
organisation and no longer depends on the old location redirecting.
Why cut a release at all
The move needed no code change, but users do need to obtain the code that
points at the new address. wave selfupdate short-circuits when there is
nothing newer (it exits as soon as the local version is >= the published one),
so simply changing the address would leave existing installs on the old URL.
Raising the version to 2.6.0 makes wave selfupdate actually run for anyone on
2.5.x, completing the migration in one step.
What you need to do
Recommended: run the self-update once
wave selfupdate
Your local copy then carries the new addresses and no longer relies on the old
location redirecting.
Or: reinstall
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/MacWaveOrg/MacWave/HEAD/lib/install.sh)"
Or: do nothing
The old address still works today (verified: it serves the same content as
the new one), and wave install / list / uninstall are unaffected. Your
installed packages, configuration and unversioned links are untouched - this
self-update only rewrites MacWave's own code files and VERSION.json.
Upgrade path verified
| From | To | Behaviour |
|---|---|---|
2.5.0 / 2.5.1 / 2.5.2 |
2.6.0 |
self-update runs ✅ |
2.6.0 |
2.6.0 |
already up to date, exits early |
About the config migration: no directory structure change is involved, but
configdata still ships updatedata/2.6.0 - the same idempotent script as the
2.5 migration, which only acts when it finds a pre-2.5 configuration and
otherwise exits immediately, so a normal upgrade will not move your config
directory.
The copy is shipped anyway because install.sh / selfupdate.sh look the
migration up by the exact version being installed. Without it, upgrading from
a pre-2.5 install with install.sh into a user-level directory would skip the
migration and keep the stale system-level /opt/macwave_config, which then
shadows the new user-level config.
New addresses
| Repository | https://github.com/MacWaveOrg/MacWave |
| Releases | https://github.com/MacWaveOrg/MacWave/releases |
| Website | https://macwave.org (unchanged) |
| Feedback | hi@macwave.org (unchanged) |
Only the official addresses
The only official repository is MacWaveOrg/MacWave, the only official
website is macwave.org, and the only official feedback address is
hi@macwave.org. Do not run install commands from anywhere else.
Other Changes
- Version
2.5.2→2.6.0, build number280H1200 - The version shown on the site is now
2.6.0 configdatagainsupdatedata/2.6.0(see above)
Install 安装
运行以下命令以下载此版本
Run the following command to download/update to this version.
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/MacWaveOrg/MacWave/HEAD/lib/install.sh)" && source ~/.zshrc
也可以直接执行一次自更新
Or simply run the self-update once
wave selfupdate
Site Notice-2026100701
Site Notice 站点公告
姊妹项目 LinuxWave 的官网已迁移:linuxwave.macwave.org → linuxwave.org
MacWave 这边已同步更新指向 LinuxWave 的链接。旧地址现在返回 301,会自动跳转到新域,
所以旧链接不会报错;不过 301 只是重定向在兜底,仍建议更新你的书签,把文档和脚本里
留的地址一并换成新的。
你需要做什么
没有装过 MacWave 的:什么都不用做。安装命令一个字都没变——它指向的是
raw.githubusercontent.com,跟 LinuxWave 的官网域名无关,所以以前复制过的命令照样能用:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/MacWaveOrg/MacWave/HEAD/lib/install.sh)"
已经装过 MacWave 的:也不需要做任何事。MacWave 的代码里不含任何 LinuxWave 网址,
所以这次域名迁移不影响你的安装、配置或已装软件包。
若只是想让本地版本号与线上保持一致,可以顺手跑一次(可选):
wave selfupdate
变了的部分
本次只有 MacWave 网站上指向 LinuxWave 的链接发生变化:
macwave.org顶部的「Linux? View LinuxWave →」按钮 → 现指向linuxwave.orgsw.macwave.org的 LinuxWave 项目卡片,以及「关于项目」表格里的 LinuxWave 链接 → 同上
没有变的部分
- MacWave 自己的域名没有变(
macwave.org、sw.macwave.org)——搬的是 LinuxWave 的官网。 - 反馈邮箱仍是
hi@macwave.org,请继续用它。 - MacWave 的仓库地址没有变(
github.com/MacWaveOrg/MacWave,自 2.6.0 起就是这个)。 - 安装命令、安装目录、配置文件、已装软件包的位置都没有变。本次不涉及目录结构变更,
自更新会跳过迁移步骤,不会搬动你的任何数据。
一句话
LinuxWave 换了官网,我们的链接已经跟过去;你自己的 MacWave 一切都照旧。
有问题请发邮件到 hi@macwave.org。
Site Notice
The sister project LinuxWave's site has moved: linuxwave.macwave.org → linuxwave.org
MacWave has updated the links pointing at LinuxWave. The old address now returns
301 and redirects to the new one, so old links still work; the 301 is only a
redirect doing the catching, though, so updating your bookmarks is still
recommended - please replace the old address anywhere it is kept in docs or scripts.
What you need to do
If you have not installed MacWave: nothing. The install command is unchanged - it
points at raw.githubusercontent.com and has nothing to do with LinuxWave's site
domain, so a command you copied earlier still works:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/MacWaveOrg/MacWave/HEAD/lib/install.sh)"
If you already have MacWave installed: you also need to do nothing. MacWave's
code contains no LinuxWave URL at all, so this domain move does not affect your
install, your configuration or your installed packages.
If you would simply like your local version to match what the site serves, you can
run the self-update (optional):
wave selfupdate
What changed
Only the links pointing at LinuxWave on MacWave's websites:
- The "Linux? View LinuxWave →" button on
macwave.org→ now points atlinuxwave.org - The LinuxWave project card on
sw.macwave.org, and the LinuxWave link in the About
table → same
What did not change
- MacWave's own domains are unchanged (
macwave.org,sw.macwave.org) - it is
LinuxWave's site that moved. - The feedback address is still
hi@macwave.org; keep using it. - MacWave's repository is unchanged (
github.com/MacWaveOrg/MacWave, as since 2.6.0). - The install command, install directories, config files and installed packages are
all unchanged. No directory structure change is involved, so the self-update skips
the migration step and will not move any of your data.
In one line
LinuxWave moved its website, our links followed; your own MacWave is untouched.
Questions? Mail hi@macwave.org.
MacWave 2.5.2
Release Note 更新日志
一次以实测驱动的修复版:在隔离沙箱里端到端跑安装/卸载,找出并修掉了几个缺陷(其中一个可能造成不可逆的损害),并把这套隔离测试固化成了仓库里的回归脚本。
修复:按官方写法卸载会删掉 /bin/bash
文档给出的卸载命令一直是 /bin/bash -c "$(curl -fsSL ...)"。在这种调用方式下,$0 不是脚本路径,而是解释器自身;带参数时则是 --:
/bin/bash -c "$(curl ...)" → $0=/bin/bash → 收尾执行 rm -f /bin/bash
/bin/bash -c "$(curl ...)" -- --force → $0=-- → 收尾执行 rm -f --(missing operand)
而卸载器收尾有一句 rm -f "$0",本意是删掉刚刚下载下来的脚本。于是按官方文档卸载、并且用了 sudo 的话,会把系统的 /bin/bash 删掉——此后任何 #!/bin/bash 脚本都再也跑不起来。
现在只有当 $0 确实是一个含本脚本标记的普通文件时才自删;$0 是解释器路径、--,或是不含标记的其它文件时一律跳过。以文件方式运行(bash uninstall.sh)仍然和以前一样删除自己。
修复:卸载器在无终端时会照删不误
卸载器第一次确认写的是 read -n 1 -r(读 stdin),而取消条件要求 REPLY 非空。stdin 是 EOF(< /dev/null、CI、被别的命令吃掉输入)时 REPLY 为空,于是跳过取消、直接删除:
bash uninstall.sh < /dev/null → 安装树、配置、PATH 全被删掉(预期:应拒绝)
echo n | bash uninstall.sh → 取消(这条一直是对的)
删除不可逆,不能把「读不到回答」当成「同意」。现在改为读 /dev/tty,读不到就中止并退出 1,同时提示需要显式加 --force。
修复:拒绝许可协议后留下悬空 PATH
「安装完成」的提示排在协议确认之前,而拒绝时会删掉安装树与配置目录——于是一个拒绝协议的用户会先被告知安装成功,随后一切被清空,而且 ~/.zshrc 里那行 PATH 没人清,留成指向已删目录的死条目。
现在:协议在「安装完成」之前询问;拒绝时把安装树、配置目录与 rc 里的 PATH 一起回滚干净。
顺带修掉同一条路径上的提权问题:清理配置目录那一行原本写死 sudo,而它上一行用的是按需提权的 run_cmd。用户级安装全程不需要 sudo,却在最后一步索要密码;sudo 不可用时会让清理中断在半路(配置目录残留)。
修复:-h / --help 对子命令不成立,且 selfupdate --help 会真的自更新
帮助里写着 -h, --help Show help for any command,实测却是一地鸡毛:
wave install --help → Error: Input contains illegal fields. exit=1
wave uninstall --help → Warning: unknown flag '--help' ignored. 然后继续执行 exit=1
wave link --help → 被当成链接请求:Error: Nothing to link. exit=1
wave list --help → 静默忽略 exit=0
wave selfupdate --help → 🌊 Fetching the latest version... ← 真的开始自更新
selfupdate 这条最要命:在旧版本上 wave selfupdate --help 会直接升级。现在在命令分发之前统一拦下 -h / --help,打印该命令自己的用法(用法行、旗标、示例)后以 0 退出,覆盖 install / uninstall / list / search / info / selfupdate / link / unlink / linkquery / version。旗标列表按各 handler 的白名单写,不承诺做不到的事。
修复:中途失败只留一个光秃秃的 curl 退出码
安装过程中途失败时(网络中断、权限、磁盘满),用户只看到 curl 的错误与一个非零退出码,不知道该做什么;而残留的安装树与配置会让人以为「已经装过」。
现在会打印:安装未完成(含退出码)、没有删除任何东西、已下载内容可能残留的具体路径、以及「修好后重跑即可,重跑是安全的」。刻意不自动删除——升级安装时删掉安装树会把用户原有的可用安装一起毁掉。
实现上要给脚本加 set -E(errtrace):默认情况下 ERR 陷阱不继承进函数,而下载是在 run_cmd 函数里跑的。set -E 之后,显式 return 1、调用方用 || 容忍的失败、if 条件里的失败都不会误触发(已逐条验证)。
新增:隔离安装测试 scripts/sandbox_test.sh
把这次的实测手法固化成回归脚本,64 条断言,全程跑在 macOS 自带的 sandbox-exec 里(默认拒绝,只允许写沙箱目录、/dev 与临时目录):/opt、/usr/local、/etc 与真实家目录一个字节都不会被写;取数改成 file:// 本地镜像(只改 BASE_URL / CONFIGDATA_URL 两行),因此不需要网络。
覆盖:用户级安装目录端到端(安装树 / 配置落位 / VERSION.json / PATH / 入口可执行)→ 系统级目录的落位(越过沙箱边界即失败,并打印选中的安装与配置目录)→ 参数校验 → 协议顺序与拒绝后的完整回滚 → 中途失败的指引且不误删 → 卸载确认语义(无终端 / n / y / --force)→ 卸载器自删($0 是文件 vs 解释器,且 /bin/bash 必须存活)→ 复用 link_test.sh。另有一组沙箱自检:断言 /opt 不可写、沙箱内可写——这组要是挂了,说明隔离没生效,其余断言都不可信。
macOS 侧的范围差异(写在脚本头部):LinuxWave 用
unshare -rm做用户+挂载命名空间并把系统目录换成影子副本;macOS 没有这些,改用sandbox-exec的「拒绝写沙箱外」。因此系统级目录只断言落位(安装必然在越过边界时失败,而失败指引会打印选中的目录),另外没有共享安装与账号删除那一组断言(那是 Linux 专有功能)。
其他改动
- 版本号
2.5.1→2.5.2,构建号280H0111 README.md:无人值守安装统一为/bin/bash -c "$(curl …)",选项放在--之后;补充「不带--force时卸载器需要终端」的说明PROJECT_CONTEXT.md:登记新脚本,并记录本次全部修复configdata:latest_version更新为2.5.2,并补上updatedata/2.5.2(安装器按完整版本号查找迁移目录,缺了它,从 2.5 之前升级到 2.5.2 就不会执行 2.5 的配置迁移)- 网站显示的版本号同步为
2.5.2
Release Notes
A fix release driven by end-to-end testing: the installer and uninstaller were run inside an isolated sandbox, which surfaced several defects (one of them capable of irreversible damage). The isolation harness itself is now a regression script in the repository.
Fix: the documented uninstall command deleted /bin/bash
The documented uninstall command has always been /bin/bash -c "$(curl -fsSL ...)". Under that invocation $0 is not the script path but the interpreter itself, or -- when arguments follow:
/bin/bash -c "$(curl ...)" → $0=/bin/bash → ends with rm -f /bin/bash
/bin/bash -c "$(curl ...)" -- --force → $0=-- → ends with rm -f -- (missing operand)
The uninstaller ended with rm -f "$0", there to remove the script it had just downloaded. So uninstalling the documented way, with sudo, deleted the system's /bin/bash - after which no #!/bin/bash script could run again.
The self-delete now happens only when $0 is a regular file carrying this script's marker; an interpreter path, --, or an unrelated file is left alone. Running it as a file (bash uninstall.sh) still removes itself exactly as before.
Fix: the uninstaller deleted anyway when there was no terminal
The first confirmation used read -n 1 -r (from stdin) and only cancelled when REPLY was non-empty. With stdin at EOF (< /dev/null, CI, or input consumed by something else) REPLY stayed empty, so it skipped the cancel and deleted:
bash uninstall.sh < /dev/null → tree, config and PATH all removed (should have refused)
echo n | bash uninstall.sh → cancelled (this path was always correct)
Deleting is not reversible, so "no answer" must not mean "yes". It now reads /dev/tty and stops with exit 1 when it cannot, telling the user to pass --force.
Fix: declining the agreement left a dangling PATH entry
The "Installation complete!" banner was printed before the agreement gate, and declining deletes the install tree and config - so someone who declined was first told the install succeeded, then had everything wiped, with the PATH line still sitting in ~/.zshrc pointing at a directory that no longer exists.
The agreement is now asked before the banner, and declining rolls back the tree, the config directory and the rc PATH entry together.
A privilege bug on the same path is fixed too: the config cleanup hardcoded sudo while the line above it used the on-demand run_cmd. A user-level install needs no sudo at all, yet asked for a password at the last step; where sudo was unavailable the cleanup aborted halfway, leaving the config behind.
Fix: -h / --help did not work per command, and selfupdate --help performed an update
Help promises -h, --help Show help for any command, but reality was a mess:
wave install --help → Error: Input contains illegal fields. exit=1
wave uninstall --help → Warning: unknown flag '--help' ignored. then ran anyway exit=1
wave link --help → treated as a link request: Error: Nothing to link. exit=1
wave list --help → silently ignored exit=0
wave selfupdate --help → 🌊 Fetching the latest version... ← actually started updating
The selfupdate one matters most: on an older install, wave selfupdate --help upgraded it. -h / --help is now intercepted before command dispatch and prints that command's usage (usage line, flags, examples) with exit 0, for install / uninstall / list / search / info / selfupdate / link / unlink / linkquery / version. The flag lists come from each handler's own whitelist, so nothing is promised that the command does not accept.
Fix: a mid-install failure left only a bare curl exit code
When an install failed partway (network drop, permissions, disk full) all the user saw was curl's error and a non-zero status, with no idea what to do - and the leftover tree and config made it look installed.
It now prints: the install did not finish (with the exit code), that nothing was removed, the exact paths where downloaded content may be left, and that rerunning the installer is safe and will overwrite what it downloaded. It deliberately does not delete anything on its own: on an upgrade, removing the tree would destroy the user's working install.
This needed set -E (errtrace): by default an ERR trap is not inherited into functions, and the download runs inside run_cmd. With -E on, explicit return 1, failures the caller tolerates with ||, and failures inside if conditions all stay silent (each verified).
New: scripts/sandbox_test.sh
The testing behind this release is now a regression script with 64 assertions, running inside macOS's own sandbox-exec (default-deny, with only the sandbox directory, /dev and the temp dirs writable), so not a byte of /opt, /usr/local, /etc or the real home is written. Fetches point at a local file:// mirror (only BASE_URL / CONFIGDATA_URL change), so it needs no network.
Coverage: user-level install end to end (tree, config placement, VERSION.json, PATH, executable entry) → system-level directories' placement (the install must fail at the sandbox boundary, and the failure prints the chosen install and config directories) → argument validation → agreement ordering and full rollback after declining → mid-failure guidance without deleting anything → uninstall confirmation semantics (no terminal / n / y / --force) → uninstaller self-delete ($0 as a file vs the interpreter, and /bin/bash must survive) → plus link_test.sh. A separate sandbox self-check asserts /opt is unwritable and the sandbox is writable - if that group fails, the isolation is not in effect and the rest cannot be trusted.
macOS-specific scope, documented in the script header: LinuxWave uses
unshare -rmfor a user+mount namespace and swaps the system directories for shadow copies; macOS has neither, so this usessandbox-exec's "writes outside the sandbox are denied". System-level directories are therefore asserted for placement only (the install necessarily fails at the boundary, and the failure prints the chosen directories), and there are no shared-install or account-removal assertions, since those are Linux-only features.
Other Changes
- Version
2.5.1→2.5.2, build number280H0111 README.md: unattended install is uniformly `/bin/b...