Repository: readthedocs/readthedocs.org

Issue:
Detect when the default branch is renamed (e.g. from `master` to `main`)
## Details

RTD, by default, attempts to guess what the default branch is based on the VCS you have configured. For `git` it defaults to assuming the name `master`. With GitHub migrating to `main` (see: https://github.com/github/renaming) this results in a build failure until the user notices and manually sets the branch to `main` in the advanced settings.

## Expected Result

RTD should keep building after the default branch name is changed to `main`.

## Actual Result

It fails and users need to manually set it in the advanced settings.


Discussion:
@reaperhulk can you describe what steps you followed?

If you import your project by using our wizard ("Import a Project" button), `main` being the default branch should have been detected automatically.

If you has a project already imported and you rename `master` to `main`, we don't have a way to know that and I'm not sure that we can make that assumption.
The latter (rename) is what occurred. If RTD can’t detect arbitrary branch rename (although has anyone reported an issue to GH about that?) then in a case where master disappears it seems worthwhile to probe for main and use that if present. It’s possible this will be wrong but it will be right far more often. 
Here we aren't assuming the name of the branch, it's the name already given to the latest version, it is hard to us to assume the branch was re-named and not deleted, we are stopping from "doing magic" in favor of users to be explicit. So, I don't think we would implement some custom logic for this case. You can use the API to automate this https://docs.readthedocs.io/en/stable/api/v3.html#project-update ("default_branch").
I have a question related to this. In my case, I actually *renamed* `master` to `main` (not creating a duplicate and also keeping `master`). In the RTD admin panel, it still says `latest` is grabbing from `master` and have an option for me to activate `main` separately. But in my settings, I have latest set to `----`, which says, "What branch 'latest' points to. Leave empty to use the default value for your VCS (eg. trunk or master)." But the default (by which I mean the value GitHub API returns when you ask for default branch) is now "main." But RTD is not reporting failure. What is really going on here? I am confused.
I too have a question relating to this. My packages main branch is `main`, it has never had a `master` branch. When I first setup the project in RTD it chooses the branch main and successfully builds the docs, however, after the first build RTD changes to use the `master` branch causing the error `error: pathspec 'master' did not match any file(s) known to git.`. When I go into advanced settings to change the branch the only option available is `----------`. Is it possible for me too explicitly specify a branch for RTD to use?
@pllim @this-josh when the option is empty (`------`) it defaults to "master" not to the default branch set in GitHub. If there is another problem, please link me to your project so I can take a look.
@stsewd.  My project: https://github.com/this-josh/felling as you can see there isn't a "master" branch, and in RTD the only option is `-------` which is defaulting to "master" so I cannot get my docs to build.
@this-josh can you link me to your Read the Docs project? (not the repo)
@stsewd  Sorry, here it is: https://felling.readthedocs.io/en/latest/ . Note it built when I first made the project as it correctly identified the `main` branch.
> it defaults to "master" not to the default branch set in GitHub

Is there a reason why the RTD default cannot be consistent with `default_branch` from https://docs.github.com/en/rest/reference/repos ?
> Is there a reason why the RTD default cannot be consistent with `default_branch` from https://docs.github.com/en/rest/reference/repos ?

The value is harcoded. We could make it dynamic, but not all repos are connected to a remote repository.
@this-josh looks like your project was building with main at the beginning, but them for some reason all versions disappeared. That's why you only see `---` as option. Not sure how that happened. I have set the default branch to main manually for now. Maybe did you change your project to be single version?

https://readthedocs.org/projects/felling/builds/13325683/

Let us know if you see this happening again.

Edit: looks like your project isn't creating the versions, this may be a bug, I'm looking into that.
@this-josh I found the problem, you have two versions with the _same_ name: `Felling-v0.0.2` and `felling-v0.0.2`, we are creating the versions in bulk so our sluggify code isn't called as we expect :/. I'll send a PR, you can remove one of those in the meantime, or don't click "save" in the admin of your project till next week the fix is live, your project already has the "main" branch as default.
@stsewd Thanks for manually setting it to main. No, I haven't used the single version option as ideally I'd like multiple versions eventually. 

Thank you for finding that! This is the first time I've tried to properly version a package and I've certainly had a few hiccups along the way. I have removed the tag `Felling-v0.0.2` as this one wasn't required, and I won't interact with the RTD admin section until next week.
FYI: This affected at least 2 of PyPA's projects as well, and it is a very subtle breakage that isn't detected until someone notices things being missing in the changelog or something.

The fix is relatively straightforward, once you know what's happening though. I've described what I had to do for pypa/packaging in https://github.com/pypa/packaging/issues/415#issuecomment-827884116.
Hi all - we acknowledge this is affecting lots of projects these days, and that RTD is not very smart about it. We have some internal changes underway that could simplify this transition or make it invisible for users, but it will still take us some time to get there.

In the meanwhile, if you rename your default branch from `master` to `main` (or something else), please follow these steps to get the builds working again:

1. Go to `https://readthedocs.org/dashboard/{your_project}/advanced/`, and change the "Default branch" from `-----` to `main` (or whatever your default branch is)
2. This will trigger a rebuild of your "Default version" (`stable`, `latest`, or something else). If needed, launch a rebuild of the version you are interested in.

From here, everything should work again.

Please notice that:

- For newly imported projects, RTD will pick the correct default branch, as @humitos said above.
- It is unlikely that we implement solutions like polling GitHub to detect branch renames, since the number of projects we host is just too large.

I am renaming this issue to clarify what's actually happening, and pinning it so it's more visible.

We will update here if we improve the detection of branch renames for projects linked to GitHub repositories. Sorry all for the inconvenience and thanks for your patience!
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.


Resolving patch:
diff --git a/readthedocs/projects/models.py b/readthedocs/projects/models.py
--- a/readthedocs/projects/models.py
+++ b/readthedocs/projects/models.py
@@ -1168,6 +1168,10 @@ def get_default_branch(self):
         """Get the version representing 'latest'."""
         if self.default_branch:
             return self.default_branch
+
+        if self.remote_repository and self.remote_repository.default_branch:
+            return self.remote_repository.default_branch
+
         return self.vcs_class().fallback_branch
 
     def add_subproject(self, child, alias=None):


Test patch:
diff --git a/readthedocs/rtd_tests/tests/test_project_views.py b/readthedocs/rtd_tests/tests/test_project_views.py
--- a/readthedocs/rtd_tests/tests/test_project_views.py
+++ b/readthedocs/rtd_tests/tests/test_project_views.py
@@ -1,4 +1,3 @@
-from datetime import timedelta
 from unittest import mock
 
 from allauth.socialaccount.models import SocialAccount
@@ -6,7 +5,6 @@
 from django.http.response import HttpResponseRedirect
 from django.test import TestCase, override_settings
 from django.urls import reverse
-from django.utils import timezone
 from django.views.generic.base import ContextMixin
 from django_dynamic_fixture import get, new
 
@@ -127,7 +125,7 @@ def test_form_pass(self):
         self.assertEqual(proj.documentation_type, 'sphinx')
 
     def test_remote_repository_is_added(self):
-        remote_repo = get(RemoteRepository)
+        remote_repo = get(RemoteRepository, default_branch="default-branch")
         socialaccount = get(SocialAccount, user=self.user)
         get(
             RemoteRepositoryRelation,
@@ -144,6 +142,7 @@ def test_remote_repository_is_added(self):
         proj = Project.objects.get(name='foobar')
         self.assertIsNotNone(proj)
         self.assertEqual(proj.remote_repository, remote_repo)
+        self.assertEqual(proj.get_default_branch(), remote_repo.default_branch)
 
     def test_remote_repository_invalid_type(self):
         self.step_data['basics']['remote_repository'] = 'Invalid id'
@@ -264,7 +263,7 @@ def test_form_missing_extra(self):
         self.assertWizardFailure(resp, 'documentation_type')
 
     def test_remote_repository_is_added(self):
-        remote_repo = get(RemoteRepository)
+        remote_repo = get(RemoteRepository, default_branch="default-branch")
         socialaccount = get(SocialAccount, user=self.user)
         get(
             RemoteRepositoryRelation,
@@ -283,6 +282,7 @@ def test_remote_repository_is_added(self):
         proj = Project.objects.get(name='foobar')
         self.assertIsNotNone(proj)
         self.assertEqual(proj.remote_repository, remote_repo)
+        self.assertEqual(proj.get_default_branch(), remote_repo.default_branch)
 
 
 @mock.patch('readthedocs.core.utils.trigger_build', mock.MagicMock())