Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow users to change version slug #11930

Open
wants to merge 19 commits into
base: main
Choose a base branch
from

Conversation

stsewd
Copy link
Member

@stsewd stsewd commented Jan 15, 2025

Basically what I had in my old design doc #6273

  • When changing the slug, we check that it's a valid one (lowercase, only number, etc), in case the slug is not valid, we suggest a valid slug based on the invalid one. Why not just transform the value to a valid slug? Changing the slug is a breaking operation, we want to avoid surprises for the user, if they set a slug to "foo/bar", and then the actual slug is "foo-bar", that's unexpected.
  • project is used as s hidden field, following the same pattern we already have for other forms (this way all normal validations like the unique constraint work instead of getting a 500).
  • Editing the slug is disabled for machine created versions (stable, latest).
  • I'm not restricting the usage of latest or stable, since those will be non-machine created, similar to what a user does already if they name a version stable/latest.
  • If a version was active and its slug was changed, the resources attached to the old slug are removed before triggering a new build.
  • Cache is always purged after deleting resources from the version.

I was playing with using the "pattern" attribute for the input, so it validates the slug before submitting the form.

vlcsnap-2025-01-28-12h30m33s792

But I think showing the error with the suggested slug works better, maybe we can port that to JS instead as a future improvement.

Screenshot 2025-01-28 at 12-30-10 rynoh-pdf - test-builds - Read the Docs

Decisions

  • Expose this as a feature flag first, or just document it right away?

TODO

  • Allow changing the slug using the API.
  • Suggest a redirect from the old slug to the new one.

The idea implemented here comes from this comment: https://github.com/readthedocs/meta/discussions/147#discussioncomment-11789455

ref https://github.com/readthedocs/meta/discussions/147

@stsewd stsewd requested a review from a team as a code owner January 15, 2025 22:37
@stsewd stsewd requested a review from humitos January 15, 2025 22:37
@stsewd stsewd marked this pull request as draft January 15, 2025 22:37
Copy link
Member

@humitos humitos left a comment

Choose a reason for hiding this comment

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

These changes look very promising, in particular because they are pretty small. I did a quick test locally and I was able to change a slug and serve the documentation under the new URL 🎉

