713 lines
22 KiB
JavaScript
713 lines
22 KiB
JavaScript
const builder = require("electron-builder")
|
|
const { execFileSync } = require("child_process")
|
|
const fs = require("fs")
|
|
const path = require("path")
|
|
const packageMetadata = require("./package.json")
|
|
const {
|
|
normalizeBundledMacChromiumForPackaging,
|
|
} = require("./scripts/prepare-export-chromium.cjs")
|
|
|
|
const APP_ID = "com.presenton.presenton"
|
|
const TEAM_ID = "S6W5C54KL6"
|
|
const macTarget = process.env.PRESENTON_MAC_TARGET
|
|
const isMasBuild = macTarget === "mas" || macTarget === "mas-dev"
|
|
const isDirectMacBuild = !isMasBuild && (process.platform === "darwin" || !!macTarget)
|
|
const requireDirectMacSigning = process.env.PRESENTON_REQUIRE_MAC_SIGNING === "1"
|
|
const masDevProvisioningProfile = resolveProvisioningProfileForTarget({
|
|
target: "mas-dev",
|
|
label: "MAS development",
|
|
candidates: [
|
|
"build/AppleDevelopment.provisionprofile",
|
|
"build/AppleDev.provisionprofile",
|
|
"build/AppDev.provisionprofile",
|
|
],
|
|
})
|
|
const masProvisioningProfile = resolveProvisioningProfileForTarget({
|
|
target: "mas",
|
|
label: "Mac App Store distribution",
|
|
candidates: [
|
|
"build/MacAppStore.provisionprofile",
|
|
"build/AppDistri.provisionprofile",
|
|
],
|
|
})
|
|
const masDevIdentity =
|
|
process.env.PRESENTON_MAS_DEV_IDENTITY || process.env.CSC_NAME || ""
|
|
const masSigningIdentities = resolveMasSigningIdentitiesForTarget()
|
|
const masIdentityQualifier = masSigningIdentities.qualifier
|
|
const masAppSigningIdentity = masSigningIdentities.appIdentity
|
|
const masInstallerSigningIdentity = masSigningIdentities.installerIdentity
|
|
const appStoreBundleShortVersion =
|
|
macTarget === "mas" ? getAppStoreBundleShortVersion() : undefined
|
|
const appStoreBundleVersion =
|
|
macTarget === "mas"
|
|
? getAppStoreBundleVersion(appStoreBundleShortVersion)
|
|
: undefined
|
|
const macDistributionIdentity =
|
|
process.env.PRESENTON_MAC_SIGN_IDENTITY ||
|
|
process.env.CSC_NAME ||
|
|
undefined
|
|
const shouldNotarizeDirectMacBuild =
|
|
isDirectMacBuild && process.env.PRESENTON_SKIP_NOTARIZATION !== "1"
|
|
const masSigningExtraArgs =
|
|
process.env.PRESENTON_CODESIGN_TIMESTAMP === "1" ? [] : ["--timestamp=none"]
|
|
|
|
function getAppStoreBundleShortVersion() {
|
|
const configuredVersion = process.env.PRESENTON_APP_STORE_VERSION
|
|
if (configuredVersion) {
|
|
validateAppStoreVersion(configuredVersion, "PRESENTON_APP_STORE_VERSION")
|
|
return configuredVersion
|
|
}
|
|
|
|
const match = /^(\d+)\.(\d+)\.(\d+)/.exec(packageMetadata.version)
|
|
if (!match) {
|
|
throw new Error(
|
|
`Cannot derive an App Store version from package version "${packageMetadata.version}". Set PRESENTON_APP_STORE_VERSION to three period-separated integers, for example 1.0.0.`
|
|
)
|
|
}
|
|
return `${match[1]}.${match[2]}.${match[3]}`
|
|
}
|
|
|
|
function getAppStoreBundleVersion(bundleShortVersion) {
|
|
const configuredBuild = process.env.PRESENTON_APP_STORE_BUILD
|
|
if (configuredBuild) {
|
|
validateAppStoreBuild(configuredBuild, "PRESENTON_APP_STORE_BUILD")
|
|
return configuredBuild
|
|
}
|
|
return bundleShortVersion
|
|
}
|
|
|
|
function validateAppStoreVersion(value, source) {
|
|
if (!/^\d+\.\d+\.\d+$/.test(value)) {
|
|
throw new Error(
|
|
`${source} must be three period-separated integers, for example 1.0.0. Received: ${value}`
|
|
)
|
|
}
|
|
}
|
|
|
|
function validateAppStoreBuild(value, source) {
|
|
if (!/^\d+(\.\d+){0,2}$/.test(value)) {
|
|
throw new Error(
|
|
`${source} must be one to three period-separated integers, for example 42 or 1.0.1. Received: ${value}`
|
|
)
|
|
}
|
|
}
|
|
|
|
function resolveProvisioningProfileForTarget({ target, label, candidates }) {
|
|
try {
|
|
return resolveProvisioningProfile({ target, label, candidates })
|
|
} catch (error) {
|
|
console.error(`\n${error.message}\n`)
|
|
process.exit(1)
|
|
}
|
|
}
|
|
|
|
function resolveProvisioningProfile({ target, label, candidates }) {
|
|
if (macTarget !== target) {
|
|
return undefined
|
|
}
|
|
|
|
if (process.platform !== "darwin") {
|
|
throw new Error(
|
|
`${label} builds must be run on macOS because Apple signing tools are required.`
|
|
)
|
|
}
|
|
|
|
const undecodableProfiles = []
|
|
|
|
for (const candidate of candidates) {
|
|
const candidatePath = path.join(__dirname, candidate)
|
|
if (!fs.existsSync(candidatePath)) {
|
|
continue
|
|
}
|
|
|
|
if (canDecodeProvisioningProfile(candidatePath)) {
|
|
return candidate
|
|
}
|
|
|
|
undecodableProfiles.push(candidate)
|
|
}
|
|
|
|
if (undecodableProfiles.length > 0) {
|
|
throw new Error(
|
|
`Found ${label} provisioning profile, but macOS could not decode it: ${undecodableProfiles.join(", ")}. Re-download the matching Apple provisioning profile and replace the local file.`
|
|
)
|
|
}
|
|
|
|
throw new Error(
|
|
`Missing ${label} provisioning profile. Expected: ${candidates.join(", ")}`
|
|
)
|
|
}
|
|
|
|
function canDecodeProvisioningProfile(profilePath) {
|
|
if (commandSucceeds("security", ["cms", "-D", "-i", profilePath])) {
|
|
return true
|
|
}
|
|
|
|
if (
|
|
commandSucceeds("openssl", [
|
|
"cms",
|
|
"-inform",
|
|
"DER",
|
|
"-verify",
|
|
"-noverify",
|
|
"-in",
|
|
profilePath,
|
|
"-out",
|
|
"/dev/null",
|
|
])
|
|
) {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
function commandSucceeds(command, args) {
|
|
try {
|
|
execFileSync(command, args, { stdio: "ignore" })
|
|
return true
|
|
} catch {
|
|
return false
|
|
}
|
|
}
|
|
|
|
function resolveMasSigningIdentitiesForTarget() {
|
|
if (macTarget !== "mas") {
|
|
return {
|
|
qualifier: "",
|
|
appIdentity: "",
|
|
installerIdentity: "",
|
|
}
|
|
}
|
|
|
|
try {
|
|
return resolveMasSigningIdentities()
|
|
} catch (error) {
|
|
console.error(`\n${error.message}\n`)
|
|
process.exit(1)
|
|
}
|
|
}
|
|
|
|
function resolveMasSigningIdentities() {
|
|
const identities = getAppleSigningIdentities()
|
|
const explicitIdentity =
|
|
process.env.PRESENTON_MAS_DISTRIBUTION_IDENTITY ||
|
|
process.env.PRESENTON_MAS_IDENTITY
|
|
|
|
const qualifier = explicitIdentity
|
|
? validateMasIdentityQualifier(
|
|
explicitIdentity,
|
|
"PRESENTON_MAS_DISTRIBUTION_IDENTITY/PRESENTON_MAS_IDENTITY",
|
|
identities
|
|
)
|
|
: process.env.CSC_NAME
|
|
? validateMasIdentityQualifier(
|
|
String(process.env.CSC_NAME),
|
|
"CSC_NAME",
|
|
identities
|
|
)
|
|
: discoverMasIdentityQualifier(identities)
|
|
|
|
const appIdentity = identities.find((identity) =>
|
|
isMasAppIdentityName(identity.name) && identity.name.includes(qualifier)
|
|
)
|
|
const installerIdentity = identities.find((identity) =>
|
|
isMasInstallerIdentityName(identity.name) && identity.name.includes(qualifier)
|
|
)
|
|
if (!appIdentity || !installerIdentity) {
|
|
throw buildMissingMasIdentityError(qualifier, identities, {
|
|
appIdentityFound: !!appIdentity,
|
|
installerIdentityFound: !!installerIdentity,
|
|
})
|
|
}
|
|
|
|
return {
|
|
qualifier,
|
|
appIdentity: appIdentity.name,
|
|
installerIdentity: installerIdentity.name,
|
|
}
|
|
}
|
|
|
|
function validateMasIdentityQualifier(value, source, identities) {
|
|
const identity = value.trim()
|
|
if (!identity) {
|
|
throw new Error(`${source} is set but empty.`)
|
|
}
|
|
|
|
const matchedIdentity = identities.find(
|
|
(candidate) => candidate.hash === identity || candidate.name === identity
|
|
)
|
|
const identityName = matchedIdentity?.name || identity
|
|
if (isDevelopmentIdentityName(identityName)) {
|
|
throw new Error(
|
|
`${source} points to a development identity (${identityName}). MAS distribution builds require Apple Distribution or 3rd Party Mac Developer Application.`
|
|
)
|
|
}
|
|
if (isMasAppIdentityName(identityName) || isMasInstallerIdentityName(identityName)) {
|
|
return getMasIdentityQualifier(identityName)
|
|
}
|
|
if (/^[A-Fa-f0-9]{40}$/.test(identity) && !matchedIdentity) {
|
|
throw new Error(
|
|
`${source} is a certificate hash that was not found in the keychain, so the matching MAS installer certificate cannot be discovered. Use the certificate name or team qualifier instead.`
|
|
)
|
|
}
|
|
|
|
return identity
|
|
}
|
|
|
|
function discoverMasIdentityQualifier(identities) {
|
|
const appIdentity = identities.find((identity) => isMasAppIdentityName(identity.name))
|
|
if (appIdentity) {
|
|
return getMasIdentityQualifier(appIdentity.name)
|
|
}
|
|
|
|
throw buildMissingMasIdentityError("", identities, {
|
|
appIdentityFound: false,
|
|
installerIdentityFound: identities.some((identity) =>
|
|
isMasInstallerIdentityName(identity.name)
|
|
),
|
|
})
|
|
}
|
|
|
|
function getAppleSigningIdentities() {
|
|
const outputs = []
|
|
for (const args of [
|
|
["find-identity", "-v"],
|
|
["find-identity", "-v", "-p", "codesigning"],
|
|
]) {
|
|
try {
|
|
outputs.push(execFileSync("security", args, { encoding: "utf8" }))
|
|
} catch {
|
|
continue
|
|
}
|
|
}
|
|
|
|
const seen = new Set()
|
|
return outputs
|
|
.join("\n")
|
|
.split(/\r?\n/)
|
|
.map((line) => {
|
|
const match = line.match(/^\s*\d+\)\s+([A-Fa-f0-9]{40})\s+"([^"]+)"/)
|
|
return match ? { hash: match[1], name: match[2] } : undefined
|
|
})
|
|
.filter(Boolean)
|
|
.filter((identity) => {
|
|
if (seen.has(identity.name)) {
|
|
return false
|
|
}
|
|
seen.add(identity.name)
|
|
return true
|
|
})
|
|
}
|
|
|
|
function isMasAppIdentityName(name) {
|
|
return /^(Apple Distribution|3rd Party Mac Developer Application):/.test(name)
|
|
}
|
|
|
|
function isMasInstallerIdentityName(name) {
|
|
return /^3rd Party Mac Developer Installer:/.test(name)
|
|
}
|
|
|
|
function isDevelopmentIdentityName(name) {
|
|
return /^(Apple Development|Mac Developer):/.test(name)
|
|
}
|
|
|
|
function isDeveloperIdApplicationIdentityName(name) {
|
|
return /^Developer ID Application:/.test(name)
|
|
}
|
|
|
|
function getMasIdentityQualifier(identityName) {
|
|
return identityName.replace(
|
|
/^(Apple Distribution|3rd Party Mac Developer Application|3rd Party Mac Developer Installer):\s*/,
|
|
""
|
|
)
|
|
}
|
|
|
|
function buildMissingMasIdentityError(qualifier, identities, status) {
|
|
const availableIdentities = identities.length
|
|
? identities.map((identity) => ` - ${identity.name}`).join("\n")
|
|
: " (no valid Apple signing identities found)"
|
|
const expectedQualifier = qualifier ? ` matching "${qualifier}"` : ""
|
|
return new Error(
|
|
[
|
|
"Missing MAS signing identity.",
|
|
`MAS distribution builds require both an app certificate and an installer certificate${expectedQualifier}:`,
|
|
" - Apple Distribution or 3rd Party Mac Developer Application",
|
|
" - 3rd Party Mac Developer Installer",
|
|
"",
|
|
`App certificate found: ${status.appIdentityFound ? "yes" : "no"}`,
|
|
`Installer certificate found: ${status.installerIdentityFound ? "yes" : "no"}`,
|
|
"",
|
|
"Create/download the missing certificate from Apple Developer Certificates,",
|
|
"install it in Keychain Access, then rerun the build.",
|
|
"",
|
|
"Available Apple signing identities:",
|
|
availableIdentities,
|
|
].join("\n")
|
|
)
|
|
}
|
|
|
|
function assertCodesignCanUseIdentity(identity) {
|
|
if (!identity || macTarget !== "mas" || process.platform !== "darwin") {
|
|
return
|
|
}
|
|
|
|
const tempDir = fs.mkdtempSync(path.join(require("os").tmpdir(), "presenton-codesign-"))
|
|
const tempFile = path.join(tempDir, "preflight")
|
|
try {
|
|
fs.writeFileSync(tempFile, "Presenton MAS signing preflight\n")
|
|
execFileSync(
|
|
"codesign",
|
|
["--force", "--sign", identity, "--timestamp=none", tempFile],
|
|
{ stdio: "ignore", timeout: 30000 }
|
|
)
|
|
} catch (error) {
|
|
const timedOut = error && error.signal === "SIGTERM"
|
|
throw new Error(
|
|
[
|
|
"Could not complete MAS signing preflight with the selected distribution identity.",
|
|
timedOut
|
|
? "codesign timed out, usually because macOS is waiting for Keychain/private-key access."
|
|
: "codesign failed before electron-builder started signing the app.",
|
|
"Unlock the login keychain and allow codesign access to the Apple Distribution private key, then rerun the build.",
|
|
`Identity: ${identity}`,
|
|
].join("\n")
|
|
)
|
|
} finally {
|
|
fs.rmSync(tempDir, { recursive: true, force: true })
|
|
}
|
|
}
|
|
|
|
function assertDirectMacReleaseReadiness() {
|
|
if (!isDirectMacBuild || !requireDirectMacSigning) {
|
|
return
|
|
}
|
|
|
|
if (process.platform !== "darwin") {
|
|
throw new Error(
|
|
"Signed macOS distribution builds must be run on macOS because Apple codesign and notarytool are required."
|
|
)
|
|
}
|
|
|
|
const identity = resolveDeveloperIdApplicationIdentity()
|
|
assertDirectMacNotarizationCredentials()
|
|
assertCodesignCanUseDirectIdentity(identity)
|
|
}
|
|
|
|
function resolveDeveloperIdApplicationIdentity() {
|
|
const identities = getAppleSigningIdentities()
|
|
const configuredIdentity = macDistributionIdentity
|
|
|
|
if (configuredIdentity) {
|
|
const matchedIdentity = identities.find(
|
|
(candidate) =>
|
|
candidate.hash === configuredIdentity || candidate.name === configuredIdentity
|
|
)
|
|
const identityName = matchedIdentity?.name || configuredIdentity
|
|
|
|
if (!matchedIdentity) {
|
|
throw new Error(
|
|
[
|
|
"The configured macOS signing identity was not found in the keychain.",
|
|
"Direct distribution requires a Developer ID Application certificate.",
|
|
`Configured identity: ${configuredIdentity}`,
|
|
"",
|
|
buildAvailableDeveloperIdIdentityList(identities),
|
|
].join("\n")
|
|
)
|
|
}
|
|
|
|
if (!isDeveloperIdApplicationIdentityName(identityName)) {
|
|
throw new Error(
|
|
[
|
|
"The configured macOS signing identity is not valid for direct distribution.",
|
|
"Use a Developer ID Application certificate, not Apple Development, Apple Distribution, or a Mac App Store certificate.",
|
|
`Configured identity: ${identityName}`,
|
|
].join("\n")
|
|
)
|
|
}
|
|
|
|
return identityName
|
|
}
|
|
|
|
const discoveredIdentity = identities.find((identity) =>
|
|
isDeveloperIdApplicationIdentityName(identity.name)
|
|
)
|
|
if (!discoveredIdentity) {
|
|
throw new Error(
|
|
[
|
|
"Missing Developer ID Application signing identity.",
|
|
"Install the certificate in Keychain Access or set PRESENTON_MAC_SIGN_IDENTITY to its exact name.",
|
|
"",
|
|
buildAvailableDeveloperIdIdentityList(identities),
|
|
].join("\n")
|
|
)
|
|
}
|
|
|
|
return discoveredIdentity.name
|
|
}
|
|
|
|
function buildAvailableDeveloperIdIdentityList(identities) {
|
|
const available = identities.length
|
|
? identities.map((identity) => ` - ${identity.name}`).join("\n")
|
|
: " (no valid Apple signing identities found)"
|
|
|
|
return ["Available Apple signing identities:", available].join("\n")
|
|
}
|
|
|
|
function assertDirectMacNotarizationCredentials() {
|
|
if (!shouldNotarizeDirectMacBuild) {
|
|
throw new Error(
|
|
"PRESENTON_SKIP_NOTARIZATION=1 is not allowed when PRESENTON_REQUIRE_MAC_SIGNING=1."
|
|
)
|
|
}
|
|
|
|
const hasAppleIdCredentials =
|
|
!!process.env.APPLE_ID &&
|
|
!!process.env.APPLE_APP_SPECIFIC_PASSWORD &&
|
|
!!process.env.APPLE_TEAM_ID
|
|
const hasApiKeyCredentials =
|
|
!!process.env.APPLE_API_KEY &&
|
|
!!process.env.APPLE_API_KEY_ID &&
|
|
!!process.env.APPLE_API_ISSUER
|
|
const hasKeychainProfile = !!process.env.APPLE_KEYCHAIN_PROFILE
|
|
|
|
if (hasAppleIdCredentials || hasApiKeyCredentials || hasKeychainProfile) {
|
|
return
|
|
}
|
|
|
|
throw new Error(
|
|
[
|
|
"Missing Apple notarization credentials.",
|
|
"Set one of these credential groups before running the signed macOS release build:",
|
|
" - APPLE_ID, APPLE_APP_SPECIFIC_PASSWORD, APPLE_TEAM_ID",
|
|
" - APPLE_API_KEY, APPLE_API_KEY_ID, APPLE_API_ISSUER",
|
|
" - APPLE_KEYCHAIN_PROFILE, optionally APPLE_KEYCHAIN",
|
|
].join("\n")
|
|
)
|
|
}
|
|
|
|
function assertCodesignCanUseDirectIdentity(identity) {
|
|
const tempDir = fs.mkdtempSync(path.join(require("os").tmpdir(), "presenton-codesign-"))
|
|
const tempFile = path.join(tempDir, "preflight")
|
|
try {
|
|
fs.writeFileSync(tempFile, "Presenton direct macOS signing preflight\n")
|
|
execFileSync(
|
|
"codesign",
|
|
["--force", "--sign", identity, "--options", "runtime", tempFile],
|
|
{ stdio: "ignore", timeout: 30000 }
|
|
)
|
|
} catch (error) {
|
|
const timedOut = error && error.signal === "SIGTERM"
|
|
throw new Error(
|
|
[
|
|
"Could not complete direct macOS signing preflight with the selected Developer ID identity.",
|
|
timedOut
|
|
? "codesign timed out, usually because macOS is waiting for Keychain/private-key access."
|
|
: "codesign failed before electron-builder started signing the app.",
|
|
"Unlock the login keychain and allow codesign access to the Developer ID private key, then rerun the build.",
|
|
`Identity: ${identity}`,
|
|
].join("\n")
|
|
)
|
|
} finally {
|
|
fs.rmSync(tempDir, { recursive: true, force: true })
|
|
}
|
|
}
|
|
|
|
// AfterPack hook: set executable permissions and repair packaged runtime bundles on macOS.
|
|
const afterPack = async (context) => {
|
|
if (context.electronPlatformName === "darwin") {
|
|
const appPath = context.appOutDir
|
|
const appBundleName = `${context.packager.appInfo.productFilename}.app`
|
|
const resourcesRoot = resolvePackagedResourcesRoot(
|
|
appPath,
|
|
appBundleName
|
|
)
|
|
const fastapiPath = path.join(resourcesRoot, "fastapi", "fastapi")
|
|
const exportPyDir = path.join(resourcesRoot, "export", "py")
|
|
const converterCandidates = [
|
|
`convert-${process.platform}-${process.arch}`,
|
|
`convert-${process.platform}`,
|
|
"convert",
|
|
]
|
|
|
|
console.log("Setting executable permissions for FastAPI binary...")
|
|
console.log("FastAPI path:", fastapiPath)
|
|
|
|
if (fs.existsSync(fastapiPath)) {
|
|
fs.chmodSync(fastapiPath, 0o755)
|
|
console.log("✓ Execute permissions set for FastAPI")
|
|
} else {
|
|
console.warn("⚠ FastAPI binary not found at:", fastapiPath)
|
|
}
|
|
|
|
console.log("Setting executable permissions for export converter binary...")
|
|
let converterFound = false
|
|
for (const candidate of converterCandidates) {
|
|
const candidatePath = path.join(exportPyDir, candidate)
|
|
if (fs.existsSync(candidatePath)) {
|
|
fs.chmodSync(candidatePath, 0o755)
|
|
console.log("✓ Execute permissions set for converter:", candidatePath)
|
|
converterFound = true
|
|
}
|
|
}
|
|
if (!converterFound) {
|
|
console.warn("⚠ No converter binary found in:", exportPyDir)
|
|
}
|
|
|
|
const fastapiDir = path.join(resourcesRoot, "fastapi")
|
|
if (fs.existsSync(fastapiDir)) {
|
|
console.log("FastAPI directory contents:", fs.readdirSync(fastapiDir))
|
|
}
|
|
|
|
if (fs.existsSync(exportPyDir)) {
|
|
console.log("Export py directory contents:", fs.readdirSync(exportPyDir))
|
|
}
|
|
|
|
normalizeBundledMacChromiumForPackaging(resourcesRoot)
|
|
}
|
|
}
|
|
|
|
function resolvePackagedResourcesRoot(appPath, appBundleName) {
|
|
const contentsResourcesRoot = path.join(appPath, appBundleName, "Contents", "Resources")
|
|
const candidates = [
|
|
path.join(contentsResourcesRoot, "app.asar.unpacked", "resources"),
|
|
path.join(contentsResourcesRoot, "app", "resources"),
|
|
]
|
|
return candidates.find((candidate) => fs.existsSync(candidate)) || candidates[0]
|
|
}
|
|
|
|
const config = {
|
|
appId: APP_ID,
|
|
productName: "Presenton",
|
|
asar: true,
|
|
asarUnpack: [
|
|
"resources/**",
|
|
// LiteParse runs from FastAPI via Electron-as-Node and needs real package dirs.
|
|
"node_modules/**",
|
|
],
|
|
copyright: "Copyright © 2026 Presenton",
|
|
directories: {
|
|
output: "dist",
|
|
buildResources: "build",
|
|
},
|
|
files: [
|
|
"resources",
|
|
"app_dist",
|
|
"node_modules",
|
|
"NOTICE"
|
|
],
|
|
afterPack,
|
|
mac: {
|
|
artifactName: "Presenton-${version}.${ext}",
|
|
target: [macTarget || "dmg"],
|
|
category: "public.app-category.productivity",
|
|
hardenedRuntime: !isMasBuild,
|
|
gatekeeperAssess: false,
|
|
entitlements: isMasBuild ? undefined : "build/entitlements.mac.plist",
|
|
entitlementsInherit: isMasBuild ? undefined : "build/entitlements.mac.inherit.plist",
|
|
identity:
|
|
macTarget === "mas" || macTarget === "mas-dev"
|
|
? null
|
|
: macDistributionIdentity,
|
|
notarize: isMasBuild || !shouldNotarizeDirectMacBuild ? false : true,
|
|
icon: "build/icon.icns",
|
|
bundleShortVersion: appStoreBundleShortVersion,
|
|
bundleVersion: appStoreBundleVersion,
|
|
extendInfo: {
|
|
ElectronTeamID: TEAM_ID,
|
|
},
|
|
},
|
|
masDev: {
|
|
type: "development",
|
|
identity: masDevIdentity || undefined,
|
|
provisioningProfile: masDevProvisioningProfile,
|
|
entitlements: "build/entitlements.mas.plist",
|
|
entitlementsInherit: "build/entitlements.mas.inherit.plist",
|
|
// osx-sign always adds --timestamp; this later flag keeps local MAS dev signing offline-tolerant.
|
|
additionalArguments: ["--timestamp=none"],
|
|
},
|
|
mas: {
|
|
type: "distribution",
|
|
identity: masIdentityQualifier || undefined,
|
|
provisioningProfile: masProvisioningProfile,
|
|
entitlements: "build/entitlements.mas.plist",
|
|
entitlementsInherit: "build/entitlements.mas.inherit.plist",
|
|
// Avoid codesign hanging on Apple's timestamp service during local MAS packaging.
|
|
additionalArguments: masSigningExtraArgs,
|
|
},
|
|
linux: {
|
|
artifactName: "Presenton-${version}.${ext}",
|
|
target: ["AppImage", "deb"],
|
|
icon: "build/icons",
|
|
},
|
|
deb: {
|
|
afterInstall: "build/after-install.tpl",
|
|
},
|
|
win: {
|
|
target: ["nsis", "appx"],
|
|
icon: "build/icon.ico",
|
|
artifactName: "Presenton-${version}.${ext}",
|
|
executableName: "Presenton",
|
|
},
|
|
nsis: {
|
|
oneClick: false,
|
|
perMachine: false,
|
|
allowToChangeInstallationDirectory: true,
|
|
allowElevation: true,
|
|
installerIcon: "build/icon.ico",
|
|
uninstallerIcon: "build/icon.ico",
|
|
installerHeaderIcon: "build/icon.ico",
|
|
createDesktopShortcut: true,
|
|
createStartMenuShortcut: true,
|
|
shortcutName: "Presenton",
|
|
uninstallDisplayName: "Presenton",
|
|
},
|
|
dmg: {
|
|
sign: false,
|
|
size: "2300m",
|
|
},
|
|
appx: {
|
|
identityName: "PresentonAI.Presenton",
|
|
publisher: "CN=8A2C57B5-F1C6-473A-93EE-2E9B72134341",
|
|
displayName: "Presenton",
|
|
publisherDisplayName: "Presenton Inc.",
|
|
applicationId: "PresentonAI.Presenton",
|
|
|
|
},
|
|
}
|
|
|
|
const effectiveMacTarget = macTarget || "dmg"
|
|
const targets =
|
|
process.platform === "darwin"
|
|
? builder.Platform.MAC.createTarget([effectiveMacTarget])
|
|
: undefined
|
|
|
|
if (macTarget === "mas" && process.env.PRESENTON_SKIP_CODESIGN_PREFLIGHT !== "1") {
|
|
assertCodesignCanUseIdentity(masAppSigningIdentity)
|
|
}
|
|
|
|
assertDirectMacReleaseReadiness()
|
|
|
|
if (isDirectMacBuild && process.platform === "darwin") {
|
|
console.log("[macOS direct] Distribution settings:", {
|
|
target: effectiveMacTarget,
|
|
identity: macDistributionIdentity || "auto",
|
|
hardenedRuntime: true,
|
|
notarize: shouldNotarizeDirectMacBuild,
|
|
signingRequired: requireDirectMacSigning,
|
|
})
|
|
}
|
|
|
|
if (macTarget === "mas") {
|
|
console.log("[MAS] Signing preflight:", {
|
|
identityQualifier: masIdentityQualifier || "auto",
|
|
appIdentity: masAppSigningIdentity || "none",
|
|
installerIdentity: masInstallerSigningIdentity || "none",
|
|
provisioningProfile: masProvisioningProfile || "none",
|
|
additionalArguments: masSigningExtraArgs,
|
|
})
|
|
}
|
|
|
|
builder.build({ targets, config })
|