Skip to content

Conversation

@devimarj
Copy link

@devimarj devimarj commented Jan 25, 2026

Summary

This PR introduces anonymized commit names to complement the existing private e-mail feature, improving GDPR compliance.

It implements an explicit state logic for this new field. Currently, with e-mails, users relying on a "private-by-default" setting become unintentionally public if the administrator later toggles the global default. This PR ensures that a user's commit name privacy preference is locked in and preserved, regardless of future changes to the global PRIVATE_COMMIT_NAME_OPT_IN setting.

Changes

  • Models: Added commit_name to Profile with fallbacks to prevent Git fatal errors (empty idents).
  • Logic: Implemented explicit state saving for privacy persistence.
  • Integration: Updated weblate.auth.models.User.get_author_name() for proper Git attribution.
  • Forms: Added UI in profile settings that dynamically adjusts to global defaults and template availability.
  • Settings: Introduced PRIVATE_COMMIT_NAME_TEMPLATE and PRIVATE_COMMIT_NAME_OPT_IN. Added {user_id} and {site_domain} support for both name and e-mail templates.
  • Tests & Docs: Updated configuration docs and added comprehensive tests.

Checklist

  • Tested the changes locally.
  • Added tests for the new logic.
  • Updated documentation (docs/admin/config.rst).

@devimarj devimarj marked this pull request as ready for review January 25, 2026 00:15
@nijel nijel requested a review from Copilot January 26, 2026 07:07
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds configurable/anonymized commit-name attribution (similar to private commit e-mail) to improve privacy/GDPR compliance and make commit-name preference persist via an explicit state.

Changes:

  • Add Profile.commit_name with logic to choose public vs anonymized name based on explicit user choice and global defaults.
  • Use Profile.get_commit_name() for VCS author attribution and expose the setting in the profile “Commit” form.
  • Add Docker env-var support plus docs/tests for the new settings and behavior.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
weblate/settings_docker.py Adds env var parsing for commit-name template and opt-in/out default.
weblate/auth/models.py Switches commit author name to Profile.get_commit_name() for attribution.
weblate/accounts/models.py Introduces commit_name field, new settings defaults, and commit-name generation logic.
weblate/accounts/migrations/0022_add_commit_name.py Adds DB migration for the new commit_name field.
weblate/accounts/forms.py Adds commit-name radio setting and populates choices based on defaults/template.
weblate/accounts/tests/test_models.py Adds unit tests for commit-name generation and default/explicit behaviors.
docs/admin/install/docker.rst Documents new Docker env vars for commit-name anonymization.
docs/admin/config.rst Documents new settings and expands template placeholder docs.

