Skip to content

Getting Started

This guide walks you through everything you need — from creating a GitHub account to running your first test — so you can start contributing to canVODpy even if you have never used Git, GitHub, or Python tooling before.


1. Create a GitHub account

GitHub is a website that hosts code and lets teams collaborate on software projects. If you don't already have an account, sign up at github.com/signup.


2. Install Homebrew (macOS only)

Homebrew is the standard package manager for macOS — it lets you install developer tools with a single command. Open Terminal (press Cmd+Space, type "Terminal", press Enter) and run:

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

Follow the on-screen instructions. When it finishes, it will tell you to run two commands to add Homebrew to your PATH — copy and run them. Then verify:

brew --version

Note

If you already have Homebrew installed, run brew update to make sure it's current.


3. Install Git

Git is the version-control system that tracks changes in the codebase. Install it for your operating system:

brew install git
sudo apt update && sudo apt install git
sudo dnf install git

Download and run the installer from git-scm.com. Accept the default options during installation. Afterwards, open Git Bash (installed with Git) to run the commands in this guide.

Verify the installation:

git --version

You should see something like git version 2.x.x.


4. Set up an SSH key for GitHub

SSH keys let you securely connect to GitHub without typing your password every time.

Generate a key

ssh-keygen -t ed25519 -C "your_email@example.com"

Press Enter three times to accept the defaults (default file location, no passphrase).

ssh-keygen -t ed25519 -C "your_email@example.com"

Press Enter three times to accept the defaults.

Add the key to the SSH agent

eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519

Copy the public key

pbcopy < ~/.ssh/id_ed25519.pub
cat ~/.ssh/id_ed25519.pub

Select and copy the output.

clip < ~/.ssh/id_ed25519.pub

Add the key to GitHub

  1. Go to github.com/settings/keys.
  2. Click New SSH key.
  3. Give it a title (e.g. "My Laptop"), paste the key, and click Add SSH key.

Test the connection

ssh -T git@github.com

You should see: Hi <username>! You've successfully authenticated...


5. Configure your Git identity

Tell Git who you are (this information appears in your commits):

git config --global user.name "Your Name"
git config --global user.email "your_email@example.com"

Use the same email you registered on GitHub.


6. Install development tools

canVODpy uses two command-line tools to manage the project:

  • uv — a fast Python package manager that handles dependencies and virtual environments.
  • just — a command runner (like a simplified Makefile) that provides shortcuts for common tasks.

Install uv

brew install uv
curl -LsSf https://astral.sh/uv/install.sh | sh
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

Install just

brew install just

This works on all Linux distributions (Ubuntu, Mint, Fedora, etc.):

curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to ~/.local/bin

Make sure ~/.local/bin is on your PATH. Add this to your ~/.bashrc or ~/.zshrc if needed:

export PATH="$HOME/.local/bin:$PATH"
sudo apt install just

Warning

This does not work on Linux Mint or older Ubuntu versions. Use the "Linux (any distro)" tab instead.

winget install Casey.Just

Verify both tools

uv --version
just --version

7. Fork and clone the repository

A fork is your own copy of the project on GitHub. You make changes in your fork and then propose them back to the original project via a "pull request."

Fork on GitHub

  1. Go to github.com/nfb2021/canvodpy.
  2. Click the Fork button in the top-right corner.
  3. GitHub will create a copy at github.com/YOUR_USERNAME/canvodpy.

Clone your fork

git clone git@github.com:YOUR_USERNAME/canvodpy.git
cd canvodpy

Replace YOUR_USERNAME with your actual GitHub username.

Add the upstream remote

This lets you pull in updates from the original repository later:

git remote add upstream git@github.com:nfb2021/canvodpy.git

Verify your remotes:

git remote -v

You should see origin (your fork) and upstream (the original).


8. Initialize submodules

The repository uses two Git submodules that contain test data and demo data. Initialize them after cloning:

git submodule update --init --recursive

This pulls:

  • packages/canvod-readers/tests/test_data — validation test data (falsified/corrupted RINEX files for testing)
  • demo — clean real-world data for demos and documentation

If you skip this step, tests that require these datasets will be automatically skipped.


9. Set up the development environment

From inside the canvodpy directory, run:

# Verify required tools are available
just check-dev-tools

# Install all Python dependencies into a virtual environment
uv sync

# Install pre-commit hooks (automatic code checks before each commit)
just hooks

