Contributing to Exoplanet

Copy Markdown View Source

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).

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:

mix deps.compile --force

Running the checks

Run the full CI suite locally before opening a pull request:

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:

ModuleResponsibility
ExoplanetEntry point: fetches feeds concurrently, filters, sorts, caps
Exoplanet.ConfigConfiguration struct; loaded from an .exs file
Exoplanet.FetcherHTTP fetch + Exoplanet.Cache interaction (fetch/2)
Exoplanet.ParserPure parse(body, url, name) → RSS/Atom XML parsing into Post structs
Exoplanet.PostOne feed entry
Exoplanet.FiltersCategory allow/block, HTML sanitization, excerpts
Exoplanet.DateTimeParserRFC 822 date parser (generated, see below)
Exoplanet.CacheOptional 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, 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.

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:

    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).

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 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 instead.

License

By contributing, you agree that your contributions will be licensed under the Apache-2.0 license (see LICENSE).