Skip to content

Commit 9eb448e

Browse files
authored
Refactor: Font subsetting engine, GUI overhaul, immutable plan snapshots & full packaging/CI (#20)
1 parent 1232fd1 commit 9eb448e

207 files changed

Lines changed: 34985 additions & 628 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/dependabot.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: pip
4+
directory: /
5+
schedule:
6+
interval: weekly
7+
groups:
8+
python-dependencies:
9+
patterns: ["*"]
10+
- package-ecosystem: github-actions
11+
directory: /
12+
schedule:
13+
interval: weekly

.github/workflows/integration.yml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
name: real-media-integration
2+
3+
on:
4+
workflow_dispatch:
5+
pull_request:
6+
paths:
7+
- "plexmuxy/**"
8+
- "plexmuxy_gui/api.py"
9+
- "tests/integration/**"
10+
- "pyproject.toml"
11+
- "uv.lock"
12+
- "*.spec"
13+
- ".github/workflows/integration.yml"
14+
push:
15+
branches: [main, refactor]
16+
tags: ["v*"]
17+
18+
permissions:
19+
contents: read
20+
21+
jobs:
22+
mkvmerge:
23+
name: ${{ matrix.os }} real media
24+
runs-on: ${{ matrix.os }}
25+
strategy:
26+
fail-fast: false
27+
matrix:
28+
os: [ubuntu-latest, windows-latest]
29+
steps:
30+
- uses: actions/checkout@v7
31+
with:
32+
persist-credentials: false
33+
- uses: actions/setup-python@v6
34+
with:
35+
python-version: "3.12"
36+
- if: runner.os == 'Linux'
37+
run: sudo apt-get update && sudo apt-get install -y ffmpeg mkvtoolnix p7zip-full
38+
- if: runner.os == 'Windows'
39+
shell: pwsh
40+
run: choco install ffmpeg mkvtoolnix -y --no-progress
41+
- run: python -m pip install -e ".[dev]"
42+
- run: python -m pytest -m integration -v --basetemp .pytest-tmp/integration -o cache_dir=.pytest-tmp/cache
43+

.github/workflows/release.yml

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
name: release
2+
3+
on:
4+
push:
5+
tags: ["v*"]
6+
7+
permissions:
8+
contents: write
9+
10+
jobs:
11+
release:
12+
runs-on: windows-latest
13+
env:
14+
WINDOWS_CERTIFICATE_BASE64: ${{ secrets.WINDOWS_CERTIFICATE_BASE64 }}
15+
WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
16+
steps:
17+
- uses: actions/checkout@v7
18+
with:
19+
persist-credentials: false
20+
- uses: actions/setup-python@v6
21+
with:
22+
python-version: "3.12"
23+
- name: Verify tag matches package version
24+
shell: pwsh
25+
run: |
26+
$expected = "v$(python -c 'import plexmuxy; print(plexmuxy.__version__)')"
27+
if ($env:GITHUB_REF_NAME -ne $expected) { throw "Tag $env:GITHUB_REF_NAME does not match $expected" }
28+
- run: python -m pip install -e ".[dev,build,gui]" cyclonedx-bom
29+
- name: Install Inno Setup
30+
run: choco install innosetup -y --no-progress
31+
- run: python -m pytest -m "not integration" --basetemp .pytest-tmp/release -o cache_dir=.pytest-tmp/cache
32+
- run: python -m build
33+
- name: Generate PyInstaller version-info resource
34+
shell: python
35+
run: |
36+
import re, pathlib
37+
version = pathlib.Path("plexmuxy/VERSION").read_text(encoding="utf-8").strip()
38+
m = re.match(r"^(\d+)\.(\d+)\.(\d+)", version)
39+
if not m:
40+
raise SystemExit(f"Cannot parse version '{version}' as MAJOR.MINOR.PATCH")
41+
filevers = f"{int(m[1])}, {int(m[2])}, {int(m[3])}, 0"
42+
info = f"""# UTF-8
43+
#
44+
VSVersionInfo(
45+
ffi=FixedFileInfo(
46+
filevers=({filevers}),
47+
prodvers=({filevers}),
48+
mask=0x3f,
49+
flags=0x0,
50+
OS=0x40004,
51+
fileType=0x1,
52+
subtype=0x0,
53+
date=(0, 0)
54+
),
55+
kids=[
56+
StringFileInfo([
57+
StringTable(
58+
u'040904B0',
59+
[StringStruct(u'CompanyName', u'PlexMuxy contributors'),
60+
StringStruct(u'FileDescription', u'PlexMuxy'),
61+
StringStruct(u'FileVersion', u'{version}'),
62+
StringStruct(u'InternalName', u'plexmuxy'),
63+
StringStruct(u'LegalCopyright', u'MIT License'),
64+
StringStruct(u'OriginalFilename', u'plexmuxy-gui.exe'),
65+
StringStruct(u'ProductName', u'PlexMuxy'),
66+
StringStruct(u'ProductVersion', u'{version}')])
67+
]),
68+
VarFileInfo([VarStruct(u'Translation', [1033, 1200])])
69+
]
70+
)
71+
"""
72+
pathlib.Path("packaging").mkdir(exist_ok=True)
73+
pathlib.Path("packaging/version_info.txt").write_text(info, encoding="utf-8")
74+
- run: python -m PyInstaller --clean --noconfirm plexmuxy-cli.spec
75+
- run: python -m PyInstaller --clean --noconfirm plexmuxy-gui.spec
76+
- name: Sign portable executables when a certificate is configured
77+
if: env.WINDOWS_CERTIFICATE_BASE64 != ''
78+
shell: pwsh
79+
run: |
80+
$certificate = Join-Path $env:RUNNER_TEMP "plexmuxy-signing.pfx"
81+
[IO.File]::WriteAllBytes($certificate, [Convert]::FromBase64String($env:WINDOWS_CERTIFICATE_BASE64))
82+
try {
83+
Get-ChildItem dist -Recurse -Filter *.exe | ForEach-Object {
84+
signtool sign /fd SHA256 /td SHA256 /tr http://timestamp.digicert.com /f $certificate /p $env:WINDOWS_CERTIFICATE_PASSWORD $_.FullName
85+
}
86+
} finally {
87+
Remove-Item -Force -ErrorAction SilentlyContinue $certificate
88+
}
89+
- name: Build Windows installer with fixed application identity
90+
shell: pwsh
91+
run: |
92+
$version = python -c "import plexmuxy; print(plexmuxy.__version__)"
93+
iscc "/DMyAppVersion=$version" packaging/plexmuxy.iss
94+
- name: Sign installer when a certificate is configured
95+
if: env.WINDOWS_CERTIFICATE_BASE64 != ''
96+
shell: pwsh
97+
run: |
98+
$certificate = Join-Path $env:RUNNER_TEMP "plexmuxy-signing.pfx"
99+
[IO.File]::WriteAllBytes($certificate, [Convert]::FromBase64String($env:WINDOWS_CERTIFICATE_BASE64))
100+
try {
101+
Get-ChildItem dist -File -Filter *-setup.exe | ForEach-Object {
102+
signtool sign /fd SHA256 /td SHA256 /tr http://timestamp.digicert.com /f $certificate /p $env:WINDOWS_CERTIFICATE_PASSWORD $_.FullName
103+
}
104+
} finally {
105+
Remove-Item -Force -ErrorAction SilentlyContinue $certificate
106+
}
107+
- name: Generate release SBOM
108+
run: python -m cyclonedx_py environment --output-format JSON --output-file dist/plexmuxy-sbom.cdx.json
109+
- name: Package executables and checksums
110+
shell: pwsh
111+
run: |
112+
Compress-Archive -Path dist/plexmuxy/* -DestinationPath dist/plexmuxy-windows-x64.zip
113+
Compress-Archive -Path dist/plexmuxy-gui/* -DestinationPath dist/plexmuxy-gui-windows-x64.zip
114+
$artifacts = Get-ChildItem dist -File
115+
$manifest = [ordered]@{
116+
version = (python -c "import plexmuxy; print(plexmuxy.__version__)")
117+
commit = $env:GITHUB_SHA
118+
python = (python --version)
119+
artifacts = @($artifacts | ForEach-Object { [ordered]@{ name = $_.Name; size = $_.Length; sha256 = (Get-FileHash -Algorithm SHA256 -LiteralPath $_.FullName).Hash.ToLower() } })
120+
}
121+
$manifest | ConvertTo-Json -Depth 5 | Set-Content -Encoding utf8 dist/release-manifest.json
122+
Get-ChildItem dist -File | Where-Object Name -ne 'SHA256SUMS.txt' | ForEach-Object {
123+
$hash = (Get-FileHash -Algorithm SHA256 -LiteralPath $_.FullName).Hash.ToLower()
124+
"$hash $($_.Name)" | Add-Content -Encoding ascii dist/SHA256SUMS.txt
125+
}
126+
- name: Create GitHub release
127+
shell: pwsh
128+
env:
129+
GH_TOKEN: ${{ github.token }}
130+
run: gh release create $env:GITHUB_REF_NAME (Get-ChildItem dist -File | ForEach-Object FullName) --verify-tag --generate-notes --title "PlexMuxy $env:GITHUB_REF_NAME"
131+
132+
publish-pypi:
133+
name: Publish source and wheel with PyPI Trusted Publishing
134+
needs: release
135+
runs-on: ubuntu-latest
136+
environment:
137+
name: pypi
138+
url: https://pypi.org/p/plexmuxy
139+
permissions:
140+
id-token: write
141+
steps:
142+
- uses: actions/checkout@v7
143+
with:
144+
persist-credentials: false
145+
- uses: actions/setup-python@v6
146+
with:
147+
python-version: "3.12"
148+
- run: python -m pip install build
149+
- run: python -m build
150+
- uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b
151+

.github/workflows/security.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name: dependency-security
2+
3+
on:
4+
workflow_dispatch:
5+
schedule:
6+
- cron: "17 8 * * 1"
7+
8+
permissions:
9+
contents: read
10+
11+
jobs:
12+
audit:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- uses: actions/checkout@v7
16+
with:
17+
persist-credentials: false
18+
- uses: actions/setup-python@v6
19+
with:
20+
python-version: "3.12"
21+
- run: python -m pip install -e . pip-audit
22+
- run: python -m pip_audit

.github/workflows/test.yml

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
name: test
2+
3+
on:
4+
push:
5+
pull_request:
6+
7+
permissions:
8+
contents: read
9+
10+
jobs:
11+
unit:
12+
name: ${{ matrix.os }} / Python ${{ matrix.python }}
13+
runs-on: ${{ matrix.os }}
14+
strategy:
15+
fail-fast: false
16+
matrix:
17+
os: [ubuntu-latest, windows-latest, macos-latest]
18+
python: ["3.10", "3.11", "3.12", "3.13", "3.14"]
19+
steps:
20+
- uses: actions/checkout@v7
21+
with:
22+
persist-credentials: false
23+
- uses: actions/setup-python@v6
24+
with:
25+
python-version: ${{ matrix.python }}
26+
- run: python -m pip install -e ".[dev,gui]"
27+
- run: python -c "import os; os.makedirs('.pytest-tmp', exist_ok=True)" && python -m pytest -m "not integration" --basetemp .pytest-tmp/unit -o cache_dir=.pytest-tmp/cache
28+
29+
quality:
30+
runs-on: ubuntu-latest
31+
steps:
32+
- uses: actions/checkout@v7
33+
with:
34+
persist-credentials: false
35+
- uses: actions/setup-python@v6
36+
with:
37+
python-version: "3.12"
38+
- run: python -m pip install -e ".[dev,gui]"
39+
- run: python -m ruff check plexmuxy plexmuxy_gui tests
40+
- run: python -m mypy plexmuxy plexmuxy_gui
41+
- run: python -c "import os; os.makedirs('.pytest-tmp', exist_ok=True)" && python -m pytest -m "not integration" --basetemp .pytest-tmp/coverage -o cache_dir=.pytest-tmp/cache --cov --cov-report=term --cov-fail-under=75
42+
43+
package:
44+
runs-on: ubuntu-latest
45+
steps:
46+
- uses: actions/checkout@v7
47+
with:
48+
persist-credentials: false
49+
- uses: actions/setup-python@v6
50+
with:
51+
python-version: "3.12"
52+
- run: python -m pip install build
53+
- run: python -m build
54+
- name: Test wheel in a clean environment
55+
run: |
56+
python -m venv /tmp/plexmuxy-wheel-test
57+
/tmp/plexmuxy-wheel-test/bin/pip install dist/*.whl
58+
cd /tmp
59+
/tmp/plexmuxy-wheel-test/bin/plexmuxy --help
60+
/tmp/plexmuxy-wheel-test/bin/plexmuxy show-config
61+
/tmp/plexmuxy-wheel-test/bin/python -m plexmuxy --help

.gitignore

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,22 @@
11
__pycache__
2+
.coverage
3+
.mypy_cache/
4+
.venv/
5+
.ruff_cache/
6+
.uv-cache/
7+
.pytest_cache/
8+
.pytest-tmp/
9+
*.egg-info/
10+
dist/
211
.idea
312
*.bat
413
*.exe
514
build/
615
main.spec
7-
*.mo
16+
*.mo
17+
packaging/version_info.txt
18+
19+
.workbuddy/
20+
.codebuddy/
21+
.codex/
22+
.vscode/

0 commit comments

Comments
 (0)