Sign in

Automating my Calendar: Cloud Computing and Hosting on a (GitHub) Budget

How I used GitHub Actions to automatically maintain and host a personalised iCalendar file without hassle, for free.

Maintaining a clean calendar without clutter can be challenging, especially since my calendar partially depends on a public calendar where only a handful of events are applicable to me. Manually downloading and removing the events that are not applicable to me is not possible, as the public calendar I depend on changes frequently. I therefore decided to set up an automated workflow using both GitHub Actions and GitHub Pages that would do this boring and repetitive task for me. What’s even better is that my usage stays well below the usage limits of GitHub Actions, so I can host this workflow as well as my automatically updating iCalendar file for free!

Image of a computer screen showing GitHub.
Photo by Richy Great on Unsplash

The setup itself requires only a minimal effort and time. The only things needed are:

  • The public URL to the iCalendar file we want to extract events from. This is typically the URL that you use to import the calendar in your calendar program, such as Google Calendar,
  • A GitHub repository (it can be a private one),
  • A bit of Python code to create the personalised calendar from the public one, and
  • A file specifying the GitHub Actions workflow.

Without further ado, lets jump straight to the small bit of Python code needed to set up this workflow:

import icalendar
import requests

ICAL_URL = "http://example.com/calendar.ics"
ABBR = "XYZ"

def main():
r = requests.get(ICAL_URL, timeout=30)
cal_str = r.content
cal = icalendar.Calendar.from_ical(cal_str)
my_cal = icalendar.Calendar()
events = cal.subcomponents
for event in events:
# Check if the event is applicable
if event["SUMMARY"].split(";")[0] == ABBR:
my_cal.add_component(event)
with open("mycal.ics", "wb") as fp:
fp.write(my_cal.to_ical())

if __name__ == '__main__':
main()

The code fragment above will request the public iCalendar file, convert the content to an icalendar.Calendar object, and then filter the events that are applicable to me in a new calendar. This new calendar is then written to a file mycal.ics. Notice that, in this case, filtering the applicable events can be done by looking for a specific abbreviation in the summary field of each event. Depending on the specific use case, this check might have to be tweaked a bit.

In order to host this workflow on GitHub, A new GitHub repository should be created. We will assume that the fragment above is committed as src/main.py. Moreover, since this code depends on some modules that are not installed by default, the requirements file src/requirements.txt should be added as well, consisting of the following two lines:

icalendar>=4.0
requests>=2

And that’s all the code we need! Next, it’s time to set up the actual GitHub Action workflow in a file named .github/workflows/main.yml :

name: Update Calendar

on:
schedule:
- cron: "0 */2 * * *"
workflow_dispatch:

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2

- name: Setup Python
uses: actions/setup-python@v2

- name: install required dependencies
run: pip install -r src/requirements.txt

- name: Run script
run: python src/main.py

- name: Git Commit on Workflow
uses: matheusalbino/git-commit@v1.0.1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}

This file largely consists of two parts; the first part defines how the workflow can be triggered, whereas the second part defines what will happen when the workflow is triggered.

Under on: , two triggers are defined. The first one, schedule, specifies that the workflow should trigger automatically every other hour. By changing "0 */2 * * *" to a different value, the interval between two triggers can be changed. Keep in mind that the amount of free computation time is limited on GitHub, so choosing a very short interval is only recommended when the public calendar updates frequently. The second trigger, workflow_dispatch allows the workflow to be triggered manually through the GitHub website. Although this trigger is not strictly needed, it might come in handy while testing and/or debugging the workflow.

The workflow job itself is configured to run on a Ubuntu setup. Different setups are possible, but this option is currently the cheapest in terms of computation time. The different steps are as follows:

  • The repository is checked out, so that we can access our Python code.
  • The Python environment itself is added, so that we can execute our code.
  • The additional modules specified in src/requirements.txt are installed.
  • The Python script is executed and a new calendar file is created.
  • To keep this new calendar after the workflow finishes, the updates in the repository (in this case only the new calendar) are committed to the repository.

At this point, the calendar will be updated automatically by pushing new commits in the GitHub repository. However, unless we want to manually download and add the calendar each time, we still need to create a public URL to this file so that our calendar program can pull new versions automatically. To this end, it suffices to enable GitHub Pages in this repository (more information on this topic can be found here). Once enabled, you can access your calendar through the URL http://<username>.github.io/<repository>/mycal.ics . Keep in mind that this URL is public, even if the repository itself is private, so this approach might not be a good idea if your calendar contains sensitive information. In our case, this should be fine, as the original URL is publicly accessible as well.

While testing this setup, it might look as if the events do not update in e.g. Google Calendar. This is not an issue of our setup, but mostly a limitation of these calendar tools. The reason for this is that they typically cache the iCalendar file to reduce network usage and to avoid flooding the server hosting the file with requests. In my personal experience with Google Calendar, it might take multiple hours before changes are visible in my calendar. Unfortunately, there seems to be no way to manually force Google Calendar to refresh the calendar…

To conclude, although GitHub Actions is mostly intended to assist with the development and deployment process of software products, it can easily be used to automate other small tasks as well. For example, the workflow above could easily be adapted to edit or add details of each event, break one big calendar into multiple smaller ones, or even to combine multiple public calendars into one personalised one.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store