Internal API Reference

Overview

This document provides a comprehensive reference for cpm's internal Go packages and APIs. These packages form the core functionality that the CLI commands utilize. While primarily for internal use, they can be imported and used programmatically for building custom tooling.

Package Structure

internal/
├── config/         # Configuration management
├── db/             # Database operations
├── org/            # Organization management
├── repo/           # Repository operations
├── server/         # Server management
└── ssh/            # SSH key operations

config Package

Import: cpm/internal/config

Types

Config

type Config struct {
    MainServer   string // Main server address (user@host)
    DataDir      string // Local repository storage directory
    SSHKeyPath   string // Default SSH private key path
    DatabasePath string // SQLite database file path
}

Functions

Init

func Init(cfgFile string) error

Initialize configuration system. Creates default config if not exists.

Parameters:

  • cfgFile: Path to config file (empty string for default)

Returns: error if initialization fails

Example:

if err := config.Init(""); err != nil {
    log.Fatal(err)
}

Load

func Load() (*Config, error)

Load configuration from file.

Returns:

  • *Config: Configuration struct
  • error: Loading error if any

Example:

cfg, err := config.Load()
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Main server: %s\n", cfg.MainServer)

Save

func Save(cfg *Config) error

Save configuration to file.

Parameters:

  • cfg: Configuration to save

Returns: error if save fails

Example:

cfg.MainServer = "git@newserver.com"
if err := config.Save(cfg); err != nil {
    log.Fatal(err)
}

OpenDB

func OpenDB(cfg *Config) (*sql.DB, error)

Open database connection.

Parameters:

  • cfg: Configuration with database path

Returns:

  • *sql.DB: Database connection
  • error: Connection error if any

db Package

Import: cpm/internal/db

Types

DB

type DB struct {
    // contains filtered or unexported fields
}

Database connection wrapper.

User

type User struct {
    ID        int64     `json:"id"`
    Username  string    `json:"username"`
    Email     string    `json:"email"`
    PublicKey string    `json:"public_key,omitempty"`
    CreatedAt time.Time `json:"created_at"`
}

Organization

type Organization struct {
    ID          int64     `json:"id"`
    Name        string    `json:"name"`
    Description string    `json:"description"`
    CreatedAt   time.Time `json:"created_at"`
}

Repository

type Repository struct {
    ID          int64     `json:"id"`
    Name        string    `json:"name"`
    OrgID       int64     `json:"org_id"`
    Path        string    `json:"path"`
    Description string    `json:"description"`
    CreatedAt   time.Time `json:"created_at"`
}

Functions

Open

func Open(dbPath string) (*DB, error)

Open database connection and initialize schema.

Parameters:

  • dbPath: Path to SQLite database file

Returns:

  • *DB: Database instance
  • error: Connection/initialization error

Example:

db, err := db.Open("/path/to/cpm.db")
if err != nil {
    log.Fatal(err)
}
defer db.Close()

CreateUser

func (db *DB) CreateUser(username, email, publicKey string) (int64, error)

Create new user.

Parameters:

  • username: Unique username
  • email: User email address
  • publicKey: SSH public key (optional, can be empty)

Returns:

  • int64: New user ID
  • error: Creation error if any

Example:

userID, err := db.CreateUser("alice", "alice@example.com", "ssh-ed25519 AAAA...")
if err != nil {
    log.Fatal(err)
}

CreateRepository

func (db *DB) CreateRepository(name, path string, orgID int64) (int64, error)

Create repository record.

Parameters:

  • name: Repository name
  • path: File system path to bare repository
  • orgID: Organization ID

Returns:

  • int64: New repository ID
  • error: Creation error

CreateOrganization

func (db *DB) CreateOrganization(name, description string) (int64, error)

Create new organization.

Parameters:

  • name: Unique organization name
  • description: Organization description

Returns:

  • int64: New organization ID
  • error: Creation error

repo Package

Import: cpm/internal/repo

Functions

InitRepo

func InitRepo(name, path string) error

Initialize bare git repository.

Parameters:

  • name: Repository name
  • path: Directory to create repository in

Returns: error if initialization fails

Example:

if err := repo.InitRepo("myproject", "/var/cpm/repos"); err != nil {
    log.Fatal(err)
}

RepoExists

func RepoExists(path string) (bool, error)

Check if repository exists at path.

Parameters:

  • path: Repository path

Returns:

  • bool: true if exists
  • error: Check error

ListLocal

func ListLocal(dataDir string) ([]string, error)

List repositories in local directory.

Parameters:

  • dataDir: Data directory to scan

Returns:

  • []string: List of repository names
  • error: List error

PushRepo

func PushRepo(repoPath, remotePath, sshKeyPath string) error

