SD and OS X version script

foundation
finder
asobjc

(Ed Stockly) #1

The following is based on a nifty script that Chris posted ages ago. The idea is that if you’re going to post something about SD, you can easily put the app and system version into the clipboard by running from the script menu.

Here’s what ends up in the clipboard:

–>Script Debugger 6.0.4 (6A198) on OSX 10.11.6

I’ve been using it for that, but I also put it at the top of my scripts so if there’s a problem later I know which version I’m using.

But today I realized that sometimes I didn’t want that info in the clipboard, I just wanted a quick look to see what versions of either I have.

To solve that this script changes its own name using the SD and OS X versions. The first time you run it it will change the name to this: “Copy SD 6.0.4 (6A198) OSX 10.11.6.scptd” (If you’re running it from an untitled document in SD or SE it won’t do anything)

After that anytime you want to know which version you’re using just pull down the menu and there it is.

-->Script Debugger 6.0.4 (6A198) on OSX 10.11.6

set fileNameFormat to {"Copy SD ", "version and", " OSX ", "version"}
--"Copy SD version and OSX version"
copy fileNameFormat to newFileName
set sdPath to POSIX path of (path to application id "com.latenightsw.ScriptDebugger6")
set sdPlist to quoted form of (sdPath & "Contents/Info.plist")
set plListCommand to "/usr/libexec/PlistBuddy -c Print:CFBundleShortVersionString -c Print:CFBundleVersion " & sdPlist
set sdVersInfo to paragraphs of (do shell script plListCommand)
set systemVers to system version of (get system info)
set sdVersInfo to item 1 of sdVersInfo & " (" & item 2 of sdVersInfo & ")"
set item 2 of newFileName to sdVersInfo
set item 4 of newFileName to systemVers
set AppleScript's text item delimiters to {""}
set newFileName to newFileName as text
set sdVersInfo to "-->Script Debugger " & sdVersInfo
set versInfo to sdVersInfo & " on OSX " & systemVers
set the clipboard to versInfo
set thisScript to path to me as alias

--Unnote next line and run script to set script name back to default
--set newFileName to fileNameFormat as text

tell application "Finder"
	set oldFileName to the name of thisScript
	if "Unsaved Script Debugger Document" is in oldFileName then return
	if "Untitled" is in oldFileName then return
	set nameExt to the name extension of thisScript
	set newFileName to newFileName & "." & nameExt
	if oldFileName is not newFileName then set the name of thisScript to newFileName
end tell


#2

Ed, I tried your script but it didn’t do anything on my Mac. Your code looked pretty complex and I figured I would come up with a simpler version. The main body of the program is now much simpler but I also added 4 handlers. So while it is simpler, it is now longer. So there’s good and bad :poop:. But it does show a simpler way to get to the first 4 values your script calculated. It looked like your script would add extra text every time it was run. But I couldn’t be totally sure since I couldn’t run it. So instead of fixing it to run on my Mac I wrote a new script.

I tried to change it so the script would be smater and not keep adding the new text to the file name every time it was run but I was not sucessful :slight_frown:. Every fix I came up with caused a new problem. So this script is not very helpful when it is run more then once on the same file :slight_smile: While the previous line was really not a smily face line it got one anyway so I could get alternating frown and smily faces.

I did try for over an hour to get it to stop infinetly adding a beginning and end to the Finder file name each time I ran the script, but apparently I’m not smart enough for that :slight_frown:. Probably Chris with his string matching skills could figure that out without any trouble :slight_smile:. The only way I came up with for stopping the infinite adding was putting the actual file name in the comment field for the file but that messed up using it for comments and it seemed like cheating anyway :slight_frown:. But the comment field was a good place to store the real file name without the added text.

So here is my imperfect script in all of its inperfect glory :zipper_mouth:.

use scripting additions
use framework "Foundation"

-- This returns the fully formatted file name
on FormatSDName(FirstPartOfName, BundleVersion, SystemVersion, SDVersion)
	-- This is the first part of the file name before the period and file extension
	set FirstPartOfName to "Copy " & FirstPartOfName & " SD " & SDVersion & " (" & BundleVersion & ") OSX " & SystemVersion
	return FirstPartOfName
end FormatSDName

