Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature(installer): update installer version and improve desktop version launcher #2157

Merged
merged 45 commits into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
cf4d0f9
update installer to v0.3.3
sylvlecl Sep 25, 2024
85899f4
build desktop
sylvlecl Sep 25, 2024
531de20
test fixes
sylvlecl Sep 25, 2024
1f61684
update installer
sylvlecl Sep 25, 2024
b9ed900
update installer
sylvlecl Sep 25, 2024
82dcfd9
update installer
sylvlecl Sep 25, 2024
e2c9606
update installer
sylvlecl Sep 26, 2024
ddcabd2
update installer
sylvlecl Sep 26, 2024
fad5312
fix: alembic files added as data to the package, not as script
sylvlecl Sep 27, 2024
eb510e7
fix syntax
sylvlecl Sep 27, 2024
dffd049
fix syntax
sylvlecl Sep 27, 2024
07bd40e
update installer
sylvlecl Sep 27, 2024
cd05210
update installer
sylvlecl Sep 27, 2024
9c34f70
update installer
sylvlecl Sep 27, 2024
1d802f8
update installer
sylvlecl Sep 27, 2024
6786b64
update installer
sylvlecl Sep 27, 2024
601b456
update installer
sylvlecl Sep 27, 2024
d6cb772
improve GUI code organization, add notification
sylvlecl Sep 30, 2024
f53e676
update installer
sylvlecl Sep 30, 2024
cb7b55b
try fixing systray disparition
sylvlecl Sep 30, 2024
ea8ec2c
fix: attache systray icon to application
sylvlecl Sep 30, 2024
f77e700
fix: keep system tray menu alive
sylvlecl Sep 30, 2024
afe6c78
update installer
sylvlecl Sep 30, 2024
a643ac4
fix: keep alive tray
sylvlecl Sep 30, 2024
bb04028
fix: show context menu on left click, open app on double click
sylvlecl Sep 30, 2024
a067642
fix: add a check that the server is started before trying to start it
sylvlecl Sep 30, 2024
a0e1268
feature: try more modern toast lib
sylvlecl Sep 30, 2024
92c76ce
fix: fix behaviour when server already running
sylvlecl Oct 1, 2024
609cf9a
fix: remove double click, does not work
sylvlecl Oct 1, 2024
4fe01b8
fix: update installer for v2.18 config
sylvlecl Oct 1, 2024
b2246f1
fix: update installer
sylvlecl Oct 1, 2024
d8e9654
Merge remote-tracking branch 'origin/dev' into feature/update-installer
sylvlecl Oct 1, 2024
42e6574
fix: update installer
sylvlecl Oct 2, 2024
ca61bea
fix: speedup startup when getting version
sylvlecl Oct 2, 2024
2efdeb6
fix: remove unwanted log
sylvlecl Oct 2, 2024
a4c3790
fix: update installer
sylvlecl Oct 7, 2024
55af4ba
fix: fix healthcheck URL
sylvlecl Oct 8, 2024
7cb6225
fix: update installer to not throw when target dir does not exit
sylvlecl Oct 8, 2024
1a02de5
doc: add some docstrings
sylvlecl Oct 9, 2024
b796a9a
update installer for logs
sylvlecl Oct 10, 2024
df27b63
update installer for windows proxy
sylvlecl Oct 11, 2024
ccbae1e
update installer to main branch
sylvlecl Oct 11, 2024
d454c68
Merge remote-tracking branch 'origin/dev' into feature/update-installer
sylvlecl Oct 11, 2024
d1951e4
fix: copyright headers
sylvlecl Oct 11, 2024
fba79d3
fix: revert change to deploy.yaml
sylvlecl Oct 11, 2024
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
1 change: 1 addition & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
branches:
- "master"
- "hotfix/**"
- "feature/update-installer"
Copy link
Contributor

Choose a reason for hiding this comment

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

