Refactor: switch native builds to onedir
This commit is contained in:
parent
9ba68dcfaf
commit
54f0f89dc8
2 changed files with 56 additions and 27 deletions
43
.github/workflows/native-build.yml
vendored
43
.github/workflows/native-build.yml
vendored
|
|
@ -60,6 +60,7 @@ jobs:
|
|||
|
||||
- name: Replace app.spec with version from workflow branch
|
||||
if: ${{ github.event.inputs.useWorkflowSpec == 'true' }}
|
||||
shell: bash
|
||||
run: cp temp-spec/app.spec ./app.spec
|
||||
|
||||
- name: Cache Python venv
|
||||
|
|
@ -78,6 +79,7 @@ jobs:
|
|||
architecture: "x64"
|
||||
|
||||
- name: Install Python dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
pip install uv
|
||||
uv venv --system-site-packages --relocatable
|
||||
|
|
@ -97,18 +99,34 @@ jobs:
|
|||
|
||||
- name: Build frontend
|
||||
working-directory: ui
|
||||
shell: bash
|
||||
run: |
|
||||
pnpm install --production --prefer-offline --frozen-lockfile
|
||||
pnpm run generate
|
||||
|
||||
- name: Clean build outputs (Unix)
|
||||
if: runner.os != 'Windows'
|
||||
shell: bash
|
||||
run: rm -rf build dist release || true
|
||||
|
||||
- name: Clean build outputs (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
shell: powershell
|
||||
run: |
|
||||
if (Test-Path build) { Remove-Item -Recurse -Force build }
|
||||
if (Test-Path dist) { Remove-Item -Recurse -Force dist }
|
||||
if (Test-Path release) { Remove-Item -Recurse -Force release }
|
||||
|
||||
- name: Build native binary (Windows)
|
||||
if: matrix.os == 'windows-latest'
|
||||
shell: powershell
|
||||
run: |
|
||||
.\.venv\Scripts\activate
|
||||
uv run pyinstaller ./app.spec
|
||||
|
||||
- name: Build native binary (Linux/macOS)
|
||||
if: matrix.os != 'windows-latest'
|
||||
shell: bash
|
||||
run: |
|
||||
. .venv/bin/activate
|
||||
uv run pyinstaller ./app.spec
|
||||
|
|
@ -117,34 +135,45 @@ jobs:
|
|||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: app-${{ runner.os }}-${{ matrix.arch }}-${{ env.TAG_NAME }}
|
||||
path: dist/*
|
||||
path: dist/YTPTube/**
|
||||
|
||||
- name: Package artifact (Unix)
|
||||
if: startsWith(env.TAG_NAME, 'v') && runner.os != 'Windows'
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p release
|
||||
PKG_DIR="YTPTube-${{ env.TAG_NAME }}"
|
||||
ZIP_NAME="ytptube-${{ runner.os }}-${{ matrix.arch }}-${{ env.TAG_NAME }}.zip"
|
||||
|
||||
if [ ! -d dist ] || [ -z "$(ls -A dist)" ]; then
|
||||
echo "dist directory is empty, skipping packaging."
|
||||
if [ ! -f "dist/YTPTube/YTPTube" ]; then
|
||||
echo "Missing dist/YTPTube/YTPTube"
|
||||
find dist -maxdepth 3 -type f | head -n 200
|
||||
exit 1
|
||||
fi
|
||||
|
||||
zip -r "release/${ZIP_NAME}" dist/
|
||||
rm -rf "dist/${PKG_DIR}"
|
||||
mv "dist/YTPTube" "dist/${PKG_DIR}"
|
||||
|
||||
zip -r "release/${ZIP_NAME}" "dist/${PKG_DIR}"
|
||||
|
||||
- name: Package artifact (Windows)
|
||||
if: startsWith(env.TAG_NAME, 'v') && runner.os == 'Windows'
|
||||
shell: powershell
|
||||
run: |
|
||||
$pkgDir = "YTPTube-${{ env.TAG_NAME }}"
|
||||
$zipName = "ytptube-${{ runner.os }}-${{ matrix.arch }}-${{ env.TAG_NAME }}.zip"
|
||||
New-Item -ItemType Directory -Force -Path release | Out-Null
|
||||
|
||||
if (!(Test-Path dist) -or !(Get-ChildItem -Path dist)) {
|
||||
Write-Host "dist directory is empty, skipping packaging."
|
||||
if (!(Test-Path "dist\YTPTube\YTPTube.exe")) {
|
||||
Write-Host "Missing dist\YTPTube\YTPTube.exe"
|
||||
Get-ChildItem -Recurse dist | Select-Object -First 200 | Format-Table -AutoSize
|
||||
exit 1
|
||||
}
|
||||
|
||||
Compress-Archive -Path dist\* -DestinationPath "release\$zipName"
|
||||
if (Test-Path "dist\$pkgDir") { Remove-Item -Recurse -Force "dist\$pkgDir" }
|
||||
Move-Item "dist\YTPTube" "dist\$pkgDir"
|
||||
|
||||
Compress-Archive -Path "dist\$pkgDir" -DestinationPath "release\$zipName" -Force
|
||||
|
||||
- name: Upload to GitHub Release
|
||||
if: startsWith(env.TAG_NAME, 'v')
|
||||
|
|
|
|||
40
app.spec
40
app.spec
|
|
@ -13,10 +13,6 @@ MANUAL_MAP = {
|
|||
|
||||
|
||||
def top_level_modules(dist_name: str) -> list[str]:
|
||||
"""
|
||||
Resolve distribution name -> importable top-level modules.
|
||||
Honors MANUAL_MAP first; falls back to metadata or normalized name.
|
||||
"""
|
||||
manual: list[str] | None = MANUAL_MAP.get(dist_name) or MANUAL_MAP.get(dist_name.replace("-", "_"))
|
||||
if manual:
|
||||
return manual
|
||||
|
|
@ -24,7 +20,7 @@ def top_level_modules(dist_name: str) -> list[str]:
|
|||
try:
|
||||
dist = importlib.metadata.distribution(dist_name)
|
||||
|
||||
top_level = []
|
||||
top_level: list[str] = []
|
||||
for f in dist.files or []:
|
||||
if f.name == "top_level.txt":
|
||||
txt = (dist.locate_file(f)).read_text().splitlines()
|
||||
|
|
@ -44,7 +40,7 @@ def top_level_modules(dist_name: str) -> list[str]:
|
|||
return [dist_name.replace("-", "_")]
|
||||
|
||||
|
||||
def parse_pyproject(path="pyproject.toml") -> set[str]:
|
||||
def parse_pyproject(path: str = "pyproject.toml") -> set[str]:
|
||||
with open(path, "rb") as f:
|
||||
data = tomllib.load(f)
|
||||
|
||||
|
|
@ -52,12 +48,11 @@ def parse_pyproject(path="pyproject.toml") -> set[str]:
|
|||
for extra in data["project"].get("optional-dependencies", {}).values():
|
||||
reqs.update(extra)
|
||||
|
||||
# Strip env markers and versions
|
||||
return {re.split(r"[ ;<>=]", r, 1)[0] for r in reqs} # noqa: B034
|
||||
|
||||
|
||||
dist_names = parse_pyproject()
|
||||
hidden = []
|
||||
hidden: list[str] = []
|
||||
|
||||
for dist in dist_names:
|
||||
if sys.platform != "win32" and dist in ("python-magic-bin", "tzdata"):
|
||||
|
|
@ -69,14 +64,13 @@ for dist in dist_names:
|
|||
hidden += [
|
||||
"engineio.async_drivers.aiohttp",
|
||||
"socketio.async_drivers.aiohttp",
|
||||
"app",
|
||||
"dotenv"
|
||||
"socketio",
|
||||
"socketio", # python-socketio top-level
|
||||
"engineio",
|
||||
"aiohttp",
|
||||
"engineio"
|
||||
"dotenv",
|
||||
"app",
|
||||
]
|
||||
|
||||
# Deduplicate
|
||||
hidden = sorted(set(hidden))
|
||||
|
||||
a = Analysis( # noqa: F821 # type: ignore
|
||||
|
|
@ -90,23 +84,29 @@ a = Analysis( # noqa: F821 # type: ignore
|
|||
hiddenimports=hidden,
|
||||
hookspath=[],
|
||||
runtime_hooks=[],
|
||||
excludes=["libstdc++.so.6"],
|
||||
excludes=[], # do NOT exclude libstdc++.so.6
|
||||
cipher=block_cipher,
|
||||
)
|
||||
|
||||
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) # type: ignore # noqa: F821
|
||||
|
||||
exe = EXE( # type: ignore # noqa: F821
|
||||
exe = EXE( # noqa: F821 # type: ignore
|
||||
pyz,
|
||||
a.scripts,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
name="YTPTube",
|
||||
debug=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
console=True,
|
||||
icon="ui/public/favicon.ico",
|
||||
onefile=True,
|
||||
exclude_binaries=True,
|
||||
)
|
||||
|
||||
coll = COLLECT( # noqa: F821 # type: ignore
|
||||
exe,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
strip=False,
|
||||
upx=False,
|
||||
name="YTPTube",
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in a new issue