ci: run tests and linting on pull requests - #12
Merged
Conversation
Adds a CI workflow that runs on every pull request and on pushes to main. It uses the same paths-filter as the release workflow, so only the language directories a PR touches are exercised, and aggregates into a single `ci` check that can be made required for branch protection. Each language now has lint, static analysis and tests: - JavaScript: oxlint, `tsc --noEmit`, build, vitest, on Node 20 and 22. typescript-eslint was not an option — its peer range stops at TypeScript <6.1.0 and this package is on TypeScript 7. - Python: ruff (lint + format check) and mypy in strict mode, tests on 3.9 and 3.13. Lint and type checks run once, on the newest runtime. - PHP: PHP_CodeSniffer against PSR-12 and PHPStan at level 8, on PHP 8.1 and 8.4. Getting to a clean baseline required a handful of code changes: docstrings on the remaining `__init__` methods, `Optional[X]` rewritten as `X | None`, narrower types on the PHP transport and test helpers, and a couple of brace-placement fixes. Also fixes the JavaScript build, which was already failing on main: tsup's `--dts` flag loads rollup-plugin-dts, which cannot read TypeScript 7's compiler API. Declarations are now emitted by `tsc` directly, via a build tsconfig that excludes the test files. One test assertion changed: testAcceptsArrayRecipients asserted on the mocked response rather than the request, so it passed regardless of how recipients were serialised. It now checks the sent payload.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Nothing ran on pull requests. Tests only executed inside the publish workflows, after a merge to
main— so a broken change was caught while it was being released, not before. There was also no linting anywhere.What changed
A new
CIworkflow runs on every pull request and on pushes tomain. It reuses the release workflow's paths-filter, so a PR that only touchespython/doesn't spin up Node and PHP runners. A finalcijob aggregates the three and is the one to mark as a required check in branch protection — it stays green when a language is skipped for being untouched.Per language:
tsc --noEmitruff format --checkLint and type checks run once per language, on the newest runtime — older versions only need to prove the tests pass.
On the JavaScript linter
typescript-eslint is the obvious choice and it does not work here: its peer range is
typescript >=4.8.4 <6.1.0, and this package is on TypeScript 7.npm installrefuses outright. oxlint has no TypeScript peer dependency, covers the same correctness rules, and runs in milliseconds. If the repo ever moves back to TypeScript 5.x, swapping to typescript-eslint is a small change.no-await-in-loopis disabled — the retry loop awaits sequentially by design.require-awaitis disabled onEmailService.sendwith a comment, becauseasyncthere is what turns its argument validation into a rejected promise rather than a synchronous throw.Fixes found along the way
The JavaScript build was already broken on
main.tsup --dtsloads rollup-plugin-dts, which reads the TypeScript compiler API and crashes on TypeScript 7 (Cannot read properties of undefined (reading 'useCaseSensitiveFileNames')). Sincenpm run buildonly ran during publish, this would have failed the next release. Declarations now come fromtsc --emitDeclarationOnlyvia atsconfig.build.jsonthat excludes test files. Verified by type-checking a consumer against the emitteddist/undernode16resolution.A PHP test asserted the wrong thing.
testAcceptsArrayRecipientsasserted on the mocked response body, which is fixed by the test itself — it passed no matter how recipients were serialised. It now asserts on the request payload, which is what the test is named for.Baseline cleanup
Bringing three linters to zero touched a fair number of files, all mechanical:
__init__methods,Optional[X]→X | None(safe: every module hasfrom __future__ import annotations), and explicit types where strict mypy could not followAnythrough the transport.non-empty-stringon the HTTP method parameter, array value types on test helpers,\CurlHandleon the header callback, and three PSR-12 brace placements.Verified locally
Python 60 passed, ruff and mypy clean. JavaScript 55 passed, oxlint,
tsc --noEmitand build clean. PHP 56 passed / 86 assertions, phpcs and PHPStan clean.