# Contributing to Exoplanet
Thanks for your interest in improving Exoplanet! This document explains how
to set up a development environment, run the checks that CI runs, and get a
change merged.
## Getting started
You need Elixir `~> 1.17` and a compatible OTP release (CI tests Elixir
1.17–1.19 on OTP 27–28; see `.github/workflows/elixir.yml`).
```bash
git clone https://github.com/milmazz/exoplanet.git
cd exoplanet
mix deps.get
mix test
```
Some dependencies (`fast_rss`, `lazy_html`) are NIF-based. If tests fail in
unexpected ways after switching Elixir/OTP versions, rebuild them:
```bash
mix deps.compile --force
```
## Running the checks
Run the full CI suite locally before opening a pull request:
```bash
mix precommit
```
This runs, in order: `compile --warnings-as-errors`, `format
--check-formatted`, `deps.unlock --check-unused`, `docs --warnings-as-errors`,
and `test`.
## Project layout
The pipeline flows **Config → Fetcher (HTTP+Cache) → Parser (per source) →
Post → Filters → sorted list**:
| Module | Responsibility |
|---|---|
| `Exoplanet` | Entry point: fetches feeds concurrently, filters, sorts, caps |
| `Exoplanet.Config` | Configuration struct; loaded from an `.exs` file |
| `Exoplanet.Fetcher` | HTTP fetch + `Exoplanet.Cache` interaction (`fetch/2`) |
| `Exoplanet.Parser` | Pure `parse(body, url, name)` → RSS/Atom XML parsing into `Post` structs |
| `Exoplanet.Post` | One feed entry |
| `Exoplanet.Filters` | Category allow/block, HTML sanitization, excerpts |
| `Exoplanet.DateTimeParser` | RFC 822 date parser (generated, see below) |
| `Exoplanet.Cache` | Optional behaviour for HTTP conditional-GET caching |
See `example/planet_beam.exs` for a fuller config file (it leaves the
sanitizer `drop_tags` / `drop_attrs` at their secure defaults).
## Testing
Tests never hit the network. HTTP requests are stubbed with
[`Req.Test`](https://hexdocs.pm/req/Req.Test.html), wired up in
`test/test_helper.exs` and keyed on `Exoplanet.Fetcher`.
- Feed XML fixtures live in `test/support/fixtures/feeds/*.xml`.
- `test/support/test_helpers.ex` provides
`stub_feed/1` (one fixture for every request) and `stub_feeds/1`
(dispatch by host) helpers.
- Cache adapter tests use in-process `Agent`-backed adapters defined inline
in `test/exoplanet/fetcher_cache_test.exs`.
When fixing a bug, please add a regression test that documents the bug it
guards against — see the existing tests for the style.
```bash
mix test # everything
mix test test/exoplanet_test.exs # one file
mix test test/exoplanet_test.exs:42 # one test, by line number
```
## The generated DateTimeParser
`lib/exoplanet/datetime_parser.ex` is **generated output — do not edit it
directly**. The source definition is `lib/exoplanet/datetime_parser.ex.exs`
(a NimbleParsec parser). The generated file is committed because
`nimble_parsec` is a dev/test-only dependency.
To change date parsing:
1. Edit `lib/exoplanet/datetime_parser.ex.exs`.
2. Regenerate the committed file:
```bash
mix nimble_parsec.compile lib/exoplanet/datetime_parser.ex.exs
```
3. Run `mix test` and commit **both** files together.
## Submitting changes
1. Fork the repository and create a feature branch from `main` — never
commit directly to `main`.
2. Make your change, with tests.
3. Run `mix precommit`.
4. Open a pull request with a clear description of the problem and the
solution. Reference any related issue.
5. Update the `[unreleased]` section of `CHANGELOG.md` when
the change is user-visible (the file follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/)).
For larger changes (new filter types, parser rework, new behaviours),
please open an issue first so the design can be discussed before you invest
time in an implementation.
## Reporting issues
Open a [GitHub issue](https://github.com/milmazz/exoplanet/issues) with:
- What you expected and what happened instead.
- The feed URL or a minimal XML snippet that reproduces the problem, when
the issue is feed-specific.
- Elixir/OTP versions and the Exoplanet version.
If you believe you have found a security-sensitive problem (for example in
the HTML sanitizer), please **do not** open a public issue. Follow the
private reporting process in
[SECURITY.md](https://github.com/milmazz/exoplanet/blob/main/SECURITY.md)
instead.
## License
By contributing, you agree that your contributions will be licensed under
the Apache-2.0 license (see `LICENSE`).