• Shopping Cart Shopping Cart
    0Shopping Cart
London App Developer
  • Courses
  • Consulting
  • Tutorials
  • About
  • Click to open the search input field Click to open the search input field Search
  • Menu Menu
DevOps, Django, Git, Git Flow, GitHub Actions, Python, Tutorials, Uncategorized

How to use GitHub Actions

In our advanced course on building REST APIs, we teach how to use Travis-CI to run unit tests and linting for your project.

Since launching the course, GitHub has launched their own CI/CD workflow tool called GitHub Actions and many students have asked how to use that instead.

In this post I’m going to show you how to setup a basic workflow using GitHub Actions.

You can find the full completed source code for this tutorial here: github.com/LondonAppDeveloper/gh-actions-completed-code

What we’ll be doing

We’ll do the following:

  1. Clone a sample project we’ve provided for testing
  2. Configure GitHub Actions on the project
  3. Setup a job that runs when new changes are pushed
  4. Configure a staging deployment job to run if merging into the main branch
  5. Configure a production deployment job to run if merging into the production branch

Note: For the last two steps, we won’t be performing an actual deployment, but setting up a sample job to simulate it.

Deployment workflow

Even though we are using GitHub for this tutorial, we’ll be implementing a workflow process that aligns with GitLab Flow, which works as follows:

  1. Projects contain both a main (formally master) and production branch
  2. Changes are implemented on branches prefixed by feature/ or bugfix/
  3. Pull Requests are used to merge changes into main
  4. Code merged into main is automatically deployed to a staging environment
  5. Deployments to a production environment are triggered by merging main into a branch called production

If you want to learn more, you can find the full detailed explanation on the Introduction to GitLab Flow page.

If you don’t want to use this workflow, you should be able to modify what’s taught in this guide to create your own.

Cloning the project

The project we’ll be using is a Django application that is setup to run within a Docker.

Note: We’re not going to cover the project setup in this guide, but if you want to learn about it we have a separate tutorial for this on YouTube: Prepare a Django app for Deployment using Docker.

Head over to the gh-actions-starter-code project, and select Fork to make a copy on your own GitHub account.

Screenshot of GitHub project with Fork option highlighted.
GitHub project fork option.

Once complete, you should see the project in your own GitHub account.

Screenshot of project forked to GitHub account.
Forked project in GitHub.

Select Clone and copy the clone URL by clicking on the clipboard icon.

Screenshot of the Clone and clipboard icons on GitHub

Open up the Terminal (macOS) or Command Prompt/PowerShell (Windows), and enter the following command:

git clone <paste your clone URL>

Once done, it should look like this:

Screenshot of Terminal cloning project.
Screenshot of cloning project.

Note: It may look different depending on your Operating System setup. Also, the clone URL should be the one from your account.

Now you’re ready to configure the project to use GitHub Actions.

Configure GitHub Actions

Next we’re going to make the necessary changes to the project code to configure GitHub Actions.

In order to do this, we must setup something called a Workflow.

Workflows need to be defined as YAML files stored inside a directory called .github/workflows in the root of our project.

Since we’re creating a workflow for continuous deployment, we’ll create our configuration in cd.yml, and store it in the full path: .github/workflows/cd.yml

Go ahead and create that file now:

Gif of VSCode with project open
Create a new cd.yml file

Running a simple job

Once you’ve created the file, add the following contents:

---
name: Continuous Deployment

on: push

On the first line we have --- which is used in YAML to indicate the start of the file.

Then we have name: Continuous Deployment. This is the name of our workflow and appears inside GitHub Actions.

After that we define an on: push line, which is used to tell GitHub Actions when we want to run the job. In this case, we’re setting it up to run every time changes are pushed to GitHub.

Next, let’s add the following to our .github/workflows/cd.yml file:

jobs:
  hello-gh-actions:
    name: Hello GitHub Actions
    runs-on: ubuntu-20.04
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: Run Tests
        run: echo "RUNNING TESTS!"

The above block of code defines a job for our workflow.

I’ll break this down line to explain what’s going on…

The jobs: line is the start of the block. Everything indented below it will be a job or part of the job specification.

Then we have hello-gh-actions:, this defines a new job within our jobs block. This can be anything you want, as long as it’s a unique name (you can’t have two jobs with the same name) and starts with an alphanumeric value (for more details, see the official docs for jobs.<job_id>).

