GitHub is rolling out the GitHub Actions features which allow developers to automate CI/CD workflow without using any other apps. The Github Actions can be considered as GitHub’s own CI/CD solution inbuilt within Github itself. The developers can create workflows using the GUI Editor or from the code. The GitHub Actions can be used for building, testing and distributing the Swift libraries build with Apple’s Swift Package Manager. In this post, we will see how we can set up CI/CD for Swift Packages using Docker-based Github Actions.
GitHub Actions
The GitHub Action is still in the limited public beta at the time of writing this post and it’s likely to add more features soon. However, it’s worth understanding the concept behind the GitHub Actions and Workflows. There is official developer documentation on how to configure workflows and actions but in a summary,
- GitHub Actions consist of a WorkFlow with various actions inside it. The workflow can be defined using the visual editor Or in the code inside .github/main.workflow file.
- A Workflow must resolve in the specific action.
- Each action executes inside the Docker containers and can be dependent on another action.
- An action can be defined locally, in the Github repo or in the remote Docker repositories.
Users who signed up for the GitHub Actions can able to use this feature on public repositories for the push builds. Instead of talking about the theory, let’s use the GitHub Actions for CI/CD of the Swift Packages built with Swift Package Manager.
Swift Package Manager
Apple has announced its own Package Manager to build and distribute the Swift packages. Currently, Swift package manager doesn’t support iOS platform but standalone packages can be built and shared. In order to demonstrate how to set up a CI/CD workflow for the Swift Package, let’s create a package using the Swift Package Manager. We can create a directory with package name e.g SwiftPM-GithubAction and get the template using following commands
1 2 3 |
$ mkdir SwiftPM-GithubAction $ cd SwiftPM-GithhubAction $ swift package init --type library |
This will generate the template source code for the library with unit tests. In the Package.swift we can add another dependency for the demo purpose e.g Apple’s Fisher Yates package. At this point, we can build and test our Swift package on Linux or MacOS where Swift and other required tools installed. The build and test of the Swift package can be done using following commands.
1 2 |
$ swift build $ swift test |
We will see that package is built and unit tests are run after executing these commands. Now that, our Swift package is ready for the automation with GitHub Actions.
GitHub Actions for Swift Packages
As mentioned earlier, every action runs inside the Docker container, so we have to start with setting up Docker and then configure the Workflow actions. Basically, there are three steps involved in this process.
- Create Dockerfile with all the required tools and copy the Package inside the Docker image.
- Optionally set Docker ENTRYPOINT to run the first command in the Docker container.
- Define the workflow and actions in the .github/main.workflow file.
Let’s explore each step in details.
Create Dockerfile
A Dokerfile is a template for provisioning tools and building images with a set of instructions. There is a complete reference of Dockerfile on the official Docker documentation. As Docker containers are based on the Linux, we need to select the base the Docker image from the Docker Hub which supports the Swift. The mostly used Docker image for the building Swift libraries is official Swift image on Docker Hub here. We can use that image as base reference add our custom commands on top of that. We can create a Dockerfile with the following content.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
FROM swift:4.2 USER root LABEL "com.github.actions.name"="SwiftPM-GitHubAction" LABEL "com.github.actions.description"="Testing Swift Packages with Github Actions" LABEL "com.github.actions.icon"="airplay" LABEL "com.github.actions.color"="orange" LABEL "repository"="http://github.com/shashikant86/SwiftPM-GitHubAction" LABEL "homepage"="http://github.com/actions" LABEL "maintainer"="Shashikant Jagtap <shashikant.jagtap@icloud.com>" RUN apt-get update && apt-get install -y git RUN mkdir /SwiftPM-GitHubAction WORKDIR /SwiftPM-GitHubAction COPY . /SwiftPM-GitHubAction |
Basically, we are labelling the Swift Docker image and copying the Swift package content inside the Docker image.
Set ENTRYPOINT (Optional)
Setting the ENTRYPOINT to the Docker container is an optional step. We can add another executable file enterypoint.sh which accept some arguments that can be passed from the workflow actions. e.g we can define swift in the entry point file and add arguments later like build, test etc. The content of the entrypoint.sh will be very simple
1 2 3 |
#!/bin/sh -l set -e sh -c "swift $*" |
We have to make sure that, the file is executable and also need to add the following like to the Dockerfile t use the ENTRYPOINT.
1 |
ENTRYPOINT ["/SwiftPM-GitHubAction/entrypoint.sh"] |
We can skip the ENTRYPOINT part for now. There is an option in the workflow action that can override the ENTRYPOINT setup which gives more power to execute the commands.
Define WorkFlow and Actions
The GitHub workflow and actions can be defined in the .github/main.workflow file. There is comprehensive documentation on creating an workflows and actions. In case of deploying the Swift packages, we have the following phases
- Fetching the dependencies
- Building the Swift Package
- Testing the Swift Package
- Tagging and Publishing Package
We can define the workflow and four actions to accomplish this.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
workflow "SwiftPM-Workflow" { on = "push" resolves = ["Swift Package Publish"] } action "Swift Package Fetch" { uses = "YOUR_PACKAGE_REPO@GIT_BRAANCH" runs = "swift package resolve" } action "Swift Package Build" { uses = "YOUR_PACKAGE_REPO@GIT_BRAANCH" needs = ["Swift Package Fetch"] runs = "swift build --build-tests" } action "Swift Package Test" { uses = "YOUR_PACKAGE_REPO@GIT_BRAANCH" needs = ["Swift Package Build"] runs = "swift test --parallel " } action "Swift Package Publish" { uses = "YOUR_PACKAGE_REPO@GIT_BRAANCH" needs = ["Swift Package Test"] runs = "git tag 0.0.1" } |
In this file, we have defined the workflow and four actions which are dependent on the previous action. In the uses field, replace your package name and branch you want to use for the action. Also, we have used the runs to override the ENTRYPOINT from the Dockerfile.
Once, we define this workflow and push the commit to the repository. It will trigger the action in the action tab in the GitHub UI. This will create the workflow with four phases running one after another as shown below:
Once execution of the all phases in the workflow is finished then we can see the logs of each phase e.g the logs for the Swift Package Build phase looks like this:
In the Publish Phase, we need to configure the Git credential in the Docker image to publish the package on the Github. Once the Publish phase finished, then we can see that our Package is tagged and release on the Github.
Source Code
The source code of this tutorial is available on the Github repo Shashikant86/SwiftPM-GItHubAction.
GitHub Action Gotcha
Still, GitHub Actions feature is in the public beta, we will hear about the improvements in the near future. However, the things which are badly missed while trying the Github Actions are
- Support for the Docker Compose At first created the docker-compose.yml file to execute the swift commands in the different containers but the docker compose not pre-installed. Also, the Docker images are auto built with actions.
- Can’t specify the additional options to the docker run when the Docker images are building. e.g copy the GitHub config mount volumes, specify ports etc.
- Github Actions recommend the creating the local actions in the action directory e.g action-a but by doing that we can’t copy the Swift Package Content in the Docker image as it falls outside of the Docker build context.
- Can’t execute multiple commands using the runs in the action. Also, the && character isn’t allowed.
- GitHub Actions can’t support the macOS at the moment. It’s just based on the Docker and Linux at the moment.
Conclusion
GitHub Actions feature claimed that this is the future of workflow automation where developers can create and maintain workflows and actions from GitHub itself. The CI/CD will be painless and right in the fingertips. We have just seen that how to deploy Swift Packages using Github Actions but it can be applied for any technology that can be built inside Docker. This is just the start of GitHub Action. Let’s wait and watch what it bing in the future.