c52e0814d6
- 新增 channel-identity.mjs 单点定义渠道安装身份,默认渠道保持基线 productName 与 identifier - build-release.mjs 渠道 --config 同批注入 productName、identifier 与 updater 端点 - build-macos-ci.mjs 按发布渠道解析产品名,派生 .app、updater 归档与 DMG 名称 - main.rs 与 windows.rs 窗口标题取构建期产品名,同机并存的渠道客户端可区分 - config.rs 提权 ACL 的 managed 范围按目录名识别基线目录与渠道派生目录 - check-config.mjs 断言基线等于默认渠道身份且非默认渠道身份必须隔离 - build-release.test.mjs 与 prepare-macos-codex.test.mjs 补齐渠道身份与渠道首装包用例 - 更新 AGC 更新主规范、开发运维文档、共享记忆与渠道安装身份隔离的里程碑与实施计划
74 lines
2.4 KiB
JavaScript
74 lines
2.4 KiB
JavaScript
/**
|
||
* AGC 渠道 → 安装身份。
|
||
*
|
||
* 渠道同时决定两件事:
|
||
* - 更新端点:OSS 分区 `<channel>-win` / `<channel>-mac` 的清单地址;
|
||
* - 安装身份:`productName` 与 `identifier`。
|
||
*
|
||
* 安装身份决定 Windows 安装目录与卸载项、macOS `.app` 名字与 bundle id、
|
||
* Windows WebView2 数据目录以及 `%APPDATA%\<identifier>` 客户端数据目录。
|
||
* 因此不同渠道的包体在同一台设备上并存时互不顶掉,也不会共享登录态、
|
||
* 本地项目与运行锁。
|
||
*
|
||
* 默认渠道 `dev` 保持已发布客户端身份不变:升级链路与既有安装不能断。
|
||
*/
|
||
|
||
export const AGC_DEFAULT_CHANNEL = 'dev';
|
||
export const AGC_PRODUCT_NAME = '陶泥儿';
|
||
export const AGC_APP_IDENTIFIER = 'world.genarrative.ai-game-creator';
|
||
|
||
const reservedChannelNames = new Set([
|
||
'win',
|
||
'mac',
|
||
'windows',
|
||
'macos',
|
||
'darwin',
|
||
'linux',
|
||
]);
|
||
|
||
/** 校验渠道名:小写字母开头,允许数字与连字符,系统名不属于渠道。 */
|
||
export function validateReleaseChannel(channel) {
|
||
if (
|
||
typeof channel !== 'string' ||
|
||
!/^[a-z][a-z0-9-]{0,31}$/u.test(channel) ||
|
||
channel.endsWith('-') ||
|
||
reservedChannelNames.has(channel) ||
|
||
/-(win|mac)$/u.test(channel)
|
||
) {
|
||
throw new Error(
|
||
'发布渠道无效:请使用 dev、release 或最多 32 位的小写字母、数字和连字符名称,系统名称不属于渠道',
|
||
);
|
||
}
|
||
return channel;
|
||
}
|
||
|
||
export function resolveReleaseChannel(env = process.env) {
|
||
return validateReleaseChannel(env.AGC_UPDATE_CHANNEL?.trim() ?? 'dev');
|
||
}
|
||
|
||
/** 安装身份里的展示后缀:`release` → `Release`,`beta-2` → `Beta-2`。 */
|
||
export function channelDisplaySuffix(channel) {
|
||
return validateReleaseChannel(channel)
|
||
.split('-')
|
||
.map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1))
|
||
.join('-');
|
||
}
|
||
|
||
/**
|
||
* 渠道对应的安装身份。默认渠道返回基线身份,其它渠道派生渠道后缀,
|
||
* 保证同一台设备上不同渠道互不覆盖。
|
||
*/
|
||
export function resolveChannelInstallIdentity(channel = AGC_DEFAULT_CHANNEL) {
|
||
validateReleaseChannel(channel);
|
||
if (channel === AGC_DEFAULT_CHANNEL) {
|
||
return Object.freeze({
|
||
productName: AGC_PRODUCT_NAME,
|
||
identifier: AGC_APP_IDENTIFIER,
|
||
});
|
||
}
|
||
return Object.freeze({
|
||
productName: `${AGC_PRODUCT_NAME} ${channelDisplaySuffix(channel)}`,
|
||
identifier: `${AGC_APP_IDENTIFIER}.${channel}`,
|
||
});
|
||
}
|