Github Actions, Docker, and the Beloved Cron

March 14, 2021

I've written before about my love for Github Actions and Docker.

A little over a week ago I tweeted that I was interested in writing a blog post about using Github Actions and Docker to schedule a job to do something interesting, but I didn't have a fun use case.

Fortunately, my internet friends delivered.

So I decided to write a quick script to do this, and that's how I spent my Friday evening as my wife and I watched Avengers. πŸ˜‚

I've provided a link to the Github Repository with the full code but here's the short instructions if you're interested.

1. Register for a developer account on Twitter and get your API credentials

This is a pretty easy process and you just sign up and outline what you're doing (it's also free). Note that you'll need to store 4 variables in a .env file like below:

1TWITTER_API_KEY=
2TWITTER_API_SECRET=
3TWITTER_ACCESS_TOKEN=
4TWITTER_ACCESS_TOKEN_SECRET=

2. Write a Python Script to Tweet a Dog Photo

This was pretty straightforward thanks to the Tweepy library in Python and this super random dog photo API (God bless the internet).

Here's what that Python code looks like:

1import os
2import json
3import requests
4import tweepy
5
6def get_random_dog(filename: str='temp') -> None:
7    r = requests.get('https://dog.ceo/api/breeds/image/random')
8    rd = json.loads(r.content)
9    r2 = requests.get(rd['message'])
10
11    with open(filename, 'wb') as image:
12        for chunk in r2:
13            image.write(chunk)
14
15def main(message: str, filename: str='temp') -> None:
16    auth = tweepy.OAuthHandler(
17        os.environ.get('TWITTER_API_KEY'), 
18        os.environ.get('TWITTER_API_SECRET')
19    )
20    auth.set_access_token(
21        os.environ.get('TWITTER_ACCESS_TOKEN'), 
22        os.environ.get("TWITTER_ACCESS_TOKEN_SECRET")
23    )
24    api = tweepy.API(auth)
25    get_random_dog(filename) 
26
27    try:
28        api.verify_credentials()
29        print("Twitter Authentication Succeeded")
30    
31        try:
32            api.update_with_media(filename, status=message)
33            print('Tweet successfully sent!')
34
35        except Exception as e:
36            print('Error sending tweet \n %s' % e)
37    except:
38        print("Twitter Authentication Failed")
39
40
41if __name__ == '__main__':
42    main("Hey, @camdotbio! πŸ‘‹ \n\nHere's your daily dog photo!")

3. Create a Docker Container to run the Python Script

This is a contentious area where some may argue docker is execessive for this but I use different computers with different operating systems so this works for me and I like it.

In short, I created a Dockerfile and a docker-compose file where I run the script. The benefit of this is that I don't have to worry about this script not working on my Linux machine or not working on my Mac, it works on both!

4. Use a Github Action to Schedule a Cron Job

This was also straightforward and I've copied the code below:

1name: Build and Deploy πŸš€
2
3on:
4    schedule:
5        - cron: '0 15 * * *'
6
7jobs:
8    build:
9        runs-on: ubuntu-latest
10
11        steps:
12            - uses: actions/checkout@v2
13
14            - name: Make envfile
15              uses: SpicyPizza/create-envfile@v1
16              with:
17                  envkey_TWITTER_API_KEY: ${{ secrets.TWITTER_API_KEY }}
18                  envkey_TWITTER_API_SECRET: ${{ secrets.TWITTER_API_SECRET }}
19                  envkey_TWITTER_ACCESS_TOKEN: ${{ secrets.TWITTER_ACCESS_TOKEN }}
20                  envkey_TWITTER_ACCESS_TOKEN_SECRET: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }}
21                  file_name: .env
22
23            - name: Build the docker container
24              run: docker build .
25
26            - name: Run the script πŸš€
27              run: docker-compose up

Note that you have to create Action Secrets in your Github Repository (available in the Settings tab) and add the credentials from Step 1.

Conclusion

And that's it! 🐢

Pushing the code to GitHub handles the rest, isn't that wonderful?

Anyways, thanks to my internet friends for the fun idea. I didn't end up sending it to Camilo's phone number because I would have to pay Twilio $0.0075.


Have some feedback? Feel free to let me know!