Skip to content

Conversation

@RonnyPfannschmidt
Copy link
Contributor

@RonnyPfannschmidt RonnyPfannschmidt commented Oct 12, 2025

this splits the code into 2 packages to enable others to use it without needing setuptoools

  • split into 2 packages
  • reshape a as a monorepo
  • fix uv workspace issues
  • release management
  • review abstraction
  • ensure we can change the still private apis sanely after initial releases
  • unify docs
  • rework

DimitriPapadopoulos and others added 24 commits October 12, 2025 12:39
Indeed setuptools will automatically pick up files with standard names,
including `LICEN[CS]E*`:
https://setuptools.pypa.io/en/latest/userguide/miscellaneous.html
This is a major refactoring that extracts core VCS functionality into a
new vcs_versioning package, with setuptools_scm becoming an integration
layer on top of it.

## Phase 1: Package Structure Setup

- Updated vcs_versioning/pyproject.toml:
  - Added dependencies: packaging, typing-extensions, tomli
  - Defined entry points for version_scheme, local_scheme, parse_scm, parse_scm_fallback
  - Added vcs-versioning CLI command
  - Registered setuptools_scm.* entry point groups for backward compatibility

- Created directory structure:
  - vcs_versioning/_backends/ for private VCS backend modules

## Phase 2: Core Functionality Migration

### Moved Core APIs:
- _config.py → config.py (now public)
- version.py → _version_schemes.py (private, accessed via entry points)
- Created scm_version.py for ScmVersion class (public API)
- _version_cls.py → _version_cls.py (private)

### Moved VCS Backends (all private):
- git.py → _backends/_git.py
- hg.py → _backends/_hg.py
- hg_git.py → _backends/_hg_git.py
- scm_workdir.py → _backends/_scm_workdir.py

### Moved Core Utilities:
- discover.py → _discover.py (private)
- fallbacks.py → _fallbacks.py (private)
- _run_cmd.py, _node_utils.py, _modify_version.py
- _types.py, _entrypoints.py, _log.py
- _compat.py, _overrides.py, _requirement_cls.py
- integration.py → _integration.py
- _get_version_impl.py (core version logic)

### Moved CLI:
- _cli.py → _cli.py (private)
- __main__.py

### Split Pyproject Reading:
- Created _pyproject_reading.py with core logic
- Supports both [tool.vcs-versioning] and [tool.setuptools_scm]
- Setuptools-specific logic will stay in setuptools_scm

### Created Public API:
- vcs_versioning/__init__.py exports:
  - Configuration, ScmVersion
  - Version, NonNormalizedVersion
  - DEFAULT_VERSION_SCHEME, DEFAULT_LOCAL_SCHEME

## Import Updates

- Updated all imports in moved files to use new module structure
- Fixed circular import issues with TYPE_CHECKING guards
- Used canonical private name _git with lazy imports where needed
- Made dump_version optional (setuptools-specific functionality)

## Progress Tracking

- Created .wip/ directory with:
  - progress.md - phase completion checklist
  - api-mapping.md - old → new import path mapping
  - test-status.md - test suite migration status

## Next Steps

- Phase 3: Create backward compatibility layer
- Phase 4: Update public API (mostly done)
- Phase 5: Rebuild setuptools_scm as integration layer
- Phase 6: Migrate test suite

Note: Skipping pre-commit hooks as setuptools_scm module errors are
expected until Phase 5 when we create re-export stubs.
This commit completes Phase 5 and sets up the uv workspace so both packages
can be developed and tested together.

## Phase 5: setuptools_scm Re-export Layer

Created backward compatibility layer in setuptools_scm that re-exports
from vcs_versioning while maintaining the same public API.

### Re-export Stubs Created:

**Core Modules:**
- _types.py - Type definitions
- _log.py - Logging utilities
- _entrypoints.py - Entry point utilities
- _version_cls.py - Version classes (+ _version_as_tuple, _validate_version_cls)
- _config.py - Configuration
- _get_version_impl.py - Version getting logic (+ all helper functions)
- _run_cmd.py - Command execution utilities

**VCS Backends:**
- git.py - Git backend re-export
- hg.py - Mercurial backend re-export
- discover.py - Discovery utilities
- fallbacks.py - Fallback parsers
- integration.py - Integration utilities

**Version & Schemes:**
- version.py - ScmVersion and all version schemes

**CLI:**
- _cli.py - CLI wrapper

**Integration:**
- _integration/toml.py - TOML utilities
- _integration/pyproject_reading.py - Extended with setuptools-specific logic
  - Inherits from vcs_versioning's PyProjectData
  - Adds should_infer() method
  - Adds has_build_package_with_extra() for setuptools[simple] support

## vcs_versioning Fixes

