diff --git a/.gitignore b/.gitignore index 480521a..c84a992 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,9 @@ dmypy.json # Environment files .env +# PyPI credentials +.pypirc + # pytest .pytest_cache/ .coverage diff --git a/.pypirc.example b/.pypirc.example new file mode 100644 index 0000000..d27d421 --- /dev/null +++ b/.pypirc.example @@ -0,0 +1,25 @@ +# .pypirc.example +# Configuration file for uploading packages to Forgejo PyPI +# +# This file is a template - copy it to .pypirc and fill in your credentials: +# cp .pypirc.example .pypirc +# +# IMPORTANT: Never commit .pypirc to version control! +# It should already be in .gitignore + +[distutils] +index-servers = + forgejo + +[forgejo] +repository = https://git.him-tools.de/api/packages/HIM-public/pypi +username = __token__ +password = YOUR_FORGEJO_ACCESS_TOKEN_HERE + +# How to get your Forgejo access token: +# 1. Log in to your Forgejo instance: https://git.him-tools.de +# 2. Go to Settings > Applications > Generate New Token +# 3. Select 'write:package' scope +# 4. Copy the token and paste it above (replace YOUR_FORGEJO_ACCESS_TOKEN_HERE) +# +# Note: The username should always be "__token__" when using an access token diff --git a/README.md b/README.md index c02a6c9..682b3ce 100644 --- a/README.md +++ b/README.md @@ -136,6 +136,64 @@ pytest ``` +## Building & Publishing + +### Update Version + +Update the version number across all project files: + +```bash +# PowerShell +.\update_version.ps1 0.2.0 + +# Python +python update_version.py 0.2.0 + +# Bash +./update_version.sh 0.2.0 +``` + +### Build Wheel + +Build distribution packages: + +```bash +# PowerShell +.\build_wheel.ps1 + +# Python +python build_wheel.py + +# Bash +./build_wheel.sh +``` + +### Upload to Forgejo PyPI + +First, create your `.pypirc` configuration: + +```bash +cp .pypirc.example .pypirc +# Edit .pypirc with your Forgejo access token +``` + +Then upload: + +```bash +# PowerShell +.\upload_wheel_to_forgejo_pypi.ps1 + +# Bash +./upload_wheel_to_forgejo_pypi.sh + +# Windows Batch +upload_wheel_to_forgejo_pypi.bat +``` + +The package will be available at: +`https://git.him-tools.de/HIM-public/-/packages/pypi/agravity-client` + + ## Licence MIT diff --git a/build_wheel.ps1 b/build_wheel.ps1 new file mode 100644 index 0000000..9df068e --- /dev/null +++ b/build_wheel.ps1 @@ -0,0 +1,188 @@ +# build_wheel.ps1 +# Build wheel distribution for Agravity Client (PowerShell) +# +# Usage: +# .\build_wheel.ps1 +# +# Requires: +# - Python 3.9+ +# - setuptools>=65.0 +# - wheel +# - build (recommended) + +param( + [switch]$Clean = $false, + [switch]$Upload = $false +) + +# Define symbols as variables to avoid encoding issues +$symSuccess = "[OK]" +$symError = "[!]" +$symBullet = "[*]" + +# Color functions for output +function Write-Success { + Write-Host "$symSuccess $args" -ForegroundColor Green +} + +function Write-Error-Custom { + Write-Host "$symError $args" -ForegroundColor Red +} + +function Write-Header { + Write-Host ("=" * 70) + Write-Host $args + Write-Host ("=" * 70) +} + +function Write-SubHeader { + Write-Host ("-" * 70) + Write-Host $args + Write-Host ("-" * 70) +} + +# Main script +$projectRoot = Split-Path -Parent $MyInvocation.MyCommand.Path +$distDir = Join-Path $projectRoot "dist" +$buildDir = Join-Path $projectRoot "build" +$eggInfoDir = Join-Path $projectRoot "agravity_client.egg-info" + +Write-Header "Agravity Client - Wheel Builder (PowerShell)" + +# Clean if requested +if ($Clean) { + Write-Host "" + Write-Host "Cleaning previous builds..." + + @($distDir, $buildDir, $eggInfoDir) | ForEach-Object { + if (Test-Path $_) { + Remove-Item $_ -Recurse -Force -ErrorAction SilentlyContinue + Write-Success "Removed $_" + } + } +} + +# Check if build directory exists from previous build +if (Test-Path $distDir) { + Write-Host "" + Write-Host "Cleaning dist directory..." + Remove-Item $distDir -Recurse -Force -ErrorAction SilentlyContinue + Write-Success "Removed old distributions" +} + +# Install build requirements if needed +Write-Host "" +Write-Host "Checking build dependencies..." +$buildCheck = python -m pip list 2>$null | Select-String "build" +if (-not $buildCheck) { + Write-Host "Installing build tools..." + python -m pip install -q build setuptools wheel + if ($LASTEXITCODE -eq 0) { + Write-Success "Build tools installed" + } else { + Write-Error-Custom "Failed to install build tools" + exit 1 + } +} + +# Build +Write-SubHeader "Building distributions..." +Write-Host "" + +Push-Location $projectRoot +try { + python -m build + if ($LASTEXITCODE -ne 0) { + Write-Error-Custom "Build failed" + exit 1 + } +} finally { + Pop-Location +} + +# Verify output +Write-SubHeader "Build Results" +Write-Host "" + +if (!(Test-Path $distDir)) { + Write-Error-Custom "dist directory not created" + exit 1 +} + +$wheels = @(Get-ChildItem -Path $distDir -Filter "*.whl" -ErrorAction SilentlyContinue) +$sdists = @(Get-ChildItem -Path $distDir -Filter "*.tar.gz" -ErrorAction SilentlyContinue) + +if ($wheels.Count -eq 0 -and $sdists.Count -eq 0) { + Write-Error-Custom "No distributions created" + exit 1 +} + +Write-Success "Build successful!" +Write-Host "" + +if ($wheels.Count -gt 0) { + Write-Host "Wheels:" + $wheels | ForEach-Object { + $sizeMB = [Math]::Round($_.Length / 1MB, 2) + Write-Host " $symBullet $($_.Name) ($sizeMB MB)" + } +} + +if ($sdists.Count -gt 0) { + Write-Host "" + Write-Host "Source Distributions:" + $sdists | ForEach-Object { + $sizeMB = [Math]::Round($_.Length / 1MB, 2) + Write-Host " $symBullet $($_.Name) ($sizeMB MB)" + } +} + +Write-Host "" +Write-Success "Distributions saved to: $distDir" + +# Installation instructions +Write-SubHeader "Installation Instructions" +Write-Host "" + +if ($wheels.Count -gt 0) { + $wheel = $wheels[0] + Write-Host "Install the wheel locally:" + Write-Host " pip install `"$($wheel.FullName)`"" + Write-Host "" + + Write-Host "Or upload to PyPI:" + Write-Host " pip install twine" + Write-Host " twine upload dist/*" + Write-Host "" +} + +# Optional upload +if ($Upload) { + Write-SubHeader "Uploading to PyPI..." + Write-Host "" + + $twineCheck = python -m pip list 2>$null | Select-String "twine" + if (-not $twineCheck) { + Write-Host "Installing twine..." + python -m pip install -q twine + } + + Write-Host "Running twine upload..." + Push-Location $projectRoot + try { + python -m twine upload dist/* + if ($LASTEXITCODE -eq 0) { + Write-Success "Upload complete!" + } else { + Write-Error-Custom "Upload failed" + exit 1 + } + } finally { + Pop-Location + } +} + +Write-Host "" +Write-Host "Done!" +Write-Host "" +exit 0 diff --git a/build_wheel.py b/build_wheel.py new file mode 100644 index 0000000..2b64673 --- /dev/null +++ b/build_wheel.py @@ -0,0 +1,152 @@ +#!/usr/bin/env python +"""Build wheel distribution for Agravity Client. + +This script builds wheel and source distributions for the Agravity Client package. +It requires setuptools and wheel to be installed. + +Usage: + python build_wheel.py + +Output: + - Wheels saved to dist/ directory + - Source distribution (sdist) also created for reference +""" + +import shutil +import subprocess +import sys +from pathlib import Path + + +def main() -> int: + """Build wheel distribution. + + Returns: + Exit code (0 for success, 1 for failure) + """ + project_root = Path(__file__).parent + dist_dir = project_root / "dist" + + print("=" * 70) + print("Agravity Client - Wheel Builder") + print("=" * 70) + + # Clean previous builds + print("\nCleaning previous builds...") + if dist_dir.exists(): + try: + shutil.rmtree(dist_dir) + print(f"✓ Removed {dist_dir}") + except OSError as e: + print(f"✗ Failed to remove {dist_dir}: {e}") + return 1 + + build_dir = project_root / "build" + if build_dir.exists(): + try: + shutil.rmtree(build_dir) + print(f"✓ Removed {build_dir}") + except OSError as e: + print(f"✗ Failed to remove {build_dir}: {e}") + return 1 + + egg_info_dir = project_root / "agravity_client.egg-info" + if egg_info_dir.exists(): + try: + shutil.rmtree(egg_info_dir) + print(f"✓ Removed {egg_info_dir}") + except OSError as e: + print(f"✗ Failed to remove {egg_info_dir}: {e}") + return 1 + + # Build wheel and sdist + print("\n" + "-" * 70) + print("Building distributions...") + print("-" * 70 + "\n") + + try: + # Use python -m build for modern approach + result = subprocess.run( + [sys.executable, "-m", "build"], + cwd=project_root, + check=False + ) + + if result.returncode != 0: + print("\n✗ Build failed") + print("\nTrying alternative build method...") + + # Fallback to direct setuptools invocation + result = subprocess.run( + [sys.executable, "setup.py", "sdist", "bdist_wheel"], + cwd=project_root, + check=False + ) + + if result.returncode != 0: + print("\n✗ Build failed with fallback method") + return 1 + except FileNotFoundError: + # If 'build' module not available, use setup.py + print("Using setuptools directly...\n") + result = subprocess.run( + [sys.executable, "setup.py", "sdist", "bdist_wheel"], + cwd=project_root, + check=False + ) + + if result.returncode != 0: + print("\n✗ Build failed") + return 1 + + # Verify build output + print("\n" + "-" * 70) + print("Build Results") + print("-" * 70 + "\n") + + if not dist_dir.exists(): + print("✗ dist directory not created") + return 1 + + wheels = list(dist_dir.glob("*.whl")) + sdists = list(dist_dir.glob("*.tar.gz")) + + if not wheels and not sdists: + print("✗ No distributions created") + return 1 + + print("✓ Build successful!\n") + + if wheels: + print("Wheels:") + for wheel in wheels: + size_mb = wheel.stat().st_size / (1024 * 1024) + print(f" • {wheel.name} ({size_mb:.2f} MB)") + + if sdists: + print("\nSource Distributions:") + for sdist in sdists: + size_mb = sdist.stat().st_size / (1024 * 1024) + print(f" • {sdist.name} ({size_mb:.2f} MB)") + + print(f"\n✓ Distributions saved to: {dist_dir}") + + # Installation instructions + print("\n" + "-" * 70) + print("Installation Instructions") + print("-" * 70 + "\n") + + if wheels: + wheel = wheels[0] + print(f"Install the wheel locally:\n") + print(f" pip install {dist_dir / wheel.name}\n") + + print(f"Or upload to PyPI:\n") + print(f" pip install twine") + print(f" twine upload dist/*\n") + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/build_wheel.sh b/build_wheel.sh new file mode 100644 index 0000000..bae6198 --- /dev/null +++ b/build_wheel.sh @@ -0,0 +1,197 @@ +#!/bin/bash +# build_wheel.sh - Build wheel distribution for Agravity Client +# +# Usage: +# ./build_wheel.sh [--clean] [--upload] +# +# Options: +# --clean Clean previous builds before building +# --upload Upload to PyPI after building +# +# Requires: +# - Python 3.9+ +# - setuptools>=65.0 +# - wheel +# - build (recommended) + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Functions +print_success() { + echo -e "${GREEN}✓ $1${NC}" +} + +print_error() { + echo -e "${RED}✗ $1${NC}" +} + +print_header() { + echo "========================================================================" + echo "$1" + echo "========================================================================" +} + +print_subheader() { + echo "------------------------------------------------------------------------" + echo "$1" + echo "------------------------------------------------------------------------" +} + +# Parse arguments +CLEAN=false +UPLOAD=false + +while [[ $# -gt 0 ]]; do + case $1 in + --clean) + CLEAN=true + shift + ;; + --upload) + UPLOAD=true + shift + ;; + *) + echo "Unknown option: $1" + echo "Usage: $0 [--clean] [--upload]" + exit 1 + ;; + esac +done + +# Get project root +PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +DIST_DIR="$PROJECT_ROOT/dist" +BUILD_DIR="$PROJECT_ROOT/build" +EGG_INFO_DIR="$PROJECT_ROOT/agravity_client.egg-info" + +print_header "Agravity Client - Wheel Builder" + +# Clean if requested +if [ "$CLEAN" = true ]; then + echo "" + echo "Cleaning previous builds..." + for dir in "$DIST_DIR" "$BUILD_DIR" "$EGG_INFO_DIR"; do + if [ -d "$dir" ]; then + rm -rf "$dir" + print_success "Removed $dir" + fi + done +fi + +# Clean dist directory for fresh build +if [ -d "$DIST_DIR" ]; then + rm -rf "$DIST_DIR" + print_success "Removed old distributions" +fi + +# Check build dependencies +echo "" +echo "Checking build dependencies..." +if ! python3 -m pip list 2>/dev/null | grep -q "^build "; then + echo "Installing build tools..." + python3 -m pip install -q build setuptools wheel + if [ $? -eq 0 ]; then + print_success "Build tools installed" + else + print_error "Failed to install build tools" + exit 1 + fi +fi + +# Build +print_subheader "Building distributions..." +echo "" + +cd "$PROJECT_ROOT" +if ! python3 -m build; then + print_error "Build failed" + exit 1 +fi + +# Verify output +print_subheader "Build Results" +echo "" + +if [ ! -d "$DIST_DIR" ]; then + print_error "dist directory not created" + exit 1 +fi + +# Count wheels and sdists +WHEEL_COUNT=$(find "$DIST_DIR" -maxdepth 1 -name "*.whl" 2>/dev/null | wc -l) +SDIST_COUNT=$(find "$DIST_DIR" -maxdepth 1 -name "*.tar.gz" 2>/dev/null | wc -l) + +if [ $WHEEL_COUNT -eq 0 ] && [ $SDIST_COUNT -eq 0 ]; then + print_error "No distributions created" + exit 1 +fi + +print_success "Build successful!" +echo "" + +if [ $WHEEL_COUNT -gt 0 ]; then + echo "Wheels:" + for wheel in "$DIST_DIR"/*.whl; do + SIZE=$(du -h "$wheel" | cut -f1) + echo " • $(basename "$wheel") ($SIZE)" + done +fi + +if [ $SDIST_COUNT -gt 0 ]; then + echo "" + echo "Source Distributions:" + for sdist in "$DIST_DIR"/*.tar.gz; do + SIZE=$(du -h "$sdist" | cut -f1) + echo " • $(basename "$sdist") ($SIZE)" + done +fi + +echo "" +print_success "Distributions saved to: $DIST_DIR" + +# Installation instructions +print_subheader "Installation Instructions" +echo "" + +if [ $WHEEL_COUNT -gt 0 ]; then + WHEEL=$(ls "$DIST_DIR"/*.whl | head -1) + echo "Install the wheel locally:" + echo " pip install \"$WHEEL\"" + echo "" + echo "Or upload to PyPI:" + echo " pip install twine" + echo " twine upload dist/*" + echo "" +fi + +# Optional upload +if [ "$UPLOAD" = true ]; then + print_subheader "Uploading to PyPI..." + echo "" + + if ! python3 -m pip list 2>/dev/null | grep -q "^twine "; then + echo "Installing twine..." + python3 -m pip install -q twine + fi + + echo "Running twine upload..." + cd "$PROJECT_ROOT" + if python3 -m twine upload dist/*; then + print_success "Upload complete!" + else + print_error "Upload failed" + exit 1 + fi +fi + +echo "" +echo "Done!" +echo "" +exit 0 diff --git a/update_version.ps1 b/update_version.ps1 new file mode 100644 index 0000000..d2c6722 --- /dev/null +++ b/update_version.ps1 @@ -0,0 +1,82 @@ +param( + [Parameter(Mandatory = $true, Position = 0)] + [string]$Version +) + +function Test-VersionFormat { + param([string]$Ver) + $pattern = '^\d+\.\d+\.\d+(?:-[a-zA-Z0-9]+)?$' + return $Ver -match $pattern +} + +function Update-FileVersion { + param( + [string]$FilePath, + [string]$Pattern, + [string]$NewVersion + ) + try { + $content = Get-Content -Path $FilePath -Raw + $newContent = $content -replace $Pattern, $NewVersion + if ($content -eq $newContent) { + Write-Host "[OK] $(Split-Path -Leaf $FilePath) already up-to-date" -ForegroundColor Green + return $true + } + Set-Content -Path $FilePath -Value $newContent -NoNewline + Write-Host "[OK] Updated $(Split-Path -Leaf $FilePath)" -ForegroundColor Green + return $true + } + catch { + Write-Host "[ERROR] Error: $_" -ForegroundColor Red + return $false + } +} + +if (-not (Test-VersionFormat $Version)) { + Write-Host "[ERROR] Invalid version format: $Version" -ForegroundColor Red + exit 1 +} + +$projectRoot = Split-Path -Parent $PSCommandPath +$pyprojectPath = Join-Path $projectRoot "pyproject.toml" +$initPath = Join-Path (Join-Path $projectRoot "agravity_client") "__init__.py" + +Write-Host "======================================================================" +Write-Host "Updating Agravity Client to version $Version" +Write-Host "======================================================================" + +$success = $true + +if (Test-Path $pyprojectPath) { + $pattern = 'version = "[^"]+"' + $newVersion = "version = `"$Version`"" + if (-not (Update-FileVersion $pyprojectPath $pattern $newVersion)) { + $success = $false + } +} +else { + Write-Host "[ERROR] pyproject.toml not found" -ForegroundColor Red + $success = $false +} + +if (Test-Path $initPath) { + $pattern = '__version__ = "[^"]+"' + $newVersion = "__version__ = `"$Version`"" + if (-not (Update-FileVersion $initPath $pattern $newVersion)) { + $success = $false + } +} +else { + Write-Host "[ERROR] init file not found" -ForegroundColor Red + $success = $false +} + +Write-Host "======================================================================" +if ($success) { + Write-Host "[OK] Version successfully updated to $Version" -ForegroundColor Green + exit 0 +} +else { + Write-Host "[ERROR] Update completed with errors" -ForegroundColor Red + exit 1 +} diff --git a/update_version.py b/update_version.py new file mode 100644 index 0000000..98cb726 --- /dev/null +++ b/update_version.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python +"""Update version across Agravity Client project files. + +This script updates the version in: +- pyproject.toml +- agravity_client/__init__.py + +Usage: + python update_version.py + python update_version.py 0.2.0 + python update_version.py 1.0.0 + +The version must follow semantic versioning (major.minor.patch). +""" + +import re +import sys +from pathlib import Path + + +def validate_version(version: str) -> bool: + """Validate semantic version format. + + Args: + version: Version string to validate + + Returns: + True if valid, False otherwise + """ + pattern = r'^\d+\.\d+\.\d+(?:-[a-zA-Z0-9]+)?$' + return bool(re.match(pattern, version)) + + +def update_file(file_path: Path, old_pattern: str, new_version: str) -> bool: + """Update version in a file. + + Args: + file_path: Path to file to update + old_pattern: Regex pattern to find version + new_version: New version string + + Returns: + True if successful, False otherwise + """ + try: + content = file_path.read_text() + updated_content = re.sub(old_pattern, new_version, content) + + if content == updated_content: + print(f"✓ {file_path.name} already up-to-date") + return True + + file_path.write_text(updated_content) + print(f"✓ Updated {file_path.name}") + return True + except Exception as e: + print(f"✗ Error updating {file_path.name}: {e}") + return False + + +def main() -> int: + """Update version in project files. + + Returns: + Exit code (0 for success, 1 for failure) + """ + if len(sys.argv) != 2: + print(f"Usage: {sys.argv[0]} ") + print(f"Example: {sys.argv[0]} 0.2.0") + return 1 + + new_version = sys.argv[1] + + if not validate_version(new_version): + print(f"✗ Invalid version format: {new_version}") + print("Version must follow semantic versioning (major.minor.patch)") + print("Examples: 1.0.0, 0.2.0, 1.0.0-beta") + return 1 + + project_root = Path(__file__).parent + pyproject_path = project_root / "pyproject.toml" + init_path = project_root / "agravity_client" / "__init__.py" + + print("=" * 70) + print(f"Updating Agravity Client to version {new_version}") + print("=" * 70) + + success = True + + # Update pyproject.toml + if pyproject_path.exists(): + pattern = r'version = "[^"]+"' + success &= update_file( + pyproject_path, + pattern, + f'version = "{new_version}"' + ) + else: + print(f"✗ {pyproject_path} not found") + success = False + + # Update __init__.py + if init_path.exists(): + pattern = r'__version__ = "[^"]+"' + success &= update_file( + init_path, + pattern, + f'__version__ = "{new_version}"' + ) + else: + print(f"✗ {init_path} not found") + success = False + + if success: + print("=" * 70) + print(f"✓ Version successfully updated to {new_version}") + print("=" * 70) + return 0 + else: + print("=" * 70) + print("✗ Version update completed with errors") + print("=" * 70) + return 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/update_version.sh b/update_version.sh new file mode 100644 index 0000000..9c94a6f --- /dev/null +++ b/update_version.sh @@ -0,0 +1,123 @@ +#!/bin/bash +# Update version across Agravity Client project files. +# +# This script updates the version in: +# - pyproject.toml +# - agravity_client/__init__.py +# +# Usage: +# ./update_version.sh +# ./update_version.sh 0.2.0 +# +# The version must follow semantic versioning (major.minor.patch). + +set -euo pipefail + +# Color codes +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +validate_version() { + local version=$1 + if [[ ! $version =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+)?$ ]]; then + return 1 + fi + return 0 +} + +update_file() { + local file_path=$1 + local pattern=$2 + local new_version=$3 + + if [[ ! -f "$file_path" ]]; then + echo -e "${RED}✗ File not found: $file_path${NC}" + return 1 + fi + + # Check if content would change + local content + content=$(cat "$file_path") + + # Use sed to update the file (handles both macOS and Linux) + if [[ "$OSTYPE" == "darwin"* ]]; then + # macOS requires -i '' for in-place editing + sed -i '' "$pattern" "$file_path" + else + # Linux + sed -i "$pattern" "$file_path" + fi + + local new_content + new_content=$(cat "$file_path") + + if [[ "$content" == "$new_content" ]]; then + echo -e "${GREEN}✓ $(basename "$file_path") already up-to-date${NC}" + else + echo -e "${GREEN}✓ Updated $(basename "$file_path")${NC}" + fi + return 0 +} + +main() { + if [[ $# -ne 1 ]]; then + echo "Usage: $0 " + echo "Example: $0 0.2.0" + exit 1 + fi + + local new_version=$1 + + if ! validate_version "$new_version"; then + echo -e "${RED}✗ Invalid version format: $new_version${NC}" + echo -e "${YELLOW}Version must follow semantic versioning (major.minor.patch)${NC}" + echo -e "${YELLOW}Examples: 1.0.0, 0.2.0, 1.0.0-beta${NC}" + exit 1 + fi + + local project_root + project_root="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + local pyproject_path="$project_root/pyproject.toml" + local init_path="$project_root/agravity_client/__init__.py" + + echo "======================================================================" + echo "Updating Agravity Client to version $new_version" + echo "======================================================================" + + local success=true + + # Update pyproject.toml + if [[ -f "$pyproject_path" ]]; then + local pattern_pyproject="s|version = \"[^\"]*\"|version = \"$new_version\"|g" + if ! update_file "$pyproject_path" "$pattern_pyproject"; then + success=false + fi + else + echo -e "${RED}✗ $pyproject_path not found${NC}" + success=false + fi + + # Update __init__.py + if [[ -f "$init_path" ]]; then + local pattern_init="s|__version__ = \"[^\"]*\"|__version__ = \"$new_version\"|g" + if ! update_file "$init_path" "$pattern_init"; then + success=false + fi + else + echo -e "${RED}✗ $init_path not found${NC}" + success=false + fi + + echo "======================================================================" + if [[ "$success" == true ]]; then + echo -e "${GREEN}✓ Version successfully updated to $new_version${NC}" + exit 0 + else + echo -e "${RED}✗ Version update completed with errors${NC}" + exit 1 + fi +} + +main "$@" diff --git a/upload_wheel_to_forgejo_pypi.bat b/upload_wheel_to_forgejo_pypi.bat new file mode 100644 index 0000000..1ef9f0d --- /dev/null +++ b/upload_wheel_to_forgejo_pypi.bat @@ -0,0 +1,103 @@ +@echo off +REM Upload wheel to Forgejo PyPI for Agravity Client +REM +REM Prerequisites: +REM 1. Create .pypirc file in project root +REM 2. Copy from .pypirc.example and add your credentials +REM +REM Usage: +REM upload_wheel_to_forgejo_pypi.bat [--build] +REM Options: --build Force rebuild before upload + +setlocal enabledelayedexpansion + +set PYPIRC_SRC=%CD%\.pypirc +set PYPIRC_DEST=%USERPROFILE%\.pypirc + +echo. +echo ======================================================================== +echo Agravity Client - Upload to Forgejo PyPI +echo ======================================================================== +echo. + +REM Check for .pypirc +if not exist ".pypirc" ( + echo [!] .pypirc not found in project root! + echo. + echo Setup instructions: + echo 1. cp .pypirc.example .pypirc + echo 2. Edit .pypirc with your Forgejo credentials + echo 3. Run this script again + echo. + pause + exit /b 1 +) + +REM Copy .pypirc to user profile +echo Configuring credentials... +copy /Y "%PYPIRC_SRC%" "%PYPIRC_DEST%" > nul + +REM Activate virtual environment (if exists) +if exist .venv\Scripts\activate.bat ( + call .venv\Scripts\activate.bat +) + +REM Install twine if needed +python -m pip list | find /I "twine" > nul +if errorlevel 1 ( + echo Installing twine... + python -m pip install -q twine +) + +cd /d "%~dp0" + +REM Check for --build flag +if "%~1"=="--build" ( + echo Building wheel (forced)... + rmdir /s /q dist > nul 2>&1 + python build_wheel.py + if errorlevel 1 ( + del "%PYPIRC_DEST%" + if exist .venv\Scripts\deactivate.bat call .venv\Scripts\deactivate.bat + exit /b 1 + ) +) else ( + REM Build wheel if not present + if not exist dist\*.whl ( + echo Building wheel... + python build_wheel.py + if errorlevel 1 ( + del "%PYPIRC_DEST%" + if exist .venv\Scripts\deactivate.bat call .venv\Scripts\deactivate.bat + exit /b 1 + ) + ) +) + +REM Upload to Forgejo PyPI +echo. +echo ======================================================================== +echo Uploading to Forgejo PyPI... +echo ======================================================================== +echo. + +twine upload -r forgejo dist\*.whl +set UPLOAD_RESULT=%errorlevel% + +REM Cleanup +del "%PYPIRC_DEST%" +if exist .venv\Scripts\deactivate.bat call .venv\Scripts\deactivate.bat + +echo. +echo ======================================================================== +if %UPLOAD_RESULT% equ 0 ( + echo Upload successful! +) else ( + echo Upload failed +) +echo ======================================================================== +echo. + +endlocal +pause +exit /b %UPLOAD_RESULT% diff --git a/upload_wheel_to_forgejo_pypi.ps1 b/upload_wheel_to_forgejo_pypi.ps1 new file mode 100644 index 0000000..41813fc --- /dev/null +++ b/upload_wheel_to_forgejo_pypi.ps1 @@ -0,0 +1,130 @@ +# upload_wheel_to_forgejo_pypi.ps1 +# Upload wheel to Forgejo PyPI for Agravity Client +# +# Prerequisites: +# 1. Create .pypirc file with your Forgejo credentials +# 2. Copy from .pypirc.example and add your credentials +# +# Usage: +# .\upload_wheel_to_forgejo_pypi.ps1 + +param( + [switch]$Build = $false, + [switch]$Help = $false +) + +if ($Help) { + Write-Host @" +Upload wheel to Forgejo PyPI for Agravity Client + +USAGE: + .\upload_wheel_to_forgejo_pypi.ps1 [-Build] + +OPTIONS: + -Build Force rebuild wheel before uploading + -Help Display this help message + +SETUP: + 1. Create .pypirc in project root + 2. Copy from .pypirc.example as template + 3. Add your Forgejo repository URL and credentials: + [distutils] + index-servers = forgejo + + [forgejo] + repository = https://git.him-tools.de/api/packages/HIM-public/pypi + username = __token__ + password = YOUR_ACCESS_TOKEN + + 4. Keep .pypirc in .gitignore (already added) + +EXAMPLE: + .\upload_wheel_to_forgejo_pypi.ps1 # Upload existing wheel + .\upload_wheel_to_forgejo_pypi.ps1 -Build # Rebuild and upload +"@ + exit 0 +} + +$projectRoot = Split-Path -Parent $MyInvocation.MyCommand.Path +$pypiRcSrc = Join-Path $projectRoot ".pypirc" +$pypiRcDest = Join-Path $env:USERPROFILE ".pypirc" + +Write-Host "" +Write-Host "========================================================================" +Write-Host "Agravity Client - Upload to Forgejo PyPI" +Write-Host "========================================================================" +Write-Host "" + +# Check for .pypirc +if (!(Test-Path $pypiRcSrc)) { + Write-Host "[!] .pypirc not found in project root!" -ForegroundColor Red + Write-Host "" + Write-Host "Setup instructions:" + Write-Host " 1. cp .pypirc.example .pypirc" + Write-Host " 2. Edit .pypirc with your Forgejo credentials" + Write-Host " 3. Run this script again" + Write-Host "" + Write-Host "For help: .\upload_wheel_to_forgejo_pypi.ps1 -Help" + Write-Host "" + exit 1 +} + +# Copy .pypirc to user profile +Write-Host "Configuring credentials..." +Copy-Item -Path $pypiRcSrc -Destination $pypiRcDest -Force + +# Activate virtual environment +$activateScript = Join-Path $projectRoot ".venv\Scripts\Activate.ps1" +if (Test-Path $activateScript) { + & $activateScript +} + +# Install/upgrade twine if needed +python -m pip list | Select-String "^twine " > $null +if ($LASTEXITCODE -ne 0) { + Write-Host "Installing twine..." + python -m pip install -q twine +} + +# Build wheel if requested or not present +Push-Location $projectRoot +if ($Build -or !(Get-ChildItem -Path "dist" -Filter "*.whl" -ErrorAction SilentlyContinue)) { + if ($Build) { + Write-Host "Building wheel (forced)..." + Remove-Item "dist" -Recurse -Force -ErrorAction SilentlyContinue + } else { + Write-Host "Building wheel..." + } + python build_wheel.py + if ($LASTEXITCODE -ne 0) { + Remove-Item $pypiRcDest -Force + exit 1 + } +} + +# Upload to Forgejo PyPI +Write-Host "" +Write-Host "========================================================================" +Write-Host "Uploading to Forgejo PyPI..." +Write-Host "========================================================================" +Write-Host "" + +twine upload -r forgejo dist/*.whl +$uploadResult = $LASTEXITCODE + +Pop-Location + +# Cleanup credentials +Remove-Item $pypiRcDest -Force + +Write-Host "" +Write-Host "========================================================================" +if ($uploadResult -eq 0) { + Write-Host "Upload successful!" -ForegroundColor Green +} else { + Write-Host "Upload failed" -ForegroundColor Red +} +Write-Host "========================================================================" +Write-Host "" + +exit $uploadResult diff --git a/upload_wheel_to_forgejo_pypi.sh b/upload_wheel_to_forgejo_pypi.sh new file mode 100644 index 0000000..05c2f8d --- /dev/null +++ b/upload_wheel_to_forgejo_pypi.sh @@ -0,0 +1,100 @@ +#!/bin/bash +# upload_wheel_to_forgejo_pypi.sh +# Upload wheel to Forgejo PyPI for Agravity Client +# +# Prerequisites: +# 1. Create .pypirc file in project root +# 2. Copy from .pypirc.example and add your credentials +# +# Usage: +# ./upload_wheel_to_forgejo_pypi.sh [--build] +# Options: --build Force rebuild before upload + +set -e + +PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PYPIRC_SRC="$PROJECT_ROOT/.pypirc" +PYPIRC_DEST="$HOME/.pypirc" + +echo "" +echo "========================================================================" +echo "Agravity Client - Upload to Forgejo PyPI" +echo "========================================================================" +echo "" + +# Check for .pypirc +if [ ! -f "$PYPIRC_SRC" ]; then + echo "[!] .pypirc not found in project root!" + echo "" + echo "Setup instructions:" + echo " 1. cp .pypirc.example .pypirc" + echo " 2. Edit .pypirc with your Forgejo credentials" + echo " 3. Run this script again" + echo "" + exit 1 +fi + +# Copy .pypirc to user home +echo "Configuring credentials..." +cp "$PYPIRC_SRC" "$PYPIRC_DEST" +chmod 600 "$PYPIRC_DEST" + +# Activate virtual environment (if exists) +if [ -f "$PROJECT_ROOT/.venv/bin/activate" ]; then + source "$PROJECT_ROOT/.venv/bin/activate" +fi + +# Install twine if needed +if ! pip list 2>/dev/null | grep -q "^twine "; then + echo "Installing twine..." + pip install -q twine +fi + +# Check for --build flag +if [ "$1" = "--build" ]; then + echo "Building wheel (forced)..." + rm -rf "$PROJECT_ROOT/dist" + cd "$PROJECT_ROOT" + python build_wheel.py + if [ $? -ne 0 ]; then + rm -f "$PYPIRC_DEST" + exit 1 + fi +else + # Build wheel if not present + if ! ls "$PROJECT_ROOT/dist"/*.whl > /dev/null 2>&1; then + echo "Building wheel..." + cd "$PROJECT_ROOT" + python build_wheel.py + if [ $? -ne 0 ]; then + rm -f "$PYPIRC_DEST" + exit 1 + fi + fi +fi + +# Upload to Forgejo PyPI +echo "" +echo "========================================================================" +echo "Uploading to Forgejo PyPI..." +echo "========================================================================" +echo "" + +cd "$PROJECT_ROOT" +twine upload -r forgejo dist/*.whl +UPLOAD_RESULT=$? + +# Cleanup +rm -f "$PYPIRC_DEST" + +echo "" +echo "========================================================================" +if [ $UPLOAD_RESULT -eq 0 ]; then + echo "Upload successful!" +else + echo "Upload failed" +fi +echo "========================================================================" +echo "" + +exit $UPLOAD_RESULT