Skip to content

Commit f32a0f2

Browse files
committed
Merge tag '8.6.0' into rel
2 parents 711539e + c31899b commit f32a0f2

File tree

80 files changed

+2051
-1446
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

80 files changed

+2051
-1446
lines changed

.github/workflows/pip-tools.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,15 @@ on:
1111
# Run weekly on day 0 at 00:00 UTC
1212
- cron: "0 0 * * 0"
1313

14+
permissions:
15+
contents: read
16+
1417
jobs:
1518
update-dependencies:
19+
permissions:
20+
contents: write # to create branch (peter-evans/create-pull-request)
21+
pull-requests: write # to create a PR (peter-evans/create-pull-request)
22+
1623
name: Update dependencies
1724
runs-on: ubuntu-latest
1825
steps:

CHANGELOG.rst

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,24 @@
1+
Version 8.6.0
2+
-------------
3+
4+
:Date: September 28, 2022
5+
6+
* `@github-actions[bot] <https://github.com/github-actions[bot]>`__: Dependencies: all packages updated via pip-tools (`#9621 <https://github.com/readthedocs/readthedocs.org/pull/9621>`__)
7+
* `@evildmp <https://github.com/evildmp>`__: Made some small changes to the MyST migration how-to (`#9620 <https://github.com/readthedocs/readthedocs.org/pull/9620>`__)
8+
* `@boahc077 <https://github.com/boahc077>`__: ci: add minimum GitHub at the workflow level for pip-tools.yaml (`#9617 <https://github.com/readthedocs/readthedocs.org/pull/9617>`__)
9+
* `@stsewd <https://github.com/stsewd>`__: Search: refactor API view (`#9613 <https://github.com/readthedocs/readthedocs.org/pull/9613>`__)
10+
* `@sashashura <https://github.com/sashashura>`__: GitHub Workflows security hardening (`#9609 <https://github.com/readthedocs/readthedocs.org/pull/9609>`__)
11+
* `@stsewd <https://github.com/stsewd>`__: Redirects: test with/without organizations (`#9605 <https://github.com/readthedocs/readthedocs.org/pull/9605>`__)
12+
* `@humitos <https://github.com/humitos>`__: Builds: concurrency small optimization (`#9602 <https://github.com/readthedocs/readthedocs.org/pull/9602>`__)
13+
* `@uvidyadharan <https://github.com/uvidyadharan>`__: Update intersphinx.rst (`#9601 <https://github.com/readthedocs/readthedocs.org/pull/9601>`__)
14+
* `@ericholscher <https://github.com/ericholscher>`__: Release 8.5.0 (`#9600 <https://github.com/readthedocs/readthedocs.org/pull/9600>`__)
15+
* `@github-actions[bot] <https://github.com/github-actions[bot]>`__: Dependencies: all packages updated via pip-tools (`#9596 <https://github.com/readthedocs/readthedocs.org/pull/9596>`__)
16+
* `@stsewd <https://github.com/stsewd>`__: OAuth: save refresh token (`#9594 <https://github.com/readthedocs/readthedocs.org/pull/9594>`__)
17+
* `@stsewd <https://github.com/stsewd>`__: Redirects: allow update (`#9593 <https://github.com/readthedocs/readthedocs.org/pull/9593>`__)
18+
* `@stsewd <https://github.com/stsewd>`__: Unresolver: strict validation for external versions and other fixes (`#9534 <https://github.com/readthedocs/readthedocs.org/pull/9534>`__)
19+
* `@stsewd <https://github.com/stsewd>`__: New unresolver implementation (`#9500 <https://github.com/readthedocs/readthedocs.org/pull/9500>`__)
20+
* `@stsewd <https://github.com/stsewd>`__: API v3: fix organizations permissions (`#8771 <https://github.com/readthedocs/readthedocs.org/pull/8771>`__)
21+
122
Version 8.5.0
223
-------------
324

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@
6969

7070
master_doc = "index"
7171
copyright = "2010, Read the Docs, Inc & contributors"
72-
version = "8.5.0"
72+
version = "8.6.0"
7373
release = version
7474
exclude_patterns = ["_build"]
7575
default_role = "obj"