- Removed scm_version.py module (circular import)
- ScmVersion stays in _version_schemes.py
- Fixed all imports from scm_version to use _version_schemes
- Updated __init__.py to export ScmVersion from _version_schemes
- Fixed _entrypoints.py to use _version_schemes.ScmVersion type hints
- Fixed _types.py to import _version_schemes instead of scm_version

## Workspace Setup

- Added uv workspace configuration with both packages
- Added vcs-versioning to build-system.requires
- Configured workspace sources for vcs-versioning dependency
- Fixed deprecated license configuration (license.file → license = "MIT")
- Removed deprecated license classifier

## Linting

- Added ruff noqa: F405 to re-export modules (star import warnings expected)
- All mypy errors resolved
- Pre-commit hooks pass

## Testing

- Workspace builds successfully with `uv sync`
- Both packages install correctly
- Tests are running (6/8 passing in test_better_root_errors.py)
- Minor test failures to be addressed in next phase

## Next Steps

- Fix remaining test failures
- Phase 6: Migrate test suite
- Update documentation
- Remove re-export stubs for private APIs (_compat, _overrides, _requirement_cls)
- Update tests to import private APIs directly from vcs_versioning
- Update mypy.ini to include both source paths and use Python 3.9
- Remove _VersionT from __all__ since it's a private type used only in tests

Tests now correctly import:
- vcs_versioning._compat instead of setuptools_scm._compat
- vcs_versioning._overrides instead of setuptools_scm._overrides
- vcs_versioning._requirement_cls instead of setuptools_scm._requirement_cls
- vcs_versioning._backends._git for git private APIs
- vcs_versioning._log for log private APIs
- vcs_versioning._version_cls._VersionT for type alias

This maintains the separation where setuptools_scm only re-exports
public APIs, while tests that need private APIs import from vcs_versioning
directly.
## vcs-versioning src Layout

- Moved vcs_versioning/ to src/vcs_versioning/ for consistency
- Updated pyproject.toml with hatch build configuration
- Updated mypy.ini with new src path

## Test Migration

Moved 79 passing VCS-agnostic tests to vcs-versioning:
- test_compat.py (4 tests) - Path normalization utilities
- test_internal_log_level.py (2 tests) - Logging configuration
- test_version.py (73 tests) - Version scheme implementations

Kept in setuptools_scm:
- test_overrides.py (18 tests) - Has environment-specific behavior
- All integration tests - Require setuptools

## Test Configuration

- Added pytest.ini_options to vcs-versioning/pyproject.toml
- Added conftest.py and __init__.py to vcs-versioning/testing
- Configured 'issue' marker for pytest

## Test Results

- vcs-versioning: 79/79 tests passing ✅
- setuptools_scm: 18/18 tests passing in test_overrides.py ✅
- Both packages build successfully with uv sync ✅

This separates the core VCS functionality tests from setuptools
integration tests, making vcs-versioning independently testable.
- Removed VCS-related entry points from setuptools_scm (parse_scm, parse_scm_fallback, local_scheme, version_scheme)
- Kept file_finders entry points in setuptools_scm (setuptools-specific)
- vcs-versioning now provides all VCS functionality entry points
- Fixed get_version() to pass force_write_version_files=False to avoid deprecation warning

## Entry Point Migration

**Removed from setuptools_scm:**
- setuptools_scm.parse_scm (git, hg)
- setuptools_scm.parse_scm_fallback (git_archival, hg_archival, PKG-INFO, pyproject.toml, setup.py)
- setuptools_scm.local_scheme (all schemes)
- setuptools_scm.version_scheme (all schemes)

**Kept in setuptools_scm:**
- setuptools_scm.files_command (git, hg) - setuptools-specific
- setuptools_scm.files_command_fallback (git_archival, hg_archival) - setuptools-specific

**Provided by vcs-versioning:**
- All VCS backend entry points
- All version and local scheme entry points
- CLI entry point (vcs-versioning command)

## Test Status

