Skip to content

Commit 78f9494

Browse files
committed
Expose environment variables from database into build commands
Environment variables can be added from the Admin and they will be used when running build commands. All the variables for that particular project will be expose to all the commands.
1 parent 1648006 commit 78f9494

File tree

6 files changed

+100
-14
lines changed

6 files changed

+100
-14
lines changed

readthedocs/doc_builder/environments.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,7 @@ class BuildEnvironment(BaseEnvironment):
440440
ProjectBuildsSkippedError,
441441
YAMLParseError,
442442
BuildTimeoutError,
443-
MkDocsYAMLParseError
443+
MkDocsYAMLParseError,
444444
)
445445

446446
def __init__(self, project=None, version=None, build=None, config=None,
@@ -466,7 +466,7 @@ def __exit__(self, exc_type, exc_value, tb):
466466
project=self.project.slug,
467467
version=self.version.slug,
468468
msg='Build finished',
469-
)
469+
),
470470
)
471471
return ret
472472

readthedocs/projects/admin.py

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,38 @@
1+
# -*- coding: utf-8 -*-
12
"""Django administration interface for `projects.models`"""
23

3-
from __future__ import absolute_import
4-
from django.contrib import admin
5-
from django.contrib import messages
4+
from __future__ import (
5+
absolute_import,
6+
division,
7+
print_function,
8+
unicode_literals,
9+
)
10+
11+
from django.contrib import admin, messages
612
from django.contrib.admin.actions import delete_selected
713
from django.utils.translation import ugettext_lazy as _
814
from guardian.admin import GuardedModelAdmin
915

16+
from readthedocs.builds.models import Version
1017
from readthedocs.core.models import UserProfile
1118
from readthedocs.core.utils import broadcast
12-
from readthedocs.builds.models import Version
13-
from readthedocs.redirects.models import Redirect
1419
from readthedocs.notifications.views import SendNotificationView
20+
from readthedocs.redirects.models import Redirect
1521

1622
from .forms import FeatureForm
17-
from .models import (Project, ImportedFile, Feature,
18-
ProjectRelationship, EmailHook, WebHook, Domain)
23+
from .models import (
24+
Domain,
25+
EmailHook,
26+
EnvironmentVariable,
27+
Feature,
28+
ImportedFile,
29+
Project,
30+
ProjectRelationship,
31+
WebHook,
32+
)
1933
from .notifications import ResourceUsageNotification
2034
from .tasks import remove_dir
2135

22-
2336
class ProjectSendNotificationView(SendNotificationView):
2437
notification_classes = [ResourceUsageNotification]
2538

@@ -202,7 +215,14 @@ def project_count(self, feature):
202215
return feature.projects.count()
203216

204217

218+
class EnvironmentVariableAdmin(admin.ModelAdmin):
219+
model = EnvironmentVariable
220+
list_display = ('name', 'value', 'project', 'created')
221+
search_fields = ('name', 'project__slug')
222+
223+
205224
admin.site.register(Project, ProjectAdmin)
225+
admin.site.register(EnvironmentVariable, EnvironmentVariableAdmin)
206226
admin.site.register(ImportedFile, ImportedFileAdmin)
207227
admin.site.register(Domain, DomainAdmin)
208228
admin.site.register(Feature, FeatureAdmin)
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# -*- coding: utf-8 -*-
2+
# Generated by Django 1.11.16 on 2018-11-12 13:57
3+
from __future__ import unicode_literals
4+
5+
from django.db import migrations, models
6+
import django.db.models.deletion
7+
import django_extensions.db.fields
8+
9+
10+
class Migration(migrations.Migration):
11+
12+
dependencies = [
13+
('projects', '0032_increase_webhook_maxsize'),
14+
]
15+
16+
operations = [
17+
migrations.CreateModel(
18+
name='EnvironmentVariable',
19+
fields=[
20+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
21+
('created', django_extensions.db.fields.CreationDateTimeField(auto_now_add=True, verbose_name='created')),
22+
('modified', django_extensions.db.fields.ModificationDateTimeField(auto_now=True, verbose_name='modified')),
23+
('name', models.CharField(help_text='Name of the environment variable', max_length=128)),
24+
('value', models.CharField(help_text='Value of the environment variable', max_length=256)),
25+
],
26+
options={
27+
'ordering': ('-modified', '-created'),
28+
'get_latest_by': 'modified',
29+
'abstract': False,
30+
},
31+
),
32+
migrations.AddField(
33+
model_name='environmentvariable',
34+
name='project',
35+
field=models.ForeignKey(help_text='Project where this variable will be used', on_delete=django.db.models.deletion.CASCADE, to='projects.Project'),
36+
),
37+
]

