util-provisioning-profiles.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. /**
  2. * @module util-provisioning-profiles
  3. */
  4. 'use strict'
  5. const path = require('path')
  6. const Promise = require('bluebird')
  7. const plist = require('plist')
  8. const util = require('./util')
  9. const debuglog = util.debuglog
  10. const debugwarn = util.debugwarn
  11. const getAppContentsPath = util.getAppContentsPath
  12. const flatList = util.flatList
  13. const copyFileAsync = util.copyFileAsync
  14. const execFileAsync = util.execFileAsync
  15. const lstatAsync = util.lstatAsync
  16. const readdirAsync = util.readdirAsync
  17. /**
  18. * @constructor
  19. * @param {string} filePath - Path to provisioning profile.
  20. * @param {Object} message - Decoded message in provisioning profile.
  21. */
  22. var ProvisioningProfile = module.exports.ProvisioningProfile = function (filePath, message) {
  23. this.filePath = filePath
  24. this.message = message
  25. }
  26. Object.defineProperty(ProvisioningProfile.prototype, 'name', {
  27. get: function () {
  28. return this.message['Name']
  29. }
  30. })
  31. Object.defineProperty(ProvisioningProfile.prototype, 'platforms', {
  32. get: function () {
  33. if ('ProvisionsAllDevices' in this.message) return ['darwin'] // Developer ID
  34. else if (this.type === 'distribution') return ['mas'] // Mac App Store
  35. else return ['darwin', 'mas'] // Mac App Development
  36. }
  37. })
  38. Object.defineProperty(ProvisioningProfile.prototype, 'type', {
  39. get: function () {
  40. if ('ProvisionedDevices' in this.message) return 'development' // Mac App Development
  41. else return 'distribution' // Developer ID or Mac App Store
  42. }
  43. })
  44. /**
  45. * Returns a promise resolving to a ProvisioningProfile instance based on file.
  46. * @function
  47. * @param {string} filePath - Path to provisioning profile.
  48. * @returns {Promise} Promise.
  49. */
  50. var getProvisioningProfileAsync = module.exports.getProvisioningProfileAsync = function (filePath) {
  51. return execFileAsync('security', [
  52. 'cms',
  53. '-D', // Decode a CMS message
  54. '-i', filePath // Use infile as source of data
  55. ])
  56. .then(function (result) {
  57. var provisioningProfile = new ProvisioningProfile(filePath, plist.parse(result))
  58. debuglog('Provisioning profile:', '\n',
  59. '> Name:', provisioningProfile.name, '\n',
  60. '> Platforms:', provisioningProfile.platforms, '\n',
  61. '> Type:', provisioningProfile.type, '\n',
  62. '> Path:', provisioningProfile.filePath, '\n',
  63. '> Message:', provisioningProfile.message)
  64. return provisioningProfile
  65. })
  66. }
  67. /**
  68. * Returns a promise resolving to a list of suitable provisioning profile within the current working directory.
  69. * @function
  70. * @param {Object} opts - Options.
  71. * @returns {Promise} Promise.
  72. */
  73. var findProvisioningProfilesAsync = module.exports.findProvisioningProfilesAsync = function (opts) {
  74. return Promise.map([
  75. process.cwd() // Current working directory
  76. ], function (dirPath) {
  77. return readdirAsync(dirPath)
  78. .map(function (name) {
  79. var filePath = path.join(dirPath, name)
  80. return lstatAsync(filePath)
  81. .then(function (stat) {
  82. if (stat.isFile()) {
  83. switch (path.extname(filePath)) {
  84. case '.provisionprofile':
  85. return filePath
  86. }
  87. }
  88. return undefined
  89. })
  90. })
  91. })
  92. .then(flatList)
  93. .map(function (filePath) {
  94. return getProvisioningProfileAsync(filePath)
  95. .then(function (provisioningProfile) {
  96. if (provisioningProfile.platforms.indexOf(opts.platform) >= 0 && provisioningProfile.type === opts.type) return provisioningProfile
  97. debugwarn('Provisioning profile above ignored, not for ' + opts.platform + ' ' + opts.type + '.')
  98. return undefined
  99. })
  100. })
  101. .then(flatList)
  102. }
  103. /**
  104. * Returns a promise embedding the provisioning profile in the app Contents folder.
  105. * @function
  106. * @param {Object} opts - Options.
  107. * @returns {Promise} Promise.
  108. */
  109. module.exports.preEmbedProvisioningProfile = function (opts) {
  110. function embedProvisioningProfile () {
  111. if (opts['provisioning-profile']) {
  112. debuglog('Looking for existing provisioning profile...')
  113. var embeddedFilePath = path.join(getAppContentsPath(opts), 'embedded.provisionprofile')
  114. return lstatAsync(embeddedFilePath)
  115. .then(function (stat) {
  116. debuglog('Found embedded provisioning profile:', '\n',
  117. '* Please manually remove the existing file if not wanted.', '\n',
  118. '* Current file at:', embeddedFilePath)
  119. })
  120. .catch(function (err) {
  121. if (err.code === 'ENOENT') {
  122. // File does not exist
  123. debuglog('Embedding provisioning profile...')
  124. return copyFileAsync(opts['provisioning-profile'].filePath, embeddedFilePath)
  125. } else throw err
  126. })
  127. }
  128. }
  129. if (opts['provisioning-profile']) {
  130. // User input provisioning profile
  131. debuglog('`provisioning-profile` passed in arguments.')
  132. if (opts['provisioning-profile'] instanceof ProvisioningProfile) {
  133. return embedProvisioningProfile()
  134. } else {
  135. return getProvisioningProfileAsync(opts['provisioning-profile'])
  136. .then(function (provisioningProfile) {
  137. opts['provisioning-profile'] = provisioningProfile
  138. })
  139. .then(embedProvisioningProfile)
  140. }
  141. } else {
  142. // Discover provisioning profile
  143. debuglog('No `provisioning-profile` passed in arguments, will find in current working directory and in user library...')
  144. return findProvisioningProfilesAsync(opts)
  145. .then(function (provisioningProfiles) {
  146. if (provisioningProfiles.length > 0) {
  147. // Provisioning profile(s) found
  148. if (provisioningProfiles.length > 1) {
  149. debuglog('Multiple provisioning profiles found, will use the first discovered.')
  150. } else {
  151. debuglog('Found 1 provisioning profile.')
  152. }
  153. opts['provisioning-profile'] = provisioningProfiles[0]
  154. } else {
  155. // No provisioning profile found
  156. debuglog('No provisioning profile found, will not embed profile in app contents.')
  157. }
  158. })
  159. .then(embedProvisioningProfile)
  160. }
  161. }