Skip to content

Commit 8ef4db8

Browse files
committed
Automatically generate Django series roadmaps from Release data
This branch adds a new view that replaces the Roadmap wiki pages, e.g.: * https://code.djangoproject.com/wiki/Version5.2Roadmap * https://code.djangoproject.com/wiki/Version6.0Roadmap * https://code.djangoproject.com/wiki/Version6.1Roadmap The wiki pages were almost identical except for version-specific details and target dates. This information is now generated dynamically from the Release model in the database. The supported and future Roadmap tables were reorganized so they follow a more intuitive timeline (from older to newer versions).
1 parent 2538a99 commit 8ef4db8

File tree

6 files changed

+281
-45
lines changed

6 files changed

+281
-45
lines changed

djangoproject/scss/_style.scss

Lines changed: 38 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2529,14 +2529,48 @@ dl.data {
25292529
white-space: normal;
25302530
}
25312531

2532+
table {
2533+
th {
2534+
background: var(--sidebar-bg);
2535+
font-weight: bold;
2536+
text-align: left;
2537+
}
2538+
2539+
td {
2540+
border-bottom: 1px solid var(--hairline-color);
2541+
}
2542+
2543+
td,
2544+
th {
2545+
padding: 0.5em 1em;
2546+
}
2547+
}
2548+
25322549
table.foundation td {
2533-
border-bottom: 1px solid var(--hairline-color);
25342550
padding: 0 5px;
25352551
}
25362552

