fix(gh_trending): scrape github.com/trending for real trending data - #1

Merged
datj9 merged 2 commits into
masterfrom
fix/gh-trending-scrape
Jun 7, 2026
Merged

fix(gh_trending): scrape github.com/trending for real trending data#1
datj9 merged 2 commits into
masterfrom
fix/gh-trending-scrape

Conversation

@datj9

@datj9datj9 commented Jun 7, 2026

Copy link
Copy Markdown
Owner

Problem

The `gh_trending` Lambda never read github.com/trending. It queried the GitHub Search API for repos created in the last 7/30 days, sorted by all-time stars — a fundamentally different list from github.com/trending (which ranks by stars gained in a window). This is why the trending data on the live site looked wrong.

Two latent bugs rode along:

  • `_fetch_readme` referenced an undefined `HEADERS` → `NameError`, silently swallowed, so README context was never used.
  • The handler emitted a nested schema (`{generated_at, weekly:{period,repos}}`) that did not match what the Astro site reads. The site expects a flat `{updated_at, weekly[], monthly[]}` with a `stars_this_period` field — so the "+N this week" badges were always broken.
  • `email_digest` read the old nested shape (`data.get("weekly", {}).get("repos")`), which throws `AttributeError` against the real (flat) data — the Friday email's trending section was already broken on master.

Change

  • Scrape `https://github.com/trending?since=weekly|monthly\` (public HTML, no token required) via BeautifulSoup. Parse name / url / description / language / stars / stars_this_period per `article.Box-row`.
  • Emit the flat schema the site expects: `{updated_at, weekly[], monthly[]}` — fixes the broken period badges.
  • README enrichment is now best-effort: works unauthenticated, returns `""` on failure so summaries still build from the trending page's own name/description/language.
  • Fix `email_digest` to read the flat `weekly[]`.
  • Add `beautifulsoup4` dependency + parser unit tests with a fixture.

Cron schedule (weekly Mon 07:00 UTC) and all infra are unchanged. CDK bundling installs the new dependency automatically from `requirements.txt`.

Test plan

  • 7 parser unit tests pass (`pytest tests/test_gh_trending.py`)
  • ruff clean on changed files
  • Live smoke test: parsed 20 weekly + 19 monthly repos from the real page, correct stars/period/language, no token used
  • `email_digest._extract_top_repos` verified against flat data
  • infra TypeScript compiles (`tsc --noEmit`); other 3 Lambdas unaffected (no cross-imports)
  • Post-merge: CI `deploy.yml` rebuilds + deploys; verify `gh_trending` next run writes real trending data

Out of scope

The site has been stale since 2026-05-12 because SSM `/tech-bytes/github-token` is still `PLACEHOLDER`, so the auto-rebuild trigger no-ops. That's a separate ops fix (set a real PAT) and does not block this correctness fix.

The handler queried the GitHub Search API for recently-created repos
sorted by all-time stars, which is not actual trending data and never
matched github.com/trending. It also emitted a nested schema
({generated_at, weekly:{period,repos}}) that did not match what the
Astro site reads, and referenced an undefined HEADERS in the README
fetch (NameError, silently swallowed).
- Scrape https://github.com/trending?since=weekly|monthly (public HTML,
no token required) via BeautifulSoup; parse name/url/description/
language/stars/stars_this_period per Box-row article.
- Emit the flat schema the site expects: {updated_at, weekly[], monthly[]}
with stars_this_period, fixing the broken "+N this week" badges.
- README enrichment is now best-effort: works unauthenticated, returns
"" on failure so summaries still build from name/description/language.
- Fix email_digest reading the old nested weekly shape (AttributeError
on the new flat data — the Friday digest's trending section was broken).
- Add beautifulsoup4 dependency and parser unit tests with a fixture.
Cron schedule (weekly Mon 07:00 UTC) and infra are unchanged.
The preview.yml build-check ran `cdk synth`, which failed at
route53.HostedZone.fromLookup with StackAccountRegionNotSpecified:
the stack account came from CDK_DEFAULT_ACCOUNT, which is unset in CI
(no AWS creds), so the lookup key could not be formed.
- Fall back to the known deployment account (333674319299) when
CDK_DEFAULT_ACCOUNT is absent, so synth has an account/region.
- Commit cdk.context.json (un-ignore it) so the hosted-zone lookup is
served from cache and synth needs no live AWS call. This is the
CDK-recommended practice for reproducible synth.
Unblocks the PR build-check. Deploy (deploy.yml) is unaffected — it
already provides real credentials via OIDC.
@datj9
datj9 merged commit 4020077 into masterJun 7, 2026
2 checks passed
datj9 added a commit that referenced this pull request Jul 11, 2026
* fix(gh_trending): scrape github.com/trending for real trending data
The handler queried the GitHub Search API for recently-created repos
sorted by all-time stars, which is not actual trending data and never
matched github.com/trending. It also emitted a nested schema
({generated_at, weekly:{period,repos}}) that did not match what the
Astro site reads, and referenced an undefined HEADERS in the README
fetch (NameError, silently swallowed).
- Scrape https://github.com/trending?since=weekly|monthly (public HTML,
no token required) via BeautifulSoup; parse name/url/description/
language/stars/stars_this_period per Box-row article.
- Emit the flat schema the site expects: {updated_at, weekly[], monthly[]}
with stars_this_period, fixing the broken "+N this week" badges.
- README enrichment is now best-effort: works unauthenticated, returns
"" on failure so summaries still build from name/description/language.
- Fix email_digest reading the old nested weekly shape (AttributeError
on the new flat data — the Friday digest's trending section was broken).
- Add beautifulsoup4 dependency and parser unit tests with a fixture.
Cron schedule (weekly Mon 07:00 UTC) and infra are unchanged.
* fix(infra): make cdk synth work in CI without AWS credentials
The preview.yml build-check ran `cdk synth`, which failed at
route53.HostedZone.fromLookup with StackAccountRegionNotSpecified:
the stack account came from CDK_DEFAULT_ACCOUNT, which is unset in CI
(no AWS creds), so the lookup key could not be formed.
- Fall back to the known deployment account (333674319299) when
CDK_DEFAULT_ACCOUNT is absent, so synth has an account/region.
- Commit cdk.context.json (un-ignore it) so the hosted-zone lookup is
served from cache and synth needs no live AWS call. This is the
CDK-recommended practice for reproducible synth.
Unblocks the PR build-check. Deploy (deploy.yml) is unaffected — it
already provides real credentials via OIDC.
---------
Co-authored-by: Dat <dat.nguyen@ringkas.co.id>
@datj9
datj9 deleted the fix/gh-trending-scrape branch July 11, 2026 05:38
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@datj9@datnguyen-ringkas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(gh_trending): scrape github.com/trending for real trending data - #1

Merged
datj9 merged 2 commits into
masterfrom
fix/gh-trending-scrape
Jun 7, 2026
Merged

fix(gh_trending): scrape github.com/trending for real trending data#1
datj9 merged 2 commits into
masterfrom
fix/gh-trending-scrape

Conversation

@datj9

@datj9datj9 commented Jun 7, 2026

Copy link
Copy Markdown
Owner

Problem

The `gh_trending` Lambda never read github.com/trending. It queried the GitHub Search API for repos created in the last 7/30 days, sorted by all-time stars — a fundamentally different list from github.com/trending (which ranks by stars gained in a window). This is why the trending data on the live site looked wrong.

Two latent bugs rode along:

  • `_fetch_readme` referenced an undefined `HEADERS` → `NameError`, silently swallowed, so README context was never used.
  • The handler emitted a nested schema (`{generated_at, weekly:{period,repos}}`) that did not match what the Astro site reads. The site expects a flat `{updated_at, weekly[], monthly[]}` with a `stars_this_period` field — so the "+N this week" badges were always broken.
  • `email_digest` read the old nested shape (`data.get("weekly", {}).get("repos")`), which throws `AttributeError` against the real (flat) data — the Friday email's trending section was already broken on master.

Change

  • Scrape `https://github.com/trending?since=weekly|monthly\` (public HTML, no token required) via BeautifulSoup. Parse name / url / description / language / stars / stars_this_period per `article.Box-row`.
  • Emit the flat schema the site expects: `{updated_at, weekly[], monthly[]}` — fixes the broken period badges.
  • README enrichment is now best-effort: works unauthenticated, returns `""` on failure so summaries still build from the trending page's own name/description/language.
  • Fix `email_digest` to read the flat `weekly[]`.
  • Add `beautifulsoup4` dependency + parser unit tests with a fixture.

