Pacepard is a reward and engagement software for talents and product teams. We support the development of Open Source Software that solves problems faced daily by Africans, and we are creating points of entry into machine learning research.
By using Pacepard, African talents, organisations, and EdTech providers can collaborate, track talent skill mastery progress, and host competitions while leveraging our AI-powered engagement analytics.
- Prerequisites
- Getting Started
- Project Structure
- Development
- Adding Dependencies
- Building
- Docker Deployment
- Scripts Reference
- Contributing
Before you begin, ensure you have the following installed:
- Node.js >= 20.x (Download)
- pnpm >= 9.0.0 (Installation Guide)
- Git (Download)
- Docker (optional, for containerized deployments) (Download)
To verify your installations:
node --version # Should be >= 20
pnpm --version # Should be >= 9.0.0
git --version
docker --version # Optional, for Docker deploymentsgit clone https://github.com/pacepard/pacepard.git
cd pacepardInstall all dependencies for the monorepo:
pnpm installThis will install dependencies for all apps and packages in the workspace.
This is a monorepo managed by Turborepo and pnpm workspaces. The project is organized as follows:
pacepard/
├── apps/ # Applications
│ ├── web/ # Next.js web application (@pacepard/web)
│ │ └── Dockerfile
│ ├── api/ # Express API server (@pacepard/api)
│ │ └── Dockerfile
│ ├── app/ # Main application (@pacepard/app)
│ ├── main/ # Main app entry point (@pacepard/app)
│ │ └── Dockerfile
│ ├── demo/ # Demo application (@pacepard/demo)
│ ├── service/ # Service application (@pacepard/service)
│ │ └── Dockerfile
│ └── docs/ # Documentation site (@pacepard/docs)
├── packages/ # Shared packages
│ ├── ui/ # UI component library (@pacepard/ui)
│ ├── tiptap/ # Tiptap editor package (@pacepard/tiptap)
│ └── sdk/ # SDK package (@pacepard/sdk)
├── configs/ # Shared configurations
│ ├── eslint/ # ESLint configuration (@pacepard/configs/eslint)
│ └── typescript/ # TypeScript configuration (@pacepard/configs/typescript)
├── scripts/ # Utility scripts
├── docs/ # Documentation
│ ├── docker-setup.md
│ ├── coolify-monorepo-setup.md
│ └── github-actions-implementation.md
├── .dockerignore # Docker ignore file
├── package.json # Root package.json
├── pnpm-workspace.yaml # pnpm workspace configuration
├── turbo.json # Turborepo configuration
└── tsconfig.json # Root TypeScript configuration
All packages use the @pacepard/* namespace:
-
Apps:
@pacepard/web- Next.js web application (runs on port 3020)@pacepard/api- Express API server@pacepard/app- Main application (runs on port 5196)@pacepard/demo- Demo application showcasing Tiptap editors (runs on port 5186)@pacepard/service- Service application@pacepard/docs- Documentation site@pacepard/api-docs- API documentation
-
Packages:
@pacepard/ui- Shared UI component library (shadcn/ui based)@pacepard/tiptap- Tiptap editor functionality@pacepard/sdk- SDK utilities and shared logic
-
Configs:
@pacepard/configs/eslint- Shared ESLint configuration@pacepard/configs/typescript- Shared TypeScript configuration
Start all applications in development mode
For a single terminal view of builds and tasks:
pnpm devOR
For a visual interface with Turbo UI to monitor builds and tasks (best option):
pnpm dev:uiYou can run specific applications or groups:
# Frontend applications (web + app)
pnpm dev:fe
# Backend applications (api + api-docs)
pnpm dev:be
# Individual applications
pnpm dev:web # Next.js web app
pnpm dev:api # Express API server
pnpm dev:app # Main application
pnpm dev:service # Service application
pnpm dev:docs # API documentation- Main App: http://localhost:5176
- Website App: http://localhost:3020
- Services App: http://localhost:3015
- API: http://localhost:5015
- API Docs: http://localhost:3010
To add a dependency to a specific workspace package:
# Add to a specific app/package
pnpm add <package-name> --filter @pacepard/web
pnpm add <package-name> --filter @pacepard/api
pnpm add <package-name> --filter @pacepard/ui
pnpm add <package-name> --filter @pacepard/sdkpnpm add -D <package-name> --filter @pacepard/webTo add a dependency at the root level (shared across all packages):
pnpm add <package-name> -wYou can add to multiple packages at once:
pnpm add <package-name> --filter @pacepard/web --filter @pacepard/appTo use a workspace package in another package, reference it in package.json:
{
"dependencies": {
"@pacepard/ui": "workspace:*",
"@pacepard/sdk": "workspace:*"
}
}The workspace:* protocol tells pnpm to use the local workspace version.
This section provides a comprehensive guide for building the monorepo locally. The build process uses Turborepo to orchestrate builds across all packages and applications, ensuring proper dependency resolution and optimal build order.
Build all apps and packages:
pnpm buildThis command uses Turborepo to build all packages in the correct order based on dependencies. Turborepo automatically:
- Resolves workspace dependencies (packages must build before apps that depend on them)
- Caches build outputs for faster subsequent builds
- Runs builds in parallel where possible
- Handles dependency graph resolution
Follow these steps for a complete local build of the monorepo:
Ensure you have the required tools installed:
node --version # Should be >= 20
pnpm --version # Should be >= 9.0.0From the monorepo root, install all dependencies:
pnpm installThis installs dependencies for:
- Root workspace dependencies
- All apps (
apps/*) - All packages (
packages/*) - All configs (
configs/*)
Note: The workspace uses pnpm's hoisting strategy, so shared dependencies are installed at the root level.
Turborepo automatically handles build order based on the dependsOn: ["^build"] configuration in turbo.json. This ensures:
-
Config packages build first (no dependencies):
@pacepard/configs/typescript- TypeScript configurations@pacepard/configs/eslint- ESLint configurations
-
Shared packages build next (depend on configs):
@pacepard/tiptap- Tiptap editor functionality (no build script, TypeScript source only)@pacepard/ui- UI component library (no build script, TypeScript source only)@pacepard/sdk- SDK utilities (no build script, TypeScript source only)
-
Applications build last (depend on packages):
@pacepard/web- Next.js web application@pacepard/api- Express API server@pacepard/app- Vite/React SPA (main app)@pacepard/service- Next.js service application
If you want to build packages explicitly before applications:
# Build all packages
pnpm build --filter './packages/*'
# Build all configs
pnpm build --filter './configs/*'
# Build both packages and configs
pnpm build --filter './packages/*' --filter './configs/*'Note: Most packages (@pacepard/ui, @pacepard/sdk, @pacepard/tiptap) don't have build scripts as they're TypeScript source files consumed directly. However, if you need to verify TypeScript compilation, you can type-check them.
Build a specific application and its dependencies:
# Build web application (Next.js)
pnpm build --filter @pacepard/web
# Build API server (Express)
pnpm build --filter @pacepard/api
# Build main app (Vite/React)
pnpm build --filter @pacepard/app
# Build service application (Next.js)
pnpm build --filter @pacepard/serviceNote: Using --filter automatically builds all dependencies first, so you don't need to manually build packages.
Build all applications in the monorepo:
pnpm buildThis command:
- Builds all packages and configs that have build scripts
- Builds all applications
- Uses Turborepo caching for faster rebuilds
- Respects dependency order automatically
Each package and application has specific build configurations. Here's what happens when you build each:
- Package:
apps/web/package.json - Build Script:
next build - Output:
.next/directory (Next.js production build) - Dependencies:
@pacepard/ui,@pacepard/tiptap - Build Details:
- Compiles Next.js application
- Generates optimized production bundle
- Outputs static pages where applicable
- Creates server-side rendering artifacts
- Package:
apps/api/package.json - Build Script:
tsc --noEmit false && tsc-alias && copyfiles -u 1 src/_data/**/* dist/ && copyfiles -u 1 src/views/**/* dist/ - Output:
dist/directory (compiled JavaScript) - Dependencies:
@pacepard/configs/typescript - Build Details:
- Compiles TypeScript to JavaScript (
tsc) - Resolves TypeScript path aliases (
tsc-alias) - Copies static files from
src/_data/todist/ - Copies template files from
src/views/todist/
- Environment variable
PORTaffects build (configured inturbo.json)
- Compiles TypeScript to JavaScript (
- Package:
apps/main/package.json - Build Script:
tsc -b && vite build - Output:
dist/directory (optimized production build) - Dependencies:
@pacepard/ui,@pacepard/sdk,@pacepard/tiptap,@pacepard/configs/typescript - Build Details:
- Type-checks and compiles TypeScript (
tsc -b) - Builds production bundle with Vite
- Generates optimized static assets
- Uses Vite for fast builds and code splitting
- Type-checks and compiles TypeScript (
- Package:
apps/service/package.json - Build Script:
next build - Output:
.next/directory (Next.js production build) - Dependencies:
@pacepard/ui,@pacepard/configs/eslint,@pacepard/configs/typescript - Build Details:
- Compiles Next.js application
- Generates optimized production bundle
- Similar to
@pacepard/webbuild process
These packages don't have build scripts as they're TypeScript source files consumed directly:
- Package:
apps/packages/ui/package.json - Build Script: None (TypeScript source consumed directly)
- Entry Point:
./src/index.ts - Usage: Imported as
workspace:*dependency in applications - Note: TypeScript compiles these files during application builds
- Package:
apps/packages/sdk/package.json - Build Script: None (TypeScript source consumed directly)
- Entry Point:
./src/index.ts - Usage: Imported as
workspace:*dependency in applications
- Package:
packages/tiptap/package.json - Build Script: None (TypeScript source consumed directly)
- Entry Point:
src/index.ts - Usage: Imported as
workspace:*dependency in applications
To add new Tiptap components, extensions, or utilities to the @pacepard/tiptap package, use the Tiptap CLI:
# From the monorepo root
cd packages/tiptap
# Add a new Tiptap resource (example: heading-button)
pnpm dlx @tiptap/cli@latest add heading-button
# Or add multiple resources at once
pnpm dlx @tiptap/cli@latest add heading-button list-button mark-buttonThe CLI will automatically:
- Install necessary dependencies
- Create component files in the appropriate directories
- Update exports if needed
Note: After adding resources, ensure they are properly exported from packages/tiptap/src/index.ts if they should be available to consuming applications.
- Package:
configs/typescript/package.json - Build Script: None (JSON configuration files)
- Usage: Extended via
extendsintsconfig.jsonfiles
- Package:
configs/eslint/package.json - Build Script: None (JavaScript configuration files)
- Usage: Imported in ESLint config files
After building, outputs are located in:
pacepard/
├── apps/
│ ├── web/
│ │ └── .next/ # Next.js build output
│ ├── api/
│ │ └── dist/ # Compiled JavaScript
│ ├── main/
│ │ └── dist/ # Vite build output
│ └── service/
│ └── .next/ # Next.js build output
└── packages/ # No build outputs (source TypeScript)
The build process is configured in turbo.json:
{
"build": {
"dependsOn": ["^build"],
"inputs": ["$TURBO_DEFAULT$", ".env*"],
"outputs": [".next/**", "dist/**"]
}
}dependsOn: ["^build"]: Ensures workspace dependencies build firstinputs: Files that trigger rebuilds (includes.env*files)outputs: Build output directories to cache
After building, verify the builds:
# Check if build outputs exist
ls apps/web/.next # Should exist for web
ls apps/api/dist # Should exist for API
ls apps/main/dist # Should exist for main app
ls apps/service/.next # Should exist for service
# Run production builds locally (if supported)
cd apps/web && pnpm start
cd apps/api && pnpm startSolution: Ensure packages are built or are available as workspace dependencies:
pnpm install # Reinstall dependencies
pnpm build --filter './packages/*' # Build packages explicitlySolution: Type-check before building:
pnpm check-typesSolution: Clear Turborepo cache:
pnpm build --force
# Or
turbo run build --forceSolution: Ensure .env files are present:
# Check for .env files in application directories
ls apps/api/.env*
ls apps/web/.env*Solution: The command is available via pnpm workspace hoisting. If issues persist:
# Ensure root has the dependency
pnpm add -D tsc-alias copyfiles -wSolution: Verify workspace protocol in package.json:
{
"dependencies": {
"@pacepard/ui": "workspace:*",
"@pacepard/sdk": "workspace:*"
}
}pnpm build- Build all packages and applicationspnpm build --filter <package>- Build specific package/app and dependenciespnpm build --filter './packages/*'- Build all packagespnpm check-types- Type-check all packages (doesn't emit files)
pnpm build --filter @pacepard/web- Build web applicationpnpm build --filter @pacepard/api- Build API serverpnpm build --filter @pacepard/app- Build main applicationpnpm build --filter @pacepard/service- Build service application
Build documentation site:
pnpm build:docsThe monorepo includes Dockerfiles for each application, optimized for production deployment.
Each application has its own Dockerfile:
- API (
apps/api/Dockerfile) - Express server - Web (
apps/web/Dockerfile) - Next.js application - App (
apps/main/Dockerfile) - Vite/React SPA (served with nginx) - Service (
apps/service/Dockerfile) - Next.js application
All Dockerfiles use multi-stage builds for optimized production images.
- Docker installed and running (Docker Desktop or Docker Engine)
- Build context must be the monorepo root directory
From the monorepo root, build images for each application:
# API
docker build -f apps/api/Dockerfile -t pacepard-api:latest .
# Web
docker build -f apps/web/Dockerfile -t pacepard-web:latest .
# App (Main)
docker build -f apps/main/Dockerfile -t pacepard-app:latest .
# Service
docker build -f apps/service/Dockerfile -t pacepard-service:latest .# API (default port 3000)
docker run -p 3000:3000 --env-file .env.production pacepard-api:latest
# Web (default port 3000)
docker run -p 3000:3000 --env-file .env.production pacepard-web:latest
# App (nginx on port 80)
docker run -p 8080:80 pacepard-app:latest
# Service (default port 3000)
docker run -p 3000:3000 --env-file .env.production pacepard-service:latestDefault ports (configurable via PORT environment variable):
- API: 3000
- Web: 3000
- App: 80 (nginx)
- Service: 3000
The Dockerfiles handle the monorepo structure by:
- Building from root context: All Dockerfiles expect the build context to be the monorepo root
- Handling workspace dependencies: Builds workspace packages (
@pacepard/ui,@pacepard/sdk, etc.) before applications - Multi-stage builds: Optimizes final image size by separating build and runtime dependencies
Build order:
# 1. Install all dependencies
pnpm install --frozen-lockfile
# 2. Build workspace packages first
pnpm build --filter './packages/*' --filter './configs/*'
# 3. Build the application
pnpm build --filter @pacepard/api # (or web, app, service)For Coolify deployments:
- Application Type: Select "Dockerfile"
- Build Context: Set to monorepo root (
.) - Dockerfile Path:
- API:
apps/api/Dockerfile - Web:
apps/web/Dockerfile - App:
apps/main/Dockerfile - Service:
apps/service/Dockerfile
- API:
- Root Directory:
- API:
apps/api - Web:
apps/web - App:
apps/main - Service:
apps/service
- API:
Each application requires specific environment variables. Set these in your Docker deployment:
PORT=3000
NODE_ENV=production
DATABASE_URL=<your-database-url>
REDIS_URL=<your-redis-url>
# ... other API-specific variablesPORT=3000
NODE_ENV=production
# Next.js specific environment variablesNo Node.js environment variables needed (static files served via nginx).
- Ensure build context is the monorepo root
- Verify workspace dependencies are built before the app
- Check that
pnpm-workspace.yamlis correct
- These tools are available via pnpm workspace hoisting
- If issues persist, ensure they're installed at root:
pnpm add -D tsc-alias copyfiles -w
- Ensure packages are built:
pnpm build --filter './packages/*' - Verify workspace protocol in package.json:
"@pacepard/ui": "workspace:*"
pacepard/
├── .dockerignore # Files excluded from Docker builds
├── apps/
│ ├── api/
│ │ └── Dockerfile # API Dockerfile
│ ├── web/
│ │ └── Dockerfile # Web Dockerfile
│ ├── main/
│ │ └── Dockerfile # App Dockerfile
│ └── service/
│ └── Dockerfile # Service Dockerfile
└── docs/
└── docker-setup.md # Detailed Docker documentation
For detailed Docker setup and advanced configuration, see:
- Docker Setup Guide - Comprehensive Docker documentation
- Coolify Monorepo Setup - Coolify-specific deployment guide
- GitHub Actions Implementation - CI/CD with Docker
| Script | Description |
|---|---|
pnpm dev |
Start all applications in development mode |
pnpm dev:fe |
Start frontend applications (web + app) |
pnpm dev:be |
Start backend applications (api + api-docs) |
pnpm dev:web |
Start web application only |
pnpm dev:api |
Start API server only |
pnpm dev:app |
Start main app only |
pnpm dev:service |
Start service app only |
pnpm dev:docs |
Start docs app only |
pnpm dev:ui |
Start with Turborepo UI |
pnpm build |
Build all packages |
pnpm build:docs |
Build documentation |
pnpm test |
Run tests across all packages |
pnpm lint |
Lint all packages |
pnpm format |
Format code with Prettier |
pnpm check-types |
Type-check all packages |
pnpm clean |
Clean build artifacts |
pnpm clean:modules |
Remove all node_modules |
pnpm clean:all |
Remove node_modules and pnpm-lock.yaml |
Each app/package may have its own scripts. Check individual package.json files for details.
We welcome contributions! Here's how you can help:
# Fork the repository on GitHub (https://github.com/pacepard/pacepard), then:
git clone https://github.com/your-username/pacepard.git
cd pacepardUse the format @username/feature-your-task for branch names:
git checkout -b @username/feature-your-task
# or for bug fixes
git checkout -b @username/fix-your-bug-fixpnpm install- Write clean, maintainable code
- Follow the existing code style
- Add tests if applicable
- Update documentation as needed
Before committing, ensure everything passes:
# Type checking
pnpm check-types
# Linting
pnpm lint
# Format code
pnpm format
# Build (optional, but recommended)
pnpm buildWe use Changesets for version management. For significant changes, create a changeset:
pnpm changesetFollow conventional commit messages:
feat: add new feature
fix: fix bug
docs: update documentation
style: formatting changes
refactor: code refactoring
test: add tests
chore: maintenance tasks
Most teams follow Conventional Commits.
| Type | Description |
|---|---|
feat |
New feature |
fix |
Bug fix |
chore |
Maintenance, configs, dependencies |
refactor |
Code change that does not add features or fix bugs |
docs |
Documentation only |
test |
Tests only |
style |
Formatting, no logic change |
perf |
Performance improvement |
ci |
CI or pipeline changes |
build |
Build system or dependencies |
Before pushing or merging your feature, make sure your branch is up to date:
git fetch origin
git rebase origin/staginggit push origin @username/feature-your-taskCreate a Pull Request on GitHub with the following guidelines:
- Target Branch: Your PR should target
staging— notmaster - Reference Issues: Include the issue number in the PR description (e.g.,
Closes #502) - Add Context: Provide context and screenshots/logs when helpful
- Request Reviewers: Request reviewers before merging
Once your PR is approved:
git checkout staging
git merge @username/feature-your-task-name
git push origin stagingWhen ready for deployment, create a release branch from staging:
git checkout staging
git checkout -b release/v1.0.2
git push origin release/v1.0.2Final QA and bug-fixing happen on this release/* branch before production deployment.
After final QA on the release branch, merge it into both master and staging:
# Merge into master
git checkout master
git merge release/v1.0.2
git push origin master
# Merge back into staging to ensure it stays updated
git checkout staging
git merge release/v1.0.2
git push origin stagingIf you discover a bug or have a suggestion, raise an issue via the GitHub Issues tab (if you have permission), or notify your team lead for triage and assignment.
- Code Style: Follow the ESLint and Prettier configurations
- TypeScript: All code should be properly typed
- Testing: Add tests for new features when possible
- Documentation: Update README and code comments as needed
- Workspace Packages: Use workspace protocol (
workspace:*) for internal dependencies
- UI Components: Add new components to
@pacepard/uipackage - Shared Logic: Put shared utilities in
@pacepard/sdk - API Changes: Update API documentation in
apps/docs - Environment Variables: Use
.envfiles (they're gitignored)
- PRs should target the
stagingbranch (notmaster) - Reference issues using
Closes #issue-numberin the PR description - Add context and screenshots/logs when helpful
- Request reviewers before merging
- Docker Setup Guide - Comprehensive Docker deployment guide
- Coolify Monorepo Setup - Coolify deployment instructions
- GitHub Actions Implementation - CI/CD pipeline documentation
- Workflow Documentation - Development workflow and best practices
- Turborepo Documentation
- pnpm Workspaces
- Changesets Documentation
- Next.js Documentation
- TypeScript Documentation
- Docker Documentation
This project is licensed under the MIT License - see the LICENSE file for details.