Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
fc2b513
feat: add per-library Slack channel URL with cppalliance fallback
julhoang Jul 6, 2026
93053c7
feat: wire up documentation card
julhoang Jul 6, 2026
e0d39b7
feat: version-aware all contributors with identity-based dedup
julhoang Jul 6, 2026
e3c4e7a
feat: ingest and parse optional meta/website.adoc into LibraryVersion
julhoang Jul 6, 2026
58210c6
feat: render website.adoc About/Install/Quick Start/Freeform with emp…
julhoang Jul 6, 2026
ea05c29
feat: render Designed For as a markdown card with V3 demo
julhoang Jul 6, 2026
3377d80
feat: wire up benchmark card
julhoang Jul 7, 2026
7192424
feat: wire up compiler explorer URL
julhoang Jul 8, 2026
f39e785
feat: store raw website.adoc source with admin section-status editing
julhoang Jul 8, 2026
98cc580
chore: remove caption and source from the benchmark section
julhoang Jul 8, 2026
47eaa0c
fix: fix cropped benchmark card
julhoang Jul 8, 2026
3e1f442
feat: skip website.adoc refresh during beta cycle
julhoang Jul 9, 2026
bd50f7c
refactor: build category tag links via Category.get_filter_url
julhoang Jul 9, 2026
52541b1
refactor: extract LibraryDetail v3 context helpers
julhoang Jul 10, 2026
679e0d8
fix: harden website.adoc fetch against timeouts and connection errors
julhoang Jul 10, 2026
fa09761
fix: guard godbolt version map against malformed API payload
julhoang Jul 10, 2026
5ac2177
fix: add default to library slack_url field
julhoang Jul 10, 2026
bc581c2
fix: count all releases in All Contributors for master/develop views
julhoang Jul 10, 2026
0289ac9
fix: preserve // lines inside code fences in freeform parser
julhoang Jul 10, 2026
e8c117d
chore: fix template wording and exception chaining nitpicks
julhoang Jul 10, 2026
2187471
chore: improve website demo file content
julhoang Jul 10, 2026
3d86f88
feat: implement autoflow masonry layout
julhoang Jul 10, 2026
26ececb
chore: renamed added_text template param to first_boost_version
julhoang Jul 10, 2026
4781434
style: improve card ordering on mobile view
julhoang Jul 10, 2026
e576330
style: add missing green theme to the benchmark card
julhoang Jul 10, 2026
1cdb5c6
feat: add placeholder for library subpage empty state
julhoang Jul 10, 2026
10d70d1
style: nit adjustment to card order
julhoang Jul 13, 2026
28804ed
chore: update fetch source ref from master to develop branch
julhoang Jul 13, 2026
c6eaf61
doc: add regex pattern examples
julhoang Jul 21, 2026
3b9143a
fix: ensure versions are sorted by numerical value instead of name
julhoang Jul 21, 2026
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
6 changes: 6 additions & 0 deletions config/celery.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ def setup_periodic_tasks(sender, **kwargs):
app.signature("libraries.tasks.update_library_version_dependencies"),
)

# Refresh parsed meta/website.adoc for the current release. Daily at 8:35 AM
sender.add_periodic_task(
crontab(hour=8, minute=35),
app.signature("libraries.tasks.update_library_version_website_adoc"),
)

