Using Zapier to import GitHub issues into OmniFocus

I use OmniFocus for personal task-tracking and Zapier to automate work when possible.  I also coordinate open source work on GitHub. Recently I was wondering if there was a way to make OmniFocus automatically import any GitHub issue assigned to me. It turns out there is!

What you need:

Optional tools include Hazel or Lingon to automate the running of the ParseInbox applescript.

Before I continue, thank you to Joe Buhling for sharing his collection of OmniFocus scripts!

Part One: Create zap on Zapier

If you haven’t already, enable OmniSync and create a Mail Drop email address. You’ll also need a GitHub account and there should be a newly created issue assigned to you.

To get started, while logged into Zapier, click MAKE A ZAP! button.

Step 1: GitHub app, new issue trigger

Select GitHub as the trigger app:

Screen capture of selecting "GitHub" as trigger app.
Select “GitHub” as trigger app.

Next, select New Issue as the GitHub trigger:

Screen capture of selecting "new issue" as GitHub trigger.
Select “new issue” as GitHub trigger.

Next, set up the GitHub new issue trigger according to your preferences:

Screen capture of setting up GitHub issue trigger according to your preferences.
Set up GitHub issue trigger according to your preferences.

In my Zap, I’ve selected Only issues assigned to you and for the time being, I’ve limited it to a single GitHub organization. What you select is up to you.

Next, test this step to ensure it is retrieving the data from GitHub that you expect it to. Before you run the test, Zapier will remind you to have a recently created issue that matches your trigger options:

Screen capture of testing your GitHub new issue trigger.
Test your GitHub new issue trigger.

Once everything looks good, save the step.

Next, you’ll create a formatter step to format any dates attached to GitHub issues via their assigned milestones.

Step 2: Formatter app, date/time action

The app you’ll select for this 2nd step is Formatter by Zapier:

Screen capture of selecting step 2 app as Formatter by Zapier
Step 2 app is Formatter by Zapier.

The action you’ll use for Formatter is Date / Time:

Screen capture of using the date action for app Formatter.
Use the date action for app Formatter.

Next, set up the Date / Time action. You’ll want to set the following fields accordingly:

  • Transform: Format
  • Input: Step 1 Milestone Due On
  • To Format: MM/DD/YY
Screen capture of configuring date transform.
Configure date transform action.

Now test the action to ensure the result is as you expect. You’ll see something like:

Test results for date formatter action.
Test results for date formatter action.

Once everything looks good, save the step.

Step 3: Code by Zapier app, run Python action

In this step, I’m using Python code to map GitHub repository names to OmniFocus folders and GitHub milestones to OmniFocus projects within those folders. If you have a different organizational scheme, you’ll want to modify the code in this step accordingly.

First, select the Code by Zapier app:

For Step 3, select the Code by Zapier app.
For Step 3, select the Code by Zapier app.

Next, select Run Python as the Code by Zapier action:

Select Run Python as Code by Zapier action.
Select Run Python as Code by Zapier action.

(You could also select Run Javascript and re-write the Python code below in Javascript.)

Next, configure the Input Data for use with our custom python code. You’ll want to set the following:

  • repo: Step 1: Repository Name
  • milestone: Step 1: Milestone Title

The names of the fields on the left doesn’t really matter, but it must match the key names we’ll use in our Python code.

Configure input data for custom python code.
Configure input data for custom python code.

Next, you’ll enter the following Python code into the Code field:

# want to set the project as
# repo milestone
# or just repo if no milestone

output = {'project' : input_data.get('repo').replace("-", " ")}

if input_data.get('milestone'):
    repo = input_data.get('repo')
    milestone = input_data.get('milestone')
    project = repo.replace("-", " ") + ' ' + milestone.replace("-", " ")
    output = {'project' : project}

If the issue being processed by Zapier has a milestone, this code sets project to Repository name milestone name, replacing any hyphens with spaces. Otherwise, it sets project to simply Repository name, also replacing any hyphens with spaces.

This works for me because I organize NumFOCUS projects in GitHub like this:

  • [repository] my-project
    • [milestone] milestone 1
    • [milestone] milestone 2

And in OmniFocus, I organize projects like this:

  • [folder] My Project
    • [project] Milestone 1
    • [project] Milestone 2

If you structure your projects differently, you’ll need to update the Python code above accordingly.

When you’re ready, test the Python code and check to see that it creates the expected output:

Results of testing custom Python code.
Results of testing custom Python code.

When everything looks good, save this step and continue on to creating the 4th and final step.

