Skip to content

Merge branch 'main' of https://github.com/Spec-DY/popup-window #13

Merge branch 'main' of https://github.com/Spec-DY/popup-window

Merge branch 'main' of https://github.com/Spec-DY/popup-window #13

Workflow file for this run

name: Build Pyinstaller Client App
on:
push:
branches:
- main
paths:
- "client/**"
- ".github/workflows/BuildApp.yaml"
permissions:
contents: write
# build job
jobs:
build:
runs-on: ${{ matrix.os }}
name: Build Client App (${{ matrix.os }})
strategy:
matrix:
os: [windows-latest, ubuntu-latest]
steps:
- name: 🍗Checkout code
uses: actions/checkout@v4
- name: 🍎Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: 🍟Install dependencies (Windows)
if: matrix.os == 'windows-latest'
run: |
python -m pip install --upgrade pip
if (Test-Path "client/requirements.txt") { pip install -r client/requirements.txt }
pip install pyinstaller
shell: pwsh
- name: 🍟Install dependencies (Linux)
if: matrix.os == 'ubuntu-latest'
run: |
python -m pip install --upgrade pip
if [ -f client/requirements.txt ]; then pip install -r client/requirements.txt; fi
pip install pyinstaller
shell: bash
- name: 🎨Create version file (Windows)
if: matrix.os == 'windows-latest'
run: |
$versionContent = @"
# UTF-8
VSVersionInfo(
ffi=FixedFileInfo(
mask=0x3f,
flags=0x0,
OS=0x40004,
fileType=0x1,
subtype=0x0,
date=(0, 0)
),
kids=[
StringFileInfo(
[
StringTable(
u'040904B0',
[
StringStruct(u'CompanyName', u'Spec-DY'),
StringStruct(u'FileDescription', u'Popup Window Like Malware'),
StringStruct(u'InternalName', u'client'),
StringStruct(u'LegalCopyright', u'Copyright (C) 2025 Spec-DY'),
StringStruct(u'OriginalFilename', u'client.exe'),
StringStruct(u'ProductName', u'Popups'),
]
)
]
),
VarFileInfo([VarStruct(u'Translation', [1033, 1200])])
]
)
"@
New-Item -Path "client/util" -ItemType Directory -Force
Set-Content -Path "client/util/version.txt" -Value $versionContent -Encoding UTF8
Write-Output "✅ Version file created at client/util/version.txt"
shell: pwsh
- name: 🌭Build the app (Windows)
if: matrix.os == 'windows-latest'
run: |
pyinstaller --onefile --noconsole --version-file=client/util/version.txt --add-data "client/assets/appicon.ico;." --add-data "client/assets/appicon.jpg;." --icon client/assets/appicon.jpg client/client.py
Write-Output "✅ Executable built successfully"
shell: pwsh
- name: 🔐Sign executable (Windows)
if: matrix.os == 'windows-latest'
run: |
Write-Output "🔐 Creating self-signed certificate (2 years validity)..."
$cert = New-SelfSignedCertificate -DnsName "Spec-DY" -Type CodeSigning -CertStoreLocation cert:\CurrentUser\My -NotAfter (Get-Date).AddYears(2)
Write-Output "Certificate created: $($cert.Subject)"
Write-Output "Certificate valid until: $($cert.NotAfter)"
Write-Output "🔐 Signing executable..."
Set-AuthenticodeSignature -FilePath "dist/client.exe" -Certificate $cert
Write-Output "🔐 Verifying signature..."
$signature = Get-AuthenticodeSignature -FilePath "dist/client.exe"
Write-Output "Signature status: $($signature.Status)"
Write-Output "Signer: $($signature.SignerCertificate.Subject)"
Write-Output "Signature valid until: $($signature.SignerCertificate.NotAfter)"
# Check if signature exists (even if not fully trusted)
if ($signature.SignerCertificate -ne $null) {
Write-Output "✅ File signed successfully with 2-year certificate (Status: $($signature.Status))"
} else {
Write-Output "❌ Signature verification failed"
exit 1
}
shell: pwsh
- name: 🍬Build the app (Ubuntu)
if: matrix.os == 'ubuntu-latest'
run: |
pyinstaller --onefile --add-data "client/assets/appicon.ico:." --add-data "client/assets/appicon.jpg:." client/client.py
- name: 🍩Upload artifacts
uses: actions/upload-artifact@v4
with:
name: client-${{ matrix.os }}
path: ./dist/
retention-days: 7
# release job
release:
needs: build
runs-on: ubuntu-latest
steps:
- name: 🍗Checkout code (for server.py)
uses: actions/checkout@v4
- name: 🍭Generate timestamp version
id: version
run: |
VERSION="v$(date +'%Y.%m.%d-%H%M%S')"
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: 📥Download artifacts
uses: actions/download-artifact@v4
with:
path: ./artifacts
- name: 📥Download existing release assets
id: download_existing
run: |
echo "🔍 Checking for existing release..."
LATEST_RELEASE=$(curl -s "https://api.github.com/repos/${{ github.repository }}/releases/latest" || echo "{}")
# check current release
if [ "$(echo $LATEST_RELEASE | jq -r '.message')" != "Not Found" ]; then
echo "✅ Found existing release, downloading assets..."
# create directory for existing assets
mkdir -p ./existing-assets
# download existing apk
APK_URL=$(echo $LATEST_RELEASE | jq -r '.assets[] | select(.name | contains(".apk")) | .browser_download_url')
if [ "$APK_URL" != "null" ] && [ -n "$APK_URL" ]; then
echo "📱 Downloading existing APK..."
curl -L -o "./existing-assets/$(basename $(echo $APK_URL))" "$APK_URL"
fi
# download existing files except client executables
echo $LATEST_RELEASE | jq -r '.assets[] | select(.name | contains("server.py")) | .browser_download_url' | while read url; do
if [ -n "$url" ]; then
echo "📥 Downloading: $(basename $url)"
curl -L -o "./existing-assets/$(basename $url)" "$url"
fi
done
echo "existing_files=true" >> $GITHUB_OUTPUT
else
echo "ℹ️ No existing release found"
echo "existing_files=false" >> $GITHUB_OUTPUT
fi
- name: 🍩Rename and prepare files
run: |
mkdir -p ./release
# copy current files
if [ "${{ steps.download_existing.outputs.existing_files }}" == "true" ]; then
cp -r ./existing-assets/* ./release/ 2>/dev/null || true
fi
# add new python file to release directory
if [ -f ./artifacts/client-windows-latest/client.exe ]; then
mv ./artifacts/client-windows-latest/client.exe ./release/client-${{ steps.version.outputs.version }}-windows.exe
fi
if [ -f ./artifacts/client-ubuntu-latest/client ]; then
mv ./artifacts/client-ubuntu-latest/client ./release/client-${{ steps.version.outputs.version }}-ubuntu
fi
# add server.py file if it exists
if [ -f ./server.py ]; then
cp ./server.py ./release/server.py
fi
# clean up old files
find ./release -name "client-v*-windows.exe" ! -name "client-${{ steps.version.outputs.version }}-windows.exe" -delete 2>/dev/null || true
find ./release -name "client-v*-ubuntu" ! -name "client-${{ steps.version.outputs.version }}-ubuntu" -delete 2>/dev/null || true
echo "=== Final release files ==="
ls -la ./release/
- name: 🍆Create Release and Upload Assets
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ steps.version.outputs.version }}
name: Release ${{ steps.version.outputs.version }}
body: |
🚀 **Multi-Platform Release**
Automatically built and released from commit ${{ github.sha }}
## 📋 Server Setup
1. Clone repository and deploy `server.py` on your server
3. Run: `python3 server.py`
4. Server will listen on port 12345 by default
## 💻 Desktop Clients
1. Download the appropriate client for your OS
2. Run the executable
3. If server and client are on same device, use `127.0.0.1` as server address
## 📱 Mobile App (Android)
1. Download the APK file
2. Enable "Install from unknown sources" in Android settings
3. Install the APK on your device
4. Configure server address in the app
## 📦 Available Downloads
- **Windows Desktop**: `client-${{ steps.version.outputs.version }}-windows.exe`
- **Linux Desktop**: `client-${{ steps.version.outputs.version }}-ubuntu`
- **Android Mobile**: `client-${{ steps.version.outputs.version }}-android.apk`
- **Server**: Clone repository for `server.py`
files: ./release/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}