From 09972bc3dafe23d2235d6ed59649c1bde7f07323 Mon Sep 17 00:00:00 2001 From: ayahaustine Date: Mon, 16 Mar 2026 16:34:58 +0300 Subject: [PATCH 1/6] chore: add MANIFEST.in for package distribution --- MANIFEST.in | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 MANIFEST.in diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..0b3a71c --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,28 @@ +# Include documentation files +include README.md +include LICENSE +include CHANGELOG.md +include CONTRIBUTING.md +include CODE_OF_CONDUCT.md + +# Include static assets +recursive-include internal_admin/static *.css *.js *.png *.jpg *.svg *.ico +recursive-include internal_admin/templates *.html + +# Include configuration +include pyproject.toml + +# Exclude development and build artifacts +exclude .env +exclude .env.example +exclude demo.py +exclude example.py +exclude cookies.txt +exclude *.db +recursive-exclude tests * +recursive-exclude .github * +recursive-exclude __pycache__ * +recursive-exclude *.egg-info * +global-exclude *.pyc +global-exclude *.pyo +global-exclude .DS_Store From 16caba8dafe77d93467bb74fac3e6778917b5042 Mon Sep 17 00:00:00 2001 From: ayahaustine Date: Mon, 16 Mar 2026 16:35:07 +0300 Subject: [PATCH 2/6] chore: update build config for source dist --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 1a21554..86627a6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -80,6 +80,7 @@ include = [ "/CHANGELOG.md", "/README.md", "/LICENSE", + "/pyproject.toml", ] # --------------------------------------------------------------------------- From b29fd3c289b67dfb821168691da23efeeee5cbab Mon Sep 17 00:00:00 2001 From: ayahaustine Date: Mon, 16 Mar 2026 16:35:09 +0300 Subject: [PATCH 3/6] ci: enhance release workflow with tests and verification --- .github/workflows/release.yml | 94 ++++++++++++++++++++++++++++++++++- 1 file changed, 93 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 143b72f..4a2d189 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,6 +3,17 @@ name: Release on: push: branches: ["main"] + workflow_dispatch: + inputs: + release_type: + description: 'Release type (leave empty for auto)' + required: false + type: choice + options: + - '' + - 'patch' + - 'minor' + - 'major' # Only one release job runs at a time; queued runs wait rather than cancel. concurrency: @@ -10,9 +21,40 @@ concurrency: cancel-in-progress: false jobs: + # Run tests before releasing + test: + name: Run tests before release + runs-on: ubuntu-latest + if: "!contains(github.event.head_commit.message, '[skip ci]')" + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + cache: pip + + - name: Install dependencies + run: pip install -e ".[dev]" + + - name: Run linting + run: | + black --check internal_admin tests + isort --check-only internal_admin tests + ruff check internal_admin tests + + - name: Run type checking + run: mypy internal_admin + + - name: Run tests + run: pytest tests/ --cov=internal_admin --cov-report=term-missing + release: name: Semantic release runs-on: ubuntu-latest + needs: test # Prevent release from running on the automated version-bump commit itself. if: "!contains(github.event.head_commit.message, '[skip ci]')" @@ -21,6 +63,11 @@ jobs: id-token: write # required for PyPI trusted publishing contents: write # required to push the version-bump commit and tag + outputs: + released: ${{ steps.semantic.outputs.released }} + version: ${{ steps.semantic.outputs.version }} + tag: ${{ steps.semantic.outputs.tag }} + steps: - uses: actions/checkout@v4 with: @@ -33,7 +80,7 @@ jobs: python-version: "3.12" cache: pip - - name: Install dev dependencies + - name: Install dependencies run: pip install -e ".[dev]" - name: Configure git identity @@ -56,8 +103,53 @@ jobs: env: GH_TOKEN: ${{ secrets.GH_TOKEN }} + - name: Upload build artifacts + if: steps.semantic.outputs.released == 'true' + uses: actions/upload-artifact@v4 + with: + name: dist-packages + path: dist/ + retention-days: 30 + - name: Publish to PyPI if: steps.semantic.outputs.released == 'true' run: semantic-release publish env: GH_TOKEN: ${{ secrets.GH_TOKEN }} + + - name: Create release summary + if: steps.semantic.outputs.released == 'true' + run: | + echo "## 🎉 Release Published" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Version:** ${{ steps.semantic.outputs.version }}" >> $GITHUB_STEP_SUMMARY + echo "**Tag:** ${{ steps.semantic.outputs.tag }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### 📦 Published to" >> $GITHUB_STEP_SUMMARY + echo "- [PyPI](https://pypi.org/project/internal-admin/${{ steps.semantic.outputs.version }}/)" >> $GITHUB_STEP_SUMMARY + echo "- [GitHub Releases](https://github.com/${{ github.repository }}/releases/tag/${{ steps.semantic.outputs.tag }})" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### 📥 Install with" >> $GITHUB_STEP_SUMMARY + echo '```bash' >> $GITHUB_STEP_SUMMARY + echo "pip install internal-admin==${{ steps.semantic.outputs.version }}" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + + verify: + name: Verify PyPI publication + runs-on: ubuntu-latest + needs: release + if: needs.release.outputs.released == 'true' + + steps: + - name: Wait for PyPI to update + run: sleep 60 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Verify package on PyPI + run: | + pip install internal-admin==${{ needs.release.outputs.version }} + python -c "from internal_admin import AdminSite, ModelAdmin, AdminConfig; print('✓ Package successfully installed from PyPI')" From 5540ba7d1b4749cb574cad1d2ba5ef6a735f2d96 Mon Sep 17 00:00:00 2001 From: ayahaustine Date: Mon, 16 Mar 2026 16:35:15 +0300 Subject: [PATCH 4/6] ci: add TestPyPI publishing workflow --- .github/workflows/publish-testpypi.yml | 113 +++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 .github/workflows/publish-testpypi.yml diff --git a/.github/workflows/publish-testpypi.yml b/.github/workflows/publish-testpypi.yml new file mode 100644 index 0000000..74f8c0f --- /dev/null +++ b/.github/workflows/publish-testpypi.yml @@ -0,0 +1,113 @@ +name: Publish to TestPyPI + +# Manual workflow for testing package publishing to TestPyPI +# This allows you to verify the package before publishing to production PyPI + +on: + workflow_dispatch: + inputs: + build_only: + description: 'Only build package (do not upload)' + required: false + type: boolean + default: false + +jobs: + build: + name: Build distribution packages + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + cache: pip + + - name: Install build dependencies + run: | + python -m pip install --upgrade pip + pip install build twine + + - name: Build package + run: python -m build + + - name: Check package + run: twine check dist/* + + - name: List package contents + run: | + echo "## 📦 Package Contents" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Wheel file:" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + unzip -l dist/*.whl | grep -E "(internal_admin|\.py$|static|templates)" | head -40 >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: dist-packages + path: dist/ + retention-days: 7 + + publish-testpypi: + name: Publish to TestPyPI + runs-on: ubuntu-latest + needs: build + if: inputs.build_only != true + + permissions: + id-token: write # Required for trusted publishing + + steps: + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + name: dist-packages + path: dist/ + + - name: Publish to TestPyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + repository-url: https://test.pypi.org/legacy/ + skip-existing: true + print-hash: true + + - name: Create success summary + run: | + echo "## Published to TestPyPI" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "Test installation with:" >> $GITHUB_STEP_SUMMARY + echo '```bash' >> $GITHUB_STEP_SUMMARY + echo "pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ internal-admin" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "View on TestPyPI: https://test.pypi.org/project/internal-admin/" >> $GITHUB_STEP_SUMMARY + + verify: + name: Verify TestPyPI installation + runs-on: ubuntu-latest + needs: publish-testpypi + if: inputs.build_only != true + + steps: + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Wait for TestPyPI to update + run: sleep 30 + + - name: Install from TestPyPI + run: | + pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ internal-admin + continue-on-error: true + + - name: Test import + run: | + python -c "from internal_admin import AdminSite, ModelAdmin, AdminConfig; print('✓ Package successfully installed from TestPyPI')" + continue-on-error: true From c22630be5967a8da06e5b280751424256fb974ea Mon Sep 17 00:00:00 2001 From: ayahaustine Date: Mon, 16 Mar 2026 16:35:16 +0300 Subject: [PATCH 5/6] chore: add pre-publish validation script --- scripts/pre_publish_check.sh | 204 +++++++++++++++++++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100755 scripts/pre_publish_check.sh diff --git a/scripts/pre_publish_check.sh b/scripts/pre_publish_check.sh new file mode 100755 index 0000000..00b9fc7 --- /dev/null +++ b/scripts/pre_publish_check.sh @@ -0,0 +1,204 @@ +#!/usr/bin/env bash + +# Pre-publish verification script for internal-admin +# Run this script before publishing to PyPI to ensure everything is ready + +set -e # Exit on any error + +echo "🔍 Pre-publish Checklist for internal-admin" +echo "===========================================" +echo "" + +# Color codes +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Counters +PASSED=0 +FAILED=0 +WARNINGS=0 + +check_pass() { + echo -e "${GREEN}✓${NC} $1" + ((PASSED++)) +} + +check_fail() { + echo -e "${RED}✗${NC} $1" + ((FAILED++)) +} + +check_warn() { + echo -e "${YELLOW}⚠${NC} $1" + ((WARNINGS++)) +} + +# 1. Check if virtual environment is activated +echo "1. Checking virtual environment..." +if [[ -n "$VIRTUAL_ENV" ]]; then + check_pass "Virtual environment is activated" +else + check_warn "Virtual environment not activated (recommended)" +fi +echo "" + +# 2. Check if required files exist +echo "2. Checking required files..." +for file in "README.md" "LICENSE" "pyproject.toml" "CHANGELOG.md" "MANIFEST.in"; do + if [[ -f "$file" ]]; then + check_pass "$file exists" + else + check_fail "$file is missing" + fi +done +echo "" + +# 3. Check Python version +echo "3. Checking Python version..." +PYTHON_VERSION=$(python --version 2>&1 | awk '{print $2}') +REQUIRED_MAJOR=3 +REQUIRED_MINOR=10 + +CURRENT_MAJOR=$(echo $PYTHON_VERSION | cut -d. -f1) +CURRENT_MINOR=$(echo $PYTHON_VERSION | cut -d. -f2) + +if [[ $CURRENT_MAJOR -ge $REQUIRED_MAJOR && $CURRENT_MINOR -ge $REQUIRED_MINOR ]]; then + check_pass "Python version $PYTHON_VERSION (>= 3.10)" +else + check_fail "Python version $PYTHON_VERSION (requires >= 3.10)" +fi +echo "" + +# 4. Check if build tools are installed +echo "4. Checking build tools..." +if python -c "import build" 2>/dev/null; then + check_pass "build package is installed" +else + check_fail "build package not installed (run: pip install build)" +fi + +if python -c "import twine" 2>/dev/null; then + check_pass "twine package is installed" +else + check_warn "twine package not installed (optional, run: pip install twine)" +fi +echo "" + +# 5. Run linting checks +echo "5. Running linting checks..." +if command -v black &> /dev/null; then + if black --check internal_admin tests &> /dev/null; then + check_pass "black formatting check passed" + else + check_fail "black formatting check failed (run: black internal_admin tests)" + fi +else + check_warn "black not installed" +fi + +if command -v isort &> /dev/null; then + if isort --check-only internal_admin tests &> /dev/null; then + check_pass "isort check passed" + else + check_fail "isort check failed (run: isort internal_admin tests)" + fi +else + check_warn "isort not installed" +fi + +if command -v ruff &> /dev/null; then + if ruff check internal_admin tests &> /dev/null; then + check_pass "ruff check passed" + else + check_fail "ruff check failed (run: ruff check internal_admin tests)" + fi +else + check_warn "ruff not installed" +fi +echo "" + +# 6. Run tests +echo "6. Running tests..." +if command -v pytest &> /dev/null; then + if pytest tests/ -q &> /dev/null; then + check_pass "All tests passed" + else + check_fail "Some tests failed (run: pytest tests/ -v)" + fi +else + check_warn "pytest not installed" +fi +echo "" + +# 7. Check version consistency +echo "7. Checking version consistency..." +VERSION_TOML=$(grep '^version = ' pyproject.toml | cut -d'"' -f2) +VERSION_INIT=$(grep '^__version__ = ' internal_admin/__init__.py | cut -d'"' -f2) + +if [[ "$VERSION_TOML" == "$VERSION_INIT" ]]; then + check_pass "Version consistent: $VERSION_TOML" +else + check_fail "Version mismatch: pyproject.toml ($VERSION_TOML) vs __init__.py ($VERSION_INIT)" +fi +echo "" + +# 8. Build the package +echo "8. Building package..." +if python -m build &> /tmp/build.log; then + check_pass "Package built successfully" + + # Check wheel contents + if unzip -l dist/*.whl | grep -q "internal_admin/static"; then + check_pass "Static files included in wheel" + else + check_fail "Static files missing from wheel" + fi + + if unzip -l dist/*.whl | grep -q "internal_admin/templates"; then + check_pass "Template files included in wheel" + else + check_fail "Template files missing from wheel" + fi +else + check_fail "Package build failed (check /tmp/build.log)" +fi +echo "" + +# 9. Check package with twine +echo "9. Validating package with twine..." +if command -v twine &> /dev/null; then + if twine check dist/* &> /dev/null; then + check_pass "Package validation passed" + else + check_fail "Package validation failed" + fi +else + check_warn "twine not available, skipping validation" +fi +echo "" + +# Summary +echo "" +echo "===========================================" +echo "Summary:" +echo -e "${GREEN}Passed: $PASSED${NC}" +echo -e "${YELLOW}Warnings: $WARNINGS${NC}" +echo -e "${RED}Failed: $FAILED${NC}" +echo "" + +if [[ $FAILED -eq 0 ]]; then + echo -e "${GREEN}✓ Package is ready for publishing!${NC}" + echo "" + echo "Next steps:" + echo " 1. Review PUBLISHING.md for publishing instructions" + echo " 2. Commit changes using conventional commits" + echo " 3. Push to main branch to trigger automatic release" + echo " OR" + echo " Manual publish: twine upload dist/*" + exit 0 +else + echo -e "${RED}✗ Please fix the failed checks before publishing${NC}" + exit 1 +fi From a6f7adcbc129e2c3789ec60c25a54cb568156e83 Mon Sep 17 00:00:00 2001 From: ayahaustine Date: Mon, 16 Mar 2026 16:35:24 +0300 Subject: [PATCH 6/6] docs: add workflow status badges to README --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 5691dec..bd8a09e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,11 @@ # Internal Admin +[![CI](https://github.com/ayahaustine/internal-admin/workflows/CI/badge.svg)](https://github.com/ayahaustine/internal-admin/actions/workflows/ci.yml) +[![Release](https://github.com/ayahaustine/internal-admin/workflows/Release/badge.svg)](https://github.com/ayahaustine/internal-admin/actions/workflows/release.yml) +[![PyPI version](https://badge.fury.io/py/internal-admin.svg)](https://pypi.org/project/internal-admin/) +[![Python Versions](https://img.shields.io/pypi/pyversions/internal-admin.svg)](https://pypi.org/project/internal-admin/) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) + A reusable, pip-installable administrative framework for FastAPI applications. Automatically generates a full CRUD interface from your SQLAlchemy models, with session-based authentication, role-based permissions, and a clean Bootstrap 5 UI