UNPKG

6.69 kBJavaScriptView Raw
1/**
2 * @module util
3 */
4
5'use strict'
6
7const child = require('child_process')
8const fs = require('fs-extra')
9const path = require('path')
10
11const debug = require('debug')
12
13/**
14 * This callback is used across signing and flattening.
15 * @callback RequestCallback
16 * @param {?Error} err
17 */
18
19/** @function */
20const debuglog = module.exports.debuglog = debug('electron-osx-sign')
21debuglog.log = console.log.bind(console)
22
23/** @function */
24const debugwarn = module.exports.debugwarn = debug('electron-osx-sign:warn')
25debugwarn.log = console.warn.bind(console)
26
27/** @function */
28const removePassword = function (input) {
29 return input.replace(/(-P |pass:|\/p|-pass )([^ ]+)/, function (match, p1, p2) {
30 return `${p1}***`
31 })
32}
33
34/** @function */
35module.exports.execFileAsync = function (file, args, options) {
36 if (debuglog.enabled) {
37 debuglog('Executing...', file, args && Array.isArray(args) ? removePassword(args.join(' ')) : '')
38 }
39
40 return new Promise(function (resolve, reject) {
41 child.execFile(file, args, options, function (err, stdout, stderr) {
42 if (err) {
43 debuglog('Error executing file:', '\n',
44 '> Stdout:', stdout, '\n',
45 '> Stderr:', stderr)
46 reject(err)
47 return
48 }
49 resolve(stdout)
50 })
51 })
52}
53
54/**
55 * This function returns a flattened list of elements from an array of lists.
56 * @function
57 * @param {*} list - List.
58 * @returns Flattened list.
59 */
60module.exports.flatList = function (list) {
61 function populateResult(list) {
62 if (!Array.isArray(list)) {
63 result.push(list)
64 } else if (list.length > 0) {
65 for (let item of list) if (item) populateResult(item)
66 }
67 }
68
69 let result = []
70 populateResult(list)
71 return result
72}
73
74/**
75 * This function returns the path to app contents.
76 * @function
77 * @param {Object} opts - Options.
78 * @returns {string} App contents path.
79 */
80var getAppContentsPath = module.exports.getAppContentsPath = function (opts) {
81 return path.join(opts.app, 'Contents')
82}
83
84/**
85 * This function returns the path to app frameworks within contents.
86 * @function
87 * @param {Object} opts - Options.
88 * @returns {string} App frameworks path.
89 */
90var getAppFrameworksPath = module.exports.getAppFrameworksPath = function (opts) {
91 return path.join(getAppContentsPath(opts), 'Frameworks')
92}
93
94/**
95 * This function returns a promise copying a file from the source to the target.
96 * @function
97 * @param {string} source - Source path.
98 * @param {string} target - Target path.
99 * @returns {Promise} Promise.
100 */
101module.exports.copyFileAsync = function (source, target) {
102 debuglog('Copying file...', '\n',
103 '> Source:', source, '\n',
104 '> Target:', target)
105 return new Promise(function (resolve, reject) {
106 var readStream = fs.createReadStream(source)
107 readStream.on('error', reject)
108 var writeStream = fs.createWriteStream(target)
109 writeStream.on('error', reject)
110 writeStream.on('close', resolve)
111 readStream.pipe(writeStream)
112 })
113}
114
115/**
116 * This function returns a promise with platform resolved.
117 * @function
118 * @param {Object} opts - Options.
119 * @returns {Promise} Promise resolving platform.
120 */
121var detectElectronPlatformAsync = module.exports.detectElectronPlatformAsync = function (opts) {
122 return new Promise(function (resolve) {
123 var appFrameworksPath = getAppFrameworksPath(opts)
124 // The presence of Squirrel.framework identifies a Mac App Store build as used in https://github.com/atom/electron/blob/master/docs/tutorial/mac-app-store-submission-guide.md
125 return fs.lstat(path.join(appFrameworksPath, 'Squirrel.framework'))
126 .then(function () {
127 resolve('darwin')
128 })
129 .catch(function () {
130 resolve('mas')
131 })
132 })
133}
134
135const isBinaryFile = require("isbinaryfile").isBinaryFile;
136
137/**
138 * This function returns a promise resolving the file path if file binary.
139 * @function
140 * @param {string} filePath - Path to file.
141 * @returns {Promise} Promise resolving file path or undefined.
142 */
143const getFilePathIfBinaryAsync = module.exports.getFilePathIfBinaryAsync = function (filePath) {
144 return isBinaryFile(filePath)
145 .then(function (isBinary) {
146 return isBinary ? filePath : undefined
147 })
148}
149
150/**
151 * This function returns a promise validating opts.app, the application to be signed or flattened.
152 * @function
153 * @param {Object} opts - Options.
154 * @returns {Promise} Promise.
155 */
156module.exports.validateOptsAppAsync = async function (opts) {
157 if (!opts.app) {
158 throw new Error('Path to aplication must be specified.')
159 }
160 if (path.extname(opts.app) !== '.app') {
161 throw new Error('Extension of application must be `.app`.')
162 }
163 await fs.lstat(opts.app)
164}
165
166/**
167 * This function returns a promise validating opts.platform, the platform of Electron build. It allows auto-discovery if no opts.platform is specified.
168 * @function
169 * @param {Object} opts - Options.
170 * @returns {Promise} Promise.
171 */
172module.exports.validateOptsPlatformAsync = function (opts) {
173 if (opts.platform) {
174 if (opts.platform === 'mas' || opts.platform === 'darwin') {
175 return Promise.resolve()
176 } else {
177 debugwarn('`platform` passed in arguments not supported, checking Electron platform...')
178 }
179 } else {
180 debugwarn('No `platform` passed in arguments, checking Electron platform...')
181 }
182
183 return detectElectronPlatformAsync(opts)
184 .then(function (platform) {
185 opts.platform = platform
186 })
187}
188
189/**
190 * This function returns a promise resolving all child paths within the directory specified.
191 * @function
192 * @param {string} dirPath - Path to directory.
193 * @returns {Promise} Promise resolving child paths needing signing in order.
194 */
195module.exports.walkAsync = async function (dirPath) {
196 debuglog('Walking... ' + dirPath)
197
198 async function _walkAsync(dirPath) {
199 const names = await fs.readdir(dirPath)
200 return await Promise.all(names.map(async (name) => {
201 let filePath = path.join(dirPath, name)
202 const stat = await fs.lstat(filePath)
203 if (stat.isFile()) {
204 switch (path.extname(filePath)) {
205 case '.cstemp': // Temporary file generated from past codesign
206 debuglog('Removing... ' + filePath)
207 await fs.unlink(filePath)
208 return
209 default:
210 return getFilePathIfBinaryAsync(filePath)
211 }
212 } else if (stat.isDirectory() && !stat.isSymbolicLink()) {
213 const result = await _walkAsync(filePath)
214 switch (path.extname(filePath)) {
215 case '.app': // Application
216 case '.framework': // Framework
217 result.push(filePath)
218 }
219 return result
220 }
221 }))
222 }
223
224 return module.exports.flatList(await _walkAsync(dirPath))
225}