Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 35 additions & 5 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,38 @@ body:
label: Mouser version
description: >
Which version of Mouser are you using? Check the release you downloaded
from the [releases page](https://github.com/TomBadash/MouseControl/releases),
or look at the zip/dmg filename (e.g. "v3.5.0").
placeholder: "e.g. v3.5.0"
from the [releases page](https://github.com/TomBadash/Mouser/releases),
the version shown inside the app, or the exact asset filename you ran.
If you are running from source or a self-built package, say that here too.
placeholder: "e.g. v3.5.3, source checkout, or self-built from main"
validations:
required: true

- type: dropdown
id: install-method
attributes:
label: How are you running Mouser?
options:
- Official GitHub release asset
- Run from source (`python main_qml.py`)
- Self-built app / executable
- Other
validations:
required: true

- type: input
id: build-details
attributes:
label: Build / asset details
description: >
Include the exact zip/dmg/exe filename, architecture, and commit / branch
if you built it yourself. This is critical when a source checkout works
but a packaged build does not.
placeholder: >
e.g. Mouser-macOS-intel.zip, Windows zip from v3.5.3, or main @ abc1234
validations:
required: false

- type: dropdown
id: os
attributes:
Expand Down Expand Up @@ -123,8 +149,12 @@ body:
label: Log file contents
description: >
Logs help a lot when diagnosing issues. Paste the relevant portion of
your log file (the last ~100 lines around the time of the bug are
usually enough).
your log file, including the startup lines if possible. The startup
lines identify the Mouser version, commit, and whether it was launched
from a packaged app or a source checkout.


The last ~100 lines around the time of the bug are usually enough.


**Log file locations:**
Expand Down
26 changes: 16 additions & 10 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,19 @@ on:
permissions:
contents: write

env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
MOUSER_VERSION: ${{ startsWith(github.ref, 'refs/tags/') && github.ref_name || '' }}
MOUSER_GIT_COMMIT: ${{ github.sha }}
MOUSER_GIT_DIRTY: "false"

jobs:
build-windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- uses: actions/setup-python@v5
- uses: actions/setup-python@v6
with:
python-version: "3.12"

Expand All @@ -27,7 +33,7 @@ jobs:
- name: Create archive
run: Compress-Archive -Path dist\Mouser -DestinationPath Mouser-Windows.zip

- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v7
with:
name: Mouser-Windows
path: Mouser-Windows.zip
Expand All @@ -52,9 +58,9 @@ jobs:
archive_name: Mouser-macOS-intel.zip
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- uses: actions/setup-python@v5
- uses: actions/setup-python@v6
with:
python-version: "3.12"
architecture: ${{ matrix.python_architecture }}
Expand All @@ -72,17 +78,17 @@ jobs:
- name: Create archive
run: cd dist && zip -r -y "../${{ matrix.archive_name }}" Mouser.app

- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v7
with:
name: ${{ matrix.artifact_name }}
path: ${{ matrix.archive_name }}

build-linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- uses: actions/setup-python@v5
- uses: actions/setup-python@v6
with:
python-version: "3.12"

Expand All @@ -98,7 +104,7 @@ jobs:
- name: Create archive
run: cd dist && zip -r -y ../Mouser-Linux.zip Mouser

- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v7
with:
name: Mouser-Linux
path: Mouser-Linux.zip
Expand All @@ -108,7 +114,7 @@ jobs:
needs: [build-windows, build-macos, build-linux]
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v8

- name: Create or update GitHub Release
env:
Expand Down
66 changes: 66 additions & 0 deletions Mouser-linux.spec
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,73 @@ Output: dist/Mouser/ (directory with Mouser executable + dependencies)
"""

import os
import json
import subprocess

ROOT = os.path.abspath(".")
BUILD_INFO_PATH = os.path.join(ROOT, "build", "mouser_build_info.json")


def _load_app_version() -> str:
version_path = os.path.join(ROOT, "core", "version.py")
namespace = {"__file__": version_path}
with open(version_path, encoding="utf-8") as version_file:
exec(version_file.read(), namespace)
return namespace["APP_VERSION"]


def _run_git(args):
try:
return subprocess.check_output(
["git", *args],
cwd=ROOT,
stderr=subprocess.DEVNULL,
text=True,
timeout=0.5,
).strip()
except (OSError, subprocess.SubprocessError):
return ""


def _git_dirty():
try:
result = subprocess.run(
["git", "status", "--porcelain", "--untracked-files=no"],
cwd=ROOT,
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL,
text=True,
timeout=0.5,
check=False,
)
except (OSError, subprocess.SubprocessError):
return False
return result.returncode == 0 and bool(result.stdout.strip())


def _write_build_info(version: str) -> str:
commit = os.environ.get("MOUSER_GIT_COMMIT", "").strip() or _run_git(["rev-parse", "HEAD"])
dirty_env = os.environ.get("MOUSER_GIT_DIRTY")
if dirty_env:
dirty = dirty_env.strip().lower() in {"1", "true", "yes", "on"}
else:
dirty = _git_dirty()

os.makedirs(os.path.dirname(BUILD_INFO_PATH), exist_ok=True)
with open(BUILD_INFO_PATH, "w", encoding="utf-8") as build_info_file:
json.dump(
{
"version": version,
"commit": commit,
"dirty": dirty,
},
build_info_file,
)
return BUILD_INFO_PATH


APP_VERSION = _load_app_version()
BUILD_INFO_DATA = _write_build_info(APP_VERSION)

a = Analysis(
["main_qml.py"],
Expand All @@ -19,6 +84,7 @@ a = Analysis(
datas=[
(os.path.join(ROOT, "ui", "qml"), os.path.join("ui", "qml")),
(os.path.join(ROOT, "images"), "images"),
(BUILD_INFO_DATA, "."),
],
hiddenimports=[
"hid",
Expand Down
70 changes: 68 additions & 2 deletions Mouser-mac.spec
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ environment supports it:
"""

import os
import json
import subprocess

ROOT = os.path.abspath(".")
COMMITTED_ICON = os.path.join(ROOT, "images", "AppIcon.icns")
GENERATED_ICON = os.path.join(ROOT, "build", "macos", "Mouser.icns")
BUILD_INFO_PATH = os.path.join(ROOT, "build", "mouser_build_info.json")
TARGET_ARCH = os.environ.get("PYINSTALLER_TARGET_ARCH", "").strip() or None
if TARGET_ARCH not in (None, "arm64", "x86_64", "universal2"):
raise SystemExit(
Expand All @@ -27,13 +30,76 @@ else:
ICON_PATH = None
BUNDLE_ID = "io.github.tombadash.mouser"


def _load_app_version() -> str:
version_path = os.path.join(ROOT, "core", "version.py")
namespace = {"__file__": version_path}
with open(version_path, encoding="utf-8") as version_file:
exec(version_file.read(), namespace)
return namespace["APP_VERSION"]


def _run_git(args):
try:
return subprocess.check_output(
["git", *args],
cwd=ROOT,
stderr=subprocess.DEVNULL,
text=True,
timeout=0.5,
).strip()
except (OSError, subprocess.SubprocessError):
return ""


def _git_dirty():
try:
result = subprocess.run(
["git", "status", "--porcelain", "--untracked-files=no"],
cwd=ROOT,
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL,
text=True,
timeout=0.5,
check=False,
)
except (OSError, subprocess.SubprocessError):
return False
return result.returncode == 0 and bool(result.stdout.strip())


def _write_build_info(version: str) -> str:
commit = os.environ.get("MOUSER_GIT_COMMIT", "").strip() or _run_git(["rev-parse", "HEAD"])
dirty_env = os.environ.get("MOUSER_GIT_DIRTY")
if dirty_env:
dirty = dirty_env.strip().lower() in {"1", "true", "yes", "on"}
else:
dirty = _git_dirty()

os.makedirs(os.path.dirname(BUILD_INFO_PATH), exist_ok=True)
with open(BUILD_INFO_PATH, "w", encoding="utf-8") as build_info_file:
json.dump(
{
"version": version,
"commit": commit,
"dirty": dirty,
},
build_info_file,
)
return BUILD_INFO_PATH


APP_VERSION = _load_app_version()
BUILD_INFO_DATA = _write_build_info(APP_VERSION)

a = Analysis(
["main_qml.py"],
pathex=[ROOT],
binaries=[],
datas=[
(os.path.join(ROOT, "ui", "qml"), os.path.join("ui", "qml")),
(os.path.join(ROOT, "images"), "images"),
(BUILD_INFO_DATA, "."),
],
hiddenimports=[
"hid",
Expand Down Expand Up @@ -242,8 +308,8 @@ app = BUNDLE(
info_plist={
"CFBundleDisplayName": "Mouser",
"CFBundleName": "Mouser",
"CFBundleShortVersionString": "3.5.3",
"CFBundleVersion": "3.5.3",
"CFBundleShortVersionString": APP_VERSION,
"CFBundleVersion": APP_VERSION,
"LSMinimumSystemVersion": "12.0",
"LSUIElement": True,
"NSHighResolutionCapable": True,
Expand Down
Loading
Loading