diff --git a/product-source/hololake-native-desktop/scripts/release-pipeline.mjs b/product-source/hololake-native-desktop/scripts/release-pipeline.mjs index 5b9979618..b05a7cffd 100644 --- a/product-source/hololake-native-desktop/scripts/release-pipeline.mjs +++ b/product-source/hololake-native-desktop/scripts/release-pipeline.mjs @@ -120,8 +120,9 @@ export function materializeUpdaterPrivateKey(env, readText = (file) => fs.readFi readText(privateKeyPath), 'HOLOLAKE_RELEASE_PIPELINE_UPDATER_PRIVATE_KEY_EMPTY', ) + const { TAURI_SIGNING_PRIVATE_KEY_PATH: _privateKeyPath, ...childEnvironment } = env return { - ...env, + ...childEnvironment, TAURI_SIGNING_PRIVATE_KEY: privateKey, } } @@ -234,6 +235,18 @@ function localDateDirectory() { return `${date.getFullYear()}-${part(date.getMonth() + 1)}-${part(date.getDate())}` } +function unsignedExecutableSha256(executable) { + const temporary = fs.mkdtempSync(path.join(os.tmpdir(), 'hololake-unsigned-executable-')) + const copy = path.join(temporary, path.basename(executable)) + try { + fs.copyFileSync(executable, copy) + run('/usr/bin/codesign', ['--remove-signature', copy]) + return sha256File(copy) + } finally { + fs.rmSync(temporary, { recursive: true, force: true }) + } +} + export function formatPlistDate(date) { const weekdays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'] const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] @@ -260,7 +273,9 @@ function createXcodeArchive(application, input) { add('Name', 'string', `HoloLake ${input.version} ${input.sourceCommit.slice(0, 12)}`) add('SchemeName', 'string', 'HoloLake') add('HoloLakeSourceCommit', 'string', input.sourceCommit) - add('HoloLakeExecutableSha256', 'string', sha256File(path.join(application, 'Contents/MacOS/hololake-native-desktop'))) + const executable = path.join(application, 'Contents/MacOS/hololake-native-desktop') + add('HoloLakePreNotarizationExecutableSha256', 'string', sha256File(executable)) + add('HoloLakeUnsignedExecutableSha256', 'string', unsignedExecutableSha256(executable)) add('ApplicationProperties', 'dict', '') add('ApplicationProperties:ApplicationPath', 'string', 'Applications/HoloLake.app') add('ApplicationProperties:ArchiveVersion', 'integer', '2') @@ -298,11 +313,13 @@ function resolveNotarizedApplication(argument, input) { fail('HOLOLAKE_RELEASE_PIPELINE_NOTARIZED_APP_IDENTITY_MISMATCH') } const archive = findPreparedXcodeArchive(input) - const expectedExecutableSha256 = run('/usr/libexec/PlistBuddy', [ - '-c', 'Print :HoloLakeExecutableSha256', path.join(archive, 'Info.plist'), + const expectedUnsignedExecutableSha256 = run('/usr/libexec/PlistBuddy', [ + '-c', 'Print :HoloLakeUnsignedExecutableSha256', path.join(archive, 'Info.plist'), ], { capture: true }).trim() - const actualExecutableSha256 = sha256File(path.join(application, 'Contents/MacOS/hololake-native-desktop')) - if (actualExecutableSha256 !== expectedExecutableSha256) { + const actualUnsignedExecutableSha256 = unsignedExecutableSha256( + path.join(application, 'Contents/MacOS/hololake-native-desktop'), + ) + if (actualUnsignedExecutableSha256 !== expectedUnsignedExecutableSha256) { fail('HOLOLAKE_RELEASE_PIPELINE_NOTARIZED_APP_SOURCE_BINARY_MISMATCH') } return application diff --git a/product-source/hololake-native-desktop/scripts/release-pipeline.test.mjs b/product-source/hololake-native-desktop/scripts/release-pipeline.test.mjs index 118453447..9d71ec463 100644 --- a/product-source/hololake-native-desktop/scripts/release-pipeline.test.mjs +++ b/product-source/hololake-native-desktop/scripts/release-pipeline.test.mjs @@ -102,6 +102,7 @@ test('release pipeline materializes a protected updater key path only inside the return 'encrypted-private-key-material' }) assert.equal(hydrated.TAURI_SIGNING_PRIVATE_KEY, 'encrypted-private-key-material') + assert.equal(hydrated.TAURI_SIGNING_PRIVATE_KEY_PATH, undefined) assert.equal(source.TAURI_SIGNING_PRIVATE_KEY, undefined) }) @@ -109,7 +110,9 @@ test('release pipeline verifies the updater signature against embedded product t const source = readFileSync(new URL('./release-pipeline.mjs', import.meta.url), 'utf8') assert.match(source, /--example',\s*'verify_updater_signature'/) assert.match(source, /src-tauri\/release-trust\.json/) - assert.match(source, /HoloLakeExecutableSha256/) + assert.match(source, /HoloLakePreNotarizationExecutableSha256/) + assert.match(source, /HoloLakeUnsignedExecutableSha256/) + assert.match(source, /--remove-signature/) assert.match(source, /NOTARIZED_APP_SOURCE_BINARY_MISMATCH/) assert.match(source, /APPLE_APP_NOTARIZATION_ACCEPTED_AND_STAPLED_DMG_CONTAINS_NOTARIZED_APP/) assert.match(source, /path\.resolve\(appExecutable, '\.\.\/\.\.\/\.\.'\)/)