readthedocs/projects/models.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from django.db import models
1616
from django.utils.encoding import python_2_unicode_compatible
1717
from django.utils.translation import ugettext_lazy as _
18+
from django_extensions.db.models import TimeStampedModel
1819
from future.backports.urllib.parse import urlparse # noqa
1920
from guardian.shortcuts import assign
2021
from taggit.managers import TaggableManager
@@ -1109,3 +1110,19 @@ def get_feature_display(self):
11091110
implement this behavior.
11101111
"""
11111112
return dict(self.FEATURES).get(self.feature_id, self.feature_id)
1113+
1114+
1115+
class EnvironmentVariable(TimeStampedModel, models.Model):
1116+
name = models.CharField(
1117+
max_length=128,
1118+
help_text='Name of the environment variable',
1119+
)
1120+
value = models.CharField(
1121+
max_length=256,
1122+
help_text='Value of the environment variable',
1123+
)
1124+
project = models.ForeignKey(
1125+
Project,
1126+
on_delete=models.CASCADE,
1127+
help_text='Project where this variable will be used',
1128+
)

readthedocs/projects/tasks.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -548,7 +548,7 @@ def get_build(build_pk):
548548
if build_pk:
549549
build = api_v2.build(build_pk).get()
550550
private_keys = [
551-
'project', 'version', 'resource_uri', 'absolute_uri'
551+
'project', 'version', 'resource_uri', 'absolute_uri',
552552
]
553553
return {
554554
key: val
@@ -605,7 +605,7 @@ def get_env_vars(self):
605605
env = {
606606
'READTHEDOCS': True,
607607
'READTHEDOCS_VERSION': self.version.slug,
608-
'READTHEDOCS_PROJECT': self.project.slug
608+
'READTHEDOCS_PROJECT': self.project.slug,
609609
}
610610

611611
if self.config.conda is not None:
@@ -616,7 +616,7 @@ def get_env_vars(self):
616616
self.project.doc_path,
617617
'conda',
618618
self.version.slug,
619-
'bin'
619+
'bin',
620620
),
621621
})
622622
else:
@@ -625,10 +625,15 @@ def get_env_vars(self):
625625
self.project.doc_path,
626626
'envs',
627627
self.version.slug,
628-
'bin'
628+
'bin',
629629
),
630630
})
631631

632+
# Update environment from Project's specific environment variables
633+
env.update(
634+
api_v2.project(self.project.pk).environment_variables().get(),
635+
)
636+
632637
return env
633638

634639
def set_valid_clone(self):

readthedocs/restapi/views/model_views.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,13 @@ def valid_versions(self, request, **kwargs):
117117
'flat': version_strings,
118118
})
119119

120+
@detail_route(permission_classes=[permissions.IsAdminUser])
121+
def environment_variables(self, request, *_, **__):
122+
return Response({
123+
env.name: env.value
124+
for env in self.get_object().environmentvariable_set.all()
125+
})
126+
120127
@detail_route()
121128
def translations(self, *_, **__):
122129
translations = self.get_object().translations.all()

0 commit comments

Comments
 (0)