How to create an OSX Application to wrap a call to a shell script?

My goal is to include in a zip file what amounts to a shortcut, instead of telling my customer(s) to open up Terminal and run a shell script.

My deployable essentially looks like this:

$ unzip Deliverable.zip 
$ cd Deliverable; ls
app.jar run.sh

Script in run.sh:

#!/bin/sh
java -jar app.jar

There's a lot more in the directory; suffice to say I need to run the script from the directory Deliverable since I need to access paths relative to it. However, I can't guarantee where a customer is going to open Deliverable.zip (could be home directory, could be right in the Downloads directory, etc.)

I found this that describes how to create a new workflow in Automator, then save it as an application to launch a shell script. I tried to do that to wrap run.sh but it says it can't find run.sh.

Somebody suggested I use applescript and also sent me a link to how to use applescript to switch into the current directory; there's an applescript "action" in automator; so I made a new workflow with that, and saved it as an application. This is what that looks like:

the automator applescript program

The code:

on run {input, parameters}
    tell application "Finder"
        set current_path to container of (path to me) as alias
    end tell

    do shell script "java -jar app.jar" 

    return input
end run

And this is the error I get when I run it:

welcome to 2015

C'mon, this should be pretty simple. What I am doing wrong here?


Solution 1:

Rename your .sh file to .command and you can cd to the directory that the .command file is located in with the following at the beginning of the file:

cd "$(dirname $BASH_SOURCE)"

Solution 2:

I can see a couple of things wrong there.

Firstly you have opened a workflow and not an Application.

You should choose Application when you make your selection for the type of Automator file.

enter image description here

And the code you have will not work as you expect, since you have not changed directory. (cd).

In the code as you have it, all it is doing is getting the path as an alias and storing it in the variable current_path and in a format unsuitable for the unix commands.

But you do not use it.

So the current directory will most likely be your home folder

At this stage there is no telling what it is trying to launch.

If I run it as you have it I get.

enter image description here

Which makes sense since I do not have Java installed. But if I did I would not expect it to find the right file.

The Applescript need to look like this.

on run {input, parameters}
    tell application "Finder"
        set current_path to container of (path to me) as alias
    end tell

    do shell script "cd " & quoted form of (POSIX path of current_path) & " ;ls | open -fe"

    return input
end run 

In my example

do shell script "cd " & quoted form of (POSIX path of current_path) & " ;/bin/ls | /usr/bin/open -fe"

I cd to the POSIX path of the alias in the variable current_path

i.e from "alias "Macintosh HD:Applications:"" to "/Applications/"

The quoted form of escapes the path using quotes.

I have used the /bin/ls command and pipe it to the open in TextEdit stdin as a demonstration here so that you can test to see if you are getting to the area you expect.

You would use something like;

do shell script "cd " & quoted form of (POSIX path of current_path) & " ;\"java -jar app.jar\""

update:

A another way is just use pure Applescript.

on run {input, parameters}
    tell application "Finder"
        set current_path to container of (path to me)

        set theFile to (current_path) as alias

        open file "java -jar app.jar" of theFile
    end tell


end run

Solution 3:

Path to Script

In your AppleScript, you need to change the current working directory (cwd) before issuing the java command. Do this with an AppleScript line like:

do shell script "cd " & quoted form of (POSIX path of file_path) & " && java -jar app.jar"

The && is important. If the cd succeeds, the java command will be launched within the right cwd. If cd fails, the java command will not be run.

Problems you will likely encounter include:

  • escaping the POSIX path passed to cd; users will have oddly named folders and spaces in their paths.
  • the cd may fail; wrap up your AppleScript in a try block to catch some errors and warn the user.

perl

Personally, I would wrap use a short perl script in place of a bash script.

#!/usr/bin/env perl

use strict;
use warnings;
use FindBin qw($Bin); # $Bin is a path to the script's parent folder

`cd "$Bin" && java -jar app.jar`;

There are much better ways to write this perl snippet but this should work.

Automator Application

The best approach is to work through the problems with your Automator approach. @markhunte's answer discusses how to fix the path and create an application. This should get you most of the way.

Also see AppleScript path relative to script location.

appify — create the simplest possible Mac app from a shell script

Alternatively, you can use Thomas Aylott's appify script to bundle your shell script into an OS X application. Mathias Bynen's article walks through how to use the script, how to create simple Mac apps from shell scripts.

#!/bin/bash

if [ "$1" = "-h" -o "$1" = "--help" -o -z "$1" ]; then cat <<EOF
appify v3.0.1 for Mac OS X - http://mths.be/appify
Creates the simplest possible Mac app from a shell script.
Appify takes a shell script as its first argument:
    `basename "$0"` my-script.sh
Note that you cannot rename appified apps. If you want to give your app
a custom name, use the second argument:
    `basename "$0"` my-script.sh "My App"
Copyright (c) Thomas Aylott <http://subtlegradient.com/>
Modified by Mathias Bynens <http://mathiasbynens.be/>
EOF
exit; fi

APPNAME=${2:-$(basename "$1" ".sh")}
DIR="$APPNAME.app/Contents/MacOS"

if [ -a "$APPNAME.app" ]; then
    echo "$PWD/$APPNAME.app already exists :("
    exit 1
fi

mkdir -p "$DIR"
cp "$1" "$DIR/$APPNAME"
chmod +x "$DIR/$APPNAME"

echo "$PWD/$APPNAME.app"

Community contributed improvements to this script are available:

  • appify - modified plist layout
  • appify - adds an application icon

Code Signing

Once you have created your application bundle, it should be code signed. A code signed app will launch without requiring your clients to disable Gatekeeper.

Code sign your application using your Apple Developer ID and the codesign command:

codesign -s <identity> -v <code-path> …

Code signing is a security technology, used in OS X, that allows you to certify that an app was created by you. Once an app is signed, the system can detect any change to the app—whether the change is introduced accidentally or by malicious code.

Learn more about code signing on the Apple Developer site.