diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6acdc4c59d4..81511995eea 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -52,6 +52,9 @@ jobs: run: | python3 -m pip install --upgrade pip setuptools python3 -m pip install tox numpy + + - name: Check metadata consistency + run: python scripts/check_metadata.py - name: Execute Python tests run: tox diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ffaa6b8a875..04e99ec85e4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -41,6 +41,49 @@ pre-commit install # 5. Run tests cd test/unit python -m pytest -v + +``` + +## Supported Environments + +To help contributors get started quickly, below is a summary of supported environments and testing expectations. + +### Python Support +Metaflow primarily supports Python versions: + +- Python 3.7+ +- Recommended: Python 3.10 or newer + +Please ensure your local environment matches one of the supported versions. + +--- + +### R Support +Metaflow includes limited support for R components. However: + +- Most development and testing workflows are Python-focused +- R changes are validated in CI via the dedicated `R` job in `.github/workflows/test.yml` + +Contributors working on R-related features should still validate changes locally before opening a PR. +--- + +### CI Validation +Continuous Integration (CI) runs automated tests on: + +- Supported Python versions (via GitHub Actions) +- Core functionality and workflows + +CI ensures that contributions do not break existing functionality. + +--- + +### Local Testing Guide + +Before submitting a PR, contributors should run: + +```bash +pip install -e . +pytest ``` **That's it!** Now read the requirements below before submitting your PR. diff --git a/scripts/check_metadata.py b/scripts/check_metadata.py new file mode 100644 index 00000000000..c8046ba4aad --- /dev/null +++ b/scripts/check_metadata.py @@ -0,0 +1,73 @@ +import configparser +import re +import sys + + +def read_setup_cfg(): + config = configparser.ConfigParser() + config.read("setup.cfg") + + return { + "name": config.get("metadata", "name", fallback=None), + "version": config.get("metadata", "version", fallback=None), + } + + +def read_setup_py(): + with open("setup.py", "r") as f: + content = f.read() + + name_match = re.search(r"name\s*=\s*['\"](.+?)['\"]", content) + + return { + "name": name_match.group(1) if name_match else None, + } + + +def read_version_file(): + with open("metaflow/version.py", "r") as f: + line = f.read().splitlines()[0] + + version = line.split("=")[1].strip(" \"'") + return version + + +def main(): + cfg = read_setup_cfg() + py = read_setup_py() + version = read_version_file() + + errors = [] + checks_performed = 0 + + # Check name ONLY if present in both + if cfg["name"] is not None and py["name"] is not None: + checks_performed += 1 + if cfg["name"] != py["name"]: + errors.append( + f"Name mismatch: setup.cfg='{cfg['name']}' vs setup.py='{py['name']}'" + ) + + # Check version ONLY if present in cfg + if cfg["version"] is not None: + checks_performed += 1 + if cfg["version"] != version: + errors.append( + f"Version mismatch: setup.cfg='{cfg['version']}' vs version.py='{version}'" + ) + + # If nothing to check → warn instead of false pass + if checks_performed == 0: + print("⚠️ No overlapping metadata fields found to validate.") + sys.exit(0) + + if errors: + print("❌ Metadata consistency check FAILED:\n") + for err in errors: + print(f"- {err}") + sys.exit(1) + + print("✅ Metadata consistency check PASSED") + +if __name__ == "__main__": + main()