cmux-dev-workflow
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinesecmux Dev Workflow
cmux 开发工作流
Initial setup
初始设置
Run the setup script to initialize submodules, build GhosttyKit, and install the pbxproj normalization pre-commit hook:
bash
./scripts/setup.sh运行setup脚本以初始化子模块、构建GhosttyKit并安装pbxproj规范化的pre-commit钩子:
bash
./scripts/setup.shXcode toolchain
Xcode工具链
The team is pinned to Xcode 26.x. records the major; carries , which is what Xcode 26 writes by default. (objectVersion 77 is reserved for projects that adopt synchronized folder groups, which cmux does not use yet. Bumping to a different value requires a deliberate team decision.)
.xcode-versioncmux.xcodeproj/project.pbxprojobjectVersion = 60scripts/setup.shscripts/git-hooks/pre-commitscripts/normalize-pbxproj.pycmux.xcodeproj/project.pbxprojscripts/check-pbxproj.shobjectVersion.xcode-version.xcode-versioncmux.xcodeprojobjectVersionscripts/check-pbxproj.shobjectVersion团队固定使用Xcode 26.x。记录主版本号;中,这是Xcode 26默认生成的值。(objectVersion 77是为采用同步文件夹组的项目预留的,cmux目前尚未使用。更改此值需要团队做出明确决策。)
.xcode-versioncmux.xcodeproj/project.pbxprojobjectVersion = 60scripts/setup.shscripts/git-hooks/pre-commitcmux.xcodeproj/project.pbxprojscripts/normalize-pbxproj.pyscripts/check-pbxproj.shobjectVersion.xcode-version.xcode-versioncmux.xcodeprojobjectVersionscripts/check-pbxproj.shobjectVersionSidebar extension point (dev tagging)
侧边栏扩展点(开发标签)
Each tagged dev build gets its own ExtensionKit sidebar extension point so concurrent dev builds don't collide. Three build settings drive this:
- (default
CMUX_SIDEBAR_EXTENSION_POINT_ID): the extension point identifier baked into Info.plist at build time.com.cmuxterm.app.cmux.sidebar - (default empty): inserted into the app and appex bundle ids so a tagged extension gets a distinct identity that pkd records separately.
CMUX_BUNDLE_ID_SUFFIX - (default empty): appended to the appex
CMUX_DISPLAY_NAME_SUFFIX. The OS groups sidebar extensions by display name for the enable/disable + availability counts the host reads (CFBundleDisplayNameexposes onlyAppExtensionIdentity,bundleIdentifier,localizedName,extensionPointIdentifier— cmux already keys its own identity off the stableid, but the OS-level grouping is by name). Two same-named appexes installed side by side (a base build and a tagged build) are treated as one logical extension, so toggling one perturbs the other; a per-tag display name keeps them distinct.bundleIdentifier
The host resolves its point id at runtime from the Info.plist key via . scopes the host point to . builds a matching tag-scoped sample extension, passing , , and . It installs exactly what xcodebuild produced (xcodebuild ad-hoc signs with entitlements intact) — it does NOT re-sign, because a bare strips the appex entitlements and the extension then drops its host XPC connection. pkd ingests the tagged copy because its bundle id is distinct. Verify with .
CMUXSidebarExtensionPointIdentifierCmuxSidebarExtensionPoint.identifier(in:)./scripts/reload.sh --tag <tag>com.cmuxterm.app.debug.<tag>.cmux.sidebar./scripts/reload-extension.sh --tag <tag> [--host-bundle-id <id>] [--example sample|tabs|both]CMUX_SIDEBAR_EXTENSION_POINT_ID=<host-bundle-id>.cmux.sidebarCMUX_BUNDLE_ID_SUFFIX=.<tag>CMUX_DISPLAY_NAME_SUFFIX=" <tag>"codesign --force --sign -pluginkit -m -p <host-bundle-id>.cmux.sidebarTo author a NEW sample extension that is tag-ready:
- appex Info.plist: .
EXAppExtensionAttributes:EXExtensionPointIdentifier = $(CMUX_SIDEBAR_EXTENSION_POINT_ID) - add (default
CMUX_SIDEBAR_EXTENSION_POINT_ID),com.cmuxterm.app.cmux.sidebar(default empty), andCMUX_BUNDLE_ID_SUFFIX(default empty) build settings to the app and appex targets in all build configs.CMUX_DISPLAY_NAME_SUFFIX - =
PRODUCT_BUNDLE_IDENTIFIERfor the app target and<appBase>$(CMUX_BUNDLE_ID_SUFFIX)for the appex (suffix before the appex leaf so the appex id stays prefixed by the app id).<appBase>$(CMUX_BUNDLE_ID_SUFFIX).<leaf> - appex (or the
INFOPLIST_KEY_CFBundleDisplayNameInfo.plist value) =CFBundleDisplayName.<Name>$(CMUX_DISPLAY_NAME_SUFFIX) - it must be ad-hoc signed by xcodebuild (Info.plist bound, entitlements intact) for pkd to ingest the tagged copy; do not re-sign post-build.
每个带标签的开发构建都有自己的ExtensionKit侧边栏扩展点,这样并发的开发构建不会冲突。三个构建设置驱动这一机制:
- (默认值
CMUX_SIDEBAR_EXTENSION_POINT_ID):构建时嵌入到Info.plist中的扩展点标识符。com.cmuxterm.app.cmux.sidebar - (默认空):插入到app和appex的bundle id中,使带标签的扩展获得一个独特的标识,以便pkd单独记录。
CMUX_BUNDLE_ID_SUFFIX - (默认空):附加到appex的
CMUX_DISPLAY_NAME_SUFFIX后。操作系统会按显示名称对侧边栏扩展进行分组,用于主机读取的启用/禁用和可用性计数(CFBundleDisplayName仅暴露AppExtensionIdentity、bundleIdentifier、localizedName、extensionPointIdentifier——cmux已经基于稳定的id标识自身,但操作系统层面是按名称分组)。如果两个同名的appex并排安装(一个基础构建和一个带标签的构建),会被视为一个逻辑扩展,因此切换其中一个会影响另一个;每个标签对应一个显示名称可使它们保持区分。bundleIdentifier
主机通过从Info.plist的键在运行时解析其扩展点ID。会将主机扩展点限定为。会构建一个匹配标签范围的示例扩展,传入、和。它会安装xcodebuild生成的完整产物(xcodebuild会进行临时签名并保留权限)——不会重新签名,因为单纯的会移除appex的权限,导致扩展断开与主机的XPC连接。由于其bundle id是唯一的,pkd会加载带标签的副本。可以用进行验证。
CmuxSidebarExtensionPoint.identifier(in:)CMUXSidebarExtensionPointIdentifier./scripts/reload.sh --tag <tag>com.cmuxterm.app.debug.<tag>.cmux.sidebar./scripts/reload-extension.sh --tag <tag> [--host-bundle-id <id>] [--example sample|tabs|both]CMUX_SIDEBAR_EXTENSION_POINT_ID=<host-bundle-id>.cmux.sidebarCMUX_BUNDLE_ID_SUFFIX=.<tag>CMUX_DISPLAY_NAME_SUFFIX=" <tag>"codesign --force --sign -pluginkit -m -p <host-bundle-id>.cmux.sidebar要创建一个支持标签的新示例扩展:
- appex的Info.plist中设置。
EXAppExtensionAttributes:EXExtensionPointIdentifier = $(CMUX_SIDEBAR_EXTENSION_POINT_ID) - 在所有构建配置中,为app和appex目标添加(默认
CMUX_SIDEBAR_EXTENSION_POINT_ID)、com.cmuxterm.app.cmux.sidebar(默认空)和CMUX_BUNDLE_ID_SUFFIX(默认空)构建设置。CMUX_DISPLAY_NAME_SUFFIX - app目标的=
PRODUCT_BUNDLE_IDENTIFIER,appex的为<appBase>$(CMUX_BUNDLE_ID_SUFFIX)(后缀在appex的leaf之前,这样appex的ID始终以app的ID为前缀)。<appBase>$(CMUX_BUNDLE_ID_SUFFIX).<leaf> - appex的(或Info.plist中的
INFOPLIST_KEY_CFBundleDisplayName值) =CFBundleDisplayName。<Name>$(CMUX_DISPLAY_NAME_SUFFIX) - 必须由xcodebuild进行临时签名(绑定Info.plist,保留权限),pkd才能加载带标签的副本;构建后不要重新签名。