Contributing
Welcome aboard! We're excited that you are interested in contributing to the Tempest framework. We value all contributions to the project and have assembled the following resources to help you get started. Thanks for being a contributor!
Report an error or bug
To report an error or a bug, please:
- Head over to the issue page to open an issue.
- Provide as much context about the problem you are running into and the environment you are running Tempest in.
- Provide the version and, if relevant, the component you are running into issues with.
- For a shot at getting our "Perfect Storm" label, submit a PR with a failing test!
Once the issue has been opened, the Tempest team will:
- Label the issue appropriately.
- Assign the issue to the appropriate team member.
- Try and get a response to you as quickly as possible.
In the event that an issue is opened, but we get no response within 30 days, the issue will be closed.
Request a feature
Tempest is a work in progress. We recognize that some features you might benefit from or expect may be missing. If you do have a feature request, please:
- Head over to the issue page to open an issue.
- Provide as much detail about the feature you are looking for and how it might benefit you and others.
Once the feature request has been opened, the Tempest team will:
- Label the issue appropriately.
- Ask any clarifying question to help better understand the use case.
- If the feature requested is accepted, the Tempest team will assign the
Uncharted waterslabel. A Tempest team member or a member of the community can contribute the code for this.
We welcome all contributions and greatly value your time and effort. To ensure your work aligns with Tempest's vision and avoids unnecessary effort, we aim to provide clear guidance and feedback throughout the process.
Contribute documentation
Documentation is how users learn about the framework, and developers begin to understand how Tempest works under the hood. It's critical to everything we do! Thank you in advance for your assistance in ensuring Tempest documentation is extensive, user-friendly, and up-to-date.
We welcome contributions of any size! Feel free to submit a pull request, even if it's just fixing a typo or adding a sentence. We especially value additions coming from new users' perspectives, which help make Tempest more accessible.
To contribute to Tempest's documentation, please:
- Set up Tempest locally and head to
/docs. - Add or edit any relevant documentation in a manner consistent with the rest of the documentation.
- Re-read what you wrote and run it through a spell checker.
- Optionally,
- Head over to the Tempest docs repository to fork the project.
- Run
tempest docs:symlinkto link the/docsof your local Tempest clone to the documentation website. - Preview your changes by running
bun run dev.
- Open a pull request with your changes.
Once a pull request has been opened, the Tempest team will:
- Use GitHub reviews to review your pull request.
- If necessary, ask for revisions.
- If we decide to pass on your pull request, we will thank you for your contribution and explain our decision. We appreciate all the time contributors put into Tempest!
- If your pull request is accepted, we will mark it as such and merge it into the project. It will be released in the next tagged version! π
Contribute code
So, you want to dive into the code. To make the most of your time, please ensure that any contributions pertain to an approved feature request or a confirmed bug. This helps us focus on the vision for Tempest and ensuring the best developer experience.
To contribute to Tempest's code, you will need to first setup Tempest locally. Then,
- Make the relevant code changes.
- Write tests that verify that your contribution works as expected.
- Run
composer qato ensure you are adhering to our style guidelines. - Create a pull request with your changes.
- If your pull request is connected to an open issue, add a line in your description that says
Fixes #xxx, where#xxxis the number of the issue you're fixing.
We use conventional commits to automatically generate readable changelogs. You may help with this by providing a clear pull request title, which will appear in the changelog and needs to be understandable without the pull request's content as a context. Read more about this in the pull requests section.
Once a pull request has been opened, the Tempest team will:
- Use GitHub reviews to review your pull request.
- Ensure all CI pipelines are passing.
- If necessary, ask for revisions.
- If we decide to pass on your pull request, we will thank you for your contribution and explain our decision. We appreciate all the time contributors put into Tempest!
- If your pull request is accepted, we will mark it as such and merge it into the project. It will be released in the next tagged version! π
Setting up Tempest locally
- Install PHP.
- Install Composer.
- Install Bun or Node.
- Fork and clone the Tempest repository.
In your terminal, run:
cd /path/to/your/clone composer update bun install bun dev
You're ready to get started!
Linking your local Tempest to another local Tempest application
If you have another Tempest application with which you want to use your local version of the framework, you may do so with composer symlinking.
Add the following in your composer.json, replacing /path/to/your/clone with the absolute path to your local version of the framework:
{ // ... "repositories": [ { "type": "path", "url": "/path/to/your/clone" } ], "minimum-stability": "dev", "prefer-stable": true // ... }
You may then run composer require "tempest/framework:*".
If you are also working on one of the JavaScript packages, you may also symlink them to your local Tempest application by running bun install /path/to/your/clone/package. Note that the path must be to the actual JavaScript package, and not the root of the framework.
For instance, assuming you cloned the framework in /Users/you/Code/forks/tempest, the command to symlink vite-plugin-tempest should look like that:
bun install /Users/you/Code/forks/tempest/packages/vite-plugin-tempest
Do not forget to run bun dev in the root of your local version of the framework, so your changes can be reflected on your local application without needing to run bun build each time.
Code style and conventions
Tempest uses a modified version of PSR-12. We automate the entire styling process because we know everyone is used to different standards and workflows. To see some of the rules we enforce, check out our Mago and Rector configurations.
The following outlines some other guidelines we have established for Tempest.
final and readonly as a default
Whenever possible, classes should be final and readonly. This practice promotes immutability and prevents inadvertent changes to logic.
You may watch this video to understand @brendt_gd's thoughts about using final.
Acronym casing
Tempest uses a modified version of the .NET best practices for acronym casing. Please see below for our guidelines:
Do capitalize all characters of two to three character acronyms, except the first word of a camel-cased identifier.
A class named IPAddress is an example of a short acronym (IP) used as the first word of a Pascal-cased identifier. A parameter named ipAddress is an example of a short acronym (ip) used as the first word of a camel-cased identifier.
Do capitalize only the first character of acronyms with four or more characters, except the first word of a camel-cased identifier.
A class named UuidGenerator is an example of a long acronym (Uuid) used as the first word of a Pascal-cased identifier. A parameter named uuidGenerator is an example of a long acronym (uuid) used as the first word of a camel-cased identifier.
Do not capitalize any of the characters of any acronyms, whatever their length, at the beginning of a camel-cased identifier.
A class named Uuid is an example of a long acronym (Uuid) used as the first word of a camel-cased identifier. A parameter named dbUsername is an example of a short acronym (db) used as the first word of a camel-cased identifier.
Validation classes
When writing error messages for validation rules, refrain from including ending punctuation such as periods, exclamation marks, or question marks. This helps in maintaining a uniform style and prevents inconsistency in error message presentation.
- Value should be a valid email address! + Value should be a valid email address
Exception classes
Exception classes can be thought of as events and should be named accordingly. Use a subject-verb structure in the past tense to describe what happened (e.g., DatabaseOperationFailed, StorageUsageWasForbidden, AuthenticatedUserWasMissing).
- Do not suffix class names with
Exception; the context ofthroworcatchlanguage constructs makes their purpose clear. - All exception classes must extend PHP's built-in
\Exception. - When appropriate, define marker interfaces such as
CacheException,DatabaseException, orFilesystemExceptionto group related exceptions. These interfaces must be suffixed withException. - Set the exception message within the exception class itselfβnot where it is thrown.
- Override the constructor to only accept relevant context-specific input.
For instance, the following exception accepts a the relevant cache key as a constructor argument, and keeps it accessible through a public property:
final class LockAcquisitionTimedOut extends Exception implements CacheException { public function __construct( public readonly string $key, ) { parent::__construct("Lock with key `{$key}` could not be acquired on time."); } }
AI contributions
Behind Tempest is a small group of humans who are passionate about code and this project. We welcome anyone who's passionate about PHP and programming to join Tempest, regardless of the tools they are using. At the same time, we expect a level of respect between each other.
As an example, PRs that were AI-generated without any self-review from the contributor's side don't show mutual respect, as it puts the burden of reviewing LLM-generated code on the maintainer's side. We expect each contributor to take a level of ownership and responsibility over their contributions (before they are merged), to make sure the submitted code is clean and understandable, well-tested and adhering to our code style. In turn, from our side, we will be happy to guide anyone who's eager to learn, as long as it goes both ways. We also expect each contributor to ensure that code they are contributing is compatible with the Tempest License.
In other words, you may use whatever tools you want to write code (editors, IDEs, AI chat, agents, β¦), but you must take ownership and responsibility over your PRs. The least we ask is that you yourself understand the code you've written or generated and are able to explain it in full.
Release workflow
Tempest uses sub-splits to allow components to be installed as individual packages. The following outlines how this process works.
Workflow steps
-
Trigger event
- When a pull request is merged, or a new tag is created, the
.github/workflows/subsplit-packages.ymlaction is run.
- When a pull request is merged, or a new tag is created, the
-
Package information retrieval
- When the
subsplit-packages.ymlis run, it callsbin/get-packages. - This PHP script uses a combination of Composer and the filesystem to return (in JSON) some information about every package. It returns the:
- Directory
- Name
- Package
- Organization
- Repository
- When the
-
Action matrix creation
- The result of the
get-packagescommand is then used to create an action matrix. - This ensures that the next steps are performed for every package discovered.
- The result of the
-
Monorepo split action
- The
symplify/monorepo-split-github-action@v2.3.0GitHub action is called for every package and provided the necessary information (destination repo, directory, etc.). - This action takes any changes and pushes them to the sub-split repository determined by combining the "Organization" and "Repository" values returned in step 2.
- Depending on whether a tag is found or not, a tag is also supplied so the repository is tagged appropriately.
- The
Commit and merge conventions
Commits must all respect the conventional commit specification, so the changelog and release notes are generated using the commit history.
Commit descriptions
Commit descriptions should not start with an uppercase letter and should use imperative mood:
- feat(support): Adds some cool feature + feat(support): add some cool feature
Commit scopes
Scopes are not mandatory, but are highly recommended for consistency and easy of read. The following scopes are the most commonly used:
featβ for a new featurefixβ for a bug fixrefactorβ for changes in code that are neither bug fixes or new featuresdocsβ for any change related to the documentationperfβ for code refactoring that improves performancetestβ for code related to automatic testingstyleβ for refactoring related to the code style (not for CSS)ciβ for changes related to our continuous integration pipelinechoreβ for anything else
Here are some commit examples:
feat(support): add `StringHelper` class feat(support/string): add `uuid` method perf(discovery): improve cache efficiency refactor(highlight): improve code readability docs: mention new `highlight` package chore: update dependencies style: apply php-cs-fixer
Pull requests
Pull request titles and descriptions should be as explicit as possible to ease the review process.
Contributors are not required to respect conventional commits within pull requests, but doing so will ease the review process by removing some overhead for core contributors.
All pull requests will be renamed to the conventional commit convention if necessary before being squash-merged to keep the commit history and changelog clean.
Release cycles
Tempest current does not follow a fixed release cycle. In general, bug fixes and minor features can be released as soon as possible. For breaking changes, though, we aim to bundle as many as possible in a single major release.
Milestones
Even though bug fixes and minor features can be released whenever available, we do some level of long-term planning to ensure Tempest stays on track. There should always be two active milestones, and one for future versions.
- The current minor milestone includes all issues that should be addressed as patch or minor versions within the current major version. Anything in this milestone should be considered "ready to work on" and can be done at any point in time before the next major release.
- The next major milestone includes all issues that are planned for the next major release, many will be breaking changes. Oftentimes, we'll work on both current minor and next major milestones at the same time.
- The next minor milestone includes all issues that should be addressed as patch or minor versions after the next major release has been tagged.
- All other issues that don't get assigned a milestone are considered to be "unplanned". They might at one point be added to a milestone, but there's no guarantee on timing.
As an example:
- The current Tempest version is
2.14, that means that the current minor milestone is2.x - The next major release is planned for
3.0, so the next major milestone is3.0 - The next minor milestone includes features that are planned after 3.0 is released, and thus go in the
3.xmilestone
For clarity, each milestone will get its corresponding name, with the target branch or tag at the end. For the previous example, the milestones are called:
current minor (2.x)next major (3.0)next minor (3.x)
Finally, as we close in on tagging next major, features that would usually go in current minor can be targeted to next major instead, in order to avoid too many merge conflicts between the two milestones.
Milestone deadlines
Even though we release on a non-fixed schedule, we do assign deadlines to the next major version. This gives all contributors a clear goal to work towards, and helps us stay on track. The dealine for next major also determines the end date of current minor