Push repository to remote using rsync.

Parameters:

  • repoPath: Local repository path
  • remotePath: Remote path (user@host:path format)
  • sshKeyPath: SSH private key path

Returns: error if push fails


PullRepo

func PullRepo(remotePath, localPath, sshKeyPath string) error

Pull repository from remote using rsync.

Parameters:

  • remotePath: Remote path (user@host:path format)
  • localPath: Local destination path
  • sshKeyPath: SSH private key path

Returns: error if pull fails

ssh Package

Import: cpm/internal/ssh

Functions

GenerateED25519KeyPair

func GenerateED25519KeyPair(name string) (*KeyPair, error)

Generate ed25519 SSH key pair.

Parameters:

  • name: Key name for file naming

Returns:

  • *KeyPair: Generated key pair info
  • error: Generation error

Example:

keyPair, err := ssh.GenerateED25519KeyPair("main-server")
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Private key: %s\n", keyPair.PrivateKeyPath)

DeployKey

func DeployKey(publicKey, server string) error

Deploy public key to server's authorized_keys.

Parameters:

  • publicKey: SSH public key content
  • server: Server address (user@host format)

Returns: error if deployment fails


GetFingerprint

func GetFingerprint(publicKeyPath string) (string, error)

Calculate SSH key fingerprint.

Parameters:

  • publicKeyPath: Path to public key file

Returns:

  • string: Fingerprint (SHA256 format)
  • error: Calculation error

server Package

Import: cpm/internal/server

Types

Server

type Server struct {
    ID         int64
    Name       string
    Host       string
    Port       int
    User       string
    SSHKeyPath string
    IsNeighbor bool
}

SyncManager

type SyncManager struct {
    // contains filtered or unexported fields
}

Handles repository synchronization.

Functions

NewSyncManager

func NewSyncManager(serverManager *ServerManager, repoBasePath string) *SyncManager

Create sync manager instance.

Parameters:

  • serverManager: Server manager instance
  • repoBasePath: Base path for repositories

Returns: *SyncManager


SyncRepoTo

func (sm *SyncManager) SyncRepoTo(repoName, targetServer string) (*SyncResult, error)

Synchronize repository to server.

Parameters:

  • repoName: Repository name
  • targetServer: Target server name

Returns:

  • *SyncResult: Sync result with statistics
  • error: Sync error

Example:

result, err := syncMgr.SyncRepoTo("myrepo", "origin")
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Synced in %v\n", result.Duration)

Usage Examples

Complete Workflow Example

package main

import (
    "log"
    "cpm/internal/config"
    "cpm/internal/db"
    "cpm/internal/repo"
    "cpm/internal/ssh"
)

func main() {
    // Load configuration
    if err := config.Init(""); err != nil {
        log.Fatal(err)
    }

    cfg, err := config.Load()
    if err != nil {
        log.Fatal(err)
    }

    // Open database
    database, err := db.Open(cfg.DatabasePath)
    if err != nil {
        log.Fatal(err)
    }
    defer database.Close()

    // Create organization
    orgID, err := database.CreateOrganization("engineering", "Engineering team")
    if err != nil {
        log.Fatal(err)
    }

    // Initialize repository
    repoPath := cfg.DataDir + "/myproject.git"
    if err := repo.InitRepo("myproject", repoPath); err != nil {
        log.Fatal(err)
    }

    // Register in database
    repoID, err := database.CreateRepository("myproject", repoPath, orgID)
    if err != nil {
        log.Fatal(err)
    }

    log.Printf("Created repository ID: %d\n", repoID)
}

Custom Repository Sync Tool

package main

import (
    "log"
    "cpm/internal/config"
    "cpm/internal/repo"
)

func main() {
    cfg, _ := config.Load()

    repos, err := repo.ListLocal(cfg.DataDir)
    if err != nil {
        log.Fatal(err)
    }

    for _, repoName := range repos {
        localPath := cfg.DataDir + "/" + repoName
        remotePath := cfg.MainServer + ":" + cfg.DataDir + "/" + repoName

        log.Printf("Syncing %s...\n", repoName)
        if err := repo.PushRepo(localPath, remotePath, cfg.SSHKeyPath); err != nil {
            log.Printf("Failed: %v\n", err)
            continue
        }
        log.Printf("Success\n")
    }
}

Error Handling

All functions return Go-style errors. Check and handle appropriately:

result, err := someFunction()
if err != nil {
    // Handle error
    log.Printf("Error: %v", err)
    return err
}
// Use result

Thread Safety

  • Database operations are thread-safe (SQLite serialized mode)
  • Configuration loading is not thread-safe (load once, use everywhere)
  • SSH operations are not thread-safe per connection
  • Sync operations can run concurrently for different repositories

See Also