There are some things to consider still:

  1. I understand we don't need to touch the sync versions code because it's based on verbose_name and we are not changing that, right?
  2. There is no need to save the "old slug" because if required it can be generated based on the verbose_name, as we are doing currently.
  3. The dashboard uses verbose_name when mentioning versions; so there is going to be a mismatch for those versions where we change the slug. What would be a good solution for this?
  4. Flyout Addon uses slug to sort versions, which will keep working with the changes from this PR. However, I was told that we should use verbose_name there as well. In that case, sorting will break (Addons: use verbose_name instead of slug for sorting versions #11182). Should we keep the sorting by slug then?

readthedocs/builds/forms.py Outdated Show resolved Hide resolved
@stsewd
Copy link
Member Author

stsewd commented Jan 22, 2025

I guess everything is clear after the meeting, just answering for the record

I understand we don't need to touch the sync versions code because it's based on verbose_name and we are not changing that, right?

No, we don't need to.

There is no need to save the "old slug" because if required it can be generated based on the verbose_name, as we are doing currently.

I don't think we need to have access to the old slug at all, we can try to guess the original slug by inferring it from the verbose_name, but that isn't necessarily true as slugs can't be duplicated, so some end with _a suffixes. In any case, if we need the old slug for auditing, using a historical record is a better alternative.

The dashboard uses verbose_name when mentioning versions; so there is going to be a mismatch for those versions where we change the slug. What would be a good solution for this?

I suggested we use another field instead (display_name) that users can change to show in the UI/version selector. That way we don't change verbose_name which is used to keep a one to one match with the versions from the repo.

Flyout Addon uses slug to sort versions, which will keep working with the changes from this PR. However, I was told that we should use verbose_name there as well. In that case, sorting will break (
#11182). Should we keep the sorting by slug then?

I still think we shouldn't use the slug for sorting (the name we show to users in the listings won't match the order), if users want further customization, then sorting should be based on the new display_name field, so the listing matches the order.

@ericholscher
Copy link
Member

I don't think we need to have access to the old slug at all, we can try to guess the original slug by inferring it from the verbose_name, but that isn't necessarily true as slugs can't be duplicated, so some end with _a suffixes. In any case, if we need the old slug for auditing, using a historical record is a better alternative.

This could be useful for creating redirects, but perhaps we just do that automatically when they rename the slug?

@stsewd
Copy link
Member Author

stsewd commented Jan 22, 2025

This could be useful for creating redirects, but perhaps we just do that automatically when they rename the slug?

My idea would be to create a notification or show the message after the user has done the rename (about a link to create a redirect), we don't need to keep that information forever, specially if the user changes the slug again.

@ericholscher
Copy link
Member

This could be useful for creating redirects, but perhaps we just do that automatically when they rename the slug?

My idea would be to create a notification or show the message after the user has done the rename (about a link to create a redirect), we don't need to keep that information forever, specially if the user changes the slug again.

Yea, I think honestly even just putting it in cache and checking for that on the redirects page for a suggested redirect. I'd like to do something similar with the filetreediff stuff and redirect suggestions as well.

@stsewd stsewd force-pushed the allow-users-to-change-version-slug branch from 6f13b4a to a081a95 Compare January 23, 2025 20:14
stsewd added a commit that referenced this pull request Jan 28, 2025
Instead of having a custom field with some extra complexity, we can
achieve the same result by using a more standard django pattern,
populating the field if isn't defined and using a validator. This will
help to re-use this code in
#11930.
@readthedocs readthedocs deleted a comment from Allenqel Jan 28, 2025
@stsewd stsewd marked this pull request as ready for review January 28, 2025 18:32
stsewd added a commit to readthedocs/ext-theme that referenced this pull request Jan 28, 2025
@agjohnson agjohnson linked an issue Jan 29, 2025 that may be closed by this pull request
2 tasks
Copy link
Member

@humitos humitos left a comment

Choose a reason for hiding this comment

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

Looks good with some suggestions/changes.

readthedocs/builds/forms.py Outdated Show resolved Hide resolved
Comment on lines +130 to +138
# NOTE: we call clean_resources over the instance with the previous slug,
# as all resources are associated with that slug.
if "slug" in self.changed_data and self._was_active:
self.instance.slug = self._previous_slug
self.instance.clean_resources()
self.instance.slug = self.cleaned_data["slug"]
# We need to set the flag to False,
# so the post_save method triggers a new build.
self._was_active = False
Copy link
Member

Choose a reason for hiding this comment

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

This looks a little hacky to me: setting the previous slug to the current instance, and then re-set it to the one the user has chosen. I understand why we are doing this, but I wonder if there is a more explicit way to call this cleanup, like running clean_resources(project_slug, version_slug) instead, or similar.

Copy link
Member

Choose a reason for hiding this comment

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

Agreed, seems like a small refactor could make this nicer.

readthedocs/rtd_tests/tests/test_build_forms.py Outdated Show resolved Hide resolved
readthedocs/rtd_tests/tests/test_build_forms.py Outdated Show resolved Hide resolved
Copy link
Member

@ericholscher ericholscher left a comment

Choose a reason for hiding this comment

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

This looks great to me overall, will let @humitos 👍 it once the last review items are addressed.

Comment on lines +130 to +138
# NOTE: we call clean_resources over the instance with the previous slug,
# as all resources are associated with that slug.
if "slug" in self.changed_data and self._was_active:
self.instance.slug = self._previous_slug
self.instance.clean_resources()
self.instance.slug = self.cleaned_data["slug"]
# We need to set the flag to False,
# so the post_save method triggers a new build.
self._was_active = False
Copy link
Member

Choose a reason for hiding this comment

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

Agreed, seems like a small refactor could make this nicer.

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.

Versions: allow editable versions
3 participants