10. Configure the project

canVODpy uses three YAML configuration files in the config/ directory:

File Purpose
sites.yaml Defines research sites: data root paths, receiver definitions (name, type, directory), and VOD analysis pairs. Each receiver's directory is the full relative path from the site data root to the raw RINEX date folders (e.g. 01_reference/01_GNSS/01_raw).
processing.yaml Processing parameters: metadata, credentials (NASA Earthdata), auxiliary data settings (agency, product type), time aggregation, compression, Icechunk storage, and store strategies.
sids.yaml Signal ID (SID) filtering: choose all, a named preset (e.g. gps_galileo), or list custom SIDs to keep.

Each file has a corresponding .example template. To initialize them:

just config-init

After editing, validate your configuration:

just config-validate

To view the resolved configuration:

just config-show

11. Verify everything works

Run the test suite:

just test

Run code-quality checks (linting, formatting, type checking):

just check

If both commands complete without errors, your environment is ready.


12. Keeping your fork up to date

The upstream repository (nfb2021/canvodpy) receives regular updates. To incorporate these into your fork, you need to fetch from upstream and merge into your local branch. This section explains every step.

Why this is necessary

When you forked the repository, GitHub created a snapshot of main at that moment. Any commits pushed to the upstream main after your fork was created are not automatically reflected in your fork. If you work on a feature branch for several days, the upstream main may have moved ahead — your branch will be "behind." Merging upstream changes prevents conflicts from accumulating and ensures you are building on the latest codebase.

One-time setup (already done if you followed step 7)

Verify that you have the upstream remote configured:

git remote -v

You should see two remotes:

origin    git@github.com:YOUR_USERNAME/canvodpy.git (fetch)
origin    git@github.com:YOUR_USERNAME/canvodpy.git (push)
upstream  git@github.com:nfb2021/canvodpy.git (fetch)
upstream  git@github.com:nfb2021/canvodpy.git (push)

If upstream is missing, add it:

git remote add upstream git@github.com:nfb2021/canvodpy.git

Update your local main branch

Every time you want to sync with the latest upstream changes:

# 1. Switch to your local main branch
git checkout main

# 2. Fetch all new commits from the upstream repository
git fetch upstream

# 3. Merge upstream/main into your local main
#    This is a fast-forward merge (no merge commit) if you have not
#    committed directly to your local main — which you should not do.
git merge upstream/main

# 4. Push the updated main to your fork on GitHub
git push origin main

After these four commands, your fork's main branch on GitHub is identical to the upstream main.

Update your feature branch

If you are working on a feature branch, incorporate the freshly updated main:

# Switch to your feature branch
git checkout feature/my-feature

# Rebase your feature branch onto the updated main
git rebase main

Rebasing replays your commits on top of the latest main, producing a linear history. If Git reports a merge conflict during rebase, it will pause and show you which files conflict. Resolve each conflict by editing the file, then:

git add <resolved-file>
git rebase --continue

If you want to abort the rebase and return to the state before:

git rebase --abort

Force push after rebase

Rebasing rewrites commit history. If you have already pushed your feature branch to your fork, you will need to force-push after rebasing:

git push --force-with-lease origin feature/my-feature

--force-with-lease is safer than --force because it refuses to overwrite commits that someone else may have pushed to the same branch.

Quick reference

# Full sync sequence (run periodically):
git checkout main
git fetch upstream
git merge upstream/main
git push origin main
git checkout feature/my-feature
git rebase main

12b. Working in teams

During collaborative sprints, each team works on its own topic (often aligned with a package such as canvod-grids or canvod-readers). A shared develop branch acts as the integration point — no one commits to it directly. Instead, each team gets its own team branch.

Branch structure

main
└── develop/sprint-2026              ← integration branch (shared by all teams)
    ├── team-grids/                  ← Team A
    │   ├── (direct commits)         ← Workflow A
    │   └── team-grids/add-healpix   ← Workflow B feature branches
    ├── team-readers/                ← Team B
    └── team-vod/                    ← Team C

Set up the team branch

The team lead creates the branch once:

git checkout develop/sprint-2026
git checkout -b team-grids
git push -u origin team-grids

All other team members fetch and switch to it:

git fetch origin
git checkout team-grids

From here, choose the workflow that fits your team:

Best for small teams (2–3 people) or when members work on separate files.

Everyone commits and pushes to the team branch:

