feat(desktop): name commit and canary builds in the product display name
Commit builds now show 'Hermes Agent <sha7>' (e.g. Hermes Agent abc1234) and canary builds 'Hermes Canary' / 'Hermes Light Canary' / 'Hermes Agent Canary' as the OS-visible product name, so side-by-side installs and per-commit artifacts are readable at a glance. Display-only by design: appId, appNamePascal, and msixAppIdWithOrg are unchanged, so a canary MSIX still updates in place over stable and userData / single-instance sharing with the stable install is unaffected. bundle-electron-main.mjs derives the commit from the install stamp (source='commit-build') so the baked runtime identity matches the packaging identity.
This commit is contained in:
@@ -17,6 +17,7 @@ beforeEach(() => {
|
||||
afterEach(() => {
|
||||
delete process.env.HERMES_DESKTOP_VARIANT
|
||||
delete process.env.HERMES_PAYLOAD_TAG
|
||||
delete process.env.HERMES_BUILD_COMMIT
|
||||
vi.resetModules()
|
||||
})
|
||||
|
||||
@@ -58,6 +59,38 @@ test('a canary payload tag moves BOTH variants onto their canary feed channel',
|
||||
assert.equal(light.channel, 'light-canary')
|
||||
})
|
||||
|
||||
test('a canary tag renames the DISPLAY name only, keeping machine identity intact', async () => {
|
||||
const stable = await identityForVariant(undefined)
|
||||
process.env.HERMES_PAYLOAD_TAG = 'v0.28.0-canary.20260818'
|
||||
const canaryIdentity = await identityForVariant(undefined)
|
||||
|
||||
assert.equal(canaryIdentity.displayName, 'Hermes Canary')
|
||||
// Machine identity is shared with stable so the canary MSIX still updates
|
||||
// in place over stable and userData/single-instance stay shared.
|
||||
assert.equal(canaryIdentity.appId, stable.appId)
|
||||
assert.equal(canaryIdentity.appNamePascal, stable.appNamePascal)
|
||||
assert.equal(canaryIdentity.msixAppIdWithOrg, stable.msixAppIdWithOrg)
|
||||
})
|
||||
|
||||
test('a commit build names the SHA in the display name, machine identity intact', async () => {
|
||||
process.env.HERMES_BUILD_COMMIT = 'abcdef1234567890abcdef1234567890abcdef12'
|
||||
const full = await identityForVariant(undefined)
|
||||
assert.equal(full.displayName, 'Hermes abcdef1')
|
||||
|
||||
const bundled = await identityForVariant('bundled')
|
||||
assert.equal(bundled.displayName, 'Hermes Agent abcdef1')
|
||||
|
||||
delete process.env.HERMES_BUILD_COMMIT
|
||||
const plain = await identityForVariant(undefined)
|
||||
assert.equal(plain.displayName, 'Hermes')
|
||||
|
||||
// Malformed commit values must not leak into the name (commit builds
|
||||
// validate the full SHA elsewhere; the display derivation stays total).
|
||||
process.env.HERMES_BUILD_COMMIT = 'not-a-sha'
|
||||
const malformed = await identityForVariant(undefined)
|
||||
assert.equal(malformed.displayName, 'Hermes')
|
||||
})
|
||||
|
||||
test('stable tags and tagless dev builds publish to the stable channels', async () => {
|
||||
process.env.HERMES_PAYLOAD_TAG = 'v0.28.0'
|
||||
assert.equal((await identityForVariant(undefined)).channel, 'latest')
|
||||
|
||||
@@ -42,13 +42,26 @@ const name = variants[store ? 'bundled' : (variant || '')]
|
||||
// never overwrite the stable feed file, and vice versa.
|
||||
const canary = /-canary\.20\d{6}(?:\d{6})?$/.test(process.env.HERMES_PAYLOAD_TAG || '')
|
||||
|
||||
// Display-name qualifiers, display-only by design: appId/appNamePascal/
|
||||
// msixAppIdWithOrg stay fixed so a canary MSIX still updates in place
|
||||
// over stable and userData/single-instance sharing is unaffected. A
|
||||
// commit build names the exact SHA it was built from (Hermes Agent abc1234);
|
||||
// a canary tags itself so side-by-side installs are readable at a glance.
|
||||
const buildCommitEnv = process.env.HERMES_BUILD_COMMIT || ''
|
||||
const buildCommit = /^[a-f0-9]{40}$/.test(buildCommitEnv) ? buildCommitEnv.slice(0, 7) : null
|
||||
const displayName = buildCommit
|
||||
? `${name.display} ${buildCommit}`
|
||||
: canary
|
||||
? `${name.display} Canary`
|
||||
: name.display
|
||||
|
||||
/** @typedef {import("./product-identity.d.cts")} ProductIdentity */
|
||||
|
||||
/** @type {ProductIdentity} */
|
||||
const identity = {
|
||||
store,
|
||||
light,
|
||||
displayName: name.display,
|
||||
displayName,
|
||||
appId: `com.nousresearch.${name.kebab}`,
|
||||
// The store build never publishes to a release feed (the Store owns its
|
||||
// updates); null means "no feed" for its publish config.
|
||||
|
||||
@@ -18,7 +18,10 @@ function productIdentity(source, stamp) {
|
||||
}
|
||||
return execFileSync(process.execPath, ['-e', 'console.log(JSON.stringify(require(process.argv[1])))',
|
||||
join(source, 'apps/desktop/product-identity.cjs')], {
|
||||
env: { ...process.env, HERMES_DESKTOP_VARIANT: variant, HERMES_PAYLOAD_TAG: stamp.tag || '' },
|
||||
env: { ...process.env, HERMES_DESKTOP_VARIANT: variant, HERMES_PAYLOAD_TAG: stamp.tag || '',
|
||||
// Commit builds stamp source='commit-build'; the display name carries
|
||||
// the short SHA (see product-identity.cjs). Tagged builds pass ''.
|
||||
HERMES_BUILD_COMMIT: stamp.source === 'commit-build' ? (stamp.commit || '') : '' },
|
||||
encoding: 'utf8',
|
||||
}).trim()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user