diff --git a/.gitignore b/.gitignore index e1e5b5f..e4eb950 100644 --- a/.gitignore +++ b/.gitignore @@ -104,6 +104,11 @@ ENV/ env.bak/ venv.bak/ +# Environment and credentials +.env +.env.local +.pypirc + # Spyder project settings .spyderproject .spyproject diff --git a/.pypirc.example b/.pypirc.example new file mode 100644 index 0000000..3ee100c --- /dev/null +++ b/.pypirc.example @@ -0,0 +1,22 @@ +[distutils] +index-servers = + forgejo + +[forgejo] +# Forgejo PyPI Repository Configuration +# Repository: https://your-forgejo-instance.com/api/packages/{username}/pypi +# Documentation: https://docs.forgejo.org/en/latest/usage/packages/pypi/ +# +# To use this file: +# 1. Replace YOUR_FORGEJO_INSTANCE with your Forgejo URL +# 2. Replace YOUR_USERNAME with your Forgejo username +# 3. Replace YOUR_ACCESS_TOKEN with your Forgejo personal access token +# (Generate at: https://your-forgejo-instance.com/user/settings/applications) +# 4. Keep this file secure (credentials inside!) +# +# WARNING: This file contains sensitive credentials. +# Add to .gitignore to prevent accidental commits! + +repository = https://git.him-tools.de/api/packages/HIM-public/pypi/ +username = __token__ +password = YOUR_ACCESS_TOKEN diff --git a/BUILD.md b/BUILD.md index e37f3d3..bee8087 100644 --- a/BUILD.md +++ b/BUILD.md @@ -151,68 +151,217 @@ Or with dev dependencies: pip install -e ".[dev]" ``` -## Uploading to PyPI +## Uploading to Forgejo PyPI -### Prerequisites +This project uses **Forgejo PyPI** for package distribution, not the public PyPI. Forgejo is a self-hosted Git service with integrated package registry support. -1. Create PyPI account at https://pypi.org/ -2. Create PyPI token: https://pypi.org/manage/account/tokens/ -3. Configure credentials +### Setup Forgejo PyPI -### Using twine +#### Step 1: Create .pypirc Configuration + +Copy the example configuration: ```bash -# Install twine -pip install twine - -# Upload to PyPI (after building) -twine upload dist/* - -# Or to TestPyPI for testing first -twine upload -r testpypi dist/* +cp .pypirc.example .pypirc ``` -### Authentication - -**Option 1: Using .pypirc file** - -Create `~/.pypirc`: +Edit `.pypirc` with your Forgejo details: ```ini [distutils] index-servers = - pypi - testpypi + forgejo -[pypi] -repository = https://upload.pypi.org/legacy/ +[forgejo] +repository = https://your-forgejo-instance.com/api/packages/YOUR_USERNAME/pypi username = __token__ -password = pypi_your_token_here - -[testpypi] -repository = https://test.pypi.org/legacy/ -username = __token__ -password = pypi_your_test_token_here +password = YOUR_ACCESS_TOKEN ``` -**Option 2: Interactive prompt** +#### Step 2: Generate Forgejo Personal Access Token -twine will prompt for username and password when uploading. +1. Visit your Forgejo instance: `https://your-forgejo-instance.com` +2. Go to: **User Settings** → **Applications** +3. Click **Create New Token** +4. Name it: `PyPI Upload` +5. Grant scope: **write:packages** +6. Copy the generated token +7. Paste into `.pypirc` as `password` -### Upload to Test PyPI First +#### Step 3: Secure .pypirc -Before uploading to production, test with TestPyPI: +**IMPORTANT:** Add `.pypirc` to `.gitignore` to prevent committing credentials! ```bash -twine upload -r testpypi dist/* +echo ".pypirc" >> .gitignore +git add .gitignore +git commit -m "chore: add .pypirc to gitignore" ``` -Then test installation: +### Uploading to Forgejo PyPI + +**Requirement:** twine must be installed (see Prerequisites section above) + +#### Option 1: Using PowerShell Script (Windows - Recommended) + +```powershell +# Activate virtual environment first +.\.venv\Scripts\Activate.ps1 + +# Upload existing wheel +.\upload_wheel_to_forgejo_pypi.ps1 + +# Rebuild and upload +.\upload_wheel_to_forgejo_pypi.ps1 -Build + +# Show help +.\upload_wheel_to_forgejo_pypi.ps1 -Help +``` + +#### Option 2: Using Batch Script (Windows) + +```batch +upload_wheel_to_forgejo_pypi.bat + +REM Rebuild and upload +upload_wheel_to_forgejo_pypi.bat --build +``` + +#### Option 3: Using Shell Script (Unix/Linux) ```bash -pip install -i https://test.pypi.org/simple/ elytra-pim-client==0.1.0 +# Make script executable (first time only) +chmod +x upload_wheel_to_forgejo_pypi.sh + +# Upload existing wheel +./upload_wheel_to_forgejo_pypi.sh + +# Rebuild and upload +./upload_wheel_to_forgejo_pypi.sh --build + +# Show help +./upload_wheel_to_forgejo_pypi.sh --help ``` +#### Option 4: Using twine Directly + +```bash +# Activate virtual environment +.venv\Scripts\activate # Windows +source .venv/bin/activate # macOS/Linux + +# Upload wheel to Forgejo (twine is already in requirements.txt) +twine upload -r forgejo dist/*.whl +``` + +### What the Upload Scripts Do + +1. **Load credentials** - Reads .pypirc configuration +2. **Setup environment** - Copies .pypirc to user home directory temporarily +3. **Activate venv** - Sets up Python virtual environment +4. **Build wheel** - Builds wheel if not present (or with --build flag) +5. **Upload** - Uploads wheel to Forgejo PyPI repository using twine +6. **Cleanup** - Removes temporary credentials from home directory +7. **Deactivate** - Deactivates virtual environment + +### Accessing Your Package + +After upload, your package is available in the Forgejo PyPI repository: + +```bash +# Install from Forgejo PyPI +pip install --index-url https://your-forgejo-instance.com/api/packages/YOUR_USERNAME/pypi/simple/ elytra-pim-client + +# Or add to requirements.txt +-i https://your-forgejo-instance.com/api/packages/YOUR_USERNAME/pypi/simple/ +elytra-pim-client +``` + +### Troubleshooting Upload Issues + +#### "Upload failed" Error + +**Check:** Verify .pypirc configuration +```ini +[forgejo] +repository = https://correct-url.com/api/packages/USERNAME/pypi +username = __token__ +password = VALID_TOKEN +``` + +**Verify:** Token has correct permissions +- Token must have `write:packages` scope +- Visit: `https://your-forgejo-instance.com/user/settings/applications` + +**Network:** Ensure Forgejo instance is reachable +```bash +curl https://your-forgejo-instance.com/api/v1/user +``` + +#### ".pypirc not found" Error + +Create it from template: +```bash +cp .pypirc.example .pypirc +# Then edit with your credentials +``` + +#### "twine: command not found" Error + +**Solution:** twine is included in `requirements.txt` as a development dependency. Install all dependencies: + +```bash +pip install -r requirements.txt +``` + +### Example Workflow + +```bash +# 1. Setup Forgejo PyPI (first time only) +cp .pypirc.example .pypirc +# Edit .pypirc with your Forgejo repository URL and credentials +echo ".pypirc" >> .gitignore +git add .gitignore +git commit -m "chore: add .pypirc to gitignore" + +# 2. Make code changes +git commit -am "feat: add new feature" + +# 3. Update version in pyproject.toml +# version = "0.2.0" + +# 4. Build and upload to Forgejo PyPI +.\upload_wheel_to_forgejo_pypi.ps1 # PowerShell +# or +./upload_wheel_to_forgejo_pypi.sh # Bash/Shell +# or +./upload_wheel_to_forgejo_pypi.bat # Batch + +# 5. Tag and push release +git tag v0.2.0 +git push origin v0.2.0 +``` + +## Uploading to Public PyPI (Future) + +To upload to public PyPI instead of Forgejo: + +1. Install twine: `pip install twine` +2. Create account at https://pypi.org/ +3. Generate token at https://pypi.org/manage/account/tokens/ +4. Create `.pypirc`: + ```ini + [distutils] + index-servers = + pypi + + [pypi] + repository = https://upload.pypi.org/legacy/ + username = __token__ + password = pypi_your_token_here + ``` +5. Upload: `twine upload -r pypi dist/*.whl` + ## Versioning ### Version Format @@ -325,53 +474,77 @@ python -m build --verbose ``` elytra_client/ -├── build_wheel.py # Python build script (all platforms) -├── build_wheel.ps1 # PowerShell build script (Windows) -├── build_wheel.sh # Shell build script (Unix/Linux) -├── build_requirements.txt # Build dependencies -├── setup.py # Setup configuration (legacy compatibility) -├── pyproject.toml # Modern build configuration (PEP 517/518) -├── MANIFEST.in # (Optional) File inclusion rules -└── dist/ # Output directory for distributions +├── build_wheel.py # Python build script (all platforms) +├── build_wheel.ps1 # PowerShell build script (Windows) +├── build_wheel.sh # Shell build script (Unix/Linux) +├── upload_wheel_to_forgejo_pypi.bat # Batch upload script (Windows) +├── upload_wheel_to_forgejo_pypi.ps1 # PowerShell upload script (Windows) +├── upload_wheel_to_forgejo_pypi.sh # Shell upload script (Unix/Linux) +├── .pypirc.example # Forgejo PyPI configuration template +├── .pypirc # Forgejo PyPI credentials (in .gitignore!) +├── build_requirements.txt # Build dependencies +├── setup.py # Setup configuration (legacy compatibility) +├── pyproject.toml # Modern build configuration (PEP 517/518) +└── dist/ # Output directory for distributions ├── elytra_pim_client-0.1.0-py3-none-any.whl └── elytra_pim_client-0.1.0.tar.gz ``` ## Best Practices -1. **Always test before releasing** +1. **Setup Forgejo PyPI (first time only)** ```bash - twine upload -r testpypi dist/* - pip install -i https://test.pypi.org/simple/ elytra-pim-client==0.1.0 + cp .pypirc.example .pypirc + # Edit .pypirc with your Forgejo credentials + echo ".pypirc" >> .gitignore ``` -2. **Increment version for each release** +2. **Complete release workflow** + ```bash + # Update version in pyproject.toml + # version = "0.2.0" + + # Build and upload + .\upload_wheel_to_forgejo_pypi.ps1 + + # Tag and push + git tag v0.2.0 + git push origin v0.2.0 + ``` + +3. **Increment version for each release** - Patch: Bug fixes (0.1.1) - Minor: New features (0.2.0) - Major: Breaking changes (1.0.0) -3. **Clean before rebuilding** +4. **Clean before rebuilding** ```bash python build_wheel.py # Automatically cleans # Or manually rm -rf dist/ build/ *.egg-info/ ``` -4. **Keep dependencies minimal** +5. **Keep dependencies minimal** - Only required packages in `dependencies` - Development tools in `[project.optional-dependencies]` -5. **Document changes** - - Update CHANGELOG.md (if present) - - Update version in pyproject.toml - - Create git tag for release +6. **Use .pypirc template** + - Never commit `.pypirc` with real credentials + - Always keep `.pypirc` in `.gitignore` + - Share `.pypirc.example` with template values + +7. **Secure credential handling** + - Use Forgejo personal access tokens with limited scope + - Rotate tokens regularly + - Never share tokens or .pypirc files ## Resources +- [Forgejo Documentation](https://docs.forgejo.org/) +- [Forgejo PyPI Package Registry](https://docs.forgejo.org/en/latest/usage/packages/pypi/) - [Python Packaging Guide](https://packaging.python.org/) - [PEP 517 - Build System Interface](https://www.python.org/dev/peps/pep-0517/) - [PEP 518 - pyproject.toml](https://www.python.org/dev/peps/pep-0518/) - [PEP 440 - Version Identification](https://www.python.org/dev/peps/pep-0440/) - [setuptools Documentation](https://setuptools.pypa.io/) - [twine Documentation](https://twine.readthedocs.io/) -- [PyPI Help](https://pypi.org/help/) diff --git a/requirements.txt b/requirements.txt index f3a8737..fd6bc9b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,3 +9,4 @@ black>=23.0.0 isort>=5.12.0 flake8>=6.0.0 mypy>=1.0.0 +twine>=4.0.0 diff --git a/upload_wheel_to_forgejo_pypi.bat b/upload_wheel_to_forgejo_pypi.bat new file mode 100644 index 0000000..b6ce66a --- /dev/null +++ b/upload_wheel_to_forgejo_pypi.bat @@ -0,0 +1,153 @@ +@echo off +REM Upload wheel to Forgejo PyPI for Elytra PIM 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 Elytra PIM 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 +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%" + 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%" + 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%" +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% + if errorlevel 1 ( + echo Error: Build failed + del "%PYPIRC_DEST%" + call .venv\Scripts\deactivate.bat + pause + exit /b 1 + ) + ) +) + +REM Upload to Forgejo PyPI +echo. +echo ======================================================================== +echo Uploading to Forgejo PyPI... +echo ======================================================================== +echo. + +twine upload -r forgejo dist\*.whl +if errorlevel 1 ( + echo. + echo Error: Upload failed + echo. + echo Troubleshooting: + echo - Verify .pypirc has correct credentials + echo - Check Forgejo instance is reachable + echo - Ensure access token is valid + echo - Visit: https://your-forgejo-instance.com/user/settings/applications + echo. + del "%PYPIRC_DEST%" + call .venv\Scripts\deactivate.bat + pause + exit /b 1 +) + +REM Cleanup +echo. +echo ======================================================================== +echo Upload complete! +echo ======================================================================== +echo. + +call .venv\Scripts\deactivate.bat +del "%PYPIRC_DEST%" + +echo [OK] Virtual environment deactivated +echo [OK] Credentials removed +echo. +echo Your package is now available in your Forgejo PyPI repository. +echo. + +pause +endlocal diff --git a/upload_wheel_to_forgejo_pypi.ps1 b/upload_wheel_to_forgejo_pypi.ps1 new file mode 100644 index 0000000..36a6562 --- /dev/null +++ b/upload_wheel_to_forgejo_pypi.ps1 @@ -0,0 +1,129 @@ +# upload_wheel_to_forgejo_pypi.ps1 +# Upload wheel to Forgejo PyPI for Elytra PIM 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 Elytra PIM 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://your-forgejo-instance.com/api/packages/YOUR_USERNAME/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 "Elytra PIM 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" +& $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..d4e5104 --- /dev/null +++ b/upload_wheel_to_forgejo_pypi.sh @@ -0,0 +1,98 @@ +#!/bin/bash +# upload_wheel_to_forgejo_pypi.sh +# Upload wheel to Forgejo PyPI for Elytra PIM 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 "Elytra PIM 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 +source "$PROJECT_ROOT/.venv/bin/activate" + +# 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