macOS: Notarize in Script? - macos
Because the codesigning and archiving by Xcode is time-consuming, boring and problematic, I've always codesigned, archived and shipped my Developer ID signed macOS app using the command-line tools xcodebuild, codesign, etc. via my own script. Notarizing looks like it is going to be a major pain. Is it possible to add notarizing to my script?
Yes. Unfortunately, the official answer leaves some loose ends, for example this important tidbit from Quinn "the Eskimo". Here is how to do it:
One-Time Setups
Get an App-Specific Password
Decide on a name for your "app" of notarizing apps. I use the name of my product-shipping script, SSYShipProduct.pl because this is the "app" which will use this password. We shall refer to whatever name you compose as your-notarizing-name.
Browse to https://appleid.apple.com/account/manage, scroll to Security > App-Specific Password, and generate an App-Specific password for an app named your-notarizing-name. Copy the password that it gives you. We shall call that app-specific-password.
Add the App-Specific password to your macOS Keychain
Run this command to add the password you just created to your keychain:
security add-generic-password -a "your-apple-ID-email" -w "app-specific-password" -s "your-notarizing-name"
The -s parameter is the name that this item will have in your Keychain. I think you could actually use a different name, but in my mind it makes sense to use your-notarizing-name here too.
You can verify that it worked by searching in the Keychain Access application. However, be aware that new items are not listed in Keychain Access until after you quit and relaunch it.
Maybe, get the relevant itc-provider
If your Apple ID is associated with more than one Apple Developer Connection team (such as if you do contract work), you will need the itc_provider of the team for which this app should be notarized.
To find the itc_provider of your team, execute this command:
/Applications/Xcode.app/Contents/Applications/Application\ Loader.app/Contents/itms/bin/iTMSTransporter -m provider -u "your-apple-ID-email" -p "app-specific-password"
Scroll to the end of the output printed by this command and look at the Provider listing table. Copy the Short Name of the desired team. We shall call this "developer-team-itc-provider".
For each shipment (Scriptable!)
If you sign components of your app using the /usr/bin/codesign command line tool, each invocation of codesign must have the following new argument parameter , which tells codesign to sign with the so-called hardened runtime:
`--options runtime`
Conversely if your app is signed in Xcode, you must set the Build Setting Hardened runtime, available in Xcode 10 or later, to Yes in all executable component targets.
Other than that, your script should create a build of your app in Release configuration and codesign it, same as in pre-notarization days.
Upload to Apple Notary service
Your script should then archive your app to a .zip or .dmg. Note that this is an interim file which will only be uploaded to the Apple Notary service, not shipped.
Then, your script should compose a primary bundle ID value, which will be your app's bundle identifier with .zip or .dmg appended. Example: your-pbid-value = com.mycompany.YourApp.zip.
In what follows, your script will use altool, which is Apple's name for Application Loader Tool.
Your script should then run this command to get your .zip or .dmg notarized:
/usr/bin/xcrun altool --notarize-app --primary-bundle-id "your-pbid-value" --username "your-apple-id-email" --password "#keychain:your-notarizing-name" -itc_provider "developer-team-itc-provider" --file /path/to/YourApp.zip/or/YourApp.dmg --output-format "xml"
(Note that, in the above command, oddly, all argument names are preceded by two dashes except -itc_provider is preceded by only one dash. Also, if the scripting language you are using interpolates # characters in strings, code it to prevent interpolation of #keychain).
After a minute or so, xcrun will exit and print to stdout some XML which, if your submission was accepted (note: not approved yet), will look like this example:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>notarization-upload</key>
<dict>
<key>RequestUUID</key>
<string>2ab59b26-19ec-4a30-84cf-6d2cb8d3c97e</string>
</dict>
<key>os-version</key>
<string>10.15.0</string>
<key>success-message</key>
<string>No errors uploading 'path/to/YourApp.zip'.</string>
<key>tool-path</key>
<string>/Applications/Xcode.app/Contents/Applications/Application Loader.app/Contents/Frameworks/ITunesSoftwareService.framework</string>
<key>tool-version</key>
<string>1.1.1138</string>
</dict>
</plist>
All you really need out of there is that RequestUUID value. However, since I ship four apps often, and because it ruins my day when my shipping script fails without providing helpful error information, and because (see below) you are going to make another call which also returns interesting XML, I invested some time in adding to my script a subroutine which takes two parameters, XML and a key path, and returns the value of the XML at a given key path. In the case above, I call this subroutine to get the RequestUUID, and then again to get the success-message.
(My script is in Perl. Although there is available in CPAN a module named XML::Simple which can do this parsing in a line or two, it is marked by the maintainer as not for use in new designs. So, to avoid needing to install and wrangle with a real XML parser, I opted instead to use PlistBuddy as suggested in the comment by #khuttun. This was slightly painful also because, unfortunately, altool does not have an option to write its output to a file, and PlistBuddy is not documented to accept stdin. So my subroutine writes the stdout from altool to a temporary file, and then passes that temporary file's path to PlistBuddy. Kind of disgusting, but it works.)
Delete the Un-stapled zip package
At this point, I recommend that your script delete the .zip or .dmg file which it uploaded. Reason: That file was archived from a product which does not yet have your notarization ticket stapled to it. At the end of your script, you will create a new .zip or .dmg from a modified app which has the ticket. Deleting the file immediately prevents you from shipping an un-stapled app by mistake.
Wait in a Loop for Apple's Response
Your script can then start pestering Apple's server for your final results, by running this command in a loop along with with some sleep:
`/usr/bin/xcrun altool --notarization-info --username "your-apple-id-email" --password "#keychain:your-notarizing-name" --output-format "xml"
If your script runs this command immediately, it will get returned in stdout some xml which will look something like this example:
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>notarization-info</key>
<dict>
<key>Date</key>
<date>2019-08-07T01:17:37Z</date>
<key>RequestUUID</key>
<string>4ba71353-9d99-4b52-b579-37f384717130</string>
<key>Status</key>
<string>in progress</string>
</dict>
<key>os-version</key>
<string>10.15.0</string>
<key>success-message</key>
<string>No errors getting notarization info.</string>
<key>tool-path</key>
<string>/Applications/Xcode.app/Contents/Applications/Application Loader.app/Contents/Frameworks/ITunesSoftwareService.framework</string>
<key>tool-version</key>
<string>1.1.1138</string>
</dict>
</plist>
The significant key path in there is notarization-info:Status, whose value in progress means that Apple is still working on your submission. After a few minutes usually (Apple says "should be less than an hour", but I experienced times of up to three and a half hours on the USA holiday afternoon of 2019-Jul-04), altool will retturn to your script a different xml in stdout, something like this:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>notarization-info</key>
<dict>
<key>Date</key>
<date>2019-08-06T23:28:25Z</date>
<key>LogFileURL</key>
<string>https://osxapps-ssl.itunes.apple.com/itunes-assets/Enigma113/v4/f6/09/be/f609bee3-b031-323a-0987-d1f620a78758/developer_log.json?accessKey=1565410613_1722173034418364591_TvycjBAzd6FRTYGKZEFU6EwDfsws8Wa1MV%2FYnTiJ1zyOZamc%2FoeO5RMeIzZN669ZQJgO2Q4W48ipKNFO%2BQGuq%2FITXN8MQAetbNe90w9ogzqXbrzTHg%2FgYK89yvEFmiiRxhaVlZqLI93NBpY0hwBqXv2bvvlg%2FRCc%2BVaCNRJ%2BrnE%3D</string>
<key>RequestUUID</key>
<string>07fc3745-b0ff-4d1a-9b15-37f384717130</string>
<key>Status</key>
<string>success</string>
<key>Status Code</key>
<integer>0</integer>
<key>Status Message</key>
<string>Package Approved</string>
</dict>
<key>os-version</key>
<string>10.15.0</string>
<key>success-message</key>
<string>No errors getting notarization info.</string>
<key>tool-path</key>
<string>/Applications/Xcode.app/Contents/Applications/Application Loader.app/Contents/Frameworks/ITunesSoftwareService.framework</string>
<key>tool-version</key>
<string>1.1.1138</string>
</dict>
</plist>
After some reverse-engineering, you see that, in each loop iteration, your script should parse the XML and break out of the loop whenever the value of Status is something other than in progress, or if you prefer, when LogFileURL is defined. Or if you prefer email triggers, your script can look for an email from Apple with subject line You can now distribute your Mac software..
UPDATE 2019-11-02
After having trouble with this step in my last couple shipments, and again today, I have now confirmed a bug in Apple's Notary Service. The bug is that the altool --notarization-info command will fail for 1-5 hours, returning nonzero exit codes, and in stdout an error code 1519 "Could not find the RequestUUID", as in the following example stdout:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>os-version</key>
<string>10.15.1</string>
<key>product-errors</key>
<array>
<dict>
<key>code</key>
<integer>1519</integer>
<key>message</key>
<string>Could not find the RequestUUID.</string>
<key>userInfo</key>
<dict>
<key>NSLocalizedDescription</key>
<string>Could not find the RequestUUID.</string>
<key>NSLocalizedFailureReason</key>
<string>Apple Services operation failed.</string>
<key>NSLocalizedRecoverySuggestion</key>
<string>Could not find the RequestUUID.</string>
</dict>
</dict>
</array>
<key>tool-path</key>
<string>/Applications/Xcode.app/Contents/SharedFrameworks/ContentDeliveryServices.framework/Versions/A/Frameworks/AppStoreService.framework</string>
<key>tool-version</key>
<string>4.00.1181</string>
</dict>
</plist>
This is a bug because, of course my script did submit the Request UUID which it just received from Apple Notary Service, Apple should be able to find it and, furthermore, when I kept sending the command manually, after about 2 hours, suddenly, the command returned Success and continued to return Success with subsequent commands, and I got the Success email from Apple. This delay happened today with 7 different good Request UUIDs, the longest was 5 hours. Possibly, at this time, there is a 1-5 hour delay between Apple Notary Service creating and sending you a Request UUID, and it appearing in the database which Apple Notary Service uses to respond to notarization-info requests, so you get this false error. Very sad.
Since I have no control over when Apple assigns people to fix bugs, I have modified this stage of my script to parse the response from Apple and die only if the command returns nonzero exit status and the code of the first (index=0) product-errors array entry is not 1519. If you are using PlistBuddy to parse XML as I am, the key path for that is code should be product-errors:0:code. The loop in my script prints each time Error 1519 is received, so I can see what is going on, and of course, I've modified its while condition to not exit if the error code is 1519.
After so fixing my script, I had several apps to ship. Apple Notary Service treated the first one nicely: No Errors 1519, and Success after about two minutes. The next one, however, needed this new feature of my script. At time 09:54 (HH:mm) my script received the Request UUID from Apple. 20 seconds later, it sent the first altool --notarization-info query. The response was a false Error 1519. Subsequent queries also returned false Errors 1519, for almost 3 hours, through 12:44. Then, at 12:45, all of a sudden it received an in progress response. After 5 more in progress responses, at 12:47, finally, Success.
One more thing before leaving this topic: An hour after that request succeeded with no Errors 1519, a prior request from an hour ago suddenly started returning in progress and then a few minutes later, Success. Conclusion: Request UUIDs which get detoured into the Error 1519 morass are not queued FIFO with later Request UUIDs which might, by chance, avoid the Error 1519 detour. So, a better workaround might be to abandon a Request UUID after receiving one more Error 1519 responses and start over by re-uploading the app to Apple Notary Service and getting another Request UUID which you hope will work better. Of course, you'll get many emails during the next few hours as all of the Request UUIDs which you abandoned eventually succeed.
At any rate, now, on to the next step in the script…
Check Apple's Log file
Your script should parse out the value of the LogFileURL so it can check the log, because because even if notarization succeeds the Log file created by Apple might contain warnings. To get the Log file your script should, of course,
curl <LogFileURL-Value>
The Log file is apparently JSON. Warnings or Errors are presented as an array, which is the value of key issues . So your script should parse that curl output with a JSON parser and if the value of key issues is a JSON null or an empty array, continue shipping.
Staple the Ticket to your App
This step is pretty easy…
xcrun stapler staple /path/to/YourApp.app
Running this command will add to your app's package a new file: YourApp.app/Contents/CodeResources. This is apparently your notarization ticket. Note that this file is in addition to the file YourApp.app/Contents/_CodeSignature/CodeResources which is still there, and contains the code signature, the same as in pre-notarization days.
Verify the Ticket Stapling
But there is a better way to verify that your app now has a good ticket. Your script should now run (or re-run) a Gatekeeper check:
spctl -a -v /path/to/YourApp.app
The result, in stderr, should be,
/path/to/YourApp.app: accepted
source=Notarized Developer ID
which is the same result as pre-notarization, except for the insertion of Notarized. Astute scripts will parse that stderr and abort shipping if the above words are not detected.
Zip and Ship
Now that the ticket has been added, your script can zip or dmg your .app again, but this time, ship it.
Here's a reusable and freely-licensed notarize & staple script for automated builds:
https://github.com/rednoah/notarize-app/blob/master/notarize-app
It'll run and wait and only exit once everything is done:
Run altool --notarize-app
Run altool --notarization-info periodically until notarization is complete
Run stapler staple
Here's an example auto-notarizer I wrote for RawTherapee. First we notarize the app:
https://github.com/Beep6581/RawTherapee/blob/6fa533c40b34dec527f1176d47cc6c683422a73f/tools/osx/macosx_bundle.sh#L225-L250
Then we notarize the dmg:
https://github.com/Beep6581/RawTherapee/blob/6fa533c40b34dec527f1176d47cc6c683422a73f/tools/osx/macosx_bundle.sh#L283-L307
The notary credentials are passed thru the cmake command directive
-DNOTARY="--username user#mail.com --password abcd-efgh-ijkl-mnop"
Related
Launch shell script on login in Mac OS (OS X)
I have this shell script Test.sh: #! /bin/bash FILE_TO_CHECK="/Users/test/start.txt" EXIT=0 while [ $EXIT -eq 0 ]; do if [ -f "$FILE_TO_CHECK" ] then /usr/bin/java -jar myapp.jar EXIT=1 else sleep 30 fi done I need to start this script automatically after login. So I put it inside a folder Test in /System/Library/StartupItems/ When I reboot the Mac, nothing happens after I log in. Any clue? I also tried Automator, but with the same result: the java program is not running.
Ivan Kovacevic's pointers, especially the superuser.com link, are helpful; since at least OS X 10.9.2, your options for creating run-at-login scripts are: Note: The methods are annotated with respect to whether they are: specific to a given user ("[user-SPECIFIC]"); i.e., the installation must be performed for each user, if desired; scripts are typically stored in a user-specific location, and root (administrative) privileges are NOT required for installation. effective for ALL users ("[ALL users]"); i.e., the installation takes effect for ALL users; scripts are typically stored in a shared location and root (administrative) privileges ARE required for installation. The scripts themselves will run invisibly, but - with the exception of the com.apple.loginwindow login-hook method - you can open applications visibly from them; things to note: There is no guarantee that any such application will be frontmost, so it may be obscured by other windows opened during login. If you want to run another shell script visibly, simply use open /path/to/your-script, which will open it in Terminal.app; however, the Terminal window will automatically close when your script terminates. Automator [user-SPECIFIC]: File > New, type Application Add a Run Shell Script action, which adds an embedded bash script, and either paste your script code there or add a command that invokes an existing script from there. Save the *.app bundle and add it to the Login Items list in System Preferences > User & Groups > Login Items. Note: The embedded script runs with the default "C" locale. $PATH is fixed to /usr/bin:/bin:/usr/sbin:/sbin, which notably does NOT include /usr/local/bin The working dir. is the current user's home directory. com.apple.loginwindowlogin hook [ALL users - DEPRECATED, but still works]: If you have admin privileges, this is the easiest method, but it is DEPRECATED, for a variety of reasons (security, limited to a single, shared script, synchronous execution); Apple especially cautions against use of this mechanism as part of a software product. Place your script, e.g., Test.sh, in a shared location - e.g., /Users/Shared - and make sure it is executable (chmod +x /Users/Shared/Test.sh). From Terminal.app, run the following: sudo defaults write com.apple.loginwindow LoginHook /Users/Shared/Test.sh Note: The script will run as the root user, so exercise due caution. Among the methods listed here, this is the only way to run a script as root. There's only one system-wide login hook. Note that there's also a log-OUT hook, LogoutHook, which provides run-at-logout functionality - unlike the other approaches. The login-hook script runs synchronously before other login actions, and should therefore be kept short. Notably, it runs before the desktop is displayed; you cannot launch applications from the script, but you can create simple interactions via osascript and AppleScript snippets (e.g., osascript -e 'display dialog "Proceed?"'); however, any interactions block the login process. The script runs in the context of the root user and he username of the user logging on is passed as the 1st argument to the script. The script runs with the default "C" locale. $PATH is fixed to /usr/bin:/bin:/usr/sbin:/sbin, which notably does NOT include /usr/local/bin The working dir. is /. launchd agents: launchd-agent-executed scripts can be installed for a SPECIFIC user OR for ALL users - the latter requires administrative privileges. While using launchd is Apple's preferred method, it's also the most cumbersome, as it requires creating a separate *.plist configuration file. On the upside, you can install multiple scripts independently. Note: No specific timing or sequencing of launchd scripts is guaranteed; loosely speaking, they "run at the same time at login"; there is even no guaranteed timing between the user-specific and the all-user tasks. The script runs with the default "C" locale. $PATH is fixed to /usr/bin:/bin:/usr/sbin:/sbin, which notably does NOT include /usr/local/bin The working dir. is / by default, but you can configure it via the .plist file - see below. The script-file path must be specified as a full, literal path (e.g., /Users/jdoe/script.sh; notably , ~-prefixed paths do not work. For a description of all keys that can be used in *.plist configuration files, see man launchd.plist. Both user-specific and all-users tasks run as the current user (the user logging on). launchd [user-SPECIFIC]: Note: Lingon 3 ($5 as of early 2014) is a GUI application that facilitates the process below, but only for user-specific scripts. Place your script, e.g., Test.sh, in your home folder, e.g., /Users/jdoe Create a file with extension .plist in ~/Library/LaunchAgents, e.g., ~/Library/LaunchAgents/LoginScripts.Test.plist, by running the following in Terminal.app: touch ~/Library/LaunchAgents/LoginScripts.Test.plist Open the file and save it with the following content: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <!-- YOUR SELF-CHOSEN *UNIQUE* LABEL (TASK ID) HERE --> <string>LoginScripts.Test.sh</string> <key>ProgramArguments</key> <array> <!-- YOUR *FULL, LITERAL* SCRIPT PATH HERE --> <string>/Users/jdoe/Test.sh</string> </array> <key>RunAtLoad</key> <true/> </dict> </plist> The <!-- ... --> comments indicate the places to customize; you're free to choose a label, but it should be unique - ditto for the .plist filename; for simplicity, keep the label and the filename root the same. From Terminal.app, run the following: launchctl load ~/Library/LaunchAgents/LoginScripts.Test.plist Note that, as a side effect, the script will execute right away. From that point on, the script will execute whenever the CURRENT user logs on. It is not strictly necessary to run launchctl load -- since, by virtue of the file's location, it will be picked up automatically on next login -- but it's helpful for verifying that the file loads correctly. launchd [ALL users] Place your script, e.g., Test.sh, in a SHARED location, e.g., /Users/Shared Create a file with extension .plist in /Library/LaunchAgents (requires admin privileges), e.g., /Library/LaunchAgents/LoginScripts.Test.plist, by running the following in Terminal.app: sudo touch /Library/LaunchAgents/LoginScripts.Test.plist Open the file and save it with the following content (make sure your text editor prompts for admin privileges on demand; alternatively, use sudo nano /Library/LaunchAgents/LoginScripts.Test.plist): <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <!-- YOUR SELF-CHOSEN *UNIQUE* LABEL (TASK ID) HERE --> <string>LoginScripts.Test.sh</string> <key>ProgramArguments</key> <array> <!-- YOUR *FULL, LITERAL* SCRIPT PATH HERE --> <string>/Users/Shared/Test.sh</string> </array> <key>RunAtLoad</key> <true/> </dict> </plist> The <!-- ... --> comments indicate the places to customize; you're free to choose a label, but it should be unique - ditto for the .plist filename; for simplicity, keep the label and the filename root the same. From Terminal.app, run the following: sudo chown root /Library/LaunchAgents/LoginScripts.Test.plist sudo launchctl load /Library/LaunchAgents/LoginScripts.Test.plist Note that, as a side effect, the script will execute right away. From that point on, the script will execute whenever ANY user logs on. It is not strictly necessary to run launchctl load -- since, by virtue of the file's location, it will be picked up automatically on next login -- but it's helpful for verifying that the file loads correctly.
You can't just place plain scripts in that folder. You need a "specialized bundle" how Apple calls it, basically a folder with your executable, and a .plist configuration. And you should put it in /Library/StartupItems since /System/Library/StartupItems/ is reserved for the operating system. Read all about it here: https://developer.apple.com/library/mac/documentation/macosx/conceptual/bpsystemstartup/chapters/StartupItems.html Also note that the whole stuff is marked as deprecated technology. And that Apple is suggesting the use of launchd. There is an example how to set it up here: https://superuser.com/questions/229773/run-command-on-startup-login-mac-os-x
launchd-oneshot is used to install script as a launchd job to run on login, with brew install cybertk/formulae/launchd-oneshot sudo launchd-oneshot Test.sh --on-login Disclosure: I am the author of this package.
Run applescript in Automator
I need to start and stop a small server with automator but my knoledge is very limited. I can't manage to set the path where the file is and I don't know how to stop the server. So far I have this: on run set r to display dialog "Start or stop the NINJAM server ?" buttons {"Stop", "Start"} if button returned of r is "Start" then #tell application "Terminal" # activate do shell script "cd \"/Applications/ MUSIC/ Utilities/Audio IP/NINJAM/NINJAM/NinjamOSXServer ./ninjamsrv Server.cfg\"" #end tell else do shell script "Stop" end if end run Any help is really apprectated. Thanks in advance.
NOTE that I'm using my own path here -- I put the ninjam server folder in the top level of my Applications folder. I had to create a 'term' file, which is a text file with this in it: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>WindowSettings</key> <array> <dict> <key>ExecutionString</key> <string>cd /Applications/NinjamOSXServer/;./ninjamsrv config.cfg</string> </dict> </array> </dict> </plist> I saved this as LaunchNinjamSrvr.term and put it in the same folder as the ninjamsrvr. Then the script to use is: set r to display dialog "Start or stop the NINJAM server ?" buttons {"Stop", "Start"} if button returned of r is "Start" then do shell script "open /Applications/NinjamOSXServer/LaunchNinjamSrvr.term" else do shell script "killall -INT -v ninjamsrv" end if [Occurs to me that I should give some explanation. Directly using the full path with the " config.cfg" parameter makes 'do shell script' choke. Splitting into two commands (but still using do shell script), like you see in the .term file, works to launch ninjamsrv, but makes the script editor (I use Smile) freeze. So that is (presumably -- I didn't want to test it by other means [script app, etc.]) a problem, and why I resorted to using the .term file. It used to be that you could, from the File menu in Terminal (as I recall), save a .term file directly, but that seems to have fallen by the way-side. So, at this point, I have a template that I use and just paste commands into the appropriate line. (But see http://discussions.apple.com/thread/3139585?start=0&tstart=0 -- wherein the technique of exporting Terminal Preference file is explained). I'm being a bit lazy in that the new form is .terminal, not .term ... anyway ... So now all that is left is doing the actual AS script. 'open' is a basic command line command which is just like opening or double-clicking in the Finder. If, for some reason your file opens in the wrong app or doesn't open, you might need to map it to Terminal.app (in the get info window) and/or change the extension to the more up--to-date '.terminal'. killall is like kill, designed to kill processes in various ways. I chose -INT because this is essentially like doing a control-c to interrupt the process.]
defaults write help (Mac)
I am trying to set a preference for Firefox to open in 32bit mode on 130 macs via Apple Remote Desktop. The easiest way to do this usually is with defaults write unfortunately I havent found any documention on this, and I cant figure out the syntax for writing a string thats not at the root level. The file is ~/Library/Preferences/com.apple.LaunchServices.plist I need to change the string x86_64 to i386 <dict> <key>LSArchitecturesForX86_64v2</key> <dict> <key>org.mozilla.firefox</key> <array> <data> Ym9va2QCAAAAAAQQMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAYAEAAAwAAAABAQAAQXBwbGljYXRpb25zCwAAAAEB AABGaXJlZm94LmFwcAAIAAAAAQYAAAQAAAAYAAAACAAAAAQDAABq ix4BAAAAAAgAAAAEAwAAWYNjAQAAAAAIAAAAAQYAADwAAABMAAAA CAAAAAAEAABBtfgZbgAAABgAAAABAgAAAgAAAAAAAAAPAAAAAAAA AAAAAAAAAAAAAAAAAAEFAAAMAAAAAQEAAE1hY2ludG9zaCBIRAgA AAAEAwAAAICcregAAAAIAAAAAAQAAEG0QBQYAAAAJAAAAAEBAAA0 NkFGNUYyOC1DNTExLTM0MEMtQkU1RS1DREYzMTgyQThFOTIYAAAA AQIAAIEAAAABAAgA7z8AAAEACAAAAAAAAQAAAAEAAAABAQAALwAA AAQAAAADAwAAAAAAIBkAAAABAQAAL0FwcGxpY2F0aW9ucy9GaXJl Zm94LmFwcAAAAMwAAAD+////AQAAAAAAAAAQAAAABBAAACwAAAAA AAAABRAAAFwAAAAAAAAAEBAAAHwAAAAAAAAAIBAAABgAAAAAAAAA MBAAAEwAAAAAAAAAQBAAAGwAAAAAAAAAAiAAACQBAAAAAAAAECAA AKQAAAAAAAAAESAAANgAAAAAAAAAEiAAALgAAAAAAAAAEyAAAMgA AAAAAAAAICAAAAQBAAAAAAAAMCAAAJwAAAAAAAAAAdAAAJwAAAAA AAAAENAAADABAAAAAAAAAP8AADwBAAAAAAAA </data> <string>x86_64</string> </array> </dict>
I don't think there's a way to do this with defaults (well, there is, but it involves dumping the entire LSArchitecturesForX86_64v2 dictionary, editing it, then reimporting). But PlistBuddy can do the job: /usr/libexec/PlistBuddy -c "set :LSArchitecturesForX86_64v2:org.mozilla.firefox:1 i386" ~/Library/Preferences/com.apple.LaunchServices.plist And the usual caveats before deploying anything to 130 computers: test this first (I tried it, once...), and have it back up the relevant file on each computer so if something does go sideways you can roll it back.
Running script upon login in mac OS X [closed]
Closed. This question is off-topic. It is not currently accepting answers. Want to improve this question? Update the question so it's on-topic for Stack Overflow. Closed 10 years ago. This post was edited and submitted for review 10 months ago and failed to reopen the post: Original close reason(s) were not resolved Improve this question I am wondering if anyone is able to help me out with getting a shell (.sh) program to automatically run whenever I log in to my account on my computer. I am running Mac OS X 10.6.7. I have a file "Example.sh" that I want to run when I log onto my computer. I do not have a problem running it when I am already logged in, but I want this to run automatically.
Follow this: start Automator.app select Application click Show library in the toolbar (if hidden) add Run shell script (from the Actions/Utilities) copy & paste your script into the window test it save somewhere (for example you can make an Applications folder in your HOME, you will get an your_name.app) go to System Preferences -> Users & Groups -> Login items (or System Preferences -> Accounts -> Login items / depending of your MacOS version) add this app test & done ;) EDIT: I've recently earned a "Good answer" badge for this answer. While my solution is simple and working, the cleanest way to run any program or shell script at login time is described in #trisweb's answer, unless, you want interactivity. With automator solution you can do things like next: so, asking to run a script or quit the app, asking passwords, running other automator workflows at login time, conditionally run applications at login time and so on...
tl;dr: use OSX's native process launcher and manager, launchd. To do so, make a launchctl daemon. You'll have full control over all aspects of the script. You can run once or keep alive as a daemon. In most cases, this is the way to go. Create a .plist file according to the instructions in the Apple Dev docs here or more detail below. Place in ~/Library/LaunchAgents Log in (or run manually via launchctl load [filename.plist]) For more on launchd, the wikipedia article is quite good and describes the system and its advantages over other older systems. Here's the specific plist file to run a script at login. Updated 2017/09/25 for OSX El Capitan and newer (credit to José Messias Jr): <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.user.loginscript</string> <key>ProgramArguments</key> <array><string>/path/to/executable/script.sh</string></array> <key>RunAtLoad</key> <true/> </dict> </plist> Replace the <string> after the Program key with your desired command (note that any script referenced by that command must be executable: chmod a+x /path/to/executable/script.sh to ensure it is for all users). Save as ~/Library/LaunchAgents/com.user.loginscript.plist Run launchctl load ~/Library/LaunchAgents/com.user.loginscript.plist and log out/in to test (or to test directly, run launchctl start com.user.loginscript) Tail /var/log/system.log for error messages. The key is that this is a User-specific launchd entry, so it will be run on login for the given user. System-specific launch daemons (placed in /Library/LaunchDaemons) are run on boot. If you want a script to run on login for all users, I believe LoginHook is your only option, and that's probably the reason it exists.
Create a shell script named as login.sh in your $HOME folder. Paste the following one-line script into Script Editor: do shell script "$HOME/login.sh" Then save it as an application. Finally add the application to your login items. If you want to make the script output visual, you can swap step 2 for this: tell application "Terminal" activate do script "$HOME/login.sh" end tell If multiple commands are needed something like this can be used: tell application "Terminal" activate do script "cd $HOME" do script "./login.sh" in window 1 end tell
How do I make the dock icon stop bouncing after my app wrapper script starts?
So I recently made an .app wrapper for a zsh script, but when I run it, the icon for the app keeps hopping in the Dock. The app is basically: % find Example.app -type f Example.app/Contents/Info.plist Example.app/Contents/MacOS/wrapper.sh % cat Example.app/Contents/Info.plist <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <!-- tell OSX what the name of the executable is --> <key>CFBundleExecutable</key> <string>wrapper.sh</string> </dict> </plist> % cat Example.app/Contents/MacOS/wrapper.sh #!/usr/bin/env zsh # not my real script, but something else that hangs for a while sleep 10 % You can also grab it from github. Is there any way I can get the app icon to stop hopping, and act like a normal app, and just sit there with a triangle next to it?
An application's icon will bounce in the OS X Dock until the app enters its main event loop and begins responding to user input. As your script probably doesn't run a Carbon or Cocoa event loop (See documentation for Carbon Event Manager and NSApplication, respectively), the Dock is waiting continuously, expecting a regular foreground GUI application to emerge. A script-wrapping tool that I've had success with is Platypus. It can run its own event loop while executing your script, and do a number of other convenient things.
You want DropScript. The dock doesn't work with shell scripts like that, it expects something a bit more full-fledged.
I think if you add LSUIElement to your Info.plist file, it should work.