|
2 | 2 | # |
3 | 3 | # Warum es das gibt (04.09.2026): der Nexus-Support konnte die hochgeladene |
4 | 4 | # Datei nicht freigeben, weil Virenscanner sie als Malware markierten. Teil |
5 | | -# der Antwort darauf ist Nachvollziehbarkeit — jede veroeffentlichte EXE |
| 5 | +# der Antwort darauf ist Nachvollziehbarkeit — jede veroeffentlichte Datei |
6 | 6 | # soll beweisbar aus DIESEM oeffentlichen Quellcode stammen und nicht aus |
7 | | -# einem Ordner auf einem Laptop. Das ist ausserdem die Voraussetzung fuer |
8 | | -# kostenloses Code-Signing durch die SignPath Foundation, die nur |
9 | | -# Artefakte aus einer automatisierten Bau-Kette signiert. |
| 7 | +# einem Ordner auf einem Laptop. |
10 | 8 | # |
11 | | -# Die Bau-Anweisung selbst steht NICHT hier, sondern in tools/build_exe.py |
| 9 | +# Seit 1.21.0 (05.09.2026) gibt es keinen PyInstaller mehr: S2Tweaker.exe |
| 10 | +# ist die unveraenderte, von der Python Software Foundation signierte |
| 11 | +# pythonw.exe der python.org-Installation, und der Python-Code liegt lesbar |
| 12 | +# daneben. Die Bau-Anweisung steht NICHT hier, sondern in tools/build_exe.py |
12 | 13 | # (dasselbe Skript, das build.bat lokal aufruft) — sonst haetten wir zwei |
13 | | -# Kopien, die auseinanderlaufen. |
| 14 | +# Kopien, die auseinanderlaufen. Das Skript prueft seinen Ordner selbst |
| 15 | +# (Signaturen, Hash-Gleichheit mit pythonw.exe, verbotene Dateien) und |
| 16 | +# startet ihn einmal probeweise. Der Schritt "Gegenprobe" unten wiederholt |
| 17 | +# die wichtigsten Punkte unabhaengig davon mit Windows-Bordmitteln. |
14 | 18 | name: build |
15 | 19 |
|
16 | 20 | on: |
@@ -43,56 +47,49 @@ jobs: |
43 | 47 | run: python tools/build_repak.py |
44 | 48 |
|
45 | 49 | - name: Tests ohne Spieldaten |
46 | | - # Die volle Batterie (32 Suiten) braucht den vanilla/-Ordner mit |
47 | | - # extrahierten GameData. Die duerfen nie ins Repo (GSC-Copyright), |
48 | | - # also laeuft hier nur, was ohne sie auskommt; der Rest vor jedem |
49 | | - # Release lokal. |
| 50 | + # Die volle Batterie braucht den vanilla/-Ordner mit extrahierten |
| 51 | + # GameData. Die duerfen nie ins Repo (GSC-Copyright), also laeuft |
| 52 | + # hier nur, was ohne sie auskommt; der Rest vor jedem Release lokal. |
50 | 53 | run: python tests/run_ci.py |
51 | 54 |
|
52 | | - - name: EXE bauen |
| 55 | + - name: Programmordner bauen (inkl. Gegenprobe und Selbsttest) |
53 | 56 | run: python tools/build_exe.py |
54 | 57 |
|
55 | | - - name: Gegenprobe — Versions-Ressource und Layout |
56 | | - # Genau die zwei Dinge, die den Virenscanner-Fehlalarm beguenstigt |
57 | | - # haben: fehlende Herkunftsangaben und die selbstentpackende |
58 | | - # Einzeldatei. Wenn eines davon zurueckfaellt, soll der Build rot |
59 | | - # werden und nicht still ein Artefakt hochladen. |
| 58 | + - name: Gegenprobe — signierter Starter, keine Netzmodule |
| 59 | + # Unabhaengig vom Bau-Skript, mit Windows-Bordmitteln: der Starter |
| 60 | + # muss Byte fuer Byte die pythonw.exe des Runners sein und von der |
| 61 | + # Python Software Foundation signiert; jede DLL/PYD muss gueltig |
| 62 | + # signiert sein (einzige Ausnahme: das selbst gebaute repak.exe); |
| 63 | + # und nichts, was Netz oder TLS kann, darf im Paket liegen. |
60 | 64 | shell: pwsh |
61 | 65 | run: | |
62 | | - $exe = "dist/S2Tweaker/S2Tweaker.exe" |
| 66 | + $dist = "dist/S2Tweaker" |
| 67 | + $exe = "$dist/S2Tweaker.exe" |
63 | 68 | if (-not (Test-Path $exe)) { throw "EXE fehlt: $exe" } |
64 | | - if (-not (Test-Path "dist/S2Tweaker/_internal")) { |
65 | | - throw "_internal fehlt - wurde wieder --onefile gebaut?" |
| 69 | + $py = Join-Path $env:pythonLocation "pythonw.exe" |
| 70 | + $h1 = (Get-FileHash $exe -Algorithm SHA256).Hash |
| 71 | + $h2 = (Get-FileHash $py -Algorithm SHA256).Hash |
| 72 | + "S2Tweaker.exe : $h1" |
| 73 | + "pythonw.exe : $h2" |
| 74 | + if ($h1 -ne $h2) { throw "S2Tweaker.exe ist nicht die unveraenderte pythonw.exe" } |
| 75 | + $sig = Get-AuthenticodeSignature $exe |
| 76 | + "Signatur : $($sig.Status) / $($sig.SignerCertificate.Subject)" |
| 77 | + if ($sig.Status -ne 'Valid' -or $sig.SignerCertificate.Subject -notmatch 'Python Software Foundation') { |
| 78 | + throw "Starter nicht gueltig von der Python Software Foundation signiert" |
66 | 79 | } |
67 | | - $vi = (Get-Item $exe).VersionInfo |
68 | | - "CompanyName : $($vi.CompanyName)" |
69 | | - "ProductName : $($vi.ProductName)" |
70 | | - "FileVersion : $($vi.FileVersion)" |
71 | | - "Groesse : $((Get-Item $exe).Length) Bytes" |
72 | | - foreach ($f in 'CompanyName', 'ProductName', 'FileVersion') { |
73 | | - if ([string]::IsNullOrWhiteSpace($vi.$f)) { |
74 | | - throw "Versions-Ressource unvollstaendig: $f ist leer" |
75 | | - } |
| 80 | + $bins = Get-ChildItem $dist -Recurse -Include *.exe,*.dll,*.pyd |
| 81 | + $unsigned = @($bins | Where-Object { (Get-AuthenticodeSignature $_.FullName).Status -ne 'Valid' }) |
| 82 | + "Binaerdateien : $($bins.Count), davon unsigniert: $($unsigned.Name -join ', ')" |
| 83 | + if (@($unsigned | Where-Object { $_.Name -ne 'repak.exe' }).Count -gt 0) { |
| 84 | + throw "Unsignierte Binaerdatei ausser repak.exe im Paket" |
76 | 85 | } |
77 | | - # Der Starter darf kein eingebettetes Archiv mehr sein. Die alte |
78 | | - # Schranke von 8 MB hat das NICHT geprueft: 1.19.1 kam mit |
79 | | - # 3,17 MB durch und bestand zu 90 % aus genau dem Archiv, das |
80 | | - # hier ausgeschlossen sein sollte. Mit --debug noarchive liegt |
81 | | - # der Starter bei ~345 KB; 1 MB laesst Luft und schlaegt an, |
82 | | - # sobald der Klumpen zurueckkehrt. |
83 | | - if ((Get-Item $exe).Length -gt 1MB) { |
84 | | - throw "Starter ist $((Get-Item $exe).Length) Bytes gross - steckt wieder ein Archiv drin?" |
85 | | - } |
86 | | - # Gegenprobe am Inhalt statt nur an der Groesse: die Module |
87 | | - # muessen als .pyc in _internal liegen. |
88 | | - $pyc = (Get-ChildItem "dist/S2Tweaker/_internal" -Recurse -Filter *.pyc | Measure-Object).Count |
89 | | - "Module als .pyc : $pyc" |
90 | | - if ($pyc -lt 100) { |
91 | | - throw "Nur $pyc .pyc-Dateien - steckt der Python-Code wieder in der EXE?" |
| 86 | + foreach ($bad in '_ssl', '_socket', '_hashlib', 'libssl', 'libcrypto', 'sqlite3', '_multiprocessing') { |
| 87 | + if (Get-ChildItem $dist -Recurse -Filter "$bad*") { throw "$bad* im Paket" } |
92 | 88 | } |
| 89 | + if (-not (Test-Path "$dist/_internal/sitecustomize.py")) { throw "Starter-Modul fehlt" } |
| 90 | + "Groesse : $((Get-ChildItem $dist -Recurse -File | Measure-Object Length -Sum).Sum) Bytes" |
93 | 91 |
|
94 | 92 | - name: Programmordner als Artefakt |
95 | | - # Genau dieses Artefakt bekommt spaeter die SignPath-Signatur. |
96 | 93 | uses: actions/upload-artifact@v4 |
97 | 94 | with: |
98 | 95 | name: S2Tweaker-${{ github.sha }} |
|
0 commit comments