feat: add configurable link to external podcast (#384)

* feat: add configurable link to external podcast Add a podcast_url site parameter in hugo.toml (defaults to empty/disabled). When configured, a 'Podcast' link appears in the site footer navigation pointing to the external podcast platform page. - No audio hosting, embed, or player added - No RSS feed changes - Link opens in new tab with rel=noopener - BaseURL-safe (uses site.Params, not relative path) - Tests verify config presence, conditional rendering, and default state Closes #307 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: sanitize podcast_url with safeURL and tighten test assertions - Pipe podcast_url through Hugo's safeURL to prevent unsafe schemes - Tighten test to assert target/rel attributes on the Podcast link specifically rather than matching any link in the footer Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address podcast link review feedback Sanitize the podcast URL in the footer, add noreferrer to the external link, and tighten the footer test so it validates the Podcast anchor specifically. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Juan Manuel Servera committed Jun 11, 2026 at 23:39 UTC 4d9cc28e7a8df5ae9424d4b5fa4355e1f44e601a
3 files changed +42
hugo.toml
+4
@@ -34,6 +34,10 @@ rssLimit = 20
34 ShowAllPagesInArchive = true
35 mainSections = ['weekly', 'monthly', 'yearly']
36
37 + # External podcast link (leave empty to hide)
38 + # When set, a "Podcast" link appears in the site footer navigation.
39 + podcast_url = ""
40 +
41 [params.homeInfoParams]
42 Title = 'Weekly tech signal from GitHub'
43 Content = 'Claracle turns GitHub activity into readable weekly, monthly, and yearly trend reports for busy developers and tech leads.'
layouts/partials/footer.html
+3
@@ -9,6 +9,9 @@
9 <a href="https://github.com/jmservera/SquadScope" target="_blank" rel="noopener">GitHub</a>
10 <a href="{{ "index.xml" | absLangURL }}">RSS</a>
11 <a href="{{ "archive/" | absLangURL }}">Archive</a>
12 + {{- with site.Params.podcast_url }}
13 + <a href="{{ site.Params.podcast_url | trim | safeURL }}" target="_blank" rel="noopener noreferrer">Podcast</a>
14 + {{- end }}
15 </nav>
16 <p class="site-footer__meta">© 2026 jmservera · Generated 2026-05-25 · Powered by <a href="https://gohugo.io/?utm_source=squadscope" rel="noopener" target="_blank">Hugo</a></p>
17 </div>
tests/test_podcast_link.py new
+35
@@ -0,0 +1,35 @@
1 +"""Tests for the configurable external podcast link feature."""
2 +
3 +from __future__ import annotations
4 +
5 +from pathlib import Path
6 +import re
7 +
8 +REPO_ROOT = Path(__file__).resolve().parent.parent
9 +
10 +
11 +def test_podcast_url_param_exists_in_hugo_config() -> None:
12 + """hugo.toml must define podcast_url param (can be empty)."""
13 + config = (REPO_ROOT / "hugo.toml").read_text(encoding="utf-8")
14 + assert "podcast_url" in config
15 +
16 +
17 +def test_footer_conditionally_renders_podcast_link() -> None:
18 + """Footer template must conditionally render podcast link from site param."""
19 + footer = (REPO_ROOT / "layouts" / "partials" / "footer.html").read_text(encoding="utf-8")
20 + # Must use Hugo's `with` or `if` to conditionally render
21 + assert "site.Params.podcast_url" in footer
22 + # Must pipe through safeURL to prevent unsafe schemes
23 + assert "safeURL" in footer
24 + podcast_link_match = re.search(r'<a\s+href="{{\s*site\.Params\.podcast_url\s*\|\s*trim\s*\|\s*safeURL\s*}}"\s+([^>]*)>Podcast</a>', footer)
25 + assert podcast_link_match, "Footer must contain a Podcast link with a sanitized podcast_url href"
26 + podcast_link = podcast_link_match.group(1)
27 + assert 'target="_blank"' in podcast_link
28 + assert 'rel="noopener noreferrer"' in podcast_link
29 +
30 +
31 +def test_podcast_url_defaults_to_empty() -> None:
32 + """When podcast_url is empty, no link should render (Hugo `with` handles this)."""
33 + config = (REPO_ROOT / "hugo.toml").read_text(encoding="utf-8")
34 + # The default value must be empty string
35 + assert 'podcast_url = ""' in config