Repository navigation
Development Guide
This guide covers everything you need to know to contribute to Flowcase development, including setup, architecture, coding standards, and contribution workflow.
Before you begin developing with Flowcase, ensure you have:
- Git: Version control system
- Docker: 20.0+ with Docker Compose
- Code Editor: VS Code, PyCharm, or your preferred editor
- Basic Knowledge: Flask, Docker, SQLAlchemy, JavaScript/HTML/CSS
[!INFO] Development Approach: Flowcase development primarily uses Docker containers.
# Fork the repository on GitHub first, then:
git clone https://github.com/your-username/flowcase.git
cd flowcase
# Add upstream remote
git remote add upstream https://github.com/flowcase/flowcase.git# Start development environment
docker compose -f docker-compose.dev.yml up -d
# View logs
docker compose -f docker-compose.dev.yml logs -fflowcase/
├── __init__.py # Flask application factory
├── run.py # Application entry point
├── config/
│ └── config.py # Configuration management
├── models/ # Database models
│ ├── __init__.py
│ ├── user.py # User and Group models
│ ├── droplet.py # Droplet and Instance models
│ ├── log.py # Logging model
│ └── registry.py # Registry model
├── routes/ # Flask blueprints/routes
│ ├── __init__.py
│ ├── auth.py # Authentication routes
│ ├── admin.py # Admin panel routes
│ └── droplet.py # Droplet management routes
├── utils/ # Utility functions
│ ├── __init__.py
│ ├── docker.py # Docker integration
│ ├── logger.py # Logging utilities
│ ├── permissions.py # Permission checking
│ └── setup.py # Initial setup
├── templates/ # Jinja2 templates
│ ├── 404.html
│ ├── dashboard.html
│ ├── droplet.html
│ └── login.html
├── static/ # Static assets
│ ├── css/ # Stylesheets
│ ├── js/ # JavaScript files
│ └── img/ # Images
├── docker-compose.yml # Production compose
├── docker-compose.dev.yml # Development compose
├── web.Dockerfile # Web container definition
├── gunicorn.conf.py # WSGI server config
└── requirements.txt # Python dependencies
- Application factory pattern
- Blueprint registration
- Database initialization
- Error handling
- SQLAlchemy ORM models
- Database schema definitions
- Model relationships
- Flask blueprints for different features
- API endpoints
- Request handling logic
- Docker integration
- Permission management
- System utilities
# Update main branch
git checkout main
git pull upstream main
# Create feature branch
git checkout -b feature/your-feature-name- Make changes to the codebase
- Test locally using development environment
- Write/update tests (when test framework is available)
- Update documentation if needed
- Commit changes with clear messages
Important
Testing Requirement: Until automated testing is implemented, thorough manual testing is essential. Test all affected functionality and edge cases before submitting pull requests.
# Restart development environment
docker compose -f docker-compose.dev.yml restart
# Check logs for errors
docker compose -f docker-compose.dev.yml logs -f web
# Test manually through web interface
# Access http://localhost:5000Tip
Manual Testing Checklist: Test authentication, admin panel functionality, droplet operations, and error handling scenarios. Document your testing process in pull request descriptions.
Until automated testing is implemented, follow these manual testing procedures:
-
Authentication:
- Login with admin/user accounts
- Logout functionality
- Session persistence
-
Admin Panel:
- User management (create, edit, delete)
- Group management
- Droplet management
- System information display
-
Droplet Operations:
- Instance creation
- Connection to instances
- Instance destruction
- File upload/download
-
Error Handling:
- Invalid credentials
- Non-existent resources
- Network failures
Test across different browsers:
- Chrome/Chromium
- Firefox
- Safari
- Edge
Planned testing framework:
# tests/test_auth.py (example)
import unittest
from __init__ import create_app, db
class AuthTestCase(unittest.TestCase):
def setUp(self):
self.app = create_app({'TESTING': True})
self.client = self.app.test_client()
def test_login(self):
# Test login functionality
pass
def test_logout(self):
# Test logout functionality
passWarning
Testing Status: Automated testing framework is not yet implemented. Currently, all testing is manual. Setting up automated testing is a high-priority development goal for improving code quality and reliability.
[!HELP_WANTED] Contribution Opportunity: Implementing automated testing (unit tests, integration tests, end-to-end tests) is a great way to contribute to the project and would be highly valuable.
Follow PEP 8 with these specific guidelines:
# Standard library imports
import os
import sys
import uuid
# Third-party imports
from flask import Flask, request, jsonify
from sqlalchemy import Column, String, DateTime
# Local imports
from __init__ import db
from models.user import User
from utils.permissions import Permissions# Functions: snake_case
def create_user(username, password):
pass
# Classes: PascalCase
class DropletInstance:
pass
# Constants: UPPER_SNAKE_CASE
DEFAULT_MEMORY_LIMIT = 1024def create_droplet_instance(droplet_id, user_id, resolution=None):
"""Create a new droplet instance for a user.
Args:
droplet_id (str): UUID of the droplet to instantiate
user_id (str): UUID of the user creating the instance
resolution (str, optional): Screen resolution (e.g., "1920x1080")
Returns:
DropletInstance: The created instance object
Raises:
ValueError: If droplet or user doesn't exist
ResourceError: If insufficient resources available
"""
pass// Use const/let instead of var
const apiUrl = '/api/droplets';
let currentUser = null;
// Arrow functions for callbacks
droplets.forEach(droplet => {
console.log(droplet.display_name);
});
// Async/await for promises
async function getDroplets() {
try {
const response = await fetch('/api/droplets');
return await response.json();
} catch (error) {
console.error('Failed to fetch droplets:', error);
}
}// Always handle errors gracefully
function createInstance(dropletId) {
fetch('/api/instance/request', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({droplet_id: dropletId})
})
.then(response => {
if (!response.ok) {
throw new Error('Failed to create instance');
}
return response.json();
})
.then(data => {
if (data.success) {
window.location.href = data.connect_url;
} else {
showNotification(data.error, 'error');
}
})
.catch(error => {
console.error('Error:', error);
showNotification('Network error occurred', 'error');
});
}<!-- Use semantic HTML elements -->
<nav class="navigation">
<h1>Flowcase</h1>
<div class="nav-links">
<a href="#" class="user-link">
<i class="fas fa-user"></i> Username
</a>
</div>
</nav>
<main class="content">
<section class="droplets">
<article class="droplet">
<h2>Droplet Name</h2>
<p>Description</p>
</article>
</section>
</main>/* Use consistent naming and organization */
.nav-links {
display: flex;
align-items: center;
gap: 1rem;
}
.nav-links a {
color: var(--text-color);
text-decoration: none;
transition: color 0.2s ease;
}
.nav-links a:hover {
color: var(--accent-color);
}# models/new_model.py
import uuid
from sqlalchemy.sql import func
from __init__ import db
class NewModel(db.Model):
id = db.Column(db.String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
name = db.Column(db.String(80), nullable=False)
created_at = db.Column(db.DateTime, server_default=func.now())
def to_dict(self):
"""Convert model to dictionary for JSON serialization."""
return {
'id': self.id,
'name': self.name,
'created_at': self.created_at.isoformat() if self.created_at else None
}# For manual migration during development
from __init__ import create_app, db
app = create_app()
with app.app_context():
# Create new tables
db.create_all()
# Or drop and recreate (DEVELOPMENT ONLY)
db.drop_all()
db.create_all()# routes/new_feature.py
from flask import Blueprint, request, jsonify
from flask_login import login_required, current_user
from models.new_model import NewModel
from __init__ import db
new_bp = Blueprint('new_feature', __name__)
@new_bp.route('/api/new-resource', methods=['GET'])
@login_required
def get_resources():
"""Get all resources for current user."""
try:
resources = NewModel.query.all()
return jsonify({
'success': True,
'resources': [r.to_dict() for r in resources]
})
except Exception as e:
return jsonify({
'success': False,
'error': str(e)
}), 500
@new_bp.route('/api/new-resource', methods=['POST'])
@login_required
def create_resource():
"""Create a new resource."""
try:
data = request.get_json()
# Validate input
if not data or 'name' not in data:
return jsonify({
'success': False,
'error': 'Name is required'
}), 400
# Create resource
resource = NewModel(name=data['name'])
db.session.add(resource)
db.session.commit()
return jsonify({
'success': True,
'resource': resource.to_dict()
}), 201
except Exception as e:
db.session.rollback()
return jsonify({
'success': False,
'error': str(e)
}), 500# examples/custom-droplet.Dockerfile
FROM ubuntu:20.04
# Install desktop environment and VNC
RUN apt-get update && apt-get install -y \
ubuntu-desktop-minimal \
tigervnc-standalone-server \
tigervnc-xorg-extension \
supervisor \
&& rm -rf /var/lib/apt/lists/*
# Install your custom application
RUN apt-get update && apt-get install -y \
your-custom-app \
&& rm -rf /var/lib/apt/lists/*
# Configure VNC and supervisor
COPY supervisord.conf /etc/supervisor/conf.d/
COPY startup.sh /usr/local/bin/
EXPOSE 5901
CMD ["supervisord", "-c", "/etc/supervisor/supervisord.conf"]# Build custom image
docker build -f custom-droplet.Dockerfile -t my-custom-droplet .
# Test locally
docker run -d -p 5901:5901 my-custom-droplet
# Add to Flowcase as droplet
# Use image name: my-custom-droplet# Enable debug mode in development
app.config['DEBUG'] = True
# Add debug prints
import logging
logging.basicConfig(level=logging.DEBUG)
def debug_function():
logging.debug(f"Variable value: {some_variable}")# Enable SQL query logging
app.config['SQLALCHEMY_ECHO'] = True
# Manual database inspection
from __init__ import create_app, db
app = create_app()
with app.app_context():
result = db.engine.execute("SELECT * FROM user")
for row in result:
print(row)// Add debug logging
console.log('Debug info:', data);
console.error('Error occurred:', error);
// Inspect variables
debugger; // Breakpoint for browser debugger
// Network debugging
fetch('/api/endpoint')
.then(response => {
console.log('Response status:', response.status);
console.log('Response headers:', response.headers);
return response.json();
});# List all containers
docker ps -a
# Access container shell
docker exec -it flowcase-web bash
docker exec -it flowcase_generated_xxx bash
# View container logs
docker logs flowcase-web
docker logs -f flowcase_generated_xxx
# Inspect container configuration
docker inspect flowcase-web# Build development image
docker build -f web.Dockerfile -t flowcase:dev .
# Test with development compose
docker compose -f docker-compose.dev.yml up --build# Build production image
docker build -f web.Dockerfile -t flowcase:latest .
# Test production configuration
docker compose up --build
# Verify functionality
curl -I http://localhost:80Warning
Production Testing: Since automated testing is not yet available, production builds require extensive manual testing. Test all critical functionality before release.
type(scope): short description
Longer description explaining what and why vs. how.
Can include multiple paragraphs.
Fixes #123
Refs #456
Types:
-
feat: New feature -
fix: Bug fix -
docs: Documentation changes -
style: Code style changes (formatting) -
refactor: Code refactoring -
test: Adding/updating tests -
chore: Maintenance tasks
[!INFO] Testing Commits: Use the
testtype when contributing to the testing framework setup or adding test cases for future implementation.
- Create feature branch from main
- Make your changes following coding standards
- Test thoroughly using development environment
- Update documentation if needed
- Commit with clear messages
- Push to your fork
-
Create pull request with:
- Clear description of changes
- Screenshots for UI changes
- Testing instructions
- Links to relevant issues
- Keep PRs focused and reasonably sized
- Include tests when possible
- Document complex logic
- Respond promptly to review feedback
- Check functionality and code quality
- Verify security implications
- Test manually when needed
- Provide constructive feedback
# Update version in __init__.py
__version__ = "1.2.0"
# Tag release
git tag -a v1.2.0 -m "Release version 1.2.0"
git push upstream v1.2.0- Update version number
- Update CHANGELOG.md
- Test installation from scratch
- Verify all documentation is current
- Create release notes
- Tag and push release
- IDEs: VS Code, PyCharm, Sublime Text
- Database: SQLite Browser, DBeaver
- API Testing: Postman, Insomnia, curl
- Docker: Docker Desktop, Portainer
- GitHub Discussions
- Issues Tracker
- Project Discord/IRC (if available)
- Fork and clone repository
- Set up development environment
- Run development server
- Make a small test change
- Read through existing code
- Join community discussions
- Find an issue to work on
- Submit your first PR
Welcome to the Flowcase development community! 🎉