Skip to content

stable version stuck on a specific commit #3913

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

Merged
merged 41 commits into from
Jun 19, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
1998603
Test for stuck stable
stsewd Apr 5, 2018
7e77a24
Add new test
stsewd Apr 8, 2018
fbc0f85
More tests
stsewd Apr 9, 2018
d992885
Fix tests
stsewd Apr 10, 2018
718a392
Better comments
stsewd Apr 10, 2018
94ef515
Add tests for latest
stsewd Apr 10, 2018
9fe4c59
Fix comment
stsewd Apr 10, 2018
42129c3
Add tests for user defined latest
stsewd Apr 10, 2018
19df5e8
Remove unnecessary method call
stsewd Apr 10, 2018
fd270ae
Add addiotional validations
stsewd Apr 16, 2018
fdd5cd2
Mark tests as fails
stsewd Apr 16, 2018
cea0bc9
Refactor
stsewd Apr 16, 2018
4329a6f
Use variables
stsewd Apr 16, 2018
5469673
Recover RTD's stable after delete the one from the user
stsewd Apr 17, 2018
78ed557
Unmark xfaild
stsewd Apr 17, 2018
1b68e4c
Fix tests
stsewd Apr 17, 2018
24daa9b
Refactor
stsewd Apr 17, 2018
7722387
Unmark xfail
stsewd Apr 17, 2018
8e2fa4a
Better comments
stsewd Apr 17, 2018
8ddb2f4
Use reverse
stsewd Apr 18, 2018
3efd402
Fix variable names
stsewd Apr 18, 2018
7383e85
Rename tests
stsewd Apr 18, 2018
8a46707
Remove redundant checks
stsewd Apr 18, 2018
c3815f6
Add todo
stsewd May 2, 2018
9425ebd
Fix test
stsewd May 2, 2018
569ef09
Make test fail
stsewd May 2, 2018
d9b5dda
Update latest if user doesn't have one
stsewd May 2, 2018
a82309f
Fix latest tests
stsewd May 15, 2018
e4c660c
Add test
stsewd May 15, 2018
60a1289
Refactor
stsewd May 15, 2018
cc847c9
Use version_name
stsewd May 15, 2018
b558ede
Refactor
stsewd Jun 5, 2018
46d23d7
Tests for check duplicate reserved versions
stsewd Jun 12, 2018
cdc3de6
Validate reserved names for versions
stsewd Jun 12, 2018
185f504
Merge branch 'master' into stuck-stable
stsewd Jun 12, 2018
b37f876
Typo
stsewd Jun 12, 2018
e0e5003
Docstring
stsewd Jun 12, 2018
027fb25
Use decorator to restore cwd
stsewd Jun 12, 2018
d83bfb4
Fix command
stsewd Jun 12, 2018
245939d
Clean the repo when an duplicated version is found
stsewd Jun 12, 2018
1ce87f0
Don't check for removing a tag
stsewd Jun 18, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions readthedocs/projects/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ class RepositoryError(BuildEnvironmentError):
'One or more submodule URLs are not valid.'
)

DUPLICATED_RESERVED_VERSIONS = _(
'You can not have two versions with the name latest or stable.'
)

def get_default_message(self):
if settings.ALLOW_PRIVATE_REPOS:
return self.PRIVATE_ALLOWED
Expand Down
38 changes: 30 additions & 8 deletions readthedocs/projects/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
rebuilding documentation.
"""

from __future__ import absolute_import
from __future__ import (
absolute_import, division, print_function, unicode_literals)

import datetime
import hashlib
Expand All @@ -14,7 +15,7 @@
import os
import shutil
import socket
from collections import defaultdict
from collections import Counter, defaultdict

import requests
from builtins import str
Expand All @@ -31,12 +32,10 @@
from .exceptions import RepositoryError
from .models import ImportedFile, Project, Domain
from .signals import before_vcs, after_vcs, before_build, after_build, files_changed
from readthedocs.builds.constants import (LATEST,
BUILD_STATE_CLONING,
BUILD_STATE_INSTALLING,
BUILD_STATE_BUILDING,
BUILD_STATE_FINISHED)
from readthedocs.builds.models import Build, Version, APIVersion
from readthedocs.builds.constants import (
BUILD_STATE_BUILDING, BUILD_STATE_CLONING, BUILD_STATE_FINISHED,
BUILD_STATE_INSTALLING, LATEST, LATEST_VERBOSE_NAME, STABLE_VERBOSE_NAME)
from readthedocs.builds.models import APIVersion, Build, Version
from readthedocs.builds.signals import build_complete
from readthedocs.builds.syncers import Syncer
from readthedocs.core.resolver import resolve_path
Expand Down Expand Up @@ -141,6 +140,8 @@ def sync_repo(self):
} for v in version_repo.branches
]

self.validate_duplicate_reserved_versions(version_post_data)

try:
# Hit the API ``sync_versions`` which may trigger a new build
# for the stable version
Expand All @@ -150,6 +151,27 @@ def sync_repo(self):
except Exception:
log.exception('Unknown Sync Versions Exception')

def validate_duplicate_reserved_versions(self, data):
Copy link
Member

Choose a reason for hiding this comment

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

I'd like to see a docstring here ;)

"""
Check if there are duplicated names of reserved versions.

The user can't have a branch and a tag with the same name of
``latest`` or ``stable``. Raise a RepositoryError exception
if there is a duplicated name.

:param data: Dict containing the versions from tags and branches
"""
version_names = [
version['verbose_name']
for version in data.get('tags', []) + data.get('branches', [])
]
counter = Counter(version_names)
for reserved_name in [STABLE_VERBOSE_NAME, LATEST_VERBOSE_NAME]:
if counter[reserved_name] > 1:
raise RepositoryError(
RepositoryError.DUPLICATED_RESERVED_VERSIONS
)

# TODO this is duplicated in the classes below, and this should be
# refactored out anyways, as calling from the method removes the original
# caller from logging.
Expand Down
94 changes: 82 additions & 12 deletions readthedocs/restapi/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@

from rest_framework.pagination import PageNumberPagination

from readthedocs.builds.constants import NON_REPOSITORY_VERSIONS
from readthedocs.builds.constants import (LATEST, LATEST_VERBOSE_NAME,
NON_REPOSITORY_VERSIONS, STABLE,
STABLE_VERBOSE_NAME)
from readthedocs.builds.models import Version
from readthedocs.search.indexes import PageIndex, ProjectIndex, SectionIndex

Expand All @@ -18,18 +20,41 @@

def sync_versions(project, versions, type): # pylint: disable=redefined-builtin
"""Update the database with the current versions from the repository."""
old_versions = {}
old_version_values = project.versions.filter(type=type).values(
'identifier', 'verbose_name')
for version in old_version_values:
old_versions[version['verbose_name']] = version['identifier']
old_version_values = project.versions.filter(type=type).values_list(
'verbose_name', 'identifier'
)
old_versions = dict(old_version_values)

added = set()
# Add new versions
added = set()
has_user_stable = False
has_user_latest = False
for version in versions:
version_id = version['identifier']
version_name = version['verbose_name']
if version_name in old_versions:
if version_name == STABLE_VERBOSE_NAME:
has_user_stable = True
created_version, created = set_or_create_version(
project=project,
slug=STABLE,
version_id=version_id,
verbose_name=version_name,
type_=type
)
if created:
added.add(created_version.slug)
elif version_name == LATEST_VERBOSE_NAME:
has_user_latest = True
created_version, created = set_or_create_version(
project=project,
slug=LATEST,
version_id=version_id,
verbose_name=version_name,
type_=type
)
if created:
added.add(created_version.slug)
elif version_name in old_versions:
if version_id == old_versions[version_name]:
# Version is correct
continue
Expand All @@ -44,23 +69,68 @@ def sync_versions(project, versions, type): # pylint: disable=redefined-builtin

log.info(
'(Sync Versions) Updated Version: [%s=%s] ',
version['verbose_name'],
version['identifier'],
version_name,
version_id,
)
else:
# New Version
created_version = Version.objects.create(
project=project,
type=type,
identifier=version['identifier'],
verbose_name=version['verbose_name'],
identifier=version_id,
verbose_name=version_name,
)
added.add(created_version.slug)
if not has_user_stable:
stable_version = (
project.versions
.filter(slug=STABLE, type=type)
.first()
Copy link
Member

Choose a reason for hiding this comment

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

Instead of .first you can use .exists here, since it's exactly what we need.

Copy link
Member Author

Choose a reason for hiding this comment

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

But we need the object on the next line

)
if stable_version:
# Put back the RTD's stable version
stable_version.machine = True
stable_version.save()
if not has_user_latest:
latest_version = (
project.versions
.filter(slug=LATEST, type=type)
.first()
)
if latest_version:
# Put back the RTD's latest version
latest_version.machine = True
latest_version.identifier = project.get_default_branch()
latest_version.verbose_name = LATEST_VERBOSE_NAME
latest_version.save()
if added:
log.info('(Sync Versions) Added Versions: [%s] ', ' '.join(added))
return added


def set_or_create_version(project, slug, version_id, verbose_name, type_):
Copy link
Member

Choose a reason for hiding this comment

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

Can you add some docstring here, please?

"""Search or create a version and set its machine atribute to false."""
version = (
project.versions
.filter(slug=slug)
.first()
)
if version:
version.identifier = version_id
version.machine = False
version.type = type_
version.save()
else:
created_version = Version.objects.create(
project=project,
type=type_,
identifier=version_id,
verbose_name=verbose_name,
)
return created_version, True
return version, False


def delete_versions(project, version_data):
"""Delete all versions not in the current repo."""
current_versions = []
Expand Down
59 changes: 59 additions & 0 deletions readthedocs/rtd_tests/tests/test_celery.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import division, print_function, unicode_literals

import os
import json
import shutil
Expand All @@ -9,14 +11,19 @@
from mock import patch, MagicMock

from readthedocs.builds.constants import BUILD_STATE_INSTALLING, BUILD_STATE_FINISHED, LATEST
from readthedocs.projects.exceptions import RepositoryError
from readthedocs.builds.models import Build
from readthedocs.projects.models import Project
from readthedocs.projects import tasks

from readthedocs.rtd_tests.utils import (
create_git_branch, create_git_tag, delete_git_branch, delete_git_tag)
from readthedocs.rtd_tests.utils import make_test_git
from readthedocs.rtd_tests.base import RTDTestCase
from readthedocs.rtd_tests.mocks.mock_api import mock_api

from readthedocs.doc_builder.environments import BuildEnvironment


class TestCeleryBuilding(RTDTestCase):

Expand Down Expand Up @@ -124,6 +131,58 @@ def test_sync_repository(self):
)
self.assertTrue(result.successful())

@patch('readthedocs.projects.tasks.api_v2')
def test_check_duplicate_reserved_version_latest(self, api_v2):
create_git_branch(self.repo, 'latest')
create_git_tag(self.repo, 'latest')

version = self.project.versions.get(slug=LATEST)
sync_repository = tasks.UpdateDocsTaskStep()
sync_repository.version = version
sync_repository.project = self.project
with self.assertRaises(RepositoryError) as e:
sync_repository.sync_repo()
self.assertEqual(
str(e.exception),
RepositoryError.DUPLICATED_RESERVED_VERSIONS
)

delete_git_branch(self.repo, 'latest')
sync_repository.sync_repo()
api_v2.project().sync_versions.post.assert_called()

@patch('readthedocs.projects.tasks.api_v2')
def test_check_duplicate_reserved_version_stable(self, api_v2):
create_git_branch(self.repo, 'stable')
create_git_tag(self.repo, 'stable')

version = self.project.versions.get(slug=LATEST)
sync_repository = tasks.UpdateDocsTaskStep()
sync_repository.version = version
sync_repository.project = self.project
with self.assertRaises(RepositoryError) as e:
sync_repository.sync_repo()
self.assertEqual(
str(e.exception),
RepositoryError.DUPLICATED_RESERVED_VERSIONS
)

# TODO: Check that we can build properly after
# deleting the tag.

@patch('readthedocs.projects.tasks.api_v2')
def test_check_duplicate_no_reserved_version(self, api_v2):
create_git_branch(self.repo, 'no-reserved')
create_git_tag(self.repo, 'no-reserved')

version = self.project.versions.get(slug=LATEST)
sync_repository = tasks.UpdateDocsTaskStep()
sync_repository.version = version
sync_repository.project = self.project
sync_repository.sync_repo()

api_v2.project().sync_versions.post.assert_called()

def test_public_task_exception(self):
"""
Test when a PublicTask rises an Exception.
Expand Down
Loading