Skip to content

Commit 4410824

Browse files
authored
Merge pull request #7 from SHIINASAMA/dev-updater
Unify cross-platform update flow (macOS self-update still disabled)
2 parents 26354d5 + 0b7c51c commit 4410824

File tree

17 files changed

+478
-172
lines changed

17 files changed

+478
-172
lines changed

.github/workflows/release.yml

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ on:
1111
permissions:
1212
contents: write
1313

14+
env:
15+
APP_NAME: App
16+
1417
jobs:
1518
check-version:
1619
runs-on: ubuntu-latest
@@ -51,7 +54,7 @@ jobs:
5154

5255
- uses: actions/setup-python@v5
5356
with:
54-
python-version: '3.11'
57+
python-version: "3.11"
5558
- uses: yezz123/setup-uv@v4
5659

5760
- name: Before script (Linux)
@@ -76,29 +79,39 @@ jobs:
7679
if: runner.os == 'macOS'
7780
run: |
7881
brew install create-dmg
79-
xattr -cr build/*.app
80-
create-dmg --volname App --window-pos 200 120 --window-size 600 400 --icon-size 100 --app-drop-link 450 185 App.dmg ./build/*.app
82+
xattr -cr build/${{ env.APP_NAME }}.app
83+
./build-dmg.sh
84+
mkdir -p release
85+
cd build
86+
zip -r \
87+
../release/${{ env.APP_NAME }}-${{ matrix.name }}-${{ matrix.arch }}-${{ github.ref_name }}.zip \
88+
${{ env.APP_NAME }}.app/
8189
8290
- name: Package artifact (Linux)
8391
if: runner.os == 'Linux'
8492
run: |
85-
mv ./build/App ./Package
86-
zip -r App-${{ matrix.name }}-${{ matrix.arch }}.zip Package/
93+
mv ./build/${{ env.APP_NAME }} ./build/${{ env.APP_NAME }}-${{ matrix.name }}-${{ matrix.arch }}
94+
mkdir -p release
95+
cd build
96+
zip -r \
97+
../release/${{ env.APP_NAME }}-${{ matrix.name }}-${{ matrix.arch }}-${{ github.ref_name }}.zip \
98+
${{ env.APP_NAME }}-${{ matrix.name }}-${{ matrix.arch }}/
8799
88100
- name: Package artifact (Windows)
89101
if: runner.os == 'Windows'
90102
shell: pwsh
91103
run: |
92-
Move-Item -Path ./build/App -Destination ./Package
93-
Compress-Archive -Path .\Package\ -DestinationPath App-${{ matrix.name }}-${{ matrix.arch }}.zip
104+
Move-Item -Path ./build/${{ env.APP_NAME }} -Destination ./build/${{ env.APP_NAME }}-${{ matrix.name }}-${{ matrix.arch }}
105+
New-Item -ItemType Directory -Path release
106+
Compress-Archive -Path ./build/${{ env.APP_NAME }}-${{ matrix.name }}-${{ matrix.arch }} `
107+
-DestinationPath ./release/${{ env.APP_NAME }}-${{ matrix.name }}-${{ matrix.arch }}-${{ github.ref_name }}.zip
94108
95109
- name: Upload artifact
96110
uses: actions/upload-artifact@v4
97111
with:
98112
name: App-${{ matrix.name }}-${{ matrix.arch }}
99113
path: |
100-
./App-*
101-
./*.dmg
114+
./release/*
102115
103116
build_whl:
104117
runs-on: ubuntu-latest
@@ -110,7 +123,7 @@ jobs:
110123

111124
- uses: actions/setup-python@v5
112125
with:
113-
python-version: '3.11'
126+
python-version: "3.11"
114127
- uses: yezz123/setup-uv@v4
115128

116129
- name: Setup, install dependencies, build

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/.idea
22
/build
3+
/release
34
/App.lock
45
/updater.json
56

.gitlab-ci.yml

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ stages:
44
- build
55
- release
66

7+
variables:
8+
APP_NAME: "App"
9+
710
check_version:
811
stage: validate
912
image: reg.mikumikumi.xyz/mirror/bash:latest
@@ -27,13 +30,12 @@ check_version:
2730
REPORT_FILE: report-unit.xml
2831
rules:
2932
- changes:
30-
- app/**
31-
- tool/**
33+
- app/**
3234
script:
33-
- pip install uv
34-
- uv sync
35-
- uv run pyside-cli build --stage rc
36-
- uv run pyside-cli test -- -v --junitxml=${REPORT_FILE}
35+
- pip install uv
36+
- uv sync
37+
- uv run pyside-cli build --stage rc
38+
- uv run pyside-cli test -- -v --junitxml=${REPORT_FILE}
3739
artifacts:
3840
reports:
3941
junit: report-*.xml
@@ -44,13 +46,12 @@ check_version:
4446
- pip install uv
4547
- uv sync
4648
- uv run pyside-cli build --onedir
47-
- mv ./build/App ./Package
4849
rules:
4950
- if: $CI_COMMIT_TAG
5051
artifacts:
5152
name: "build-${CI_COMMIT_SHA}"
5253
paths:
53-
- ./release/App-*
54+
- ./release/*
5455
expire_in: 3 days
5556

5657
test_windows_x64:
@@ -74,23 +75,26 @@ build_whl:
7475
- uv sync
7576
- uv run pyside-cli build --stage rc
7677
- uv build --no-sources
77-
- mkdir release
78+
- mkdir -p release
7879
- mv dist/*.whl release
7980
rules:
8081
- if: $CI_COMMIT_TAG
8182
artifacts:
8283
name: "build-${CI_COMMIT_SHA}"
8384
paths:
84-
- ./release/*.whl
85+
- ./release/*
8586
expire_in: 3 days
8687

8788
build_windows_x64:
8889
extends: .build
8990
tags:
9091
- windows
9192
after_script:
93+
- Move-Item -Path ./build/${APP_NAME} -Destination ./build/${APP_NAME}-windows-x64
9294
- New-Item -Path release -Type Directory
93-
- Compress-Archive -Path .\Package\ -DestinationPath .\release\App-windows-x64.zip -CompressionLevel Optimal
95+
- Compress-Archive -Path ./build/${APP_NAME}-windows-x64/ `
96+
-DestinationPath ./release/${APP_NAME}-windows-x64-${CI_COMMIT_TAG}.zip `
97+
-CompressionLevel Optimal
9498

9599
build_linux_x64:
96100
extends: .build
@@ -99,8 +103,31 @@ build_linux_x64:
99103
- apt-get update
100104
- apt-get install -y patchelf
101105
after_script:
102-
- mkdir release
103-
- tar -czf ./release/App-linux-x64.tar.gz Package/
106+
- mv ./build/${APP_NAME} ./build/${APP_NAME}-linux-x64
107+
- mkdir -p release
108+
- cd build
109+
- tar -czf ../release/${APP_NAME}-linux-x64-${CI_COMMIT_TAG}.tar.gz \
110+
${APP_NAME}-linux-x64/
111+
112+
# build_macos_arm64:
113+
# stage: build
114+
# tags:
115+
# - macos
116+
# script:
117+
# - pip install uv
118+
# - uv sync
119+
# - uv run pyside-cli build
120+
# - xattr -cr build/${APP_NAME}.app
121+
# - brew install create-dmg
122+
# - ./build-dmg.sh
123+
# - mkdir -p release
124+
# - cd build
125+
# - zip -r "../release/${APP_NAME}-macos-arm64-${CI_COMMIT_TAG}.zip" "./${APP_NAME}.app"
126+
# artifacts:
127+
# name: "build-${CI_COMMIT_SHA}"
128+
# paths:
129+
# - ./release/*
130+
# expire_in: 3 days
104131

105132
# bug: glab issues in private repositories
106133
# https://gitlab.com/gitlab-org/cli/-/issues/7859
@@ -115,11 +142,12 @@ release:
115142
artifacts: true
116143
- job: build_linux_x64
117144
artifacts: true
145+
# - job: build_macos_arm64
146+
# artifacts: true
118147
rules:
119148
- if: $CI_COMMIT_TAG
120149
script:
121150
- export GITLAB_HOST=$CI_SERVER_HOST
122151
- export GITLAB_TOKEN=$CI_JOB_TOKEN
123152
- glab auth login --job-token $CI_JOB_TOKEN --hostname $CI_SERVER_HOST --api-protocol $CI_SERVER_PROTOCOL
124153
- glab release create $CI_COMMIT_TAG --ref $CI_COMMIT_TAG -n "Release ${CI_COMMIT_TAG}" -F CHANGE.md ./release/*
125-

app/__main__.py

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
import asyncio
2-
import os.path
32
import sys
3+
import os
44

5-
from PySide6.QtCore import QTranslator, QLockFile, QCoreApplication
5+
from PySide6.QtCore import QTranslator, QLockFile
66
from qasync import QApplication, run
77

8-
from app.builtin.gitlab_updater import GitlabUpdater
98
from app.builtin.locale import detect_system_ui_language
10-
from app.builtin.misc import running_in_bundle
9+
from app.builtin.utils import get_updater, init_app, running_in_bundle
10+
from app.builtin.paths import AppPaths
1111
from app.main_window import MainWindow
12-
from qdarktheme import enable_hi_dpi
1312

1413

1514
async def task():
@@ -25,29 +24,23 @@ async def task():
2524

2625

2726
def main(enable_updater: bool = True):
27+
# init QApplication
28+
app = init_app()
29+
paths = AppPaths()
30+
2831
# init updater, updater will remove some arguments
2932
# and do update logic
30-
# updater = GithubUpdater()
31-
# updater.project_name = "SHIINASAMA/pyside_template"
32-
updater = GitlabUpdater()
33-
updater.base_url = "https://gitlab.mikumikumi.xyz"
34-
updater.project_name = "kaoru/pyside_template"
33+
updater = get_updater()
3534
# self-updating is not available on macOS
3635
updater.is_enable = False if running_in_bundle else enable_updater
3736

3837
# override updater config
39-
if os.path.exists("updater.json"):
40-
updater.load_from_file_and_override("updater.json")
41-
42-
# enable hdpi
43-
enable_hi_dpi()
44-
45-
# init QApplication
46-
app = QApplication(sys.argv)
38+
config_file = paths.update_dir / "updater.json"
39+
if os.getenv("DEBUG", "0") == "1" and config_file.exists() and config_file.is_file():
40+
updater.load_from_file_and_override(config_file)
4741

4842
# check if the app is already running
49-
lock_dir = QCoreApplication.applicationDirPath()
50-
lock_file = QLockFile(lock_dir + "/App.lock")
43+
lock_file = QLockFile(str(paths.base_dir) + "/App.lock")
5144
if not lock_file.lock():
5245
sys.exit(0)
5346

app/builtin/args.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import sys
2+
3+
4+
def pop_arg_pair(name) -> str:
5+
argv = sys.argv
6+
7+
try:
8+
idx = argv.index(name)
9+
except ValueError:
10+
raise RuntimeError(f"Missing required argument: {name}")
11+
12+
if idx + 1 >= len(argv):
13+
raise RuntimeError(f"Argument {name} requires a value")
14+
15+
value = argv[idx + 1]
16+
17+
del argv[idx + 1]
18+
del argv[idx]
19+
20+
return value
21+
22+
23+
def pop_arg(name, default_value):
24+
if name in sys.argv:
25+
sys.argv.remove(name)
26+
return True
27+
else:
28+
return default_value

app/builtin/config.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from typing import Literal
2+
3+
APP_NAME = "App"
4+
APP_DISPLAY_NAME = "App"
5+
ORG_NAME = "PySide Template"
6+
7+
RemoteType = Literal["GitHub", "GitLab"]
8+
9+
UPDATER_REMOTE_TYPE: RemoteType = "GitHub"
10+
UPDATER_URL = "https://api.github.com"
11+
UPDATER_PROJECT_NAME = "SHIINASAMA/pyside_template"
12+
UPDATER_APP_NAME = APP_NAME

app/builtin/github_updater.py

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
1-
import platform
2-
31
from glom import glom
42
from httpx import AsyncClient
53

64
from singleton_decorator import singleton
75

86

9-
from .update import Updater, Version
7+
from app.builtin.update import Updater, Version, get_arch, get_sysname
8+
from app.builtin.paths import AppPaths
109

1110

1211
@singleton
1312
class GithubUpdater(Updater):
1413
base_url: str = "https://api.github.com"
1514
project_name: str = ""
15+
app_name: str = "App"
1616
timeout = 5
1717
token = None
1818

@@ -33,6 +33,7 @@ async def fetch(self):
3333
r = await client.get(
3434
url=f"{self.base_url}/repos/{self.project_name}/releases",
3535
params={"pre_page": "100", "page": "1"},
36+
follow_redirects=True
3637
)
3738
r.raise_for_status()
3839
releases = []
@@ -50,28 +51,14 @@ async def fetch(self):
5051
self.remote_version = Version(latest_release["tag_name"])
5152
self.description = latest_release["body"]
5253

53-
arch = platform.machine().lower()
54-
if arch in ["x86_64", "amd64"]:
55-
arch = "x64"
56-
elif arch in ["aarch64", "arm64"]:
57-
arch = "arm64"
58-
else:
59-
raise RuntimeError(f"Unknown architecture: {arch}")
60-
61-
sysname = platform.system().lower()
62-
if sysname == "windows":
63-
sysname = "windows"
64-
elif sysname == "darwin":
65-
sysname = "macos"
66-
elif sysname == "linux":
67-
sysname = "linux"
68-
else:
69-
raise RuntimeError(f"Unknown system: {sysname}")
70-
package_name = f"App-{sysname}-{arch}.zip"
54+
arch = get_arch()
55+
sysname = get_sysname()
56+
package_name = f"{self.app_name}-{sysname}-{arch}"
7157

7258
self.download_url = None
7359
for assets in glom(latest_release, "assets", default={}):
74-
if assets["name"] == package_name:
60+
if package_name in assets["name"]:
61+
package_name = assets["name"]
7562
self.download_url = assets["browser_download_url"]
7663
break
7764

@@ -83,4 +70,5 @@ async def fetch(self):
8370
r = await client.head(url=self.download_url, follow_redirects=True)
8471
r.raise_for_status()
8572

86-
self.filename = package_name
73+
paths = AppPaths()
74+
self.filename = f"{paths.update_dir}/{package_name}"

0 commit comments

Comments
 (0)