The Puzzle of Pull Requests: Testing Calibre-Web in IIAB with GitHub Actions Contexts
By Jimmy Lindsey
Sept. 3, 2025 | Categories: devops, IIAB, open-source, ci-cdBack in July, I got a message from Adam over at Internet-in-a-Box. There were some new developers who started work on iiab/calibre-web and he asked if I could set up a GitHub Actions workflow to automatically test the installation inside IIAB. If you are not familiar, IIAB is an offline server for educational resources and Calibre-Web is a web application for browsing and reading eBooks and videos. At first it seemed simple, but we quickly ran into a tricky problem. How do you make sure a workflow actually tests the pull requests branch instead of quietly defaulting back to master? What followed was a mix of trial, error and learning how GitHub Actions contexts really behave.
Setting up the Smoke Test
The goal is to smoke test Calibre-Web changes inside of IIAB, which requires:
- A way to tell IIAB which repo and branch to grab for the PR
- Grab the current branch if the test is being run directly on a branch
Luckily, Adam let me know that IIAB could already be configured to install Calibre-Web from a different repo and branch. Normally to install Calibre-Web, you would provide the first two lines below, with the additional configuration being the two lines after.
calibreweb_install: True
calibreweb_enabled: True
# works out to https://github.com/Akatama/calibre-web, which is my fork
calibreweb_repo_url: $GITHUB_SERVER_URL/$GITHUB_REPOSITORY
# works out to: smoke_test
calibreweb_version: $GITHUB_REF_NAME
This satisfied requirement 2 by running the smoke_test branch of my fork, but PR runs still defaulted to iiab/calibre-web’s master branch. This meant PR changes weren’t actually tested, giving a false sense that everything was fine.
Adam and I tried a lot of things that didn’t help. In the end, Adam did more research into GitHub Actions contexts. With this, Adam was able to come up with a workable solution that solved both requirements.
- name: Set up /etc/iiab/local_vars.yml
run: |
echo "calibreweb_install: True" > local_vars.yml
echo "calibreweb_enabled: True" >> local_vars.yml
# Solves requirement 1
# Here's where we make sure PRs use the right repo and branch instead of the master branch of iiab/calibre-web
if [ "${{ github.event_name }}" == "pull_request" ]; then
echo "calibreweb_repo_url: $GITHUB_SERVER_URL/${{ github.event.pull_request.head.repo.owner.login }}/${{ github.event.repository.name }}" >> local_vars.yml
echo "calibreweb_version: $GITHUB_HEAD_REF" >> local_vars.yml
# Solves requirement 2
# works for both push and workflow_dispatch
else
echo "calibreweb_repo_url: $GITHUB_SERVER_URL/$GITHUB_REPOSITORY" >> local_vars.yml
echo "calibreweb_version: $GITHUB_REF_NAME" >> local_vars.yml
fi
The first two lines are the same as what I showed above.
The if statement checks whether the workflow is running on a pull request. If it is, then append the following to the local_vars.yml file:
- The $GITHUB_SERVER_URL (which is always https://github.com) with the creator of the pull request's username and the repository name. The result is the URL of the repo for the fork, like https://github.com/Akatama/calibre-web.
- It takes the $GITHUB_HEAD_REF, which in this case would be the name of the branch on the fork, like smoke_test
This resolves requirement 1.
If it isn't a pull request, then it grabs the URL of the repo and the branch name as shown in the original code block, which as I already stated resolves requirement 2.
The Full Workflow
After the install was done, we wanted to run any Calibre-Web tests so we could verify the application was working appropriately. Here is the complete workflow broken up into chunks. You can also see the full version here.
First we clone the repo and move it to the /opt/iiab/iiab directory.
- name: Clean clone to /opt/iiab/iiab (chmod permissions preserved)
run: |
sudo git clone <https://github.com/iiab/iiab> # in $GITHUB_WORKSPACE == /home/runner/work/calibre-web/calibre-web
sudo mkdir /opt/iiab
sudo mv iiab /opt/iiab
Here we're setting up the local_vars.yml configuration file, which tells IIAB what to install. We configure it to install only Calibre-Web, then move it to /etc/iiab.
# PR logic: use fork + branch from PR, otherwise fall back to current repo + branch
- name: Set up /etc/iiab/local_vars.yml
run: |
echo "calibreweb_install: True" > local_vars.yml # in $GITHUB_WORKSPACE == /home/runner/work/calibre-web/calibre-web
echo "calibreweb_enabled: True" >> local_vars.yml
if [ "${{ github.event_name }}" == "pull_request" ]; then
echo "calibreweb_repo_url: $GITHUB_SERVER_URL/${{ github.event.pull_request.head.repo.owner.login }}/${{ github.event.repository.name }}" >> local_vars.yml
echo "calibreweb_version: $GITHUB_HEAD_REF" >> local_vars.yml
else # works for both push and workflow_dispatch
echo "calibreweb_repo_url: $GITHUB_SERVER_URL/$GITHUB_REPOSITORY" >> local_vars.yml
echo "calibreweb_version: $GITHUB_REF_NAME" >> local_vars.yml
fi
cat >> local_vars.yml << EOF
nodocs: True
kolibri_install: False
kolibri_enabled: False
kiwix_install: False
kiwix_enabled: False
# ...all other services are not enabled or installed, cut for length
EOF
sudo mkdir /etc/iiab
sudo mv local_vars.yml /etc/iiab
cat /etc/iiab/local_vars.yml
Next install Ansible and then IIAB.
- run: sudo /opt/iiab/iiab/scripts/ansible # Install Ansible
- run: sudo ./iiab-install # Install IIAB!
working-directory: /opt/iiab/iiab/
Next, preparation for integration tests:
- Install Firefox
- Activate the already existing Python virtual environment
- Install the dependencies in integration-tests-requirements.txt. Note that the dependencies in requirements.txt were already installed in this virtual environment by IIAB.
# Install Firefox, activate venv, and install test dependencies
- name: Install Firefox
uses: browser-actions/setup-firefox@v1
- run: firefox --version
- name: Activate virtual environment
run: source bin/activate
working-directory: /usr/local/calibre-web-py3
- name: Install Python dependencies for testing
uses: py-actions/py-dependency-install@v4
with:
path: /usr/local/calibre-web-py3/integration-tests-requirements.txt
Last, we run the integration tests. Note that I added the -p no:cacheprovider argument to Pytest to suppress a harmless cache warning. Since this is a fresh install, there is nothing useful for Pytest to cache anyway.
- name: Execute Integration Tests for Firefox
# Suppress Pytest cache warning with -p no:cacheprovider
run: pytest -p no:cacheprovider -s --splinter-webdriver firefox --splinter-headless
working-directory: /usr/local/calibre-web-py3
As mentioned before, we have only two integration tests. When we add more tests, we'll need to decide if they're worth running in this smoke test. At the moment, both tests take less than 30 seconds, so it's not an issue.
Conclusion
In the end, we were able to get a proper smoke test set up for any future changes to Calibre-Web. While I have some experience with GitHub Actions, I was not familiar with the context variables. Normally, pull requests run tests on the PR branch by default, but the complexity of this workflow made it necessary to use contexts. If nothing else, this little adventure proves that sometimes the simplest requests make for the best deep dives. Who knew smoke tests could spark so much smoke?