Files
windmill-git-sync/app/server.py
Bill 0509c44497 Add MIT License and Docker build validation script
- Add MIT License with William Ballou as copyright holder
- Create scripts/validate_docker_build.sh for testing Docker builds independently
- Update documentation to reflect API-based secret configuration model
- Refactor sync.py to accept config via function parameters instead of env vars
- Update server.py to parse JSON payloads and validate required fields
- Improve security by removing secrets from environment variables
2025-11-09 20:29:52 -05:00

98 lines
3.0 KiB
Python

#!/usr/bin/env python3
"""
Flask server for receiving webhook triggers from Windmill to sync workspace to Git.
Internal service - not exposed outside Docker network.
"""
import logging
from flask import Flask, jsonify, request
from sync import sync_windmill_to_git
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
app = Flask(__name__)
@app.route('/health', methods=['GET'])
def health():
"""Health check endpoint."""
return jsonify({'status': 'healthy'}), 200
@app.route('/sync', methods=['POST'])
def trigger_sync():
"""
Trigger a sync from Windmill workspace to Git repository.
This endpoint is only accessible within the Docker network.
Expected JSON payload:
{
"windmill_token": "string (required)",
"git_remote_url": "string (required)",
"git_token": "string (required)",
"workspace": "string (optional, default: 'admins')",
"git_branch": "string (optional, default: 'main')",
"git_user_name": "string (optional, default: 'Windmill Git Sync')",
"git_user_email": "string (optional, default: 'windmill@example.com')"
}
"""
logger.info("Sync triggered via webhook")
# Parse JSON payload
try:
config = request.get_json(force=True)
if not config:
return jsonify({
'success': False,
'message': 'Request body must be valid JSON'
}), 400
except Exception as e:
logger.error(f"Failed to parse JSON payload: {str(e)}")
return jsonify({
'success': False,
'message': f'Invalid JSON payload: {str(e)}'
}), 400
# Validate required fields
required_fields = ['windmill_token', 'git_remote_url', 'git_token']
missing_fields = [field for field in required_fields if not config.get(field)]
if missing_fields:
error_message = f"Missing required fields: {', '.join(missing_fields)}"
logger.error(error_message)
return jsonify({
'success': False,
'message': error_message
}), 400
# Log configuration (without exposing secrets)
workspace = config.get('workspace', 'admins')
git_branch = config.get('git_branch', 'main')
logger.info(f"Sync configuration - workspace: {workspace}, branch: {git_branch}")
try:
result = sync_windmill_to_git(config)
if result['success']:
logger.info(f"Sync completed successfully: {result['message']}")
return jsonify(result), 200
else:
logger.error(f"Sync failed: {result['message']}")
return jsonify(result), 500
except Exception as e:
logger.exception("Unexpected error during sync")
return jsonify({
'success': False,
'message': f'Sync failed with error: {str(e)}'
}), 500
if __name__ == '__main__':
logger.info("Starting Windmill Git Sync server on port 8080")
app.run(host='0.0.0.0', port=8080, debug=False)