Step 4: OmniFocus app, create task action

In this last step you’ll configure the OmniFocus app to create an OmniFocus task with information from the retrieved GitHub issue.

First, select the OmniFocus app:

Select OmniFocus app for Step 4.
Select OmniFocus app for Step 4.

Now select the Create Task action for the OmniFocus app:

Select create task action for OmniFocus app.
Select create task action for OmniFocus app.

Next you’ll need to connect your OmniFocus account if you haven’t already and select which connection you’d like to use.

Next, set up the create task action. You’ll configure only the Title field as such:

--Step 1 Title @GitHub ::Step 3 Project #Step 2 Output //Step 1 Html Url

Let’s break this down:

  • The -- sets the name of the task.
  • The @ sets the context.
  • The:: sets the the name of the project.
  • The# sets the due date.
  • The// sets the text of the note.

Two notes:

  • The name of the project is fuzzy matched against flatted name of folders and projects, so you don’t need to use a colon between folder and project name.
  • With the applescript I’m using to parse OmniFocus’ Inbox, I had trouble with dates including time, so this is why I simplify the due date format in Step 2 of the Zap.
  • If you wanted to dynamically set the name of the context based on some attribute of the GitHub issue (e.g. label), you could do that by modifying the Run Python action in Step 3.

For details on the syntax used for parsing the inbox, see this post.

Here’s what the the task looks like in Zapier:

Configure create task omnifocus action.
Configure create task OmniFocus action.

As always, test the action before proceeding to make sure everything looks right:

Test create task OmniFocus action.
Test create task OmniFocus action.

If this looks good, click Create & Continue to create the task. Once you do this, flip over to OmniFocus and wait for the task to appear in your Inbox. It’ll look something like this:

New task in OmniFocus Inbox.
New task in OmniFocus Inbox.

Now you’re ready to setup the script to parse that monster-looking task out of your OmniFocus Inbox and into the right spot!

Part 2: Parsing tasks in OmniFocus’ Inbox

Step 1: Manually run the ParseInbox script

For this part, if you haven’t already, you’ll want to grab a copy of the AutoParser scripts from either the original author or myself.

The repositories linked to above contain a collection of applescripts for use with OmniFocus. (Thank you Joe Buhling for putting these together!)

There are two main options for running the script manually.

Option 1: You can run any of the scripts from the command line with the osascript command:

/usr/bin/osascript "/Users/christie/Bin/OFScripts/Auto-Parser/ParseInbox.applescript"

Option 2: If you don’t want to use the command line to run scripts, you can copy the ParseInbox.applescript into OmniFocus’ scripts folder. To find out where this is, go to Help > Open Scripts Folder in OmniFocus and it will open a new finder window at that location. Once you do this, you’ll see Script: ParseInbox as an option in the View > Customize Toolbar… window. Drag this icon to your toolbar for ease of use.

When you run the ParseInbox script, it will transform the Inbox task Zapier created that looks like this:

--install certbot @GitHub ::sustainbility index project kick off #06/08/17 //

Into the task install certbot, belonging to the project Project Kick Off in the folder Sustainability Index. The task will now have a due date of 6/8/2017, and note text that includes a link back to the original GitHub issue:

Task in OmniFocus after it has been parsed from Inbox.
Task in OmniFocus after it has been parsed from Inbox.

If at this point you realize that your Zap isn’t quite configured correctly or exactly how you want it, you can go back and adjust it. And, if you get tired of waiting for OmniFocus to sync with the server to retrieve the new task, just remember you can copy and paste the test output from Step 4 of your Zap.

Step 2 (optional): Automatically running ParseInbox

This step is totally optional and you can skip it if you’re happy manually running the script when you want to parse Inbox items.

However, if you don’t want to have to remember to do this, or if you want OmniFocus to be able to process Inbox items while you’re out and about, then you’ll want to automate it.

There are a few options for doing this. They all require your computer be on, but OmniFocus doesn’t have to be open (the script will open it if closed).

Option 1 is to use Hazel to run the script when your OmniFocus has been updated. Joe explains how to configure this option on his blog here. I had mixed results with this method. The script seemed to run sometime and not others. YMMV.

Option 2 is to schedule the script using launchd (macOS’s version of cron). This involves editing plist files, which I hate doing, so I bought Lingon X to make this easy.

Here’s what my settings for Lingon look like:

Lingon settings for scheduling ParseInbox script.
Lingon settings for scheduling ParseInbox script.

And the plist generated by Lingon looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "">
<plist version="1.0">