Continuous Integration & Automated Releases

Continuous Integration

.github/workflows/run-tests.yml runs the full automated test suite on every push and pull request targeting main or dev, against a real MySQL 8.0 service container on PHP 8.3 — the exact same bin/test command a developer runs locally, so CI can never diverge from the local dev workflow.

Automated Semantic Versioning — Zero Node, Zero npm

Every merge to main is automatically versioned and published, in keeping with the project's zero third-party-runtime-dependency ethos: no Node, no npm, no third-party release tooling. Two small PHP scripts under bin/, plus git and GitHub's own gh CLI (already present on every Actions runner), do the whole job.

Every commit subject must follow Conventional Commits: type(scope): description. The type determines the version bump:

feat(auth): add OAuth login support        -> minor bump
fix: correct off-by-one in pagination      -> patch bump

feat!: remove legacy API endpoint          -> major bump
BREAKING CHANGE: the /v1/legacy endpoint has been removed entirely.

Previewing the Next Release Locally

Previewing the Next Release Locally bash
bin/release --dry-run

How the Two Release Workflows Fit Together

.github/workflows/lint-commit-messages.yml runs bin/check-commit-messages on every pull request, so a malformed commit subject is caught immediately instead of silently producing no release later.

.github/workflows/release.yml is triggered by workflow_run — it only fires after Run Automated Tests (CI) has already succeeded on main, so a broken commit is never tagged or published. When it runs, bin/release reads every commit since the last v* tag, computes the next version, appends a CHANGELOG.md section, tags the release, and publishes it with gh release create.