Skip to content

Workflow/push to dokku#164

Open
gideonmaina wants to merge 30 commits into
masterfrom
workflow/push_to_dokku
Open

Workflow/push to dokku#164
gideonmaina wants to merge 30 commits into
masterfrom
workflow/push_to_dokku

Conversation

@gideonmaina

Copy link
Copy Markdown
Collaborator

Description

Closes #161 — Adds a fully automated GitHub Actions CI/CD pipeline for testing, Docker image building, and Dokku deployment (staging + production) for sensors.AFRICA-api.

Workflow Architecture

graph TD
    PR[Pull Request → master] --> Test[🧪 Test<br/>Python 3.7 / pytest<br/>PostgreSQL service]
    Test --> Build[🐳 Build & Push<br/>Docker Hub<br/>semver auto-versioning]
    Build --> DeployStaging[🚀 Deploy Staging<br/>Dokku git:from-image]
    DeployStaging --> Integration[🔍 Integration Tests<br/>against staging]
    
    Push[Push → master] --> Test2[🧪 Test]
    Test2 --> Build2[🐳 Build & Push]
    Build2 --> DeployProd[🚀 Deploy Production<br/>Dokku git:from-image]
Loading
  • PRstestbuild-and-pushdeploy-stagingintegration-tests
  • Merge to mastertestbuild-and-pushdeploy-production

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • This change requires a documentation update

Checklist:

  • I have performed a self-review of my own code
  • I have made corresponding changes to the documentation

Key Features

Docker Image Versioning

  • Automatically creates the Docker Hub repository if it doesn't exist
  • Resolves the next semantic version by scanning existing tags (e.g., v0.1.0v0.1.1)
  • PR builds get beta-pr-{N}-{sha} and pr-{N} tags; master builds get v{version} and latest

Database Compatibility (Test Job)

  • Tests default to SQLite in-memory for local/CI speed
  • PostgreSQL-only tests (@pytest.mark.postgres_only) are auto-skipped on SQLite via a pytest_collection_modifyitems hook
  • Set SENSORSAFRICA_TEST_DATABASE_URL to run the full PostgreSQL suite when needed

Installation Cleanup

Dependencies that were scattered across manual pip install steps in the workflow are now consolidated in requirements.txt, with upgraded versions for Python 3.7 binary wheel compatibility (gevent==1.4.0, greenlet==0.4.17). feinstaub is the only package installed separately (--no-deps) due to its broken opbeat dependency.

Code Changes

File Change Reason
.github/workflows/deploy.yml New — full CI/CD pipeline Automates test → build → deploy
requirements.txt Added missing deps, bumped gevent/greenlet Consolidates install step, cp37 wheel compat
sensorsafrica/tests/conftest.py Lazy Django imports in fixtures, pytest_collection_modifyitems hook Avoids ImproperlyConfigured during collection; auto-skip PG-only tests on SQLite
sensorsafrica/tests/settings.py SQLite default + env-var PostgreSQL override Local testing without a running PG instance
sensorsafrica/tests/test_*.py All Django/DRF/feinstaub imports moved inside test methods Prevents module-level import errors before Django settings are configured
sensorsafrica/management/commands/cache_lastactive_nodes.py Raw SQL → Django ORM PostgreSQL-specific INTERVAL syntax broke on SQLite
pytest.ini Added DJANGO_SETTINGS_MODULE and postgres_only marker Required by pytest-django

Testing

Local Simulation

act (nektos) was used to simulate GitHub Actions runs locally during development, catching issues before pushing to CI.

Fork CI

The full workflow was tested end-to-end on a fork:
https://github.com/gideonmaina/sensors.AFRICA-api/actions — with automatic Docker image creation, versioning, and Dokku deployment.

Staging & Production Demo

Both staging and production were deployed to the same Dokku host (sensors-dev) using different app names to demonstrate the full workflow:

