Skip to content

Add desktop client, cx_Freeze packaging and CI workflow for MSI/DMG builds#1

Merged
YZDame merged 1 commit into
mainfrom
codex/review-project-structure-for-improvements
May 1, 2026
Merged

Add desktop client, cx_Freeze packaging and CI workflow for MSI/DMG builds#1
YZDame merged 1 commit into
mainfrom
codex/review-project-structure-for-improvements

Conversation

@YZDame
Copy link
Copy Markdown
Owner

@YZDame YZDame commented May 1, 2026

Motivation

  • Provide a native-style desktop launcher and installer artifacts (MSI for Windows, DMG for macOS) to simplify usage without requiring manual Python execution.
  • Add reproducible CI packaging to produce and archive installer artifacts for releases and manual triggers.
  • Modernize packaging/runtime constraints and simplify the installed CLI entrypoint import behavior.

Description

  • Add a desktop launcher desktop_client.py that starts the local Flask app on a free port and opens the default browser, and ensure graceful shutdown of the embedded server.
  • Add setup_desktop.py (cx_Freeze config) and desktop_packaging.txt with packaging instructions, and update README.md to document desktop packaging and project structure changes.
  • Add GitHub Actions workflow ./github/workflows/desktop-packages.yml to build MSI on windows-latest and DMG on macos-latest, perform smoke checks, and upload build artifacts.
  • Update packaging and runtime bits: bump python_requires to >=3.10 and add modern classifiers in setup.py, and simplify the installed CLI entrypoint in wemath2md/cli.py to use import_module; reorder token/config validation flow in main.py for clarity.

Testing

  • No automated tests were executed as part of this rollout.
  • A CI job desktop-packages was added to build and upload installer artifacts on push/dispatch/releases, which will run on GitHub Actions when triggered.

Codex Task

Copilot AI review requested due to automatic review settings May 1, 2026 09:48
@codecov-commenter
Copy link
Copy Markdown

Welcome to Codecov 🎉

Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests.

ℹ️ You can also turn on project coverage checks and project coverage reporting on Pull Request comment

Thanks for integrating Codecov - We've got you covered ☂️

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a packaged “desktop launcher” distribution path (MSI on Windows, DMG on macOS) using cx_Freeze, along with a CI workflow to build and upload installer artifacts, and it updates Python packaging/entrypoints to better support installed execution.

Changes:

  • Added a desktop launcher (desktop_client.py) and cx_Freeze config (setup_desktop.py) plus packaging instructions.
  • Added a GitHub Actions workflow to build MSI/DMG installers and upload them as artifacts.
  • Updated runtime/packaging metadata (Python >= 3.10) and simplified the installed CLI entrypoint import behavior; adjusted CLI token/config validation ordering.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
wemath2md/cli.py Simplifies the installed console entrypoint to import and call main.main.
setup_desktop.py Adds cx_Freeze configuration for building MSI/DMG desktop installers.
setup.py Raises python_requires to >=3.10 and adds modern Python classifiers.
main.py Reorders token/config validation to allow --dry-run to exit before token enforcement.
desktop_packaging.txt Documents local and CI packaging steps for MSI/DMG builds.
desktop_client.py Introduces a desktop launcher that starts the Flask app on a free port and opens a browser.
README.md Fixes config.py link and documents project structure + desktop packaging.
.github/workflows/desktop-packages.yml Adds CI workflow to build and publish desktop installer artifacts.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread desktop_client.py
Comment on lines +45 to +46
def main() -> None:
os.makedirs("templates", exist_ok=True)
Copy link

Copilot AI May 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

desktop_client.main() writes runtime state relative to the current working directory (os.makedirs("templates") and setup_logger(... log_file=...) which creates logs/ under the CWD). In a packaged MSI/DMG, the process CWD is often not the project directory (e.g., System32 on Windows) and may be non-writable, which can crash startup or cause output/logs to be written to an unexpected location. Consider removing the templates mkdir, explicitly setting a predictable working directory, and writing logs/output to a user-writable app data directory (and ensuring the web app’s output/download paths are consistent with that).

