@@ -1437,3 +1437,379 @@ helm-deploy: helm-lint
14371437helm-delete :
14381438 @echo " 🗑 Deleting $( RELEASE_NAME) release..."
14391439 helm uninstall $(RELEASE_NAME ) -n $(NAMESPACE ) || true
1440+
1441+ # =============================================================================
1442+ # 🏠 LOCAL PYPI SERVER
1443+ # Currently blocked by: https://github.com/pypiserver/pypiserver/issues/630
1444+ # =============================================================================
1445+ # help: 🏠 LOCAL PYPI SERVER
1446+ # help: local-pypi-install - Install pypiserver for local testing
1447+ # help: local-pypi-start - Start local PyPI server on :8084 (no auth)
1448+ # help: local-pypi-start-auth - Start local PyPI server with basic auth (admin/admin)
1449+ # help: local-pypi-stop - Stop local PyPI server
1450+ # help: local-pypi-upload - Upload existing package to local PyPI (no auth)
1451+ # help: local-pypi-upload-auth - Upload existing package to local PyPI (with auth)
1452+ # help: local-pypi-test - Install package from local PyPI
1453+ # help: local-pypi-clean - Full cycle: build → upload → install locally
1454+
1455+ .PHONY : local-pypi-install local-pypi-start local-pypi-start-auth local-pypi-stop local-pypi-upload \
1456+ local-pypi-upload-auth local-pypi-test local-pypi-clean
1457+
1458+ LOCAL_PYPI_DIR := $(HOME ) /local-pypi
1459+ LOCAL_PYPI_URL := http://localhost:8085
1460+ LOCAL_PYPI_PID := /tmp/pypiserver.pid
1461+ LOCAL_PYPI_AUTH := $(LOCAL_PYPI_DIR ) /.htpasswd
1462+
1463+ local-pypi-install :
1464+ @echo " 📦 Installing pypiserver..."
1465+ @/bin/bash -c " source $( VENV_DIR) /bin/activate && pip install 'pypiserver>=2.3.0' passlib"
1466+ @mkdir -p $(LOCAL_PYPI_DIR )
1467+
1468+ local-pypi-start : local-pypi-install local-pypi-stop
1469+ @echo " 🚀 Starting local PyPI server on http://localhost:8084..."
1470+ @/bin/bash -c " source $( VENV_DIR) /bin/activate && \
1471+ export PYPISERVER_BOTTLE_MEMFILE_MAX_OVERRIDE_BYTES=10485760 && \
1472+ pypi-server run -p 8084 -a . -P . $(LOCAL_PYPI_DIR ) --hash-algo=sha256 & echo \$ ! > $(LOCAL_PYPI_PID ) "
1473+ @sleep 2
1474+ @echo " ✅ Local PyPI server started at http://localhost:8084"
1475+ @echo " 📂 Package directory: $( LOCAL_PYPI_DIR) "
1476+ @echo " 🔓 No authentication required (open mode)"
1477+
1478+ local-pypi-start-auth : local-pypi-install local-pypi-stop
1479+ @echo " 🚀 Starting local PyPI server with authentication on $( LOCAL_PYPI_URL) ..."
1480+ @echo " 🔐 Creating htpasswd file (admin/admin)..."
1481+ @mkdir -p $(LOCAL_PYPI_DIR )
1482+ @/bin/bash -c " source $( VENV_DIR) /bin/activate && \
1483+ python3 -c \" import passlib.hash; print(' admin:' + passlib.hash.sha256_crypt.hash(' admin' ))\" > $(LOCAL_PYPI_AUTH ) "
1484+ @/bin/bash -c " source $( VENV_DIR) /bin/activate && \
1485+ export PYPISERVER_BOTTLE_MEMFILE_MAX_OVERRIDE_BYTES=10485760 && \
1486+ pypi-server run -p 8085 -P $(LOCAL_PYPI_AUTH ) -a update,download,list $(LOCAL_PYPI_DIR ) --hash-algo=sha256 & echo \$ ! > $(LOCAL_PYPI_PID ) "
1487+ @sleep 2
1488+ @echo " ✅ Local PyPI server started at $( LOCAL_PYPI_URL) "
1489+ @echo " 📂 Package directory: $( LOCAL_PYPI_DIR) "
1490+ @echo " 🔐 Username: admin, Password: admin"
1491+
1492+ local-pypi-stop :
1493+ @echo " 🛑 Stopping local PyPI server..."
1494+ @if [ -f $( LOCAL_PYPI_PID) ]; then \
1495+ kill $(cat $(LOCAL_PYPI_PID ) ) 2> /dev/null || true ; \
1496+ rm -f $(LOCAL_PYPI_PID ) ; \
1497+ fi
1498+ @# Kill any pypi-server processes on ports 8084 and 8085
1499+ @pkill -f " pypi-server.*808[45]" 2> /dev/null || true
1500+ @# Wait a moment for cleanup
1501+ @sleep 1
1502+ @if lsof -i :8084 > /dev/null 2>&1 ; then \
1503+ echo " ⚠️ Port 8084 still in use, force killing..." ; \
1504+ sudo fuser -k 8084/tcp 2> /dev/null || true ; \
1505+ fi
1506+ @if lsof -i :8085 > /dev/null 2>&1 ; then \
1507+ echo " ⚠️ Port 8085 still in use, force killing..." ; \
1508+ sudo fuser -k 8085/tcp 2> /dev/null || true ; \
1509+ fi
1510+ @sleep 1
1511+ @echo " ✅ Server stopped"
1512+
1513+ local-pypi-upload :
1514+ @echo " 📤 Uploading existing package to local PyPI (no auth)..."
1515+ @if [ ! -d " dist" ] || [ -z " $$ (ls -A dist/ 2>/dev/null)" ]; then \
1516+ echo " ❌ No dist/ directory or files found. Run 'make dist' first." ; \
1517+ exit 1; \
1518+ fi
1519+ @if ! curl -s http://localhost:8084 > /dev/null 2>&1 ; then \
1520+ echo " ❌ Local PyPI server not running on port 8084. Run 'make local-pypi-start' first." ; \
1521+ exit 1; \
1522+ fi
1523+ @/bin/bash -c " source $( VENV_DIR) /bin/activate && \
1524+ twine upload --verbose --repository-url http://localhost:8084 --skip-existing dist/* "
1525+ @echo " ✅ Package uploaded to local PyPI"
1526+ @echo " 🌐 Browse packages: http://localhost:8084"
1527+
1528+ local-pypi-upload-auth :
1529+ @echo " 📤 Uploading existing package to local PyPI with auth..."
1530+ @if [ ! -d " dist" ] || [ -z " $$ (ls -A dist/ 2>/dev/null)" ]; then \
1531+ echo " ❌ No dist/ directory or files found. Run 'make dist' first." ; \
1532+ exit 1; \
1533+ fi
1534+ @if ! curl -s $(LOCAL_PYPI_URL ) > /dev/null 2>&1 ; then \
1535+ echo " ❌ Local PyPI server not running on port 8085. Run 'make local-pypi-start-auth' first." ; \
1536+ exit 1; \
1537+ fi
1538+ @/bin/bash -c " source $( VENV_DIR) /bin/activate && \
1539+ twine upload --verbose --repository-url $(LOCAL_PYPI_URL ) --username admin --password admin --skip-existing dist/* "
1540+ @echo " ✅ Package uploaded to local PyPI"
1541+ @echo " 🌐 Browse packages: $( LOCAL_PYPI_URL) "
1542+
1543+ local-pypi-test :
1544+ @echo " 📥 Installing from local PyPI..."
1545+ @/bin/bash -c " source $( VENV_DIR) /bin/activate && \
1546+ pip install --index-url $(LOCAL_PYPI_URL ) /simple/ \
1547+ --extra-index-url https://pypi.org/simple/ \
1548+ --force-reinstall $(PROJECT_NAME ) "
1549+ @echo " ✅ Installed from local PyPI"
1550+
1551+ local-pypi-clean : clean dist local-pypi-start-auth local-pypi-upload-auth local-pypi-test
1552+ @echo " 🎉 Full local PyPI cycle complete!"
1553+ @echo " 📊 Package info:"
1554+ @/bin/bash -c " source $( VENV_DIR) /bin/activate && pip show $( PROJECT_NAME) "
1555+
1556+ # Convenience target to restart server
1557+ local-pypi-restart : local-pypi-stop local-pypi-start
1558+
1559+ local-pypi-restart-auth : local-pypi-stop local-pypi-start-auth
1560+
1561+ # Show server status
1562+ local-pypi-status :
1563+ @echo " 🔍 Local PyPI server status:"
1564+ @if [ -f $( LOCAL_PYPI_PID) ] && kill -0 $(cat $(LOCAL_PYPI_PID ) ) 2> /dev/null; then \
1565+ echo " ✅ Server running (PID: $( cat $( LOCAL_PYPI_PID) ) )" ; \
1566+ if curl -s http://localhost:8084 > /dev/null 2>&1 ; then \
1567+ echo " 🌐 Server on port 8084: http://localhost:8084" ; \
1568+ elif curl -s $(LOCAL_PYPI_URL ) > /dev/null 2>&1 ; then \
1569+ echo " 🌐 Server on port 8085: $( LOCAL_PYPI_URL) " ; \
1570+ fi ; \
1571+ echo " 📂 Directory: $( LOCAL_PYPI_DIR) " ; \
1572+ else \
1573+ echo " ❌ Server not running" ; \
1574+ fi
1575+
1576+ # Debug target - run server in foreground with verbose logging
1577+ local-pypi-debug :
1578+ @echo " 🐛 Running local PyPI server in debug mode (Ctrl+C to stop)..."
1579+ @/bin/bash -c " source $( VENV_DIR) /bin/activate && \
1580+ export PYPISERVER_BOTTLE_MEMFILE_MAX_OVERRIDE_BYTES=10485760 && \
1581+ export BOTTLE_CHILD=true && \
1582+ pypi-server run -p 8085 --disable-fallback -a . -P . --server=auto $(LOCAL_PYPI_DIR ) -v"
1583+
1584+
1585+ # =============================================================================
1586+ # 🏠 LOCAL DEVPI SERVER
1587+ # =============================================================================
1588+ # help: 🏠 LOCAL DEVPI SERVER
1589+ # help: devpi-install - Install devpi server and client
1590+ # help: devpi-init - Initialize devpi server (first time only)
1591+ # help: devpi-start - Start devpi server
1592+ # help: devpi-stop - Stop devpi server
1593+ # help: devpi-setup-user - Create user and dev index
1594+ # help: devpi-upload - Upload existing package to devpi
1595+ # help: devpi-test - Install package from devpi
1596+ # help: devpi-clean - Full cycle: build → upload → install locally
1597+ # help: devpi-status - Show devpi server status
1598+ # help: devpi-web - Open devpi web interface
1599+
1600+ .PHONY : devpi-install devpi-init devpi-start devpi-stop devpi-setup-user devpi-upload \
1601+ devpi-test devpi-clean devpi-status devpi-web devpi-restart
1602+
1603+ DEVPI_HOST := localhost
1604+ DEVPI_PORT := 3141
1605+ DEVPI_URL := http://$(DEVPI_HOST ) :$(DEVPI_PORT )
1606+ DEVPI_USER := $(USER )
1607+ DEVPI_PASS := dev123
1608+ DEVPI_INDEX := $(DEVPI_USER ) /dev
1609+ DEVPI_DATA_DIR := $(HOME ) /.devpi
1610+ DEVPI_PID := /tmp/devpi-server.pid
1611+
1612+ devpi-install :
1613+ @echo " 📦 Installing devpi server and client..."
1614+ @/bin/bash -c " source $( VENV_DIR) /bin/activate && \
1615+ pip install devpi-server devpi-client devpi-web"
1616+ @echo " ✅ DevPi installed"
1617+
1618+ devpi-init : devpi-install
1619+ @echo " 🔧 Initializing devpi server (first time setup)..."
1620+ @if [ -d " $( DEVPI_DATA_DIR) /server" ] && [ -f " $( DEVPI_DATA_DIR) /server/.serverversion" ]; then \
1621+ echo " ⚠️ DevPi already initialized at $( DEVPI_DATA_DIR) " ; \
1622+ else \
1623+ mkdir -p $(DEVPI_DATA_DIR ) /server; \
1624+ /bin/bash -c " source $( VENV_DIR) /bin/activate && \
1625+ devpi-init --serverdir=$(DEVPI_DATA_DIR ) /server" ; \
1626+ echo " ✅ DevPi server initialized at $( DEVPI_DATA_DIR) /server" ; \
1627+ fi
1628+
1629+ devpi-start : devpi-init devpi-stop
1630+ @echo " 🚀 Starting devpi server on $( DEVPI_URL) ..."
1631+ @/bin/bash -c " source $( VENV_DIR) /bin/activate && \
1632+ devpi-server --serverdir=$(DEVPI_DATA_DIR ) /server \
1633+ --host=$(DEVPI_HOST ) \
1634+ --port=$(DEVPI_PORT ) & "
1635+ @# Wait for server to start and get the PID
1636+ @sleep 3
1637+ @ps aux | grep " [d]evpi-server" | grep " $( DEVPI_PORT) " | awk ' {print $2}' > $(DEVPI_PID ) || true
1638+ @# Wait a bit more and test if server is responding
1639+ @sleep 2
1640+ @if curl -s $(DEVPI_URL ) > /dev/null 2>&1 ; then \
1641+ if [ -s $( DEVPI_PID) ]; then \
1642+ echo " ✅ DevPi server started at $( DEVPI_URL) " ; \
1643+ echo " 📊 PID: $( cat $( DEVPI_PID) ) " ; \
1644+ else \
1645+ echo " ✅ DevPi server started at $( DEVPI_URL) " ; \
1646+ fi ; \
1647+ echo " 🌐 Web interface: $( DEVPI_URL) " ; \
1648+ echo " 📂 Data directory: $( DEVPI_DATA_DIR) " ; \
1649+ else \
1650+ echo " ❌ Failed to start devpi server or server not responding" ; \
1651+ echo " 🔍 Check logs with: make devpi-logs" ; \
1652+ exit 1; \
1653+ fi
1654+
1655+ devpi-stop :
1656+ @echo " 🛑 Stopping devpi server..."
1657+ @# Kill process by PID if exists
1658+ @if [ -f $( DEVPI_PID) ] && [ -s $( DEVPI_PID) ]; then \
1659+ pid=$(cat $(DEVPI_PID ) ) ; \
1660+ if kill -0 $pid 2> /dev/null; then \
1661+ echo " 🔄 Stopping devpi server (PID: $pid )" ; \
1662+ kill $pid 2> /dev/null || true ; \
1663+ sleep 2; \
1664+ kill -9 $pid 2> /dev/null || true ; \
1665+ fi ; \
1666+ rm -f $(DEVPI_PID ) ; \
1667+ fi
1668+ @# Kill any remaining devpi-server processes
1669+ @pids=$(pgrep -f "devpi-server.*$(DEVPI_PORT ) " 2>/dev/null || true ) ; \
1670+ if [ -n " $pids " ]; then \
1671+ echo " 🔄 Killing remaining devpi processes: $pids " ; \
1672+ echo " $pids " | xargs -r kill 2> /dev/null || true ; \
1673+ sleep 1; \
1674+ echo " $pids " | xargs -r kill -9 2> /dev/null || true ; \
1675+ fi
1676+ @# Force kill anything using the port
1677+ @if lsof -ti :$(DEVPI_PORT ) > /dev/null 2>&1 ; then \
1678+ echo " ⚠️ Port $( DEVPI_PORT) still in use, force killing..." ; \
1679+ lsof -ti :$(DEVPI_PORT ) | xargs -r kill -9 2> /dev/null || true ; \
1680+ sleep 1; \
1681+ fi
1682+ @echo " ✅ DevPi server stopped"
1683+
1684+ devpi-setup-user : devpi-start
1685+ @echo " 👤 Setting up devpi user and index..."
1686+ @/bin/bash -c " source $( VENV_DIR) /bin/activate && \
1687+ devpi use $(DEVPI_URL ) && \
1688+ (devpi user -c $( DEVPI_USER) password=$( DEVPI_PASS) email=$( DEVPI_USER) @localhost.local 2> /dev/null || \
1689+ echo ' User $(DEVPI_USER) already exists' ) && \
1690+ devpi login $(DEVPI_USER ) --password=$(DEVPI_PASS ) && \
1691+ (devpi index -c dev bases=root/pypi volatile=False 2> /dev/null || \
1692+ echo ' Index dev already exists' ) && \
1693+ devpi use $(DEVPI_INDEX ) "
1694+ @echo " ✅ User '$( DEVPI_USER) ' and index 'dev' configured"
1695+ @echo " 📝 Login: $( DEVPI_USER) / $( DEVPI_PASS) "
1696+ @echo " 📍 Using index: $( DEVPI_INDEX) "
1697+
1698+ devpi-upload : devpi-setup-user
1699+ @echo " 📤 Uploading existing package to devpi..."
1700+ @if [ ! -d " dist" ] || [ -z " $$ (ls -A dist/ 2>/dev/null)" ]; then \
1701+ echo " ❌ No dist/ directory or files found. Run 'make dist' first." ; \
1702+ exit 1; \
1703+ fi
1704+ @if ! curl -s $(DEVPI_URL ) > /dev/null 2>&1 ; then \
1705+ echo " ❌ DevPi server not running. Run 'make devpi-start' first." ; \
1706+ exit 1; \
1707+ fi
1708+ @/bin/bash -c " source $( VENV_DIR) /bin/activate && \
1709+ devpi use $(DEVPI_INDEX ) && \
1710+ devpi upload dist/* "
1711+ @echo " ✅ Package uploaded to devpi"
1712+ @echo " 🌐 Browse packages: $( DEVPI_URL) /$( DEVPI_INDEX) "
1713+
1714+ devpi-test :
1715+ @echo " 📥 Installing package from devpi..."
1716+ @if ! curl -s $(DEVPI_URL ) > /dev/null 2>&1 ; then \
1717+ echo " ❌ DevPi server not running. Run 'make devpi-start' first." ; \
1718+ exit 1; \
1719+ fi
1720+ @/bin/bash -c " source $( VENV_DIR) /bin/activate && \
1721+ pip install --index-url $(DEVPI_URL ) /$(DEVPI_INDEX ) /+simple/ \
1722+ --extra-index-url https://pypi.org/simple/ \
1723+ --force-reinstall $(PROJECT_NAME ) "
1724+ @echo " ✅ Installed $( PROJECT_NAME) from devpi"
1725+
1726+ devpi-clean : clean dist devpi-upload devpi-test
1727+ @echo " 🎉 Full devpi cycle complete!"
1728+ @echo " 📊 Package info:"
1729+ @/bin/bash -c " source $( VENV_DIR) /bin/activate && pip show $( PROJECT_NAME) "
1730+
1731+ devpi-status :
1732+ @echo " 🔍 DevPi server status:"
1733+ @if curl -s $(DEVPI_URL ) > /dev/null 2>&1 ; then \
1734+ echo " ✅ Server running at $( DEVPI_URL) " ; \
1735+ if [ -f $( DEVPI_PID) ] && [ -s $( DEVPI_PID) ]; then \
1736+ echo " 📊 PID: $$ (cat $( DEVPI_PID) )" ; \
1737+ fi ; \
1738+ echo " 📂 Data directory: $( DEVPI_DATA_DIR) " ; \
1739+ /bin/bash -c " source $( VENV_DIR) /bin/activate && \
1740+ devpi use $(DEVPI_URL ) > /dev/null 2>&1 && \
1741+ devpi user --list 2> /dev/null || echo ' 📝 Not logged in' " ; \
1742+ else \
1743+ echo " ❌ Server not running" ; \
1744+ fi
1745+
1746+ devpi-web :
1747+ @echo " 🌐 Opening devpi web interface..."
1748+ @if curl -s $(DEVPI_URL ) > /dev/null 2>&1 ; then \
1749+ echo " 📱 Web interface: $( DEVPI_URL) " ; \
1750+ which open > /dev/null 2>&1 && open $(DEVPI_URL ) || \
1751+ which xdg-open > /dev/null 2>&1 && xdg-open $(DEVPI_URL ) || \
1752+ echo " 🔗 Open $( DEVPI_URL) in your browser" ; \
1753+ else \
1754+ echo " ❌ DevPi server not running. Run 'make devpi-start' first." ; \
1755+ fi
1756+
1757+ devpi-restart : devpi-stop devpi-start
1758+ @echo " 🔄 DevPi server restarted"
1759+
1760+ # Advanced targets for devpi management
1761+ devpi-reset : devpi-stop
1762+ @echo " ⚠️ Resetting devpi server (this will delete all data)..."
1763+ @read -p " Are you sure? This will delete all packages and users [y/N]: " confirm; \
1764+ if [ " $$ confirm" = " y" ] || [ " $$ confirm" = " Y" ]; then \
1765+ rm -rf $(DEVPI_DATA_DIR ) ; \
1766+ echo " ✅ DevPi data reset. Run 'make devpi-init' to reinitialize." ; \
1767+ else \
1768+ echo " ❌ Reset cancelled." ; \
1769+ fi
1770+
1771+ devpi-backup :
1772+ @echo " 💾 Backing up devpi data..."
1773+ @timestamp=$$(date +%Y%m%d-%H%M%S ) ; \
1774+ backup_file=" $( HOME) /devpi-backup-$$ timestamp.tar.gz" ; \
1775+ tar -czf " $$ backup_file" -C $(HOME ) .devpi 2> /dev/null && \
1776+ echo " ✅ Backup created: $$ backup_file" || \
1777+ echo " ❌ Backup failed"
1778+
1779+ devpi-logs :
1780+ @echo " 📋 DevPi server logs:"
1781+ @if [ -f " $( DEVPI_DATA_DIR) /server/devpi.log" ]; then \
1782+ tail -f " $( DEVPI_DATA_DIR) /server/devpi.log" ; \
1783+ elif [ -f " $( DEVPI_DATA_DIR) /server/.xproc/devpi-server/xprocess.log" ]; then \
1784+ tail -f " $( DEVPI_DATA_DIR) /server/.xproc/devpi-server/xprocess.log" ; \
1785+ elif [ -f " $( DEVPI_DATA_DIR) /server/devpi-server.log" ]; then \
1786+ tail -f " $( DEVPI_DATA_DIR) /server/devpi-server.log" ; \
1787+ else \
1788+ echo " ❌ No log file found. Checking if server is running..." ; \
1789+ ps aux | grep " [d]evpi-server" || echo " Server not running" ; \
1790+ echo " 📂 Expected log location: $( DEVPI_DATA_DIR) /server/devpi.log" ; \
1791+ fi
1792+
1793+ # Configuration helper - creates pip.conf for easy devpi usage
1794+ devpi-configure-pip :
1795+ @echo " ⚙️ Configuring pip to use devpi by default..."
1796+ @mkdir -p $(HOME ) /.pip
1797+ @echo " [global]" > $(HOME ) /.pip/pip.conf
1798+ @echo " index-url = $( DEVPI_URL) /$( DEVPI_INDEX) /+simple/" >> $(HOME ) /.pip/pip.conf
1799+ @echo " extra-index-url = https://pypi.org/simple/" >> $(HOME ) /.pip/pip.conf
1800+ @echo " trusted-host = $( DEVPI_HOST) " >> $(HOME ) /.pip/pip.conf
1801+ @echo " " >> $(HOME ) /.pip/pip.conf
1802+ @echo " [search]" >> $(HOME ) /.pip/pip.conf
1803+ @echo " index = $( DEVPI_URL) /$( DEVPI_INDEX) /" >> $(HOME ) /.pip/pip.conf
1804+ @echo " ✅ Pip configured to use devpi at $( DEVPI_URL) /$( DEVPI_INDEX) "
1805+ @echo " 📝 Config file: $( HOME) /.pip/pip.conf"
1806+
1807+ # Remove pip devpi configuration
1808+ devpi-unconfigure-pip :
1809+ @echo " 🔧 Removing devpi from pip configuration..."
1810+ @if [ -f " $( HOME) /.pip/pip.conf" ]; then \
1811+ rm " $( HOME) /.pip/pip.conf" ; \
1812+ echo " ✅ Pip configuration reset to defaults" ; \
1813+ else \
1814+ echo " ℹ️ No pip configuration found" ; \
1815+ fi
0 commit comments