docs/user/guides/authors.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@ and :doc:`/intro/getting-started-with-mkdocs`.
1616
cross-referencing-with-sphinx
1717
intersphinx
1818
jupyter
19-
migrate-rest-myst
19+
Migrate from rST to MyST <migrate-rest-myst>

docs/user/guides/intersphinx.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,8 @@ Result:
8383
provided by Intersphinx:
8484

8585
.. prompt:: bash $
86-
87-
python -msphinx.ext.intersphinx https://www.sphinx-doc.org/en/master/objects.inv
86+
87+
python -m sphinx.ext.intersphinx https://www.sphinx-doc.org/en/master/objects.inv
8888

8989
Intersphinx in Read the Docs
9090
----------------------------
@@ -167,7 +167,7 @@ You can use it like this:
167167
The inventory file is by default located at ``objects.inv``, for example ``https://readthedocs-docs.readthedocs-hosted.com/en/latest/objects.inv``.
168168

169169
.. code:: python
170-
170+
171171
# conf.py file
172172
173173
intersphinx_mapping = {

docs/user/guides/migrate-rest-myst.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
# Migrating from reStructuredText to MyST Markdown
1+
# How to migrate from reStructuredText to MyST Markdown
2+
3+
In this guide, you will find
4+
how you can start writing Markdown in your existing reStructuredText project,
5+
or migrate it completely.
26

37
Sphinx is usually associated with reStructuredText, the markup language
48
{pep}`designed for the CPython project in the early '00s <287>`.
@@ -9,15 +13,11 @@ The most powerful of such extensions is {doc}`MyST-Parser <myst-parser:index>`,
913
which implements a CommonMark-compliant, extensible Markdown dialect
1014
with support for the Sphinx roles and directives that make it so useful.
1115

12-
In this guide, you will find
13-
how you can start writing Markdown in your existing reStructuredText project,
14-
or migrate it completely.
15-
1616
If, **instead of migrating**, you are starting a new project from scratch,
1717
have a look at {doc}`myst-parser:intro`.
1818
If you are starting a **project for Jupyter**, you can start with Jupyter Book, which uses ``MyST-Parser``, see the official Jupyter Book tutorial: {doc}`jupyterbook:start/your-first-book`
1919

20-
## Writing your content both in reStructuredText and MyST
20+
## How to write your content both in reStructuredText and MyST
2121

2222
It is useful to ask whether a migration is necessary in the first place.
2323
Doing bulk migrations of large projects with lots of work in progress
@@ -54,7 +54,7 @@ If you want to use a different suffix, you can do so by changing your
5454
`source_suffix` configuration value in `conf.py`.
5555
```
5656

57-
## Converting existing reStructuredText documentation to MyST
57+
## How to convert existing reStructuredText documentation to MyST
5858

5959
To convert existing reST documents to MyST, you can use
6060
the `rst2myst` CLI script shipped by {doc}`rst-to-myst:index`.
@@ -71,7 +71,7 @@ $ rst2myst convert docs/**/*.rst # Convert every .rst file under the docs direc
7171

7272
This will create a `.md` MyST file for every `.rst` source file converted.
7373

74-
### Advanced usage of `rst2myst`
74+
### How to modify the behaviour of `rst2myst`
7575

7676
The `rst2myst` accepts several flags to modify its behavior.
7777
All of them have sensible defaults, so you don't have to specify them
@@ -90,7 +90,7 @@ These are a few options you might find useful:
9090
You can read the full list of options in
9191
{doc}``the `rst2myst` documentation <rst-to-myst:cli>``.
9292

93-
## Enabling optional syntax
93+
## How to enable optional syntax
9494

9595
Some reStructuredText syntax will require you to enable certain MyST plugins.
9696
For example, to write [reST definition lists], you need to add a
@@ -108,7 +108,7 @@ in their documentation.
108108

109109
[reST definition lists]: https://docutils.sourceforge.io/docs/user/rst/quickref.html#definition-lists
110110

111-
## Writing reStructuredText syntax within MyST
111+
## How to write reStructuredText syntax within MyST
112112

113113
There is a small chance that `rst2myst` does not properly understand a piece of reST syntax,
114114
either because there is a bug in the tool

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "readthedocs",
3-
"version": "8.5.0",
3+
"version": "8.6.0",
44
"description": "Read the Docs build dependencies",
55
"author": "Read the Docs, Inc <[email protected]>",
66
"scripts": {

readthedocs/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
"""Read the Docs."""
22

33

4-
__version__ = "8.5.0"
4+
__version__ = "8.6.0"

readthedocs/api/v3/mixins.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,7 @@
33
from rest_framework.response import Response
44

55
from readthedocs.builds.models import Version
6-
from readthedocs.core.history import (
7-
safe_update_change_reason,
8-
set_change_reason,
9-
)
6+
from readthedocs.core.history import safe_update_change_reason, set_change_reason
107
from readthedocs.organizations.models import Organization
118
from readthedocs.projects.models import Project
129

@@ -191,6 +188,13 @@ def has_admin_permission(self, user, organization):
191188

192189
return False
193190

191+
def is_admin_member(self, user, organization):
192+
return (
193+
Project.objects.for_admin_user(user=user)
194+
.filter(organizations__in=[organization])
195+
.exists()
196+
)
197+
194198
def admin_organizations(self, user):
195199
return Organization.objects.for_admin_user(user=user)
196200

readthedocs/api/v3/permissions.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,13 @@ def has_permission(self, request, view):
6262
return True
6363

6464

65+
class IsOrganizationAdminMember(BasePermission):
66+
def has_permission(self, request, view):
67+
organization = view._get_parent_organization()
68+
if view.is_admin_member(request.user, organization):
69+
return True
70+
71+
6572
class UserOrganizationsListing(BasePermission):
6673

6774
def has_permission(self, request, view):

readthedocs/api/v3/serializers.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,6 @@ def _validate_remote_repository(self, data):
485485
If we cannot ensure the relationship here, this method should raise a
486486
`ValidationError`.
487487
"""
488-
pass
489488

490489
def validate_name(self, value):
491490
potential_slug = slugify(value)
@@ -580,7 +579,7 @@ class ProjectSerializer(FlexFieldsModelSerializer):
580579
.. note::
581580
582581
When using organizations, projects don't have the concept of users.
583-
But we have organization.users.
582+
But we have organization.owners.
584583
"""
585584

586585
homepage = serializers.SerializerMethodField()
@@ -592,9 +591,7 @@ class ProjectSerializer(FlexFieldsModelSerializer):
592591
translation_of = serializers.SerializerMethodField()
593592
default_branch = serializers.CharField(source='get_default_branch')
594593
tags = serializers.StringRelatedField(many=True)
595-
596-
if not settings.RTD_ALLOW_ORGANIZATIONS:
597-
users = UserSerializer(many=True)
594+
users = UserSerializer(many=True)
598595

599596
_links = ProjectLinksSerializer(source='*')
600597

@@ -624,13 +621,10 @@ class Meta:
624621

625622
# NOTE: ``expandable_fields`` must not be included here. Otherwise,
626623
# they will be tried to be rendered and fail
627-
# 'users',
628624
# 'active_versions',
629-
630-
'_links',
625+
"_links",
626+
"users",
631627
]
632-
if not settings.RTD_ALLOW_ORGANIZATIONS:
633-
fields.append('users')
634628

635629
expandable_fields = {
636630
# NOTE: this has to be a Model method, can't be a
@@ -656,6 +650,15 @@ class Meta:
656650
),
657651
}
658652

653+
def __init__(self, *args, **kwargs):
654+
super().__init__(*args, **kwargs)
655+
# When using organizations, projects don't have the concept of users.
656+
# But we have organization.owners.
657+
# Set here instead of at the class level,
658+
# so is easier to test.
659+
if settings.RTD_ALLOW_ORGANIZATIONS:
660+
self.fields.pop("users", None)
661+
659662
def get_homepage(self, obj):
660663
# Overridden only to return ``None`` when the project_url is ``''``
661664
return obj.project_url or None

readthedocs/api/v3/tests/mixins.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ def setUp(self):
101101
self.others_token = fixture.get(Token, key='other', user=self.other)
102102
self.others_project = fixture.get(
103103
Project,
104+
id=2,
104105
slug='others-project',
105106
related_projects=[],
106107
main_language_project=None,

readthedocs/api/v3/views.py

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,7 @@
33
from rest_flex_fields import is_expanded
44
from rest_flex_fields.views import FlexFieldsMixin
55
from rest_framework import status
6-
from rest_framework.authentication import (
7-
SessionAuthentication,
8-
TokenAuthentication,
9-
)
6+
from rest_framework.authentication import SessionAuthentication, TokenAuthentication
107
from rest_framework.decorators import action
118
from rest_framework.metadata import SimpleMetadata
129
from rest_framework.mixins import (
@@ -20,11 +17,7 @@
2017
from rest_framework.renderers import BrowsableAPIRenderer
2118
from rest_framework.response import Response
2219
from rest_framework.throttling import AnonRateThrottle, UserRateThrottle
23-
from rest_framework.viewsets import (
24-
GenericViewSet,
25-
ModelViewSet,
26-
ReadOnlyModelViewSet,
27-
)
20+
from rest_framework.viewsets import GenericViewSet, ModelViewSet, ReadOnlyModelViewSet
2821
from rest_framework_extensions.mixins import NestedViewSetMixin
2922

3023
from readthedocs.builds.models import Build, Version
@@ -58,7 +51,12 @@
5851
UpdateChangeReasonMixin,
5952
UpdateMixin,
6053
)
61-
from .permissions import CommonPermissions, IsProjectAdmin
54+
from .permissions import (
55+
CommonPermissions,
56+
IsOrganizationAdminMember,
57+
IsProjectAdmin,
58+
UserOrganizationsListing,
59+
)
6260
from .renderers import AlphabeticalSortedJSONRenderer
6361
from .serializers import (
6462
BuildCreateSerializer,
@@ -393,16 +391,19 @@ def perform_create(self, serializer):
393391
serializer.save()
394392

395393

396-
class OrganizationsViewSetBase(APIv3Settings, NestedViewSetMixin,
397-
OrganizationQuerySetMixin,
398-
ReadOnlyModelViewSet):
394+
class OrganizationsViewSet(
395+
APIv3Settings,
396+
NestedViewSetMixin,
397+
OrganizationQuerySetMixin,
398+
ReadOnlyModelViewSet,
399+
):
399400

400401
model = Organization
401402
lookup_field = 'slug'
402403
lookup_url_kwarg = 'organization_slug'
403404
queryset = Organization.objects.all()
404405
serializer_class = OrganizationSerializer
405-
406+
permission_classes = [UserOrganizationsListing | IsOrganizationAdminMember]
406407
permit_list_expands = [
407408
'projects',
408409
'teams',
@@ -422,19 +423,16 @@ def get_queryset(self):
422423
return super().get_queryset()
423424

424425

425-
class OrganizationsViewSet(SettingsOverrideObject):
426-
_default_class = OrganizationsViewSetBase
427-
428-
429-
class OrganizationsProjectsViewSetBase(APIv3Settings, NestedViewSetMixin,
430-
OrganizationQuerySetMixin,
431-
ReadOnlyModelViewSet):
426+
class OrganizationsProjectsViewSet(
427+
APIv3Settings, NestedViewSetMixin, OrganizationQuerySetMixin, ReadOnlyModelViewSet
428+
):
432429

433430
model = Project
434431
lookup_field = 'slug'
435432
lookup_url_kwarg = 'project_slug'
436433
queryset = Project.objects.all()
437434
serializer_class = ProjectSerializer
435+
permission_classes = [IsOrganizationAdminMember]
438436
permit_list_expands = [
439437
'organization',
440438
'organization.teams',
@@ -444,10 +442,6 @@ def get_view_name(self):
444442
return f'Organizations Projects {self.suffix}'
445443

446444

447-
class OrganizationsProjectsViewSet(SettingsOverrideObject):
448-
_default_class = OrganizationsProjectsViewSetBase
449-
450-
451445
class RemoteRepositoryViewSet(
452446
APIv3Settings,
453447
RemoteQuerySetMixin,

readthedocs/builds/querysets.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,9 +205,9 @@ def concurrent(self, project):
205205
"""
206206
limit_reached = False
207207
query = Q(
208-
project__slug=project.slug,
208+
project=project,
209209
# Limit builds to 5 hours ago to speed up the query
210-
date__gte=timezone.now() - datetime.timedelta(hours=5),
210+
date__gt=timezone.now() - datetime.timedelta(hours=5),
211211
)
212212

213213
if project.main_language_project:

0 commit comments

Comments
 (0)