diff --git a/.github/workflows/version-check.yml b/.github/workflows/version-check.yml index 8d906b3..0d2cc10 100644 --- a/.github/workflows/version-check.yml +++ b/.github/workflows/version-check.yml @@ -193,20 +193,28 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const fs = require('fs'); - let mainVersion, stagingVersion, failureReason; + let mainVersion, prBranchVersion, failureReason; try { mainVersion = fs.readFileSync('main/pyproject.toml', 'utf8') .match(/^version\s*=\s*["']([^"']+)["']/m)[1]; - stagingVersion = fs.readFileSync('staging/pyproject.toml', 'utf8') - .match(/^version\s*=\s*["']([^"']+)["']/m)[1]; } catch (e) { + console.error('Failed to read main version:', e.message); mainVersion = 'unknown'; - stagingVersion = 'unknown'; + } + + try { + prBranchVersion = fs.readFileSync('pr-branch/pyproject.toml', 'utf8') + .match(/^version\s*=\s*["']([^"']+)["']/m)[1]; + } catch (e) { + console.error('Failed to read PR branch version:', e.message); + prBranchVersion = 'unknown'; } // Get failure reason from step output failureReason = '${{ steps.failure_reason.outputs.reason }}' || ''; + const prBranch = context.payload.pull_request.head.ref; + let body = `❌ **Version Check Failed**\n\n`; if (failureReason) { @@ -215,13 +223,13 @@ jobs: body += `**Version Bump:**\n` + `- Main version: \`${mainVersion}\`\n` + - `- Staging version: \`${stagingVersion}\`\n\n` + + `- PR branch (\`${prBranch}\`) version: \`${prBranchVersion}\`\n\n` + `Please update the version in **BOTH** files before merging to main:\n\n` + `1. \`pyproject.toml\` (under \`[project]\` section)\n` + `2. \`cartha_validator/__init__.py\` (fallback \`__version__\`)\n\n` + `**Requirements:**\n` + `- Both files must have the **same version**\n` + - `- The staging version must be **greater** than the main version\n` + + `- The PR branch version must be **greater** than the main version\n` + `- Use semantic versioning: \`major.minor.patch\`\n` + `- Repository version_key must be >= subnet weights_version (netuid 35)\n\n` + `Check the workflow logs above for detailed error messages.`; diff --git a/cartha_validator/__init__.py b/cartha_validator/__init__.py index 5e7701d..aaf5163 100644 --- a/cartha_validator/__init__.py +++ b/cartha_validator/__init__.py @@ -1,6 +1,6 @@ """Validator tooling for Cartha.""" -__version__ = "1.0.1" +__version__ = "1.0.2" # Convert version string (e.g., "1.0.0") to spec_version integer (e.g., 1000) # Format: 1000 * major + 10 * minor + 1 * patch diff --git a/pyproject.toml b/pyproject.toml index f3f13ff..311250f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "cartha-subnet-validator" -version = "1.0.1" +version = "1.0.2" description = "Cartha subnet validator reference implementation." requires-python = ">=3.11,<3.12" authors = [ diff --git a/scripts/validator_manager.py b/scripts/validator_manager.py index 9dad27c..6eec7c5 100755 --- a/scripts/validator_manager.py +++ b/scripts/validator_manager.py @@ -243,6 +243,73 @@ def initialize_validator_uid( ) return False + def get_running_validator_version(self) -> str | None: + """ + Extract the running validator version from its logs. + + Returns: + Version string if found, None otherwise + """ + try: + # Get recent logs from PM2 + logs = self.pm2_manager.get_logs(lines=200) + + # Look for version pattern: "Validator version: 1.0.1" or "__version__ = 1.0.1" + import re + patterns = [ + r'Validator version:\s*([\d.]+)', + r'__version__\s*=\s*["\']([\d.]+)["\']', + r'version_key=(\d+)', # Fallback: extract from version_key + ] + + for pattern in patterns: + match = re.search(pattern, logs) + if match: + version = match.group(1) + # If we got version_key, convert it back (e.g., 1001 -> 1.0.1) + if pattern == patterns[2] and len(version) == 4: + major = int(version[0]) + minor = int(version[1:3]) + patch = int(version[3]) + return f"{major}.{minor}.{patch}" + return version + + return None + except Exception as e: + print(f"Warning: Could not extract running validator version: {e}", file=sys.stderr) + return None + + def check_running_version_mismatch(self) -> bool: + """ + Check if running validator version differs from local code version. + This handles cases where code was updated but validator wasn't restarted. + + Returns: + True if versions don't match and restart is needed, False otherwise + """ + try: + local_version = get_version_from_pyproject() + running_version = self.get_running_validator_version() + + if running_version is None: + # Can't determine running version, assume it's fine + return False + + if local_version != running_version: + print( + f"⚠ Version mismatch detected!\n" + f" Local code version: {local_version}\n" + f" Running validator version: {running_version}\n" + f" Restarting validator to sync versions...", + file=sys.stderr + ) + return True + + return False + except Exception as e: + print(f"Warning: Could not check version mismatch: {e}", file=sys.stderr) + return False + def check_for_updates(self) -> tuple[bool, str | None]: """ Check if a new release is available on GitHub. @@ -384,7 +451,16 @@ def run_update_loop(self) -> None: while True: try: - # Check for updates + # First, check if running validator version matches local code version + # This handles cases where code was updated but validator wasn't restarted + if self.check_running_version_mismatch(): + print("Restarting validator to sync with local code version...") + self.pm2_manager.restart_validator() + time.sleep(5) # Wait for restart + if not self.pm2_manager.is_running(): + print("Warning: Validator failed to restart after version sync", file=sys.stderr) + + # Check for updates from GitHub update_available, latest_version = self.check_for_updates() if update_available: diff --git a/uv.lock b/uv.lock index bed81f8..061e642 100644 --- a/uv.lock +++ b/uv.lock @@ -250,7 +250,7 @@ wheels = [ [[package]] name = "cartha-subnet-validator" -version = "1.0.1" +version = "1.0.2" source = { editable = "." } dependencies = [ { name = "apscheduler" },