Cron schedule (weekly Mon 07:00 UTC) and all infra are unchanged. CDK bundling installs the new dependency automatically from `requirements.txt`.

Test plan

  • 7 parser unit tests pass (`pytest tests/test_gh_trending.py`)
  • ruff clean on changed files
  • Live smoke test: parsed 20 weekly + 19 monthly repos from the real page, correct stars/period/language, no token used
  • `email_digest._extract_top_repos` verified against flat data
  • infra TypeScript compiles (`tsc --noEmit`); other 3 Lambdas unaffected (no cross-imports)
  • Post-merge: CI `deploy.yml` rebuilds + deploys; verify `gh_trending` next run writes real trending data

Out of scope

The site has been stale since 2026-05-12 because SSM `/tech-bytes/github-token` is still `PLACEHOLDER`, so the auto-rebuild trigger no-ops. That's a separate ops fix (set a real PAT) and does not block this correctness fix.

The handler queried the GitHub Search API for recently-created repos
sorted by all-time stars, which is not actual trending data and never
matched github.com/trending. It also emitted a nested schema
({generated_at, weekly:{period,repos}}) that did not match what the
Astro site reads, and referenced an undefined HEADERS in the README
fetch (NameError, silently swallowed).
- Scrape https://github.com/trending?since=weekly|monthly (public HTML,
no token required) via BeautifulSoup; parse name/url/description/
language/stars/stars_this_period per Box-row article.
- Emit the flat schema the site expects: {updated_at, weekly[], monthly[]}
with stars_this_period, fixing the broken "+N this week" badges.
- README enrichment is now best-effort: works unauthenticated, returns
"" on failure so summaries still build from name/description/language.
- Fix email_digest reading the old nested weekly shape (AttributeError
on the new flat data — the Friday digest's trending section was broken).
- Add beautifulsoup4 dependency and parser unit tests with a fixture.
Cron schedule (weekly Mon 07:00 UTC) and infra are unchanged.
The preview.yml build-check ran `cdk synth`, which failed at
route53.HostedZone.fromLookup with StackAccountRegionNotSpecified:
the stack account came from CDK_DEFAULT_ACCOUNT, which is unset in CI
(no AWS creds), so the lookup key could not be formed.
- Fall back to the known deployment account (333674319299) when
CDK_DEFAULT_ACCOUNT is absent, so synth has an account/region.
- Commit cdk.context.json (un-ignore it) so the hosted-zone lookup is
served from cache and synth needs no live AWS call. This is the
CDK-recommended practice for reproducible synth.
Unblocks the PR build-check. Deploy (deploy.yml) is unaffected — it
already provides real credentials via OIDC.
@datj9
datj9 merged commit 4020077 into masterJun 7, 2026
2 checks passed
datj9 added a commit that referenced this pull request Jul 11, 2026
* fix(gh_trending): scrape github.com/trending for real trending data
The handler queried the GitHub Search API for recently-created repos
sorted by all-time stars, which is not actual trending data and never
matched github.com/trending. It also emitted a nested schema
({generated_at, weekly:{period,repos}}) that did not match what the
Astro site reads, and referenced an undefined HEADERS in the README
fetch (NameError, silently swallowed).
- Scrape https://github.com/trending?since=weekly|monthly (public HTML,
no token required) via BeautifulSoup; parse name/url/description/
language/stars/stars_this_period per Box-row article.
- Emit the flat schema the site expects: {updated_at, weekly[], monthly[]}
with stars_this_period, fixing the broken "+N this week" badges.
- README enrichment is now best-effort: works unauthenticated, returns
"" on failure so summaries still build from name/description/language.
- Fix email_digest reading the old nested weekly shape (AttributeError
on the new flat data — the Friday digest's trending section was broken).
- Add beautifulsoup4 dependency and parser unit tests with a fixture.
Cron schedule (weekly Mon 07:00 UTC) and infra are unchanged.
* fix(infra): make cdk synth work in CI without AWS credentials
The preview.yml build-check ran `cdk synth`, which failed at
route53.HostedZone.fromLookup with StackAccountRegionNotSpecified:
the stack account came from CDK_DEFAULT_ACCOUNT, which is unset in CI
(no AWS creds), so the lookup key could not be formed.
- Fall back to the known deployment account (333674319299) when
CDK_DEFAULT_ACCOUNT is absent, so synth has an account/region.
- Commit cdk.context.json (un-ignore it) so the hosted-zone lookup is
served from cache and synth needs no live AWS call. This is the
CDK-recommended practice for reproducible synth.
Unblocks the PR build-check. Deploy (deploy.yml) is unaffected — it
already provides real credentials via OIDC.
---------
Co-authored-by: Dat <dat.nguyen@ringkas.co.id>
@datj9
datj9 deleted the fix/gh-trending-scrape branch July 11, 2026 05:38
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@datj9@datnguyen-ringkas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(gh_trending): scrape github.com/trending for real trending data - #1

Merged
datj9 merged 2 commits into
masterfrom
fix/gh-trending-scrape
Jun 7, 2026
Merged

fix(gh_trending): scrape github.com/trending for real trending data#1
datj9 merged 2 commits into
masterfrom
fix/gh-trending-scrape

Conversation

@datj9

@datj9datj9 commented Jun 7, 2026

Copy link
Copy Markdown
Owner

Problem

The `gh_trending` Lambda never read github.com/trending. It queried the GitHub Search API for repos created in the last 7/30 days, sorted by all-time stars — a fundamentally different list from github.com/trending (which ranks by stars gained in a window). This is why the trending data on the live site looked wrong.

Two latent bugs rode along:

  • `_fetch_readme` referenced an undefined `HEADERS` → `NameError`, silently swallowed, so README context was never used.
  • The handler emitted a nested schema (`{generated_at, weekly:{period,repos}}`) that did not match what the Astro site reads. The site expects a flat `{updated_at, weekly[], monthly[]}` with a `stars_this_period` field — so the "+N this week" badges were always broken.
  • `email_digest` read the old nested shape (`data.get("weekly", {}).get("repos")`), which throws `AttributeError` against the real (flat) data — the Friday email's trending section was already broken on master.

Change

  • Scrape `https://github.com/trending?since=weekly|monthly\` (public HTML, no token required) via BeautifulSoup. Parse name / url / description / language / stars / stars_this_period per `article.Box-row`.
  • Emit the flat schema the site expects: `{updated_at, weekly[], monthly[]}` — fixes the broken period badges.
  • README enrichment is now best-effort: works unauthenticated, returns `""` on failure so summaries still build from the trending page's own name/description/language.
  • Fix `email_digest` to read the flat `weekly[]`.
  • Add `beautifulsoup4` dependency + parser unit tests with a fixture.

Cron schedule (weekly Mon 07:00 UTC) and all infra are unchanged. CDK bundling installs the new dependency automatically from `requirements.txt`.

Test plan

  • 7 parser unit tests pass (`pytest tests/test_gh_trending.py`)
  • ruff clean on changed files
  • Live smoke test: parsed 20 weekly + 19 monthly repos from the real page, correct stars/period/language, no token used
  • `email_digest._extract_top_repos` verified against flat data
  • infra TypeScript compiles (`tsc --noEmit`); other 3 Lambdas unaffected (no cross-imports)
  • Post-merge: CI `deploy.yml` rebuilds + deploys; verify `gh_trending` next run writes real trending data

Out of scope

The site has been stale since 2026-05-12 because SSM `/tech-bytes/github-token` is still `PLACEHOLDER`, so the auto-rebuild trigger no-ops. That's a separate ops fix (set a real PAT) and does not block this correctness fix.

The handler queried the GitHub Search API for recently-created repos
sorted by all-time stars, which is not actual trending data and never
matched github.com/trending. It also emitted a nested schema
({generated_at, weekly:{period,repos}}) that did not match what the
Astro site reads, and referenced an undefined HEADERS in the README
fetch (NameError, silently swallowed).
- Scrape https://github.com/trending?since=weekly|monthly (public HTML,
no token required) via BeautifulSoup; parse name/url/description/
language/stars/stars_this_period per Box-row article.
- Emit the flat schema the site expects: {updated_at, weekly[], monthly[]}
with stars_this_period, fixing the broken "+N this week" badges.
- README enrichment is now best-effort: works unauthenticated, returns
"" on failure so summaries still build from name/description/language.
- Fix email_digest reading the old nested weekly shape (AttributeError
on the new flat data — the Friday digest's trending section was broken).
- Add beautifulsoup4 dependency and parser unit tests with a fixture.
Cron schedule (weekly Mon 07:00 UTC) and infra are unchanged.
The preview.yml build-check ran `cdk synth`, which failed at
route53.HostedZone.fromLookup with StackAccountRegionNotSpecified:
the stack account came from CDK_DEFAULT_ACCOUNT, which is unset in CI
(no AWS creds), so the lookup key could not be formed.
- Fall back to the known deployment account (333674319299) when
CDK_DEFAULT_ACCOUNT is absent, so synth has an account/region.
- Commit cdk.context.json (un-ignore it) so the hosted-zone lookup is
served from cache and synth needs no live AWS call. This is the
CDK-recommended practice for reproducible synth.
Unblocks the PR build-check. Deploy (deploy.yml) is unaffected — it
already provides real credentials via OIDC.
@datj9
datj9 merged commit 4020077 into masterJun 7, 2026
2 checks passed
datj9 added a commit that referenced this pull request Jul 11, 2026
* fix(gh_trending): scrape github.com/trending for real trending data
The handler queried the GitHub Search API for recently-created repos
sorted by all-time stars, which is not actual trending data and never
matched github.com/trending. It also emitted a nested schema
({generated_at, weekly:{period,repos}}) that did not match what the
Astro site reads, and referenced an undefined HEADERS in the README
fetch (NameError, silently swallowed).
- Scrape https://github.com/trending?since=weekly|monthly (public HTML,
no token required) via BeautifulSoup; parse name/url/description/
language/stars/stars_this_period per Box-row article.
- Emit the flat schema the site expects: {updated_at, weekly[], monthly[]}
with stars_this_period, fixing the broken "+N this week" badges.
- README enrichment is now best-effort: works unauthenticated, returns
"" on failure so summaries still build from name/description/language.
- Fix email_digest reading the old nested weekly shape (AttributeError
on the new flat data — the Friday digest's trending section was broken).
- Add beautifulsoup4 dependency and parser unit tests with a fixture.
Cron schedule (weekly Mon 07:00 UTC) and infra are unchanged.
* fix(infra): make cdk synth work in CI without AWS credentials
The preview.yml build-check ran `cdk synth`, which failed at
route53.HostedZone.fromLookup with StackAccountRegionNotSpecified:
the stack account came from CDK_DEFAULT_ACCOUNT, which is unset in CI
(no AWS creds), so the lookup key could not be formed.
- Fall back to the known deployment account (333674319299) when
CDK_DEFAULT_ACCOUNT is absent, so synth has an account/region.
- Commit cdk.context.json (un-ignore it) so the hosted-zone lookup is
served from cache and synth needs no live AWS call. This is the
CDK-recommended practice for reproducible synth.
Unblocks the PR build-check. Deploy (deploy.yml) is unaffected — it
already provides real credentials via OIDC.
---------
Co-authored-by: Dat <dat.nguyen@ringkas.co.id>
@datj9
datj9 deleted the fix/gh-trending-scrape branch July 11, 2026 05:38
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@datj9@datnguyen-ringkas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(gh_trending): scrape github.com/trending for real trending data - #1

Merged
datj9 merged 2 commits into
masterfrom
fix/gh-trending-scrape
Jun 7, 2026
Merged

fix(gh_trending): scrape github.com/trending for real trending data#1
datj9 merged 2 commits into
masterfrom
fix/gh-trending-scrape

Conversation

@datj9

@datj9datj9 commented Jun 7, 2026

Copy link
Copy Markdown
Owner

Problem

The `gh_trending` Lambda never read github.com/trending. It queried the GitHub Search API for repos created in the last 7/30 days, sorted by all-time stars — a fundamentally different list from github.com/trending (which ranks by stars gained in a window). This is why the trending data on the live site looked wrong.

Two latent bugs rode along:

  • `_fetch_readme` referenced an undefined `HEADERS` → `NameError`, silently swallowed, so README context was never used.
  • The handler emitted a nested schema (`{generated_at, weekly:{period,repos}}`) that did not match what the Astro site reads. The site expects a flat `{updated_at, weekly[], monthly[]}` with a `stars_this_period` field — so the "+N this week" badges were always broken.
  • `email_digest` read the old nested shape (`data.get("weekly", {}).get("repos")`), which throws `AttributeError` against the real (flat) data — the Friday email's trending section was already broken on master.

Change

  • Scrape `https://github.com/trending?since=weekly|monthly\` (public HTML, no token required) via BeautifulSoup. Parse name / url / description / language / stars / stars_this_period per `article.Box-row`.
  • Emit the flat schema the site expects: `{updated_at, weekly[], monthly[]}` — fixes the broken period badges.
  • README enrichment is now best-effort: works unauthenticated, returns `""` on failure so summaries still build from the trending page's own name/description/language.
  • Fix `email_digest` to read the flat `weekly[]`.
  • Add `beautifulsoup4` dependency + parser unit tests with a fixture.

Cron schedule (weekly Mon 07:00 UTC) and all infra are unchanged. CDK bundling installs the new dependency automatically from `requirements.txt`.

Test plan

  • 7 parser unit tests pass (`pytest tests/test_gh_trending.py`)
  • ruff clean on changed files
  • Live smoke test: parsed 20 weekly + 19 monthly repos from the real page, correct stars/period/language, no token used
  • `email_digest._extract_top_repos` verified against flat data
  • infra TypeScript compiles (`tsc --noEmit`); other 3 Lambdas unaffected (no cross-imports)
  • Post-merge: CI `deploy.yml` rebuilds + deploys; verify `gh_trending` next run writes real trending data

Out of scope

The site has been stale since 2026-05-12 because SSM `/tech-bytes/github-token` is still `PLACEHOLDER`, so the auto-rebuild trigger no-ops. That's a separate ops fix (set a real PAT) and does not block this correctness fix.

The handler queried the GitHub Search API for recently-created repos
sorted by all-time stars, which is not actual trending data and never
matched github.com/trending. It also emitted a nested schema
({generated_at, weekly:{period,repos}}) that did not match what the
Astro site reads, and referenced an undefined HEADERS in the README
fetch (NameError, silently swallowed).
- Scrape https://github.com/trending?since=weekly|monthly (public HTML,
no token required) via BeautifulSoup; parse name/url/description/
language/stars/stars_this_period per Box-row article.
- Emit the flat schema the site expects: {updated_at, weekly[], monthly[]}
with stars_this_period, fixing the broken "+N this week" badges.
- README enrichment is now best-effort: works unauthenticated, returns
"" on failure so summaries still build from name/description/language.
- Fix email_digest reading the old nested weekly shape (AttributeError
on the new flat data — the Friday digest's trending section was broken).
- Add beautifulsoup4 dependency and parser unit tests with a fixture.
Cron schedule (weekly Mon 07:00 UTC) and infra are unchanged.
The preview.yml build-check ran `cdk synth`, which failed at
route53.HostedZone.fromLookup with StackAccountRegionNotSpecified:
the stack account came from CDK_DEFAULT_ACCOUNT, which is unset in CI
(no AWS creds), so the lookup key could not be formed.
- Fall back to the known deployment account (333674319299) when
CDK_DEFAULT_ACCOUNT is absent, so synth has an account/region.
- Commit cdk.context.json (un-ignore it) so the hosted-zone lookup is
served from cache and synth needs no live AWS call. This is the
CDK-recommended practice for reproducible synth.
Unblocks the PR build-check. Deploy (deploy.yml) is unaffected — it
already provides real credentials via OIDC.
@datj9
datj9 merged commit 4020077 into masterJun 7, 2026
2 checks passed
datj9 added a commit that referenced this pull request Jul 11, 2026
* fix(gh_trending): scrape github.com/trending for real trending data
The handler queried the GitHub Search API for recently-created repos
sorted by all-time stars, which is not actual trending data and never
matched github.com/trending. It also emitted a nested schema
({generated_at, weekly:{period,repos}}) that did not match what the
Astro site reads, and referenced an undefined HEADERS in the README
fetch (NameError, silently swallowed).
- Scrape https://github.com/trending?since=weekly|monthly (public HTML,
no token required) via BeautifulSoup; parse name/url/description/
language/stars/stars_this_period per Box-row article.
- Emit the flat schema the site expects: {updated_at, weekly[], monthly[]}
with stars_this_period, fixing the broken "+N this week" badges.
- README enrichment is now best-effort: works unauthenticated, returns
"" on failure so summaries still build from name/description/language.
- Fix email_digest reading the old nested weekly shape (AttributeError
on the new flat data — the Friday digest's trending section was broken).
- Add beautifulsoup4 dependency and parser unit tests with a fixture.
Cron schedule (weekly Mon 07:00 UTC) and infra are unchanged.
* fix(infra): make cdk synth work in CI without AWS credentials
The preview.yml build-check ran `cdk synth`, which failed at
route53.HostedZone.fromLookup with StackAccountRegionNotSpecified:
the stack account came from CDK_DEFAULT_ACCOUNT, which is unset in CI
(no AWS creds), so the lookup key could not be formed.
- Fall back to the known deployment account (333674319299) when
CDK_DEFAULT_ACCOUNT is absent, so synth has an account/region.
- Commit cdk.context.json (un-ignore it) so the hosted-zone lookup is
served from cache and synth needs no live AWS call. This is the
CDK-recommended practice for reproducible synth.
Unblocks the PR build-check. Deploy (deploy.yml) is unaffected — it
already provides real credentials via OIDC.
---------
Co-authored-by: Dat <dat.nguyen@ringkas.co.id>
@datj9
datj9 deleted the fix/gh-trending-scrape branch July 11, 2026 05:38
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@datj9@datnguyen-ringkas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(gh_trending): scrape github.com/trending for real trending data - #1

Merged
datj9 merged 2 commits into
masterfrom
fix/gh-trending-scrape
Jun 7, 2026
Merged

fix(gh_trending): scrape github.com/trending for real trending data#1
datj9 merged 2 commits into
masterfrom
fix/gh-trending-scrape

Conversation

@datj9

@datj9datj9 commented Jun 7, 2026

Copy link
Copy Markdown
Owner

Problem

The `gh_trending` Lambda never read github.com/trending. It queried the GitHub Search API for repos created in the last 7/30 days, sorted by all-time stars — a fundamentally different list from github.com/trending (which ranks by stars gained in a window). This is why the trending data on the live site looked wrong.

Two latent bugs rode along:

  • `_fetch_readme` referenced an undefined `HEADERS` → `NameError`, silently swallowed, so README context was never used.
  • The handler emitted a nested schema (`{generated_at, weekly:{period,repos}}`) that did not match what the Astro site reads. The site expects a flat `{updated_at, weekly[], monthly[]}` with a `stars_this_period` field — so the "+N this week" badges were always broken.
  • `email_digest` read the old nested shape (`data.get("weekly", {}).get("repos")`), which throws `AttributeError` against the real (flat) data — the Friday email's trending section was already broken on master.

Change

  • Scrape `https://github.com/trending?since=weekly|monthly\` (public HTML, no token required) via BeautifulSoup. Parse name / url / description / language / stars / stars_this_period per `article.Box-row`.
  • Emit the flat schema the site expects: `{updated_at, weekly[], monthly[]}` — fixes the broken period badges.
  • README enrichment is now best-effort: works unauthenticated, returns `""` on failure so summaries still build from the trending page's own name/description/language.
  • Fix `email_digest` to read the flat `weekly[]`.
  • Add `beautifulsoup4` dependency + parser unit tests with a fixture.

Cron schedule (weekly Mon 07:00 UTC) and all infra are unchanged. CDK bundling installs the new dependency automatically from `requirements.txt`.

Test plan

  • 7 parser unit tests pass (`pytest tests/test_gh_trending.py`)
  • ruff clean on changed files
  • Live smoke test: parsed 20 weekly + 19 monthly repos from the real page, correct stars/period/language, no token used
  • `email_digest._extract_top_repos` verified against flat data
  • infra TypeScript compiles (`tsc --noEmit`); other 3 Lambdas unaffected (no cross-imports)
  • Post-merge: CI `deploy.yml` rebuilds + deploys; verify `gh_trending` next run writes real trending data

Out of scope

The site has been stale since 2026-05-12 because SSM `/tech-bytes/github-token` is still `PLACEHOLDER`, so the auto-rebuild trigger no-ops. That's a separate ops fix (set a real PAT) and does not block this correctness fix.

The handler queried the GitHub Search API for recently-created repos
sorted by all-time stars, which is not actual trending data and never
matched github.com/trending. It also emitted a nested schema
({generated_at, weekly:{period,repos}}) that did not match what the
Astro site reads, and referenced an undefined HEADERS in the README
fetch (NameError, silently swallowed).
- Scrape https://github.com/trending?since=weekly|monthly (public HTML,
no token required) via BeautifulSoup; parse name/url/description/
language/stars/stars_this_period per Box-row article.
- Emit the flat schema the site expects: {updated_at, weekly[], monthly[]}
with stars_this_period, fixing the broken "+N this week" badges.
- README enrichment is now best-effort: works unauthenticated, returns
"" on failure so summaries still build from name/description/language.
- Fix email_digest reading the old nested weekly shape (AttributeError
on the new flat data — the Friday digest's trending section was broken).
- Add beautifulsoup4 dependency and parser unit tests with a fixture.
Cron schedule (weekly Mon 07:00 UTC) and infra are unchanged.
The preview.yml build-check ran `cdk synth`, which failed at
route53.HostedZone.fromLookup with StackAccountRegionNotSpecified:
the stack account came from CDK_DEFAULT_ACCOUNT, which is unset in CI
(no AWS creds), so the lookup key could not be formed.
- Fall back to the known deployment account (333674319299) when
CDK_DEFAULT_ACCOUNT is absent, so synth has an account/region.
- Commit cdk.context.json (un-ignore it) so the hosted-zone lookup is
served from cache and synth needs no live AWS call. This is the
CDK-recommended practice for reproducible synth.
Unblocks the PR build-check. Deploy (deploy.yml) is unaffected — it
already provides real credentials via OIDC.
@datj9
datj9 merged commit 4020077 into masterJun 7, 2026
2 checks passed
datj9 added a commit that referenced this pull request Jul 11, 2026
* fix(gh_trending): scrape github.com/trending for real trending data
The handler queried the GitHub Search API for recently-created repos
sorted by all-time stars, which is not actual trending data and never
matched github.com/trending. It also emitted a nested schema
({generated_at, weekly:{period,repos}}) that did not match what the
Astro site reads, and referenced an undefined HEADERS in the README
fetch (NameError, silently swallowed).
- Scrape https://github.com/trending?since=weekly|monthly (public HTML,
no token required) via BeautifulSoup; parse name/url/description/
language/stars/stars_this_period per Box-row article.
- Emit the flat schema the site expects: {updated_at, weekly[], monthly[]}
with stars_this_period, fixing the broken "+N this week" badges.
- README enrichment is now best-effort: works unauthenticated, returns
"" on failure so summaries still build from name/description/language.
- Fix email_digest reading the old nested weekly shape (AttributeError
on the new flat data — the Friday digest's trending section was broken).
- Add beautifulsoup4 dependency and parser unit tests with a fixture.
Cron schedule (weekly Mon 07:00 UTC) and infra are unchanged.
* fix(infra): make cdk synth work in CI without AWS credentials
The preview.yml build-check ran `cdk synth`, which failed at
route53.HostedZone.fromLookup with StackAccountRegionNotSpecified:
the stack account came from CDK_DEFAULT_ACCOUNT, which is unset in CI
(no AWS creds), so the lookup key could not be formed.
- Fall back to the known deployment account (333674319299) when
CDK_DEFAULT_ACCOUNT is absent, so synth has an account/region.
- Commit cdk.context.json (un-ignore it) so the hosted-zone lookup is
served from cache and synth needs no live AWS call. This is the
CDK-recommended practice for reproducible synth.
Unblocks the PR build-check. Deploy (deploy.yml) is unaffected — it
already provides real credentials via OIDC.
---------
Co-authored-by: Dat <dat.nguyen@ringkas.co.id>
@datj9
datj9 deleted the fix/gh-trending-scrape branch July 11, 2026 05:38
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@datj9@datnguyen-ringkas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(gh_trending): scrape github.com/trending for real trending data - #1

Merged
datj9 merged 2 commits into
masterfrom
fix/gh-trending-scrape
Jun 7, 2026
Merged

fix(gh_trending): scrape github.com/trending for real trending data#1
datj9 merged 2 commits into
masterfrom
fix/gh-trending-scrape

Conversation

@datj9

@datj9datj9 commented Jun 7, 2026

Copy link
Copy Markdown
Owner

Problem

The `gh_trending` Lambda never read github.com/trending. It queried the GitHub Search API for repos created in the last 7/30 days, sorted by all-time stars — a fundamentally different list from github.com/trending (which ranks by stars gained in a window). This is why the trending data on the live site looked wrong.

Two latent bugs rode along:

  • `_fetch_readme` referenced an undefined `HEADERS` → `NameError`, silently swallowed, so README context was never used.
  • The handler emitted a nested schema (`{generated_at, weekly:{period,repos}}`) that did not match what the Astro site reads. The site expects a flat `{updated_at, weekly[], monthly[]}` with a `stars_this_period` field — so the "+N this week" badges were always broken.
  • `email_digest` read the old nested shape (`data.get("weekly", {}).get("repos")`), which throws `AttributeError` against the real (flat) data — the Friday email's trending section was already broken on master.

Change

  • Scrape `https://github.com/trending?since=weekly|monthly\` (public HTML, no token required) via BeautifulSoup. Parse name / url / description / language / stars / stars_this_period per `article.Box-row`.
  • Emit the flat schema the site expects: `{updated_at, weekly[], monthly[]}` — fixes the broken period badges.
  • README enrichment is now best-effort: works unauthenticated, returns `""` on failure so summaries still build from the trending page's own name/description/language.
  • Fix `email_digest` to read the flat `weekly[]`.
  • Add `beautifulsoup4` dependency + parser unit tests with a fixture.

Cron schedule (weekly Mon 07:00 UTC) and all infra are unchanged. CDK bundling installs the new dependency automatically from `requirements.txt`.

Test plan

  • 7 parser unit tests pass (`pytest tests/test_gh_trending.py`)
  • ruff clean on changed files
  • Live smoke test: parsed 20 weekly + 19 monthly repos from the real page, correct stars/period/language, no token used
  • `email_digest._extract_top_repos` verified against flat data
  • infra TypeScript compiles (`tsc --noEmit`); other 3 Lambdas unaffected (no cross-imports)
  • Post-merge: CI `deploy.yml` rebuilds + deploys; verify `gh_trending` next run writes real trending data

Out of scope

The site has been stale since 2026-05-12 because SSM `/tech-bytes/github-token` is still `PLACEHOLDER`, so the auto-rebuild trigger no-ops. That's a separate ops fix (set a real PAT) and does not block this correctness fix.

The handler queried the GitHub Search API for recently-created repos
sorted by all-time stars, which is not actual trending data and never
matched github.com/trending. It also emitted a nested schema
({generated_at, weekly:{period,repos}}) that did not match what the
Astro site reads, and referenced an undefined HEADERS in the README
fetch (NameError, silently swallowed).
- Scrape https://github.com/trending?since=weekly|monthly (public HTML,
no token required) via BeautifulSoup; parse name/url/description/
language/stars/stars_this_period per Box-row article.
- Emit the flat schema the site expects: {updated_at, weekly[], monthly[]}
with stars_this_period, fixing the broken "+N this week" badges.
- README enrichment is now best-effort: works unauthenticated, returns
"" on failure so summaries still build from name/description/language.
- Fix email_digest reading the old nested weekly shape (AttributeError
on the new flat data — the Friday digest's trending section was broken).
- Add beautifulsoup4 dependency and parser unit tests with a fixture.
Cron schedule (weekly Mon 07:00 UTC) and infra are unchanged.
The preview.yml build-check ran `cdk synth`, which failed at
route53.HostedZone.fromLookup with StackAccountRegionNotSpecified:
the stack account came from CDK_DEFAULT_ACCOUNT, which is unset in CI
(no AWS creds), so the lookup key could not be formed.
- Fall back to the known deployment account (333674319299) when
CDK_DEFAULT_ACCOUNT is absent, so synth has an account/region.
- Commit cdk.context.json (un-ignore it) so the hosted-zone lookup is
served from cache and synth needs no live AWS call. This is the
CDK-recommended practice for reproducible synth.
Unblocks the PR build-check. Deploy (deploy.yml) is unaffected — it
already provides real credentials via OIDC.
@datj9
datj9 merged commit 4020077 into masterJun 7, 2026
2 checks passed
datj9 added a commit that referenced this pull request Jul 11, 2026
* fix(gh_trending): scrape github.com/trending for real trending data
The handler queried the GitHub Search API for recently-created repos
sorted by all-time stars, which is not actual trending data and never
matched github.com/trending. It also emitted a nested schema
({generated_at, weekly:{period,repos}}) that did not match what the
Astro site reads, and referenced an undefined HEADERS in the README
fetch (NameError, silently swallowed).
- Scrape https://github.com/trending?since=weekly|monthly (public HTML,
no token required) via BeautifulSoup; parse name/url/description/
language/stars/stars_this_period per Box-row article.
- Emit the flat schema the site expects: {updated_at, weekly[], monthly[]}
with stars_this_period, fixing the broken "+N this week" badges.
- README enrichment is now best-effort: works unauthenticated, returns
"" on failure so summaries still build from name/description/language.
- Fix email_digest reading the old nested weekly shape (AttributeError
on the new flat data — the Friday digest's trending section was broken).
- Add beautifulsoup4 dependency and parser unit tests with a fixture.
Cron schedule (weekly Mon 07:00 UTC) and infra are unchanged.
* fix(infra): make cdk synth work in CI without AWS credentials
The preview.yml build-check ran `cdk synth`, which failed at
route53.HostedZone.fromLookup with StackAccountRegionNotSpecified:
the stack account came from CDK_DEFAULT_ACCOUNT, which is unset in CI
(no AWS creds), so the lookup key could not be formed.
- Fall back to the known deployment account (333674319299) when
CDK_DEFAULT_ACCOUNT is absent, so synth has an account/region.
- Commit cdk.context.json (un-ignore it) so the hosted-zone lookup is
served from cache and synth needs no live AWS call. This is the
CDK-recommended practice for reproducible synth.
Unblocks the PR build-check. Deploy (deploy.yml) is unaffected — it
already provides real credentials via OIDC.
---------
Co-authored-by: Dat <dat.nguyen@ringkas.co.id>
@datj9
datj9 deleted the fix/gh-trending-scrape branch July 11, 2026 05:38
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@datj9@datnguyen-ringkas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(gh_trending): scrape github.com/trending for real trending data - #1

Merged
datj9 merged 2 commits into
masterfrom
fix/gh-trending-scrape
Jun 7, 2026
Merged

fix(gh_trending): scrape github.com/trending for real trending data#1
datj9 merged 2 commits into
masterfrom
fix/gh-trending-scrape

Conversation

@datj9

@datj9datj9 commented Jun 7, 2026

Copy link
Copy Markdown
Owner

Problem

The `gh_trending` Lambda never read github.com/trending. It queried the GitHub Search API for repos created in the last 7/30 days, sorted by all-time stars — a fundamentally different list from github.com/trending (which ranks by stars gained in a window). This is why the trending data on the live site looked wrong.

Two latent bugs rode along:

  • `_fetch_readme` referenced an undefined `HEADERS` → `NameError`, silently swallowed, so README context was never used.
  • The handler emitted a nested schema (`{generated_at, weekly:{period,repos}}`) that did not match what the Astro site reads. The site expects a flat `{updated_at, weekly[], monthly[]}` with a `stars_this_period` field — so the "+N this week" badges were always broken.
  • `email_digest` read the old nested shape (`data.get("weekly", {}).get("repos")`), which throws `AttributeError` against the real (flat) data — the Friday email's trending section was already broken on master.

Change

  • Scrape `https://github.com/trending?since=weekly|monthly\` (public HTML, no token required) via BeautifulSoup. Parse name / url / description / language / stars / stars_this_period per `article.Box-row`.
  • Emit the flat schema the site expects: `{updated_at, weekly[], monthly[]}` — fixes the broken period badges.
  • README enrichment is now best-effort: works unauthenticated, returns `""` on failure so summaries still build from the trending page's own name/description/language.
  • Fix `email_digest` to read the flat `weekly[]`.
  • Add `beautifulsoup4` dependency + parser unit tests with a fixture.

Cron schedule (weekly Mon 07:00 UTC) and all infra are unchanged. CDK bundling installs the new dependency automatically from `requirements.txt`.

Test plan

  • 7 parser unit tests pass (`pytest tests/test_gh_trending.py`)
  • ruff clean on changed files
  • Live smoke test: parsed 20 weekly + 19 monthly repos from the real page, correct stars/period/language, no token used
  • `email_digest._extract_top_repos` verified against flat data
  • infra TypeScript compiles (`tsc --noEmit`); other 3 Lambdas unaffected (no cross-imports)
  • Post-merge: CI `deploy.yml` rebuilds + deploys; verify `gh_trending` next run writes real trending data

Out of scope

The site has been stale since 2026-05-12 because SSM `/tech-bytes/github-token` is still `PLACEHOLDER`, so the auto-rebuild trigger no-ops. That's a separate ops fix (set a real PAT) and does not block this correctness fix.

The handler queried the GitHub Search API for recently-created repos
sorted by all-time stars, which is not actual trending data and never
matched github.com/trending. It also emitted a nested schema
({generated_at, weekly:{period,repos}}) that did not match what the
Astro site reads, and referenced an undefined HEADERS in the README
fetch (NameError, silently swallowed).
- Scrape https://github.com/trending?since=weekly|monthly (public HTML,
no token required) via BeautifulSoup; parse name/url/description/
language/stars/stars_this_period per Box-row article.
- Emit the flat schema the site expects: {updated_at, weekly[], monthly[]}
with stars_this_period, fixing the broken "+N this week" badges.
- README enrichment is now best-effort: works unauthenticated, returns
"" on failure so summaries still build from name/description/language.
- Fix email_digest reading the old nested weekly shape (AttributeError
on the new flat data — the Friday digest's trending section was broken).
- Add beautifulsoup4 dependency and parser unit tests with a fixture.
Cron schedule (weekly Mon 07:00 UTC) and infra are unchanged.
The preview.yml build-check ran `cdk synth`, which failed at
route53.HostedZone.fromLookup with StackAccountRegionNotSpecified:
the stack account came from CDK_DEFAULT_ACCOUNT, which is unset in CI
(no AWS creds), so the lookup key could not be formed.
- Fall back to the known deployment account (333674319299) when
CDK_DEFAULT_ACCOUNT is absent, so synth has an account/region.
- Commit cdk.context.json (un-ignore it) so the hosted-zone lookup is
served from cache and synth needs no live AWS call. This is the
CDK-recommended practice for reproducible synth.
Unblocks the PR build-check. Deploy (deploy.yml) is unaffected — it
already provides real credentials via OIDC.
@datj9
datj9 merged commit 4020077 into masterJun 7, 2026
2 checks passed
datj9 added a commit that referenced this pull request Jul 11, 2026
* fix(gh_trending): scrape github.com/trending for real trending data
The handler queried the GitHub Search API for recently-created repos
sorted by all-time stars, which is not actual trending data and never
matched github.com/trending. It also emitted a nested schema
({generated_at, weekly:{period,repos}}) that did not match what the
Astro site reads, and referenced an undefined HEADERS in the README
fetch (NameError, silently swallowed).
- Scrape https://github.com/trending?since=weekly|monthly (public HTML,
no token required) via BeautifulSoup; parse name/url/description/
language/stars/stars_this_period per Box-row article.
- Emit the flat schema the site expects: {updated_at, weekly[], monthly[]}
with stars_this_period, fixing the broken "+N this week" badges.
- README enrichment is now best-effort: works unauthenticated, returns
"" on failure so summaries still build from name/description/language.
- Fix email_digest reading the old nested weekly shape (AttributeError
on the new flat data — the Friday digest's trending section was broken).
- Add beautifulsoup4 dependency and parser unit tests with a fixture.
Cron schedule (weekly Mon 07:00 UTC) and infra are unchanged.
* fix(infra): make cdk synth work in CI without AWS credentials
The preview.yml build-check ran `cdk synth`, which failed at
route53.HostedZone.fromLookup with StackAccountRegionNotSpecified:
the stack account came from CDK_DEFAULT_ACCOUNT, which is unset in CI
(no AWS creds), so the lookup key could not be formed.
- Fall back to the known deployment account (333674319299) when
CDK_DEFAULT_ACCOUNT is absent, so synth has an account/region.
- Commit cdk.context.json (un-ignore it) so the hosted-zone lookup is
served from cache and synth needs no live AWS call. This is the
CDK-recommended practice for reproducible synth.
Unblocks the PR build-check. Deploy (deploy.yml) is unaffected — it
already provides real credentials via OIDC.
---------
Co-authored-by: Dat <dat.nguyen@ringkas.co.id>
@datj9
datj9 deleted the fix/gh-trending-scrape branch July 11, 2026 05:38
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@datj9@datnguyen-ringkas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(gh_trending): scrape github.com/trending for real trending data - #1

Merged
datj9 merged 2 commits into
masterfrom
fix/gh-trending-scrape
Jun 7, 2026
Merged

fix(gh_trending): scrape github.com/trending for real trending data#1
datj9 merged 2 commits into
masterfrom
fix/gh-trending-scrape

Conversation

@datj9

@datj9datj9 commented Jun 7, 2026

Copy link
Copy Markdown
Owner

Problem

The `gh_trending` Lambda never read github.com/trending. It queried the GitHub Search API for repos created in the last 7/30 days, sorted by all-time stars — a fundamentally different list from github.com/trending (which ranks by stars gained in a window). This is why the trending data on the live site looked wrong.

Two latent bugs rode along:

  • `_fetch_readme` referenced an undefined `HEADERS` → `NameError`, silently swallowed, so README context was never used.
  • The handler emitted a nested schema (`{generated_at, weekly:{period,repos}}`) that did not match what the Astro site reads. The site expects a flat `{updated_at, weekly[], monthly[]}` with a `stars_this_period` field — so the "+N this week" badges were always broken.
  • `email_digest` read the old nested shape (`data.get("weekly", {}).get("repos")`), which throws `AttributeError` against the real (flat) data — the Friday email's trending section was already broken on master.

Change

  • Scrape `https://github.com/trending?since=weekly|monthly\` (public HTML, no token required) via BeautifulSoup. Parse name / url / description / language / stars / stars_this_period per `article.Box-row`.
  • Emit the flat schema the site expects: `{updated_at, weekly[], monthly[]}` — fixes the broken period badges.
  • README enrichment is now best-effort: works unauthenticated, returns `""` on failure so summaries still build from the trending page's own name/description/language.
  • Fix `email_digest` to read the flat `weekly[]`.
  • Add `beautifulsoup4` dependency + parser unit tests with a fixture.

Cron schedule (weekly Mon 07:00 UTC) and all infra are unchanged. CDK bundling installs the new dependency automatically from `requirements.txt`.

Test plan

  • 7 parser unit tests pass (`pytest tests/test_gh_trending.py`)
  • ruff clean on changed files
  • Live smoke test: parsed 20 weekly + 19 monthly repos from the real page, correct stars/period/language, no token used
  • `email_digest._extract_top_repos` verified against flat data
  • infra TypeScript compiles (`tsc --noEmit`); other 3 Lambdas unaffected (no cross-imports)
  • Post-merge: CI `deploy.yml` rebuilds + deploys; verify `gh_trending` next run writes real trending data

Out of scope

The site has been stale since 2026-05-12 because SSM `/tech-bytes/github-token` is still `PLACEHOLDER`, so the auto-rebuild trigger no-ops. That's a separate ops fix (set a real PAT) and does not block this correctness fix.

The handler queried the GitHub Search API for recently-created repos
sorted by all-time stars, which is not actual trending data and never
matched github.com/trending. It also emitted a nested schema
({generated_at, weekly:{period,repos}}) that did not match what the
Astro site reads, and referenced an undefined HEADERS in the README
fetch (NameError, silently swallowed).
- Scrape https://github.com/trending?since=weekly|monthly (public HTML,
no token required) via BeautifulSoup; parse name/url/description/
language/stars/stars_this_period per Box-row article.
- Emit the flat schema the site expects: {updated_at, weekly[], monthly[]}
with stars_this_period, fixing the broken "+N this week" badges.
- README enrichment is now best-effort: works unauthenticated, returns
"" on failure so summaries still build from name/description/language.
- Fix email_digest reading the old nested weekly shape (AttributeError
on the new flat data — the Friday digest's trending section was broken).
- Add beautifulsoup4 dependency and parser unit tests with a fixture.
Cron schedule (weekly Mon 07:00 UTC) and infra are unchanged.
The preview.yml build-check ran `cdk synth`, which failed at
route53.HostedZone.fromLookup with StackAccountRegionNotSpecified:
the stack account came from CDK_DEFAULT_ACCOUNT, which is unset in CI
(no AWS creds), so the lookup key could not be formed.
- Fall back to the known deployment account (333674319299) when
CDK_DEFAULT_ACCOUNT is absent, so synth has an account/region.
- Commit cdk.context.json (un-ignore it) so the hosted-zone lookup is
served from cache and synth needs no live AWS call. This is the
CDK-recommended practice for reproducible synth.
Unblocks the PR build-check. Deploy (deploy.yml) is unaffected — it
already provides real credentials via OIDC.
@datj9
datj9 merged commit 4020077 into masterJun 7, 2026
2 checks passed
datj9 added a commit that referenced this pull request Jul 11, 2026
* fix(gh_trending): scrape github.com/trending for real trending data
The handler queried the GitHub Search API for recently-created repos
sorted by all-time stars, which is not actual trending data and never
matched github.com/trending. It also emitted a nested schema
({generated_at, weekly:{period,repos}}) that did not match what the
Astro site reads, and referenced an undefined HEADERS in the README
fetch (NameError, silently swallowed).
- Scrape https://github.com/trending?since=weekly|monthly (public HTML,
no token required) via BeautifulSoup; parse name/url/description/
language/stars/stars_this_period per Box-row article.
- Emit the flat schema the site expects: {updated_at, weekly[], monthly[]}
with stars_this_period, fixing the broken "+N this week" badges.
- README enrichment is now best-effort: works unauthenticated, returns
"" on failure so summaries still build from name/description/language.
- Fix email_digest reading the old nested weekly shape (AttributeError
on the new flat data — the Friday digest's trending section was broken).
- Add beautifulsoup4 dependency and parser unit tests with a fixture.
Cron schedule (weekly Mon 07:00 UTC) and infra are unchanged.
* fix(infra): make cdk synth work in CI without AWS credentials
The preview.yml build-check ran `cdk synth`, which failed at
route53.HostedZone.fromLookup with StackAccountRegionNotSpecified:
the stack account came from CDK_DEFAULT_ACCOUNT, which is unset in CI
(no AWS creds), so the lookup key could not be formed.
- Fall back to the known deployment account (333674319299) when
CDK_DEFAULT_ACCOUNT is absent, so synth has an account/region.
- Commit cdk.context.json (un-ignore it) so the hosted-zone lookup is
served from cache and synth needs no live AWS call. This is the
CDK-recommended practice for reproducible synth.
Unblocks the PR build-check. Deploy (deploy.yml) is unaffected — it
already provides real credentials via OIDC.
---------
Co-authored-by: Dat <dat.nguyen@ringkas.co.id>
@datj9
datj9 deleted the fix/gh-trending-scrape branch July 11, 2026 05:38
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@datj9@datnguyen-ringkas