days since this article was written, please be aware of its timeliness
Preface
Recently, I purchased a new Mac Studio equipped with an M1 Max chip and 64GB of RAM, delivering exceptional performance. Meanwhile, in my previous blog processing workflow], I frequently encountered slow network speeds when uploading images pulled from Craft (this step is fast since both are overseas) to Tencent Cloud COS using GitHub Actions.

After some research, I discovered that GitHub does not support setting the region for Action servers]. Given this limitation, I decided to move the publishing workflow locally to speed up the build process, hence this article. Below, AppleScript will be abbreviated as AS.
Why Use AppleScript Instead of Other Options?
First and foremost, I wanted to minimize the operational steps and complexity, so I defined the workflow as follows:
- Click the publish button in Craft.
- Perform a local build while displaying the build log in the terminal.
- Push the build results to GitHub.
Since this requires one app to invoke another via a link, AppleScript is the only viable option. However, although AS is used, it essentially executes the existing JavaScript files—just moving the workflow tasks from craft_publish_ci] to local execution.
Introduction to AppleScript
Conceptual diagram:

What AppleScript Can Do
- Interact with users, such as responding to input or sending notifications via Notification Center.
- Control other apps—either directly if the app provides AS interfaces or indirectly by manipulating UI elements like buttons if no interface is exposed.
- Automate tasks like sending emails, running scheduled scripts, opening music players, locking the screen, or executing scripts in response to custom URL Schemes.
Basic Syntax
- Prefix a line with
---(three hyphens) or#to comment it out. - Use
(*``*xxx**``)(parentheses + asterisk) to wrapxxxfor block comments. - The rest is similar to spoken language, but if you’re unsure about English grammar (e.g., the nuances of “besides,” “against,” or “over”), stick to basic syntax.
Forms of AppleScript
AppleScript commonly comes in two forms: Script and Application. Here, I’ll only cover these two, as I haven’t used Script Bundle or Text.

Note: Even with identical code, the interface differs when saved as a script versus an application.
Script
A script is an executable file, similar to JS, but AS runs in the macOS desktop environment while JS runs in Chrome. Double-click to execute. Below is the progress display of a script process, launched by clicking the “Run” button in the script’s top-right corner:

Application
An AS saved as an Application has the same suffix as regular apps (e.g., Safari, Chrome, WeChat) and ends with .app. You can also inspect its package contents. It supports multiple launch methods: URL Scheme invocation, double-clicking, or dragging content onto the app icon. You’ll need to write event handlers to respond to user actions. Below is the progress display of an application process, launched by double-clicking “xxx.app”:

Others
Beyond these two, other forms are less common. However, if you need to save a script as a Service to invoke it from the menu bar or right-click context menus, you’ll need a different format:

Saving the script as a system Service displays the progress bar in the top menu bar:

Common Operation Code
The following operations are based on the “Application” type of AS. The syntax can be quickly tested in the terminal using
osascript -e '语法内容'. Note that multiline expressions, like progress bars, cannot be executed viaosascript. While you can chain multiple-eparameters for multiline commands liketell(e.g.,osascript -e ‘tell application “Finder”’ -e ‘end tell’), this doesn’t work for progress bars.
User Interaction
Display Messages in Notification Center
1 | |

Pop-up Dialogs
1 | |

Get User Input
1 | |

Note: AS cannot generate form-like components, only dialog boxes like the one above, similar to prompts in web pages.
Play Given Text
1 | |
Others
Allowing users to select folders, files, colors, or choose an item from a list is temporarily omitted.
Execute Command Line
1 | |
Note: The PATH environment variable in the command line is
/usr/bin:/bin:/usr/sbin:/sbin, so it cannot execute commands likenodeornvmthat were installed later. These commands require manually specifying their locations when executed.
Execute a Node script:
1 | |
Retrieve Output from the Previous Statement
Directly use result immediately after the previous statement to represent the result:
1 | |

Display Debug Information
Simply use the log statement, then retrieve the output in the Replies section below Script Editor. Here, you can see the content output to the console during the execution of the shell script command, such as console.log when executing a JS script.
1 | |

Show Progress
Displaying progress uses progress, and the syntax is relatively complex because it needs to show different statuses, requiring multiple lines:
1 | |
Related screenshots can be seen above.
Execute After a Delay
1 | |
n represents the number of seconds, supporting decimals. This is generally used to simulate user operations with delays, preventing issues where scripts run too fast before the page loads. Some odd problems can also be resolved by delayed execution. This statement can be likened to the setTimeout technique in js.
Call Other Applications
Note: This will trigger a system warning requiring your confirmation to execute:

After confirmation, you can verify in 设置-安全与隐私-隐私-自动化:

You can call other applications to perform operations. Below is an example of launching the Terminal app, activating it, and executing a command:
1 | |
This statement includes a check: if the first opened window exists, it activates it and executes the command (do script) inside; if not, it creates a new window and executes the command there. Since each do script opens a new Terminal, this check ensures window reuse. However, note that if window 1 already has an existing process (e.g., an unstopped server), the command will not execute and requires additional checks, which are not elaborated here.

Respond to URL Scheme
Sometimes, you don’t want to first locate the AS script and double-click to run it, or you need to trigger your AS app from another application. This is where URL Scheme comes in. There’s an introduction on ](https://example.com) you can check out.
First, you need to modify the Info.plist file. You can find it in Finder by right-clicking “Show in Finder” in Script Editor’s sidebar:

Navigate up one level to see the file:

The content is XML-like. Edit it and add the following field:
1 | |
Then verify by opening the file in Xcode to see a similar entry:

This indicates success.
This operation means the AS app will respond to URLs starting with xhelper (you can modify this to any prefix you want, as long as it doesn’t conflict with existing ones).
After setting this up, you need to save it in Script Editor, or cmd + L it, or run it once (or do all three) to recompile and make it effective.
To make AS respond to the URL, you need to set up an event listener:
1 | |
In the code above, this_URL is the link opened with your xhelper.
Now, test a link in a browser (the screenshot shows Chrome):

The AS app will respond and display a popup showing xhelper://你好!. Note that no URL encoding is required here.
My Usage
Here, I share my Craft build code, which uses the URL Scheme mentioned above. The Node-executed JS scripts are adapted from previous workflows ](https://example.com), with no new logic. Additionally, AS syntax is quite colloquial, so each line’s meaning is self-explanatory, and I won’t explain them one by one.
1 | |
Result:

Reference Links
-
AppleScript Tutorial (Written on Notion)
-
Concise AppleScript Fundamentals (Authored by iDoraemon Nathan)
I often wish that when facing some key decisions in life, someone could tell me the best course of action so that I would not waste my precious time. Putting myself in others' shoes, I therefore write blogs often, hoping to record in this tiny corner of the vast Internet the once-in-a-lifetime experiences that matter to me, and to help those who seek help.