After that, we have name: Hello GitHub Actions, which sets the name that will be displayed in the GitHub Actions page which we’ll see later.

Next we specify runs-on: ubuntu-20.04 which sets the type of machine you want to run your job on. GitHub Actions provided various hosted runners which you can use for free (up to a limit). In our example, we’re using Ubuntu (20.04). You can find the full list here.

Next we define the steps: block. This contains each step that you want to run for your job. Each step corresponds to a command you want to run for your job. The steps are executed in the order they are defined.

Our sample job above has two steps, the first is:

      - name: Checkout
        uses: actions/checkout@v2

This step is used to check out project out in Git. If you’ve used other tools such as GitLab CI/CD or Travis-CI then you may be expecting this to happen automatically. However, with GitHub Actions, this step needs to be done manually for each job. I assume the reason is to save time if you want to run a job that doesn’t need to use the code in your project (for example, to notify a Slack channel for a deployment).

The -name: Checkout part is the name of the step, and uses: actions/checkout@v2 will use GitHub’s pre-built “action” to run this task. You can see the docs for this action here.

Finally, we define the follow step:

      - name: Run Tests
        run: echo "RUNNING TESTS!"

As you may expect, this is a dummy step we added simply for testing GitHub Actions.

Let’s go ahead and test this job. We can do this by committing and pushing the change to GitHub, then heading over to our GitHub Project and selecting the Actions tab.

Gif showing the git push process and the action starting.

Once you’re on the actions page, you should see an entry with the last commit message you pushed. Click the message to open the workflow:

Screenshot of GitHub Actions workflow list page

On the workflow page, you should see an entry for our Hello GitHub Actions job which we defined above. Click on that job to see the details:

Screenshot of job within running workflow

This will take you to the job page which contains a list of all steps of the job. Keep in mind that GitHub Actions automatically adds some steps like Set up job, Post Checkout and Complete job as part of the process. You should see our Checkout and Run Tests jobs sandwiched in-between.

You can select any one of these jobs to expand the details.

Screenshot of steps within Workflow job

For our Run Tests job, you can see that our echo command was run successfully:

Screenshot of expanded steps within workflow job

This is particularly useful if a job fails, because you would see any errors from a failed command here.

Running real jobs

Next we’re going to create a real job that actually runs some unit tests, and we’ll throw in some linting for good measure.

As mentioned earlier, this tutorial uses a sample project that has been setup to run unit tests through Docker, and we’ll be doing that in the job we create.

Delete whole jobs: block including and everything below it.

Then, replace it with the following:

jobs:
  test:
    name: Test
    runs-on: ubuntu-20.04
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: Test
        run: docker-compose run --rm app sh -c "python manage.py test"

  lint:
    name: Lint
    runs-on: ubuntu-20.04
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: Lint
        run: docker-compose run --rm app sh -c "flake8"

Important: Pay attention to the indentation, as this is important for YAML files.

This will add two separate jobs to our project.

The Test job (defined first) runs the unit tests for our project.

The command for running tests on a Django project is usually python manage.py test.

However, since we configured the project to use Docker, we’re running this using docker-compose, which is available as part of the ubuntu-20.04 runner we’re using.

The second job we define is Lint. This is almost identical to the Test job, except the command is modified to run flake8 instead of the test command.

Choosing when jobs run

As mentioned previously, we use the on: block to determine when workflows are run.

This is useful because the resources used to run jobs cost money. Although GitHub provides a generous 2,000 free minutes per month (usually more than enough for small projects), you’ll need to part with some cash if you have projects that require more job minutes.

Therefore, it’s useful to be configure jobs to only run in certain cases.

For example, you may only need unit testing and linting to run when you make a new pull request to merge changes.

We can do this by modifying the on: block as follows:

on:
  push:
    branches:
      - main
      - production
  pull_request: []

This change will make the workflow run in the following cases:

  • A push to main or production branches (this includes pull requests being merged into these branches)
  • A new pull request being created

Because most developers will work from a feature or bugfix branch (or at least they should), making this change will drastically reduce the job minute budget, because jobs will only run in the above scenarios, instead of every time a push is made.

Running deployment jobs

When including deployment in your pipeline, it’s useful to be able to run jobs conditionally, based on the branch being affected.

