|
|
@@ -0,0 +1,108 @@
|
|
|
+import { mkdtemp, rm, writeFile } from 'node:fs/promises'
|
|
|
+import { tmpdir } from 'node:os'
|
|
|
+import { join } from 'node:path'
|
|
|
+import { describe, expect, it } from 'vitest'
|
|
|
+import { loadDesktopPackageEnvironment, validateDesktopPackageEnvironment } from '../scripts/desktop-package-environment.mjs'
|
|
|
+
|
|
|
+const WINDOWS = { platform: 'win32', arch: 'x64' } as const
|
|
|
+const MACOS = { platform: 'darwin', arch: 'arm64' } as const
|
|
|
+const RELEASE = { DSH_DESKTOP_APP_ID: 'com.example.desktop', DOWNLOAD_TEST_ORIGIN: 'https://updates.example.com' }
|
|
|
+const MAC_IDENTITY = { DSH_DESKTOP_MACOS_SIGNING_IDENTITY: 'Example Company (TEAMID1234)', DSH_DESKTOP_MACOS_TEAM_ID: 'TEAMID1234' }
|
|
|
+
|
|
|
+async function withDirectory(action: (directory: string) => Promise<void>): Promise<void> {
|
|
|
+ const directory = await mkdtemp(join(tmpdir(), 'desktop-env-'))
|
|
|
+ try {
|
|
|
+ await action(directory)
|
|
|
+ } finally {
|
|
|
+ await rm(directory, { recursive: true, force: true })
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+describe('Desktop local packaging configuration', () => {
|
|
|
+ it('selects the platform file, preserves literal secrets, and excludes stale ambient release settings', async () => {
|
|
|
+ await withDirectory(async (directory) => {
|
|
|
+ await writeFile(join(directory, '.env.windows'), '\uFEFFDSH_DESKTOP_APP_ID=com.example.windows\r\nDSH_DESKTOP_WINDOWS_TOKEN_PIN=" #!$%&literal "\r\nDSH_DESKTOP_WINDOWS_CER_FILE="keys/public certificate.cer"\r\n')
|
|
|
+ await writeFile(join(directory, '.env.macos'), 'DSH_DESKTOP_APP_ID=com.example.mac\nAPPLE_KEYCHAIN_PROFILE=release\n')
|
|
|
+ const parent = {
|
|
|
+ PATH: 'build-tools', DSH_DESKTOP_APP_ID: 'com.stale.desktop',
|
|
|
+ DSH_DESKTOP_WINDOWS_TOKEN_PIN: 'stale-pin', APPLE_ID: 'stale-apple-id',
|
|
|
+ CSC_LINK: 'stale-certificate', DOWNLOAD_TEST_ORIGIN: 'https://stale.example.com',
|
|
|
+ dsh_desktop_windows_key_container: 'case-insensitive-stale-container',
|
|
|
+ }
|
|
|
+ expect(loadDesktopPackageEnvironment('win32', parent, directory)).toEqual({
|
|
|
+ PATH: 'build-tools', DSH_DESKTOP_APP_ID: 'com.example.windows',
|
|
|
+ DSH_DESKTOP_WINDOWS_TOKEN_PIN: ' #!$%&literal ',
|
|
|
+ DSH_DESKTOP_WINDOWS_CER_FILE: join(directory, 'keys', 'public certificate.cer'),
|
|
|
+ })
|
|
|
+ expect(loadDesktopPackageEnvironment('darwin', parent, directory)).toEqual({
|
|
|
+ PATH: 'build-tools', DSH_DESKTOP_APP_ID: 'com.example.mac', APPLE_KEYCHAIN_PROFILE: 'release',
|
|
|
+ })
|
|
|
+ expect(parent.DSH_DESKTOP_WINDOWS_TOKEN_PIN).toBe('stale-pin')
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ it('requires the local file even when ambient configuration exists and rejects other-platform fields', async () => {
|
|
|
+ await withDirectory(async (directory) => {
|
|
|
+ expect(() => loadDesktopPackageEnvironment('win32', RELEASE, directory)).toThrow(/copy .*\.env.windows.example/u)
|
|
|
+ await writeFile(join(directory, '.env.windows'), 'APPLE_APP_SPECIFIC_PASSWORD=secret-sentinel\n')
|
|
|
+ expect(() => loadDesktopPackageEnvironment('win32', {}, directory)).toThrow(/unsupported setting APPLE_APP_SPECIFIC_PASSWORD/u)
|
|
|
+ expect(() => loadDesktopPackageEnvironment('win32', {}, directory)).not.toThrow(/secret-sentinel/u)
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ it('checks application and update configuration before Windows credentials while preserving unsigned and preparation modes', () => {
|
|
|
+ expect(() => {
|
|
|
+ validateDesktopPackageEnvironment({}, WINDOWS, { unsigned: true })
|
|
|
+ }).toThrow(/DSH_DESKTOP_APP_ID/u)
|
|
|
+ expect(() => {
|
|
|
+ validateDesktopPackageEnvironment({ DSH_DESKTOP_APP_ID: 'invalid' }, WINDOWS)
|
|
|
+ }).toThrow(/reverse-DNS/u)
|
|
|
+ expect(() => {
|
|
|
+ validateDesktopPackageEnvironment({ DSH_DESKTOP_APP_ID: RELEASE.DSH_DESKTOP_APP_ID }, WINDOWS)
|
|
|
+ }).toThrow(/DOWNLOAD_TEST_ORIGIN/u)
|
|
|
+ expect(() => {
|
|
|
+ validateDesktopPackageEnvironment(RELEASE, WINDOWS)
|
|
|
+ }).toThrow(/DSH_DESKTOP_WINDOWS_CER_FILE/u)
|
|
|
+ expect(() => {
|
|
|
+ validateDesktopPackageEnvironment({ DSH_DESKTOP_APP_ID: RELEASE.DSH_DESKTOP_APP_ID }, WINDOWS, { unsigned: true })
|
|
|
+ }).not.toThrow()
|
|
|
+ expect(() => {
|
|
|
+ validateDesktopPackageEnvironment({ DSH_DESKTOP_APP_ID: RELEASE.DSH_DESKTOP_APP_ID }, WINDOWS, { prepareOnly: true })
|
|
|
+ }).not.toThrow()
|
|
|
+ })
|
|
|
+
|
|
|
+ it('rejects incomplete macOS identity and credentials and checks referenced files without contacting Apple', async () => {
|
|
|
+ expect(() => {
|
|
|
+ validateDesktopPackageEnvironment(RELEASE, MACOS)
|
|
|
+ }).toThrow(/DSH_DESKTOP_MACOS_SIGNING_IDENTITY/u)
|
|
|
+ expect(() => {
|
|
|
+ validateDesktopPackageEnvironment({ ...RELEASE, ...MAC_IDENTITY }, MACOS)
|
|
|
+ }).toThrow(/macOS packaging requires/u)
|
|
|
+ expect(() => {
|
|
|
+ validateDesktopPackageEnvironment({ ...RELEASE, ...MAC_IDENTITY, APPLE_API_KEY: 'missing.p8' }, MACOS)
|
|
|
+ }).toThrow(/APPLE_API_KEY_ID/u)
|
|
|
+ expect(() => {
|
|
|
+ validateDesktopPackageEnvironment({ ...RELEASE, ...MAC_IDENTITY, APPLE_KEYCHAIN_PROFILE: 'release' }, MACOS)
|
|
|
+ }).not.toThrow()
|
|
|
+ expect(() => {
|
|
|
+ validateDesktopPackageEnvironment({ ...RELEASE, ...MAC_IDENTITY, APPLE_KEYCHAIN_PROFILE: 'release', APPLE_API_KEY: '' }, MACOS)
|
|
|
+ }).toThrow(/exactly one macOS notarization strategy/u)
|
|
|
+ expect(() => {
|
|
|
+ validateDesktopPackageEnvironment({ ...RELEASE, ...MAC_IDENTITY, APPLE_ID: 'user@example.com', APPLE_APP_SPECIFIC_PASSWORD: 'fixture', APPLE_TEAM_ID: 'TEAMID1234' }, MACOS)
|
|
|
+ }).not.toThrow()
|
|
|
+ await withDirectory(async (directory) => {
|
|
|
+ const appleApiKey = join(directory, 'AuthKey.p8')
|
|
|
+ const environment = { ...RELEASE, ...MAC_IDENTITY, APPLE_API_KEY: appleApiKey, APPLE_API_KEY_ID: 'TEST123456', APPLE_API_ISSUER: '11111111-2222-3333-4444-555555555555' }
|
|
|
+ expect(() => {
|
|
|
+ validateDesktopPackageEnvironment(environment, MACOS)
|
|
|
+ }).toThrow(/APPLE_API_KEY must identify a readable local file/u)
|
|
|
+ await writeFile(appleApiKey, 'local-file-fixture')
|
|
|
+ expect(() => {
|
|
|
+ validateDesktopPackageEnvironment(environment, MACOS)
|
|
|
+ }).not.toThrow()
|
|
|
+ expect(() => {
|
|
|
+ validateDesktopPackageEnvironment({ ...RELEASE, ...MAC_IDENTITY, APPLE_KEYCHAIN_PROFILE: 'release', APPLE_KEYCHAIN: directory }, MACOS)
|
|
|
+ }).toThrow(/APPLE_KEYCHAIN/u)
|
|
|
+ })
|
|
|
+ })
|
|
|
+})
|