Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
f2f7455
feat: migrate to modern PyPI packaging with hatchling
coketaste May 1, 2026
81eed01
docs: update CHANGELOG for v2.1.0 with PyPI packaging migration
coketaste May 1, 2026
f5c8c4d
fix(ci): add fetch-depth to lint job, flake8 config, and asset checks
coketaste May 1, 2026
d269572
chore: remove MANIFEST.in (unused by hatchling build backend)
coketaste May 1, 2026
e0ff8cc
style: format codebase with black + isort, fix unused imports/vars
coketaste May 1, 2026
8187fdc
fix(ci): exclude e2e tests that require Docker and GPU
coketaste May 1, 2026
71420a7
fix(ci): remove pytest.ini that overrode pyproject.toml and collected…
coketaste May 2, 2026
4939561
fix(test): strip ANSI codes before asserting CLI help content
coketaste May 2, 2026
2a62a64
Merge branch 'develop' into coketaste/v2-pypi
coketaste Aug 31, 2026
c51b256
style: format merged code with black + isort, fix flake8 findings
coketaste Aug 31, 2026
2bf641e
fix(ci): drop removed [dev] extra and stabilize import across isort v…
coketaste Sep 1, 2026
72b95fd
docs(changelog): restore accurate 3.8 note in the v2.0.0 section
coketaste Sep 1, 2026
6c966a8
feat: enforce pinned image digest for madengine run (#178)
coketaste Sep 1, 2026
50bef03
revert: "feat: enforce pinned image digest for madengine run (#178)"
coketaste Sep 1, 2026
5ecdacd
fix(build): package deployment preset JSON files in wheel/sdist
coketaste Sep 1, 2026
09c5a91
Merge remote-tracking branch 'origin' into coketaste/v2-pypi
coketaste Sep 1, 2026
d6174fa
Merge remote-tracking branch 'origin/develop' into coketaste/v2-pypi
coketaste Sep 1, 2026
36e6b88
style: apply black and isort to merged test files
coketaste Sep 1, 2026
5eae213
fix(test): stub kubeconfig loading in TestK8sRequirePinnedImage
coketaste Sep 1, 2026
8a94c7b
ci: do not cancel the whole test matrix on one failure
coketaste Sep 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[flake8]
max-line-length = 88
extend-ignore =
E203,
E266,
E402,
E501,
E741,
F541,
F811,
F824,
W503,
W291,
W293,
W391
per-file-ignores =
tests/*:F401,F811,F405,F403,F841,E402,E712,E713
exclude =
.git,
__pycache__,
build,
dist,
.eggs,
*.egg-info,
venv,
.venv,
src/madengine/scripts/,
tests/fixtures/dummy/scripts/
44 changes: 44 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: CI

on:
push:
branches: [develop, main]
pull_request:
branches: [develop, main]

jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ['3.9', '3.10', '3.11', '3.12']
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: pip install -e .
- name: Run tests
run: pytest -m "not slow and not integration" --ignore=tests/e2e/

lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install dependencies
run: pip install -e .
- name: Check formatting (black)
run: black --check src/ tests/
- name: Check imports (isort)
run: isort --check src/ tests/
- name: Lint (flake8)
run: flake8 src/ tests/
109 changes: 109 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
name: Publish to PyPI

on:
release:
types: [published]
workflow_dispatch:
inputs:
target:
description: 'Publish target'
required: true
default: 'testpypi'
type: choice
options:
- testpypi
- pypi

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install build tools
run: pip install build
- name: Build sdist and wheel
run: python -m build
- uses: actions/upload-artifact@v4
with:
name: dist
path: dist/

test-install:
needs: build
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.9', '3.10', '3.11', '3.12']
steps:
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- name: Install wheel
run: pip install dist/*.whl
- name: Verify CLI entry point
run: madengine --help
- name: Verify import and version
run: python -c "import madengine; print(madengine.__version__)"
- name: Verify package assets
run: |
python -c "
from pathlib import Path
import madengine
pkg = Path(madengine.__file__).parent
missing = []
for p in [
'scripts/common/tools.json',
'deployment/templates/kubernetes/job.yaml.j2',
'deployment/templates/slurm/job.sh.j2',
'py.typed',
]:
if not (pkg / p).exists():
missing.append(p)
if missing:
raise SystemExit(f'Missing package assets: {missing}')
print('All package assets verified')
"
Comment thread
coketaste marked this conversation as resolved.

Comment thread
coketaste marked this conversation as resolved.
publish-testpypi:
needs: test-install
if: >-
github.event_name == 'workflow_dispatch' &&
github.event.inputs.target == 'testpypi'
runs-on: ubuntu-latest
environment: testpypi
permissions:
id-token: write
steps:
- uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/

publish-pypi:
needs: test-install
if: >-
github.event_name == 'release' ||
(github.event_name == 'workflow_dispatch' &&
github.event.inputs.target == 'pypi')
runs-on: ubuntu-latest
environment: pypi
permissions:
id-token: write
steps:
- uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- uses: pypa/gh-action-pypi-publish@release/v1
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ share/python-wheels/
.installed.cfg
*.egg
MANIFEST
*.whl
*.tar.gz

# PyInstaller
# Usually these files are written by a Python script from a template
Expand Down
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed

- **Modern PyPI packaging**: Migrated from legacy `setup.py` to `pyproject.toml`-only build using `hatchling` as the build backend. Version is now derived automatically from git tags via `versioningit` — no hardcoded version strings. Added PyPI classifiers, keywords, license metadata, and `py.typed` (PEP 561) marker for type checker support.

- **Python 3.9+ baseline**: Raised minimum Python version from 3.8 to 3.9. Updated `black`, `mypy`, and classifier targets accordingly.

- **Dependency lower bounds**: All core dependencies now specify minimum version constraints (e.g. `pandas>=1.3`, `sqlalchemy>=1.4`) instead of unpinned names, improving reproducibility.

### Added

- **CI workflow** (`.github/workflows/ci.yml`): Runs unit tests on Python 3.9–3.12 matrix and lint checks (black, isort, flake8) on every push/PR to `develop` and `main`.

- **Publish workflow** (`.github/workflows/publish.yml`): Builds sdist + wheel, verifies install and CLI entry point across Python 3.9–3.12, then publishes via PyPI trusted publishing. Supports manual dispatch to TestPyPI and automatic publish on GitHub release.

### Removed

- **`setup.py`**: Deleted the 307-line legacy setup script. All packaging configuration now lives in `pyproject.toml`.

- **`MANIFEST.in`**: Removed — hatchling ignores it; sdist/wheel inclusion is configured via `[tool.hatch.build]` in `pyproject.toml`.

## [2.1.3] - 2026-07-15

### Added
Expand Down
48 changes: 32 additions & 16 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,19 @@ authors = [
]
description = "MAD Engine is a set of interfaces to run various AI models from public MAD."
readme = "README.md"
requires-python = ">=3.8"
license = {text = "MIT"}
requires-python = ">=3.9"
keywords = ["ai", "ml", "docker", "rocm", "amd", "benchmark", "automation", "gpu"]
dependencies = [
"pandas",
"GitPython",
"jsondiff",
"sqlalchemy",
"paramiko",
"tqdm",
"typing-extensions",
"pymongo",
"toml",
"pandas>=1.3",
"GitPython>=3.1",
"jsondiff>=2.0",
"sqlalchemy>=1.4",
"paramiko>=2.9",
"tqdm>=4.60",
"typing-extensions>=4.0",
"pymongo>=4.0",
"toml>=0.10",
"typer>=0.9.0",
"rich>=13.0.0",
"click>=8.0.0",
Expand All @@ -40,24 +42,38 @@ dependencies = [
"pre-commit",
]
classifiers = [
"Programming Language :: Python :: 3",
"Development Status :: 4 - Beta",
"Environment :: Console",
"Intended Audience :: Developers",
"Intended Audience :: Science/Research",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Software Development :: Testing",
"Topic :: Scientific/Engineering :: Artificial Intelligence",
]

[project.scripts]
madengine = "madengine.cli.app:cli_main"

[project.urls]
Homepage = "https://github.com/ROCm/madengine"
Repository = "https://github.com/ROCm/madengine"
Issues = "https://github.com/ROCm/madengine/issues"
Changelog = "https://github.com/ROCm/madengine/blob/develop/CHANGELOG.md"


[tool.hatch.build.targets.wheel]
[tool.hatch.build]
# scripts/ is listed in .gitignore (to exclude external MAD project scripts/ dirs during dev).
# presets/**/*.json are excluded by the blanket *.json gitignore rule (for user models.json etc).
# artifacts bypasses VCS exclusion without risk of duplicate-file errors from force-include.
artifacts = [
"src/madengine/scripts/**",
"src/madengine/deployment/presets/**/*.json",
]

[tool.hatch.version]
Expand All @@ -78,7 +94,7 @@ distance-dirty = "{base_version}.post{distance}+{vcs}{rev}.d{build_date:%Y%m%d}"
# Code formatting and linting configuration
[tool.black]
line-length = 88
target-version = ['py38', 'py39', 'py310', 'py311']
target-version = ['py39', 'py310', 'py311', 'py312']
include = '\.pyi?$'
extend-exclude = '''
/(
Expand All @@ -102,7 +118,7 @@ known_first_party = ["madengine"]
known_third_party = ["pytest", "pandas", "numpy", "sqlalchemy"]

[tool.mypy]
python_version = "3.8"
python_version = "3.9"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = false
Expand All @@ -128,9 +144,9 @@ module = [
ignore_missing_imports = true

[tool.pytest.ini_options]
testpaths = ["tests/unit", "tests/integration", "tests/e2e"]
testpaths = ["tests"]
pythonpath = ["src"]
addopts = "-v --tb=short -ra --strict-markers -W default"
addopts = "-v --tb=short -ra --strict-markers"
minversion = "7.0"
filterwarnings = [
"ignore::DeprecationWarning",
Expand Down
Loading
Loading