mirror of
https://github.com/jmagar/unraid-mcp.git
synced 2026-03-01 16:04:24 -08:00
feat: harden API safety and expand command docs with full test coverage
This commit is contained in:
218
docs/PUBLISHING.md
Normal file
218
docs/PUBLISHING.md
Normal file
@@ -0,0 +1,218 @@
|
||||
# Publishing Guide for unraid-mcp
|
||||
|
||||
This guide covers how to publish `unraid-mcp` to PyPI so it can be installed via `uvx` or `pip` from anywhere.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. **PyPI Account**: Create accounts on both:
|
||||
- [Test PyPI](https://test.pypi.org/account/register/) (for testing)
|
||||
- [PyPI](https://pypi.org/account/register/) (for production)
|
||||
|
||||
2. **API Tokens**: Generate API tokens for automated publishing:
|
||||
- Test PyPI: https://test.pypi.org/manage/account/token/
|
||||
- PyPI: https://pypi.org/manage/account/token/
|
||||
|
||||
3. **Save Tokens Securely**:
|
||||
```bash
|
||||
# Add to ~/.pypirc (never commit this file!)
|
||||
cat > ~/.pypirc << 'EOF'
|
||||
[distutils]
|
||||
index-servers =
|
||||
pypi
|
||||
testpypi
|
||||
|
||||
[pypi]
|
||||
username = __token__
|
||||
password = pypi-YOUR-API-TOKEN-HERE
|
||||
|
||||
[testpypi]
|
||||
username = __token__
|
||||
password = pypi-YOUR-TEST-API-TOKEN-HERE
|
||||
repository = https://test.pypi.org/legacy/
|
||||
EOF
|
||||
|
||||
chmod 600 ~/.pypirc # Secure the file
|
||||
```
|
||||
|
||||
## Version Management
|
||||
|
||||
Before publishing, update the version in `pyproject.toml`:
|
||||
|
||||
```toml
|
||||
[project]
|
||||
version = "0.2.1" # Follow semantic versioning: MAJOR.MINOR.PATCH
|
||||
```
|
||||
|
||||
**Semantic Versioning Guide:**
|
||||
- **MAJOR** (1.0.0): Breaking changes
|
||||
- **MINOR** (0.2.0): New features (backward compatible)
|
||||
- **PATCH** (0.2.1): Bug fixes (backward compatible)
|
||||
|
||||
## Publishing Workflow
|
||||
|
||||
### 1. Clean Previous Builds
|
||||
|
||||
```bash
|
||||
# Remove old build artifacts
|
||||
rm -rf dist/ build/ *.egg-info/
|
||||
```
|
||||
|
||||
### 2. Run Quality Checks
|
||||
|
||||
```bash
|
||||
# Lint and format code
|
||||
uv run ruff check unraid_mcp/
|
||||
uv run ruff format unraid_mcp/
|
||||
|
||||
# Type check
|
||||
uv run ty check unraid_mcp/
|
||||
|
||||
# Run tests
|
||||
uv run pytest
|
||||
|
||||
# Check coverage
|
||||
uv run pytest --cov=unraid_mcp --cov-report=html
|
||||
```
|
||||
|
||||
### 3. Build the Package
|
||||
|
||||
```bash
|
||||
# Build both wheel and source distribution
|
||||
uv run python -m build
|
||||
```
|
||||
|
||||
This creates:
|
||||
- `dist/unraid_mcp-VERSION-py3-none-any.whl` (wheel)
|
||||
- `dist/unraid_mcp-VERSION.tar.gz` (source distribution)
|
||||
|
||||
### 4. Validate the Package
|
||||
|
||||
```bash
|
||||
# Check that the package meets PyPI requirements
|
||||
uv run twine check dist/*
|
||||
```
|
||||
|
||||
Expected output: `PASSED` for both files.
|
||||
|
||||
### 5. Test on Test PyPI (IMPORTANT!)
|
||||
|
||||
Always test on Test PyPI first:
|
||||
|
||||
```bash
|
||||
# Upload to Test PyPI
|
||||
uv run twine upload --repository testpypi dist/*
|
||||
|
||||
# Test installation from Test PyPI
|
||||
uvx --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ unraid-mcp-server
|
||||
|
||||
# Or with pip in a test environment
|
||||
pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ unraid-mcp
|
||||
```
|
||||
|
||||
**Note**: `--extra-index-url https://pypi.org/simple/` is needed because dependencies come from production PyPI.
|
||||
|
||||
### 6. Publish to PyPI (Production)
|
||||
|
||||
Once testing passes:
|
||||
|
||||
```bash
|
||||
# Upload to production PyPI
|
||||
uv run twine upload dist/*
|
||||
```
|
||||
|
||||
### 7. Verify Installation
|
||||
|
||||
```bash
|
||||
# Install and run from PyPI using uvx (no installation required!)
|
||||
uvx unraid-mcp-server --help
|
||||
|
||||
# Or install globally
|
||||
uv tool install unraid-mcp
|
||||
|
||||
# Or install in a project
|
||||
uv add unraid-mcp
|
||||
```
|
||||
|
||||
## Post-Publishing Checklist
|
||||
|
||||
- [ ] Create a GitHub Release with the same version tag
|
||||
- [ ] Update CHANGELOG.md with release notes
|
||||
- [ ] Test installation on a fresh machine
|
||||
- [ ] Update documentation if API changed
|
||||
- [ ] Announce release (if applicable)
|
||||
|
||||
## Running from Any Machine with uvx
|
||||
|
||||
Once published to PyPI, users can run the server without installing:
|
||||
|
||||
```bash
|
||||
# Run directly with uvx (recommended)
|
||||
uvx unraid-mcp-server
|
||||
|
||||
# Or with custom environment variables
|
||||
UNRAID_API_URL=https://your-server uvx unraid-mcp-server
|
||||
```
|
||||
|
||||
**Benefits of uvx:**
|
||||
- No installation required
|
||||
- Automatic virtual environment management
|
||||
- Always uses the latest version (or specify version: `uvx unraid-mcp-server@0.2.0`)
|
||||
- Clean execution environment
|
||||
|
||||
## Automation with GitHub Actions (Future)
|
||||
|
||||
Consider adding `.github/workflows/publish.yml`:
|
||||
|
||||
```yaml
|
||||
name: Publish to PyPI
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: astral-sh/setup-uv@v4
|
||||
- name: Build package
|
||||
run: uv run python -m build
|
||||
- name: Publish to PyPI
|
||||
env:
|
||||
TWINE_USERNAME: __token__
|
||||
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
|
||||
run: uv run twine upload dist/*
|
||||
```
|
||||
|
||||
> **Tip:** Consider using [PyPI Trusted Publishing](https://docs.pypi.org/trusted-publishers/) instead of API token secrets. Trusted Publishing uses OpenID Connect (OIDC) to authenticate directly from GitHub Actions without storing long-lived secrets.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "File already exists" Error
|
||||
|
||||
PyPI doesn't allow re-uploading the same version. Options:
|
||||
1. Increment version number in `pyproject.toml`
|
||||
2. Delete old dist files and rebuild
|
||||
|
||||
### Missing Dependencies
|
||||
|
||||
If installation fails due to missing dependencies:
|
||||
1. Check that all dependencies are in `pyproject.toml` `dependencies` section
|
||||
2. Ensure dependency version constraints are correct
|
||||
3. Test in a clean virtual environment
|
||||
|
||||
### Import Errors After Installation
|
||||
|
||||
If the package installs but imports fail:
|
||||
1. Verify package structure in wheel: `unzip -l dist/*.whl`
|
||||
2. Check that `__init__.py` files exist in all package directories
|
||||
3. Ensure `packages = ["unraid_mcp"]` in `[tool.hatch.build.targets.wheel]`
|
||||
|
||||
## Resources
|
||||
|
||||
- [PyPI Publishing Guide](https://packaging.python.org/tutorials/packaging-projects/)
|
||||
- [Semantic Versioning](https://semver.org/)
|
||||
- [Python Packaging User Guide](https://packaging.python.org/)
|
||||
- [uv Documentation](https://docs.astral.sh/uv/)
|
||||
- [Twine Documentation](https://twine.readthedocs.io/)
|
||||
Reference in New Issue
Block a user