7.2 KiB

Contribution Guide

Thanks for contributing to Cannery! Please read over the style tips to help make contributing to Cannery (hopefully) as great of an experience as you found it!

Translations needed!


If you're multilingual, this project can use your translations! Visit weblate for more information.

Style Tips

  • In order to keep code concise and improve readability, please try to make your functions as short as possible while keeping variable names descriptive! For instance, use inline do: blocks for short functions and make your aliases as short as possible without introducing ambiguity.
    • I.e. since there's only one Pack in the app, please alias Pack.t() instead of using Cannery.Ammo.Pack.t()
  • Use pipelines when possible. If only calling a single method, a pipeline isn't strictly necessary but still encouraged for future modification.
  • Please add typespecs to your functions! Even your private functions may be used by others later down the line, and typespecs will be able to help document your code just a little bit better, and improve the debugging process.
    • Typespec arguments can be named like @spec function(arg_name :: type()) :: return_type(). Please use these for generic types, such as map() when the input data isn't immediately obvious.
    • Please define all typespecs for a function together in one place, instead of each function header.
  • When making new models, please take inspiration from the existing models in regards to layout of sections, typespec design, and formatting.
  • With Elixir convention, for methods that raise on error please name them like function_that_raises!(), and functions that return a boolean like function_that_returns_boolean?(). For other methods, it's encouraged to use status tuples for other functions like {:ok, result} or {:error, reason_or_changeset} instead of just returning result or nil for easy pattern matching.
  • Instead of using the . operator, try to use pattern matching instead, especially for function headers. . in templates is fine to keep things concise.
  • Use Enum functions over comprehensions whenever possible for clarity. However, comprehensions in templates are fine for legibility.
  • When adding text, please use gettext macros to enable things to be translated in the future. After adding gettext macros, run mix format in order to add your new text strings to the files in priv/gettext.
    • Existing domains: "default" (for anything general), "prompts" (informational messages as a result of the user doing an action, i.e. in flashes), "actions" (actions that the user can take), "emails", and "errors". Using these domains accurately will let translators know which translations are higher and lower priority. Thank you!
  • Before submitting a PR, please make sure all tests are passing using mix test.

And as always, thank you!

Technical Information


More information can be found in the documentation generated by mix docs. These are located in the /docs folder, and are generated in HTML and ePub. Check them out!


  1. Clone the repo
  2. Install the elixir and erlang binaries. I recommend using asdf version manager, which will use the .tool-versions file to install the correct versions of Erlang, Elixir and npm for this project!
  3. Run mix deps.get and mix compile to fetch all dependencies
  4. Run mix setup to initialize your database. You can reset your database at any time with mix ecto.reset.
  5. Run migrations with mix ecto.migrate or rollback with mix ecto.rollback.
  6. Run mix phx.server to start the development server.


For development, I recommend setting environment variables with direnv.

By default, Cannery will always bind to all external IPv4 and IPv6 addresses in dev and prod mode, respectively. If you would like to use different values, they will need to be overridden in config/dev.exs and config/runtime.exs for dev and prod modes, respectively.


In dev mode, Cannery will listen for these environment variables at runtime.

  • HOST: External url to generate links with. Set this especially if you're behind a reverse proxy. Defaults to localhost. External URLs will always be generated with https:// and port 443.
  • PORT: Internal port to bind to. Defaults to 4000.
  • DATABASE_URL: Controls the database url to connect to. Defaults to ecto://postgres:postgres@localhost/cannery_dev.
  • ECTO_IPV6: Controls if Ecto should use IPv6 to connect to PostgreSQL. Defaults to false.
  • POOL_SIZE: Controls the pool size to use with PostgreSQL. Defaults to 10.
  • REGISTRATION: Controls if user sign-up should be invite only or set to public. Set to public to enable public registration. Defaults to invite.
  • LOCALE: Sets a custom default locale. Defaults to en_US.
    • Available options: en_US, de, fr, and es


In test mode (or in the Docker container), Cannery will listen for the same environment variables as dev mode, but also include the following at runtime:

  • TEST_DATABASE_URL: REPLACES DATABASE_URL. Controls the database url to connect to. Defaults to ecto://postgres:postgres@localhost/cannery_test.
  • MIX_TEST_PARTITION: Only used if TEST_DATABASE_URL is not specified. Appended to the default database url if you would like to partition your test databases. Defaults to not set.


In prod mode (or in the Docker container), Cannery will listen for the same environment variables as dev mode, but also include the following at runtime:

  • SECRET_KEY_BASE: Secret key base used to sign cookies. Must be generated with docker run -it shibaobun/cannery mix phx.gen.secret and set for server to start.
  • SMTP_HOST: The url for your SMTP email provider. Must be set
  • SMTP_PORT: The port for your SMTP relay. Defaults to 587.
  • SMTP_USERNAME: The username for your SMTP relay. Must be set!
  • SMTP_PASSWORD: The password for your SMTP relay. Must be set!
  • SMTP_SSL: Set to true to enable SSL for emails. Defaults to false.
  • EMAIL_FROM: Sets the sender email in sent emails. Defaults to no-reply@HOST where HOST was previously defined.
  • EMAIL_NAME: Sets the sender name in sent emails. Defaults to "Cannery".


Thank you so much for your contributions!