Contributing

ClimaCore.jl is developed in the open, and contributions of every size are welcome: bug reports, documentation fixes, examples, and features. This page says how to report a problem, how to set up a development environment, what the pre-commit hooks and CI check, and how the documentation is built.

Developer guides

ClimaCore follows the shared CliMA Developer Guides, vendored at docs/dev-guides/ and synced monthly. They hold the code style, comment and documentation policy, GPU and type-stability rules, and the review checklist; the repository-specific guide is docs/clima_core_specific.md. Edits to the shared guides belong upstream, not in the vendored copy.

Ways to contribute

  • Run ClimaCore, and open an issue for anything that is wrong or hard to use.
  • Take an existing issue; those labeled good first issue need no prior knowledge of the code base.
  • Improve the documentation, or write an example or tutorial.
  • Implement a feature.

Before starting on something larger than a fix, say so on the issue (or open one), so that the work is not duplicated and design questions are settled early. For questions rather than problems, use the repository's discussions.

Reporting a bug

Search the issues first. If the problem is new, open an issue with the bug-report template and include

  • a minimal code snippet that reproduces it on the latest release, or on main if the release is not affected;
  • the complete error message and stack trace, however long;
  • the output of versioninfo() and ] status, and the hardware (in particular the GPU, if any).

Development setup

  1. Fork the repository on GitHub and clone your fork:

    git clone https://github.com/<your-user-name>/ClimaCore.jl.git
    cd ClimaCore.jl
    git remote add upstream https://github.com/CliMA/ClimaCore.jl.git
  2. Instantiate the project and run the tests:

    julia --project -e 'using Pkg; Pkg.instantiate(); Pkg.test()'

    The full suite takes a long time; test/runtests.jl shows how to run a subset.

  3. Install the pre-commit hooks (next section), so that formatting is checked before each commit.

Pre-commit hooks

.pre-commit-config.yaml defines the checks CI runs on every pull request. Installing the hooks is optional but saves a round trip:

uv tool install prek   # or: pipx install prek
prek install           # installs the git hook in this clone

prek is a drop-in replacement for pre-commit; pip install pre-commit && pre-commit install reads the same configuration. The hooks then run on the staged files at every git commit; prek run --all-files sweeps the repository, and prek run julia-formatter --all-files runs one hook. The hooks are:

  • The standard pre-commit-hooks: trailing whitespace, end-of-file newline, mixed line endings, TOML and YAML syntax, merge-conflict markers, large files, case conflicts, broken symlinks.
  • julia-formatter, which runs JuliaFormatter from the version-pinned .dev/format/ environment with the rules in .JuliaFormatter.toml. Use the pinned environment: another JuliaFormatter version produces a different diff from CI. format_docstrings and format_markdown are on, so docstring bodies and .md files are formatted too, and Julia code inside a fenced block is reformatted; tag REPL transcripts and program output as julia-repl`` ortext$, not$`$julia$. The vendored docs/dev-guides/, .github/, and the hand-laid-out README.md, NEWS.md, and AGENTS.md are excluded.
  • markdown-link-ambiguity, which flags a docs-build failure that is easy to introduce and slow to diagnose: Documenter parses [text](target) as a link even across whitespace or a line break, so bracketed units followed by a parenthetical, [m/s] (at the surface), become a link with an unresolvable target. Because docs/make.jl uses checkdocs = :exports, such a link in an unrendered docstring stays latent until the symbol is added to a page. Separate the bracket and the parenthesis with punctuation, or put the units in backticks.

CI runs the same hooks through .github/workflows/format.yml. Repository-wide formatting commits are listed in .git-blame-ignore-revs; git config blame.ignoreRevsFile .git-blame-ignore-revs keeps them out of git blame.

Pull requests

The project follows the ColPrac guide for collaborative practices. Pull requests go against main from a branch of your fork (collaborators may branch in the main repository). Keep each pull request to one logical change, leave unrelated files alone, and write commit messages in the style of Chris Beams's guide. A pull request is merged once a collaborator has reviewed and approved it; an author with write access merges their own, otherwise a collaborator merges with the author's consent. Review takes time in proportion to the size of the change, so small pull requests move faster. Contributors who have opened and merged a pull request may ask for collaborator status, which adds the ability to review.

If this is your first pull request, GitHub's guide to forks and the Open Source Guides explain the workflow; for a one-line fix, the GitHub web editor forks and opens the pull request for you.

What CI checks

CheckWhat it does
Prek checksThe pre-commit hooks above, over the whole repository.
DocumentationBuilds the documentation, runs its examples and doctests, and checks cross-references; a preview is deployed for the pull request.
Unit testsPkg.test() on the latest commit of the pull request, on Linux; stale jobs are cancelled on push. Documentation-only changes skip it.
OS unit testsThe unit tests on Linux, macOS, and Windows.
ClimaCore CIThe expensive tests, the GPU tests, and the examples, as Slurm jobs on an HPC cluster through Buildkite, defined in .buildkite/pipeline.yml. docs/dev-guides/workflow/ci_triage.md is the checklist for a failure that does not reproduce locally.

CI does not measure GPU kernel launch latency, because absolute latencies vary between nodes and with load. A change to the CUDA extension (ext/ClimaCoreCUDAExt.jl, ext/cuda/) should be checked by hand with the scripts in perf/ and benchmarks/ or by profiling.

Documentation

The documentation is organized by Diátaxis mode: tutorials (Literate.jl scripts in docs/tutorials/), how-to guides (docs/src/howto/), explanation (docs/src/explanation/), and reference (docs/src/reference/, curated @docs blocks). A new page goes in the directory of its mode and in the pages tree of docs/make.jl; a how-to title is a task ("Run on a GPU"), and every code block a reader might paste is an @example block or part of a tutorial, so that the build runs it. docs/dev-guides/code-quality/documentation_policy.md is the policy for pages and docstrings.

Tutorials are Literate.jl scripts: comments become text, code runs and its output is shown, and a trailing ; suppresses the output of a line. Plots use CairoMakie with the ClimaCore.Visualize recipes. Write the comments as an article that states its goal first and shows a result at every step.

To build locally:

julia --project=docs -e 'using Pkg; Pkg.develop(path = "."); Pkg.instantiate()'
julia --project=docs docs/make.jl

The output is in docs/build/; LiveServer.servedocs() rebuilds on save. The build checks external links (LINKCHECK_STRICT=1 makes a broken link fatal) and, with STRICT_DOCSTRING_REFS=1, fails on a docstring @ref to a symbol that no page renders (docs/check_docstring_refs.jl).

Credits

This guide derives from the ClimateMachine.jl and ClimaAtmos.jl contributor guides, which in turn derive from those of Oceananigans.jl and MetPy.