This can be acheived by using the if syntax for our job.

Let’s add two new jobs to our project, ensuring the indentation of the new jobs is level with the existing test: and lint: lines.

  deploy-staging:
    name: Deploy to Staging
    runs-on: ubuntu-20.04
    needs: [test, lint]
    if: github.ref == 'refs/heads/main'
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: Deploy to Staging
        run: echo "Deploy to STAGING code here"

  deploy-prod:
    name: Deploy to Production
    runs-on: ubuntu-20.04
    needs: [test, lint]
    if: github.ref == 'refs/heads/production'
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: Deploy to Prod
        run: echo "Deploy to PRODUCTION code here"

Note: We are not actually doing any deployment in the above example, but just adding placeholder code where the deployment code would be in a real project.

In the code above, we are adding a deploy-staging and deploy-prod job.

These new jobs are very similar to our test and lint jobs, except for the follow…

We’ve added needs: [test, lint]. This ensures the job will only run after the test and lint jobs have completed successfully (our job needs the specified job to complete before it runs).

The next addition is the the if: syntax which will ensure the job only runs if the specified condition is met. So, with the following code, we run the deployment jobs as follows:

  • deploy-staging will run if the branch is main — meaning code pushed to the main branch will be deployed to staging environment
  • deploy-production will run if the branch is production — meaning code pushed to the production branch will be deployed to the production environment

Now that’s all setup, let’s test some changes.

Testing our workflow

Now we’ve configured GitHub Actions, let’s do some tests to ensure it works.

Start by checking out a new branch locally, for example:

git checkout -b feature/cool-new-feature

Then, let’s break our code by opening up app/home/views.py and changing our the def index(request): line to the following:

def index(request):
    """Render index page."""
    return None

This should make our unit tests fail.

Commit the change and push it to GitHub.

git commit -am "Oops, broke the code."
git push --set-upstream origin feature/cool-new-feature

Head over to the Actions tab on your GitHub project, and notice that the job does not run. This is because we’ve configured jobs to only run when changes are made to the main and production branches, or a pull request is made.

Let’s create a pull request from our feature/cool-new-feature branch into main.

Git of creating a new Pull Request

After a few seconds, you should see the Test and Lint jobs start.

Then, after about a minute, the jobs should fail.

You can click Details to view the details of the failed jobs.

Screenshot of jobs running within pull request

Scroll to the bottom of the log output and you’ll see that the job failed because the unit tests didn’t complete successfully.

Screenshot of failed job within pull request

Now let’s go back to app/home/views.py and put it back to what it was before:

def index(request):
    """Render index page."""
    return render(request, 'home/index.html')

Then, commit the changes, and push them to GitHub.

git commit -am "Fixed broken tests."
git push origin

Head back to your Pull Request and you’ll see the jobs automatically restart.

Screenshot of checks being run

After a few minutes they should complete successfully…

Typically this is when a co-worker would do a peer review. But since we’re going lone wolf, as soon as you see two green ticks, select Merge pull request and Confirm merge.

Screenshot of successful jobs within pull request

Once done, you can delete the feature branch (optional, but recommended) and head over to the Actions tab where you’ll find a workflow running for the latest merge.

Screenshot of merge job running

You can click on the Merge pull request #1 text in to open the workflow.

After a few minutes, you’ll see something like this:

Screenshot showing workflow jobs for Test, Lint and Deploy to Staging.

Notice that the Test and Lint jobs ran again (just to ensure they still pass before deploying) and then the Deploy to Staging job runs.

This is where you (or ideally a QA team) would do tests on the staging environment to validate any new features.

Once you’re happy with the changes, you can create and push a new branch called production.

git checkout -b production
git push --set-upstream origin production

Note: After you’ve created the branch the first time, you would merge changes from production < main using a pull request instead.

Then check your Actions page again, and you’ll see the production deployment process running.

A few moments later, you should see this:

Screenshot of workflow jobs for Test, Lint and Deploy to Production

So that’s how you use GitHub Actions to setup a simple CI/CD workflow.

Tags: CI/CD, Continuous Deployment, Continuous Integration, GitHub, GitHub Actions
Share this entry
  • Share on Facebook
  • Share on X
  • Share on WhatsApp
  • Share on Pinterest
  • Share on LinkedIn
  • Share on Tumblr
  • Share on Vk
  • Share on Reddit
  • Share by Mail
