Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 38 additions & 24 deletions djangoproject/scss/_style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2529,14 +2529,48 @@ dl.data {
white-space: normal;
}

table {
th {
background: var(--sidebar-bg);
font-weight: bold;
text-align: left;
}

td {
border-bottom: 1px solid var(--hairline-color);
}

td,
th {
padding: 0.5em 1em;
}
}

table.foundation td {
border-bottom: 1px solid var(--hairline-color);
padding: 0 5px;
}

table.docutils td,
table.docutils th {
border-bottom: 1px solid var(--hairline-color);
table.django-supported-versions,
table.django-unsupported-versions {
border: 1px solid var(--hairline-color);
color: var(--table-color);

th,
td {
border-bottom: none;
padding: 5px;
text-align: center;
}
}

table.django-supported-versions th,
table.django-supported-versions tr {
background-color: var(--secondary-accent);
}

table.django-unsupported-versions th,
table.django-unsupported-versions tr {
background-color: var(--error-light);
}

.list-links {
Expand Down Expand Up @@ -3420,26 +3454,6 @@ form .footnote {
}
}

table.django-supported-versions,
table.django-unsupported-versions {
border: 1px solid black;
text-align: center;
color: var(--table-color);

th,
td {
padding: 5px;
}
}

table.django-supported-versions tr {
background-color: var(--secondary-accent);
}

table.django-unsupported-versions tr {
background-color: var(--error-light);
}

/* Corporate membership list page */

ul.corporate-members li {
Expand Down
40 changes: 20 additions & 20 deletions djangoproject/templates/releases/download.html
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,10 @@ <h2 id="supported-versions">Supported Versions</h2>
<th>End of extended support<sup><a href="#ft2">2</a></sup></th>
</tr>
<tr>
<td>5.2 LTS</td>
<td>{% get_latest_micro_release '5.2' %}</td>
<td>December 2025</td>
<td>April 2028</td>
<td>4.2 LTS</td>
<td>{% get_latest_micro_release '4.2' %}</td>
<td>December 4, 2023</td>
<td>April 2026</td>
</tr>
<tr>
<td>5.1</td>
Expand All @@ -97,10 +97,10 @@ <h2 id="supported-versions">Supported Versions</h2>
<td>December 2025</td>
</tr>
<tr>
<td>4.2 LTS</td>
<td>{% get_latest_micro_release '4.2' %}</td>
<td>December 4, 2023</td>
<td>April 2026</td>
<td>5.2 LTS</td>
<td>{% get_latest_micro_release '5.2' %}</td>
<td>December 2025</td>
<td>April 2028</td>
</tr>
</table>

Expand All @@ -114,28 +114,28 @@ <h2 id="future-versions">Future Roadmap</h2>
<th>End of extended support<sup><a href="#ft2">2</a></sup></th>
</tr>
<tr>
<td>7.0</td>
<td>December 2027</td>
<td>August 2028</td>
<td>April 2029</td>
<td><a href="{% url 'roadmap' '6.0' %}">6.0</a></td>
<td>December 2025</td>
<td>August 2026</td>
<td>April 2027</td>
</tr>
<tr>
<td>6.2 LTS</td>
<td><a href="{% url 'roadmap' '6.1' %}">6.1</a></td>
<td>August 2026</td>
<td>April 2027</td>
<td>December 2027</td>
<td>April 2030</td>
</tr>
<tr>
<td>6.1</td>
<td>August 2026</td>
<td><a href="{% url 'roadmap' '6.2' %}">6.2 LTS</a></td>
<td>April 2027</td>
<td>December 2027</td>
<td>April 2030</td>
</tr>
<tr>
<td>6.0</td>
<td>December 2025</td>
<td>August 2026</td>
<td>April 2027</td>
<td><a href="{% url 'roadmap' '7.0' %}">7.0</a></td>
<td>December 2027</td>
<td>August 2028</td>
<td>April 2029</td>
</tr>
</table>

Expand Down
120 changes: 120 additions & 0 deletions djangoproject/templates/releases/roadmap.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
{% extends "base.html" %}
{% load date_format %}

{% block sectionid %}roadmap{% endblock %}
{% block title %}Django {{ series }} Roadmap{% endblock %}
{% block layout_class %}sidebar-right{% endblock %}

{% block header %}
<p>Download</p>
{% endblock %}

{% block content %}
<h1>Django {{ series }} Roadmap</h1>
<p>This document details the schedule and roadmap towards Django {{ series }}.</p>

<section id="what-features-will-be-released">
<h2>What features will be in Django {{ series }}?</h2>
<p>Whatever gets committed by the alpha feature freeze!</p>
<p>Django {{ series }} will be a time-based release. Any features completed and committed
to main by the alpha feature freeze deadline noted below will be included. Any
that miss the deadline won't.</p>
<p>If you have a major feature you'd like to contribute, please introduce yourself
on the <a href="https://forum.djangoproject.com/c/internals/5">django-internals forum</a>
so you can find a shepherd for your feature.</p>
<p>Minor features and bug fixes will be merged as they are completed. If you
have submitted a patch, ensure the flags on the Trac ticket are correct so it
appears in the "Patches needing review" filter of the
<a href="https://dashboard.djangoproject.com/">Django Development Dashboard</a>.
Better yet, find someone to review your patch and mark the ticket as
"Ready for checkin". Tickets marked "Ready for checkin" are regularly reviewed
by mergers.</p>
</section>

<section id="schedule">
<h2>Schedule</h2>
<p>
Major milestones along the way to {{ series }} are scheduled below.
See <a href="#process">Process</a> for more details. Dates are subject to change.
</p>
<div class="table-wrapper">
<table>
<thead>
<tr>
<th scope="col">Date</th>
<th scope="col">Milestone</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{ releases.a.date|default:"TBD" }}</td>
<td>Django {{ series }} alpha; feature freeze.</td>
</tr>
<tr>
<td>{{ releases.b.date|default:"TBD" }}</td>
<td>Django {{ series }} beta; non-release blocking bug fix freeze.</td>
</tr>
<tr>
<td>{{ releases.c.date|default:"TBD" }}</td>
<td>Django {{ series }} RC 1; translation string freeze.</td>
</tr>
<tr>
<td>{{ releases.f.date|default:"TBD" }}</td>
<td>Django {{ series }} final.</td>
</tr>
</tbody>
</table>
</div>
</section>

<section id="process">
<h2>Process</h2>
<p>Any features not completed by the feature freeze date won't make it into {{ series }}.</p>
<p>The release manager will keep the schedule updated and ensure efficient
routing of issues and reminders for deadlines.</p>

<section id="feature-freeze-alpha-1">
<h3>Feature freeze / Alpha 1</h3>
<p>All major and minor features must be merged by the Alpha 1 deadline. Any
features not done by this point will be deferred or dropped. At this time, we
will fork <code>stable/{{ series }}.x</code> from <code>main</code>.</p>
<p>After the alpha, non-release blocking bug fixes may be backported at the
mergers' discretion.</p>
</section>

<section id="beta-1">
<h3>Beta 1</h3>
<p>Beta 1 marks the end of changes that aren't release blocking bugs. Only release
blocking bug fixes will be allowed to be backported after the beta.</p>
</section>

<section id="rc-1">
<h3>RC 1</h3>
<p>If release blockers are still coming in at the planned release candidate date,
we'll release beta 2 to encourage further testing. RC 1 marks the freeze for
translation strings; translators will have two weeks to submit updates. Release
blocking bug fixes may continue to be backported.</p>
</section>

<section id="final">
<h3>Final</h3>
<p>Django {{ series }} final will ideally ship two weeks after the last RC. If no major bugs
are found by then, {{ series }} final will be issued; otherwise, the timeline will be
adjusted as needed.</p>
</section>

<section id="how-to-help">
<h3>How you can help</h3>
<p>Community effort is key. You can help by:</p>
<ul>
<li>Reading the <a href="http://docs.djangoproject.com/en/dev/internals/contributing/">guide to contributing</a>
and <a href="http://docs.djangoproject.com/en/dev/internals/release-process/">Django's release process</a>.</li>
<li>Working on patches and <a href="https://docs.djangoproject.com/en/dev/internals/contributing/triaging-tickets/">triaging tickets</a>.</li>
<li>Attending sprints.</li>
<li>Testing release snapshots (alphas, betas) against your code and reporting bugs.</li>
<li>Providing as many testers as possible to ensure a bug-free release.</li>
</ul>
</section>
</section>

{% endblock %}
87 changes: 87 additions & 0 deletions releases/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from django.contrib import admin
from django.core.exceptions import ValidationError
from django.core.files.base import ContentFile
from django.template.defaultfilters import date as datefilter
from django.test import SimpleTestCase, TestCase, override_settings
from django.urls import reverse
from django.utils.safestring import SafeString
Expand Down Expand Up @@ -567,3 +568,89 @@ def test_no_diamond_and_platinum_members(self):
self.assertNotContains(response, member.display_name)
self.assertNotContains(response, member.url)
self.assertNotContains(response, member.description)


class RoadmapViewTestCase(TestCase):

@classmethod
def setUpTestData(cls):
# Define release schedule for 5.2, 6.0, and 6.1 series.
cls.release_schedule = {
"5.2": [
("a1", datetime.date(2025, 1, 15)),
("b1", datetime.date(2025, 2, 19)),
("rc1", datetime.date(2025, 3, 19)),
("", datetime.date(2025, 4, 2)), # final
],
"6.0": [
("a1", datetime.date(2025, 9, 17)),
("b1", datetime.date(2025, 10, 22)),
("rc1", datetime.date(2025, 11, 19)),
("", datetime.date(2025, 12, 3)), # final
],
"6.1": [
("a1", datetime.date(2026, 5, 20)),
("b1", datetime.date(2026, 6, 24)),
("rc1", datetime.date(2026, 7, 22)),
("", datetime.date(2026, 8, 5)), # final
],
}
for series, milestones in cls.release_schedule.items():
for milestone, date in milestones:
version = f"{series}{milestone}" if milestone else series
Release.objects.create(
version=version,
is_active=True,
date=date,
is_lts=series.endswith(".2"),
)

def test_roadmap_page_renders_series_title(self):
for series in self.release_schedule.keys():
url = reverse("roadmap", kwargs={"series": series})
response = self.client.get(url)
self.assertContains(response, f"Django {series} Roadmap", html=True)

def test_roadmap_page_contains_milestones(self):
for series, releases in self.release_schedule.items():
with self.subTest(series=series):
url = reverse("roadmap", kwargs={"series": series})
response = self.client.get(url)
for detail, date in [
(f"Django {series} alpha; feature freeze.", releases[0][1]),
(
f"Django {series} beta; non-release blocking bug fix freeze.",
releases[1][1],
),
(
f"Django {series} RC 1; translation string freeze.",
releases[2][1],
),
(f"Django {series} final.", releases[3][1]),
]:
expected = f"<tr><td>{datefilter(date)}</td><td>{detail}</td></tr>"
self.assertContains(response, expected, html=True)

def test_series_non_digits(self):
for series in (0, "", "a.b", "2.2.0"):
with self.subTest(series=series):
response = self.client.get(f"/download/{series}/roadmap/")
self.assertEqual(response.status_code, 404)

def test_major_lower_bound(self):
for minor in (0, 1, 2, 3, 11):
with self.subTest(minor=minor):
response = self.client.get(f"/download/1.{minor}/roadmap/")
self.assertEqual(response.status_code, 404)

def test_links_to_contributing_and_release_process_present(self):
url = reverse("roadmap", kwargs={"series": "20.0"})
response = self.client.get(url)
self.assertContains(
response,
'href="http://docs.djangoproject.com/en/dev/internals/contributing/"',
)
self.assertContains(
response,
'href="http://docs.djangoproject.com/en/dev/internals/release-process/"',
)
3 changes: 2 additions & 1 deletion releases/urls.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from django.urls import path, re_path

from .views import index, redirect
from .views import index, redirect, roadmap

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