- 2/21 tests now passing in test_basic_api
- test_root_parameter_pass_by fails due to monkeypatch not affecting vcs_versioning internals
- This is expected behavior change from migration - tests that patch internal functions need updates
- Updated assert_root() to patch vcs_versioning._get_version_impl instead of setuptools_scm
- Added root parameter to get_version() to support positional arguments (issue pypa#669)
- Support PathLike in get_version() root parameter for type compatibility
- Updated test_basic_api.py to patch at the correct module level

Test improvements:
- test_root_parameter_pass_by now passes (was failing due to monkeypatch)
- 3/21 tests passing in test_basic_api
- test_parentdir_prefix now failing on output format (unrelated to refactoring)
Complete logging refactor with central registry and explicit configuration
at all entry points. See full details in commit message.
- Fixed parse_tag_regex handling to emit deprecation warning properly
- setuptools_scm.get_version now delegates to vcs_versioning.get_version
- Fixed import of strip_path_suffix in file_finders/git.py to use vcs_versioning
- Fixed test patches in test_git.py to patch _git module instead of re-exported git
- Re-export read_toml_content in pyproject_reading for test compatibility
- Add __main__.py shim in setuptools_scm importing from vcs_versioning._cli
- Implement warning when tool.setuptools.dynamic.version conflicts with setuptools-scm[simple]
- Add _check_setuptools_dynamic_version_conflict helper function
Instead of re-exporting functions just to satisfy mocks, fix the tests
to mock the correct module where the functions actually live.

- Updated test_read_pyproject_with_given_definition to patch vcs_versioning._toml.read_toml_content instead of setuptools_scm._integration.pyproject_reading.read_toml_content
- Removed unnecessary re-export of read_toml_content

All tests now passing: 329 passed, 10 skipped, 1 xfailed
✅ All 8 phases completed successfully:
- Phase 1-2: Package structure and code movement
- Phase 3: Backward compatibility layer
- Phase 4: Public API exports
- Phase 5: Integration layer rebuilt
- Phase 6: Test migration (408 tests passing)
- Phase 7: Progress tracking with commits
- Phase 8: CI/CD ready

Key achievements:
- Unified logging with separate root loggers
- 329 setuptools_scm tests + 79 vcs-versioning tests passing
- Full backward compatibility maintained
- Entry points properly distributed
- uv workspace configured
This commit creates a unified test infrastructure that can be used by both
vcs-versioning and setuptools-scm test suites.

Key changes:

1. Created vcs_versioning.test_api module:
   - Exports WorkDir, DebugMode, and test fixtures as a pytest plugin
   - Contains pytest hooks (pytest_configure, fixtures, etc.)
   - Can be imported via pytest_plugins = ['vcs_versioning.test_api']

2. Moved WorkDir class:
   - testing/wd_wrapper.py -> vcs-versioning/src/vcs_versioning/_test_utils.py
   - Updated imports to use vcs_versioning modules

3. Renamed test directory to avoid pytest conflict:
   - nextgen/vcs-versioning/testing/ -> testingB/
   - Added README explaining the pytest ImportPathMismatchError issue
   - pytest cannot distinguish between two 'testing/conftest.py' at different locations

4. Migrated backend tests to vcs-versioning:
   - testing/test_git.py -> testingB/test_git.py
   - testing/test_mercurial.py -> testingB/test_mercurial.py
   - testing/test_hg_git.py -> testingB/test_hg_git.py
   - Updated imports to use vcs_versioning backends conditionally

5. Updated all test imports:
   - setuptools_scm tests now use: from vcs_versioning.test_api import WorkDir
   - vcs-versioning tests use the same: from vcs_versioning.test_api import WorkDir
   - Removed all 'from testing.wd_wrapper import WorkDir' and 'from .wd_wrapper import WorkDir'

6. Simplified conftest files:
   - setuptools_scm/testing/conftest.py uses pytest_plugins
   - vcs-versioning/testingB/conftest.py uses pytest_plugins
   - Both delegate to vcs_versioning.test_api for common fixtures

Test results:
- All 408 tests pass across both packages
- Can run tests together: pytest -n12 testing/ nextgen/vcs-versioning/testingB/
- No pytest path conflicts or import errors
Using Self as the return type for classmethod PyProjectData.for_testing allows
subclasses (like setuptools_scm's extended PyProjectData) to have the correct
return type without needing overrides or type ignore comments.

Changes:
- Added Self import to vcs_versioning._pyproject_reading
- Changed return type from PyProjectData to Self for for_testing and empty methods
- Removed now-unnecessary type: ignore comments in setuptools.py and test_cli.py

This fixes all remaining mypy errors related to PyProjectData type compatibility.
The griffe API check was failing due to pip install issues. This commit updates
the workflow to use uv for dependency management, which is already used in the
main test workflow.

Changes:
- Added astral-sh/setup-uv@v6 action to install uv
- Replaced 'pip install' commands with 'uv sync --group test' and 'uv pip install griffe'
- Updated griffe command to use 'uv run griffe' for proper environment execution

This ensures consistent dependency resolution and fixes the installation failures.
Migrates the Read the Docs build from pip to uv for faster and more reliable
dependency installation. This follows the official Read the Docs documentation
for uv integration.

Changes:
- Added pre_create_environment step to install uv via asdf
- Added create_environment step to create venv with uv
- Replaced pip install commands with 'uv sync --frozen --group docs'
- Set UV_PROJECT_ENVIRONMENT to use Read the Docs virtualenv path

Benefits:
- Faster dependency resolution and installation
- Better workspace support (handles vcs-versioning dependency correctly)
- Respects uv.lock for reproducible builds with --frozen flag
- Consistent with local development and CI workflows

Reference: https://docs.readthedocs.com/platform/stable/build-customization.html#install-dependencies-with-uv
This merges the PEP 639 compliance changes from PR pypa#1166:
- Updates license format from 'license.file' to 'license = "MIT"'
- Bumps setuptools requirement to >=77.0.3 for PEP 639 support
- Removes 'License :: OSI Approved :: MIT License' classifier (redundant with new license field)
- License file auto-detection relies on setuptools defaults

Co-authored-by: Dimitri Papadopoulos <[email protected]>
Updates both setuptools-scm and vcs-versioning to require Python >= 3.10.

Changes:
- Set requires-python to >=3.10 in both packages
- Remove Python 3.8 and 3.9 from classifiers
- Update CI test matrix to remove Python 3.8 and 3.9

This aligns with the PEP 639 compliance which requires setuptools >= 77.0.3.
Updates the CI pipeline to use hynek/build-and-inspect-python-package@v2 for both packages:

Package job changes:
- Build vcs-versioning using baipp with 'path: nextgen/vcs-versioning'
- Build setuptools-scm using baipp at repo root
- Download both artifacts and combine into 'All-Packages' artifact

Test job changes:
- Download 'All-Packages' artifact containing both wheels
- Install vcs-versioning wheel first (dependency)
- Install setuptools-scm wheel second
- Run tests for both packages: 'testing/' and 'nextgen/vcs-versioning/testingB/'
- Tests run against installed wheels instead of source code

Distribution jobs updated:
- dist_upload, upload-release-assets, test-pypi-upload now use 'All-Packages'
- Both packages will be uploaded to PyPI on release

This ensures proper packaging validation for both packages and tests them
as they would be installed by end users.
Since we dropped Python 3.8 and 3.9 support, we can now use Python 3.10+ type features directly from typing module without version checks or typing_extensions.

Changes:
- Updated mypy.ini python_version from 3.9 to 3.10
- Removed sys.version_info >= (3, 10) checks (now always true)
- Moved TypeAlias, Concatenate, ParamSpec, TypeGuard from typing_extensions/TYPE_CHECKING to direct typing imports
- Changed Union[X, Y] to X | Y syntax throughout
- Added TypeAlias annotations to type alias definitions (_Version, VersionInferenceResult)
- Renamed _VersionT to _Version (T suffix is only for TypeVars, not TypeAliases)
- Fixed entry_points() to explicitly pass keyword args instead of **kw for better type inference
- Fixed module-level import ordering in _version_schemes.py
- Removed unused TYPE_CHECKING imports
- Added type annotations to fix no-any-return errors
- Updated test cases to use renamed _Version type

This resolves all ruff UP036, UP007, PYI043, E402, F401 errors and mypy attr-defined, return-value, arg-type errors for Python 3.10+ typing features.
@RonnyPfannschmidt RonnyPfannschmidt force-pushed the move-code-to-vcs-versioning branch from ccc824e to 7968d36 Compare October 12, 2025 22:05
The v5 version of actions/upload-artifact and actions/download-artifact
appears to not be available or stable yet. Downgrading to v4 which is
the stable version.
Configure unique 'upload-name-suffix' for each build-and-inspect-python-package
invocation to prevent artifact name conflicts:
- vcs-versioning build: Packages-vcs-versioning
- setuptools-scm build: Packages-setuptools-scm

This resolves the 409 Conflict error where both builds were trying to upload
artifacts with the same default name 'Packages'.
Configure build-and-inspect-python-package to upload artifacts with unique names
to prevent naming conflicts when building both vcs-versioning and setuptools-scm:

- Added 'upload-name-suffix: -vcs-versioning' to vcs-versioning build
- Added 'upload-name-suffix: -setuptools-scm' to setuptools-scm build
- Updated all download steps to pull both artifacts into the same dist/ path
- Removed combined artifact upload - instead download both artifacts directly where needed

This ensures each package uploads its own artifact (Packages-vcs-versioning and
Packages-setuptools-scm) and all jobs that need the packages download both artifacts
into dist/, allowing them to coexist without conflicts.
@RonnyPfannschmidt RonnyPfannschmidt force-pushed the move-code-to-vcs-versioning branch from beb7f3a to b060b51 Compare October 13, 2025 06:33
Add cleanup step between vcs-versioning and setuptools-scm builds to remove
the /tmp/baipp/dist/out directory if it exists. This directory is left over
from baipp's unpack testing and can interfere with subsequent builds.

The step logs a warning if the directory is found before removing it.
All information is now consolidated in CLAUDE.md, which is:
- Version controlled
- Visible to all contributors
- Easier to maintain and update

Deleted:
- .cursor/rules/test-running.mdc (redundant with CLAUDE.md)
- All Serena memories (project_overview, style_and_conventions, suggested_commands, done_checklist)
Core file finding logic is now in vcs_versioning._file_finders,
allowing standalone usage without setuptools dependency.

Changes:
- Created vcs_versioning._file_finders module with:
  - __init__.py: Core scm_find_files(), is_toplevel_acceptable(), find_files()
  - _git.py: Git-specific file finding and archive fallback
  - _hg.py: Mercurial-specific file finding and archive fallback
  - _pathtools.py: Path normalization utility

- Updated setuptools_scm._file_finders to be thin wrappers:
  - __init__.py: Delegates to vcs_versioning.find_files()
  - git.py: Delegates to vcs_versioning Git finders
  - hg.py: Delegates to vcs_versioning Mercurial finders
  - Removed pathtools.py (now in vcs-versioning)

- Updated test imports to use vcs_versioning._file_finders._git

Entry points remain in setuptools-scm for setuptools integration.

Tests: All setuptools-scm tests pass (185 passed)
Tests: All vcs-versioning tests pass (321 passed)
- Add @overload signatures to EnvReader.read() method to properly infer
  return types based on parameters (str, list[str], or str | None)
- Fix is_toplevel_acceptable() to use env_reader from GlobalOverrides
  context instead of directly accessing os.environ for proper architecture
- Update test fixture to use SETUPTOOLS_SCM prefix with VCS_VERSIONING
  as fallback for backwards compatibility
- Fix test imports to use vcs_versioning._file_finders modules
- Improve type safety with proper type: ignore annotations
Move test_overrides.py and test_file_finder.py to vcs-versioning as they test
core functionality without setuptools dependencies.

- test_overrides.py → test_overrides_env_reader.py
  Tests core vcs_versioning._overrides and EnvReader functionality

- test_file_finder.py → test_file_finders.py
  Tests core vcs_versioning._file_finders.find_files() functionality

- Add skip_commit marker to vcs-versioning pytest config

All 61 tests pass (59 passed, 2 skipped on case-sensitive filesystems)
Major refactorings for cleaner, more type-safe APIs:

1. Convert should_infer from method to function
   - Remove PyProjectData subclass in setuptools-scm
   - Create standalone should_infer(pyproject_data) function
   - Simpler functional approach, no inheritance needed

2. Use logger instances instead of strings in GlobalOverrides
   - Change additional_logger: str to additional_loggers: tuple[Logger, ...]
   - Accept Logger instances or list of Logger instances
   - Remove implicit string-to-logger conversion
   - Add _setuptools_scm_logger module constant

3. Simplify setuptools integration to always use from_active()
   - Remove conditional logic based on dist_name
   - Always use GlobalOverrides.from_active(dist_name=dist_name)
   - Inherits all settings, only updates dist_name field

4. Make env_reader required with validation
   - Change from optional _env_reader to required env_reader field
   - Add __post_init__ validation ensuring consistency
   - Validate dist_name and tools_names match between env_reader and GlobalOverrides
   - Update from_active() to recreate EnvReader when dist_name or tool changes
   - Remove fallback logic from property

Benefits:
- Better type safety (no None checks, no implicit conversions)
- Catches configuration bugs early via validation
- Cleaner API with no private fields
- Simpler code patterns throughout
The _check_setuptools_dynamic_version_conflict() was only running during
tests (when _given_definition was provided via test injection), never in
production code paths.

Root cause: The setuptools wrapper couldn't access the full TOML definition
that was read internally by vcs_versioning.read_pyproject().

Solution: Add PyProjectData.definition field to expose the complete parsed
TOML content, eliminating the need for duplicate reads or conditional checks.

Changes:
- Add definition: TOML_RESULT field to PyProjectData
- Store full TOML in read_pyproject() and pass to PyProjectData
- Update setuptools wrapper to use pyproject_data.definition for conflict check
- Update for_testing(), empty(), and manual composition tests

The conflict check now runs consistently in all code paths, warning users
about conflicting setuptools-scm[simple] + tool.setuptools.dynamic.version
configuration.
Merged upstream/main which includes:
- fix: don't warn about tool.setuptools.dynamic.version when only using file finder (pypa#1231)
- docs: update changelog for v9.2.2 patch release

Adapted the fix for our refactored architecture where:
- _check_setuptools_dynamic_version_conflict moved to setuptools-scm layer
- should_infer() converted from method to standalone function
- Conflict check now uses PyProjectData.definition field

Changes:
- Updated _check_setuptools_dynamic_version_conflict to only warn when should_infer() returns True
- Simplified function signature to take PyProjectData instead of separate parameters
- Fixed test to use should_infer(pyproject_data) function instead of method
- All pyproject reading tests pass
Replace TestBuildPackageWithExtra test class with parametrized function using
a custom ConfigParser-based decorator for cleaner, more maintainable test data.

Changes:
- Create parametrize_build_package_tests() decorator for INI-style test data
- Convert 9 test methods to single parametrized test with INI table
- Use ConfigParser.getboolean() for native boolean parsing
- Use str.splitlines() for list parsing (requires parameter)
- Specific implementation for has_build_package_with_extra tests

Benefits:
- More concise and readable test data format
- Table-like structure makes adding/modifying test cases easier
- Uses ConfigParser native methods (getboolean, splitlines)
- Purpose-built for specific use case instead of generic solution
- All 14 tests pass

Example format:
    [test_case_name]
    requires =
        setuptools-scm[simple]
    package_name = setuptools-scm
    extra = simple
    expected = true
Make the class name and attributes reflect its specific purpose: warning when
version is already set and inference would override it.

Changes:
- Rename VersionInferenceWarning → VersionAlreadySetWarning
- Change attribute from 'message: str' to 'dist_name: str | None'
- Format warning message in apply() method instead of storing pre-formatted string
- Update all imports and usages in tests
- Simplify test data to use dist_name directly

Benefits:
- More descriptive name clearly indicates what it warns about
- Stores semantic data (dist_name) instead of formatted message
- Consistent with other result types (VersionInferenceConfig, VersionInferenceNoOp)
- Easier to test and maintain

All 22 version inference tests pass.
Use GlobalOverrides.from_active() to inherit the existing GlobalOverrides
context from test fixtures and only override the hg_command parameter.

Changes:
- Use from_active(hg_command=hg_exe) instead of trying to set env vars
- Keep monkeypatch.setenv("PATH", ...) to break PATH (not part of overrides)
- Inherits all other settings from the active context

The test fixtures already set up a GlobalOverrides context, so from_active()
is the correct method to use here for modifying just one parameter.

Fixes:
- vcs-versioning/testing_vcs/test_hg_git.py::test_hg_command_from_env
- vcs-versioning/testing_vcs/test_mercurial.py::test_hg_command_from_env
- vcs-versioning/testing_vcs/test_file_finders.py::test_hg_command_from_env
Move type definitions closer to their usage and remove unnecessary import
aliases across both projects for better module cohesion.

Type movements:
- GivenPyProjectResult: vcs-versioning/_types.py → _pyproject_reading.py
  (moved to where it's actually used in read_pyproject)

- VersionInferenceApplicable, GetVersionInferenceConfig:
  vcs-versioning/_types.py → setuptools-scm/_integration/version_inference.py
  (setuptools-scm specific protocols moved to their implementation module)

Import simplifications:
- setuptools-scm/_integration/pyproject_reading.py:
  - Import GivenPyProjectResult directly (not via _types)
  - Import get_args_for_pyproject directly (not as alias)
  - Remove wrapper function, just re-export from vcs-versioning

- setuptools-scm/_integration/setuptools.py:
  - Import GivenPyProjectResult from _pyproject_reading
  - Import GetVersionInferenceConfig from version_inference
  - Remove _types module import (no longer needed)

- vcs-versioning/_config.py:
  - Import get_args_for_pyproject, read_pyproject directly
  - Remove unnecessary _ prefixes from imports

- vcs-versioning/_pyproject_reading.py:
  - Add GivenPyProjectResult TypeAlias definition
  - Remove _types module import, use direct import

- vcs-versioning/_types.py:
  - Remove GivenPyProjectResult (moved to _pyproject_reading)
  - Remove version inference protocols (moved to setuptools-scm)
  - Remove Protocol from imports (no longer needed)
  - Remove unused TYPE_CHECKING imports

Benefits:
- Better module cohesion - types live with their usage
- Cleaner imports without unnecessary aliases
- vcs-versioning/_types.py now only contains core VCS types
- Version inference types properly scoped to setuptools-scm
- No functional changes

All tests pass (65 tests: pyproject reading, integration, config, version inference).
Convert vcs_versioning._cli from a single module into a well-organized package
with separate concerns for argument parsing, command execution, and resources.

Package Structure:
- vcs-versioning/src/vcs_versioning/_cli/__init__.py:
  - Main CLI entry point and command execution
  - Extracted _get_version_for_cli() helper function
  - Refactored print functions (print_json, print_plain, print_key_value)
  - Added PRINT_FUNCTIONS dispatch table
  - Updated to use CliNamespace for type safety
  - Simplified file finder import (no try/except)
  - Added assertion for archival_template (guaranteed by argparse)

- vcs-versioning/src/vcs_versioning/_cli/_args.py:
  - New module for CLI argument parsing
  - CliNamespace class: typed argparse.Namespace subclass
  - Type annotations for all CLI options
  - get_cli_parser() function moved from __init__.py

- vcs-versioning/src/vcs_versioning/_cli/git_archival_stable.txt:
  - Template for stable git archival file (no branch names)
  - Loaded as package resource

- vcs-versioning/src/vcs_versioning/_cli/git_archival_full.txt:
  - Template for full git archival file (with branch info)
  - Loaded as package resource

Key Changes:
- Argument parsing:
  - --stable/--full now store_const template filenames
  - parse_args() receives CliNamespace() instance for type safety

- Version handling:
  - Extracted into _get_version_for_cli() function
  - Returns str (not str | None) - simpler type
  - Exception handling (SystemExit) happens naturally

- Template handling:
  - Archival templates moved to resource files
  - Single loading point using importlib.resources.files()
  - Removed _get_stable_archival_content() helper
  - Removed _get_full_archival_content() helper

- Print functions:
  - Renamed _print_plain -> print_plain
  - Renamed _print_key_value -> print_key_value
  - Added print_json function
  - Dispatch via PRINT_FUNCTIONS dict
  - Accept MutableMapping instead of dict for flexibility

Benefits:
- Better code organization and separation of concerns
- Type safety with CliNamespace annotations
- Easier to test individual components
- Templates are maintainable resource files
- More data-driven design (template filenames, print dispatch)
- Prepares for future CLI feature additions
- No external dependencies (importlib.resources is Python 3.10+)

All 10 CLI tests pass.
Replace matrix.include with simple list, derive path/suffix from package name.

- matrix.package: [vcs-versioning, setuptools-scm]
- path: ${{ matrix.package }}
- suffix: -${{ matrix.package }}
…it/PR ops

Move all decision logic into Python script, keep git and PR operations in workflow.
Script outputs metadata; workflow uses github-script action for PR creation.

Changes to src/vcs_versioning_workspace/create_release_proposal.py:
- Replace --dry-run flag with --event and --branch arguments
- Script decides behavior based on event type (push vs pull_request)
- Remove create_or_update_pr function (moved to workflow)
- Simplify check_existing_pr to return (branch_name, pr_number)
- Output PR metadata: pr_title, pr_body, pr_exists, pr_number
- Write outputs directly to GITHUB_OUTPUT file
- Write step summaries directly to GITHUB_STEP_SUMMARY
- Handle all cases: no fragments, validation mode, preparation mode
- Script validates/prepares but doesn't create PRs

Changes to .github/workflows/release-proposal.yml:
- Add pull_request trigger for validation
- Pass --event and --branch to script
- Proper operation ordering: prepare -> commit -> push -> create PR
- Add separate "Create or update PR" step after branch push
- Use actions/github-script@v8 for PR operations
- Pass step outputs as environment variables to github-script
- Access outputs via process.env (proper API usage)
- No shell escaping issues with multiline PR body
- Conditional steps based on event type

Benefits:
- All logic centralized in Python script (testable)
- Workflow handles only orchestration
- Proper separation of concerns
- PR created after branch exists (fixes ordering bug)
- No string interpolation issues
- Cleaner, more maintainable code
Remove all empty if TYPE_CHECKING blocks and their now-unused imports.

Files changed:
- setuptools-scm/src/setuptools_scm/_integration/version_inference.py
- vcs-versioning/src/vcs_versioning/_pyproject_reading.py
- vcs-versioning/src/vcs_versioning/_overrides.py
- vcs-versioning/src/vcs_versioning/_discover.py
- vcs-versioning/src/vcs_versioning/_backends/_hg.py

Also removed empty sys.version_info blocks from _overrides.py.
Add OutputData TypedDict for CLI output data structure.
Replace generic dict[str, Any] with typed OutputData.

Changes:
- vcs-versioning/src/vcs_versioning/_cli/__init__.py:
  - Add OutputData TypedDict extending ConfigOverridesDict
  - Replace MutableMapping[str, Any] with OutputData in print functions
  - Add type: ignore for dynamic attribute access
  - Add assertions and map(str, ...) for proper type handling
  - Better type hints throughout
Replace Union[X, Y] with X | Y and add future annotations to version template.

Changes:
- vcs-versioning/src/vcs_versioning/_pyproject_reading.py:
  - GivenPyProjectResult: Union[...] -> ... | ... | None
  - Remove Union import

- vcs-versioning/src/vcs_versioning/_types.py:
  - PathT: Union[PathLike, str] -> PathLike | str
  - Remove Union import

- vcs-versioning/src/vcs_versioning/_dump_version.py:
  - Template: Union[int, str] -> int | str
  - Template: Union[str, None] -> str | None
  - Remove Tuple, Union imports from template
  - Add 'from __future__ import annotations' to generated .py template

Generated version files now include future annotations for backward compatibility
with Python 3.10+ pipe operator syntax.
Move GivenPyProjectResult type alias after PyProjectData class definition
to use unquoted modern syntax. TypeAlias with pipe operator requires
runtime evaluation, so forward references must be avoided.

Changes:
- vcs-versioning/src/vcs_versioning/_pyproject_reading.py:
  - Move GivenPyProjectResult after PyProjectData class
  - Use unquoted: PyProjectData | InvalidTomlError | FileNotFoundError | None
  - Fixes TypeError: unsupported operand type(s) for |: 'str' and 'type'

- vcs-versioning/src/vcs_versioning/_types.py:
  - PathT: os.PathLike[str] | str (unquoted)
  - Already worked since os is imported
Remove quotes from type annotations. With 'from __future__ import annotations',
all annotations are automatically deferred, so no quotes needed.

Changes:
- vcs-versioning/src/vcs_versioning/_dump_version.py:
  - tuple[int | str, ...] instead of "tuple[int | str, ...]"
  - str | None instead of "str | None"
  - Cleaner, more readable syntax
  - Works with mypy 1.11.2 checking Python 3.8

Result: Simpler, cleaner generated version files using modern Python syntax.
Change from --group release to --all-groups to ensure all dependencies
are available for the release proposal script.
e
Add create-release-proposal console script entry point. Use git to find
repo root, handling uv working directory changes.
Add towncrier fragments documenting all major changes:

vcs-versioning (9 fragments):
- initial-release: New standalone package
- py310, env-reader, integrator-api, towncrier-scheme: New features
- cli-package, cli-typesafety, modernize-types, overrides-validation: Internal improvements

setuptools-scm (7 fragments):
- 1231: Bugfix for file finder warning
- vcs-versioning-dep: New dependency
- py310: Python 3.8/3.9 removal
- Internal refactoring and test improvements
- Add monorepo-aware configurations to both projects' pyproject.toml
  - Set tag_regex and git describe_command for project-specific tag matching
  - Configure fallback versions (setuptools-scm: 9.2.2, vcs-versioning: 0.1.0)
  - Use towncrier-fragments version scheme

- Fix towncrier version scheme to work in monorepo
  - Look for changelog.d/ relative to config file (relative_to) instead of absolute_root
  - Enables per-project changelog fragment analysis

- Enhance create-release-proposal script
  - Support local mode (works without GitHub env vars)
  - Auto-detect current branch when not specified
  - Use --draft mode for towncrier in local runs (no file changes)
  - Simplify to use Configuration.from_file() from pyproject.toml

Results:
- setuptools-scm: 10.0.0 (major bump due to removal fragment)
- vcs-versioning: 0.2.0 (minor bump due to feature fragments)
…youts

The scheme now correctly:
- Uses relative_to (config file location) when set (for monorepo support)
- Falls back to absolute_root (VCS root) when relative_to is not set

This fixes the test failures where fallback_root (which defaults to ".")
was being used instead of absolute_root, causing fragments to not be found.
- Move PathT type alias from _types.py to _compat.py
- Move norm_real() function from _file_finders/_pathtools.py to _compat.py
- Update all file finder imports to use _compat directly
- Delete _file_finders/_pathtools.py (no longer needed)
- Update _types.py to re-export PathT for backward compatibility

This consolidates all path-related compatibility utilities in a single
location, eliminating unnecessary module indirection and making the
codebase more maintainable.
- Create _scm_version.py module with ScmVersion, meta(), tag_to_version() and parsing utilities
- Create _version_schemes/ package with organized submodules:
  - _common.py: shared utilities (SEMVER constants, combine_version_with_local_parts)
  - _standard.py: standard version and local schemes
  - _towncrier.py: towncrier-based version scheme
  - __init__.py: public API with format_version()
- Update all imports throughout vcs-versioning and setuptools-scm
- Update test imports to use new module structure
- Delete old _version_schemes.py and _version_schemes_towncrier.py files

This refactoring improves code organization by:
1. Separating the core ScmVersion data structure from version schemes
2. Grouping related schemes in dedicated modules
3. Making the package structure more maintainable and extensible
4. Maintaining full backward compatibility through re-exports

All tests pass (380 vcs-versioning + 127 setuptools-scm)
Update entry point from old module name:
- vcs_versioning._version_schemes_towncrier
to new package path:
- vcs_versioning._version_schemes._towncrier

Also update documentation references in RELEASE_SYSTEM.md and CONTRIBUTING.md
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants