From 140f2b0410200637c08c1a8af4e533694e9f6fe4 Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Tue, 4 Jun 2024 14:22:38 +0200 Subject: [PATCH 1/3] VCS: remove code for old VCS support This PR removes the code for old VCS support. Besides, it touches the documentation slightly to remove mentions to particular VCS (Mercurial, Bazaar, SVN). However, it doesn't re-write all parts of the documentation where we are using "VCS" in a generic way. There should be another PR that re-phrase those senteces to mention Git directly. Related: * https://github.com/readthedocs/readthedocs.org/pull/11147 * https://github.com/readthedocs/readthedocs.org/issues/8840 * https://about.readthedocs.com/blog/2024/02/drop-support-for-subversion-mercurial-bazaar/ --- README.rst | 42 +++--- docs/user/guides/setup/git-repo-manual.rst | 8 +- docs/user/intro/import-guide.rst | 6 +- docs/user/versions.rst | 17 +-- readthedocs/api/v3/tests/test_projects.py | 4 +- readthedocs/builds/models.py | 3 +- readthedocs/doc_builder/director.py | 3 +- readthedocs/oauth/models.py | 2 +- readthedocs/projects/constants.py | 10 +- readthedocs/projects/tasks/builds.py | 7 +- readthedocs/projects/tasks/mixins.py | 26 +--- .../tests/projects/test_version_sorting.py | 77 +--------- readthedocs/rtd_tests/tests/test_backend.py | 76 ---------- .../rtd_tests/tests/test_backend_svn.py | 31 ----- .../rtd_tests/tests/test_project_forms.py | 25 ---- .../tests/test_version_commit_name.py | 14 +- readthedocs/rtd_tests/utils.py | 15 -- readthedocs/settings/docker_compose.py | 2 +- readthedocs/vcs_support/backends/__init__.py | 5 +- readthedocs/vcs_support/backends/bzr.py | 87 ------------ readthedocs/vcs_support/backends/git.py | 4 - readthedocs/vcs_support/backends/hg.py | 131 ------------------ readthedocs/vcs_support/backends/svn.py | 97 ------------- readthedocs/vcs_support/base.py | 53 +------ requirements/testing.in | 2 - requirements/testing.txt | 2 - 26 files changed, 43 insertions(+), 706 deletions(-) delete mode 100644 readthedocs/rtd_tests/tests/test_backend_svn.py delete mode 100644 readthedocs/vcs_support/backends/bzr.py delete mode 100644 readthedocs/vcs_support/backends/hg.py delete mode 100644 readthedocs/vcs_support/backends/svn.py diff --git a/README.rst b/README.rst index be2e6299ace..a6fa3d44347 100644 --- a/README.rst +++ b/README.rst @@ -6,54 +6,52 @@ Welcome to Read the Docs Purpose ------- -`Read the Docs`_ hosts documentation for the open source community. It supports -Sphinx_ docs written with reStructuredText_, and can pull from your Subversion_, -Bazaar_, Git_, and Mercurial_ repositories. +`Read the Docs`_ hosts documentation for the open source community. +It supports many documentation tools +(e.g. Sphinx_ docs written with reStructuredText_, MkDocs_ docs written with markdown_, among others), +and can pull Git_ repositories. Then we build documentation and host it for you. -Think of it as *Continuous Documentation*. +Think of it as *Continuous Documentation*, or Docs as Code. .. _Read the docs: https://readthedocs.org/ .. _Sphinx: http://www.sphinx-doc.org/ .. _reStructuredText: http://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html -.. _Subversion: http://subversion.tigris.org/ -.. _Bazaar: http://bazaar.canonical.com/ .. _Git: http://git-scm.com/ -.. _Mercurial: https://www.mercurial-scm.org/ +.. _MkDocs: https://www.mkdocs.org/ +.. _markdown: https://daringfireball.net/projects/markdown/ -Documentation for RTD ---------------------- +Documentation for Read the Docs +------------------------------- -You will find complete documentation for setting up your project at `the Read -the Docs site`_. +You will find complete documentation for setting up your project at `the Read the Docs site`_. .. _the Read the Docs site: https://docs.readthedocs.io/ Get in touch ------------ -You can find information about getting in touch with Read the Docs at our `Contribution page `_. +You can find information about getting in touch with Read the Docs at our +`Contribution page `_. Contributing ------------ -You can find information about contributing to Read the Docs at our `Contribution page `_. +You can find information about contributing to Read the Docs at our +`Contribution page `_. Quickstart for GitHub hosted projects ------------------------------------- -By the end of this quickstart, you will have a new project automatically updated -when you push to GitHub. +By the end of this quickstart, you will have a new project automatically updated when you push to GitHub. -#. Create an account on `Read the Docs`_. You will get an email verifying your - email address which you should accept within 7 days. - -#. Log in and click on "Import a Project". - -#. Click "Connect to GitHub" in order to connect your account's repositories to GitHub. +#. Create an account on `Read the Docs`_ by signing up with GitHub. #. When prompted on GitHub, give access to your account. -#. Click "Import a Repository" and select any desired repository. +#. Log in and click on "Add project". + +#. Start typing the name of your repository and select it from the list, + and click "Continue". #. Change any information if desired and click "Next". diff --git a/docs/user/guides/setup/git-repo-manual.rst b/docs/user/guides/setup/git-repo-manual.rst index 1901b16f82c..e09c6908592 100644 --- a/docs/user/guides/setup/git-repo-manual.rst +++ b/docs/user/guides/setup/git-repo-manual.rst @@ -186,13 +186,9 @@ For example, the cURL command to build the ``dev`` branch, using the token curl -X POST -d "branches=dev" -d "token=1234" -d "default_branch=main" https://readthedocs.org/api/v2/webhook/example-project/1/ -A command like the one above could be called from a cron job or from a hook -inside Git_, Subversion_, Mercurial_, or Bazaar_. +A command like the one above could be called from a cron job or from a `Git hook`_. -.. _Git: http://www.kernel.org/pub/software/scm/git/docs/githooks.html -.. _Subversion: https://www.mikewest.org/2006/06/subversion-post-commit-hooks-101 -.. _Mercurial: http://hgbook.red-bean.com/read/handling-repository-events-with-hooks.html -.. _Bazaar: http://wiki.bazaar.canonical.com/BzrHooks +.. _Git hook: http://www.kernel.org/pub/software/scm/git/docs/githooks.html Authentication ^^^^^^^^^^^^^^ diff --git a/docs/user/intro/import-guide.rst b/docs/user/intro/import-guide.rst index 32fbac07ca4..f98d2d8bfda 100644 --- a/docs/user/intro/import-guide.rst +++ b/docs/user/intro/import-guide.rst @@ -41,10 +41,8 @@ project, you will be asked for the repository URL, along with some other information for your new project. The URL is normally the URL or path name you'd use to checkout, clone, or branch your repository. Some examples: -* Git: ``https://github.com/ericholscher/django-kong.git`` -* Mercurial: ``https://bitbucket.org/ianb/pip`` -* Subversion: ``http://varnish-cache.org/svn/trunk`` -* Bazaar: ``lp:pasta`` +* ``https://github.com/ericholscher/django-kong.git`` +* ``https://gitlab.com/gitlab-org/gitlab`` Add an optional homepage URL and some tags, and then click **Next**. diff --git a/docs/user/versions.rst b/docs/user/versions.rst index f5b58e3c4e7..a490f27ac70 100644 --- a/docs/user/versions.rst +++ b/docs/user/versions.rst @@ -4,8 +4,8 @@ Versions Read the Docs supports multiple versions of your repository. On initial import, we will create a ``latest`` version. -This will point at the default branch defined in your VCS control -(by default, ``main`` on Git and ``default`` in Mercurial). +This will point at the default branch defined in your Git repository. +(by default, ``main``). If your project has any tags or branches with a name following `semantic versioning `_, we also create a ``stable`` version, tracking your most recent release. @@ -133,19 +133,6 @@ Read the Docs supports two workflows for versioning: based on tags or branches. If you have at least one tag, tags will take preference over branches when selecting the stable version. -Version Control Support Matrix -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -+------------+------------+-----------+------------+-----------+ -| | git | hg | bzr | svn | -+============+============+===========+============+===========+ -| Tags | Yes | Yes | Yes | No | -+------------+------------+-----------+------------+-----------+ -| Branches | Yes | Yes | Yes | No | -+------------+------------+-----------+------------+-----------+ -| Default | master | default | | trunk | -+------------+------------+-----------+------------+-----------+ - Version warning --------------- diff --git a/readthedocs/api/v3/tests/test_projects.py b/readthedocs/api/v3/tests/test_projects.py index 9cbc099aad1..b8cd5c04279 100644 --- a/readthedocs/api/v3/tests/test_projects.py +++ b/readthedocs/api/v3/tests/test_projects.py @@ -395,7 +395,7 @@ def test_update_project(self): "name": "Updated name", "repository": { "url": "https://bitbucket.com/rtfd/updated-repository", - "type": "hg", + "type": "git", }, "language": "es", "programming_language": "js", @@ -429,7 +429,7 @@ def test_update_project(self): self.assertEqual( self.project.repo, "https://bitbucket.com/rtfd/updated-repository" ) - self.assertEqual(self.project.repo_type, "hg") + self.assertEqual(self.project.repo_type, "git") self.assertEqual(self.project.language, "es") self.assertEqual(self.project.programming_language, "js") self.assertEqual(self.project.project_url, "https://updated-homepage.org") diff --git a/readthedocs/builds/models.py b/readthedocs/builds/models.py index bcadba3cdb8..b11ea8ee406 100644 --- a/readthedocs/builds/models.py +++ b/readthedocs/builds/models.py @@ -105,8 +105,7 @@ class Version(TimeStampedModel): # used by the vcs backend #: The identifier is the ID for the revision this is version is for. - #: This might be the revision number (e.g. in SVN), - #: or the commit hash (e.g. in Git). + #: This is the commit hash (e.g. in Git). #: If the this version is pointing to a branch, #: then ``identifier`` will contain the branch name. #: `None`/`null` means it will use the VCS default branch. diff --git a/readthedocs/doc_builder/director.py b/readthedocs/doc_builder/director.py index 806d264075b..a22203056d0 100644 --- a/readthedocs/doc_builder/director.py +++ b/readthedocs/doc_builder/director.py @@ -266,8 +266,7 @@ def checkout(self): if "image" not in build_config_key and "os" not in build_config_key: raise BuildUserError(BuildUserError.BUILD_OS_REQUIRED) - if self.vcs_repository.supports_submodules: - self.vcs_repository.update_submodules(self.data.config) + self.vcs_repository.update_submodules(self.data.config) # System dependencies (``build.apt_packages``) # NOTE: `system_dependencies` should not be possible to override by the diff --git a/readthedocs/oauth/models.py b/readthedocs/oauth/models.py index 7e0bb83c41d..e81f34df1f2 100644 --- a/readthedocs/oauth/models.py +++ b/readthedocs/oauth/models.py @@ -150,7 +150,7 @@ class RemoteRepository(TimeStampedModel): max_length=512, blank=True, validators=[ - URLValidator(schemes=["http", "https", "ssh", "git", "svn"]), + URLValidator(schemes=["http", "https", "ssh", "git"]), ], ) html_url = models.URLField(_("HTML URL"), null=True, blank=True) diff --git a/readthedocs/projects/constants.py b/readthedocs/projects/constants.py index 87cf6d59ef8..4d72e126f2f 100644 --- a/readthedocs/projects/constants.py +++ b/readthedocs/projects/constants.py @@ -81,16 +81,8 @@ ) REPO_TYPE_GIT = "git" -REPO_TYPE_SVN = "svn" -REPO_TYPE_HG = "hg" -REPO_TYPE_BZR = "bzr" -REPO_CHOICES = ( - (REPO_TYPE_GIT, _("Git")), - (REPO_TYPE_SVN, _("Subversion")), - (REPO_TYPE_HG, _("Mercurial")), - (REPO_TYPE_BZR, _("Bazaar")), -) +REPO_CHOICES = ((REPO_TYPE_GIT, _("Git")),) PUBLIC = "public" PRIVATE = "private" diff --git a/readthedocs/projects/tasks/builds.py b/readthedocs/projects/tasks/builds.py index b1c7698cbf5..ee21e863de4 100644 --- a/readthedocs/projects/tasks/builds.py +++ b/readthedocs/projects/tasks/builds.py @@ -233,12 +233,7 @@ def execute(self): verbose_name=self.data.version.verbose_name, version_type=self.data.version.type, ) - if not vcs_repository.supports_lsremote: - log.info("Syncing repository via full clone.") - vcs_repository.update() - else: - log.info("Syncing repository via remote listing.") - + log.info("Syncing repository via remote listing.") self.sync_versions(vcs_repository) diff --git a/readthedocs/projects/tasks/mixins.py b/readthedocs/projects/tasks/mixins.py index 70b215788bb..2086e19bf62 100644 --- a/readthedocs/projects/tasks/mixins.py +++ b/readthedocs/projects/tasks/mixins.py @@ -41,28 +41,12 @@ def sync_versions(self, vcs_repository): # and just validate them trigger the task. All the other logic should # be done by the BuildDirector or the VCS backend. We should not # check this here and do not depend on ``vcs_repository``. - sync_tags = vcs_repository.supports_tags and not self.data.project.has_feature( - Feature.SKIP_SYNC_TAGS + sync_tags = not self.data.project.has_feature(Feature.SKIP_SYNC_TAGS) + sync_branches = not self.data.project.has_feature(Feature.SKIP_SYNC_BRANCHES) + branches, tags = vcs_repository.lsremote( + include_tags=sync_tags, + include_branches=sync_branches, ) - sync_branches = ( - vcs_repository.supports_branches - and not self.data.project.has_feature(Feature.SKIP_SYNC_BRANCHES) - ) - tags = [] - branches = [] - if vcs_repository.supports_lsremote: - branches, tags = vcs_repository.lsremote( - include_tags=sync_tags, - include_branches=sync_branches, - ) - - # Remove this block once we drop support for Bazaar, SVG and Mercurial. - # Since we will only support Git, lsremote will be always available. - else: - if sync_tags: - tags = vcs_repository.tags - if sync_branches: - branches = vcs_repository.branches tags_data = [ { diff --git a/readthedocs/rtd_tests/tests/projects/test_version_sorting.py b/readthedocs/rtd_tests/tests/projects/test_version_sorting.py index c187b77bae4..b83274758cb 100644 --- a/readthedocs/rtd_tests/tests/projects/test_version_sorting.py +++ b/readthedocs/rtd_tests/tests/projects/test_version_sorting.py @@ -3,12 +3,7 @@ from readthedocs.builds.constants import BRANCH, LATEST from readthedocs.builds.models import Version -from readthedocs.projects.constants import ( - REPO_TYPE_BZR, - REPO_TYPE_GIT, - REPO_TYPE_HG, - REPO_TYPE_SVN, -) +from readthedocs.projects.constants import REPO_TYPE_GIT from readthedocs.projects.models import Project from readthedocs.projects.templatetags.projects_tags import sort_version_aware @@ -143,73 +138,3 @@ def test_sort_git_master_and_latest(self): ["master", "latest", "2.0", "1.10", "1.9", "1.1", "1.0"], [v.slug for v in sort_version_aware(versions)], ) - - def test_sort_hg_default(self): - identifiers = ["default", "1.0", "2.0", "1.1", "1.9", "1.10"] - self.project.repo_type = REPO_TYPE_HG - self.project.save() - self.project.versions.get(slug=LATEST).delete() - - for identifier in identifiers: - get( - Version, - project=self.project, - type=BRANCH, - identifier=identifier, - verbose_name=identifier, - slug=identifier, - ) - - versions = list(Version.objects.filter(project=self.project)) - self.assertEqual( - ["default", "2.0", "1.10", "1.9", "1.1", "1.0"], - [v.slug for v in sort_version_aware(versions)], - ) - - def test_sort_bzr_latest(self): - """ - BZR doesn't have a name for "master", - so here master gets sorted by its ascii value. - """ - identifiers = ["master", "1.0", "2.0", "1.1", "1.9", "1.10"] - self.project.repo_type = REPO_TYPE_BZR - self.project.save() - self.project.versions.get(slug=LATEST).delete() - - for identifier in identifiers: - get( - Version, - project=self.project, - type=BRANCH, - identifier=identifier, - verbose_name=identifier, - slug=identifier, - ) - - versions = list(Version.objects.filter(project=self.project)) - self.assertEqual( - ["2.0", "1.10", "1.9", "1.1", "1.0", "master"], - [v.slug for v in sort_version_aware(versions)], - ) - - def test_sort_svn_trunk(self): - identifiers = ["/trunk/", "1.0", "2.0", "1.1", "1.9", "1.10"] - self.project.repo_type = REPO_TYPE_SVN - self.project.save() - self.project.versions.get(slug=LATEST).delete() - - for identifier in identifiers: - get( - Version, - project=self.project, - type=BRANCH, - identifier=identifier, - verbose_name=identifier, - slug=identifier, - ) - - versions = list(Version.objects.filter(project=self.project)) - self.assertEqual( - ["/trunk/", "2.0", "1.10", "1.9", "1.1", "1.0"], - [v.slug for v in sort_version_aware(versions)], - ) diff --git a/readthedocs/rtd_tests/tests/test_backend.py b/readthedocs/rtd_tests/tests/test_backend.py index 1f66323f8d7..a58a7979723 100644 --- a/readthedocs/rtd_tests/tests/test_backend.py +++ b/readthedocs/rtd_tests/tests/test_backend.py @@ -20,7 +20,6 @@ get_current_commit, get_git_latest_commit_hash, make_test_git, - make_test_hg, ) @@ -357,78 +356,3 @@ def test_special_tag_stable(self): # Checkout the master branch to verify that we can checkout something # from the above clone+fetch repo.checkout("master") - - -# Avoid trying to save the commands via the API -@mock.patch("readthedocs.doc_builder.environments.BuildCommand.save", mock.MagicMock()) -class TestHgBackend(TestCase): - def setUp(self): - hg_repo = make_test_hg() - super().setUp() - self.eric = User(username="eric") - self.eric.set_password("test") - self.eric.save() - self.project = Project.objects.create( - name="Test Project", - repo_type="hg", - # Our top-level checkout - repo=hg_repo, - ) - self.project.users.add(self.eric) - self.build_environment = LocalBuildEnvironment(api_client=mock.MagicMock()) - - def test_parse_branches(self): - data = """\ - stable - default - """ - - expected_ids = ["stable", "default"] - given_ids = [ - x.identifier - for x in self.project.vcs_repo( - environment=self.build_environment - ).parse_branches(data) - ] - self.assertEqual(expected_ids, given_ids) - - def test_update_and_checkout(self): - repo = self.project.vcs_repo(environment=self.build_environment) - repo.make_clean_working_dir() - code, _, _ = repo.update() - self.assertEqual(code, 0) - code, _, _ = repo.checkout() - self.assertEqual(code, 0) - self.assertTrue(exists(repo.working_dir)) - - def test_checkout_invalid_revision(self): - repo = self.project.vcs_repo(environment=self.build_environment) - repo.update() - version = "invalid-revision" - with self.assertRaises(RepositoryError) as e: - repo.checkout(version) - self.assertEqual( - e.exception.message_id, - RepositoryError.FAILED_TO_CHECKOUT, - ) - - def test_parse_tags(self): - data = """\ - tip 13575:8e94a1b4e9a4 - 1.8.1 13573:aa1f3be38ab1 - 1.8 13515:2616325766e3 - 1.7.5 13334:2b2155623ee2 - """ - expected_tags = [ - ("aa1f3be38ab1", "1.8.1"), - ("2616325766e3", "1.8"), - ("2b2155623ee2", "1.7.5"), - ] - - given_ids = [ - (x.identifier, x.verbose_name) - for x in self.project.vcs_repo( - environment=self.build_environment - ).parse_tags(data) - ] - self.assertEqual(expected_tags, given_ids) diff --git a/readthedocs/rtd_tests/tests/test_backend_svn.py b/readthedocs/rtd_tests/tests/test_backend_svn.py deleted file mode 100644 index 7d7a951b6ab..00000000000 --- a/readthedocs/rtd_tests/tests/test_backend_svn.py +++ /dev/null @@ -1,31 +0,0 @@ -"""Tests For SVN.""" - -from unittest import mock - -from django.test import TestCase -from django_dynamic_fixture import get - -from readthedocs.builds.models import Version -from readthedocs.doc_builder.environments import LocalBuildEnvironment -from readthedocs.projects.models import Project -from readthedocs.vcs_support.backends.svn import Backend as SvnBackend - - -class TestSvnBackend(TestCase): - def test_get_url(self): - project = get(Project) - version = get(Version, project=project) - environment = LocalBuildEnvironment(api_client=mock.MagicMock()) - backend_obj = SvnBackend(project, version.slug, environment=environment) - - base = "http://example.com/" - tag = "xyz/" - self.assertEqual(backend_obj.get_url(base, tag), "http://example.com/xyz/") - - base = "http://example.com/" - tag = "/xyz/" - self.assertEqual(backend_obj.get_url(base, tag), "http://example.com/xyz/") - - base = "http://example.com" - tag = "/xyz/" - self.assertEqual(backend_obj.get_url(base, tag), "http://example.com/xyz/") diff --git a/readthedocs/rtd_tests/tests/test_project_forms.py b/readthedocs/rtd_tests/tests/test_project_forms.py index 6d3031e3057..dddc6e6bb56 100644 --- a/readthedocs/rtd_tests/tests/test_project_forms.py +++ b/readthedocs/rtd_tests/tests/test_project_forms.py @@ -19,8 +19,6 @@ MULTIPLE_VERSIONS_WITHOUT_TRANSLATIONS, PRIVATE, PUBLIC, - REPO_TYPE_GIT, - REPO_TYPE_HG, SINGLE_VERSION_WITHOUT_TRANSLATIONS, SPHINX, ) @@ -114,29 +112,6 @@ def test_empty_slug(self): self.assertFalse(form.is_valid()) self.assertIn("name", form.errors) - def test_changing_vcs_should_not_change_latest_is_not_none(self): - """ - When changing the project's VCS, - we should respect the custom default branch. - """ - project = get(Project, repo_type=REPO_TYPE_HG, default_branch="custom") - latest = project.versions.get(slug=LATEST) - self.assertEqual(latest.identifier, "custom") - - form = ProjectBasicsForm( - { - "repo": "http://github.com/test/test", - "name": "name", - "repo_type": REPO_TYPE_GIT, - "language": "en", - }, - instance=project, - ) - self.assertTrue(form.is_valid()) - form.save() - latest.refresh_from_db() - self.assertEqual(latest.identifier, "custom") - @override_settings(ALLOW_PRIVATE_REPOS=False) def test_length_of_tags(self): project = get(Project) diff --git a/readthedocs/rtd_tests/tests/test_version_commit_name.py b/readthedocs/rtd_tests/tests/test_version_commit_name.py index 167c92b9bd1..3c99dd31b2c 100644 --- a/readthedocs/rtd_tests/tests/test_version_commit_name.py +++ b/readthedocs/rtd_tests/tests/test_version_commit_name.py @@ -3,7 +3,7 @@ from readthedocs.builds.constants import BRANCH, EXTERNAL, LATEST, STABLE, TAG from readthedocs.builds.models import Version -from readthedocs.projects.constants import REPO_TYPE_GIT, REPO_TYPE_HG +from readthedocs.projects.constants import REPO_TYPE_GIT from readthedocs.projects.models import Project @@ -68,18 +68,6 @@ def test_stable_version_tag(self): "3d92b728b7d7b842259ac2020c2fa389f13aff0d", ) - def test_hg_latest_branch(self): - hg_project = get(Project, repo_type=REPO_TYPE_HG) - version = new( - Version, - identifier="default", - slug=LATEST, - verbose_name=LATEST, - type=BRANCH, - project=hg_project, - ) - self.assertEqual(version.commit_name, "default") - def test_git_latest_branch(self): git_project = get(Project, repo_type=REPO_TYPE_GIT) version = new( diff --git a/readthedocs/rtd_tests/utils.py b/readthedocs/rtd_tests/utils.py index 4c940367ac4..5825c485356 100644 --- a/readthedocs/rtd_tests/utils.py +++ b/readthedocs/rtd_tests/utils.py @@ -239,21 +239,6 @@ def get_current_commit(directory): return check_output(command, env=env).decode().strip() -@restoring_chdir -def make_test_hg(): - directory = mkdtemp() - path = get_readthedocs_app_path() - sample = abspath(pjoin(path, "rtd_tests/fixtures/sample_repo")) - directory = pjoin(directory, "sample_repo") - copytree(sample, directory) - chdir(directory) - hguser = "Test User " - log.info(check_output(["hg", "init"] + [directory])) - log.info(check_output(["hg", "add", "."])) - log.info(check_output(["hg", "commit", "-u", hguser, '-m"init"'])) - return directory - - def create_user(username, password, **kwargs): user = new(User, username=username, **kwargs) user.set_password(password) diff --git a/readthedocs/settings/docker_compose.py b/readthedocs/settings/docker_compose.py index 7c80efd9388..6c8f27c6a2e 100644 --- a/readthedocs/settings/docker_compose.py +++ b/readthedocs/settings/docker_compose.py @@ -47,7 +47,7 @@ class DockerBaseSettings(CommunityBaseSettings): HOSTIP = ips[0][:-1] + "1" # Turn this on to test ads - USE_PROMOS = False + USE_PROMOS = True ADSERVER_API_BASE = f"http://{HOSTIP}:5000" # Create a Token for an admin User and set it here. ADSERVER_API_KEY = None diff --git a/readthedocs/vcs_support/backends/__init__.py b/readthedocs/vcs_support/backends/__init__.py index 1f560ba0bc2..0ff6f90b59f 100644 --- a/readthedocs/vcs_support/backends/__init__.py +++ b/readthedocs/vcs_support/backends/__init__.py @@ -1,9 +1,6 @@ """Listing of all the VCS backends.""" -from . import bzr, git, hg, svn +from . import git backend_cls = { - "bzr": bzr.Backend, - "svn": svn.Backend, "git": git.Backend, - "hg": hg.Backend, } diff --git a/readthedocs/vcs_support/backends/bzr.py b/readthedocs/vcs_support/backends/bzr.py deleted file mode 100644 index bbe63bd5422..00000000000 --- a/readthedocs/vcs_support/backends/bzr.py +++ /dev/null @@ -1,87 +0,0 @@ -"""Bazaar-related utilities.""" - -import csv -import re -from io import StringIO - -from django.conf import settings - -from readthedocs.projects.exceptions import RepositoryError -from readthedocs.vcs_support.base import BaseVCS, Deprecated, VCSVersion - - -class Backend(Deprecated, BaseVCS): - - """Bazaar VCS backend.""" - - supports_tags = True - fallback_branch = "" - - def clone(self): - self.make_clean_working_dir() - try: - self.run("bzr", "checkout", self.repo_url, ".") - except RepositoryError: - message_id = RepositoryError.CLONE_ERROR_WITH_PRIVATE_REPO_NOT_ALLOWED - if settings.ALLOW_PRIVATE_REPOS: - message_id = RepositoryError.CLONE_ERROR_WITH_PRIVATE_REPO_ALLOWED - raise RepositoryError(message_id=message_id) - - @property - def tags(self): - try: - code, stdout, stderr = self.run("bzr", "tags", record_as_success=True) - return self.parse_tags(stdout) - except RepositoryError: - # error (or no tags found) - return [] - - def parse_tags(self, data): - """ - Parses output of bzr tags. - - Example: - - 0.1.0 171 - 0.1.1 173 - 0.1.2 174 - 0.2.0-pre-alpha 177 - - Can't forget about poorly formatted tags or tags that lack revisions, - such as: - - 3.3.0-rc1 ? - tag with spaces 123 - """ - # parse the lines into a list of tuples (commit-hash, tag ref name) - # StringIO below is expecting Unicode data, so ensure that it gets it. - if not isinstance(data, str): - data = str(data) - squashed_data = re.sub(r" +", " ", data) - raw_tags = csv.reader(StringIO(squashed_data), delimiter=" ") - vcs_tags = [] - for row in raw_tags: - name = " ".join(row[:-1]) - commit = row[-1] - if commit != "?": - vcs_tags.append(VCSVersion(self, commit, name)) - return vcs_tags - - @property - def commit(self): - _, stdout, _ = self.run("bzr", "revno") - return stdout.strip() - - def checkout(self, identifier=None): - super().checkout() - - if not identifier: - return self.up() - - try: - code, stdout, stderr = self.run("bzr", "switch", identifier) - return code, stdout, stderr - except RepositoryError: - raise RepositoryError( - RepositoryError.FAILED_TO_CHECKOUT.format(identifier), - ) diff --git a/readthedocs/vcs_support/backends/git.py b/readthedocs/vcs_support/backends/git.py index 508de6bb316..ab69cd86733 100644 --- a/readthedocs/vcs_support/backends/git.py +++ b/readthedocs/vcs_support/backends/git.py @@ -30,10 +30,6 @@ class Backend(BaseVCS): """Git VCS backend.""" - supports_tags = True - supports_branches = True - supports_submodules = True - supports_lsremote = True fallback_branch = "master" # default branch repo_depth = 50 diff --git a/readthedocs/vcs_support/backends/hg.py b/readthedocs/vcs_support/backends/hg.py deleted file mode 100644 index 6bf2e9cfc74..00000000000 --- a/readthedocs/vcs_support/backends/hg.py +++ /dev/null @@ -1,131 +0,0 @@ -"""Mercurial-related utilities.""" -from django.conf import settings - -from readthedocs.projects.exceptions import RepositoryError -from readthedocs.vcs_support.base import BaseVCS, Deprecated, VCSVersion - - -class Backend(Deprecated, BaseVCS): - - """Mercurial VCS backend.""" - - supports_tags = True - supports_branches = True - fallback_branch = "default" - - def update(self): - super().update() - return self.clone() - - def clone(self): - self.make_clean_working_dir() - try: - # Disable sparse-revlog extension when cloning because it's not - # included in older versions of Mercurial and producess an error - # when using an old version. See - # https://github.com/readthedocs/readthedocs.org/pull/9042/ - - output = self.run( - "hg", "clone", "--config", "format.sparse-revlog=no", self.repo_url, "." - ) - return output - except RepositoryError: - message_id = RepositoryError.CLONE_ERROR_WITH_PRIVATE_REPO_NOT_ALLOWED - if settings.ALLOW_PRIVATE_REPOS: - message_id = RepositoryError.CLONE_ERROR_WITH_PRIVATE_REPO_ALLOWED - raise RepositoryError(message_id=message_id) - - @property - def branches(self): - try: - _, stdout, _ = self.run( - "hg", - "branches", - "--quiet", - record_as_success=True, - ) - return self.parse_branches(stdout) - except RepositoryError: - # error (or no tags found) - return [] - - def parse_branches(self, data): - """ - Parses output of `hg branches --quiet`. - - Example: - - default - 0.2 - 0.1 - - Into VCSVersion objects with branch name as verbose_name and - identifier. - """ - names = [name.lstrip() for name in data.splitlines()] - return [VCSVersion(self, name, name) for name in names if name] - - @property - def tags(self): - try: - _, stdout, _ = self.run("hg", "tags", record_as_success=True) - return self.parse_tags(stdout) - except RepositoryError: - # error (or no tags found) - return [] - - def parse_tags(self, data): - """ - Parses output of `hg tags`. - - Example: - - tip 278:c4b2d21db51a - 0.2.2 152:6b0364d98837 - 0.2.1 117:a14b7b6ffa03 - 0.1 50:30c2c6b3a055 - maintenance release 1 10:f83c32fe8126 - - Into VCSVersion objects with the tag name as verbose_name and the - commit hash as identifier. - """ - vcs_tags = [] - tag_lines = [line.strip() for line in data.splitlines()] - # starting from the rhs of each line, split a single value (changeset) - # off at whitespace; the tag name is the string to the left of that - tag_pairs = [line.rsplit(None, 1) for line in tag_lines] - for row in tag_pairs: - if len(row) != 2: - continue - name, commit = row - if name == "tip": - continue - _, commit_hash = commit.split(":") - vcs_tags.append(VCSVersion(self, commit_hash, name)) - return vcs_tags - - @property - def commit(self): - _, stdout = self.run("hg", "identify", "--id")[:2] - return stdout.strip() - - def checkout(self, identifier=None): - super().checkout() - if not identifier: - identifier = "tip" - - try: - code, stdout, stderr = self.run( - "hg", - "update", - "--clean", - identifier, - ) - return code, stdout, stderr - except RepositoryError: - raise RepositoryError( - message_id=RepositoryError.FAILED_TO_CHECKOUT, - format_values={ - "identifier": identifier, - }, - ) diff --git a/readthedocs/vcs_support/backends/svn.py b/readthedocs/vcs_support/backends/svn.py deleted file mode 100644 index dc220610e8d..00000000000 --- a/readthedocs/vcs_support/backends/svn.py +++ /dev/null @@ -1,97 +0,0 @@ -"""Subversion-related utilities.""" - -import csv -from io import StringIO - -from django.conf import settings - -from readthedocs.projects.exceptions import RepositoryError -from readthedocs.vcs_support.base import BaseVCS, Deprecated, VCSVersion - - -class Backend(Deprecated, BaseVCS): - - """Subversion VCS backend.""" - - supports_tags = False - fallback_branch = "/trunk/" - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - if self.repo_url[-1] != "/": - self.base_url = self.repo_url - self.repo_url += "/" - elif self.repo_url.endswith("/trunk/"): - self.supports_tags = True - self.base_url = self.repo_url[:-7] - else: - self.base_url = self.repo_url - - def update(self): - super().update() - return self.co() - - def co(self, identifier=None): - self.make_clean_working_dir() - if identifier: - url = self.get_url(self.base_url, identifier) - else: - url = self.repo_url - retcode, out, err = self.run("svn", "checkout", url, ".") - if retcode != 0: - message_id = RepositoryError.CLONE_ERROR_WITH_PRIVATE_REPO_NOT_ALLOWED - if settings.ALLOW_PRIVATE_REPOS: - message_id = RepositoryError.CLONE_ERROR_WITH_PRIVATE_REPO_ALLOWED - raise RepositoryError(message_id=message_id) - return retcode, out, err - - @property - def tags(self): - retcode, stdout = self.run( - "svn", - "list", - "%s/tags/" % self.base_url, - record_as_success=True, - )[:2] - # error (or no tags found) - if retcode != 0: - return [] - return self.parse_tags(stdout) - - def parse_tags(self, data): - """ - Parses output of svn list. - - Example: - - release-1.1/ - release-1.2/ - release-1.3/ - release-1.4/ - release-1.4.1/ - release-1.5/ - """ - # parse the lines into a list of tuples (commit-hash, tag ref name) - # StringIO below is expecting Unicode data, so ensure that it gets it. - if not isinstance(data, str): - data = str(data) - raw_tags = csv.reader(StringIO(data), delimiter="/") - vcs_tags = [] - for name, _ in raw_tags: - vcs_tags.append(VCSVersion(self, "/tags/%s/" % name, name)) - return vcs_tags - - @property - def commit(self): - _, stdout = self.run("svnversion")[:2] - return stdout.strip() - - def checkout(self, identifier=None): - super().checkout() - return self.co(identifier) - - def get_url(self, base_url, identifier): - base = base_url.rstrip("/") - tag = identifier.lstrip("/") - url = "{}/{}".format(base, tag) - return url diff --git a/readthedocs/vcs_support/base.py b/readthedocs/vcs_support/base.py index c850a49e8e3..44d20b69aea 100644 --- a/readthedocs/vcs_support/base.py +++ b/readthedocs/vcs_support/base.py @@ -1,10 +1,7 @@ """Base classes for VCS backends.""" -import datetime import os -import pytz import structlog -from django.conf import settings from readthedocs.core.utils.filesystem import safe_rmtree from readthedocs.doc_builder.exceptions import BuildCancelled, BuildUserError @@ -36,46 +33,7 @@ def __repr__(self): ) -class Deprecated: - def __init__(self, *args, **kwargs): - tzinfo = pytz.timezone("America/Los_Angeles") - now = datetime.datetime.now(tz=tzinfo) - - # Brownout dates as published in https://about.readthedocs.com/blog/2024/02/drop-support-for-subversion-mercurial-bazaar/ - # fmt: off - disabled = any([ - # 12 hours browndate - datetime.datetime(2024, 4, 1, 0, 0, 0, tzinfo=tzinfo) < now < datetime.datetime(2024, 4, 1, 12, 0, 0, tzinfo=tzinfo), - # 24 hours browndate - datetime.datetime(2024, 5, 6, 0, 0, 0, tzinfo=tzinfo) < now < datetime.datetime(2024, 5, 7, 0, 0, 0, tzinfo=tzinfo), - # 48 hours browndate - datetime.datetime(2024, 5, 20, 0, 0, 0, tzinfo=tzinfo) < now < datetime.datetime(2024, 5, 22, 0, 0, 0, tzinfo=tzinfo), - # Deprecated after June 3 - datetime.datetime(2024, 6, 3, 0, 0, 0, tzinfo=tzinfo) < now, - ]) - # fmt: on - - if settings.RTD_ENFORCE_BROWNOUTS_FOR_DEPRECATIONS and disabled: - from .backends import bzr, hg, svn - - vcs = None - if isinstance(self, bzr.Backend): - vcs = "Bazaar" - elif isinstance(self, svn.Backend): - vcs = "Subversion" - elif isinstance(self, hg.Backend): - vcs = "Mercurial" - - raise BuildUserError( - message_id=BuildUserError.VCS_DEPRECATED, - format_values={ - "vcs": vcs, - }, - ) - - super().__init__(*args, **kwargs) - - +# TODO: merge this class with Git VCS class to simplify the code. class BaseVCS: """ @@ -84,13 +42,6 @@ class BaseVCS: VCS commands are executed inside a ``BaseBuildEnvironment`` subclass. """ - supports_tags = False # Whether this VCS supports tags or not. - supports_branches = False # Whether this VCS supports branches or not. - supports_submodules = False # Whether this VCS supports submodules or not. - - # Whether this VCS supports listing remotes (branches, tags) without cloning - supports_lsremote = False - # ========================================================================= # General methods # ========================================================================= @@ -159,8 +110,6 @@ def run(self, *cmd, **kwargs): # ========================================================================= # Tag / Branch related methods - # These methods only apply if supports_tags = True and/or - # support_branches = True # ========================================================================= @property diff --git a/requirements/testing.in b/requirements/testing.in index 9b1c77f3769..ca7b2252cdf 100644 --- a/requirements/testing.in +++ b/requirements/testing.in @@ -9,8 +9,6 @@ pytest-custom-exit-code pytest-django pytest-cov -Mercurial - yamale<3.0 pytest-mock diff --git a/requirements/testing.txt b/requirements/testing.txt index 8dab2ab75e4..f141fd4ce5a 100644 --- a/requirements/testing.txt +++ b/requirements/testing.txt @@ -267,8 +267,6 @@ markdown==3.6 # via -r requirements/pip.txt markupsafe==2.1.5 # via jinja2 -mercurial==6.7.3 - # via -r requirements/testing.in oauthlib==3.2.2 # via # -r requirements/pip.txt From 72c5ab60d67ba902effc913f7fb03dd6127c6a61 Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Mon, 10 Jun 2024 12:31:36 +0200 Subject: [PATCH 2/3] Add migrations --- .../migrations/0016_deprecate_old_vcs.py | 36 +++++++++++++++++++ .../migrations/0123_deprecate_old_vcs.py | 34 ++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 readthedocs/oauth/migrations/0016_deprecate_old_vcs.py create mode 100644 readthedocs/projects/migrations/0123_deprecate_old_vcs.py diff --git a/readthedocs/oauth/migrations/0016_deprecate_old_vcs.py b/readthedocs/oauth/migrations/0016_deprecate_old_vcs.py new file mode 100644 index 00000000000..4bb68466b81 --- /dev/null +++ b/readthedocs/oauth/migrations/0016_deprecate_old_vcs.py @@ -0,0 +1,36 @@ +# Generated by Django 4.2.13 on 2024-06-10 10:29 + +import django.core.validators +from django.db import migrations, models +from django_safemigrate import Safe + + +class Migration(migrations.Migration): + safe = Safe.before_deploy + dependencies = [ + ("oauth", "0015_increase_avatar_url_length"), + ] + + operations = [ + migrations.AlterField( + model_name="remoterepository", + name="clone_url", + field=models.URLField( + blank=True, + max_length=512, + validators=[ + django.core.validators.URLValidator( + schemes=["http", "https", "ssh", "git"] + ) + ], + verbose_name="Repository clone URL", + ), + ), + migrations.AlterField( + model_name="remoterepository", + name="vcs", + field=models.CharField( + blank=True, choices=[("git", "Git")], max_length=200, verbose_name="vcs" + ), + ), + ] diff --git a/readthedocs/projects/migrations/0123_deprecate_old_vcs.py b/readthedocs/projects/migrations/0123_deprecate_old_vcs.py new file mode 100644 index 00000000000..9de972262f8 --- /dev/null +++ b/readthedocs/projects/migrations/0123_deprecate_old_vcs.py @@ -0,0 +1,34 @@ +# Generated by Django 4.2.13 on 2024-06-10 10:29 + +from django.db import migrations, models +from django_safemigrate import Safe + + +class Migration(migrations.Migration): + safe = Safe.before_deploy + dependencies = [ + ("projects", "0122_add_httpheader_option"), + ] + + operations = [ + migrations.AlterField( + model_name="historicalproject", + name="repo_type", + field=models.CharField( + choices=[("git", "Git")], + default="git", + max_length=10, + verbose_name="Repository type", + ), + ), + migrations.AlterField( + model_name="project", + name="repo_type", + field=models.CharField( + choices=[("git", "Git")], + default="git", + max_length=10, + verbose_name="Repository type", + ), + ), + ] From 42ee75df8a6ed01ee2be500fe5a015b2f89c2f7e Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Mon, 10 Jun 2024 12:32:07 +0200 Subject: [PATCH 3/3] Update readthedocs/projects/constants.py Co-authored-by: Eric Holscher <25510+ericholscher@users.noreply.github.com> --- readthedocs/projects/constants.py | 1 + 1 file changed, 1 insertion(+) diff --git a/readthedocs/projects/constants.py b/readthedocs/projects/constants.py index 4d72e126f2f..9d64f0a57af 100644 --- a/readthedocs/projects/constants.py +++ b/readthedocs/projects/constants.py @@ -82,6 +82,7 @@ REPO_TYPE_GIT = "git" +# TODO: Remove this since we only have 1 type. REPO_CHOICES = ((REPO_TYPE_GIT, _("Git")),) PUBLIC = "public"