Contributing

Setup

git clone git@github.com:Dxsk/repos-manager.git
cd repos-manager

No build step needed - it's pure Bash.

Running tests

# Install bats
sudo pacman -S bats    # Arch
brew install bats-core # macOS

# Run all tests
bats tests/*.bats

Test coverage

Module Tests What's covered
cli 8 Version, help, unknown commands, provider routing
flags 11 All flag parsing combinations
match 9 Patterns, wildcards, .repos-filter, .repos-ignore
config 8 Load/init config.json, tilde expansion, protocol
providers 9 SSH/HTTPS URL extraction for all 5 providers
status 5 Clean, dirty, ahead, multiple repos detection
sync 4 Clone, update, skip dirty, clone failure
Total 60

Linting

shellcheck -x repos-manager.sh lib/*.sh sourceme.bash

CI pipeline

Every push triggers 4 checks:

Check Tool What it does
Bash lint ShellCheck Static analysis of all .sh files
Tests Bats Runs the full 60-test suite
Links Lychee Validates URLs in all markdown files
Shell compat zsh + fish Syntax checks on sourceme files

All checks must pass before merging.

Adding a provider

The provider system is modular. Each provider is a single file in lib/ implementing 4 functions:

Step 1 - Create the provider file

Create lib/yourprovider.sh:

#!/usr/bin/env bash
# YourProvider (uses yourcli)

yourprovider_login() {
    yourcli auth login
}

yourprovider_list_repos() {
    # Must return a JSON array of objects with:
    # nameWithOwner, sshUrl, url
    yourcli repo list --json ...
}

yourprovider_get_clone_url() {
    local repo_json="$1"
    if $USE_HTTPS; then
        echo "$repo_json" | jq -r '.url'
    else
        echo "$repo_json" | jq -r '.sshUrl'
    fi
}

yourprovider_get_full_name() {
    echo "$1" | jq -r '.nameWithOwner'
}

Step 2 - Register it

In repos-manager.sh:

  1. Source it: source "${REPOS_MANAGER_LIB}/yourprovider.sh"
  2. Add to VALID_PROVIDERS
  3. Add to detect_providers()
  4. Add to cmd_sync() host mapping
  5. Add to cmd_sync_all() providers array
  6. Add case in main()

Step 3 - Add tests

In tests/providers.bats:

@test "yourprovider: get ssh clone url" {
    USE_HTTPS=false
    local json='{"nameWithOwner":"user/repo","sshUrl":"git@host:user/repo.git","url":"https://host/user/repo"}'
    local result
    result=$(yourprovider_get_clone_url "$json")
    [[ "$result" == "git@host:user/repo.git" ]]
}

Step 4 - Document

Update readme.md, site/src/docs/providers.md, and the glossary.

Project structure

$ tree repos-manager/
repos-manager/
├── repos-manager.sh        # Entry point, CLI routing
├── Makefile                 # Install/uninstall
├── flake.nix                # Nix flake (optional)
├── sourceme.bash            # Bash shell integration
├── sourceme.zsh             # Zsh shell integration
├── sourceme.fish            # Fish shell integration
├── lib/
│   ├── log.sh               # Colors, log functions
│   ├── flags.sh             # Flag parsing
│   ├── config.sh            # Config file, sourceme gen
│   ├── match.sh             # Pattern matching, filter/ignore
│   ├── sync.sh              # Core sync engine (parallel)
│   ├── status.sh            # Status command
│   ├── update.sh            # Self-update
│   ├── github.sh            # GitHub provider
│   ├── gitlab.sh            # GitLab provider
│   ├── forgejo.sh           # Forgejo/Gitea provider
│   ├── bitbucket.sh         # Bitbucket provider
│   └── radicle.sh           # Radicle provider
└── tests/
    ├── test_helper.bash     # Shared test utilities
    ├── cli.bats             # CLI tests
    ├── flags.bats           # Flag parsing tests
    ├── match.bats           # Pattern matching tests
    ├── config.bats          # Config tests
    ├── providers.bats       # Provider URL tests
    ├── status.bats          # Status tests
    └── sync.bats            # Sync tests

Pull requests

Rule Details
Branch from develop
Scope One feature per PR
Tests Must pass (bats tests/*.bats)
Lint Must pass (shellcheck -x)
Commits Conventional style (feat:, fix:, docs:, test:)