Skip to content

feat: dynamic versioning and bundled license support #9

feat: dynamic versioning and bundled license support

feat: dynamic versioning and bundled license support #9

Workflow file for this run

name: Build and Release
on:
push:
branches: [ "**" ]
workflow_dispatch:
permissions:
contents: write
jobs:
create_release:
name: Create Release
runs-on: ubuntu-latest
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
tag_name: ${{ steps.tag_generator.outputs.tag_name }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Generate Tag Name
id: tag_generator
run: |
git fetch --tags
# Get the latest tag, default to v0.0.0 if none exists
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
echo "Latest tag found: $LATEST_TAG"
# Remove 'v' prefix
VERSION=${LATEST_TAG#v}
# Split version into array (assuming vX.Y.Z)
IFS='.' read -r -a parts <<< "$VERSION"
# Calculate new version
if [ ${#parts[@]} -eq 3 ]; then
# Standard X.Y.Z format
MAJOR=${parts[0]}
MINOR=${parts[1]}
PATCH=${parts[2]}
NEW_PATCH=$((PATCH + 1))
NEW_TAG="v$MAJOR.$MINOR.$NEW_PATCH"
else
# Fail-safe / Initial formatting
NEW_TAG="v0.0.1"
fi
echo "Generated new tag: $NEW_TAG"
echo "tag_name=$NEW_TAG" >> $GITHUB_OUTPUT
- name: Create Release
id: create_release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.tag_generator.outputs.tag_name }}
name: Release ${{ steps.tag_generator.outputs.tag_name }}
draft: false
prerelease: false
generate_release_notes: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
build:
name: Build Executable
needs: create_release
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [windows-latest, ubuntu-latest, macos-latest]
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pyinstaller
- name: Install Linux System Dependencies
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y python3-tk imagemagick
# Install PyQt6 system dependencies to avoid PyInstaller warnings/errors
sudo apt-get install -y libxcb-cursor0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-render-util0 libxcb-shape0 libxcb-xkb1 libxcb-util1 libxkbcommon-x11-0
- name: Build with PyInstaller (Windows)
if: runner.os == 'Windows'
env:
VERSION_TAG: ${{ needs.create_release.outputs.tag_name }}
run: |
# Create version file for dynamic versioning
echo "${{ env.VERSION_TAG }}" > core/version.txt
pyinstaller --name WASP --onefile --windowed --add-data "core;core" --add-data "gui;gui" --add-data "LICENSE;." --add-data "core/version.txt;core" main.py
mv dist/WASP.exe "dist/WASP-${{ env.VERSION_TAG }}-windows.exe"
- name: Build with PyInstaller (Linux - Binary, Deb, AppImage)
if: runner.os == 'Linux'
env:
VERSION_TAG: ${{ needs.create_release.outputs.tag_name }}
run: |
# Create version file for dynamic versioning
echo "${{ env.VERSION_TAG }}" > core/version.txt
# 1. Build Base Binary
pyinstaller --name WASP --onefile --windowed --add-data "core:core" --add-data "gui:gui" --add-data "LICENSE:." --add-data "core/version.txt:core" --hidden-import "pynput.keyboard._xorg" --hidden-import "pynput.mouse._xorg" main.py
# Rename binary
cp dist/WASP "dist/WASP-${{ env.VERSION_TAG }}-linux"
# Clean version for control file (remove v)
CLEAN_VER="${VERSION_TAG#v}"
# 2. Create .deb Package
mkdir -p deb_pkg/usr/local/bin
mkdir -p deb_pkg/DEBIAN
cp dist/WASP deb_pkg/usr/local/bin/wasp-app
chmod +x deb_pkg/usr/local/bin/wasp-app
# Control File
echo "Package: wasp-app" > deb_pkg/DEBIAN/control
echo "Version: $CLEAN_VER" >> deb_pkg/DEBIAN/control
echo "Architecture: amd64" >> deb_pkg/DEBIAN/control
echo "Maintainer: GitHub Action" >> deb_pkg/DEBIAN/control
echo "Description: WASP Application" >> deb_pkg/DEBIAN/control
dpkg-deb --build deb_pkg "dist/WASP-${{ env.VERSION_TAG }}-linux.deb"
# 3. Create AppImage
# Prepare AppDir
mkdir -p AppDir/usr/bin
cp dist/WASP AppDir/usr/bin/wasp-app
# Desktop File
echo "[Desktop Entry]" > AppDir/wasp.desktop
echo "Name=WASP" >> AppDir/wasp.desktop
echo "Exec=wasp-app" >> AppDir/wasp.desktop
echo "Icon=wasp" >> AppDir/wasp.desktop
echo "Type=Application" >> AppDir/wasp.desktop
echo "Categories=Utility;" >> AppDir/wasp.desktop
# Icon
if [ -f icon.ico ]; then convert icon.ico AppDir/wasp.png; else touch AppDir/wasp.png; fi
# AppRun
echo '#!/bin/sh' > AppDir/AppRun
echo 'exec "$APPDIR/usr/bin/wasp-app" "$@"' >> AppDir/AppRun
chmod +x AppDir/AppRun
# AppImageTool
wget -q https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage
chmod +x appimagetool-x86_64.AppImage
# Build AppImage
ARCH=x86_64 ./appimagetool-x86_64.AppImage AppDir "dist/WASP-${{ env.VERSION_TAG }}-linux.AppImage"
- name: Build with PyInstaller (macOS - App, Zip, DMG)
if: runner.os == 'macOS'
env:
VERSION_TAG: ${{ needs.create_release.outputs.tag_name }}
run: |
# Create version file for dynamic versioning
echo "${{ env.VERSION_TAG }}" > core/version.txt
pyinstaller --name WASP --onefile --windowed --add-data "core:core" --add-data "gui:gui" --add-data "LICENSE:." --add-data "core/version.txt:core" main.py
# 1. Zip the .app
cd dist
zip -r "WASP-${{ env.VERSION_TAG }}-macos.app.zip" WASP.app
cd ..
# 2. Create DMG
hdiutil create dist/WASP.dmg -srcfolder dist/WASP.app -ov -format UDZO
mv dist/WASP.dmg "dist/WASP-${{ env.VERSION_TAG }}-macos.dmg"
- name: Move to Output Folder
shell: bash
env:
VERSION_TAG: ${{ needs.create_release.outputs.tag_name }}
run: |
mkdir -p output
if [ "${{ runner.os }}" == "Windows" ]; then
cp "dist/WASP-${{ env.VERSION_TAG }}-windows.exe" output/
elif [ "${{ runner.os }}" == "Linux" ]; then
cp "dist/WASP-${{ env.VERSION_TAG }}-linux.deb" output/
cp "dist/WASP-${{ env.VERSION_TAG }}-linux.AppImage" output/
elif [ "${{ runner.os }}" == "macOS" ]; then
cp "dist/WASP-${{ env.VERSION_TAG }}-macos.dmg" output/
cp "dist/WASP-${{ env.VERSION_TAG }}-macos.app.zip" output/
fi
- name: Upload Release Assets
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ needs.create_release.outputs.tag_name }}
files: output/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: WASP-${{ matrix.os }}
path: output/*