Suggested change
def main() -> None:
os.makedirs("templates", exist_ok=True)
def _get_runtime_dir() -> str:
app_name = "WeMath2MD"
if os.name == "nt":
base_dir = os.environ.get("APPDATA") or os.path.expanduser("~")
return os.path.join(base_dir, app_name)
if os.sys.platform == "darwin":
return os.path.join(
os.path.expanduser("~/Library/Application Support"),
app_name,
)
base_dir = os.environ.get("XDG_STATE_HOME") or os.path.expanduser("~/.local/state")
return os.path.join(base_dir, app_name)
def main() -> None:
runtime_dir = _get_runtime_dir()
os.makedirs(runtime_dir, exist_ok=True)
os.chdir(runtime_dir)

Copilot uses AI. Check for mistakes.
Comment thread setup_desktop.py
Comment on lines +23 to +28
setup(
name="WeMath2MD",
version="1.0.0",
description="WeMath2MD Desktop Client",
options={"build_exe": build_exe_options},
executables=executables,
Copy link

Copilot AI May 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The desktop installer config hardcodes version="1.0.0", which can easily drift from the library version in setup.py and produce confusing/incorrect installer metadata. Consider sourcing the version from a single place (e.g., import it from the package/module metadata) so the PyPI package and desktop installers stay in sync.

Copilot uses AI. Check for mistakes.
- 'v*'
paths:
- 'desktop_client.py'
- 'setup_desktop.py'
Copy link

Copilot AI May 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The workflow’s paths filter doesn’t include templates/** (or other files bundled into the desktop build). Changes to the UI templates won’t trigger a packaging build on push, which can lead to stale MSI/DMG artifacts. Consider expanding the paths list to include templates/** (and any other inputs that affect the frozen app).

Suggested change
- 'setup_desktop.py'
- 'setup_desktop.py'
- 'templates/**'

Copilot uses AI. Check for mistakes.
Comment thread main.py
Comment on lines 178 to 179
API_TOKEN = get_mineru_token()

Copy link

Copilot AI May 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment says the API token is only needed for actual conversion, but get_mineru_token() is still called before URL collection and before the --dry-run early-exit. To match the intent (and avoid reading/validating token-related config in dry-run), consider moving token retrieval to after the dry-run branch.

Suggested change
API_TOKEN = get_mineru_token()
class _LazyMineruToken:
def __init__(self):
self._value = None
def _get_value(self):
if self._value is None:
self._value = get_mineru_token()
return self._value
def __str__(self):
return self._get_value()
def __repr__(self):
return repr(self._get_value())
def __getattr__(self, name):
return getattr(self._get_value(), name)
def __len__(self):
return len(self._get_value())
def __bool__(self):
return bool(self._get_value())
def __eq__(self, other):
return self._get_value() == other
def __hash__(self):
return hash(self._get_value())
def __contains__(self, item):
return item in self._get_value()
def __iter__(self):
return iter(self._get_value())
def __format__(self, format_spec):
return format(self._get_value(), format_spec)
API_TOKEN = _LazyMineruToken()

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8aaa345c57

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread setup_desktop.py
Executable(
"desktop_client.py",
target_name="WeMath2MD",
base="Win32GUI", # ignored on non-Windows
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Make cx_Freeze base conditional on platform

The packaging matrix builds macOS artifacts (.github/workflows/desktop-packages.yml runs bdist_dmg on macos-latest), but this executable is hard-coded to base="Win32GUI", which is a Windows-only GUI base in cx_Freeze examples and should be None/platform-specific on non-Windows builds. Keeping this value unconditional can cause macOS packaging to fail or use an unsupported launcher configuration, breaking the DMG job.

Useful? React with 👍 / 👎.

Comment on lines +9 to +13
paths:
- 'desktop_client.py'
- 'setup_desktop.py'
- '.github/workflows/desktop-packages.yml'
- 'requirements.txt'
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Include packaged template files in workflow path filters

This workflow only triggers on pushes that touch a narrow file list, but the desktop installer content also includes templates (see setup_desktop.py include_files). If templates/index.html changes, the desktop-packages workflow will be skipped on main/master pushes, so packaging regressions in shipped UI assets won't be caught until a manual run or release.

Useful? React with 👍 / 👎.

@YZDame YZDame merged commit c948209 into main May 1, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants