Skip to content

Commit e11792b

Browse files
authored
Versions: keep type of version in sync with the project (#10606)
* Versions: keep type of version in sync with the project So, there are a couple of problems here: - Versions have been created with the type set as unknown, and where never updated to the correct type. I was able to track down this to projects created before 2018, so probably an old bug allowed this to happen. - Stable machine versions are basically an alias for the latest stable version of the project, and they can be a branch or a tag, this means that if this version is a branch, so will be the "stable" version, same for tags. Currently, we aren't updating the version type, if the stable version was created as a branch, it will remain as a branch, even if the latest stable version is a tag, and vice versa. - Latest machine versions are basically an alias for the default branch of the project, they are always branches. Since we allow to have non-machine latest versions, they can be a branch or a tag. If this version was changed back to be a non-machine version, currently it will remain as branch or tag, this was incorrect. Our new clone/checkout pattern relies on the type of the version always being correct, so it was failing for some projects. - Fixes #10600 - Fixes #10601 After deploy, we need to clean up the invalid versions (versions with "unknown" type). We can do that by: - Updating all latest machine versions to be branches. ```python Version.objects.filter(type="unknown", machine=True).update(type="branch") ``` - Re-evaluate the stable version of all projects that have a machine stable version, so they can have the correct type. ``` for project in Project.objects.filter(versions__machine=True).distinct(): project.update_stable_version() ``` - Check all remaining active versions with "unknown" type, and update them to be branches or tags (324). We can use a simple regex to see the version identifier looks like a commit. ```python vesions = Version.objects.filter(type="unknown", active=True) commit_regex = re.compile(r"^([a-f0-9]{40})|([a-f0-9]{7})$") for version in versions: if commit_regex.match(version.identifier): version.type = "tag" else: version.type = "branch" version.save() ```
1 parent 797dcc6 commit e11792b

File tree

3 files changed

+106
-10
lines changed

3 files changed

+106
-10
lines changed

readthedocs/api/v2/utils.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
log = structlog.get_logger(__name__)
2323

2424

25-
def sync_versions_to_db(project, versions, type): # pylint: disable=redefined-builtin
25+
def sync_versions_to_db(project, versions, type):
2626
"""
2727
Update the database with the current versions from the repository.
2828
@@ -117,6 +117,8 @@ def sync_versions_to_db(project, versions, type): # pylint: disable=redefined-b
117117
latest_version.machine = True
118118
latest_version.identifier = project.get_default_branch()
119119
latest_version.verbose_name = LATEST_VERBOSE_NAME
120+
# The machine created latest version always points to a branch.
121+
latest_version.type = BRANCH
120122
latest_version.save()
121123
if added:
122124
log.info(
@@ -156,7 +158,7 @@ def _create_versions(project, type, versions):
156158

157159
def _set_or_create_version(project, slug, version_id, verbose_name, type_):
158160
"""Search or create a version and set its machine attribute to false."""
159-
version = (project.versions.filter(slug=slug).first())
161+
version = project.versions.filter(slug=slug).first()
160162
if version:
161163
version.identifier = version_id
162164
version.machine = False

readthedocs/projects/models.py

+14-8
Original file line numberDiff line numberDiff line change
@@ -1168,7 +1168,12 @@ def get_original_stable_version(self):
11681168
"""
11691169
Get the original version that stable points to.
11701170
1171-
Returns None if the current stable doesn't point to a valid version.
1171+
When stable is machine created, it's basically an alias
1172+
for the latest stable version (like 2.2),
1173+
that version is the "original" one.
1174+
1175+
Returns None if the current stable doesn't point to a valid version
1176+
or if isn't machine created.
11721177
"""
11731178
current_stable = self.get_stable_version()
11741179
if not current_stable or not current_stable.machine:
@@ -1199,18 +1204,19 @@ def update_stable_version(self):
11991204
new_stable = determine_stable_version(versions)
12001205
if new_stable:
12011206
if current_stable:
1202-
identifier_updated = (
1207+
version_updated = (
12031208
new_stable.identifier != current_stable.identifier
1209+
or new_stable.type != current_stable.type
12041210
)
1205-
if identifier_updated:
1211+
if version_updated:
12061212
log.info(
1207-
'Update stable version: %(project)s:%(version)s',
1208-
{
1209-
'project': self.slug,
1210-
'version': new_stable.identifier,
1211-
}
1213+
"Stable version updated.",
1214+
project_slug=self.slug,
1215+
version_identifier=new_stable.identifier,
1216+
version_type=new_stable.type,
12121217
)
12131218
current_stable.identifier = new_stable.identifier
1219+
current_stable.type = new_stable.type
12141220
current_stable.save()
12151221
return new_stable
12161222
else:

readthedocs/rtd_tests/tests/test_sync_versions.py

+88
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,94 @@ def test_delete_version(self):
219219
Version.objects.filter(slug='external').exists(),
220220
)
221221

222+
def test_update_stable_version_type(self):
223+
self.pip.update_stable_version()
224+
stable_version = self.pip.get_stable_version()
225+
self.assertEqual(stable_version.type, TAG)
226+
227+
branches_data = [
228+
{
229+
"identifier": "master",
230+
"verbose_name": "master",
231+
},
232+
{
233+
"identifier": "1.0",
234+
"verbose_name": "1.0",
235+
},
236+
{
237+
"identifier": "1.1",
238+
"verbose_name": "1.1",
239+
},
240+
{
241+
"identifier": "2.0",
242+
"verbose_name": "2.0",
243+
},
244+
]
245+
246+
# Deactivate all other versions, so we only have branches for consideration
247+
# for the new stable version.
248+
self.pip.versions.exclude(slug__in=[LATEST, STABLE]).update(active=False)
249+
sync_versions_task(
250+
self.pip.pk,
251+
branches_data=branches_data,
252+
tags_data=[],
253+
)
254+
255+
self.pip.update_stable_version()
256+
stable_version = self.pip.get_stable_version()
257+
self.assertEqual(stable_version.type, BRANCH)
258+
self.assertEqual(stable_version.identifier, "2.0")
259+
self.assertEqual(stable_version.verbose_name, "stable")
260+
261+
original_stable = self.pip.get_original_stable_version()
262+
self.assertEqual(original_stable.type, BRANCH)
263+
self.assertEqual(original_stable.slug, "2.0")
264+
self.assertEqual(original_stable.identifier, "2.0")
265+
self.assertEqual(original_stable.verbose_name, "2.0")
266+
267+
def test_update_latest_version_type(self):
268+
latest_version = self.pip.versions.get(slug=LATEST)
269+
self.assertEqual(latest_version.type, BRANCH)
270+
271+
branches_data = [
272+
{
273+
"identifier": "master",
274+
"verbose_name": "master",
275+
},
276+
]
277+
tags_data = [
278+
{
279+
"identifier": "abc123",
280+
"verbose_name": "latest",
281+
}
282+
]
283+
284+
# Latest is created as machine=False, and as a tag.
285+
sync_versions_task(
286+
self.pip.pk,
287+
branches_data=branches_data,
288+
tags_data=tags_data,
289+
)
290+
291+
latest_version = self.pip.versions.get(slug=LATEST)
292+
self.assertEqual(latest_version.type, TAG)
293+
self.assertEqual(latest_version.identifier, "abc123")
294+
self.assertEqual(latest_version.verbose_name, "latest")
295+
self.assertEqual(latest_version.machine, False)
296+
297+
# Latest is back as machine created, and as a branch.
298+
sync_versions_task(
299+
self.pip.pk,
300+
branches_data=branches_data,
301+
tags_data=[],
302+
)
303+
304+
latest_version = self.pip.versions.get(slug=LATEST)
305+
self.assertEqual(latest_version.type, BRANCH)
306+
self.assertEqual(latest_version.identifier, "master")
307+
self.assertEqual(latest_version.verbose_name, "latest")
308+
self.assertEqual(latest_version.machine, True)
309+
222310
def test_machine_attr_when_user_define_stable_tag_and_delete_it(self):
223311
"""
224312
The user creates a tag named ``stable`` on an existing repo,

0 commit comments

Comments
 (0)