# Clear the static content database cache. Executes daily at 4:05 AM.
sender.add_periodic_task(
crontab(hour=4, minute=5),
Expand Down
1 change: 1 addition & 0 deletions core/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class BadgeToken(StrEnum):


SLACK_URL = "https://cpplang.slack.com"
SLACK_JOIN_URL = "https://cppalliance.org/slack/" # URL to join the Slack workspace, not the Slack workspace itself
SLACK_MEMBER_COUNT = "24,000+"
STATIC_CONTENT_EARLY_EXIT_PATH_PREFIXES = ("releases/",)
# possible library versions are: boost_1_53_0_beta1, 1_82_0, 1_55_0b1
Expand Down
20 changes: 20 additions & 0 deletions core/githubhelper.py
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,26 @@ def get_libraries_json(self, repo_slug: str, tag: str = "master"):
else:
return response.json()

def get_website_adoc(self, repo_slug: str, tag: str = "develop"):
"""Retrieve a library's optional 'meta/website.adoc'.

Many libraries might not ship this file, so it will returns None quietly if not found.
See libraries/website_adoc.py for the parser and the file's contract.

:param repo_slug: str, the repository slugs
:param tag: str, the Git tag
:return: bytes, the file content, or None if it doesn't exist
"""
url = f"https://raw.githubusercontent.com/{self.owner}/{repo_slug}/{tag}/meta/website.adoc" # noqa

try:
response = requests.get(url, timeout=10)
response.raise_for_status()
except requests.exceptions.RequestException:
self.logger.info("website_adoc_not_found", repo=repo_slug, tag=tag)
return None
return response.content

def get_file_content(
self,
repo_slug: str = None,
Expand Down
50 changes: 42 additions & 8 deletions core/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
get_prioritized_version,
set_selected_boost_version,
modernize_boost_slug,
designed_for_html,
benchmark_sets,
)
from mailing_list import constants
from versions.models import Version, docs_path_to_boost_name
Expand Down Expand Up @@ -634,14 +636,7 @@ def _build_category_cards():
"description": category.short_description,
"badge_count": category.library_count,
"cta_label": "Start here",
"cta_href": reverse(
"libraries-list",
kwargs={
"version_slug": LATEST_RELEASE_URL_PATH_STR,
"library_view_str": "list",
"category_slug": category.slug,
},
),
"cta_href": category.get_filter_url(),
}
)
return cards
Expand Down Expand Up @@ -1941,6 +1936,45 @@ def get_context_data(self, **kwargs):
"action_url": "#",
},
]
context["designed_for_demo_html"] = designed_for_html(
[
{
"heading": "High-throughput parsing",
"description": "Handles millions of messages per second with "
"near-zero allocations on the hot path.",
},
{
"heading": "Header-only",
"description": "Add Boost to your include path — no separate "
"build step required.",
},
{
"heading": "Standards-tracking",
"description": "APIs mirror the C++ standard where applicable.",
},
]
)
context["benchmark_demo_sets"] = benchmark_sets(
[
{
"title": "Throughput",
"unit": "req/s",
"data": [
{"label": "Boost.Example", "value": 1200},
{"label": "Alternative A", "value": 800},
{"label": "Alternative B", "value": 450},
],
},
{
"title": "Latency",
"unit": "µs",
"data": [
{"label": "Boost.Example", "value": 12},
{"label": "Alternative A", "value": 30},
],
},
]
)
context["markdown_data"] = {
"title": "Markdown Block",
"markdown": dedent("""
Expand Down
48 changes: 48 additions & 0 deletions libraries/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from django.template.loader import render_to_string
from django.template.response import TemplateResponse
from django.urls import path, reverse
from django.utils.html import format_html, format_html_join
from django.utils.safestring import mark_safe
from django.shortcuts import redirect
from django.views.generic import TemplateView
Expand All @@ -21,6 +22,7 @@
from core.admin_filters import StaffUserCreatedByFilter
from config.celery import app
from libraries.forms import CreateReportForm, CreateReportFullForm
from libraries.website_adoc import build_website_adoc, website_adoc_section_statuses
from reports.generation import determine_versions
from versions.models import Version
from versions.tasks import import_all_library_versions
Expand Down Expand Up @@ -54,6 +56,7 @@
update_issues,
update_libraries,
update_library_version_documentation_urls_all_versions,
update_library_version_website_adoc,
)
from .utils import generate_release_report_filename

Expand Down Expand Up @@ -580,6 +583,40 @@ class LibraryVersionAdmin(admin.ModelAdmin):
search_fields = ["library__name", "version__name"]
change_list_template = "admin/libraryversion_change_list.html"
autocomplete_fields = ["authors", "maintainers", "dependencies"]
# website_adoc is derived from website_adoc_source on save (see save_model),
# so hide the parsed JSON; the section-status view shows what will render and
# admins can open the library page to see the result.
exclude = ["website_adoc"]
readonly_fields = ["website_adoc_sections"]

_SECTION_STATUS_DISPLAY = {
"rendered": ("✅", "#1a7f37", "shows on the page"),
"omitted": ("⚠️", "#b32d2d", "authored but won't show"),
"absent": ("⚪", "#888", "not in the file"),
}

@admin.display(description="website.adoc sections")
def website_adoc_sections(self, obj):
"""Per-section render status — rendered / authored-but-omitted / absent."""
rows = []
for section in website_adoc_section_statuses(obj.website_adoc):
icon, color, note = self._SECTION_STATUS_DISPLAY[section["status"]]
detail = f"{note} ({section['reason']})" if section["reason"] else note
rows.append((icon, color, section["label"], detail))
return format_html(
'<ul style="margin:0;list-style:none;padding:0;">{}</ul>',
format_html_join(
"",
"<li>{} <strong>{}</strong> "
'<span style="color:{};">— {}</span></li>',
((icon, label, color, detail) for icon, color, label, detail in rows),
),
)

def save_model(self, request, obj, form, change):
"""Re-derive the parsed website_adoc from the edited raw source."""
obj.website_adoc = build_website_adoc(obj.website_adoc_source)
super().save_model(request, obj, form, change)

def get_urls(self):
urls = super().get_urls()
Expand All @@ -589,6 +626,11 @@ def get_urls(self):
self.admin_site.admin_view(self.update_docs_urls),
name="update_docs_urls",
),
path(
"update_website_adoc/",
self.admin_site.admin_view(self.update_website_adoc),
name="update_website_adoc",
),
]
return my_urls + urls

Expand All @@ -603,6 +645,12 @@ def update_docs_urls(self, request):
)
return HttpResponseRedirect("../")

def update_website_adoc(self, request):
"""Run the task to refresh parsed meta/website.adoc content."""
update_library_version_website_adoc.delay()
self.message_user(request, "website.adoc content is being refreshed.")
return HttpResponseRedirect("../")


@admin.register(Issue)
class IssueAdmin(admin.ModelAdmin):
Expand Down
122 changes: 122 additions & 0 deletions libraries/godbolt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
"""Build Compiler Explorer (godbolt.org) "Edit in Compiler Explorer" links.

A library's website.adoc [#playground] code is encoded into a godbolt.org
``/clientstate/<base64>`` URL with Boost (matching the viewed release, or the
newest Compiler Explorer offers) pre-selected. Building the URL is a pure,
offline operation; only the Boost version→id lookup needs the CE libraries API,
which is cached.

Verified against Compiler Explorer:
- ClientState: {sessions:[{id,language,source,compilers:[{id,options,libs}]}]}
- libs entry: {"id": "boost", "version": "<version id>"} (e.g. "190")
- version ids come from /api/libraries/c++ -> boost.versions[].id
- the base64 is URL-safe (decoded server-side via Buffer.from(x, "base64"))
"""

import base64
import json

import requests
import structlog
from django.core.cache import caches

logger = structlog.get_logger()

GODBOLT_BASE_URL = "https://godbolt.org"
GODBOLT_LIBRARIES_URL = f"{GODBOLT_BASE_URL}/api/libraries/c++"
GODBOLT_CLIENTSTATE_URL = f"{GODBOLT_BASE_URL}/clientstate/"

# A current, widely-available gcc on Compiler Explorer. If it is ever retired
# the playground still opens with the code pre-filled — only the preselected
# compiler is lost — so this is safe to bump when convenient.
DEFAULT_COMPILER_ID = "g142" # gcc 14.2

_CACHE_ALIAS = "static_content"
_BOOST_VERSIONS_CACHE_KEY = "godbolt_boost_version_map"
_BOOST_VERSIONS_TTL = 60 * 60 * 24 # one day


def _fetch_boost_version_map():
"""Fetch {version_string: version_id} for Boost from the CE libraries API.

e.g. {"1.90.0": "190", ...}. Returns {} on any failure.
"""
try:
response = requests.get(
GODBOLT_LIBRARIES_URL,
headers={"Accept": "application/json"},
timeout=5,
)
response.raise_for_status()
libraries = response.json()
for library in libraries:
if library.get("id") == "boost":
return {
version["version"]: version["id"]
for version in library.get("versions", [])
if version.get("version") and version.get("id")
}
except (requests.RequestException, ValueError, AttributeError, TypeError, KeyError):
logger.exception("godbolt_libraries_fetch_failed")
return {}
return {}

Comment thread
coderabbitai[bot] marked this conversation as resolved.

def get_boost_version_map():
"""Return the cached Boost version→id map, fetching + caching on a miss."""
cache = caches[_CACHE_ALIAS]
version_map = cache.get(_BOOST_VERSIONS_CACHE_KEY)
if version_map is None:
version_map = _fetch_boost_version_map()
cache.set(_BOOST_VERSIONS_CACHE_KEY, version_map, _BOOST_VERSIONS_TTL)
return version_map


def _version_sort_key(version_string):
return [int(part) if part.isdigit() else 0 for part in version_string.split(".")]


def resolve_boost_version_id(boost_version, version_map=None):
"""Map a Boost release (e.g. "1.90.0") to a CE version id (e.g. "190").

Falls back to the newest version Compiler Explorer offers when there is no
exact match (CE lags new Boost releases). Returns None when CE exposes no
Boost versions.
"""
if version_map is None:
version_map = get_boost_version_map()
if not version_map:
return None
if boost_version in version_map:
return version_map[boost_version]
newest = max(version_map, key=_version_sort_key)
return version_map[newest]


def build_compiler_explorer_url(
code, boost_version, version_map=None, compiler_id=DEFAULT_COMPILER_ID
):
"""Build a godbolt.org /clientstate/ URL with `code` pre-filled and Boost
selected (matching `boost_version`, else the newest CE offers).

Returns None when there is no code to load. Boost is omitted from the
session (rather than blocking the link) if CE's version list is unavailable.
"""
if not code:
return None

version_id = resolve_boost_version_id(boost_version, version_map=version_map)
libs = [{"id": "boost", "version": version_id}] if version_id else []
client_state = {
"sessions": [
{
"id": 1,
"language": "c++",
"source": code,
"compilers": [{"id": compiler_id, "options": "", "libs": libs}],
}
]
}
payload = json.dumps(client_state, separators=(",", ":")).encode("utf-8")
encoded = base64.urlsafe_b64encode(payload).decode("ascii").rstrip("=")
return f"{GODBOLT_CLIENTSTATE_URL}{encoded}"
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import djclick as click

from django.conf import settings

from libraries.tasks import store_library_version_website_adoc
from versions.models import Version


@click.command()
@click.option(
"--release",
help="Boost version number (example: 1.81.0). A partial value processes all "
"matching versions (example: '--release=1.7' processes 1.70.0, 1.71.0, ...).",
)
@click.option(
"--new",
default=True,
type=click.BOOL,
help="True (default): process the most recent version only. False: process all "
"versions >= --min-version. Overridden by --release.",
)
@click.option(
"--min-version",
default=settings.MINIMUM_BOOST_VERSION,
help="Minimum Boost version to process (default: settings.MINIMUM_BOOST_VERSION).",
)
def command(release: str, new: bool, min_version: str):
"""Fetch, parse, and store each library's meta/website.adoc for the targeted
Boost versions.

The most recent version is fetched from `develop` (freshest maintainer content,
matching the daily task); older versions are fetched from their release tag (the
frozen snapshot, matching release import). A repo without the file is left
untouched.
"""
# Order/compare on the numeric version_array so boost-1.100.0 > boost-1.71.0
# (plain name ordering is lexicographic and breaks once minor/patch hits 100).
min_version_parts = [int(part) for part in min_version.split(".")]
version_qs = (
Version.objects.with_partials()
.active()
.with_version_split()
.filter(version_array__gte=min_version_parts)
)
most_recent = (
version_qs.filter(beta=False, full_release=True)
.order_by("-version_array")
.first()
)

if release:
versions = list(
version_qs.filter(name__icontains=release).order_by("-version_array")
)
elif new:
versions = [most_recent] if most_recent else []
else:
versions = list(version_qs.order_by("-version_array"))

for version in versions:
ref = "develop" if version == most_recent else version.name
click.secho(f"Processing {version.name} (ref={ref})...", fg="green")
store_library_version_website_adoc(version, ref=ref)

click.secho("Finished importing website.adoc content.", fg="green")
Loading
Loading