Development
Guide for contributing to Grithub development.
Prerequisites
Required
- Node.js - v20.0.0 or higher
- pnpm - v8.0.0 or higher (package manager)
- Git - For version control
- GitHub Account - For testing OAuth
Optional
- TypeScript - Knowledge helpful but not required
- SQLite - Understanding of database concepts
- GitHub API - Familiarity with REST APIs
Getting Started
Clone Repository
bash
git clone https://github.com/toneflix/grithub.git
cd grithubInstall Dependencies
bash
pnpm installThis installs:
- Runtime dependencies
- Development tools
- Type definitions
- Build tools
Project Structure
md
grithub/
├── src/ # Source code
│ ├── Commands/ # Command implementations
│ ├── Contracts/ # TypeScript interfaces
│ ├── github/ # GitHub API utilities
│ ├── utils/ # Helper functions
│ ├── cli.ts # CLI entry point
│ ├── config.ts # Configuration
│ └── db.ts # Database operations
│
├── tests/ # Test files
├── docs/ # VitePress documentation
├── bin/ # Compiled executables
├── build/ # Build output
│
├── package.json # Package configuration
├── tsconfig.json # TypeScript config
├── tsdown.config.ts # Build configuration
└── vitest.config.ts # Test configurationRun in Development
bash
pnpm runner <command>Examples:
bash
pnpm runner login
pnpm runner issues
pnpm runner generate:apisBuild
Compile TypeScript to JavaScript:
bash
pnpm buildOutput:
bin/cli.js- ESM bundlebin/cli.cjs- CommonJS bundle
Run Tests
bash
# Run all tests
pnpm test
# Run specific test
pnpm vitest tests/helpers.spec.ts
# Watch mode
pnpm vitest --watchLint Code
bash
pnpm lintDevelopment Workflow
1. Create Feature Branch
bash
git checkout -b feature/amazing-feature2. Make Changes
Edit files in src/:
typescript
// src/Commands/MyCommand.ts
import { Command } from '@h3ravel/musket';
export class MyCommand extends Command {
protected signature = 'my-command';
protected description = 'Does something amazing';
async handle() {
this.info('Hello from my command!');
}
}3. Test Changes
bash
# Test your command
pnpm runner my-command
# Run tests
pnpm test4. Build
bash
pnpm build5. Commit
bash
git add .
git commit -m "feat: add amazing feature"6. Push
bash
git push origin feature/amazing-feature7. Create Pull Request
Open PR on GitHub with:
- Clear description
- Screenshots (if UI changes)
- Test results
- Breaking changes noted
Adding Commands
Create Command File
typescript
// src/Commands/ExampleCommand.ts
import { Command } from '@h3ravel/musket';
import { useCommand } from '../hooks';
export class ExampleCommand extends Command {
protected signature = `example
{ name : User name }
{--g|greeting= : Greeting message }
`;
protected description = 'Example command';
async handle() {
const [_, setCommand] = useCommand();
setCommand(this);
const name = this.argument('name');
const greeting = this.option('greeting', 'Hello');
this.info(`${greeting}, ${name}!`).newLine();
}
}Register Command
typescript
// src/cli.ts
import { ExampleCommand } from './Commands/ExampleCommand';
Kernel.init(new Application(), {
baseCommands: [
// ... existing commands
ExampleCommand,
],
});Test Command
bash
pnpm runner example John --greeting="Hi"
# Output: Hi, John!Working with Database
Database Location
md
~/.grithub/app.dbDatabase Operations
typescript
import { read, write, init } from '../db';
// Initialize database
init();
// Write data
write('key', { value: 'data' });
// Read data
const data = read('key');
// Read with type
const config = read<IConfig>('config');Available Tables
store- Key-value storagetoken- Authentication tokenuser- User profiledefault_repo- Default repositoryconfig- User preferences
Working with GitHub API
Using Octokit
typescript
import { useOctokit } from '../hooks';
const octokit = useOctokit();
// List issues
const issues = await octokit.rest.issues.listForRepo({
owner: 'toneflix',
repo: 'grithub',
state: 'open',
});
// Create issue
await octokit.rest.issues.create({
owner: 'toneflix',
repo: 'grithub',
title: 'New issue',
body: 'Issue description',
});Error Handling
typescript
import { promiseWrapper } from '../helpers';
const [err, result] = await promiseWrapper(
octokit.rest.issues.create({
owner: 'owner',
repo: 'repo',
title: 'Issue',
}),
);
if (err) {
this.error('Failed to create issue: ' + err.message);
return;
}
this.success('Issue created: #' + result.data.number);TypeScript Guidelines
Interfaces
Define in src/Contracts/:
typescript
// src/Contracts/Interfaces.ts
export interface IIssue {
number: number;
title: string;
body: string | null;
state: 'open' | 'closed';
labels: ILabel[];
assignees: IUser[];
}Type Safety
Use strict types:
typescript
// Good
function getIssue(number: number): Promise<IIssue> {
// ...
}
// Avoid
function getIssue(number: any): any {
// ...
}Generics
typescript
function processData<T>(data: T): T {
// Process and return same type
return data;
}
const issue = processData<IIssue>(issueData);Testing
Unit Tests
typescript
// tests/helpers.spec.ts
import { describe, it, expect } from 'vitest';
import { myHelper } from '../src/helpers';
describe('myHelper', () => {
it('should do something', () => {
const result = myHelper('input');
expect(result).toBe('expected');
});
it('should handle errors', () => {
expect(() => myHelper(null)).toThrow();
});
});Run Specific Tests
bash
# Single file
pnpm vitest tests/helpers.spec.ts
# Pattern
pnpm vitest tests/**/*.spec.ts
# With coverage
pnpm vitest --coverageDocumentation
VitePress
Documentation lives in docs/:
bash
# Start dev server
pnpm docs:dev
# Build docs
pnpm docs:build
# Preview build
pnpm docs:previewWriting Docs
Create markdown files:
markdown
# My Feature
Description of the feature.
## Usage
\`\`\`bash
grithub my-command
\`\`\`
## Options
- `--option` - DescriptionUpdate sidebar:
typescript
// docs/.vitepress/config.ts
sidebar: {
'/guide/': [
{
text: 'Features',
items: [
{ text: 'My Feature', link: '/guide/my-feature' }
]
}
]
}Debugging
Enable Debug Mode
bash
# In development
export GRITHUB_DEBUG=true
pnpm runner <command>Use Debugger
typescript
// Add breakpoint
debugger;
// Or
console.log('Debug info:', variable);VS Code
Create .vscode/launch.json:
json
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Debug CLI",
"runtimeExecutable": "pnpm",
"runtimeArgs": ["runner", "issues"],
"console": "integratedTerminal"
}
]
}Best Practices
Code Style
- Use TypeScript strict mode
- Follow existing patterns
- Add JSDoc comments for public APIs
- Keep functions small and focused
Error Handling
typescript
// Use promiseWrapper
const [err, result] = await promiseWrapper(asyncOperation());
if (err) {
this.error('Operation failed: ' + err.message);
return;
}User Feedback
typescript
// Use spinners for long operations
const spinner = this.spinner('Processing...').start();
try {
await longOperation();
spinner.succeed('Success!');
} catch (error) {
spinner.fail('Failed!');
}Configuration
typescript
// Read config safely
const [getConfig] = useConfig();
const config = getConfig();
if (!config) {
// Handle missing config
}Common Tasks
Update Dependencies
bash
# Update all
pnpm update
# Update specific
pnpm update @octokit/rest
# Check outdated
pnpm outdatedAdd Dependency
bash
# Production
pnpm add package-name
# Development
pnpm add -D package-nameRelease Process
bash
# Version bump
pnpm version patch # or minor, major
# Build
pnpm build
# Publish
pnpm publishTroubleshooting
Build Fails
bash
# Clean and rebuild
rm -rf build/ bin/
pnpm install
pnpm buildTests Fail
bash
# Clear test cache
pnpm vitest --clearCache
# Run single test
pnpm vitest tests/specific.spec.tsType Errors
bash
# Check types without building
pnpm tsc --noEmitResources
Next Steps
- Contributing - Contribution guidelines
- Commands - Command structure
- API Reference - API documentation
