fix(dev): stabilize macOS safeStorage Keychain key name across dev branches (#9400)

Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
Neil 2026-07-19 21:31:36 -07:00 committed by GitHub
parent 74e2cb702c
commit e90dc38f01
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 33 additions and 2 deletions

View File

@ -1766,7 +1766,11 @@ app.whenReady().then(async () => {
}
)
electronApp.setAppUserModelId(devInstanceIdentity.appUserModelId)
app.setName(devInstanceIdentity.name)
// Why: setName drives the macOS safeStorage Keychain item name. Use the stable
// appName (not the per-branch `name`) so dev branches share one key and don't
// re-prompt per branch; the per-branch label still shows via window title,
// renderer identity, and the app-menu label passed to registerAppMenu below.
app.setName(devInstanceIdentity.appName)
// Why: managed WSL launchers live outside the Windows app bundle, so keep
// their launcher and bridge contract synchronized across app updates.
@ -2138,6 +2142,7 @@ app.whenReady().then(async () => {
logStartupMilestone('i18n-ready')
registerAppMenu({
appMenuLabel: devInstanceIdentity.name,
onCheckForUpdates: (options) => runUserInitiatedUpdateCheck(options),
onBeforeReload: ({ ignoreCache, webContentsId }) => {
if (mainWindow?.webContents.id === webContentsId) {

View File

@ -37,6 +37,9 @@ type RegisterAppMenuOptions = {
onToggleAppearance: (key: AppearanceMenuKey) => void
getAppearanceState: () => AppearanceMenuState
getKeybindings?: () => KeybindingOverrides | undefined
// Why: the macOS app-menu title. Passed the per-branch dev label since
// app.name is now pinned to a stable value for Keychain-key stability.
appMenuLabel?: string
}
function buildAndApplyMenu(options: RegisterAppMenuOptions): void {
@ -130,7 +133,7 @@ function buildAndApplyMenu(options: RegisterAppMenuOptions): void {
// redundant "Orca" entry with roles that don't apply, so we omit it there
// and distribute its items across File / Help instead.
const macAppMenu: Electron.MenuItemConstructorOptions = {
label: app.name,
label: options.appMenuLabel ?? app.name,
submenu: [
{ role: 'about' },
checkForUpdatesItem,

View File

@ -5,6 +5,7 @@ describe('dev-instance-identity', () => {
it('keeps packaged identity stable', () => {
expect(getDevInstanceIdentity(false, {})).toMatchObject({
name: 'Orca',
appName: 'Orca',
isDev: false,
devLabel: null,
dockBadgeLabel: null,
@ -12,6 +13,18 @@ describe('dev-instance-identity', () => {
})
})
it('pins a stable dev appName across branches so the safeStorage key does not churn', () => {
const a = getDevInstanceIdentity(true, { ORCA_DEV_BRANCH: 'feature/a' })
const b = getDevInstanceIdentity(true, { ORCA_DEV_BRANCH: 'feature/b' })
// Per-branch label differs (window title / app menu)...
expect(a.name).not.toBe(b.name)
// ...but the Keychain-driving appName is identical and distinct from prod.
expect(a.appName).toBe('Orca Dev')
expect(b.appName).toBe('Orca Dev')
expect(a.appName).not.toBe('Orca')
})
it('derives a readable dev label from worktree and branch env', () => {
const identity = getDevInstanceIdentity(true, {
ORCA_DEV_REPO_ROOT: '/repo/worktrees/dev-indicator',

View File

@ -8,6 +8,11 @@ const MAX_LABEL_LENGTH = 80
export type DevInstanceIdentity = AppIdentity & {
appUserModelId: string
// Why: drives app.setName → the macOS safeStorage Keychain item name
// ("<appName> Safe Storage"). Kept stable across dev branches (unlike the
// per-branch `name`) so every dev instance shares one Keychain key instead of
// creating a new one per branch and re-prompting. Distinct from prod's 'Orca'.
appName: string
}
function cleanEnvValue(value: string | undefined): string | null {
@ -50,6 +55,7 @@ export function getDevInstanceIdentity(
if (!isDev) {
return {
name: BASE_APP_NAME,
appName: BASE_APP_NAME,
isDev: false,
devLabel: null,
devBranch: null,
@ -71,6 +77,10 @@ export function getDevInstanceIdentity(
return {
name: dockTitle,
// Why: one stable Keychain key ('Orca Dev Safe Storage') for all dev
// branches; the per-branch identity still shows via `name` (window title,
// app menu, renderer label).
appName: `${BASE_APP_NAME} Dev`,
isDev: true,
devLabel,
devBranch: branch,