diff --git a/README.md b/README.md index 0262d00..8cf8a5a 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,9 @@ DocuPulse is a powerful document management system designed to streamline docume ### Prerequisites -- Node.js (version 18 or higher) -- npm or yarn +- Python 3.11 or higher +- PostgreSQL 13 or higher +- Docker and Docker Compose (for containerized deployment) ### Installation @@ -23,18 +24,50 @@ cd docupulse 2. Install dependencies: ```bash -npm install -# or -yarn install +pip install -r requirements.txt ``` -3. Start the development server: +3. Set up environment variables: ```bash -npm run dev -# or -yarn dev +# Copy example environment file +cp .env.example .env + +# Set version information for local development +python set_version.py ``` +4. Initialize the database: +```bash +flask db upgrade +flask create-admin +``` + +5. Start the development server: +```bash +python app.py +``` + +## Version Tracking + +DocuPulse uses a database-only approach for version tracking: + +- **Environment Variables**: Version information is passed via environment variables (`APP_VERSION`, `GIT_COMMIT`, `GIT_BRANCH`, `DEPLOYED_AT`) +- **Database Storage**: Instance version information is stored in the `instances` table +- **API Endpoint**: Version information is available via `/api/version` + +### Setting Version Information + +For local development: +```bash +python set_version.py +``` + +For production deployments, set the following environment variables: +- `APP_VERSION`: Application version/tag +- `GIT_COMMIT`: Git commit hash +- `GIT_BRANCH`: Git branch name +- `DEPLOYED_AT`: Deployment timestamp + ## Features - Document upload and management @@ -42,6 +75,8 @@ yarn dev - Secure document storage - User authentication and authorization - Document version control +- Multi-tenant instance management +- RESTful API ## Contributing diff --git a/docker-compose.yml b/docker-compose.yml index fd67692..3d01b32 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -21,6 +21,10 @@ services: - POSTGRES_PASSWORD=docupulse_${PORT:-10335} - POSTGRES_DB=docupulse_${PORT:-10335} - MASTER=${ISMASTER:-false} + - APP_VERSION=${APP_VERSION:-unknown} + - GIT_COMMIT=${GIT_COMMIT:-unknown} + - GIT_BRANCH=${GIT_BRANCH:-unknown} + - DEPLOYED_AT=${DEPLOYED_AT:-unknown} volumes: - docupulse_uploads:/app/uploads depends_on: diff --git a/routes/__pycache__/main.cpython-313.pyc b/routes/__pycache__/main.cpython-313.pyc index b35635b..f9d647c 100644 Binary files a/routes/__pycache__/main.cpython-313.pyc and b/routes/__pycache__/main.cpython-313.pyc differ diff --git a/routes/launch_api.py b/routes/launch_api.py index 24894a5..323bd32 100644 --- a/routes/launch_api.py +++ b/routes/launch_api.py @@ -761,48 +761,6 @@ def download_docker_compose(): else: content = response.text - # Add version.txt creation to the docker-compose content - if commit_hash: - # Create version information with both tag and commit hash - version_info = { - 'tag': latest_tag or 'unknown', - 'commit': commit_hash, - 'branch': data['branch'], - 'deployed_at': datetime.utcnow().isoformat() - } - version_json = json.dumps(version_info, indent=2) - - # Add a command to create version.txt with the version information - version_command = f'echo \'{version_json}\' > /app/version.txt' - - # Find the web service and add the command - if 'web:' in content: - # Add the command to create version.txt before the main command - lines = content.split('\n') - new_lines = [] - in_web_service = False - command_added = False - - for line in lines: - new_lines.append(line) - - if line.strip() == 'web:': - in_web_service = True - elif in_web_service and line.strip().startswith('command:'): - # Add the version.txt creation command before the main command - new_lines.append(f' - sh -c "{version_command} && {line.split("command:")[1].strip()}"') - command_added = True - continue - elif in_web_service and line.strip() and not line.startswith(' ') and not line.startswith('#'): - # We've left the web service section - if not command_added: - # If no command was found, add a new command section - new_lines.append(f' command: sh -c "{version_command} && python app.py"') - command_added = True - in_web_service = False - - content = '\n'.join(new_lines) - return jsonify({ 'success': True, 'content': content, diff --git a/routes/main.py b/routes/main.py index 838ef6e..37a3e4f 100644 --- a/routes/main.py +++ b/routes/main.py @@ -1983,32 +1983,16 @@ def init_routes(main_bp): @main_bp.route('/api/version') def api_version(): - version_file = os.path.join(current_app.root_path, 'version.txt') - version = 'unknown' - version_data = {} - - if os.path.exists(version_file): - with open(version_file, 'r') as f: - content = f.read().strip() - - # Try to parse as JSON first (new format) - try: - version_data = json.loads(content) - version = version_data.get('tag', 'unknown') - except json.JSONDecodeError: - # Fallback to old format (just commit hash) - version = content - version_data = { - 'tag': 'unknown', - 'commit': content, - 'branch': 'unknown', - 'deployed_at': 'unknown' - } + # Get version information from environment variables + version = os.getenv('APP_VERSION', 'unknown') + commit = os.getenv('GIT_COMMIT', 'unknown') + branch = os.getenv('GIT_BRANCH', 'unknown') + deployed_at = os.getenv('DEPLOYED_AT', 'unknown') return jsonify({ 'version': version, - 'tag': version_data.get('tag', 'unknown'), - 'commit': version_data.get('commit', 'unknown'), - 'branch': version_data.get('branch', 'unknown'), - 'deployed_at': version_data.get('deployed_at', 'unknown') + 'tag': version, + 'commit': commit, + 'branch': branch, + 'deployed_at': deployed_at }) \ No newline at end of file diff --git a/set_version.py b/set_version.py new file mode 100644 index 0000000..97abb44 --- /dev/null +++ b/set_version.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +""" +Utility script to set version environment variables for local development. +This replaces the need for version.txt file creation. +""" + +import os +import subprocess +import json +from datetime import datetime + +def get_git_info(): + """Get current git commit hash and branch""" + try: + # Get current commit hash + commit_hash = subprocess.check_output(['git', 'rev-parse', 'HEAD'], + text=True, stderr=subprocess.DEVNULL).strip() + + # Get current branch + branch = subprocess.check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD'], + text=True, stderr=subprocess.DEVNULL).strip() + + # Get latest tag + try: + latest_tag = subprocess.check_output(['git', 'describe', '--tags', '--abbrev=0'], + text=True, stderr=subprocess.DEVNULL).strip() + except subprocess.CalledProcessError: + latest_tag = 'unknown' + + return { + 'commit': commit_hash, + 'branch': branch, + 'tag': latest_tag + } + except (subprocess.CalledProcessError, FileNotFoundError): + return { + 'commit': 'unknown', + 'branch': 'unknown', + 'tag': 'unknown' + } + +def set_version_env(): + """Set version environment variables""" + git_info = get_git_info() + + # Set environment variables + os.environ['APP_VERSION'] = git_info['tag'] + os.environ['GIT_COMMIT'] = git_info['commit'] + os.environ['GIT_BRANCH'] = git_info['branch'] + os.environ['DEPLOYED_AT'] = datetime.utcnow().isoformat() + + print("Version environment variables set:") + print(f"APP_VERSION: {os.environ['APP_VERSION']}") + print(f"GIT_COMMIT: {os.environ['GIT_COMMIT']}") + print(f"GIT_BRANCH: {os.environ['GIT_BRANCH']}") + print(f"DEPLOYED_AT: {os.environ['DEPLOYED_AT']}") + +if __name__ == '__main__': + set_version_env() \ No newline at end of file diff --git a/static/js/launch_progress.js b/static/js/launch_progress.js index 7fa9022..af34ca8 100644 --- a/static/js/launch_progress.js +++ b/static/js/launch_progress.js @@ -451,6 +451,11 @@ async function startLaunch(data) { throw new Error(dockerComposeResult.error || 'Failed to download docker-compose.yml'); } + // Set global version variables for deployment + window.currentDeploymentVersion = dockerComposeResult.latest_tag || dockerComposeResult.commit_hash || 'unknown'; + window.currentDeploymentCommit = dockerComposeResult.commit_hash || 'unknown'; + window.currentDeploymentBranch = data.branch; + // Update the step to show success const dockerComposeStep = document.querySelectorAll('.step-item')[7]; dockerComposeStep.classList.remove('active'); @@ -2551,6 +2556,22 @@ async function deployStack(dockerComposeContent, stackName, port) { { name: 'ISMASTER', value: 'false' + }, + { + name: 'APP_VERSION', + value: window.currentDeploymentVersion || 'unknown' + }, + { + name: 'GIT_COMMIT', + value: window.currentDeploymentCommit || 'unknown' + }, + { + name: 'GIT_BRANCH', + value: window.currentDeploymentBranch || 'unknown' + }, + { + name: 'DEPLOYED_AT', + value: new Date().toISOString() } ] }), @@ -2573,34 +2594,9 @@ async function deployStack(dockerComposeContent, stackName, port) { } catch (error) { console.error('Error deploying stack:', error); - - // Check if this is a timeout error - if (error.name === 'AbortError') { - return { - success: false, - error: 'Stack deployment timed out after 10 minutes. The operation may still be in progress.' - }; - } - - // Check if this is a network error - if (error.message && error.message.includes('fetch')) { - return { - success: false, - error: 'Network error during stack deployment. Please check your connection and try again.' - }; - } - - // Check if this is a response error - if (error.message && error.message.includes('Failed to deploy stack')) { - return { - success: false, - error: error.message - }; - } - return { success: false, - error: error.message || 'Unknown error during stack deployment' + error: error.message }; } } \ No newline at end of file diff --git a/templates/main/instances.html b/templates/main/instances.html index b9aad53..76a7b47 100644 --- a/templates/main/instances.html +++ b/templates/main/instances.html @@ -8,6 +8,38 @@ .table td { vertical-align: middle; } + + /* Version column styling */ + .version-badge { + font-family: monospace; + font-size: 0.85em; + } + + .branch-badge { + font-size: 0.85em; + } + + /* Make table responsive */ + .table-responsive { + overflow-x: auto; + } + + /* Tooltip styling for version info */ + .tooltip-inner { + max-width: 300px; + text-align: left; + } + + /* Version comparison styling */ + .version-outdated { + background-color: #fff3cd !important; + border-color: #ffeaa7 !important; + } + + .version-current { + background-color: #d1ecf1 !important; + border-color: #bee5eb !important; + } {% endblock %} @@ -51,6 +83,8 @@
APP_VERSION - Application version/tagGIT_COMMIT - Git commit hashGIT_BRANCH - Git branch nameDEPLOYED_AT - Deployment timestampGET /api/version
+ {
+ "version": "v1.2.3",
+ "tag": "v1.2.3",
+ "commit": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0",
+ "branch": "main",
+ "deployed_at": "2024-01-15T10:30:00.000000"
+}
+