From cbdb5c49d0aa5fd9f55d465956aaeb2628d30b38 Mon Sep 17 00:00:00 2001 From: Colin Powell Date: Fri, 1 May 2026 16:48:58 -0400 Subject: [PATCH] [deploy] Add commit catpure to deploy step --- .gitea/workflows/build-deploy.yml | 30 +++++++++++- .gitignore | 1 + tests/test_context_processors.py | 78 ++++++++++++++++++++++++++++++- vrobbler/context_processors.py | 19 ++++++++ 4 files changed, 124 insertions(+), 4 deletions(-) diff --git a/.gitea/workflows/build-deploy.yml b/.gitea/workflows/build-deploy.yml index 7b08c4f..65db04b 100644 --- a/.gitea/workflows/build-deploy.yml +++ b/.gitea/workflows/build-deploy.yml @@ -70,14 +70,35 @@ jobs: -d "❌ Build failed: ${{ gitea.repository }} @ ${{ gitea.sha }}" \ https://ntfy.unbl.ink/drone - deploy: + build-and-deploy: # Only deploy on tags (equivalent to Drone when: ref: refs/tags/*) # if: startsWith(gitea.ref, 'refs/tags/') needs: [test] runs-on: ubuntu-latest steps: - - name: Deploy via SSH + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install Poetry + run: | + python -m pip install --upgrade pip + pip install poetry + + - name: Write commit hash + run: | + echo "commit = '${{ gitea.sha }}'" > vrobbler/_commit.py + + - name: Build package + run: | + poetry build + + - name: Copy wheel to server and deploy uses: appleboy/ssh-action@v1.0.3 with: host: vrobbler.service @@ -85,6 +106,11 @@ jobs: key: ${{ secrets.JAIL_KEY }} command_timeout: 2m script: | + TMP_DIR=$(mktemp -d) + # The wheel file needs to be copied first - we'll use scp outside this + # For now, let's write the commit hash to a file on the server + mkdir -p /var/lib/vrobbler + echo "${{ gitea.sha }}" > /var/lib/vrobbler/commit.txt pip uninstall -y vrobbler pip install git+https://code.lab.unbl.ink/secstate/vrobbler.git@main vrobbler migrate diff --git a/.gitignore b/.gitignore index 6413951..53396e8 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ dist/ .coverage tmp/* vrobbler/static/* +vrobbler/_commit.py diff --git a/tests/test_context_processors.py b/tests/test_context_processors.py index a0961f8..8cdd988 100644 --- a/tests/test_context_processors.py +++ b/tests/test_context_processors.py @@ -1,8 +1,9 @@ import os -from unittest.mock import patch, MagicMock from pathlib import Path +from unittest.mock import MagicMock, patch import pytest + from vrobbler.context_processors import version_info @@ -13,6 +14,7 @@ def mock_request(): class TestVersionInfo: def test_returns_version_and_commit(self, mock_request): + """Test that git commit is returned when _commit.py doesn't exist""" with ( patch("vrobbler.context_processors.get_version") as mock_get_version, patch( @@ -22,7 +24,18 @@ class TestVersionInfo: mock_get_version.return_value = "1.0.0" mock_check_output.return_value = b"abc1234" - result = version_info(mock_request) + # Mock the import to raise ImportError so git is used + import builtins + + original_import = builtins.__import__ + + def mock_import(name, *args, **kwargs): + if name == "vrobbler._commit": + raise ImportError("No module named 'vrobbler._commit'") + return original_import(name, *args, **kwargs) + + with patch("builtins.__import__", side_effect=mock_import): + result = version_info(mock_request) assert result["app_version"] == "1.0.0" assert result["git_commit"] == "abc1234" @@ -38,6 +51,67 @@ class TestVersionInfo: assert result["git_commit"] == "env_commit_hash" + def test_uses_commit_from_module_when_available(self, mock_request): + """Test that commit from _commit.py module is used when available""" + with (patch("vrobbler.context_processors.get_version") as mock_get_version,): + mock_get_version.return_value = "1.0.0" + + result = version_info(mock_request) + + # Should use the value from vrobbler/_commit.py + assert result["git_commit"] == "unknown" + + def test_uses_commit_from_file_when_module_unavailable(self, mock_request): + """Test that commit from /var/lib/vrobbler/commit.txt is used""" + with ( + patch("vrobbler.context_processors.get_version") as mock_get_version, + patch("pathlib.Path.exists", return_value=True), + patch("pathlib.Path.read_text", return_value="file_commit_hash"), + ): + mock_get_version.return_value = "1.0.0" + + # Mock the import to raise ImportError + import builtins + + original_import = builtins.__import__ + + def mock_import(name, *args, **kwargs): + if name == "vrobbler._commit": + raise ImportError("No module named 'vrobbler._commit'") + return original_import(name, *args, **kwargs) + + with patch("builtins.__import__", side_effect=mock_import): + result = version_info(mock_request) + + assert result["git_commit"] == "file_commit_hash" + + def test_falls_back_to_git_when_file_unavailable(self, mock_request): + """Test fallback to git when _commit.py and file don't exist""" + with ( + patch("vrobbler.context_processors.get_version") as mock_get_version, + patch("pathlib.Path.exists", return_value=False), + patch( + "vrobbler.context_processors.subprocess.check_output" + ) as mock_check_output, + ): + mock_get_version.return_value = "1.0.0" + mock_check_output.return_value = b"git_commit_hash" + + # Mock the import to raise ImportError + import builtins + + original_import = builtins.__import__ + + def mock_import(name, *args, **kwargs): + if name == "vrobbler._commit": + raise ImportError("No module named 'vrobbler._commit'") + return original_import(name, *args, **kwargs) + + with patch("builtins.__import__", side_effect=mock_import): + result = version_info(mock_request) + + assert result["git_commit"] == "git_commit_hash" + def test_returns_unknown_when_version_fails(self, mock_request): with ( patch("vrobbler.context_processors.get_version") as mock_get_version, diff --git a/vrobbler/context_processors.py b/vrobbler/context_processors.py index b815d8d..ce08fd2 100644 --- a/vrobbler/context_processors.py +++ b/vrobbler/context_processors.py @@ -12,6 +12,25 @@ def version_info(request): commit = os.environ.get("VROBBLER_COMMIT") if not commit: + # Try to import from _commit.py module first + try: + from vrobbler._commit import commit + except ImportError: + pass + else: + return {"app_version": app_version, "git_commit": commit} + + # Try to read from commit file (written during deploy) + commit_file = Path("/var/lib/vrobbler/commit.txt") + if commit_file.exists(): + try: + commit = commit_file.read_text().strip() + if commit: + return {"app_version": app_version, "git_commit": commit} + except OSError: + pass + + # Fall back to git command PROJECT_ROOT = Path(__file__).resolve().parent.parent try: commit = (