# Make your changes, then:
git add <files you changed>
git commit -m "feat(grids): add new grid type"
git push origin team-grids

If someone else pushed before you, pull first:

git pull --rebase origin team-grids
git push origin team-grids

Best for larger teams or when you want lightweight review within the team.

Create a feature branch off the team branch:

git checkout team-grids
git checkout -b team-grids/add-healpix

Work, commit, and push your feature branch:

git add <files you changed>
git commit -m "feat(grids): add HEALPix support"
git push -u origin team-grids/add-healpix

Then open a pull request on GitHub targeting team-grids.

To stay up to date with teammates' merged work:

git checkout team-grids
git pull origin team-grids
git checkout team-grids/add-healpix
git rebase team-grids

Merge the team's work into the develop branch

When your team's feature is ready, open one pull request from team-grids into the develop branch on GitHub. This is where the maintainer reviews the team's combined work.


13. Your first contribution

Make your changes

Edit files with your favorite text editor or IDE.

Run quality checks

just test
just check

Stage and commit

git add <files you changed>
git commit -m "feat(grids): add new grid type"

Commit messages follow the Conventional Commits format: type(scope): description. Common types: feat, fix, docs, test, refactor.

Push

Push to your team branch or feature branch (see Working in teams above).

Open a pull request

  1. Go to your fork on GitHub — you'll see a banner suggesting to open a pull request.
  2. Click Compare & pull request.
  3. Set the base branch to your team branch (or the develop branch, depending on your workflow).
  4. Add a title and description, then click Create pull request.

14. Pre-commit hooks and why your commit may be rejected

When you ran just hooks in step 9, a set of pre-commit hooks was installed into your local .git/hooks/ directory. These hooks run automatically every time you execute git commit. If any hook fails, the commit is aborted — your changes remain staged but no commit is created.

This is intentional: it prevents code that does not meet the project's quality standards from entering the Git history. The hooks are defined in .pre-commit-config.yaml at the repository root.

What runs on every commit

The following checks execute automatically in sequence. If any one fails, the commit is rejected.

Hook When it runs What it checks Typical failure reason
ruff check pre-commit (before commit is created) Python linting — unused imports, undefined names, style violations, type annotation issues You have an unused import or a linting rule violation
ruff format pre-commit Python formatting — consistent code style (indentation, line length, quote style, trailing commas) Your code is not formatted according to the project style
uv-lock pre-commit Lockfile consistency — verifies uv.lock matches pyproject.toml You changed a dependency in pyproject.toml but did not run uv sync
trailing-whitespace pre-commit Removes trailing whitespace from all files A line ends with invisible spaces or tabs
check-added-large-files pre-commit Blocks files larger than the threshold from being committed You are trying to commit a large binary, dataset, or log file
detect-private-key pre-commit Scans for accidentally staged private keys (SSH, PGP) You are about to commit a secret — do not override this
end-of-file-fixer pre-commit Ensures every file ends with exactly one newline A file is missing its final newline or has extra blank lines at the end
commitizen commit-msg (after you write the message) Validates that your commit message follows the Conventional Commits specification Your message does not match the type(scope): subject format

How to fix a rejected commit

When a hook fails, read the error output carefully. The most common scenarios:

ruff will often auto-fix the issue. After the commit is rejected:

# The hook already ran ruff --fix, so files may have been modified.
# Check what changed:
git diff

# If the changes look correct, stage them and commit again:
git add -u
git commit -m "feat(readers): your original message"

If ruff cannot auto-fix (e.g., an undefined variable), you must edit the code manually, then stage and commit again.

Your commit message does not follow the Conventional Commits format. The error will say something like:

commit validation: failed!
please enter a commit message in the commitizen format.

The required format is:

type(scope): subject

Where type is one of: feat, fix, docs, refactor, test, chore, perf, ci, build, style

And scope is one of: readers, aux, grids, vod, store, viz, utils, naming, ops, docs, ci, deps

Examples of valid messages:

git commit -m "feat(readers): add RINEX 4.0 parser"
git commit -m "fix(store): handle empty epoch arrays"
git commit -m "docs: update installation guide"
git commit -m "test(vod): add tau-omega edge cases"

Examples of invalid messages (will be rejected):

git commit -m "added new feature"           # no type prefix
git commit -m "feat: add stuff"              # too vague (but technically valid)
git commit -m "FEAT(readers): add parser"    # type must be lowercase
git commit -m "feat(readers) add parser"     # missing colon after scope

