feat: Add Windows executable with one-click installation (Issue #500)#518
feat: Add Windows executable with one-click installation (Issue #500)#518krishnactive wants to merge 1 commit into
Conversation
Closes journiv#500 FEATURES: - Single-click Windows executable for non-technical users - Auto-configuration on first run (no manual setup needed) - Smart port detection to avoid conflicts - Automatic browser launch - Bundled SQLite database - Secure SECRET_KEY auto-generation BUILD SYSTEM: - PyInstaller build script for creating standalone .exe - Batch launcher (run.bat) with port detection - Automatic configuration setup script - Build validation tool CI/CD: - GitHub Actions workflow for automated builds - Triggers on version tags (v*.*.*) - Automatic release generation DOCUMENTATION: - WINDOWS_GUIDE.md: Complete user & developer guide - QUICKSTART_WINDOWS.md: 5-step quick start guide - WINDOWS_EXECUTABLE_IMPLEMENTATION.md: Technical details - Updated README.md with Windows section - Updated CONTRIBUTING.md with build instructions Scripts added: - scripts/build_executable.py (164 lines) - scripts/setup_defaults.py (46 lines) - scripts/run.bat (Windows launcher) - scripts/validate_windows_build.py (92 lines) Workflows added: - .github/workflows/build-windows-exe.yml Documentation added/updated: - WINDOWS_GUIDE.md (280+ lines) - QUICKSTART_WINDOWS.md (110 lines) - WINDOWS_EXECUTABLE_IMPLEMENTATION.md (350+ lines) - README.md (added Windows section) - CONTRIBUTING.md (added build instructions) - pyproject.toml (added build dependencies) Benefits for Windows Users: - No Python installation required - No Docker required - Zero configuration needed - One-click execution - All data stored locally - Portable and privacy-focused Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
📝 WalkthroughWalkthroughAdded comprehensive Windows executable distribution infrastructure for Journiv, including a PyInstaller-based build system, GitHub Actions automation for releases, Windows launcher scripts with auto-setup, and documentation for both end-users and developers. Enables one-click Windows deployment without manual Python/Docker configuration. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Launcher as run.bat<br/>(Launcher)
participant Setup as setup_defaults.py<br/>(Setup)
participant Installer as pip install<br/>(Dependencies)
participant Server as uvicorn<br/>(App Server)
participant Browser as Browser<br/>(Client)
User->>Launcher: Double-click journiv.exe
Launcher->>Launcher: Detect available port
alt First Run (no .env)
Launcher->>Setup: Execute setup
Setup->>Setup: Create data directories
Setup->>Setup: Generate SECRET_KEY & .env
Setup-->>Launcher: Setup complete
end
Launcher->>Installer: pip install -e .
Installer-->>Launcher: Dependencies ready
Launcher->>Server: python -m uvicorn<br/>app.main:app
Server->>Server: Initialize application
Launcher->>Browser: Open browser to<br/>localhost:PORT
Browser-->>User: Display UI
Server-->>Browser: Serve application
sequenceDiagram
participant Developer
participant GitHub as GitHub
participant Workflow as build-windows-exe<br/>(Workflow)
participant Build as build_executable.py<br/>(Builder)
participant PyInstaller as PyInstaller
participant Release as GitHub Release
Developer->>GitHub: Push version tag (v*.*)
GitHub->>Workflow: Trigger workflow
Workflow->>Workflow: Setup Python 3.12
Workflow->>Build: Execute build script
Build->>Build: Verify dependencies
Build->>PyInstaller: Run PyInstaller
PyInstaller->>PyInstaller: Bundle app & deps
PyInstaller-->>Build: journiv.exe created
Build->>Build: Create journiv.bat launcher
Build->>Build: Generate README & notes
Build-->>Workflow: Build artifacts ready
Workflow->>Workflow: Create versioned ZIP
Workflow->>Release: Upload ZIP & notes
Release-->>GitHub: Release published
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 10
🧹 Nitpick comments (9)
QUICKSTART_WINDOWS.md (1)
35-42: Add language specifier to fenced code block.The directory structure code block should have a language specifier for consistency and to satisfy markdown linting rules.
📝 Proposed fix
-``` +```text journiv-windows/ ├── journiv.exe ├── journiv.bat ├── data/ │ ├── journiv.db ← Your journal database │ └── media/ ← Your images, videos, etc. +```🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@QUICKSTART_WINDOWS.md` around lines 35 - 42, The fenced code block showing the directory structure in QUICKSTART_WINDOWS.md lacks a language specifier; update the opening fence for that snippet (the block that begins with ``` and contains "journiv-windows/ ├── journiv.exe ...") to include a language tag like ```text so markdown linters accept it and the block renders consistently.scripts/run.bat (1)
115-117: Consider removing--reloadflag for production use.The
--reloadflag enables auto-reload on file changes, which is appropriate for development but adds overhead and potential instability for end-users running the packaged executable. Since this script targets end-users (one-click execution), consider removing it.♻️ Proposed fix
REM Start the application cd /d "%APP_ROOT%" -python -m uvicorn app.main:app --host 0.0.0.0 --port %PORT% --reload +python -m uvicorn app.main:app --host 0.0.0.0 --port %PORT%🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/run.bat` around lines 115 - 117, The batch script currently starts Uvicorn with the development-only --reload flag (the command invoking "python -m uvicorn app.main:app --host 0.0.0.0 --port %PORT% --reload"); remove the --reload option for production/packaged end-user execution so the app runs without automatic file-watching overhead and instability. Locate the Uvicorn start command in scripts/run.bat and delete the --reload token (or replace with a conditional/ENV check if you need a dev-mode toggle) so the command becomes the production-safe invocation.WINDOWS_GUIDE.md (1)
25-29: Same Windows version concern as QUICKSTART_WINDOWS.md.The "Windows 7 or later" claim should be verified. Python 3.12 officially supports Windows 10 and later. Consider updating to "Windows 10 or later (64-bit)" for accuracy.
📝 Proposed fix
### System Requirements -- Windows 7 or later (64-bit) +- Windows 10 or later (64-bit) - No additional software needed🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@WINDOWS_GUIDE.md` around lines 25 - 29, Update the System Requirements section in WINDOWS_GUIDE.md by changing the OS requirement line "Windows 7 or later (64-bit)" to "Windows 10 or later (64-bit)" to reflect Python 3.12 support; also make the same correction in QUICKSTART_WINDOWS.md if present so both docs stay consistent.scripts/setup_defaults.py (1)
35-38: Consider adding error handling for directory creation.The function always returns
Trueeven ifmkdirfails (it would raise an exception). Consider wrapping in try/except for consistency withsetup_default_env.♻️ Proposed improvement
def ensure_data_directories(app_root: Path): + try: for directory in [app_root / "data", app_root / "data" / "media"]: directory.mkdir(parents=True, exist_ok=True) return True + except Exception as e: + print(f"Error creating data directories: {e}") + return False🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/setup_defaults.py` around lines 35 - 38, The ensure_data_directories function unconditionally returns True despite mkdir potentially raising; wrap the loop that calls directory.mkdir(parents=True, exist_ok=True) in a try/except (catch OSError/Exception as e), mirror the error handling approach used in setup_default_env by logging or printing the exception (include the error text and which directory failed) and return False on failure, otherwise return True on success; reference ensure_data_directories and the mkdir calls for where to add the try/except.scripts/validate_windows_build.py (2)
63-71: Consider addingWINDOWS_EXECUTABLE_IMPLEMENTATION.mdto the validation list.The AI summary indicates this document is part of the Windows build infrastructure, but it's not included in the file validation checks while
WINDOWS_GUIDE.mdis. Consider adding it for completeness, or this may be intentional if it's developer-only documentation not needed at build time.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/validate_windows_build.py` around lines 63 - 71, The validation list in validate_windows_build.py (the files variable) omits WINDOWS_EXECUTABLE_IMPLEMENTATION.md while it includes WINDOWS_GUIDE.md; update the files tuple list (where files = [...] is defined, referencing app_root and the existing "WINDOWS_GUIDE.md" entry) to also include (app_root / "WINDOWS_EXECUTABLE_IMPLEMENTATION.md", "Executable implementation doc") so the new doc is validated alongside the guide, unless this omission is intentional—if intentional, add a comment explaining why it's excluded.
23-30: Python version check has edge-case logic flaw.The condition
info.major >= 3 and info.minor >= 12would incorrectly reject Python 4.0 (hypothetical future) and accept any Python 3.x where x >= 12. The current logic works for Python 3.12+ but is not future-proof.♻️ More robust version check
def check_python_version(): info = sys.version_info - if info.major >= 3 and info.minor >= 12: + if (info.major, info.minor) >= (3, 12): print(f"✓ Python: {info.major}.{info.minor}") return True else:🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/validate_windows_build.py` around lines 23 - 30, The version check in check_python_version incorrectly uses separate comparisons (info.major >= 3 and info.minor >= 12), which fails for future major versions (e.g., Python 4.x) and mis-evaluates some 3.x cases; change the logic to compare version tuples instead (e.g., compare (info.major, info.minor) >= (3, 12) or use sys.version_info >= (3, 12)) so the function reliably accepts 3.12+ and any future majors >= 3.12, then keep the existing print/return behavior in check_python_version.scripts/build_executable.py (2)
82-91:--windowedflag hides console, making troubleshooting difficult for users.The
--windowedflag suppresses the console window, so if the server fails to start, users won't see any error messages. For a server application where users need to see startup status (port, URL), consider using--consoleinstead, or providing a separate debug launcher.♻️ Use console mode for better user feedback
args = [ str(project_root / "app" / "main.py"), "--onefile", - "--windowed", + "--console", "--name", "journiv",Alternatively, keep
--windowedbut ensurejourniv.batis the primary entry point, which shows a console window.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/build_executable.py` around lines 82 - 91, The build script currently passes the "--windowed" option in the args list which hides the console; change the build invocation to use console mode so startup errors and server status are visible by replacing the "--windowed" flag with "--console" (or removing "--windowed" entirely) in the args array used when building the executable (refer to the args variable and the "--windowed" entry), or alternatively keep "--windowed" but ensure the packaging produces a journiv.bat launcher that is the documented primary entry point so users get a console for troubleshooting.
119-126: Batch launcher lacks error handling and user feedback.The generated
journiv.batsilently creates directories but doesn't inform users about server startup status or handle errors ifjourniv.exefails to launch. Since--windowedis used, users may see nothing if startup fails.♻️ Add basic error handling and status messages
batch_content = """@echo off setlocal enabledelayedexpansion set APP_DIR=%~dp0 +echo Starting Journiv... if not exist "%APP_DIR%data" mkdir "%APP_DIR%data" if not exist "%APP_DIR%data\\media" mkdir "%APP_DIR%data\\media" +echo Data directories ready. +echo. +echo Starting server (this may take a moment)... "%APP_DIR%journiv.exe" +if errorlevel 1 ( + echo. + echo Error: Journiv failed to start. Press any key to exit. + pause >nul +) endlocal """🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/build_executable.py` around lines 119 - 126, The generated batch template in variable batch_content should add user-facing status messages and basic error handling around running journiv.exe: update the template to echo a "Starting journiv..." message, confirm creation of data and data\media directories with echo, run journiv.exe in a way that captures its exit code (e.g., call or start /wait) and check %ERRORLEVEL% to echo success or a descriptive failure message, then pause on failure so users see the error; locate and modify the batch_content string in scripts/build_executable.py that builds journiv.bat and include these messages and an error-path that echoes the error and exits with a non-zero code.WINDOWS_EXECUTABLE_IMPLEMENTATION.md (1)
271-275: Minor grammar and formatting issue.The bullet point structure has inconsistent formatting. Consider revising for clarity.
📝 Suggested fix
✅ **SQLite Security** - Built into executable - No external database required -- Included in .exe bundle +- SQLite library included in .exe bundle🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@WINDOWS_EXECUTABLE_IMPLEMENTATION.md` around lines 271 - 275, The "✅ **SQLite Security**" section's three bullet lines are inconsistent in phrasing and punctuation; make them parallel and consistently formatted by adjusting the three bullets under the heading (the lines starting "Built into executable", "No external database required", and "Included in .exe bundle") so they follow the same grammatical form and punctuation—e.g., start each with a verb phrase and either omit terminal periods across all bullets or add a period to each for full sentences—so the section reads consistently and clearly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/build-windows-exe.yml:
- Around line 68-82: The workflow steps "Upload to release" (uses:
softprops/action-gh-release@v1) and "Upload artifact (for workflow dispatch)"
(uses: actions/upload-artifact@v3) are pinned to older action versions; update
their "uses" pins to the current stable major versions (e.g., bump
softprops/action-gh-release to the latest stable major and
actions/upload-artifact to v4) so they receive fixes and compatibility updates,
leaving the step names, inputs (files/body_path, name/path) and env intact.
- Around line 41-47: The ZIP currently includes the top-level folder because
Compress-Archive is called with -Path $DistPath; change the call so it archives
the contents of the dist folder rather than the folder itself (e.g. use
Compress-Archive -Path "$DistPath\*" or pass the files from Get-ChildItem for
$DistPath) and keep the same $ZipName variable; update the Compress-Archive
invocation (and any related Write-Host messaging) so extracted archives place
journiv.exe and friend files at the archive root instead of under a dist/
subfolder.
- Around line 18-24: Update the GitHub Actions steps to use the latest major
action releases: replace uses: actions/setup-python@v4 with the current stable
tag (e.g., actions/setup-python@v5) and replace uses: actions/cache@v3 with the
current stable tag (e.g., actions/cache@v4); ensure the step names ("Set up
Python" and "Cache pip packages") keep the same inputs (python-version and cache
keys) and run the workflow locally or in CI to verify no breaking changes in
setup-python or cache action input names.
In `@QUICKSTART_WINDOWS.md`:
- Around line 66-69: Update the "## 📋 System Requirements" section by replacing
the line that currently reads "Windows 7 or later (64-bit)" with "Windows 10 or
later (64-bit)" to reflect Python 3.12's supported platforms; locate the exact
string "Windows 7 or later (64-bit)" in QUICKSTART_WINDOWS.md and change it to
"Windows 10 or later (64-bit)" so the quickstart accurately documents supported
OS versions.
In `@scripts/build_executable.py`:
- Around line 69-74: The datas list in scripts/build_executable.py currently
unconditionally references app/static and app/templates which will cause
PyInstaller to fail if those directories are missing; update the code that
builds the datas variable to check existence (using Path.exists() on
project_root / "app" / "static" and project_root / "app" / "templates") and only
append the tuple for each directory if it exists, leaving the other entries
(web, alembic) unchanged so PyInstaller only packages actual present paths.
- Around line 49-58: Remove the manual sqlite3 bundling by deleting the
get_binaries() function and any usage of its result (e.g., passing its entries
to --add-binary) so PyInstaller's built-in hook handles sqlite3/_sqlite3; if you
still need to force inclusion, replace the add-binary logic with hidden imports
--hidden-import=sqlite3 --hidden-import=_sqlite3 instead of bundling the sqlite3
stdlib path.
In `@scripts/run.bat`:
- Around line 1-5: The batch script run.bat is using LF endings which breaks
Windows batch parsing; open the file containing the lines starting with "@echo
off" and "setlocal enabledelayedexpansion" and convert its line endings to CRLF,
then commit the change; also add a .gitattributes entry to force CRLF for .bat
files (e.g., set *.bat to text eol=crlf) so future commits preserve CRLF, or run
a conversion tool like unix2dos before committing to ensure the file is saved
with Windows line endings.
- Around line 8-9: The SET commands for SCRIPT_DIR and APP_ROOT must use quoted
syntax so paths with spaces don't break: update the lines that set SCRIPT_DIR
and APP_ROOT in scripts/run.bat to use the safe form (e.g., set
"SCRIPT_DIR=%~dp0" and set "APP_ROOT=%SCRIPT_DIR%..") so the values are assigned
without including stray quotes and preserve spaces when expanded.
In `@scripts/setup_defaults.py`:
- Around line 15-25: The .env template in scripts/setup_defaults.py sets
MEDIA_ROOT to an absolute Unix path (/data/media) which breaks portable Windows
deployments; update the env_content generation to use a relative media path
(e.g. data/media or ./data/media) so it points to the data/media folder created
by ensure_data_directories; locate the env_content variable and replace the
MEDIA_ROOT line accordingly, ensuring it matches the directory created by the
ensure_data_directories function.
In `@WINDOWS_EXECUTABLE_IMPLEMENTATION.md`:
- Line 129: The release tag pattern contains an extra trailing period; update
the tag string `v*.*.*.` to `v*.*.*` (replace `v*.*.*.` with `v*.*.*`) so the
automated build trigger uses the correct tag pattern.
---
Nitpick comments:
In `@QUICKSTART_WINDOWS.md`:
- Around line 35-42: The fenced code block showing the directory structure in
QUICKSTART_WINDOWS.md lacks a language specifier; update the opening fence for
that snippet (the block that begins with ``` and contains "journiv-windows/ ├──
journiv.exe ...") to include a language tag like ```text so markdown linters
accept it and the block renders consistently.
In `@scripts/build_executable.py`:
- Around line 82-91: The build script currently passes the "--windowed" option
in the args list which hides the console; change the build invocation to use
console mode so startup errors and server status are visible by replacing the
"--windowed" flag with "--console" (or removing "--windowed" entirely) in the
args array used when building the executable (refer to the args variable and the
"--windowed" entry), or alternatively keep "--windowed" but ensure the packaging
produces a journiv.bat launcher that is the documented primary entry point so
users get a console for troubleshooting.
- Around line 119-126: The generated batch template in variable batch_content
should add user-facing status messages and basic error handling around running
journiv.exe: update the template to echo a "Starting journiv..." message,
confirm creation of data and data\media directories with echo, run journiv.exe
in a way that captures its exit code (e.g., call or start /wait) and check
%ERRORLEVEL% to echo success or a descriptive failure message, then pause on
failure so users see the error; locate and modify the batch_content string in
scripts/build_executable.py that builds journiv.bat and include these messages
and an error-path that echoes the error and exits with a non-zero code.
In `@scripts/run.bat`:
- Around line 115-117: The batch script currently starts Uvicorn with the
development-only --reload flag (the command invoking "python -m uvicorn
app.main:app --host 0.0.0.0 --port %PORT% --reload"); remove the --reload option
for production/packaged end-user execution so the app runs without automatic
file-watching overhead and instability. Locate the Uvicorn start command in
scripts/run.bat and delete the --reload token (or replace with a conditional/ENV
check if you need a dev-mode toggle) so the command becomes the production-safe
invocation.
In `@scripts/setup_defaults.py`:
- Around line 35-38: The ensure_data_directories function unconditionally
returns True despite mkdir potentially raising; wrap the loop that calls
directory.mkdir(parents=True, exist_ok=True) in a try/except (catch
OSError/Exception as e), mirror the error handling approach used in
setup_default_env by logging or printing the exception (include the error text
and which directory failed) and return False on failure, otherwise return True
on success; reference ensure_data_directories and the mkdir calls for where to
add the try/except.
In `@scripts/validate_windows_build.py`:
- Around line 63-71: The validation list in validate_windows_build.py (the files
variable) omits WINDOWS_EXECUTABLE_IMPLEMENTATION.md while it includes
WINDOWS_GUIDE.md; update the files tuple list (where files = [...] is defined,
referencing app_root and the existing "WINDOWS_GUIDE.md" entry) to also include
(app_root / "WINDOWS_EXECUTABLE_IMPLEMENTATION.md", "Executable implementation
doc") so the new doc is validated alongside the guide, unless this omission is
intentional—if intentional, add a comment explaining why it's excluded.
- Around line 23-30: The version check in check_python_version incorrectly uses
separate comparisons (info.major >= 3 and info.minor >= 12), which fails for
future major versions (e.g., Python 4.x) and mis-evaluates some 3.x cases;
change the logic to compare version tuples instead (e.g., compare (info.major,
info.minor) >= (3, 12) or use sys.version_info >= (3, 12)) so the function
reliably accepts 3.12+ and any future majors >= 3.12, then keep the existing
print/return behavior in check_python_version.
In `@WINDOWS_EXECUTABLE_IMPLEMENTATION.md`:
- Around line 271-275: The "✅ **SQLite Security**" section's three bullet lines
are inconsistent in phrasing and punctuation; make them parallel and
consistently formatted by adjusting the three bullets under the heading (the
lines starting "Built into executable", "No external database required", and
"Included in .exe bundle") so they follow the same grammatical form and
punctuation—e.g., start each with a verb phrase and either omit terminal periods
across all bullets or add a period to each for full sentences—so the section
reads consistently and clearly.
In `@WINDOWS_GUIDE.md`:
- Around line 25-29: Update the System Requirements section in WINDOWS_GUIDE.md
by changing the OS requirement line "Windows 7 or later (64-bit)" to "Windows 10
or later (64-bit)" to reflect Python 3.12 support; also make the same correction
in QUICKSTART_WINDOWS.md if present so both docs stay consistent.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: c3d84b6b-e37e-4477-982a-fde792d50fff
📒 Files selected for processing (11)
.github/workflows/build-windows-exe.ymlCONTRIBUTING.mdQUICKSTART_WINDOWS.mdREADME.mdWINDOWS_EXECUTABLE_IMPLEMENTATION.mdWINDOWS_GUIDE.mdpyproject.tomlscripts/build_executable.pyscripts/run.batscripts/setup_defaults.pyscripts/validate_windows_build.py
| - name: Set up Python | ||
| uses: actions/setup-python@v4 | ||
| with: | ||
| python-version: '3.12' | ||
|
|
||
| - name: Cache pip packages | ||
| uses: actions/cache@v3 |
There was a problem hiding this comment.
Update GitHub Actions to latest versions.
The static analysis correctly identifies that these action versions are outdated and may stop working. GitHub has deprecated older action runners.
🔧 Proposed fix for action versions
- name: Set up Python
- uses: actions/setup-python@v4
+ uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Cache pip packages
- uses: actions/cache@v3
+ uses: actions/cache@v4
with:
path: ~\AppData\Local\pip\Cache📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - name: Set up Python | |
| uses: actions/setup-python@v4 | |
| with: | |
| python-version: '3.12' | |
| - name: Cache pip packages | |
| uses: actions/cache@v3 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.12' | |
| - name: Cache pip packages | |
| uses: actions/cache@v4 |
🧰 Tools
🪛 actionlint (1.7.12)
[error] 19-19: the runner of "actions/setup-python@v4" action is too old to run on GitHub Actions. update the action's version to fix this issue
(action)
[error] 24-24: the runner of "actions/cache@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue
(action)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/build-windows-exe.yml around lines 18 - 24, Update the
GitHub Actions steps to use the latest major action releases: replace uses:
actions/setup-python@v4 with the current stable tag (e.g.,
actions/setup-python@v5) and replace uses: actions/cache@v3 with the current
stable tag (e.g., actions/cache@v4); ensure the step names ("Set up Python" and
"Cache pip packages") keep the same inputs (python-version and cache keys) and
run the workflow locally or in CI to verify no breaking changes in setup-python
or cache action input names.
| - name: Create distribution archive | ||
| shell: pwsh | ||
| run: | | ||
| $DistPath = "dist" | ||
| $ZipName = "journiv-windows-${{ github.ref_name }}.zip" | ||
| Compress-Archive -Path $DistPath -DestinationPath $ZipName | ||
| Write-Host "Created $ZipName" |
There was a problem hiding this comment.
ZIP archive structure may not be as intended.
Compress-Archive -Path $DistPath creates a ZIP with dist as the root folder, meaning users will extract to journiv-windows-v*/dist/journiv.exe. The documentation implies files should be directly accessible after extraction.
🔧 Proposed fix to zip contents directly
- name: Create distribution archive
shell: pwsh
run: |
$DistPath = "dist"
$ZipName = "journiv-windows-${{ github.ref_name }}.zip"
- Compress-Archive -Path $DistPath -DestinationPath $ZipName
+ Compress-Archive -Path "$DistPath\*" -DestinationPath $ZipName
Write-Host "Created $ZipName"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/build-windows-exe.yml around lines 41 - 47, The ZIP
currently includes the top-level folder because Compress-Archive is called with
-Path $DistPath; change the call so it archives the contents of the dist folder
rather than the folder itself (e.g. use Compress-Archive -Path "$DistPath\*" or
pass the files from Get-ChildItem for $DistPath) and keep the same $ZipName
variable; update the Compress-Archive invocation (and any related Write-Host
messaging) so extracted archives place journiv.exe and friend files at the
archive root instead of under a dist/ subfolder.
| - name: Upload to release | ||
| uses: softprops/action-gh-release@v1 | ||
| if: startsWith(github.ref, 'refs/tags/') | ||
| with: | ||
| files: journiv-windows-*.zip | ||
| body_path: RELEASE_NOTES.md | ||
| env: | ||
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| - name: Upload artifact (for workflow dispatch) | ||
| uses: actions/upload-artifact@v3 | ||
| if: github.event_name == 'workflow_dispatch' | ||
| with: | ||
| name: journiv-windows-executable | ||
| path: dist/ |
There was a problem hiding this comment.
Update remaining action versions.
These actions also need version updates to ensure continued compatibility.
🔧 Proposed fix
- name: Upload to release
- uses: softprops/action-gh-release@v1
+ uses: softprops/action-gh-release@v2
if: startsWith(github.ref, 'refs/tags/')
with:
files: journiv-windows-*.zip
body_path: RELEASE_NOTES.md
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload artifact (for workflow dispatch)
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
if: github.event_name == 'workflow_dispatch'🧰 Tools
🪛 actionlint (1.7.12)
[error] 69-69: the runner of "softprops/action-gh-release@v1" action is too old to run on GitHub Actions. update the action's version to fix this issue
(action)
[error] 78-78: the runner of "actions/upload-artifact@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue
(action)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/build-windows-exe.yml around lines 68 - 82, The workflow
steps "Upload to release" (uses: softprops/action-gh-release@v1) and "Upload
artifact (for workflow dispatch)" (uses: actions/upload-artifact@v3) are pinned
to older action versions; update their "uses" pins to the current stable major
versions (e.g., bump softprops/action-gh-release to the latest stable major and
actions/upload-artifact to v4) so they receive fixes and compatibility updates,
leaving the step names, inputs (files/body_path, name/path) and env intact.
| ## 📋 System Requirements | ||
|
|
||
| - Windows 7 or later (64-bit) | ||
| - That's it! ✅ |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
What Windows versions does Python 3.12 support?
💡 Result:
Python 3.12’s own “Using Python on Windows” documentation states that it supports Windows 8.1 and newer (and suggests Python 3.8 if you need Windows 7) [1].
However, CPython’s platform-support policy (PEP 11) is to support only Windows versions that are still in Microsoft’s support lifecycle, and a CPython docs issue notes that since Windows 8.1 support ended in January 2023, Python 3.12 (released October 2023) should therefore have dropped Windows 8.1 (i.e., effectively Windows 10/11) even if some builds/installers may still run [2][3]. A core-dev discussion likewise notes “Python 3.12 will only support Windows 10/11” for Windows 8.1 users [4].
So in practice:
- Documented in the 3.12 manual: Windows 8.1+ [1]
- Intended/Policy support per PEP 11 timeframe: Windows 10 and Windows 11 (Windows 8.1 not supported) [2][3][4]
Sources: [1] [2] [3] [4]
Update Windows system requirement to reflect Python 3.12 support policy.
The documented requirement of "Windows 7 or later (64-bit)" is inaccurate. Python 3.12 dropped support for Windows 7 and 8.1. Per PEP 11 (CPython's platform-support policy), Python 3.12 only supports Windows versions still in Microsoft's support lifecycle—effectively Windows 10 and 11 only (Windows 8.1 support ended in January 2023). Change the requirement to Windows 10 or later (64-bit).
🧰 Tools
🪛 LanguageTool
[style] ~69-~69: Using many exclamation marks might seem excessive (in this case: 4 exclamation marks for a text that’s 1587 characters long)
Context: ... Windows 7 or later (64-bit) - That's it! ✅ ## 🔒 Privacy - ✅ No account requir...
(EN_EXCESSIVE_EXCLAMATION)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@QUICKSTART_WINDOWS.md` around lines 66 - 69, Update the "## 📋 System
Requirements" section by replacing the line that currently reads "Windows 7 or
later (64-bit)" with "Windows 10 or later (64-bit)" to reflect Python 3.12's
supported platforms; locate the exact string "Windows 7 or later (64-bit)" in
QUICKSTART_WINDOWS.md and change it to "Windows 10 or later (64-bit)" so the
quickstart accurately documents supported OS versions.
| def get_binaries(): | ||
| binaries = [] | ||
| try: | ||
| import sqlite3 | ||
| sqlite3_path = Path(sqlite3.__file__).parent | ||
| if sqlite3_path.exists(): | ||
| binaries.append((str(sqlite3_path), "sqlite3")) | ||
| except ImportError: | ||
| pass | ||
| return binaries |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
PyInstaller bundling sqlite3 standard library module
💡 Result:
sqlite3 is “stdlib”, but it relies on the compiled extension module _sqlite3 (and, depending on platform, an SQLite shared library). PyInstaller usually handles this automatically via its built-in hook-sqlite3.py (it collects all sqlite3.* submodules except tests). [1]
If your frozen app still fails, it’s almost always one of these cases:
- Your Python environment is missing
_sqlite3
- Verify before bundling:
python -c "import sqlite3, _sqlite3; print(sqlite3.sqlite_version)"
- If that fails, PyInstaller cannot fix it—reinstall/rebuild Python with SQLite enabled (on Linux builds, this often means installing
libsqlite3-dev/headers before building Python). [3]
- Hidden-import / analysis issue (less common for stdlib, but can happen via indirection)
- Rebuild with import debugging to see what’s missing:
pyinstaller --debug=imports your_app.py[2]
- If needed, force inclusion:
pyinstaller --hidden-import=sqlite3 --hidden-import=_sqlite3 your_app.py
- Your app creates/opens a DB using a relative path
- In a frozen app, the “current working directory” may differ from what you expect; use an absolute path (e.g., in
%APPDATA%,~/.local/share, etc.) rather than relying ondatabase.dbin the CWD. [4]
Sources: PyInstaller hook for sqlite3 [1], PyInstaller docs on --debug=imports [2], _sqlite3 missing due to Python build/env lacking SQLite support [3], common frozen-app DB path pitfall [4].
🏁 Script executed:
# First, let's understand the build_executable.py file and its context
cat -n scripts/build_executable.py | head -80Repository: journiv/journiv-app
Length of output: 2882
🏁 Script executed:
# Search for how PyInstaller is used in this project
rg -i "pyinstaller\|pyinstaller" --type py -B 2 -A 2Repository: journiv/journiv-app
Length of output: 45
🌐 Web query:
PyInstaller --collect-binaries option documentation
💡 Result:
--collect-binaries MODULENAME is a PyInstaller CLI option that collects (bundles) all binary files from the specified package or module; it can be used multiple times. [1][2]
Example:
pyinstaller --collect-binaries somepackage yourscript.pyPrimary docs: PyInstaller manual page / command reference. [1]
🏁 Script executed:
cat -n scripts/build_executable.py | tail -50Repository: journiv/journiv-app
Length of output: 1438
🏁 Script executed:
# Check for any references to sqlite3 or issues in comments/git history
rg -i "sqlite|binary|bundle" scripts/build_executable.py -B 2 -A 2Repository: journiv/journiv-app
Length of output: 950
Remove manual sqlite3 bundling—PyInstaller handles it automatically.
PyInstaller includes a built-in hook (hook-sqlite3.py) that automatically collects the sqlite3 stdlib module and its compiled extension (_sqlite3). The manual bundling in get_binaries() duplicates this, potentially causing bloat or conflicts. Remove this function and the associated --add-binary logic unless you encounter specific sqlite3 import errors, which should be resolved with --hidden-import=sqlite3 --hidden-import=_sqlite3 instead.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/build_executable.py` around lines 49 - 58, Remove the manual sqlite3
bundling by deleting the get_binaries() function and any usage of its result
(e.g., passing its entries to --add-binary) so PyInstaller's built-in hook
handles sqlite3/_sqlite3; if you still need to force inclusion, replace the
add-binary logic with hidden imports --hidden-import=sqlite3
--hidden-import=_sqlite3 instead of bundling the sqlite3 stdlib path.
| datas = [ | ||
| (str(project_root / "app" / "static"), "app/static"), | ||
| (str(project_root / "app" / "templates"), "app/templates"), | ||
| (str(project_root / "web"), "web"), | ||
| (str(project_root / "alembic"), "alembic"), | ||
| ] |
There was a problem hiding this comment.
Build may fail if app/static or app/templates directories don't exist.
Per the codebase context, app/static is conditionally mounted only if it exists, and app/templates doesn't appear to be used in main.py. If these directories are missing, PyInstaller will fail with a file-not-found error.
🛡️ Proposed fix to handle optional directories
- datas = [
- (str(project_root / "app" / "static"), "app/static"),
- (str(project_root / "app" / "templates"), "app/templates"),
- (str(project_root / "web"), "web"),
- (str(project_root / "alembic"), "alembic"),
- ]
+ datas = []
+ optional_data_dirs = [
+ (project_root / "app" / "static", "app/static"),
+ (project_root / "app" / "templates", "app/templates"),
+ ]
+ required_data_dirs = [
+ (project_root / "web", "web"),
+ (project_root / "alembic", "alembic"),
+ ]
+
+ for src, dest in optional_data_dirs:
+ if src.exists():
+ datas.append((str(src), dest))
+ else:
+ print(f"Note: Optional directory {src.name} not found, skipping")
+
+ for src, dest in required_data_dirs:
+ if not src.exists():
+ print(f"Error: Required directory {src.name} not found")
+ return False
+ datas.append((str(src), dest))📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| datas = [ | |
| (str(project_root / "app" / "static"), "app/static"), | |
| (str(project_root / "app" / "templates"), "app/templates"), | |
| (str(project_root / "web"), "web"), | |
| (str(project_root / "alembic"), "alembic"), | |
| ] | |
| datas = [] | |
| optional_data_dirs = [ | |
| (project_root / "app" / "static", "app/static"), | |
| (project_root / "app" / "templates", "app/templates"), | |
| ] | |
| required_data_dirs = [ | |
| (project_root / "web", "web"), | |
| (project_root / "alembic", "alembic"), | |
| ] | |
| for src, dest in optional_data_dirs: | |
| if src.exists(): | |
| datas.append((str(src), dest)) | |
| else: | |
| print(f"Note: Optional directory {src.name} not found, skipping") | |
| for src, dest in required_data_dirs: | |
| if not src.exists(): | |
| print(f"Error: Required directory {src.name} not found") | |
| return False | |
| datas.append((str(src), dest)) |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/build_executable.py` around lines 69 - 74, The datas list in
scripts/build_executable.py currently unconditionally references app/static and
app/templates which will cause PyInstaller to fail if those directories are
missing; update the code that builds the datas variable to check existence
(using Path.exists() on project_root / "app" / "static" and project_root / "app"
/ "templates") and only append the tuple for each directory if it exists,
leaving the other entries (web, alembic) unchanged so PyInstaller only packages
actual present paths.
| @echo off | ||
| REM Journiv Windows Launcher | ||
| REM This batch script starts Journiv with sensible defaults for Windows users | ||
|
|
||
| setlocal enabledelayedexpansion |
There was a problem hiding this comment.
Critical: Batch file must use Windows line endings (CRLF).
The static analysis correctly identifies that this batch file uses Unix line endings (LF). Windows batch parser has a 512-byte boundary bug that causes GOTO/CALL label parsing failures with LF-only line endings. This will cause the script to malfunction.
Ensure the file is saved with CRLF line endings. Configure git to handle this:
# Add to .gitattributes
*.bat text eol=crlfOr convert the file:
# Using dos2unix (install if needed)
unix2dos scripts/run.bat🧰 Tools
🪛 Blinter (1.0.112)
[error] 1-1: Unix line endings detected. Explanation: Batch file uses Unix line endings (LF-only) which can cause GOTO/CALL label parsing failures and script malfunction due to Windows batch parser 512-byte boundary bugs. Recommendation: Convert file to Windows line endings (CRLF). Use tools like dos2unix, notepad++, or configure git with 'git config core.autocrlf true'. Context: File uses Unix line endings (LF-only) - 125 LF sequences found
(E018)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/run.bat` around lines 1 - 5, The batch script run.bat is using LF
endings which breaks Windows batch parsing; open the file containing the lines
starting with "@echo off" and "setlocal enabledelayedexpansion" and convert its
line endings to CRLF, then commit the change; also add a .gitattributes entry to
force CRLF for .bat files (e.g., set *.bat to text eol=crlf) so future commits
preserve CRLF, or run a conversion tool like unix2dos before committing to
ensure the file is saved with Windows line endings.
| set SCRIPT_DIR=%~dp0 | ||
| set APP_ROOT=%SCRIPT_DIR%.. |
There was a problem hiding this comment.
Quote paths to handle spaces in directory names.
If a user extracts Journiv to a path with spaces (e.g., C:\Program Files\Journiv), the unquoted paths will break. The SET commands should use quoted syntax.
🔧 Proposed fix
REM Get the directory where this script is located
-set SCRIPT_DIR=%~dp0
-set APP_ROOT=%SCRIPT_DIR%..
+set "SCRIPT_DIR=%~dp0"
+set "APP_ROOT=%SCRIPT_DIR%.."📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| set SCRIPT_DIR=%~dp0 | |
| set APP_ROOT=%SCRIPT_DIR%.. | |
| set "SCRIPT_DIR=%~dp0" | |
| set "APP_ROOT=%SCRIPT_DIR%.." |
🧰 Tools
🪛 Blinter (1.0.112)
[error] 8-8: Unsafe SET command usage. Explanation: SET commands without proper validation or quoting can cause security issues. Recommendation: Always quote SET values and validate input: SET "var=safe value". Context: SET command value should be quoted for safety
(SEC002)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/run.bat` around lines 8 - 9, The SET commands for SCRIPT_DIR and
APP_ROOT must use quoted syntax so paths with spaces don't break: update the
lines that set SCRIPT_DIR and APP_ROOT in scripts/run.bat to use the safe form
(e.g., set "SCRIPT_DIR=%~dp0" and set "APP_ROOT=%SCRIPT_DIR%..") so the values
are assigned without including stray quotes and preserve spaces when expanded.
| env_content = f"""SECRET_KEY={secret_key} | ||
| DOMAIN_NAME=localhost:8000 | ||
| DB_DRIVER=sqlite | ||
| ENVIRONMENT=development | ||
| DEBUG=true | ||
| LOG_LEVEL=info | ||
| MEDIA_ROOT=/data/media | ||
| ENABLE_CORS=false | ||
| ACCESS_TOKEN_EXPIRE_MINUTES=15 | ||
| REFRESH_TOKEN_EXPIRE_DAYS=7 | ||
| """ |
There was a problem hiding this comment.
MEDIA_ROOT path will not work correctly for portable Windows deployment.
The MEDIA_ROOT=/data/media is an absolute Unix-style path. On Windows, this resolves to C:\data\media (root of the current drive), not the portable data folder next to the executable. This breaks the "portable" promise where all data stays in the extracted folder.
The ensure_data_directories function correctly creates app_root/data/media, but the .env file points elsewhere.
🐛 Proposed fix - use relative path
secret_key = generate_secret_key()
env_content = f"""SECRET_KEY={secret_key}
DOMAIN_NAME=localhost:8000
DB_DRIVER=sqlite
ENVIRONMENT=development
DEBUG=true
LOG_LEVEL=info
-MEDIA_ROOT=/data/media
+MEDIA_ROOT=./data/media
ENABLE_CORS=false
ACCESS_TOKEN_EXPIRE_MINUTES=15
REFRESH_TOKEN_EXPIRE_DAYS=7
"""Verify how the application handles relative vs absolute paths for MEDIA_ROOT:
#!/bin/bash
# Check how MEDIA_ROOT is used in the codebase
rg -n "MEDIA_ROOT|media_root" --type py -C 2🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/setup_defaults.py` around lines 15 - 25, The .env template in
scripts/setup_defaults.py sets MEDIA_ROOT to an absolute Unix path (/data/media)
which breaks portable Windows deployments; update the env_content generation to
use a relative media path (e.g. data/media or ./data/media) so it points to the
data/media folder created by ensure_data_directories; locate the env_content
variable and replace the MEDIA_ROOT line accordingly, ensuring it matches the
directory created by the ensure_data_directories function.
| 1. **Install PyInstaller**: `pip install pyinstaller` | ||
| 2. **Build**: `python scripts/build_executable.py` | ||
| 3. **Test**: Run `dist/journiv.exe` locally | ||
| 4. **Release**: Push tag `v*.*.*.` to trigger automated build |
There was a problem hiding this comment.
Typo in tag pattern.
The release tag pattern has an extra period: v*.*.*. should be v*.*.*.
📝 Fix typo
-4. **Release**: Push tag `v*.*.*.` to trigger automated build
+4. **Release**: Push tag `v*.*.*` to trigger automated build📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| 4. **Release**: Push tag `v*.*.*.` to trigger automated build | |
| 4. **Release**: Push tag `v*.*.*` to trigger automated build |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@WINDOWS_EXECUTABLE_IMPLEMENTATION.md` at line 129, The release tag pattern
contains an extra trailing period; update the tag string `v*.*.*.` to `v*.*.*`
(replace `v*.*.*.` with `v*.*.*`) so the automated build trigger uses the
correct tag pattern.
Description
Adds complete support for Windows users to download and run Journiv as a single-click executable, addressing GitHub Issue #500.
This feature eliminates the need for users to install Python, Docker, or manage configuration files. Everything is automated.
Type of Change
Features Implemented
Auto-Configuration System
.envfile automatically on first runSECRET_KEYusing cryptographic methodsWindows Launcher
PyInstaller Build System
CI/CD Pipeline
Comprehensive Documentation
Files Added/Modified
New Files (8)
scripts/build_executable.py- PyInstaller build script (164 lines)scripts/setup_defaults.py- Configuration auto-generator (46 lines)scripts/run.bat- Windows launcherscripts/validate_windows_build.py- Build validation tool (92 lines).github/workflows/build-windows-exe.yml- GitHub Actions workflowWINDOWS_GUIDE.md- Comprehensive guideQUICKSTART_WINDOWS.md- Quick start guideWINDOWS_EXECUTABLE_IMPLEMENTATION.md- Technical documentationModified Files (3)
pyproject.toml- Added build dependency group (PyInstaller)README.md- Added Windows executable section at topCONTRIBUTING.md- Added Windows build instructionsTesting
Manual testing checklist for maintainers:
How to Use This Feature
For End Users
journiv-windows-v*.zipfrom releasesjourniv.exeorjourniv.batFor Developers (Building)
pip install pyinstaller python scripts/build_executable.py dist/journiv.exe # Test locallyFor Releases (Automated)
Benefits
✅ No Python installation required
✅ No Docker required
✅ Auto-configuration on first run
✅ Automatic browser launch
✅ SQLite built-in
✅ Port conflict detection
✅ Local data storage with easy backup
✅ Portable (can move folder anywhere)
✅ Privacy-focused (no cloud sync)
Related Issues
Closes #500 - Single exe - One click execute
Summary
Release Notes
New Features
Documentation
Chores