Should be removed or we could add more generic branch name such as installer/** or deploy/**


jobs:
binary:
Expand Down
27 changes: 17 additions & 10 deletions AntaresWebLinux.spec
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
# -*- mode: python ; coding: utf-8 -*-
from pathlib import Path
from PyInstaller.utils.hooks import collect_dynamic_libs

block_cipher = None

# We need to analyze all alembic files to be sure the migration phase works fine
migrations_dir = Path('alembic/versions')
migration_files = [str(f) for f in migrations_dir.iterdir() if f.is_file() and f.suffix == '.py']
# We need to analyze all alembic files to be sure the migration phase works fine:
# alembic loads version files by their path, so we need to add them as "data" to the package,
# but all the dependencies they use need to be included also, wo we need to perform a
Copy link
Contributor

Choose a reason for hiding this comment

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

typo: so we need

# dedicated analyse for this.
versions_dir = Path('alembic/versions')
versions_files = [str(f) for f in versions_dir.iterdir() if f.is_file() and f.suffix == '.py']
alembic_analysis = Analysis(["alembic/env.py"] + versions_files)

binaries = [('./alembic.ini', './alembic.ini')] + collect_dynamic_libs('tables')

antares_web_server_a = Analysis(['antarest/gui.py', 'alembic/env.py'] + migration_files,
antares_web_server_a = Analysis(['antarest/gui.py'],
pathex=[],
binaries=binaries,
datas=[('./resources', './resources'), ('./alembic', './alembic')],
binaries=[],
datas=[('./resources', './resources'), ('./alembic', './alembic'), ('./alembic.ini', './')],
Copy link
Contributor

Choose a reason for hiding this comment

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

Why did you add ./ ?

Copy link
Member Author

Choose a reason for hiding this comment

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

Before we had this: binaries = [('./alembic.ini', './alembic.ini')] which created a folder called "alembic.ini" in which the file was placed.

Now the file is correctly placed at the root

hiddenimports=[
'cmath',
'antarest.dbmodel',
'plyer.platforms.linux',
'plyer.platforms.linux.notification',
'pythonjsonlogger.jsonlogger',
'tables',
],
hookspath=['extra-hooks'],
hooksconfig={},
Expand All @@ -29,8 +31,13 @@ antares_web_server_a = Analysis(['antarest/gui.py', 'alembic/env.py'] + migratio
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
antares_web_server_pyz = PYZ(antares_web_server_a.pure, antares_web_server_a.zipped_data,

all_python = antares_web_server_a.pure + alembic_analysis.pure
all_zipped_data = antares_web_server_a.zipped_data + alembic_analysis.zipped_data

antares_web_server_pyz = PYZ(all_python, all_zipped_data,
cipher=block_cipher)

antares_web_server_exe = EXE(antares_web_server_pyz,
antares_web_server_a.scripts,
[],
Expand Down
27 changes: 17 additions & 10 deletions AntaresWebWin.spec
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
# -*- mode: python ; coding: utf-8 -*-
from pathlib import Path
from PyInstaller.utils.hooks import collect_dynamic_libs

block_cipher = None

# We need to analyze all alembic files to be sure the migration phase works fine
migrations_dir = Path('alembic/versions')
migration_files = [str(f) for f in migrations_dir.iterdir() if f.is_file() and f.suffix == '.py']
# We need to analyze all alembic files to be sure the migration phase works fine:
# alembic loads version files by their path, so we need to add them as "data" to the package,
# but all the dependencies they use need to be included also, wo we need to perform a
Copy link
Contributor

Choose a reason for hiding this comment

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

same typo

# dedicated analyse for this.
versions_dir = Path('alembic/versions')
versions_files = [str(f) for f in versions_dir.iterdir() if f.is_file() and f.suffix == '.py']
alembic_analysis = Analysis(["alembic/env.py"] + versions_files)

binaries = [('./alembic.ini', './alembic.ini')] + collect_dynamic_libs('tables')

antares_web_server_a = Analysis(['antarest/gui.py', 'alembic/env.py'] + migration_files,
antares_web_server_a = Analysis(['antarest/gui.py'],
pathex=[],
binaries=binaries,
datas=[('./resources', './resources'), ('./alembic', './alembic')],
binaries=[],
datas=[('./resources', './resources'), ('./alembic', './alembic'), ('./alembic.ini', './')],
hiddenimports=[
'cmath',
'antarest.dbmodel',
'plyer.platforms.win',
'plyer.platforms.win.notification',
'pythonjsonlogger.jsonlogger',
'tables',
],
hookspath=['extra-hooks'],
hooksconfig={},
Expand All @@ -29,8 +31,13 @@ antares_web_server_a = Analysis(['antarest/gui.py', 'alembic/env.py'] + migratio
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
antares_web_server_pyz = PYZ(antares_web_server_a.pure, antares_web_server_a.zipped_data,

all_python = antares_web_server_a.pure + alembic_analysis.pure
all_zipped_data = antares_web_server_a.zipped_data + alembic_analysis.zipped_data

antares_web_server_pyz = PYZ(all_python, all_zipped_data,
cipher=block_cipher)

antares_web_server_exe = EXE(antares_web_server_pyz,
antares_web_server_a.scripts,
[],
Expand Down
158 changes: 116 additions & 42 deletions antarest/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@
import platform
import time
import webbrowser
from dataclasses import dataclass
from multiprocessing import Process
from pathlib import Path
from threading import Thread

import httpx
import uvicorn
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QAction, QApplication, QMenu, QSystemTrayIcon
from PyQt5.QtGui import QCursor, QIcon
from PyQt5.QtWidgets import QApplication, QMenu, QSystemTrayIcon

from antarest.core.utils.utils import get_local_path
from antarest.main import fastapi_app, parse_arguments
Expand All @@ -39,67 +41,139 @@ def run_server(config_file: Path) -> None:
uvicorn.run(app, host="127.0.0.1", port=8080)


def start_server(config_file: Path) -> Process:
server = multiprocessing.Process(
target=run_server,
args=(config_file,),
)
server.start()
return server


def open_app() -> None:
webbrowser.open("http://localhost:8080")


def main() -> None:
multiprocessing.freeze_support()
arguments = parse_arguments()
@dataclass(frozen=True)
class AntaresSystrayApp:
"""
Used to keep ownership of root Qt objects.
QMenu can only be owned by QWidgets, but we don't have one.
"""

app: QApplication
menu: QMenu


def create_systray_app() -> AntaresSystrayApp:
"""
Creates the small application that allows to open
the browser or shutdown the server.
"""
app = QApplication([])
app.setQuitOnLastWindowClosed(False)

# Adding an icon
icon = QIcon(str(RESOURCE_PATH / "webapp" / "logo16.png"))
# Adding item on the menu bar
tray = QSystemTrayIcon(icon, app)
tray.setToolTip("AntaresWebServer")

# Creating the options
menu = QMenu()
open_app_action = menu.addAction("Open application")
assert open_app_action is not None
open_app_action.triggered.connect(open_app)
# To quit the app
quit_action = menu.addAction("Quit")
assert quit_action is not None
quit_action.triggered.connect(app.quit)

# Adding options to the System Tray
def handle_action(reason: int) -> None:
Copy link
Contributor

Choose a reason for hiding this comment

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

This function/design is really weird but I'm far from understanding this file

"""
Shows context menu also on left click
"""
if reason == QSystemTrayIcon.Trigger: # type: ignore
menu = tray.contextMenu()
assert menu is not None
menu.popup(QCursor.pos())

tray.setContextMenu(menu)
tray.activated.connect(handle_action)

tray.setVisible(True)

return AntaresSystrayApp(app, menu)


def monitor_server_process(server: Process, app: QApplication) -> None:
"""
Quits the application when server process ends.
"""
server.join()
app.quit()


def setup_exit_application_on_server_end(server: Process, app: QApplication) -> None:
Thread(target=monitor_server_process, args=(server, app)).start()


def check_server_started() -> bool:
with contextlib.suppress(httpx.ConnectError):
res = httpx.get("http://localhost:8080/health")
if res.status_code == 200:
return True
return False


def wait_for_server_start() -> None:
for _ in range(30, 0, -1):
if check_server_started():
break
time.sleep(1)


def notification_popup(message: str, threaded: bool = True) -> None:
if platform.system() == "Windows":
# noinspection PyPackageRequirements
from win10toast import ToastNotifier # type: ignore

toaster = ToastNotifier()
toaster.show_toast(
"AntaresWebServer",
"Antares Web Server started, you can manage the application within the systray app",
message,
icon_path=RESOURCE_PATH / "webapp" / "favicon.ico",
threaded=True,
threaded=threaded,
)
else:
from plyer import notification # type: ignore

notification.notify(
title="AntaresWebServer",
message="Antares Web Server started, you can manage the application within the systray app",
message=message,
app_name="AntaresWebServer",
app_icon=RESOURCE_PATH / "webapp" / "favicon.ico",
app_icon=str(RESOURCE_PATH / "webapp" / "favicon.ico"),
timeout=600,
)
app = QApplication([])
app.setQuitOnLastWindowClosed(False)
# Adding an icon
icon = QIcon(str(RESOURCE_PATH / "webapp" / "logo16.png"))
# Adding item on the menu bar
tray = QSystemTrayIcon()
tray.setIcon(icon)
tray.setVisible(True)
# Creating the options
menu = QMenu()
open_app_action = QAction("Open application")
menu.addAction(open_app_action)
open_app_action.triggered.connect(open_app)
# To quit the app
quit_action = QAction("Quit")
quit_action.triggered.connect(app.quit)
menu.addAction(quit_action)
# Adding options to the System Tray
tray.setContextMenu(menu)
app.processEvents()
tray.setToolTip("AntaresWebServer")
server = Process(
target=run_server,
args=(arguments.config_file,),
)
server.start()
for _ in range(30, 0, -1):
with contextlib.suppress(httpx.ConnectError):
res = httpx.get("http://localhost:8080")
if res.status_code == 200:
break
time.sleep(1)
app.exec_()


def main() -> None:
multiprocessing.freeze_support()

arguments = parse_arguments()
if check_server_started():
notification_popup(
"Antares Web Server already running, you can manage the application within the system tray.", threaded=False
)
return
notification_popup("Starting Antares Web Server...")
systray_app = create_systray_app()
server = start_server(arguments.config_file)
setup_exit_application_on_server_end(server, systray_app.app)
wait_for_server_start()
notification_popup("Antares Web Server started, you can manage the application within the system tray.")
systray_app.app.exec_()
server.kill()


Expand Down
Loading