You modified a pyproject.toml (added or changed a dependency) but the lockfile is out of date:

uv sync          # regenerates uv.lock
git add uv.lock
git commit -m "feat(readers): your original message"

You are trying to commit a file that exceeds the size threshold (typically data files, .zarr stores, or log files). These should not be in the Git repository. Remove the file from staging:

git reset HEAD <large-file>
echo "<large-file>" >> .gitignore
git add .gitignore
git commit -m "chore: update gitignore"

Bypassing hooks (emergency only)

In rare situations where you need to commit despite a hook failure (e.g., you are certain the failure is a false positive), you can skip hooks:

git commit --no-verify -m "fix(store): emergency hotfix"

Use --no-verify with extreme caution

Skipping hooks defeats their purpose. The CI pipeline on GitHub runs the same checks — your pull request will fail if the code does not pass ruff, formatting, and commit message validation. Only use --no-verify when you fully understand why the hook is failing and are certain the failure is not a genuine code quality issue.


15. Continuous integration and test coverage

Every push to a branch and every pull request triggers automated checks on GitHub Actions. These are the same checks that run locally via just check and just test, but they execute in a clean environment to ensure reproducibility.

CI pipelines

Workflow Trigger What it does
Code Quality Every push Checks lockfile consistency, runs ruff linting, checks formatting, runs type checking with ty
Test with Coverage Push to main and all PRs Runs just test-coverage (pytest with coverage measurement), uploads results to Coveralls, posts a coverage summary comment on the PR
Platform Tests PRs Runs the test suite across multiple operating systems and Python versions
Deploy Docs Push to main Builds and deploys the documentation site

Test coverage with Coveralls

The project uses Coveralls to track test coverage over time. After each test run in CI:

  1. pytest generates a coverage report in LCOV format (coverage.lcov)
  2. The report is uploaded to coveralls.io/github/nfb2021/canvodpy
  3. A coverage summary is posted as a comment on the pull request, showing which lines are covered and which are not

Coverage is not a gate (your PR will not be blocked by low coverage), but it is visible to reviewers. When adding new code, include tests — this is both good scientific practice (every computation should be verifiable) and a courtesy to future contributors who need to understand what the code does.

To measure coverage locally before pushing:

just test-coverage
# Opens an HTML report in your browser showing line-by-line coverage

16. Common commands cheat sheet

Print this

Command What it does
just test Run all tests
just check Lint, format, and type-check all code
just hooks Install pre-commit hooks
just check-dev-tools Verify uv, just, and python3 are installed
just config-init Initialize configuration files from templates
just config-validate Validate the current configuration
just config-show Show the resolved configuration
just docs Preview documentation locally
just test-coverage Run tests with coverage report
just clean Remove build artifacts and caches
uv sync Install/update Python dependencies

17. Troubleshooting
"command not found" for uv, just, or git
The tool is not installed or not on your PATH. Re-run the installation step and, if needed, open a new terminal window so your shell picks up the updated PATH.
"Permission denied (publickey)" when pushing or cloning
Your SSH key is not set up correctly. Go back to step 4 and make sure the key is added to both the SSH agent and your GitHub account.
uv sync fails with a Python version error
canVODpy requires Python 3.14. Install a supported version with uv python install 3.14 and try again.
Pre-commit hook fails on commit
Run just check — it will auto-fix most linting and formatting issues. Stage the fixed files and commit again.
"push rejected" or "failed to push"

Your branch is behind the remote. Pull the latest changes first:

git pull --rebase origin my-feature
Windows: just says "could not find the shell" or "system cannot find the path"

The Justfile expects Git Bash at C:\Program Files\Git\bin\bash.exe (the default Git for Windows location). If Git is installed elsewhere, find its location by running in PowerShell:

where.exe bash

Then update the path in the first lines of the Justfile:

set windows-shell := ["C:/YOUR/ACTUAL/PATH/TO/bash.exe", "-c"]

Warning

Do not commit this change — it's specific to your machine. The default path works for most installations and for CI.

Windows: uv install fails with "execution of scripts is disabled"

PowerShell's default execution policy blocks scripts. Use the bypass flag:

powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
Windows: line ending warnings (LF will be replaced by CRLF)

Configure Git to keep Unix-style line endings:

git config --global core.autocrlf input

Next in the trail: Audit Suite · API Levels · Architecture · AI Development