2537-
table.docutils td,
2538-
table.docutils th {
2539-
border-bottom: 1px solid var(--hairline-color);
2553+
table.django-supported-versions,
2554+
table.django-unsupported-versions {
2555+
border: 1px solid var(--hairline-color);
2556+
color: var(--table-color);
2557+
2558+
th,
2559+
td {
2560+
border-bottom: none;
2561+
padding: 5px;
2562+
text-align: center;
2563+
}
2564+
}
2565+
2566+
table.django-supported-versions th,
2567+
table.django-supported-versions tr {
2568+
background-color: var(--secondary-accent);
2569+
}
2570+
2571+
table.django-unsupported-versions th,
2572+
table.django-unsupported-versions tr {
2573+
background-color: var(--error-light);
25402574
}
25412575

25422576
.list-links {
@@ -3420,26 +3454,6 @@ form .footnote {
34203454
}
34213455
}
34223456

3423-
table.django-supported-versions,
3424-
table.django-unsupported-versions {
3425-
border: 1px solid black;
3426-
text-align: center;
3427-
color: var(--table-color);
3428-
3429-
th,
3430-
td {
3431-
padding: 5px;
3432-
}
3433-
}
3434-
3435-
table.django-supported-versions tr {
3436-
background-color: var(--secondary-accent);
3437-
}
3438-
3439-
table.django-unsupported-versions tr {
3440-
background-color: var(--error-light);
3441-
}
3442-
34433457
/* Corporate membership list page */
34443458

34453459
ul.corporate-members li {

djangoproject/templates/releases/download.html

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,10 @@ <h2 id="supported-versions">Supported Versions</h2>
8585
<th>End of extended support<sup><a href="#ft2">2</a></sup></th>
8686
</tr>
8787
<tr>
88-
<td>5.2 LTS</td>
89-
<td>{% get_latest_micro_release '5.2' %}</td>
90-
<td>December 2025</td>
91-
<td>April 2028</td>
88+
<td>4.2 LTS</td>
89+
<td>{% get_latest_micro_release '4.2' %}</td>
90+
<td>December 4, 2023</td>
91+
<td>April 2026</td>
9292
</tr>
9393
<tr>
9494
<td>5.1</td>
@@ -97,10 +97,10 @@ <h2 id="supported-versions">Supported Versions</h2>
9797
<td>December 2025</td>
9898
</tr>
9999
<tr>
100-
<td>4.2 LTS</td>
101-
<td>{% get_latest_micro_release '4.2' %}</td>
102-
<td>December 4, 2023</td>
103-
<td>April 2026</td>
100+
<td>5.2 LTS</td>
101+
<td>{% get_latest_micro_release '5.2' %}</td>
102+
<td>December 2025</td>
103+
<td>April 2028</td>
104104
</tr>
105105
</table>
106106

@@ -114,28 +114,28 @@ <h2 id="future-versions">Future Roadmap</h2>
114114
<th>End of extended support<sup><a href="#ft2">2</a></sup></th>
115115
</tr>
116116
<tr>
117-
<td>7.0</td>
118-
<td>December 2027</td>
119-
<td>August 2028</td>
120-
<td>April 2029</td>
117+
<td><a href="{% url 'roadmap' '6.0' %}">6.0</a></td>
118+
<td>December 2025</td>
119+
<td>August 2026</td>
120+
<td>April 2027</td>
121121
</tr>
122122
<tr>
123-
<td>6.2 LTS</td>
123+
<td><a href="{% url 'roadmap' '6.1' %}">6.1</a></td>
124+
<td>August 2026</td>
124125
<td>April 2027</td>
125126
<td>December 2027</td>
126-
<td>April 2030</td>
127127
</tr>
128128
<tr>
129-
<td>6.1</td>
130-
<td>August 2026</td>
129+
<td><a href="{% url 'roadmap' '6.2' %}">6.2 LTS</a></td>
131130
<td>April 2027</td>
132131
<td>December 2027</td>
132+
<td>April 2030</td>
133133
</tr>
134134
<tr>
135-
<td>6.0</td>
136-
<td>December 2025</td>
137-
<td>August 2026</td>
138-
<td>April 2027</td>
135+
<td><a href="{% url 'roadmap' '7.0' %}">7.0</a></td>
136+
<td>December 2027</td>
137+
<td>August 2028</td>
138+
<td>April 2029</td>
139139
</tr>
140140
</table>
141141

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
{% extends "base.html" %}
2+
{% load date_format %}
3+
4+
{% block sectionid %}roadmap{% endblock %}
5+
{% block title %}Django {{ series }} Roadmap{% endblock %}
6+
{% block layout_class %}sidebar-right{% endblock %}
7+
8+
{% block header %}
9+
<p>Download</p>
10+
{% endblock %}
11+
12+
{% block content %}
13+
<h1>Django {{ series }} Roadmap</h1>
14+
<p>This document details the schedule and roadmap towards Django {{ series }}.</p>
15+
16+
<section id="what-features-will-be-released">
17+
<h2>What features will be in Django {{ series }}?</h2>
18+
<p>Whatever gets committed by the alpha feature freeze!</p>
19+
<p>Django {{ series }} will be a time-based release. Any features completed and committed
20+
to main by the alpha feature freeze deadline noted below will be included. Any
21+
that miss the deadline won't.</p>
22+
<p>If you have a major feature you'd like to contribute, please introduce yourself
23+
on the <a href="https://forum.djangoproject.com/c/internals/5">django-internals forum</a>
24+
so you can find a shepherd for your feature.</p>
25+
<p>Minor features and bug fixes will be merged as they are completed. If you
26+
have submitted a patch, ensure the flags on the Trac ticket are correct so it
27+
appears in the "Patches needing review" filter of the
28+
<a href="https://dashboard.djangoproject.com/">Django Development Dashboard</a>.
29+
Better yet, find someone to review your patch and mark the ticket as
30+
"Ready for checkin". Tickets marked "Ready for checkin" are regularly reviewed
31+
by mergers.</p>
32+
</section>
33+
34+
<section id="schedule">
35+
<h2>Schedule</h2>
36+
<p>
37+
Major milestones along the way to {{ series }} are scheduled below.
38+
See <a href="#process">Process</a> for more details. Dates are subject to change.
39+
</p>
40+
<div class="table-wrapper">
41+
<table>
42+
<thead>
43+
<tr>
44+
<th scope="col">Date</th>
45+
<th scope="col">Milestone</th>
46+
</tr>
47+
</thead>
48+
<tbody>
49+
<tr>
50+
<td>{{ releases.a.date|default:"TBD" }}</td>
51+
<td>Django {{ series }} alpha; feature freeze.</td>
52+
</tr>
53+
<tr>
54+
<td>{{ releases.b.date|default:"TBD" }}</td>
55+
<td>Django {{ series }} beta; non-release blocking bug fix freeze.</td>
56+
</tr>
57+
<tr>
58+
<td>{{ releases.c.date|default:"TBD" }}</td>
59+
<td>Django {{ series }} RC 1; translation string freeze.</td>
60+
</tr>
61+
<tr>
62+
<td>{{ releases.f.date|default:"TBD" }}</td>
63+
<td>Django {{ series }} final.</td>
64+
</tr>
65+
</tbody>
66+
</table>
67+
</div>
68+
</section>
69+
70+
<section id="process">
71+
<h2>Process</h2>
72+
<p>Any features not completed by the feature freeze date won't make it into {{ series }}.</p>
73+
<p>The release manager will keep the schedule updated and ensure efficient
74+
routing of issues and reminders for deadlines.</p>
75+
76+
<section id="feature-freeze-alpha-1">
77+
<h3>Feature freeze / Alpha 1</h3>
78+
<p>All major and minor features must be merged by the Alpha 1 deadline. Any
79+
features not done by this point will be deferred or dropped. At this time, we
80+
will fork <code>stable/{{ series }}.x</code> from <code>main</code>.</p>
81+
<p>After the alpha, non-release blocking bug fixes may be backported at the
82+
mergers' discretion.</p>
83+
</section>
84+
85+
<section id="beta-1">
86+
<h3>Beta 1</h3>
87+
<p>Beta 1 marks the end of changes that aren't release blocking bugs. Only release
88+
blocking bug fixes will be allowed to be backported after the beta.</p>
89+
</section>
90+
91+
<section id="rc-1">
92+
<h3>RC 1</h3>
93+
<p>If release blockers are still coming in at the planned release candidate date,
94+
we'll release beta 2 to encourage further testing. RC 1 marks the freeze for
95+
translation strings; translators will have two weeks to submit updates. Release
96+
blocking bug fixes may continue to be backported.</p>
97+
</section>
98+
99+
<section id="final">
100+
<h3>Final</h3>
101+
<p>Django {{ series }} final will ideally ship two weeks after the last RC. If no major bugs
102+
are found by then, {{ series }} final will be issued; otherwise, the timeline will be
103+
adjusted as needed.</p>
104+
</section>
105+
106+
<section id="how-to-help">
107+
<h3>How you can help</h3>
108+
<p>Community effort is key. You can help by:</p>
109+
<ul>
110+
<li>Reading the <a href="http://docs.djangoproject.com/en/dev/internals/contributing/">guide to contributing</a>
111+
and <a href="http://docs.djangoproject.com/en/dev/internals/release-process/">Django's release process</a>.</li>
112+
<li>Working on patches and <a href="https://docs.djangoproject.com/en/dev/internals/contributing/triaging-tickets/">triaging tickets</a>.</li>
113+
<li>Attending sprints.</li>
114+
<li>Testing release snapshots (alphas, betas) against your code and reporting bugs.</li>
115+
<li>Providing as many testers as possible to ensure a bug-free release.</li>
116+
</ul>
117+
</section>
118+
</section>
119+
120+
{% endblock %}

releases/tests.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from django.contrib import admin
55
from django.core.exceptions import ValidationError
66
from django.core.files.base import ContentFile
7+
from django.template.defaultfilters import date as datefilter
78
from django.test import SimpleTestCase, TestCase, override_settings
89
from django.urls import reverse
910
from django.utils.safestring import SafeString
@@ -567,3 +568,89 @@ def test_no_diamond_and_platinum_members(self):
567568
self.assertNotContains(response, member.display_name)
568569
self.assertNotContains(response, member.url)
569570
self.assertNotContains(response, member.description)
571+
572+
573+
class RoadmapViewTestCase(TestCase):
574+
575+
@classmethod
576+
def setUpTestData(cls):
577+
# Define release schedule for 5.2, 6.0, and 6.1 series.
578+
cls.release_schedule = {
579+
"5.2": [
580+
("a1", datetime.date(2025, 1, 15)),
581+
("b1", datetime.date(2025, 2, 19)),
582+
("rc1", datetime.date(2025, 3, 19)),
583+
("", datetime.date(2025, 4, 2)), # final
584+
],
585+
"6.0": [
586+
("a1", datetime.date(2025, 9, 17)),
587+
("b1", datetime.date(2025, 10, 22)),
588+
("rc1", datetime.date(2025, 11, 19)),
589+
("", datetime.date(2025, 12, 3)), # final
590+
],
591+
"6.1": [
592+
("a1", datetime.date(2026, 5, 20)),
593+
("b1", datetime.date(2026, 6, 24)),
594+
("rc1", datetime.date(2026, 7, 22)),
595+
("", datetime.date(2026, 8, 5)), # final
596+
],
597+
}
598+
for series, milestones in cls.release_schedule.items():
599+
for milestone, date in milestones:
600+
version = f"{series}{milestone}" if milestone else series
601+
Release.objects.create(
602+
version=version,
603+
is_active=True,
604+
date=date,
605+
is_lts=series.endswith(".2"),
606+
)
607+
608+
def test_roadmap_page_renders_series_title(self):
609+
for series in self.release_schedule.keys():
610+
url = reverse("roadmap", kwargs={"series": series})
611+
response = self.client.get(url)
612+
self.assertContains(response, f"Django {series} Roadmap", html=True)
613+
614+
def test_roadmap_page_contains_milestones(self):
615+
for series, releases in self.release_schedule.items():
616+
with self.subTest(series=series):
617+
url = reverse("roadmap", kwargs={"series": series})
618+
response = self.client.get(url)
619+
for detail, date in [
620+
(f"Django {series} alpha; feature freeze.", releases[0][1]),
621+
(
622+
f"Django {series} beta; non-release blocking bug fix freeze.",
623+
releases[1][1],
624+
),
625+
(
626+
f"Django {series} RC 1; translation string freeze.",
627+
releases[2][1],
628+
),
629+
(f"Django {series} final.", releases[3][1]),
630+
]:
631+
expected = f"<tr><td>{datefilter(date)}</td><td>{detail}</td></tr>"
632+
self.assertContains(response, expected, html=True)
633+
634+
def test_series_non_digits(self):
635+
for series in (0, "", "a.b", "2.2.0"):
636+
with self.subTest(series=series):
637+
response = self.client.get(f"/download/{series}/roadmap/")
638+
self.assertEqual(response.status_code, 404)
639+
640+
def test_major_lower_bound(self):
641+
for minor in (0, 1, 2, 3, 11):
642+
with self.subTest(minor=minor):
643+
response = self.client.get(f"/download/1.{minor}/roadmap/")
644+
self.assertEqual(response.status_code, 404)
645+
646+
def test_links_to_contributing_and_release_process_present(self):
647+
url = reverse("roadmap", kwargs={"series": "20.0"})
648+
response = self.client.get(url)
649+
self.assertContains(
650+
response,
651+
'href="http://docs.djangoproject.com/en/dev/internals/contributing/"',
652+
)
653+
self.assertContains(
654+
response,
655+
'href="http://docs.djangoproject.com/en/dev/internals/release-process/"',
656+
)

releases/urls.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
from django.urls import path, re_path
22

3-
from .views import index, redirect
3+
from .views import index, redirect, roadmap
44

55
urlpatterns = [
66
path("", index, name="download"),
77
re_path(
88
"^([0-9a-z_.-]+)/(tarball|wheel|checksum)/$", redirect, name="download-redirect"
99
),
10+
re_path(r"^(?P<series>\d{1,2}\.[0-2])/roadmap/$", roadmap, name="roadmap"),
1011
]

0 commit comments

Comments
 (0)