Thank you for your interest in contributing to gnucashr! This document provides guidelines and instructions for contributing.
Please be respectful and constructive in all interactions. We welcome contributors of all experience levels.
This is a monorepo with the R package in packages/gnucashr/:
gnucashr/
├── packages/gnucashr/ # R package (CRAN-submittable)
│ ├── R/ # R source files
│ ├── src/ # C++ source (Rcpp)
│ ├── tests/testthat/ # Test files
│ └── DESCRIPTION # Package metadata
├── dhall/ # Typed configuration (agent rules, templates)
├── docs/epic/ # Development roadmap
├── flake.nix # Nix flake (reproducible builds)
├── justfile # Central command runner
└── BUILD.bazel # Bazel build configuration
-
Clone the repository:
# From GitLab (primary) git clone https://gitlab.com/tinyland/projects/gnucashr.git # Or from GitHub (mirror) git clone https://github.com/Jesssullivan/gnucashr.git
-
Enter the Nix development shell:
nix develop # Or with direnv: direnv allowThis provides R, all package dependencies, C++ toolchain, Just, Dhall, and development tools in a single reproducible environment.
-
Verify setup:
just --list # Show available commands just test # Run tests just dhall-check # Validate Dhall configuration
-
Install system dependencies:
- C++17 compiler and GNU make
- macOS:
xcode-select --install - Ubuntu/Debian:
sudo apt-get install build-essential - Fedora/RHEL:
sudo dnf install gcc-c++ make - Windows: Install Rtools
-
Set up renv:
cd packages/gnucashrinstall.packages("renv") renv::restore()
-
Or install dependencies manually:
install.packages(c( "devtools", "roxygen2", "testthat", "usethis", "DBI", "RSQLite", "dplyr", "tibble", "purrr", "lubridate", "readr", "R6", "rlang", "xml2", "Rcpp", "RcppParallel", "RcppArmadillo", "jsonlite" ))
Use just from the repo root, or standard devtools from packages/gnucashr/:
# Via Justfile (preferred)
just test # Run tests
just check # R CMD check
just document # Generate docs
just build # Build tarball
just lint # Lint R code
just dhall-check # Validate Dhall config
# Via Nix
just nix-build # Build tarball via Nix
just nix-check # R CMD check via Nix
just shell # Enter Nix dev shellOr directly with R:
# From packages/gnucashr/
devtools::load_all()
devtools::test()
devtools::check()
devtools::document()Follow the tidyverse style guide:
- Use
snake_casefor function and variable names - Use
<-for assignment, not= - Limit lines to 80 characters when reasonable
- Use roxygen2 for documentation with markdown syntax
- Prefer tidyverse functions where appropriate
Linting: Consider using lintr to check your code:
lintr::lint_package()- Use modern C++17 features where beneficial
- Follow consistent naming conventions (snake_case preferred)
- Document exported functions with roxygen2 comments in the R wrapper
- Handle errors gracefully; use
Rcpp::stop()for fatal errors - Avoid raw pointers; prefer smart pointers or Rcpp types
Example Rcpp function structure:
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
NumericVector my_function(NumericVector x) {
if (x.size() == 0) {
stop("Input vector cannot be empty");
}
// Implementation
return x;
}- All exported functions must have roxygen2 documentation
- Include
@paramfor all parameters - Include
@returndescribing the return value - Include
@exampleswith runnable examples - Use
@familyto group related functions
# Via Justfile (from repo root)
just test
# Or from packages/gnucashr/:
Rscript -e 'devtools::test()'
# Run specific test file
Rscript -e 'testthat::test_file("tests/testthat/test-connection.R")'
# Run tests with coverage
just coverage- Place tests in
tests/testthat/ - Name test files
test-*.R - Use descriptive test names
- Test both success and failure cases
- Use
withrfor temporary files and state changes
Example test structure:
test_that("function_name handles edge case", {
# Setup
test_file <- withr::local_tempfile(fileext = ".gnucash")
# Exercise
result <- my_function(test_file)
# Verify
expect_equal(result, expected_value)
})- Do not commit real GnuCash files with sensitive financial data
- Use small, anonymized test files
- Consider generating test data programmatically
Use descriptive branch names:
feature/add-budget-reportsfix/xml-parsing-errordocs/update-vignetterefactor/consolidation-api
Write clear, concise commit messages:
- Use present tense ("Add feature" not "Added feature")
- First line should be 50 characters or less
- Reference issues when applicable ("Fix #123")
- Create a branch from
main - Make your changes following the guidelines above
- Run checks:
just check just dhall-check
- Update documentation if you changed exported functions
- Update
packages/gnucashr/NEWS.mdfor user-facing changes - Submit a pull request to the GitLab repository (preferred) or GitHub mirror
- Respond to review feedback promptly
Contributing to the C++ components requires extra care:
- Never return raw pointers from Rcpp functions
- Use RAII patterns for resource management
- Be careful with RcppParallel; avoid R API calls in parallel regions
#include <RcppParallel.h>
struct MyWorker : public RcppParallel::Worker {
// Input (read-only)
const RcppParallel::RVector<double> input;
// Output
RcppParallel::RVector<double> output;
// Constructor, operator(), etc.
};- Write R-level tests that exercise the C++ code
- Test edge cases: empty inputs, large inputs, invalid inputs
- Use
hedgehogfor property-based testing when appropriate
If you modify src/Makevars, ensure it works across platforms:
PKG_CXXFLAGS = $(SHLIB_OPENMP_CXXFLAGS)
PKG_LIBS = $(SHLIB_OPENMP_CXXFLAGS) $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS)When reporting bugs, please include:
- R version and OS
- gnucashr version
- Minimal reproducible example
- GnuCash file format (SQLite/XML)
- Full error messages
When requesting features, please describe:
- The use case
- Proposed solution
- Alternatives considered
- Issues: GitLab Issues
- Documentation: Package Website
By contributing to gnucashr, you agree that your contributions will be licensed under the MIT License.