-- This returns the file extension minus the period
on FileExtensionFromString(TheString)
	if (TheString = "") then
		return ""
	else if ((offset in TheString of ".") = 0) then
		set FileExtension to ""
	else
		set OldASDelimiters to text item delimiters of AppleScript
		set text item delimiters of AppleScript to {"."}
		set FileExtension to last item of (text items of TheString)
		set text item delimiters of AppleScript to OldASDelimiters
		return FileExtension
	end if
end FileExtensionFromString

-- This returns the file extension minus the period
on FileExtensionFromDocument(TheDocument)
	set FileName to name of TheDocument
	
	return FileExtensionFromString(FileName)
end FileExtensionFromDocument

-- This is the first part of the file name, before the final period and file extension
on GetFirstPartOfName(TheFullName, FileNameExtensionLength)
	try
		set NameLength to length of TheFullName
		if (TheFullName ends with ".") then
			set TheFullName to strings 1 through -2 of TheFullName
			return TheFullName
		end if
		set TheFullName to strings 1 thru (NameLength - FileNameExtensionLength) of TheFullName
		return TheFullName
	on error errMsg number errNum
		display dialog "Error " & (errNum as string) & " occured while getting the first part of file name." & return & return & errMsg ¬
			buttons {"OK"} default button "OK" with title "Error"
		return "" -- Returning an empty string indicates an error
	end try
end GetFirstPartOfName

try
	set SDBundle to current application's NSBundle's bundleWithIdentifier:"com.latenightsw.ScriptDebugger6"
	set BundleVersion to SDBundle's infoDictionary()'s objectForKey:"CFBundleVersion"
	set SystemVersion to system version of (system info) -- This comes from an Apple scripting addition
	
	tell application "Script Debugger"
		set SDVersion to version
		
		-- Find values for current SD document name
		set SDDocName to (name of document 1) as text
		set SDFileExtension to my FileExtensionFromDocument(document 1) as text
		set SDDocNameStart to my GetFirstPartOfName(SDDocName, length of SDFileExtension)
		if (SDDocNameStart = "") then return false
		set FullSDDocNameStart to my FormatSDName(SDDocNameStart, BundleVersion, SystemVersion, SDVersion)
		set NewFullDocName to FullSDDocNameStart & "." & SDFileExtension
		
		-- Find values for current Finder file name	
		if ((file spec of document 1) = missing value) then
			set FinderFileName to missing value
			set FinderFileExtension to missing value
			set FinderFileNameStart to missing value
			set FinderFileNameStart to missing value
			set NewFinderFileName to missing value
		else
			set FilePathStr to (file spec of document 1) as string
			tell application "Finder" to set FinderFileName to name of (item FilePathStr) as string
			set FinderFileExtension to my FileExtensionFromString(FinderFileName)
			set FinderFileNameStart to my GetFirstPartOfName(FinderFileName, length of FinderFileExtension)
			if (FinderFileNameStart = "") then return false
			set FullFinderFileNameStart to my FormatSDName(FinderFileNameStart, BundleVersion, SystemVersion, SDVersion)
			set NewFinderFileName to FullFinderFileNameStart & "." & FinderFileExtension
		end if
		
		if (FinderFileNameStart ≠ FullSDDocNameStart) then
			-- Save the document text to disk
			save document 1 in file ((file spec of document 1) as string)
			tell application "Finder" to set name of (item FilePathStr) to NewFinderFileName
		end if
	end tell
on error errMsg number errNum
	display dialog "Error " & (errNum as string) & " occured." & return & return & errMsg ¬
		buttons {"OK"} default button "OK" with title "Error"
	return false
end try

Bill


(Ed Stockly) #3

I’m really curious as to why it wouldn’t run on your system. Did you save it first? Did you try to step through it in SD?

My version (based on Chris’ script) sets the clipboard to the string, did you verify the clipboard wasn’t changed?

It looks your version requires Framework “Foundation” and when I try to run it on my system I get this message:

“Cannot Save Document”
“AppleScript failed to save this document because the script contains variables holding AppleScript Objective-C pointer values which cannot be saved (errOSACantStorePointers).”
“Clearing global variables and properties by recompiling the script allows AppleScript to save the script. Do you want to recompile and save again?”

