Deploying Preview Environments on Koyeb for GitHub Pull Requests
Tags: webdev, programming, tutorial, devops
Published: 2023-11-07 15:31:27

Preview environments are dynamic, temporary environments deployed from specific branches of your application in response to repository events.  They are a simple way to test and collaborate on versions of your applications before deploying to production.

In this tutorial, we will explain how to automatically deploy your application using [GitHub Actions](https://github.com/features/actions) each time a pull request is made for your GitHub repository.

By the end of this guide, you will have:

* A workflow that automatically deploys the branches from your pull requests.
* A workflow that automatically deletes the preview environment when the pull request is merged or closed.

## Requirements

To successfully follow and complete this guide, you need:

* A [GitHub account](https://github.com/) to host your repositories configure the pull request preview integration.
* A [Koyeb account](https://app.koyeb.com) to deploy and run the preview environments for each pull request.

## Steps

To complete this guide and configure preview environments to your project, you need to follow these steps:

1. [Fork the example application](#fork-the-example-application)
   * [Understanding the preview environment deployment workflow](#understanding-the-preview-environment-deployment-workflow)
   * [Understanding the preview environment cleanup workflow](#understanding-the-preview-environment-cleanup-workflow)
2. [Create a Koyeb personal access token](#create-a-koyeb-personal-access-token)
3. [Add the Koyeb personal access token as a repository secret](#add-the-koyeb-personal-access-token-as-a-repository-secret)
4. [Test the preview environment deployment](#test-the-preview-environment-deployment)
5. [Test the preview environment cleanup](#test-the-preview-environment-cleanup)

## Fork the example application

To demonstrate how to set up preview environments for a GitHub repository, we'll work with an example repository containing a basic Node.js web app built with [Express](https://expressjs.com/).  The application responds to requests with a simple "hello world" message.

To get started, visit the [preview environments example repository](https://github.com/koyeb/example-preview-environments) and fork it into your account.

The main application code is found in the `app.py` file, but for our purposes, the GitHub Actions workflow files are more relevant.  In the `.github/workflows` directory are two files: `deploy-preview.yaml` and `cleanup-preview.yaml`.

### Understanding the preview environment deployment workflow

The `deploy-preview.yaml` file contains the following contents:

```yaml
name: Deploy preview environment on Koyeb

on:
  pull_request:
    branches:
      - main

jobs:
  deploy_preview:
    runs-on: ubuntu-latest
    concurrency:
      group: "${{ github.head_ref }}"
      cancel-in-progress: true
    steps:
      - name: Install and configure the Koyeb CLI
        uses: koyeb-community/install-koyeb-cli@v2
        with:
          api_token: "${{ secrets.KOYEB_API_TOKEN }}"

      - name: Build and deploy the application to Koyeb
        uses: koyeb/action-git-deploy@v1
        with:
          git-branch: ${{ github.head_ref }}
          app-name: "expressjs-pr-${{ github.head_ref }}"
          service-name: ${{ github.head_ref }}
          service-ports: "3000:http"
          service-routes: "/:3000"

```

Let's take a look at what this configuration describes.

After naming the workflow, the `on.pull_requests.branches` structure dictates that this workflow should only automatically trigger for pull requests events against the repositories "main" branch.  If your project had a different primary branch or if you wanted to act on pull requests targeting additional branches, you could modify this value.

Next, in the `jobs` array, there's a single job called `deploy_preview` that runs the deployment.  Inside it specifies that the job should be run by a Ubuntu container and it sets up a concurrency group to make sure that only one workflow is run at a time.

The actual workflow steps are fairly simple.  First, it installs the [Koyeb CLI](https://www.koyeb.com/docs/build-and-deploy/cli/installation) using the  [`koyeb-community/install-koyeb-cli` action](https://github.com/koyeb-community/koyeb-actions).  You may notice that this step references a secret called `KOYEB_API_TOKEN`.  You will have to configure this for your fork later on.

The second step builds and deploys the pull request branch on Koyeb using the [`koyeb/action-git-deploy` action](https://github.com/koyeb/action-git-deploy).  The pull request branch name can be found in the `github.head_ref` context object.  We use this to specify the branch to deploy and to name the Koyeb App and Service.  The remaining lines configure the port and routing that the application expects.

To summarize, this workflow will automatically run whenever a pull request is created, re-opened, or updated.  When triggered, it will deploy a new preview environment with the pull request branch.

### Understanding the preview environment cleanup workflow

The `cleanup-preview.yaml` file contains the following contents:

```yaml
name: Cleanup preview environments on Koyeb

on:
  pull_request:
    types:
      - closed

jobs:
  cleanup_preview:
    runs-on: ubuntu-latest
    steps:
      - name: Install and configure the Koyeb CLI
        uses: koyeb-community/install-koyeb-cli@v2
        with:
          api_token: "${{ secrets.KOYEB_API_TOKEN }}"

      - name: Cleanup Koyeb application
        uses: koyeb/action-git-deploy/cleanup@v1
        with:
          app-name: "expressjs-pr-${{ github.head_ref }}"
```

This workflow is fairly similar to the last one.  After naming the workflow, it targets pull requests events, but unlike last time, this workflow only triggers when a pull request is closed.

The `cleanup_preview` job installs the Koyeb CLI as before and again uses the `KOYEB_API_TOKEN` to authenticate.  Afterwards, it uses the [`koyeb/action-git-deploy/cleanup` action](https://github.com/koyeb/action-git-deploy#cleaning-up-services) to delete the Koyeb app.  We pass it the same App name construction used in the deployment file so that it can target the correct App.

In summary, this workflow will automatically run whenever a pull request is merged in or closed.  When triggered, it will delete the preview environment based on the branch name.

## Create a Koyeb personal access token

In order to deploy and manage resources on Koyeb, your GitHub repository needs a token that authorizes it to do so.

To get started, in the [Koyeb control panel](https://app.koyeb.com/), click your user icon in the top right corner and click [**Account settings**](https://app.koyeb.com/user/settings).  Click the [**Personal Access Tokens**](https://app.koyeb.com/user/settings/api) item in the left-hand menu.

Give your new token a recognizable name and a useful description and the click **Create API Access Token**.  Copy the token value and store it in a safe location so that we can use it in the next step.  This value cannot be retrieved later, so the token will need to be recreated if you lose it.

## Add the Koyeb personal access token as a repository secret

In your fork of the example application on GitHub, click the **Settings** tab and select **Secrets and variables** and then **Actions**.  Click **New repository secret** to add a new secret.

Fill in the secret creation form with the following:

* **Name:** `KOYEB_API_TOKEN` (must match the secret value from the workflow files)
* **Value:** The Koyeb personal access token you generated as the secret value.

Click **Add secret** when you are finished.

Next, click the **Actions** tab for the forked repository.  You'll see a disclaimer since about how GitHub Actions have been disabled for the fork until you evaluate the workflow files:

![Enable GitHub Actions](https://www.koyeb.com/static/images/tutorials/koyeb-preview-environments/enable-github-actions.png)

If you're comfortable running the workflow files defined in the repository, click **I understand my workflows, go ahead and enable them**.  GitHub Actions can now run on your fork.

## Test the preview environment deployment

With the workflow files present and the Koyeb API token configured, your forked repository now has everything it needs to deploy and manage preview environments for pull requests.

To test the preview environment workflow, clone your fork from GitHub onto your local computer:

```bash
git clone git@github.com:<YOUR_GITHUB_ORG>/example-preview-environments.git
```

Move into the repository directory and switch to a new branch for your changes:

```bash
cd example-preview-environments
git switch -c change_message
```

Open the main `app.js` application file and modify the message to something different:

```diff-js
const express = require('express')
const app = express()
const port = process.env.PORT || 3000

app.get('/', (req, res) => {
-   res.send('<h1>Hello, world!</h1>')
+   res.send('<h1>Hello, from a different branch!</h1>')
})

app.listen(port, () => {
    console.log(`App listening on port ${port}`)
})
```

Save and close the file when you're finished.  Next, add and commit your changes and push them up to your fork:

```bash
git add :/
git commit -m 'Change greeting message'
git push origin change_message
```

In your fork on GitHub, open a new pull request based on the new branch:

**Note:** In this scenario, remember to make the pull request against your own `main` branch instead of the `koyeb` repository you forked from.

![Open pull request on GitHub](https://www.koyeb.com/static/images/tutorials/koyeb-preview-environments/open-pr.png)


After a moment, you should see the GitHub Action beginning to work in the checks at the bottom of the pull request:

![Pull request checks](https://www.koyeb.com/static/images/tutorials/koyeb-preview-environments/pull-request-checks.png)

If you click the **Details** link associated with the check or view the **Actions** tab at the top of the repository, you can track the deployment through the GitHub Actions interface.

![GitHub deploy action](https://www.koyeb.com/static/images/tutorials/koyeb-preview-environments/github-deploy-action.png)

If you visit the [Koyeb control panel](https://app.koyeb.com/), you should see a new Koyeb App and Service being deployed for the pull request branch:

![Koyeb new PR deployment](https://www.koyeb.com/static/images/tutorials/koyeb-preview-environments/koyeb-new-pr-deployment.png)

You can follow along through the build and deployment processes to see the progress.  When the deployment is live, click the **Public URL** for the service:

![Koyeb public URL](https://www.koyeb.com/static/images/tutorials/koyeb-preview-environments/koyeb-public-url.png)

You should see the updated message text from the pull request branch:

![Updated application message](https://www.koyeb.com/static/images/tutorials/koyeb-preview-environments/updated-application-message.png)

The preview environment successfully built and deployed the pull request branch.

## Test the preview environment cleanup

Next, you can test the preview environment cleanup workflow.

In your GitHub fork, close or merge the pull request (either will trigger the workflow).  If you click the **Actions** tab of the repository, you should see the cleanup workflow being processed:

![Cleanup workflow processing](https://www.koyeb.com/static/images/tutorials/koyeb-preview-environments/cleanup-workflow-processing.png)

If you visit the [Koyeb control panel](https://app.koyeb.com/), you should see the Koyeb App and Service for your pull request branch being deleted automatically:

![Koyeb app deletion](https://www.koyeb.com/static/images/tutorials/koyeb-preview-environments/koyeb-app-deletion.png)

This confirms that the cleanup workflow is able to spin down our preview environments correctly.

## Conclusion

In this tutorial, we showed how you can combine GitHub Actions and Koyeb to automatically deploy and manage preview environments based on your project's pull requests. We covered how to design workflow files that target pull request events on GitHub, how to pass the correct information to Koyeb to deploy the appropriate branches, and how to delete the resources once the pull request is closed.

By using and expanding on these workflows, you can easily integrate preview environments for your pull requests into your CI/CD pipelines.  This helps you test all changes in a realistic environment prior to merging them into your main production branch.  You can use this as a basis for building more sophisticated implementations that comment on the pull request thread with the preview environment details, notify slack channels, and more.