Using GitHub Actions to deploy Static Sites generated with Swift and Publish

By the end of this tutorial, you will be able to use Github Actions to build and deploy a static website generated with Swift on Github Pages.

Using GitHub Actions to deploy Static Sites generated with Swift and Publish

Following Moritz's previous article about publishing static websites generated in Swift with Gitlab CI/CD, this is about the automation and publication of static websites with GitHub CI/CD instead.

We have already described the benefits of creating static sites using Publish, a static site generator for Swift developers, by John Sundell in "Static Site Generation with Swift using Publish, Plot, and Ink". In this article, we will see how to deploy the static site using GitHub Action and how we can host that with GitHub Pages.

Publish comes with a DeploymentMethod API that allows you to deploy the generated website to an external service with built-in implementation.

This allows you to generate the site and gives you the ability to push it to Github every time that you run the project in Xcode. To make this work automatic, it is possible to create a new Xcode Build Scheme dedicated to deployment. But with this solution we need also to remember to manage the development scheme as well, adding more work every time we want to deploy.

Therefore, in this article, we will explain how to automate the deployment of the site using only GitHub, without the basic Publish deploy API, allowing us to create an ad-hoc automatization job-based. Thanks to GitHub Actions we can work with a single Xcode build scheme, as we are used to, using just the potential offered by git.

Hosting Static Websites on GitHub Pages

β€Œβ€ŒOn GitHub, you can use GitHub Pages to host the static website for free. It behaves exactly as you would expect, and it looks really similar to GitLab Pages.

GitHub Pages Home Page

To enable a GitHub Pages you can find the relative page in the settings of the repository. Through these settings, it is possible to publish a static site by selecting the folder in which the various files that make up the site are located. It is also possible to select a dedicated branch on which all the necessary files will be saved. This feature will allow us to logically separate the development part and all the various files generated by Publish.

Taking advantage of this feature, let's create a GitHub Action that allows us to automate the deployment.

Building Static Website with GitHub Actions

GitHub Actions is a continuous integration and continuous delivery (CI/CD) platform that allows you to automate your build, test, and deployment pipeline.

GitHub Action Home Page

You can create workflows that build and test every pull request to your repository or deploy to production and many more. You can configure a Github Actions workflow to be triggered when an event occurs in your repository, such as a pull request being opened or an issue being created, or, in our case, when a simple push on a dedicated branch is detected. There are many things that can be done, for more information about it check out the official documentation.

A workflow contains one or more jobs that can run in sequential order or parallel. Each job will run inside its own virtual machine runner, or inside a container, and has one or more steps that either run a script that you define or run another action.

To use GitHub Action in our project we have to add a .github/workflow folder inside our project folder. It must contain .yaml files that describe your actions. In our implementation, we will create a deploy.yaml file.

Create the Workflow

The idea of this Workflow is to execute the build of our project in Swift at each push on the main branch. For that, we will select a virtual machine on which macOS runs.

In the first part, we give a name to the workflow and set the mode in which will be triggered. In our case, we decide that it is launched every time we push to the main branch.

# Name of the Workflow
name: DeployPublish

# Run this workflow only on push on main branch.
on: push: branches: - main 

Now that we have created and set up the workflow, we are going to create the jobs that compose them. In our case, only one job will be needed.

In this Workflow, we will have a single job that we call "deploy". Since we need to build the Swift code, we select a virtual machine runner that runs the latest version of macOS.

# This workflow contains a single job called "deploy" 
jobs: 

    deploy: 
    
    # The type of runner that the job will run on
    runs-on: macos-latest

With this configuration is possible to run the project using the Swift compiler, the development environment is already provided and therefore no further configuration is required.

Now let's see the necessary steps of the job. A job is a set of steps in a workflow that execute on the same runner. Each step is either a shell script that will be executed, or an action that will be run. Steps are executed in order and are dependent on each other. Since each step is executed on the same runner, you can share data from one step to another.

    steps:
      - name: Checkout πŸ›ŽοΈ
        uses: actions/checkout@v2.3.1
        
      - name: Install and Build Swift πŸ”§
        run: swift run
        
      - name: Set Public Folder
        run: |
            mkdir .public
            cp -r Output/* .public
            mv .public public
                   
      - name: Deploy πŸš€
        uses: JamesIves/github-pages-deploy-action@4.1.5
        with:
          branch: gh-pages
          folder: public
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

The job that will allow us to deploy the website consists of four steps. The first is for checks-out your repository, so your workflow can access it.

- name: Checkout πŸ›ŽοΈ uses: actions/checkout@v2.3.1

In the second step, we're going to actually build the project, this will be done via the Swift CLI. By executing the swift run command, we would get the website generation by Publish. Now that our project has been executed we should expect to find the generated files of our static site in a folder called Output.

- name: Install and Build Swift πŸ”§ run: swift run

Now for simplicity and convenience, in the next step, I'm going to move all generated files to a public folder, which we will use in the next step.

- name: Set Public Folder
  run: | 
      mkdir .public
      cp -r Output/* .public
      mv .public public

And finally, in the last step, we will perform the deployment, to do so we will use one of the many actions already created by the community. As said previously, the various workflows are designed so that they can be reused for different purposes. The one I chose to use is called github-pages-deploy-action, developed by JamesIves, but feel free to use one of the many alternatives in the GitHub marketplace.

This workflow requires two main pieces of information, the destination branch to which to move the files that will be published, and the folder from which to get them. In our implementation, we have selected the public folder, created in the previous step, which contains the files generated by Publish.

- name: Deploy πŸš€
  uses: JamesIves/github-pages-deploy-action@4.1.5
  with: 
      branch: gh-pages
      folder: public
  env:
      GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Final Consideration

If you want to see the final implementation of the Workflow, you can open this Github Gist. However, it is possible to adapt numerous alternatives to the proposed approach, and it is also possible to add additional features to the workflow, for example by adding a job dedicated to testing.

For an introduction to static site generation with Swift, consider the tutorial "Static Site Generation with Swift using Publish, Plot, and Ink". To see how you can deploy through GitLab services, consider reading "Deploying Static Sites generated with Swift and Publish with Gitlab CI/CD". For more content like this, join the free mailing list to be in the loop when new content is out.