-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
CRUD for EnvironmentVariables from Project's admin #4899
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
Changes from all commits
1884e62
ac572ba
f01bbbe
9a20328
74274cf
63b208d
26eba5a
67d29a4
88d70b5
74216af
b427496
3381e85
bd0b9fa
dec39ba
e7a2ea9
663f383
ca5cf05
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
I Need Secrets (or Environment Variables) in my Build | ||
===================================================== | ||
|
||
It may happen that your documentation depends on an authenticated service to be built properly. | ||
In this case, you will require some secrets to access these services. | ||
|
||
Read the Docs provides a way to define environment variables for your project to be used in the build process. | ||
All these variables will be exposed to all the commands executed when building your documentation. | ||
|
||
To define an environment variable, you need to | ||
|
||
#. Go to your project **Admin > Environment Variables** | ||
#. Click on "Add Environment Variable" button | ||
#. Input a ``Name`` and ``Value`` (your secret needed here) | ||
#. Click "Save" button | ||
|
||
.. note:: | ||
|
||
Values will never be exposed to users, even to owners of the project. Once you create an environment variable you won't be able to see its value anymore because of security purposes. | ||
|
||
After adding an environment variable from your project's admin, you can access it from your build process using Python, for example: | ||
|
||
.. code-block:: python | ||
|
||
# conf.py | ||
import os | ||
import requests | ||
|
||
# Access to our custom environment variables | ||
username = os.environ.get('USERNAME') | ||
password = os.environ.get('PASSWORD') | ||
|
||
# Request a username/password protected URL | ||
response = requests.get( | ||
'https://httpbin.org/basic-auth/username/password', | ||
auth=(username, password), | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
"""Project forms.""" | ||
|
||
from __future__ import ( | ||
|
@@ -8,6 +9,18 @@ | |
unicode_literals, | ||
) | ||
|
||
try: | ||
# TODO: remove this when we deprecate Python2 | ||
# re.fullmatch is >= Py3.4 only | ||
from re import fullmatch | ||
except ImportError: | ||
# https://stackoverflow.com/questions/30212413/backport-python-3-4s-regular-expression-fullmatch-to-python-2 | ||
import re | ||
|
||
def fullmatch(regex, string, flags=0): | ||
"""Emulate python-3.4 re.fullmatch().""" # noqa | ||
return re.match("(?:" + regex + r")\Z", string, flags=flags) | ||
|
||
from random import choice | ||
|
||
from builtins import object | ||
|
@@ -27,11 +40,11 @@ | |
from readthedocs.integrations.models import Integration | ||
from readthedocs.oauth.models import RemoteRepository | ||
from readthedocs.projects import constants | ||
from readthedocs.projects.constants import PUBLIC | ||
from readthedocs.projects.exceptions import ProjectSpamError | ||
from readthedocs.projects.models import ( | ||
Domain, | ||
EmailHook, | ||
EnvironmentVariable, | ||
Feature, | ||
Project, | ||
ProjectRelationship, | ||
|
@@ -758,3 +771,49 @@ class Meta(object): | |
def __init__(self, *args, **kwargs): | ||
super(FeatureForm, self).__init__(*args, **kwargs) | ||
self.fields['feature_id'].choices = Feature.FEATURES | ||
|
||
|
||
class EnvironmentVariableForm(forms.ModelForm): | ||
|
||
""" | ||
Form to add an EnvironmentVariable to a Project. | ||
|
||
This limits the name of the variable. | ||
""" | ||
|
||
project = forms.CharField(widget=forms.HiddenInput(), required=False) | ||
|
||
class Meta(object): | ||
model = EnvironmentVariable | ||
fields = ('name', 'value', 'project') | ||
|
||
def __init__(self, *args, **kwargs): | ||
self.project = kwargs.pop('project', None) | ||
super(EnvironmentVariableForm, self).__init__(*args, **kwargs) | ||
|
||
def clean_project(self): | ||
return self.project | ||
|
||
def clean_name(self): | ||
name = self.cleaned_data['name'] | ||
if name.startswith('__'): | ||
raise forms.ValidationError( | ||
_("Variable name can't start with __ (double underscore)"), | ||
) | ||
elif name.startswith('READTHEDOCS'): | ||
raise forms.ValidationError( | ||
_("Variable name can't start with READTHEDOCS"), | ||
) | ||
elif self.project.environmentvariable_set.filter(name=name).exists(): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could add this as a constraint in the model There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. I suppose that we still need the validation in the form otherwise I think it will explode when trying to save it and the user will have a 500. |
||
raise forms.ValidationError( | ||
_('There is already a variable with this name for this project'), | ||
) | ||
elif ' ' in name: | ||
raise forms.ValidationError( | ||
_("Variable name can't contain spaces"), | ||
) | ||
elif not fullmatch('[a-zA-Z0-9_]+', name): | ||
raise forms.ValidationError( | ||
_('Only letters, numbers and underscore are allowed'), | ||
) | ||
return name |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💯 addition.