feat(changelog): Add `changelog.output_dir` configuration option by bilelomrani1 · Pull Request #1406 · python-semantic-release/python-semantic-release
Purpose
This PR adds a new changelog.output_dir configuration option that allows users to specify the destination directory for changelog output.
This feature is particularly valuable for monorepo setups where PSR runs from a package subdirectory but needs to write changelogs to a consolidated documentation directory, and also when users use shared remote templates (see #1404) across multiple projects but still want fine grained control over where the changelog is written.
Rationale
In monorepo environments, users often want to:
- Run PSR from a package directory (e.g.,
packages/pkg1/) - Write changelogs to a centralized docs folder (e.g.,
docs/source/pkg1/changelog.md)
Before this PR, achieving this required complex shell scripts to copy/move changelog files. The previous monorepo documentation reflected this complexity, requiring users to understand intricate template customization just to place changelogs in a different directory.
The new output_dir option provides a simple way to specify the changelog destination:
# packages/pkg1/pyproject.toml [tool.semantic_release.changelog] output_dir = "../../docs/source/pkg1"
This single line replaces what previously required custom templates and shell scripts. The Advanced Example in the monorepo documentation has been completely rewritten to use output_dir, reducing complexity significantly while enabling the same (and more) functionality.
Bug Fix: Custom Templates Now Render to CWD
This PR also fixes an inconsistency when using custom template directories from a subdirectory.
Before:
# changelog_writer.py project_dir = Path(runtime_ctx.repo_dir) # Always repo root ... destination_dir=project_dir # Hardcoded to repo root
After:
# changelog_writer.py output_dir = runtime_ctx.output_dir # Resolved from "." relative to CWD ... destination_dir=output_dir # Respects CWD
The old behavior was inconsistent: when running from a subdirectory (e.g., packages/pkg1/), template_dir was resolved relative to CWD, but destination_dir was always hardcoded to the repository root. This caused templates to be read from the subdirectory but output to be written to the repo root.
With this fix, both template reading and output writing are consistent: relative to CWD when output_dir="." (default), or to the explicitly configured output_dir. I'm expecting this to be related to #845, and could potentially fix it.
How did you test?
Unit Tests (tests/unit/semantic_release/cli/test_config.py)
test_output_dir_default_resolves_to_repo_root- Default behaviortest_output_dir_inside_repo_accepted- Valid paths acceptedtest_output_dir_outside_repo_rejected- Security: paths outside repo rejectedtest_output_dir_with_parent_traversal_rejected- Security: path traversal blockedtest_output_dir_and_changelog_file_with_dir_rejected- Ambiguous config rejectedtest_output_dir_with_bare_changelog_filename_from_subdirectory_accepted- Monorepo scenario works
End-to-End Tests (tests/e2e/cmd_changelog/test_changelog.py)
test_changelog_generated_in_output_dir- Changelog written to specified directorytest_changelog_update_mode_reads_from_output_dir- Update mode reads existing changelog from correct locationtest_changelog_file_with_directory_component_backward_compatibility- Existingchangelog_filewith directory component still works
Backward Compatibility
- Existing configurations with
changelog_file = "docs/CHANGELOG.md"(directory component) continue to work unchanged - The validation only triggers when
output_diris explicitly set alongside achangelog_filewith directory component
How to Verify
-
Basic usage: Set
output_dirin config and runsemantic-release changelog[tool.semantic_release.changelog] output_dir = "docs"
Verify changelog is written to
docs/CHANGELOG.md -
Monorepo scenario: From a package subdirectory with:
[tool.semantic_release.changelog] output_dir = "../../docs/source/pkg1"
Run
semantic-release changelogand verify output location -
Update mode: Create an existing changelog in
output_dir, run withmode = "update", verify it reads and updates the correct file -
Backward compatibility: Existing config with
changelog_file = "docs/CHANGELOG.md"(nooutput_dir) should continue working
PR Completion Checklist
-
Reviewed & followed the Contributor Guidelines
-
Changes Implemented & Validation pipeline succeeds
-
Commits follow the Conventional Commits standard
and are separated into the proper commit type and scope (recommended order: test, build, feat/fix, docs) -
Appropriate Unit tests added/updated
-
Appropriate End-to-End tests added/updated
-
Appropriate Documentation added/updated and syntax validated for sphinx build (see Contributor Guidelines)