This week I added a GitHub Action Continuous Integration Workflow (say that 10 times fast) to my project. This was interesting as I've now encountered this in a few projects I contributed to during Hacktoberfest and was eager to implement it to my own work.
Implementing it was pretty simple, I just used a suggested workflow
Which already had everything I needed, including running a linting step using flake8. The only small step I added was to run a bit of a smoke test by running the --help and --version features of the cli, which I saw suggested.
name: CLI Tester
on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.10
uses: actions/setup-python@v3
with:
python-version: "3.10"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 pytest
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Run CLI smoke test
run: |
python scan-repo.py --help
python scan-repo.py --version
- name: Test with pytest
run: |
pytest
A funny little issue I ran into when testing my pull request, was that one of my tests was passing on my local machine but failing when tested on GitHub:
And it turns out when I'm testing for the exact same times (30 day old file vs 30 day threshold), on GitHub it's probably taking slightly longer between when the mod_time is calculated and when the function calls time.time(), resulting in it failing.. I ended up just writing a separate unit test to test exact time between modified time and threshold to avoid that:
def test_file_modified_exactly_at_threshold():
"""Test file modified exactly at the threshold boundary."""
with tempfile.NamedTemporaryFile(mode='w', delete=False) as f:
f.write("test")
temp_path = f.name
try:
# Fix both timestamps to eliminate timing variance
fixed_current_time = 1000000.0
fixed_mod_time = fixed_current_time - (30 * 24 * 60 * 60)
with patch('time.time', return_value=fixed_current_time):
with patch('os.stat') as mock_stat:
mock_stat.return_value = MagicMock(st_mtime=fixed_mod_time)
result = is_recently_modified(temp_path, recent_day=30)
assert result is True
finally:
os.unlink(temp_path)
My partner's testing setup wasn't too different from mine as we are both writing our programs in python and using pytest for our testing suite. Our actual projects function differently, however, so I did have to tailor the tests to their project by using the setup_method() to create an instance of the FileProcessor() class.
def setup_method(self):
self.processor = FileProcessor()
Now that I've set up Continuous Integration for myself, I can see how useful it can be to automatically test any additions to the code without the maintainer pouring through and making sure nothing breaks. It must save hours and hours of time for bigger projects where one small change could unknowingly create a butterfly effect of bugs somewhere else in the code- and having an automatic CI workflow set up just eliminates any of that guesswork and makes sure that at the bare minimum what's being added isn't breaking any core functionality of the code.



Top comments (0)