11import { DmgOptions , MacPackager , PlatformPackager } from "app-builder-lib"
2+ import { downloadArtifact } from "app-builder-lib/out/binDownload"
23import { exec , executeFinally , exists , isEmptyOrSpaces , TmpDir } from "builder-util"
3- import * as path from "path"
4- import { hdiUtil , hdiutilTransientExitCodes } from "./hdiuil"
54import { writeFile } from "fs-extra"
5+ import * as path from "path"
66import { DmgBuildConfig } from "./dmg"
7+ import { hdiUtil , hdiutilTransientExitCodes } from "./hdiuil"
78
89export { DmgTarget } from "./dmg"
910
@@ -13,11 +14,30 @@ export function getDmgTemplatePath() {
1314 return path . join ( root , "templates" )
1415}
1516
16- export function getDmgVendorPath ( ) {
17- return path . join ( root , "vendor" )
17+ async function getDmgVendorPath ( ) : Promise < string > {
18+ const customDmgbuildPath = process . env . CUSTOM_DMGBUILD_PATH ?. trim ( )
19+ if ( customDmgbuildPath ) {
20+ return path . resolve ( customDmgbuildPath )
21+ }
22+
23+ // https://github.com/electron-userland/electron-builder-binaries/releases/tag/dmg-builder%401.1.0
24+ const releaseVersion = "9614277"
25+ const arch = process . arch === "arm64" ? "arm64" : "x86_64"
26+ const config = {
27+ "dmgbuild-bundle-arm64-9614277.tar.gz" : "28e11550cf990f78180a2d82090f35a24588beda3d9165098837714f90ee47ce" ,
28+ "dmgbuild-bundle-x86_64-9614277.tar.gz" : "4dbf1cc186af62921f8b6f4a5956b28d8622d211797a8b05eb75a260ee9c3fdb" ,
29+ }
30+ const filename : keyof typeof config = `dmgbuild-bundle-${ arch } -${ releaseVersion } .tar.gz`
31+ const file = await downloadArtifact ( {
32+ releaseName : "dmg-builder@1.1.0" ,
33+ filenameWithExt : filename ,
34+ checksums : config ,
35+ githubOrgRepo : "electron-userland/electron-builder-binaries" ,
36+ } )
37+ return path . resolve ( file , "dmgbuild" )
1838}
1939
20- export async function attachAndExecute ( dmgPath : string , readWrite : boolean , task : ( devicePath : string ) => Promise < any > ) {
40+ export async function attachAndExecute ( dmgPath : string , readWrite : boolean , forceDetach : boolean , task : ( devicePath : string ) => Promise < any > ) {
2141 //noinspection SpellCheckingInspection
2242 const args = [ "attach" , "-noverify" , "-noautoopen" ]
2343 if ( readWrite ) {
@@ -36,7 +56,7 @@ export async function attachAndExecute(dmgPath: string, readWrite: boolean, task
3656 throw new Error ( `Cannot find volume mount path for device: ${ device } ` )
3757 }
3858
39- return await executeFinally ( task ( volumePath ) , ( ) => detach ( device ) )
59+ return await executeFinally ( task ( volumePath ) , ( ) => detach ( device , forceDetach ) )
4060}
4161
4262/**
@@ -58,9 +78,9 @@ async function findMountPath(devName: string, index: number = 1): Promise<string
5878 return matches . length >= index ? matches [ index - 1 ] : null
5979}
6080
61- export async function detach ( name : string ) {
81+ export async function detach ( name : string , alwaysForce : boolean ) {
6282 return hdiUtil ( [ "detach" , "-quiet" , name ] ) . catch ( async e => {
63- if ( hdiutilTransientExitCodes . has ( e . code ) ) {
83+ if ( hdiutilTransientExitCodes . has ( e . code ) || alwaysForce ) {
6484 // Delay then force unmount with verbose output
6585 await new Promise ( resolve => setTimeout ( resolve , 3000 ) )
6686 return hdiUtil ( [ "detach" , "-force" , name ] )
@@ -105,12 +125,9 @@ export async function customizeDmg({ appPath, artifactPath, volumeName, specific
105125 const iconTextSize = isValidIconTextSize ? specification . iconTextSize : 12
106126 const volumePath = path . join ( "/Volumes" , volumeName )
107127 // https://github.com/electron-userland/electron-builder/issues/2115
108- const backgroundFile = specification . background == null ? null : await transformBackgroundFileIfNeed ( specification . background , packager . info . tempDirManager )
109128
110129 const settings : DmgBuildConfig = {
111130 title : path . basename ( volumePath ) ,
112- icon : await packager . getResource ( specification . icon ) ,
113- "badge-icon" : await packager . getResource ( specification . badgeIcon ) ,
114131 "icon-size" : specification . iconSize ,
115132 "text-size" : iconTextSize ,
116133
@@ -128,6 +145,16 @@ export async function customizeDmg({ appPath, artifactPath, volumeName, specific
128145 } ) ) || [ ] ,
129146 }
130147
148+ if ( specification . badgeIcon ) {
149+ let badgeIcon = await packager . getResource ( specification . badgeIcon )
150+ if ( badgeIcon && badgeIcon . toLowerCase ( ) . endsWith ( ".icon" ) ) {
151+ badgeIcon = await packager . generateIcnsFromIcon ( badgeIcon )
152+ }
153+ settings [ "badge-icon" ] = badgeIcon
154+ } else {
155+ settings . icon = await packager . getResource ( specification . icon )
156+ }
157+
131158 if ( specification . backgroundColor != null || specification . background == null ) {
132159 settings [ "background-color" ] = specification . backgroundColor || "#ffffff"
133160
@@ -145,8 +172,7 @@ export async function customizeDmg({ appPath, artifactPath, volumeName, specific
145172 }
146173 }
147174 } else {
148- settings . background = backgroundFile
149- delete settings [ "background-color" ]
175+ settings . background = specification . background == null ? null : await transformBackgroundFileIfNeed ( specification . background , packager . info . tempDirManager )
150176 }
151177
152178 if ( ! isEmptyOrSpaces ( settings . background ) ) {
@@ -157,15 +183,8 @@ export async function customizeDmg({ appPath, artifactPath, volumeName, specific
157183 const settingsFile = await packager . getTempFile ( ".json" )
158184 await writeFile ( settingsFile , JSON . stringify ( settings , null , 2 ) )
159185
160- const python3Check = ( ) => exec ( "command" , [ "-v" , "python3" ] )
161- const pythonCheck = ( ) => exec ( "command" , [ "-v" , "python" ] )
162- const pythonPath = process . env . PYTHON_PATH || ( await python3Check ( ) . catch ( pythonCheck ) ) || ( await pythonCheck ( ) )
163- if ( pythonPath == null || isEmptyOrSpaces ( pythonPath . trim ( ) ) ) {
164- throw new Error ( "Cannot find 'python' or 'python3' executable, please ensure Python is installed and available in PATH or set PYTHON_PATH environment variable" )
165- }
166- const vendorDir = getDmgVendorPath ( )
167- await exec ( pythonPath . trim ( ) , [ path . join ( vendorDir , "run_dmgbuild.py" ) , "-s" , settingsFile , path . basename ( volumePath ) , artifactPath ] , {
168- cwd : vendorDir ,
186+ const dmgbuild = await getDmgVendorPath ( )
187+ await exec ( dmgbuild , [ "-s" , settingsFile , path . basename ( volumePath ) , artifactPath ] , {
169188 env : {
170189 ...process . env ,
171190 PYTHONIOENCODING : "utf8" ,
@@ -175,7 +194,7 @@ export async function customizeDmg({ appPath, artifactPath, volumeName, specific
175194 // effectiveOptionComputed, when present, is purely for verifying result during test execution
176195 return (
177196 packager . packagerOptions . effectiveOptionComputed == null ||
178- ( await attachAndExecute ( artifactPath , false , async volumePath => {
197+ ( await attachAndExecute ( artifactPath , false , true , async volumePath => {
179198 return ! ( await packager . packagerOptions . effectiveOptionComputed ! ( {
180199 volumePath,
181200 specification : {
0 commit comments