Skip to content

Instantly share code, notes, and snippets.

@trevordevore
Last active June 16, 2017 14:33
Show Gist options
  • Save trevordevore/3e91724c4573690b691510d2e2dcd2a7 to your computer and use it in GitHub Desktop.
Save trevordevore/3e91724c4573690b691510d2e2dcd2a7 to your computer and use it in GitHub Desktop.
LiveCode script-only stack that will code sign LIveCode applications on OS X
script "SignApplicationForOSX"
on preOpenCard
set the visible of this stack to true
put the rect of this stack into tRect
put item 1 of tRect + 400 into item 3 of tRect
put item 2 of tRect + 400 into item 4 of tRect
set the rect of this stack to tRect
create button "Sign Application"
set the width of it to 150
set the loc of it to 200, 50
create field "Log"
set the rect of it to 16,90,384,384
set the dontWrap of it to true
set the vscrollbar of it to true
set the hscrollbar of it to true
end preOpenCard
on mouseUp
if the short name of the target is "Sign Application" then
answer file "Select bundle to sign"
put it into tBundlePath
if tBundlePath is not empty then
ask "Enter certificate name:"
put it into tCertName
end if
if tBundlePath is not empty and tCertName is not empty then
SignApplication tBundlePath, tCertName, false
put the result into tError
if tError is not empty then
answer "Error signing application:" && tError
end if
beep
end if
end if
end mouseUp
# Sign applications on OS X. lipo if preparing for MAS.
command SignApplication pBundlePath, pCertificateName, pIsMas
local theError
if pIsMas then
put "3rd Party Mac Developer Application: " & pCertificateName into pCertificateName
else
put "Developer ID Application: " & pCertificateName into pCertificateName
end if
# Sierra on up needs extended attributes stripped out
if theError is empty AND the platform is "macos" then
put format("chmod -R u+rw \"%s\"", pBundlePath) into theCmd
_log theCmd
get shell(theCmd)
if the result > 0 then
put the result into theError
end if
end if
if theError is empty AND the platform is "macos" then
put format("xattr -rc \"%s\"", pBundlePath) into theCmd
_log theCmd
get shell(theCmd)
if the result > 0 then
put the result into theError
end if
end if
# With Mavericks we have to sign everything. Just start in the MacOS folder
# and everything will end up signed.
if theError is empty then
signAndStrip pBundlePath, pCertificateName, pIsMas
put the result into theError
end if
if theError is empty then
put format("codesign -dvvv \"%s\"", pBundlePath) into theCmd
_log theCmd
get shell(theCmd)
if the result > 0 then
put the result into theError
end if
end if
if theError is empty then
put format("spctl --verbose --assess --type execute -v \"%s\"", pBundlePath) into theCmd
_log theCmd
get shell(theCmd)
if the result > 0 then
put the result into theError
end if
end if
if theError is not empty then
_log theError
end if
return theError
end SignApplication
## Monte Goulding with modifications by Trevor DeVore
command signAndStrip pBundle, pCertificate, pMas, pSignFile
local theError
# recursively parse any frameworks
if theError is empty then
put pBundle & "/Contents/Frameworks" into theRootFolder
if there is a folder theRootFolder then
_findBundlesAndStripExecutables theRootFolder, pCertificate, pMAS, pSignFiles
put the result into theError
end if
end if
# recursively parse the MacOS folder
if theError is empty then
put pBundle & "/Contents/MacOS" into theRootFolder
if there is a folder theRootFolder then
_findBundlesAndStripExecutables theRootFolder, pCertificate, pMAS, pSignFiles
put the result into theError
end if
end if
if theError is empty then
_signFile pBundle, pCertificate, pMas
put the result into theError
end if
return theError
end signAndStrip
# Signs a file or folder
private command _signFile pFilename, pCertificate, pMas
local theEntitlementsFile, theCmd, theError
if theError is empty then
put _BundleEntitlementFile(pFilename) into theEntitlementsFile
if theEntitlementsFile is not empty then
put format("codesign --verbose --force -s \"%s\" --entitlements \"%s\" \"%s\"", pCertificate, theEntitlementsFile, pFilename) into theCmd
else
put format("codesign --verbose --force -s \"%s\" \"%s\"", pCertificate, pFilename) into theCmd
end if
_log theCmd
get shell(theCmd)
put the result into theError
if theError is 1 then
put "codesign process failed:" && it into theError
else if theError is 2 then
put "invalid arguments passed to codesign:" && it into theError
else if theError is 3 then
put "requirements not met for -R flag in codesign:" && it into theError
end if
end if
# troubleshooting
if theError is not empty then
put theCmd & cr & cr & the executioncontexts
end if
return theError
end _signFile
private function _BundleEntitlementFile pBundle
local theFiles, theBundleName
# Look for an entitlement file that matches the bundle name
set the itemDelimiter to "/"
put the last item of pBundle into theBundleName
set the itemDelimiter to "."
put item 1 to -2 of theBundleName into theBundleName
put sys_fileListing(pBundle & "/Contents/Resources", true) into theFiles
filter theFiles with "*/" & theBundleName & ".entitlements"
if theFiles is empty then
put sys_fileListing(pBundle & "/Contents", true) into theFiles
filter theFiles with "*/" & theBundleName & ".entitlements"
end if
return theFiles
end _BundleEntitlementFile
private command _log pMsg
if there is a field "Log" then
put pMsg & cr after field "Log"
if the number of lines of field "Log" mod 2 is 0 then
set the backgroundcolor of the last line of field "Log" to 230,230,230
end if
end if
end _log
private function _IsMacOSExecutable pFilepath
return pFilepath ends with ".bundle" \
OR pFilepath ends with ".dylib" \
OR pFilepath ends with ".framework" \
OR pFilepath ends with ".app"
end _IsMacOSExecutable
## Monte Goulding with modifications by Trevor DeVore
private command _findBundlesAndStripExecutables pFolder, pCertificate, pMAS, pSignFiles
local theError
put pSignFiles is not false into pSignFiles
# Get list of folders and reset. Can't wait until end as it messes up recursive calls.
put the defaultFolder into theOldDefaultFolder
set the defaultFolder to pFolder
put the folders into theFolders
put the detailed files into theFilesInfo
put the files into theFiles
set the defaultFolder to theOldDefaultFolder
repeat for each line theFolder in line 2 to -1 of theFolders
if _IsMacOSExecutable(theFolder) then
signAndStrip pFolder&"/"&theFolder, pCertificate, pMAS, false # don't sign any files within bundle
put the result into theError
else if theFolder ends with ".app" OR theFolder ends with ".framework" then
signAndStrip pFolder&"/"&theFolder, pCertificate, pMAS, true # sign files within the app
put the result into theError
else
_findBundlesAndStripExecutables pFolder&"/"&theFolder, pCertificate, pMAS, pSignFiles # inherit behavior
put the result into theError
end if
if theError is not empty then exit repeat
end repeat
# On Mavericks and above additional signing is required. Anything that can contain code (or that OS X thinks can contain code)
if theError is empty then
if pSignFiles then # don't sign if part of a bundle
set the itemdelimiter to "."
# sign things like .dylib before others. Otherwise codesign will try to sign app and complain that .dylib isn't signed.
put theFiles into theFilesWOExt
filter theFilesWOExt without "*.*"
filter theFiles with "*.*"
if theFilesWOExt is not empty then
put theFilesWOExt into line (the number of lines of theFiles + 1) of theFiles
end if
repeat for each line theFile in theFiles
if the platform is "macos" AND \
(item 1 of the itemDelimiter > 10 OR item 1 of the systemVersion = 10 AND item 2 of the systemVersion >= 9) then
_signFile pFolder & "/" & theFile, pCertificate, pMAS
put the result into theError
end if
if theError is not empty then exit repeat
end repeat
end if
end if
# strip
if theError is empty then
if pMAS then
set the itemDelimiter to ","
repeat for each line theFile in theFilesInfo
# If executable bit is set then lipo it.
# Prior to 2014-05-27 we assumed 755. Now we just check for a 7 in the first char.
# TODO: error reporting
if char 1 of item 10 of theFile = "7" then
get shell(format("lipo -remove ppc \"%s\" -output \"%s\"", pFolder & "/" & URLDecode(item 1 of theFile), pFolder & "/" & URLDecode(item 1 of theFile)))
put the result into theResult
_log "lipo ppc" && pFolder & "/" & URLDecode(item 1 of theFile) && theResult
if theResult is not empty then
get shell(format("lipo -remove ppc7400 \"%s\" -output \"%s\"", pFolder & "/" & URLDecode(item 1 of theFile), pFolder & "/" & URLDecode(item 1 of theFile)))
_log "lipo ppc7400" && pFolder & "/" & URLDecode(item 1 of theFile) && theResult
end if
end if
end repeat
end if
end if
return theError
end _findBundlesAndStripExecutables
function sys_fileListing pFolder, pFullPath
local tDefault,tFiles,tFullFiles,tFile
if there is not a folder pFolder then return empty
if last char of pFolder is not slash then put slash after pFolder
put the defaultfolder into tDefault
set the defaultfolder to pFolder
put files() into tFiles
set the defaultfolder to tDefault
filter tFiles without ".*"
if pFullPath then
repeat for each line tFile in tFiles
put pFolder & tFile &cr after tFullFiles
end REPEAT
delete last char of tFullFiles
return tFullFiles
else
return tFiles
end if
end sys_fileListing
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment