AppleScript and the GitHub API
AppleScript is a powerful scripting language that allows to directly control scriptable applications and some parts of macOS. The language itself can be a little bit frustrating for those who are not familiar with natural language programming paradigm. For example, with AppleScript, you can tell application "Preview" to quit
or ask someone to print pages 1 thru 5 of document 2
.
But my goal is not to promote or demote AppleScript. Instead, I wish to share a little script that I’ve fetched while experimenting with OmniFocus 2. I hate to manually capture issues that I care about, so I started to work on a script that takes URL from an active tab in Safari and sends it to OmniFocus into the right project with useful title and body.
For example, I want d12frosted/flyspell-correct#30 to go into the flyspell-correct
project with title and description taken from that issue.
Without going into too many details about irrelevant parts of that script, I just wish to share the part that communicates with GitHub. Note that this script is not part of any repository and I doubt that it ever will. Mostly because it’s unfinished and I don’t want to maintain full API support. And any feedback is welcome (I am so very new to AppleScript!).
Here is an example of getting the title of d12frosted/private-repository#42
. First we setup GitHubClient
by setting the path to jq and by setting credentials. Then we ask it to get a specific issue and select from it the title. Select verb accepts any(?) jq
command. The latest verb commit
does all the dirty job - sends a request to GitHub and then pipes the result to jq
.
property jqPath : "/usr/local/bin/jq"
property githubUser : "d12frosted"
property githubAccessToken : "ACCESS_TOKEN"
tell GitHubClient
setupJQ(jqPath)
authorise(githubUser, githubAccessToken)
getIssue("d12frosted", "private-repository", 42)
select(".title")
commit()
end tell
Here is the implementation of GitHubClient
.
property _username : missing value
property _access_token : missing value
property _api_url : missing value
property _method : missing value
property _selector : missing value
property _jq_path : "jq"
on authorise(username, access_token)
set _username to username
set _access_token to access_token
end authorise
on setupJQ(path)
set _jq_path to path
end setupJQ
on select (selector)
set _selector to selector
end select
on commit()
local prefix, cmd
set prefix to "export LANG='" & user locale of (system info) & ".UTF-8'; shopt -s compat31; "
set cmd to "curl "
if _username is not missing value and _access_token is not missing value then
set cmd to cmd & "-u " & quoted form of (_username & ":" & _access_token) & " "
end if
set cmd to cmd & quoted form of _api_url
if _selector is not missing value then
set cmd to cmd & " | " & _jq_path & " " & quoted form of _selector
end if
tell me to do shell script prefix & cmd
return result
end commit
on apiGET(endpoint)
set _method to "GET"
set _api_url to "https://api.github.com/" & endpoint
end apiGET
This is enough to implement different methods like getIssue
or getRepoContributors
.
# API - Repos
# https://developer.github.com/v3/repos
on getMyRepos()
apiGET("user/repos")
end getMyRepos
on getRepos(owner)
apiGET("users/" & owner & "/repos")
end getRepos
on getOrgRepos(org)
apiGET("orgs/" & org & "/repos")
end getOrgRepos
on getRepo(owner, repo)
apiGET("repos/" & owner & "/" & repo)
end getRepo
on getRepoContributors(owner, repo)
apiGET("repos/" & owner & "/" & repo & "/contributors")
end getRepoContributors
on getRepoLanguages(owner, repo)
apiGET("repos/" & owner & "/" & repo & "/languages")
end getRepoLanguages
on getRepoTags(owner, repo)
apiGET("repos/" & owner & "/" & repo & "/tags")
end getRepoTags
# apiGET - Issues
# https://developer.github.com/v3/issues
on getIssue(owner, repo, issueNumber)
apiGET("repos/" & owner & "/" & repo & "/issues/" & issueNumber)
end getIssue
And so on.
While this implementation works for me, it has several drawbacks.
- If you wish to get several values from API call, you have to call
commit
for everyselect
verb. It means that the same request will be sent multiple times. GitHubClient
doesn’t support any other authentication method except for login-pass(token) pair.