Comment on lines 239 to 240
self.fields["commit_name"].choices = [
("", gettext_lazy("Use anonymous account name"))
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

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

When PRIVATE_COMMIT_NAME_OPT_IN is false but PRIVATE_COMMIT_NAME_TEMPLATE is blank/disabled, site_name will be empty and the anonymized option is not available, yet the default radio choice label is changed to “Use anonymous account name”. This becomes misleading because the effective behavior falls back to the visible account name. Consider only switching the default label when an anonymized site_name is actually available (or adjust the label to reflect the real default).

Suggested change
self.fields["commit_name"].choices = [
("", gettext_lazy("Use anonymous account name"))
default_label = (
gettext_lazy("Use anonymous account name")
if site_name
else gettext_lazy("Use account name")
)
self.fields["commit_name"].choices = [
("", default_label),

Copilot uses AI. Check for mistakes.
Comment on lines 865 to 868
commit_name = models.CharField(
verbose_name=gettext_lazy("Commit name"),
blank=True,
max_length=200,
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

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

Profile.commit_name is being used as an enum-like state field (values are expected to be "", "public", or "private"), but the model field has no choices/validation. This allows arbitrary values to be stored (e.g., via admin/DB/API), which will silently fall through to the global default path in get_commit_name(). Consider adding choices (or a TextChoices) and constraining max_length accordingly to prevent invalid states.

Suggested change
commit_name = models.CharField(
verbose_name=gettext_lazy("Commit name"),
blank=True,
max_length=200,
class CommitNameChoices(models.TextChoices):
DEFAULT = "", gettext_lazy("Use global default")
PUBLIC = "public", gettext_lazy("Public")
PRIVATE = "private", gettext_lazy("Private")
commit_name = models.CharField(
verbose_name=gettext_lazy("Commit name"),
blank=True,
default=CommitNameChoices.DEFAULT,
max_length=7,
choices=CommitNameChoices.choices,

Copilot uses AI. Check for mistakes.
Copy link
Member

Choose a reason for hiding this comment

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

I think using IntegerChoices and IntegerField will work better for this purpose.

)
def test_get_commit_name_empty_template_fallback(self) -> None:
self.profile.commit_name = Profile.COMMIT_NAME_PRIVATE
self.assertTrue(len(self.profile.get_commit_name()) > 0)
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

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

assertTrue(a > b) cannot provide an informative message. Using assertGreater(a, b) instead will give more informative messages.

Suggested change
self.assertTrue(len(self.profile.get_commit_name()) > 0)
self.assertGreater(len(self.profile.get_commit_name()), 0)

Copilot uses AI. Check for mistakes.
@codecov
Copy link

codecov bot commented Jan 26, 2026

Codecov Report

❌ Patch coverage is 95.89041% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.96%. Comparing base (efe849f) to head (f56017e).
⚠️ Report is 17 commits behind head on main.

Files with missing lines Patch % Lines
weblate/accounts/forms.py 66.66% 1 Missing and 2 partials ⚠️

❌ Your patch check has failed because the patch coverage (95.89%) is below the target coverage (100.00%). You can increase the patch coverage or adjust the target coverage.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Member

@nijel nijel left a comment

Choose a reason for hiding this comment

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

Thanks for the feature. This might be useful for some users even though you can use already any name in the profile (unlike with e-mail which is also used for notifications or password recovery).

Please refer to my review comments and add a changelog entry.


.. envvar:: WEBLATE_PRIVATE_COMMIT_NAME_TEMPLATE

.. versionadded:: 5.1x
Copy link
Member

Choose a reason for hiding this comment

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

Let's focus on 5.16

Comment on lines 865 to 868
commit_name = models.CharField(
verbose_name=gettext_lazy("Commit name"),
blank=True,
max_length=200,
Copy link
Member

Choose a reason for hiding this comment

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

I think using IntegerChoices and IntegerField will work better for this purpose.

username=self.user.username,
site_title=settings.SITE_TITLE,
site_domain=settings.SITE_DOMAIN.rsplit(":", 1)[0],
)
Copy link
Member

Choose a reason for hiding this comment

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

I think renaming format_private_email, extending it to support site_title and reusing it here would be a better approach.

@nijel nijel self-assigned this Jan 26, 2026
@nijel nijel added this to the 5.16 milestone Jan 26, 2026
@devimarj devimarj force-pushed the feat-private-commits branch from fc2096a to f4cd2b9 Compare January 27, 2026 22:00
* Added new management command :wladmin:`list_change_events`, which lists all possible change events, :ref:`addon-choice-events`.
* Added Anthropic machinery integration, see :ref:`mt-anthropic`.
* Encoding for :ref:`formats` can now be configured using :ref:`file_format_params` (e.g., ``csv_encoding``, ``properties_encoding``).
* Added support for anonymous commit names via :setting:`PRIVATE_COMMIT_NAME_TEMPLATE`.
Copy link
Author

Choose a reason for hiding this comment

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

should we also update /accounts/snippets/login-info.html?

Copy link
Member

Choose a reason for hiding this comment

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

Good catch, it indeed makes sense to adjust it.


if site_name:
name_choices.append((Profile.CommitNameChoices.PRIVATE, site_name))

Copy link
Author

Choose a reason for hiding this comment

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

should we add a comprobation here?

if instance.commit_name == Profile.CommitNameChoices.PRIVATE and not site_name: self.initial["commit_name"] = Profile.CommitNameChoices.DEFAULT

this implies that if the user saves the form, their 'Private' setting will be overwritten with 'Default'. The setting wouldn't automatically revert to 'Private' if the template is re-enabled later, but I assume this implicit cleanup is the desired behavior when the feature is disabled.

Copy link
Member

Choose a reason for hiding this comment

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

The cleanup would only happen for users saving the settings, so I don't think it's worth of the effort.

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