https://i0.wp.com/londonappdeveloper.com/wp-content/uploads/2021/04/Artboard-1-4.png?fit=606%2C474&ssl=1 474 606 mark https://londonappdeveloper.com/wp-content/uploads/2018/12/lon_website_logo-300x110.png mark2021-04-19 08:30:002021-05-02 18:52:08How to use GitHub Actions
0 replies

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Pages

  • About
  • Consulting
  • Home
  • Tutorials

Categories

  • AI
  • Android
  • Android Studio
  • Angular
  • Aptana Studio
  • AWS
  • Bootstrap
  • Career advice
  • Celery
  • Databases
  • Deployment
  • DevOps
  • Django
  • Django REST Framework
  • Docker
  • Eclipse
  • Getting Help
  • Git
  • Git Bash
  • Git Flow
  • GitHub Actions
  • Google App Engine
  • IDE
  • Ionic Framework
  • iOS
  • JavaScript
  • Job Search
  • Links
  • Linux
  • Mac OS X
  • macOS
  • Node.JS
  • OpenAI
  • PGAdmin
  • Postgres
  • Productivity
  • Python
  • React
  • Salt Stack
  • Stack Overflow
  • Tools
  • Travis-CI
  • Tutorials
  • Ubuntu
  • Uncategorized
  • Vagrant
  • VSCode
  • WebSockets
  • Windows 10
  • WordPress

London App Developer Ltd,

71-75 Shelton Street, Covent Garden, London, United Kingdom, WC2H 9JQ

Company registration number: 09718346

© London App Developer - London App Developer Ltd, Company Registration Number: 09718346, VAT: GB224061842
  • Link to X
  • Link to Youtube
  • Link to Facebook
  • Link to Reddit
Link to: How to use Terraform via Docker Compose for Pros Link to: How to use Terraform via Docker Compose for Pros How to use Terraform via Docker Compose for ProsBlog title with AWS Terraform and Docker logos Link to: Debugging a Dockerized Django app with VSCode Link to: Debugging a Dockerized Django app with VSCode Debugging a Dockerized Django app with VSCode
Scroll to top Scroll to top Scroll to top

This site uses cookies. By continuing to browse the site, you are agreeing to our use of cookies.

OKLearn more

Cookie and Privacy Settings



How we use cookies

We may request cookies to be set on your device. We use cookies to let us know when you visit our websites, how you interact with us, to enrich your user experience, and to customize your relationship with our website.

Click on the different category headings to find out more. You can also change some of your preferences. Note that blocking some types of cookies may impact your experience on our websites and the services we are able to offer.

Essential Website Cookies

These cookies are strictly necessary to provide you with services available through our website and to use some of its features.

Because these cookies are strictly necessary to deliver the website, refusing them will have impact how our site functions. You always can block or delete cookies by changing your browser settings and force blocking all cookies on this website. But this will always prompt you to accept/refuse cookies when revisiting our site.

We fully respect if you want to refuse cookies but to avoid asking you again and again kindly allow us to store a cookie for that. You are free to opt out any time or opt in for other cookies to get a better experience. If you refuse cookies we will remove all set cookies in our domain.

We provide you with a list of stored cookies on your computer in our domain so you can check what we stored. Due to security reasons we are not able to show or modify cookies from other domains. You can check these in your browser security settings.

Google Analytics Cookies

These cookies collect information that is used either in aggregate form to help us understand how our website is being used or how effective our marketing campaigns are, or to help us customize our website and application for you in order to enhance your experience.

If you do not want that we track your visit to our site you can disable tracking in your browser here:

Other external services

We also use different external services like Google Webfonts, Google Maps, and external Video providers. Since these providers may collect personal data like your IP address we allow you to block them here. Please be aware that this might heavily reduce the functionality and appearance of our site. Changes will take effect once you reload the page.

Google Webfont Settings:

Google Map Settings:

Google reCaptcha Settings:

Vimeo and Youtube video embeds:

Other cookies

The following cookies are also needed - You can choose if you want to allow them:

Privacy Policy

You can read about our cookies and privacy settings in detail on our Privacy Policy Page.

Privacy Policy
Accept settingsHide notification only