What is version of SD are you using with which mac system version? (If only there was an easy way to get all that!)

–>Script Debugger 6.0.4 (6A198) on OSX 10.10.4


#4

Ed,

This should teach me not to work on things when I am half asleep. I tried your script late at night. Now that you mention it I do remember your post talking about the clipboard. At the time I was thinking your script changed the name of the file. The window name didn’t change so I thought it did nothing. This also explains why you didn’t have a problem with the new text being added again and again. The reason your version didn’t do that was because you manually pasted and you’re smart enough to only do it once :slight_smile:

I redid my script to solve the actual problem and it is shown below. The first 2 lines in the script do require foundation because I used the NSBundle class, and the 3ed and last lines require scripting additions. Solving the correct problem did make my script a lot shorter. So that would make my version:

use scripting additions
use framework "Foundation"


set SDBundle to current application's NSBundle's bundleWithIdentifier:"com.latenightsw.ScriptDebugger6"
set BundleVersion to SDBundle's infoDictionary()'s objectForKey:"CFBundleVersion"
set SystemVersion to system version of (system info)

tell application "Script Debugger"
	set SDVersion to version
	set ScriptName to name of document 1
end tell

set the clipboard to "Copy " & ScriptName & " SD " & SDVersion & " (" & BundleVersion & ") OSX " & SystemVersion

Well this is a bit embarrassing.

Bill


(Shane Stanley) #5

It’s probably more helpful to post the full OS version if you’re ever running pre-release stuff:

set SystemVersion to current application's NSProcessInfo's processInfo()'s operatingSystemVersionString()
--> "Version 10.12.3 (Build 16D32)"

(Jim Underwood) #6

@BillKopp or @ShaneStanley,

Is it possible to get the app ver# using ASObjC directly from the NSBundle, without having to “tell” the app?

If so, that would eliminate all of the plist stuff.

Here is what I currently get, for example:

Evernote 6.9.2 (454158) on macOS 10.11.6


#7

Jim,

Yes. The following shows how to get the SD version and build number. There is a big list of constants that can be used to get different things. The list can be found at:
https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html

set SDBundle to current application's NSBundle's bundleWithIdentifier:"com.latenightsw.ScriptDebugger6"
set BundleVersion to SDBundle's infoDictionary()'s objectForKey:"CFBundleShortVersionString" -->  "6.0.4"
set BundleVersion to SDBundle's infoDictionary()'s objectForKey:"CFBundleVersion" -->  "6A198"

-- For Evernote it's
set SDBundle to current application's NSBundle's bundleWithIdentifier:"com.evernote.Evernote"
set BundleVersion to SDBundle's infoDictionary()'s objectForKey:"CFBundleShortVersionString" --> 6.10
set BundleVersion to SDBundle's infoDictionary()'s objectForKey:"CFBundleVersion" --> 454269

Bill


(Shane Stanley) #8

Bill’s answered that, but…

You shouldn’t need the PlistBuddy stuff without it.


(Jim Underwood) #9

OK, thanks Bill.

Here’s my general script for getting version info on any app:

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

set appName to "Safari"
set appID to get id of application appName

set SDBundle to current application's NSBundle's bundleWithIdentifier:appID
set appVer to SDBundle's infoDictionary()'s objectForKey:"CFBundleShortVersionString"
set appBuild to SDBundle's infoDictionary()'s objectForKey:"CFBundleVersion"

set macOS to system version of (system info)

set verStr to appName & " " & (appVer as text) & ¬
  " (" & (appBuild as text) & ¬
  ")" & " on macOS " & macOS

-->Safari 10.0.3 (11602.4.8.0.1) on macOS 10.11.6

One more question: What is the best way to deal with duplicate Bundle iDs?
For example, I have both Adobe Acrobat X and XI installed, and they have the same Bundle ID.

When I run the script for:
set appName to "Adobe Acrobat Pro"

it returns:
Adobe Acrobat Pro 10.1.15 (10.1.15) on macOS 10.11.6

Not what i was expecting.


(Jim Underwood) #10

Just to follow up.
When I run this tell script, I get a different version.

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions

tell application "Adobe Acrobat Pro"
  set appID to id
  set appVer to version
end tell

