desktop-release-environment.mjs 4.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. /** Resolve public release identifiers supplied by the packaging environment. */
  2. /** Environment variable that supplies the Electron application identifier. */
  3. export const DESKTOP_APP_ID_ENV = 'DSH_DESKTOP_APP_ID'
  4. /** Environment variable that supplies electron-builder's macOS certificate qualifier. */
  5. export const MACOS_SIGNING_IDENTITY_ENV = 'DSH_DESKTOP_MACOS_SIGNING_IDENTITY'
  6. /** Environment variable that supplies the expected Apple Developer Team ID. */
  7. export const MACOS_TEAM_ID_ENV = 'DSH_DESKTOP_MACOS_TEAM_ID'
  8. const APPLE_API_KEY_ENV = 'APPLE_API_KEY'
  9. const APPLE_API_KEY_ID_ENV = 'APPLE_API_KEY_ID'
  10. const APPLE_API_ISSUER_ENV = 'APPLE_API_ISSUER'
  11. const APPLE_ID_ENV = 'APPLE_ID'
  12. const APPLE_APP_SPECIFIC_PASSWORD_ENV = 'APPLE_APP_SPECIFIC_PASSWORD'
  13. const APPLE_TEAM_ID_ENV = 'APPLE_TEAM_ID'
  14. const APPLE_KEYCHAIN_ENV = 'APPLE_KEYCHAIN'
  15. const APPLE_KEYCHAIN_PROFILE_ENV = 'APPLE_KEYCHAIN_PROFILE'
  16. /**
  17. * Read one required non-empty environment variable.
  18. * @param {NodeJS.ProcessEnv} env - Packaging environment.
  19. * @param {string} name - Required variable name.
  20. * @returns {string} Trimmed variable value.
  21. */
  22. function requireEnvironmentValue(env, name) {
  23. const value = env[name]?.trim()
  24. if (value === undefined || value === '') {
  25. throw new Error(`desktop release environment: ${name} must be set to a non-empty value`)
  26. }
  27. return value
  28. }
  29. /**
  30. * Resolve and validate the application identifier shared by every platform target.
  31. * @param {NodeJS.ProcessEnv} env - Packaging environment.
  32. * @returns {string} Reverse-DNS application identifier.
  33. */
  34. export function resolveDesktopAppId(env) {
  35. const appId = requireEnvironmentValue(env, DESKTOP_APP_ID_ENV)
  36. if (!/^[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)+$/u.test(appId)) {
  37. throw new Error(`desktop release environment: ${DESKTOP_APP_ID_ENV} must be a reverse-DNS identifier`)
  38. }
  39. return appId
  40. }
  41. /**
  42. * Resolve and validate the public identity expected on a macOS release.
  43. * @param {NodeJS.ProcessEnv} env - Packaging environment.
  44. * @returns {{ signingIdentity: string, teamId: string }} Expected certificate qualifier and Team ID.
  45. */
  46. export function resolveMacOSSigningEnvironment(env) {
  47. const signingIdentity = requireEnvironmentValue(env, MACOS_SIGNING_IDENTITY_ENV)
  48. if (signingIdentity.startsWith('Developer ID Application:')) {
  49. throw new Error(`desktop release environment: ${MACOS_SIGNING_IDENTITY_ENV} must omit the "Developer ID Application:" prefix`)
  50. }
  51. const teamId = requireEnvironmentValue(env, MACOS_TEAM_ID_ENV)
  52. if (!/^[A-Z0-9]{10}$/u.test(teamId)) {
  53. throw new Error(`desktop release environment: ${MACOS_TEAM_ID_ENV} must contain 10 uppercase letters or digits`)
  54. }
  55. return { signingIdentity, teamId }
  56. }
  57. /**
  58. * Resolve one complete credential set accepted by Apple's notary service.
  59. * @param {NodeJS.ProcessEnv} env - Packaging environment.
  60. * @returns {{ appleId: string, appleIdPassword: string, teamId: string } | { appleApiKey: string, appleApiKeyId: string, appleApiIssuer: string } | { keychainProfile: string, keychain?: string }} Notary credentials without the submitted artifact path.
  61. */
  62. export function resolveMacOSNotarizationEnvironment(env) {
  63. const appleIdValues = [env[APPLE_ID_ENV], env[APPLE_APP_SPECIFIC_PASSWORD_ENV], env[APPLE_TEAM_ID_ENV]]
  64. if (appleIdValues.some(value => value !== undefined)) {
  65. return {
  66. appleId: requireEnvironmentValue(env, APPLE_ID_ENV),
  67. appleIdPassword: requireEnvironmentValue(env, APPLE_APP_SPECIFIC_PASSWORD_ENV),
  68. teamId: requireEnvironmentValue(env, APPLE_TEAM_ID_ENV),
  69. }
  70. }
  71. const apiKeyValues = [env[APPLE_API_KEY_ENV], env[APPLE_API_KEY_ID_ENV], env[APPLE_API_ISSUER_ENV]]
  72. if (apiKeyValues.some(value => value !== undefined)) {
  73. return {
  74. appleApiKey: requireEnvironmentValue(env, APPLE_API_KEY_ENV),
  75. appleApiKeyId: requireEnvironmentValue(env, APPLE_API_KEY_ID_ENV),
  76. appleApiIssuer: requireEnvironmentValue(env, APPLE_API_ISSUER_ENV),
  77. }
  78. }
  79. const keychainProfile = env[APPLE_KEYCHAIN_PROFILE_ENV]?.trim()
  80. if (keychainProfile !== undefined && keychainProfile !== '') {
  81. const keychain = env[APPLE_KEYCHAIN_ENV]?.trim()
  82. return keychain === undefined || keychain === ''
  83. ? { keychainProfile }
  84. : { keychainProfile, keychain }
  85. }
  86. throw new Error('desktop release environment: macOS packaging requires APPLE_API_KEY, APPLE_API_KEY_ID, and APPLE_API_ISSUER; APPLE_ID, APPLE_APP_SPECIFIC_PASSWORD, and APPLE_TEAM_ID; or APPLE_KEYCHAIN_PROFILE')
  87. }