- main branch: auto-deploy to Dokku staging
- production branch: build Docker image and deploy to K8s production
- Add DEPLOYMENT.md with workflow documentation
- Create production branch for manual production deployments
Breaking change: pushing to main no longer deploys to production
Tests are still enforced locally via precommit hooks (mix precommit).
CI now only:
- Builds Docker image
- Pushes to registry
- Updates deployment
Expected CI time: 10-12 minutes (down from 26 minutes)
Trade-off: Faster CI feedback vs relying on local testing before push.
Eliminates duplicate job overhead:
- No separate job startup time
- No duplicate code checkout
- No runner scheduling delay
- Runs sequentially in same environment
Docker build still compiles from scratch (Docker isolation) but saves ~2-3min
in job overhead. Next step: optimize Dockerfile for better layer caching.
Removes:
- Separate compile/quality/test jobs
- Format and Credo checks
- Artifact upload/download between jobs
Now:
- Single test job that compiles and tests
- Build job runs after test passes
- All caching preserved for performance
- Compile with warnings-as-errors still enforced
Add 'mix deps.get --only test' to quality and test jobs after
downloading artifacts to ensure dependencies are current and
prevent 'lock mismatch' errors.
Fast when deps cached, but ensures consistency across all jobs.
Issue: Credo failed with 'lock mismatch' errors because deps.get was
skipped when cache hit, but dependencies were out of date
Fix: Remove cache-hit conditional from deps.get step
- mix deps.get is fast when deps are already downloaded
- Ensures mix.lock is always respected
- Prevents dependency mismatch errors in quality checks
Health Check Logging:
- Update TelemetryFilter to catch both request and response logs
- Filter /health and /health/time endpoints completely
- Check phoenix_log metadata to suppress response logs
- Prevents log flooding from Kubernetes probes (every 5s)
- Refactor to reduce cyclomatic complexity (Credo compliant)
CI Artifact Actions:
- Downgrade upload-artifact from v4 to v3
- Downgrade download-artifact from v4 to v3
- v4 is not supported on Forgejo/GHES
Fixes:
- Production logs no longer flooded with health check entries
- CI pipeline works on self-hosted Forgejo instance
Replace sequential migration execution (4m8s for 172 migrations) with
structure.sql loading (416ms), reducing CI database setup time by 99.7%.
Changes:
- Configure Ecto to dump/load schema from priv/repo/structure.sql
- Update test alias: ecto.migrate → ecto.load
- Remove redundant database setup step from CI workflow
- Commit structure.sql to repository for version control
Benefits:
- CI database setup: 4m8s → 416ms (248s → 0.4s)
- Consistent schema snapshots across environments
- Faster local test runs
Maintenance:
- Run 'mix ecto.dump' after adding new migrations
- Structure file auto-updates with schema changes
- Add init container to run migrations before app starts
- Uses /app/bin/migrate script (runs Towerops.Release.migrate())
- Migrations run once per deployment, before any pods start
- Update CI/CD to set both migrate and towerops container images
- Prevents issues like missing severity column in production
- Move MIX_ENV and DATABASE_URL to job-level env vars
- Removes need to set them on individual steps
- Ensures all database operations use correct hostname (postgres)
- Fixes 'connection refused' errors from hardcoded localhost in test.exs
- Add postgresql-client to system dependencies
- Fixes 'pg_isready: command not found' error in CI
- Required for database readiness check before tests
- Remove port mapping (5432:5432) to avoid port conflicts on runner
- Use 'postgres' hostname instead of 'localhost' to access service
- Service containers in Actions are accessible via their service name
- Fixes 'port is already allocated' error on self-hosted runners
- Split deps and _build into separate caches for better granularity
- Add Elixir/OTP version to cache keys for version-specific builds
- Include lib/**/*.ex hash in build cache for change detection
- Add apt package caching to skip redundant system dependency installs
- Use version-type: strict in setup-beam for consistent versions
- Improves cache hit rate and reduces build times significantly
- Add pg_isready wait loop to ensure PostgreSQL service is fully ready
- Run mix ecto.create explicitly before tests to prevent connection errors
- Fixes 'connection refused' errors when database service is still starting
Added test job that must pass before building Docker image:
- Code formatting check (mix format --check-formatted)
- Compilation with warnings as errors
- Credo static analysis (--strict mode)
- Full test suite with PostgreSQL + TimescaleDB
Build job now depends on test job passing (needs: test).
This prevents broken code from being deployed to production.
The punycode module deprecation warning in Node.js is coming from
actions/checkout@v6. Downgrading to v4 which is more stable and
doesn't use the deprecated punycode module.
Fixed two CI/CD issues:
1. Docker daemon not accessible
- Added explicit dockerd startup step
- Configured buildx to use docker-container driver
- Driver creates its own buildkitd instance
2. Node.js punycode deprecation warning
- Downgraded actions/checkout from v6 to v4
- v4 is more stable and doesn't use deprecated punycode module
Changes ensure the workflow runs successfully in Forgejo Actions runners
that may not have Docker socket access by default.
Switch deployment pipeline from GitLab Agent kubectl to FluxCD image
automation. CI now only builds and pushes tagged images to Forgejo
registry; FluxCD detects new tags and updates the deployment manifest.
Fix Ecto.MultipleResultsError on /dashboard when a device has multiple
Preseem access point records by using limit(1) instead of Repo.get_by.
Replace inline cache with dedicated buildcache tag that stores all
intermediate builder layers. Unchanged stages like deps and compile
will be pulled from cache instead of rebuilt.
Add node:22-bookworm container image since ubuntu:latest lacks Node.js
required by JavaScript-based actions. Update checkout to v6 and
build-push-action to v6.