Skip to content

Commit 682b62f

Browse files
committed
Merge remote-tracking branch 'origin/master' into show-project-slug
2 parents 26a68c0 + a2eee02 commit 682b62f

File tree

60 files changed

+686
-551
lines changed

Some content is hidden

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

60 files changed

+686
-551
lines changed

contrib/add-project.py

+168
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
"""
2+
Script to add a Git project as cloned from a Github repository
3+
to ReadTheDocs. Invoke from the checkout.
4+
"""
5+
6+
__requires__ = ['requests-toolbelt', 'autocommand', 'keyring']
7+
8+
9+
import os
10+
import getpass
11+
import re
12+
import urllib.parse
13+
import subprocess
14+
import pathlib
15+
16+
import autocommand
17+
import keyring
18+
from requests_toolbelt import sessions
19+
20+
21+
rtd = sessions.BaseUrlSession(
22+
os.environ.get('RTD_URL', 'https://readthedocs.org/api/v2/'))
23+
github = sessions.BaseUrlSession('https://api.github.com/')
24+
github.headers.update(Accept='application/vnd.github.v3+json')
25+
26+
27+
class User:
28+
"""
29+
A User (with a password) in RTD.
30+
31+
Resolves username using ``getpass.getuser()``. Override with
32+
RTD_USERNAME environment variable.
33+
34+
Resolves password using keyring. Install keyring and run
35+
``keyring set https://readthedocs.org/ $USER`` to set the pw.
36+
Override with RTD_PASSWORD environment variable.
37+
"""
38+
def __init__(self):
39+
self.name = os.environ.get('RTD_USERNAME') or getpass.getuser()
40+
system = rtd.create_url('/')
41+
self.password = (
42+
os.environ.get('RTD_PASSWORD')
43+
or keyring.get_password(system, self.name)
44+
)
45+
46+
@property
47+
def id(self):
48+
resp = rtd.get('../v1/user/', params=dict(username=self.name))
49+
resp.raise_for_status()
50+
ob, = resp.json()['objects']
51+
return ob['id']
52+
53+
@property
54+
def tuple(self):
55+
return self.name, self.password
56+
57+
58+
class Sluggable(str):
59+
"""
60+
A name for use in RTD with a 'slug' version.
61+
"""
62+
@property
63+
def slug(self):
64+
return self.replace('.', '')
65+
66+
67+
class Repo:
68+
"""
69+
A Git repo
70+
"""
71+
72+
def __init__(self, root):
73+
self.root = root
74+
cmd = ['git', '-C', root, 'remote', 'get-url', 'origin']
75+
proc = subprocess.run(
76+
cmd, check=True, text=True, stdout=subprocess.PIPE)
77+
self.url = proc.stdout.strip()
78+
79+
@property
80+
def name(self):
81+
return Sluggable(pathlib.Path(self.url).stem)
82+
83+
84+
def create_project(repo):
85+
"""
86+
Create the project from a Repo
87+
"""
88+
user = User()
89+
payload = dict(
90+
repo=repo.url,
91+
slug=repo.name.slug,
92+
name=repo.name,
93+
users=[user.id],
94+
)
95+
resp = rtd.post('project/', json=payload, auth=user.tuple)
96+
resp.raise_for_status()
97+
98+
99+
def configure_github(name, url):
100+
"""
101+
Given a project name and webhook URL, configure the webhook
102+
in GitHub.
103+
104+
Resolves username from ``getpass.getuser()``. Override with
105+
``GITHUB_USERNAME``.
106+
107+
Resolves access token from keyring for username and system
108+
'github.com'. Override with ``GITHUB_TOKEN`` environment
109+
variable.
110+
"""
111+
user = os.environ.get('GITHUB_USERNAME') or getpass.getuser()
112+
token = (
113+
keyring.get_password('github.com', user)
114+
or os.environ['GITHUB_TOKEN']
115+
)
116+
headers = dict(Authorization=f'token {token}')
117+
path = f'/repos/{user}/{name}/hooks'
118+
params = dict(
119+
name='web',
120+
config=dict(
121+
url=url,
122+
content_type='json',
123+
),
124+
)
125+
github.post(path, json=params, headers=headers)
126+
127+
128+
def configure_webhook(name):
129+
"""
130+
Identify the webhook URL for a RTD project named name.
131+
"""
132+
login_path = '/accounts/login/'
133+
resp = rtd.get(login_path)
134+
token = rtd.cookies.get('csrftoken') or rtd.cookies['csrf']
135+
user = User()
136+
params = dict(
137+
login=user.name,
138+
password=user.password,
139+
csrfmiddlewaretoken=token,
140+
next='/',
141+
)
142+
headers = dict(Referer=rtd.create_url(login_path))
143+
resp = rtd.post(login_path, data=params, headers=headers)
144+
token = rtd.cookies.get('csrftoken') or rtd.cookies['csrf']
145+
params = dict(
146+
integration_type='github_webhook',
147+
csrfmiddlewaretoken=token,
148+
next='/',
149+
)
150+
create_path = f'/dashboard/{name.slug}/integrations/create/'
151+
headers = dict(
152+
Referer=rtd.create_url(create_path),
153+
)
154+
resp = rtd.post(
155+
create_path,
156+
data=params,
157+
headers=headers,
158+
)
159+
resp.raise_for_status()
160+
ref = re.search(f'<a href="(.*?)">.*?webhook.*?</a>', resp.text).group(1)
161+
return urllib.parse.urljoin(resp.url, ref)
162+
163+
164+
@autocommand.autocommand(__name__)
165+
def main(repo: Repo = Repo('.')):
166+
create_project(repo)
167+
url = configure_webhook(repo.name)
168+
configure_github(repo.name, url)

docs/.rstcheck.cfg

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
[rstcheck]
2-
ignore_directives=automodule,http:get
2+
ignore_directives=automodule,http:get,tabs,tab
33
ignore_roles=djangosetting,setting
44
ignore_messages=(Duplicate implicit target name: ".*")|(Hyperlink target ".*" is not referenced)

docs/api/v2.rst

+1
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ Build list
253253
:>json array results: Array of ``Build`` objects.
254254

255255
:query string project__slug: Narrow to builds for a specific ``Project``
256+
:query string commit: Narrow to builds for a specific ``commit``
256257

257258
Build detail
258259
++++++++++++

docs/conf.py

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
'sphinxcontrib.httpdomain',
2828
'djangodocs',
2929
'doc_extensions',
30+
'sphinx_tabs.tabs',
3031
]
3132
templates_path = ['_templates']
3233

docs/design.rst

+6
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,10 @@ There's not a hard browser range, but your design changes should work reasonably
5151
browsers, IE8+ -- that's not to say it needs to be pixel-perfect in older browsers! Just avoid
5252
making changes that render older browsers utterly unusable (or provide a sane fallback).
5353

54+
Brand Guidelines
55+
----------------
56+
57+
Find our branding guidelines in our guidelines documentation: https://read-the-docs-guidelines.readthedocs-hosted.com.
58+
5459
.. _Read the Docs GitHub project: https://github.com/rtfd/readthedocs.org/pulls
60+

0 commit comments

Comments
 (0)