-->appID:  "com.adobe.Acrobat.Pro"
-->appVer: "11.0.19"


#11

Shane or Mark,

I ran into the weirdest problem tonight. I was changing my post to show how to add the build number Shane suggested to include in my script. I used NSDictionary’s dictionaryWithContentsOfFile method to get the value and it works fine if I the script line comes before the “Script Debugger” tell block. But I get an error if I put line after the tell block.

I used a different method using a “do shell script” to execute “sw_vers -buildVersion” to get the value and it works before and after the “Script Debugger” tell block with no problems. I fiddled around with it for a while and couldn’t figure out why this is happening.

I set up this script up to produce 4 outputs. It does both ways before and both ways after the SD tell block. All lines but one produce the exact same correct result. But the second one using NSDictionary errors. So I commented out that line in this post. I’m not sure if I’m making a silly mistake or if there is an actual problem here.

If you can uncomment “set SystemBuildNum4 to (current application’s NSDictionary’s …” and it works then the problem is specific to my Mac. If you do get an error I’m not sure what it means.

use scripting additions
use framework "Foundation"

set SDBundle to current application's NSBundle's bundleWithIdentifier:"com.latenightsw.ScriptDebugger6"
set BundleVersion to SDBundle's infoDictionary()'s objectForKey:"CFBundleVersion"
set SystemVersion to system version of (system info)

set SystemBuildNum1 to (current application's NSDictionary's dictionaryWithContentsOfFile:"/System/Library/CoreServices/SystemVersion.plist")'s valueForKey:"ProductBuildVersion"
set SystemBuildNum2 to do shell script "sw_vers -buildVersion" --> 15G1217
tell application "Script Debugger"
	set SDVersion to version
	set ScriptName to name of document 1
end tell
set SystemBuildNum3 to do shell script "sw_vers -buildVersion" --> 15G1217
-- set SystemBuildNum4 to (current application's NSDictionary's dictionaryWithContentsOfFile:"/System/Library/CoreServices/SystemVersion.plist")'s valueForKey:"ProductBuildVersion"

-- Instead of sending the compiled string to the clipboard I just returned it as a result
set N1 to "#1 Copy " & ScriptName & " SD " & SDVersion & " (" & BundleVersion & ") OSX " & SystemVersion & " (Build " & SystemBuildNum1 & " 1)"
set N2 to "#2 Copy " & ScriptName & " SD " & SDVersion & " (" & BundleVersion & ") OSX " & SystemVersion & " (Build " & SystemBuildNum2 & " 2)"
set N3 to "#3 Copy " & ScriptName & " SD " & SDVersion & " (" & BundleVersion & ") OSX " & SystemVersion & " (Build " & SystemBuildNum3 & " 3)"
-- set N4 to "#4 Copy " & ScriptName & " SD " & SDVersion & " (" & BundleVersion & ") OSX " & SystemVersion & " (Build " & SystemBuildNum4 & " 4)"

set CombinedOutput to N1 & return & N2 & return & N3

Bill


#12

Jim,

I assume you are referring to the tendency for for the wrong application to be referenced. Duplicate Bundle IDs makes things unpredictable and not always consistent if the tell reference is just the name of the application. It is best to use a full path to specify an application when there is another application has the same Bundle ID. For example:

tell application "Bills second iMac HD:Applications:Adobe Acrobat Pro.app"
	
end tell

This will specifically reference the application pointed to by the path.

Bill


(Shane Stanley) #13

Try to avoid them. What you get is roughly what you get when you double-click on a document – usually the most recent one in the Applications folder. If that’s a problem, you need to use a path.

In a tell block, I think if you use just the name it favors any running app.

NSBundle has two methods you can use: bundleWithPath: and bundleWithURL:.


(Shane Stanley) #14

That is very odd. It looks like the tell block is acting as a sort of closure, setting current application to SD as a target and stopping ASObjC code from working from then on. Very weird indeed.

And it’s not confined to Script Debugger; you can see the same thing in Script Editor.

One workaround is to address Script Debugger by id (which I always do, and possibly explains why I’ve not seen this before). You can also get around the problem by moving the tell block into a handler.

And of course turning on Use Properties for Cocoa Terms also avoids the problem.


(Shane Stanley) #15

FWIW, that’s generally not a good idea with property list files because it fails if they’ve been saved as binary plist files (which many system ones tend to be). Better to use NSPropertyListSerialization:

use scripting additions
use framework "Foundation"

set theData to the current application's NSData's dataWithContentsOfFile:"/System/Library/CoreServices/SystemVersion.plist"
set {theDict, theError} to current application's NSPropertyListSerialization's propertyListWithData:theData options:0 |format|:(missing value) |error|:(reference)
if theDict is missing value then error (theError's localizedDescription() as text)
set SystemBuildNum to theDict's valueForKey:"ProductBuildVersion"

(But then reading files like SystemVersion.plist directly is probably a bad idea period…)


(Jim Underwood) #17

I get your point about avoid or use path, but FWIW:

set SDBundle to current application's NSBundle's bundleWithIdentifier:"com.adobe.Acrobat.Pro"
set appVer to SDBundle's infoDictionary()'s objectForKey:"CFBundleShortVersionString"

RETURNS: "10.1.15"
which is NOT the version that responds to double-click (which has the same Bundle ID).


#18

I changed the script to us application id as you suggested and it definitely now works. But that seems like an odd fix. No matter how long I worked on that I would have never thought of that. Using the app ID is a more specific reference, but what else could it get confused with? I checked to make sure I didn’t have any apps called “Script Debugger” and I didn’t. I even throw “Script Debugger lite” into the trash and dumped the trash just because the app name as similar and the problem remained. To me that is just as weird as the original problem. But thanks for the advice. Do you always use the app id for all tell block references or just Script Debugger?

For anyone following this discussion the corrected version of my script is listed below. The tell block now uses “application id” instead of “application”

use scripting additions
use framework "Foundation"

set SDBundle to current application's NSBundle's bundleWithIdentifier:"com.latenightsw.ScriptDebugger6"
set BundleVersion to SDBundle's infoDictionary()'s objectForKey:"CFBundleVersion"
set SystemVersion to system version of (system info)

set SystemBuildNum1 to (current application's NSDictionary's dictionaryWithContentsOfFile:"/System/Library/CoreServices/SystemVersion.plist")'s valueForKey:"ProductBuildVersion"
set SystemBuildNum2 to do shell script "sw_vers -buildVersion" --> 15G1217
tell application id "com.latenightsw.ScriptDebugger6"
	set SDVersion to version
	set ScriptName to name of document 1
end tell
set SystemBuildNum3 to do shell script "sw_vers -buildVersion" --> 15G1217
set SystemBuildNum4 to (current application's NSDictionary's dictionaryWithContentsOfFile:"/System/Library/CoreServices/SystemVersion.plist")'s valueForKey:"ProductBuildVersion"

-- Instead of sending the compiled string to the clipboard I just returned it as a result
set N1 to "#1 Copy " & ScriptName & " SD " & SDVersion & " (" & BundleVersion & ") OSX " & SystemVersion & " (Build " & SystemBuildNum1 & " 1)"
set N2 to "#2 Copy " & ScriptName & " SD " & SDVersion & " (" & BundleVersion & ") OSX " & SystemVersion & " (Build " & SystemBuildNum2 & " 2)"
set N3 to "#3 Copy " & ScriptName & " SD " & SDVersion & " (" & BundleVersion & ") OSX " & SystemVersion & " (Build " & SystemBuildNum3 & " 3)"
set N4 to "#4 Copy " & ScriptName & " SD " & SDVersion & " (" & BundleVersion & ") OSX " & SystemVersion & " (Build " & SystemBuildNum4 & " 4)"

set CombinedOutput to N1 & return & N2 & return & N3 & return & N4

Bill


#19

Thanks. I’m not experienced enough yet to run into binary property list. But if I was experienced I probably would have quickly seen the error of my ways. Now I will know what the error is when that happens.

Why is reading the SystemVersion.plist bad? Is it because it is unreliable, because it causes problems, or both?

Bill


(Shane Stanley) #20

Pretty much always. It’s especially useful when you distribute scripts because you can wrap it in a try block, rather than have the choose application dialog come up if the target app can’t be found.


(Shane Stanley) #21

If there’s an API for doing something, you should generally use it rather than relying on an implementation file.