Environment App Name Trigger Status
Staging sensorsafrica-staging PR opened Passed
Production sensorsafrica-prod-deploy-test Merge to master $\color{red}{Failed (nginx-validation-issue)}$

Prerequisites

Before this workflow will run in the upstream repo, configure these:

Repository Secrets

Settings → Secrets and variables → Actions → New repository secret

Secret Description
DOCKERHUB_USERNAME Docker Hub username
DOCKERHUB_TOKEN Docker Hub access token (not password)
DOCKERHUB_REPOSITORY (optional) Custom repo path, e.g. org/sensors-africa
DOKKU_SSH_STAGING_PRIVATE_KEY Private SSH key for staging Dokku host
DOKKU_SSH_PRIVATE_KEY Private SSH key for production Dokku host
STAGING_DOKKU_HOST Staging Dokku server hostname/IP
STAGING_APP_NAME App name on staging Dokku (e.g. sensorsafrica-staging)
PRODUCTION_DOKKU_HOST Production Dokku server hostname/IP
PRODUCTION_APP_NAME App name on production Dokku

SSH Keys on Dokku Servers

The public key corresponding to each private key must be added to the Dokku host:

# On the Dokku server
echo "ssh-rsa AAAAB3... your-public-key" >> ~/.ssh/authorized_keys

Dokku Apps

Create the apps on each Dokku host (or let the workflow auto-create them with dokku apps:create):

dokku apps:create sensorsafrica-staging
dokku apps:create sensorsafrica-production

Reads and bumps from VERSION file for prod deploy
Cleaner than reading VERSION file.

TODO: update to accomadate major version releases
Resolves Celery 4.3.0 package dependency
View had missing ViewSet section
- Makes test database configurable via SENSORSAFRICA_TEST_DATABASE_URL env var, defaulting to SQLite
- Adds pytest_collection_modifyitems hook to auto-skip @pytest.mark.postgres_only tests on SQLite
- Registers the postgres_only marker in pytest.ini
- Updates v2 tests to authenticate via token and use correct /v2/data/stats/air/ URLs
- Marks deprecated v1 tests (test_filter_view, test_sensordata_view, test_sensor_view) as skipped
- Marks PostgreSQL-dependent stats tests as @pytest.mark.postgres_only
- Fixes bulk_create re-fetch for SQLite (which doesn't return PKs in Django 1.11)
@gideonmaina gideonmaina requested review from VinneyJ and kilemensi July 8, 2026 08:46
@gideonmaina gideonmaina self-assigned this Jul 8, 2026
@gideonmaina gideonmaina added bug Something isn't working enhancement New feature or request chore labels Jul 8, 2026
@VinneyJ VinneyJ requested a review from nkasozi July 8, 2026 12:21
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
DOCKERHUB_REPOSITORY: ${{ secrets.DOCKERHUB_REPOSITORY }}
run: |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a bit too much for a build-and-push job? Would a simpler structure like this be easier to maintain?

  needs: test
  runs-on: ubuntu-22.04

  outputs:
    tags: ${{ steps.meta.outputs.tags }}

  steps:
    - uses: actions/checkout@v4

    - uses: docker/setup-buildx-action@v2

    - uses: docker/login-action@v2
      with:
        username: ${{ secrets.DOCKERHUB_USERNAME }}
        password: ${{ secrets.DOCKERHUB_TOKEN }}

    - name: Docker meta
      id: meta
      uses: docker/metadata-action@v6
      with:
        images: ${{ env.IMAGE_NAME }}

    - name: Build and push image
      uses: docker/build-push-action@v7
      with:
        context: .
        push: true
        cache-from: type=gha
        cache-to: type=gha,mode=max
        tags: ${{ steps.meta.outputs.tags }}
        labels: ${{ steps.meta.outputs.labels }}```

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working chore enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: Add GitHub Actions CI/CD pipeline for automated testing and Dokku deployment for sensors.AFRICA-api

2 participants