notepad/NotePad/publish.py

275 lines
8.8 KiB
Python
Raw Normal View History

2026-02-14 14:15:52 +00:00
#!/usr/bin/env python3
"""
Build script for NotePad application
Creates distributable binaries for Linux and Windows
"""
import subprocess
import sys
import os
import shutil
import tarfile
import xml.etree.ElementTree as ET
2026-02-14 14:15:52 +00:00
from pathlib import Path
def get_version():
"""Extract version from .csproj file"""
csproj_path = Path("NotePad.csproj")
if not csproj_path.exists():
print("⚠️ WARNING: NotePad.csproj not found. Using default version 0.1.0")
return "0.1.0"
try:
tree = ET.parse(csproj_path)
root = tree.getroot()
version_elem = root.find(".//Version")
if version_elem is not None and version_elem.text:
return version_elem.text
print("⚠️ WARNING: Version not found in .csproj. Using default version 0.1.0")
return "0.1.0"
except Exception as e:
print(f"⚠️ WARNING: Failed to parse .csproj: {e}. Using default version 0.1.0")
return "0.1.0"
2026-02-14 14:15:52 +00:00
def run_command(cmd, description):
"""Run a shell command and print status"""
print(f"\n{'=' * 60}")
print(f"{description}")
print(f"{'=' * 60}")
print(f"Command: {' '.join(cmd)}\n")
result = subprocess.run(cmd, capture_output=False, text=True)
if result.returncode != 0:
print(f"\n❌ ERROR: {description} failed!")
sys.exit(1)
print(f"\n{description} completed successfully!")
return result
def copy_to_publish_dir(runtime_id, platform):
"""Copy published files to centralized publish directory"""
source_dir = Path(f"bin/Release/net8.0/{runtime_id}/publish")
dest_dir = Path(f"../publish/{runtime_id}")
if dest_dir.exists():
shutil.rmtree(dest_dir)
shutil.copytree(source_dir, dest_dir)
print(f"\n📦 Copied {platform.upper()} build to: {dest_dir.absolute()}")
return dest_dir
def build_windows_installer():
"""Build Windows installer using Inno Setup"""
iscc_path = shutil.which("iscc")
if not iscc_path:
print("\n⚠️ WARNING: Inno Setup not found. Skipping installer creation.")
print(" Install from: https://jrsoftware.org/isinfo.php")
print(" Or install via winget: winget install JRSoftware.InnoSetup")
return
setup_script = Path("setup.iss")
if not setup_script.exists():
print(f"\n⚠️ WARNING: {setup_script} not found. Skipping installer creation.")
return
cmd = [iscc_path, str(setup_script)]
try:
run_command(cmd, "Creating Windows installer")
installer_dir = Path("../publish/installers")
if installer_dir.exists():
installers = list(installer_dir.glob("*.exe"))
if installers:
print(f"\n📦 Installer created: {installers[0].absolute()}")
except Exception as e:
print(f"\n⚠️ WARNING: Failed to create installer: {e}")
def build_linux_tarball(version):
2026-02-14 14:15:52 +00:00
"""Create tar.gz archive with install script"""
print(f"\n{'=' * 60}")
print("Creating Linux tar.gz archive")
print(f"{'=' * 60}\n")
source_dir = Path("../publish/linux-x64")
if not source_dir.exists():
print(f"⚠️ WARNING: {source_dir} not found. Skipping tar.gz creation.")
return
# Create tarball directory
tarball_dir = Path("../publish/tarball")
tarball_dir.mkdir(parents=True, exist_ok=True)
# Create temporary directory for tarball contents
temp_dir = tarball_dir / "notepad-temp"
if temp_dir.exists():
shutil.rmtree(temp_dir)
temp_dir.mkdir()
# Copy application files
shutil.copytree(source_dir, temp_dir / "notepad")
# Copy install scripts
for script in ["install.sh", "uninstall.sh"]:
script_path = Path(script)
if script_path.exists():
shutil.copy(script_path, temp_dir / "notepad" / script)
os.chmod(temp_dir / "notepad" / script, 0o755)
# Copy img directory (contains icon)
img_dir = Path("img")
if img_dir.exists():
shutil.copytree(img_dir, temp_dir / "notepad" / "img")
2026-02-14 14:15:52 +00:00
# Create tar.gz
tarball_path = tarball_dir / f"notepad-{version}-linux-x64.tar.gz"
2026-02-14 14:15:52 +00:00
with tarfile.open(tarball_path, "w:gz") as tar:
tar.add(temp_dir / "notepad", arcname="notepad")
# Clean up temp directory
shutil.rmtree(temp_dir)
print(f"\n✅ Tar.gz archive created successfully!")
print(f"📦 Output: {tarball_path.absolute()}")
print(f"\nTo install:")
print(f" tar -xzf {tarball_path.name}")
print(f" cd notepad")
print(f" sudo ./install.sh")
def build_linux_appimage(version):
2026-02-14 14:15:52 +00:00
"""Build Linux AppImage"""
build_script = Path("build-appimage.sh")
if not build_script.exists():
print(f"\n⚠️ WARNING: {build_script} not found. Skipping AppImage creation.")
return
try:
result = subprocess.run(
["bash", str(build_script), version],
2026-02-14 14:15:52 +00:00
capture_output=True,
text=True
)
print(result.stdout)
if result.returncode != 0:
print(result.stderr)
print("\n⚠️ WARNING: AppImage creation failed.")
else:
print("\n✅ AppImage created successfully!")
except Exception as e:
print(f"\n⚠️ WARNING: Failed to create AppImage: {e}")
def publish_platform(platform, linux_package='both'):
"""Publish for a specific platform"""
runtime_id = f"{platform}-x64"
# Find dotnet executable
dotnet_path = shutil.which("dotnet")
if not dotnet_path:
# Try common locations
home = Path.home()
common_paths = [
home / ".dotnet" / "dotnet",
Path("/usr/bin/dotnet"),
Path("/usr/local/bin/dotnet")
]
for path in common_paths:
if path.exists():
dotnet_path = str(path)
break
if not dotnet_path:
print("❌ ERROR: dotnet not found. Please install .NET SDK.")
sys.exit(1)
cmd = [
dotnet_path, "publish",
"-c", "Release",
"-r", runtime_id,
"--self-contained", "true",
"-p:PublishSingleFile=true",
"-p:IncludeNativeLibrariesForSelfExtract=true",
"-p:PublishTrimmed=false" # Avalonia doesn't work well with trimming
]
run_command(cmd, f"Building for {platform.upper()}")
# Copy to centralized publish directory
dest_dir = copy_to_publish_dir(runtime_id, platform)
# Show the output files
if dest_dir.exists():
files = list(dest_dir.glob("*"))
for file in files:
size_mb = file.stat().st_size / (1024 * 1024)
print(f" - {file.name} ({size_mb:.2f} MB)")
# Build installer for Windows
if platform == 'win':
build_windows_installer()
# Build Linux packages
if platform == 'linux':
version = get_version()
2026-02-14 14:15:52 +00:00
if linux_package in ['appimage', 'both']:
build_linux_appimage(version)
2026-02-14 14:15:52 +00:00
if linux_package in ['tarball', 'both']:
build_linux_tarball(version)
2026-02-14 14:15:52 +00:00
def main():
"""Main entry point"""
# Parse arguments
target = 'both'
linux_package = 'both'
if len(sys.argv) > 1:
target = sys.argv[1].lower()
if target not in ['linux', 'windows', 'both', 'all']:
print("Usage: python3 publish.py [linux|windows|both] [appimage|tarball|both]")
print(" Platform: linux, windows, or both (default: both)")
print(" Linux package: appimage, tarball, or both (default: both)")
print("")
print("Examples:")
print(" python3 publish.py # Build everything")
print(" python3 publish.py linux # Build Linux with both packages")
print(" python3 publish.py linux appimage # Build Linux AppImage only")
print(" python3 publish.py linux tarball # Build Linux tarball only")
print(" python3 publish.py windows # Build Windows installer")
sys.exit(1)
if len(sys.argv) > 2:
linux_package = sys.argv[2].lower()
if linux_package not in ['appimage', 'tarball', 'both']:
print("Error: Linux package must be 'appimage', 'tarball', or 'both'")
sys.exit(1)
# Change to script directory
script_dir = Path(__file__).parent
os.chdir(script_dir)
print(f"🚀 NotePad Build Script")
print(f"Target platform(s): {target.upper()}")
if target in ['linux', 'both', 'all']:
print(f"Linux package(s): {linux_package.upper()}")
# Build for requested platform(s)
if target in ['linux', 'both', 'all']:
publish_platform('linux', linux_package)
if target in ['windows', 'both', 'all']:
publish_platform('win')
print(f"\n{'=' * 60}")
print("🎉 All builds completed successfully!")
print(f"{'=' * 60}\n")
if __name__ == "__main__":
main()