Skip to content

feat(changelog): Support remote git repositories as template sources#1404

Open
bilelomrani1 wants to merge 4 commits intopython-semantic-release:masterfrom
bilelomrani1:feat/remote-git-template-source
Open

feat(changelog): Support remote git repositories as template sources#1404
bilelomrani1 wants to merge 4 commits intopython-semantic-release:masterfrom
bilelomrani1:feat/remote-git-template-source

Conversation

@bilelomrani1
Copy link

@bilelomrani1 bilelomrani1 commented Jan 1, 2026

Purpose

Closes #1401

Add support for remote repositories as template sources for changelog generation, allowing users to share templates across multiple projects without copy-paste or manual CI setup.

Rationale

Currently, template_dir only accepts local filesystem paths, requiring users to either copy templates into each project, use git submodules or add manual download steps in CI workflows.

By leveraging universal_pathlib which uses fsspec under the hood, we can abstract the storage layer and support any fsspec-compatible backend with minimal code changes.

Security considerations:

  • Chained protocols (e.g., simplecache::file://) are explicitly rejected to prevent bypass of path traversal checks
  • Local path traversal validation is preserved for file://, local://, and bare paths
  • Remote protocols skip local path validation since they cannot access the local filesystem

How did you test?

  1. Added tests covering:

    • UPathLoader template loading from memory filesystem
    • StorageOptionsConfig environment variable resolution
    • Chained protocol rejection
    • Valid template_dir acceptance for various protocols
    • Path traversal checks for local vs remote protocols
    • recursive_render with UPath sources
  2. Validated end-to-end with a real GitHub-hosted template repository:

    [tool.semantic_release.changelog]
    template_dir = "github://bilelomrani1:psr-conventional-changelog-template@main/src/psr_templates/templates"

    Successfully generated changelog using remote templates.

How to Verify

  1. Install dependencies: pip install -e .

  2. Create a test repository with a pyproject.toml:

    [tool.semantic_release.changelog]
    template_dir = "github://owner:repo@ref/path/to/templates"
  3. Run semantic-release changelog

  4. Verify templates are fetched from the remote repository


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

@bilelomrani1 bilelomrani1 force-pushed the feat/remote-git-template-source branch 4 times, most recently from da206de to dd85343 Compare January 2, 2026 00:29
@bilelomrani1
Copy link
Author

bilelomrani1 commented Jan 2, 2026

While working on this feature, I noticed a potential limitation worth discussing for a future enhancement.

When using user templates, recursive_render() mirrors the template directory structure directly to the project root. For
example: template_dir/docs/CHANGELOG.md.j2./docs/CHANGELOG.md

With remote template sources, users may want to use a template from an external repository or storage that has its own
directory structure. Currently, they cannot control where the rendered output lands in their project, it's dictated by the
template's internal layout.

Users can already control which part of a remote template to use by pointing template_dir to a specific subdirectory:

template_dir = "git+https://github.com/org/templates@main/changelogs"

But, there's no way to render templates to a location other than the project root. This matters when users want their changelog in a subdirectory like docs/ or build/, or in a more complex location, for instance in a monorepo.

I'm proposing to add a changelog.output_dir config option:

[tool.semantic_release.changelog]
template_dir = "git+https://github.com/org/templates@main"
output_dir = "docs/"

This would render the template rooted at templates on the remote, inside the docs/ directory locally. Default would be . for backwards compatibility.

I wanted to flag it as a potential follow-up. Happy to hear thoughts on whether this is a use case worth supporting and which approach would fit best with the existing design.

@bilelomrani1
Copy link
Author

Just submitted a proposal that would address the previous limitation here: #1406

@bilelomrani1
Copy link
Author

Just a note: this is one of the smaller, more self-contained features from my recent batch of submissions. It adds support for loading templates from remote git repositories, which is useful for sharing templates across multiple projects.

I don't think this conflicts with the v11 refactoring work, so it could potentially be reviewed independently whenever you have time. No rush see #1407 for the broader context on my use case and how this fits in.

@codejedi365
Copy link
Contributor

I agree this should not conflict. Thanks for putting the effort in here as it would be awhile before I would have gotten to this when it was asked for before.

@bilelomrani1 bilelomrani1 force-pushed the feat/remote-git-template-source branch from dd85343 to e17d06a Compare January 2, 2026 23:08
# we disallow them entirely.
if "::" in val:
raise ValueError("Chained protocols are not supported for template_dir.")
return val
Copy link
Author

@bilelomrani1 bilelomrani1 Jan 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can instead check for explicit "local://" and "file://" in the string to prevent accidental local filesystem access outside the project's repo. This catches common mistakes. This would allow chained protocols instead of disallowing them, which avoids confusing users with a seemingly arbitrary restriction.

I also just realized one workaround around the current protection anyway: fsspec allows users to register custom protocols that could wrap the local filesystem. We cannot detect these, but my assumption is that this is not a real attack vector: it requires the user to deliberately install and configure such a protocol on their system. So chained protocols are not really necessary nor sufficient to trigger an attack, so we might as well just allow them. Users who do this are explicitly opting into local filesystem access, which is outside our threat model.

My assumption with this path traversal check is: our goal is to prevent accidental misconfiguration and obvious attacks, not to sandbox users from their own deliberate actions.

cc @codejedi365, can you confirm how reasonable this assumption sounds to you?

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.

Support remote git repository as template source

2 participants